shaderflow 0.4.0.dev1__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of shaderflow might be problematic. Click here for more details.

Files changed (51) hide show
  1. ShaderFlow/Common/Imgui.py +0 -0
  2. ShaderFlow/Common/Notes.py +134 -0
  3. ShaderFlow/Common/__init__.py +0 -0
  4. ShaderFlow/Message.py +147 -0
  5. ShaderFlow/Module.py +162 -0
  6. ShaderFlow/Modules/Audio.py +431 -0
  7. ShaderFlow/Modules/Bouncing.py +94 -0
  8. ShaderFlow/Modules/Camera.py +451 -0
  9. ShaderFlow/Modules/Depth.py +0 -0
  10. ShaderFlow/Modules/Dynamics.py +288 -0
  11. ShaderFlow/Modules/Frametimer.py +79 -0
  12. ShaderFlow/Modules/Keyboard.py +43 -0
  13. ShaderFlow/Modules/Noise.py +80 -0
  14. ShaderFlow/Modules/OpticalFlow.py +1 -0
  15. ShaderFlow/Modules/Piano.py +362 -0
  16. ShaderFlow/Modules/Spectrogram.py +315 -0
  17. ShaderFlow/Modules/Video.py +234 -0
  18. ShaderFlow/Modules/Waveform.py +100 -0
  19. ShaderFlow/Modules/__init__.py +0 -0
  20. ShaderFlow/Resources/Images/ShaderFlow.png +0 -0
  21. ShaderFlow/Resources/Images/ShaderFlow.svg +1 -0
  22. ShaderFlow/Resources/Scenes/Examples/Demo.py +284 -0
  23. ShaderFlow/Resources/Scenes/Examples/GLSL/Bars.frag +20 -0
  24. ShaderFlow/Resources/Scenes/Examples/GLSL/Bouncing.frag +50 -0
  25. ShaderFlow/Resources/Scenes/Examples/GLSL/Life/Simulation.glsl +49 -0
  26. ShaderFlow/Resources/Scenes/Examples/GLSL/Life/Visuals.glsl +31 -0
  27. ShaderFlow/Resources/Scenes/Examples/GLSL/MotionBlur.frag +20 -0
  28. ShaderFlow/Resources/Scenes/Examples/GLSL/Multipass.frag +43 -0
  29. ShaderFlow/Resources/Scenes/Examples/GLSL/RayMarch.frag +59 -0
  30. ShaderFlow/Resources/Scenes/Examples/GLSL/ShaderToy.frag +65 -0
  31. ShaderFlow/Resources/Scenes/Examples/GLSL/Video.frag +5 -0
  32. ShaderFlow/Resources/Scenes/Examples/GLSL/Visualizer.frag +75 -0
  33. ShaderFlow/Resources/Scenes/Examples/GLSL/Waveform.frag +18 -0
  34. ShaderFlow/Resources/Shaders/Fragment/Default.glsl +65 -0
  35. ShaderFlow/Resources/Shaders/Fragment/Final.glsl +7 -0
  36. ShaderFlow/Resources/Shaders/Fragment/Loading.frag +40 -0
  37. ShaderFlow/Resources/Shaders/Fragment/Missing.glsl +23 -0
  38. ShaderFlow/Resources/Shaders/Include/Camera.glsl +166 -0
  39. ShaderFlow/Resources/Shaders/Include/Complex.glsl +0 -0
  40. ShaderFlow/Resources/Shaders/Include/ShaderFlow.glsl +444 -0
  41. ShaderFlow/Resources/Shaders/Vertex/Default.glsl +21 -0
  42. ShaderFlow/Scene.py +1124 -0
  43. ShaderFlow/Shader.py +391 -0
  44. ShaderFlow/Texture.py +528 -0
  45. ShaderFlow/Variable.py +128 -0
  46. ShaderFlow/__init__.py +11 -0
  47. ShaderFlow/__main__.py +22 -0
  48. shaderflow-0.4.0.dev1.dist-info/METADATA +79 -0
  49. shaderflow-0.4.0.dev1.dist-info/RECORD +51 -0
  50. shaderflow-0.4.0.dev1.dist-info/WHEEL +5 -0
  51. shaderflow-0.4.0.dev1.dist-info/entry_points.txt +2 -0
File without changes
@@ -0,0 +1,134 @@
1
+ import functools
2
+ import math
3
+ from typing import Any, Self
4
+
5
+ from attr import define
6
+
7
+ from Broken import BrokenFluentBuilder
8
+
9
+ PIANO_NOTES = "C C# D D# E F F# G G# A A# B".split()
10
+
11
+ @define(eq=False)
12
+ class BrokenPianoNote(BrokenFluentBuilder):
13
+ note: int = 60
14
+ start: float = 0
15
+ end: float = 0
16
+ channel: int = 0
17
+ velocity: int = 100
18
+ tuning: float = 440
19
+
20
+ def __hash__(self):
21
+ return hash((self.note, self.start, self.end, self.channel, self.velocity))
22
+
23
+ def idk_hash(self):
24
+ return hash((self.note, self.channel))
25
+
26
+ def __eq__(self, other):
27
+ return hash(self) == hash(other)
28
+
29
+ # # Initialization
30
+
31
+ @classmethod
32
+ @functools.lru_cache
33
+ def from_index(cls, note: int, **kwargs) -> Self:
34
+ return cls(note=note, **kwargs)
35
+
36
+ @classmethod
37
+ @functools.lru_cache
38
+ def from_name(cls, name: str, **kwargs) -> Self:
39
+ return cls(note=BrokenPianoNote.name_to_index(name), **kwargs)
40
+
41
+ @classmethod
42
+ @functools.lru_cache
43
+ def from_frequency(cls, frequency: float, **kwargs) -> Self:
44
+ return cls(note=BrokenPianoNote.frequency_to_index(frequency), **kwargs)
45
+
46
+ @classmethod
47
+ @functools.lru_cache
48
+ def get(cls, object: Any, **kwargs) -> Self:
49
+ if isinstance(object, BrokenPianoNote):
50
+ return object(**kwargs)
51
+ elif isinstance(object, int):
52
+ return cls.from_index(object, **kwargs)
53
+ elif isinstance(object, str):
54
+ return cls.from_name(object, **kwargs)
55
+ elif isinstance(object, float):
56
+ return cls.from_frequency(object, **kwargs)
57
+ return cls(**kwargs)
58
+
59
+ # # Conversion
60
+
61
+ @staticmethod
62
+ @functools.lru_cache
63
+ def index_to_name(index: int) -> str:
64
+ return f"{PIANO_NOTES[index % 12]}{index//12 - 1}"
65
+
66
+ @staticmethod
67
+ @functools.lru_cache
68
+ def index_to_frequency(index: int, *, tuning: float=440) -> float:
69
+ return tuning * 2**((index - 69)/12)
70
+
71
+ @staticmethod
72
+ @functools.lru_cache
73
+ def name_to_index(name: str) -> int:
74
+ note, octave = name[:-1].upper(), int(name[-1])
75
+ return PIANO_NOTES.index(note) + 12*(octave + 1)
76
+
77
+ @staticmethod
78
+ @functools.lru_cache
79
+ def name_to_frequency(name: str, *, tuning: float=440) -> float:
80
+ return BrokenPianoNote.index_to_frequency(BrokenPianoNote.name_to_index(name), tuning=tuning)
81
+
82
+ @staticmethod
83
+ @functools.lru_cache
84
+ def frequency_to_index(frequency: float, *, tuning: float=440) -> int:
85
+ return round(12*math.log2(frequency/tuning) + 69)
86
+
87
+ @staticmethod
88
+ @functools.lru_cache
89
+ def frequency_to_name(frequency: float, *, tuning: float=440) -> str:
90
+ return BrokenPianoNote.index_to_name(BrokenPianoNote.frequency_to_index(frequency, tuning=tuning))
91
+
92
+ # # Utilities
93
+
94
+ @property
95
+ def frequency(self) -> float:
96
+ return BrokenPianoNote.index_to_frequency(self.note, tuning=self.tuning)
97
+
98
+ @frequency.setter
99
+ def frequency(self, value: float):
100
+ self.note = BrokenPianoNote.frequency_to_index(value, tuning=self.tuning)
101
+
102
+ @property
103
+ def name(self) -> str:
104
+ return BrokenPianoNote.index_to_name(self.note)
105
+
106
+ @name.setter
107
+ def name(self, value: str):
108
+ self.note = BrokenPianoNote.name_to_index(value)
109
+
110
+ # Black and White
111
+
112
+ def is_white(note: int) -> bool:
113
+ return (note % 12) in {0, 2, 4, 5, 7, 9, 11}
114
+
115
+ def is_black(note: int) -> bool:
116
+ return (note % 12) in {1, 3, 6, 8, 10}
117
+
118
+ @property
119
+ def white(self) -> bool:
120
+ return BrokenPianoNote.is_white(self.note)
121
+
122
+ @property
123
+ def black(self) -> bool:
124
+ return BrokenPianoNote.is_black(self.note)
125
+
126
+ # Temporal
127
+
128
+ @property
129
+ def duration(self):
130
+ return self.end - self.start
131
+
132
+ @duration.setter
133
+ def duration(self, value: float):
134
+ self.end = self.start + value
File without changes
ShaderFlow/Message.py ADDED
@@ -0,0 +1,147 @@
1
+ from typing import Any
2
+
3
+ from attr import define
4
+
5
+
6
+ class ShaderMessage:
7
+
8
+ # # Special
9
+
10
+ class Custom:
11
+ """Any data type"""
12
+ data: Any
13
+
14
+ # # Mouse
15
+
16
+ class Mouse:
17
+
18
+ @define
19
+ class Position:
20
+ # Real
21
+ x: int = 0
22
+ y: int = 0
23
+ dx: int = 0
24
+ dy: int = 0
25
+
26
+ # Normalized
27
+ u: float = 0.0
28
+ v: float = 0.0
29
+ du: float = 0.0
30
+ dv: float = 0.0
31
+
32
+ @define
33
+ class Press:
34
+ button: int = 0
35
+
36
+ # Real
37
+ x: int = 0
38
+ y: int = 0
39
+
40
+ # Normalized
41
+ u: float = 0.0
42
+ v: float = 0.0
43
+
44
+ @define
45
+ class Release:
46
+ button: int = 0
47
+
48
+ # Real
49
+ x: int = 0
50
+ y: int = 0
51
+
52
+ # Normalized
53
+ u: float = 0.0
54
+ v: float = 0.0
55
+
56
+ @define
57
+ class Drag:
58
+ # Real
59
+ x: int = 0
60
+ y: int = 0
61
+ dx: int = 0
62
+ dy: int = 0
63
+
64
+ # Normalized
65
+ u: float = 0.0
66
+ v: float = 0.0
67
+ du: float = 0.0
68
+ dv: float = 0.0
69
+
70
+ @define
71
+ class Scroll:
72
+ # Real
73
+ dx: int = 0
74
+ dy: int = 0
75
+
76
+ # Normalized
77
+ du: float = 0.0
78
+ dv: float = 0.0
79
+
80
+ @define
81
+ class Enter:
82
+ state: bool
83
+
84
+ # # Window
85
+
86
+ class Window:
87
+
88
+ @define
89
+ class Resize:
90
+ width: int = None
91
+ height: int = None
92
+
93
+ @property
94
+ def size(self) -> tuple[int, int]:
95
+ return self.width, self.height
96
+
97
+ @define
98
+ class Iconify:
99
+ state: bool = None
100
+
101
+ @define
102
+ class FileDrop:
103
+ files: list[str] = None
104
+
105
+ @define
106
+ class Close:
107
+ ...
108
+
109
+ # # Shader
110
+
111
+ class Shader:
112
+
113
+ @define
114
+ class RecreateTextures:
115
+ ...
116
+
117
+ @define
118
+ class Compile:
119
+ ...
120
+
121
+ @define
122
+ class Render:
123
+ ...
124
+
125
+ # # Keyboard
126
+
127
+ class Keyboard:
128
+
129
+ @define
130
+ class Press:
131
+ key: int = None
132
+ action: int = None
133
+ modifiers: int = None
134
+
135
+ @define
136
+ class KeyDown:
137
+ key: int = None
138
+ modifiers: int = None
139
+
140
+ @define
141
+ class KeyUp:
142
+ key: int = None
143
+ modifiers: int = None
144
+
145
+ @define
146
+ class Unicode:
147
+ char: str = None
ShaderFlow/Module.py ADDED
@@ -0,0 +1,162 @@
1
+ from __future__ import annotations
2
+
3
+ import itertools
4
+ from abc import abstractmethod
5
+ from typing import TYPE_CHECKING, Iterable, Self, Type, Union
6
+
7
+ from attr import Factory, define, field
8
+
9
+ from Broken import BrokenAttrs, BrokenFluentBuilder, log
10
+ from Broken.Externals.FFmpeg import BrokenFFmpeg
11
+ from ShaderFlow.Message import ShaderMessage
12
+ from ShaderFlow.Variable import ShaderVariable
13
+
14
+
15
+ @define
16
+ class ShaderModule(BrokenFluentBuilder, BrokenAttrs):
17
+ scene: ShaderScene = field(default=None, repr=False)
18
+ """The ShaderScene this module belongs to. Must be set on initialization of any module with
19
+ `ShaderModule(scene=...)` even though marked as `default=None` to make flexible MRO parenting"""
20
+
21
+ uuid: int = Factory(itertools.count(1).__next__)
22
+ """A module identifier, mostly used for differentiating log statements of same type modules"""
23
+
24
+ name: str = None
25
+ """The base name for exported GLSL variables, textures, etc. It is technically optional, but
26
+ it's not a bad idea for all modules to have a default value for this attribute than None"""
27
+
28
+ @property
29
+ def who(self) -> str:
30
+ return f"({self.uuid:>2}) {type(self).__name__[:18].ljust(18)} │ ▸"
31
+
32
+ def __post__(self):
33
+ # Post-import for type checking to avoid circular reference
34
+ from ShaderFlow.Scene import ShaderScene
35
+
36
+ # The module can be a ShaderScene itself
37
+ self.scene = self.scene or self
38
+
39
+ # Module must be part of a 'scene=instance(ShaderScene)'
40
+ if not isinstance(self.scene, ShaderScene):
41
+ raise RuntimeError(log.error('\n'.join((
42
+ f"Module of type '{type(self).__name__}' must be added to a 'ShaderScene' instance",
43
+ f"• Initialize it with {type(self).__name__}(scene='instance(ShaderScene)', ...)",
44
+ ))))
45
+
46
+ log.trace(f"{self.who} Module added to the Scene")
47
+ self.scene.modules.append(self)
48
+ self.commands()
49
+
50
+ @abstractmethod
51
+ def commands(self) -> None:
52
+ """Add commands to the scene with `self.scene.typer.command(target=..., ...)`"""
53
+ ...
54
+
55
+ # # Messaging
56
+
57
+ def relay(self, message: Union[ShaderMessage, Type[ShaderMessage]]) -> Self:
58
+ """Send a message to all modules in the scene. Handle it defining a `self.handle(message)`"""
59
+ if isinstance(message, type):
60
+ message = message()
61
+ for module in self.scene.modules:
62
+ module.handle(message)
63
+ return self
64
+
65
+ @abstractmethod
66
+ def handle(self, message: ShaderMessage) -> None:
67
+ """Whenever a module relays a message on the scene, all modules are signaled via this method
68
+ for potentially acting on it. A Camera might move on WASD keys, for example"""
69
+ ...
70
+
71
+ def find(self, type: Type[ShaderModule]) -> Iterable[ShaderModule]:
72
+ for module in self.scene.modules:
73
+ if isinstance(module, type):
74
+ yield module
75
+
76
+ @property
77
+ @abstractmethod
78
+ def duration(self) -> float:
79
+ """Self-reported 'time to render' until the module is finished. A ShaderAudio shall export
80
+ the input audio duration, for example"""
81
+ return 0.0
82
+
83
+ @abstractmethod
84
+ def build(self) -> None:
85
+ """Create Textures, child ShaderModules, load base shaders, etc. Happens only once, and it's
86
+ a good place to set default values for attributes, such as a background image that can be
87
+ later changed on `self.setup()` or, better yet, on the CLI of the module/custom Scene"""
88
+ pass
89
+
90
+ @abstractmethod
91
+ def setup(self) -> None:
92
+ """Called every time before the initialization (rendering) of the Scene. Useful for managing
93
+ the behavior of batch exporting per export index; also a good place to reset values to their
94
+ defaults or create procedural objects (seeds) after `self.build()`"""
95
+ pass
96
+
97
+ @abstractmethod
98
+ def pipeline(self) -> Iterable[ShaderVariable]:
99
+ """Returns the list of variables that will be exported to all Shaders of the scene. The
100
+ first compilation happens after `self.build()`, where all variables are metaprogrammed into
101
+ the GLSL code. Subsequent calls happens after all `self.update()` on every frame and the
102
+ variables are updated to the yielded values here"""
103
+ return []
104
+
105
+ @abstractmethod
106
+ def update(self) -> None:
107
+ """Called every frame. This defines the main behavior of the module inside the event loop.
108
+ All non-ShaderModules are called first, then the former ones. Access state data directly
109
+ on the Scene with `self.scene.{dt,time,width,height,...}`"""
110
+ pass
111
+
112
+ @abstractmethod
113
+ def ffmpeg(self, ffmpeg: BrokenFFmpeg) -> None:
114
+ """When exporting the Scene, after the initial CLI configuration of FFmpeg by the Scene's
115
+ `self.main` method, all modules have an option to change the FFmpeg settings on the fly.
116
+ Note that this can also be implemented on a custom Scene itself, and behavior _can_ be
117
+ changed per batch exporting"""
118
+ pass
119
+
120
+ # ------------------------------------------|
121
+ # Stuff pending a remaster
122
+
123
+ def __process_include__(self, include: str) -> str:
124
+ return include.format({f"${k}": v for k, v in vars(self).items()})
125
+
126
+ @abstractmethod
127
+ def includes(self) -> Iterable[str]:
128
+ yield ""
129
+
130
+ @abstractmethod
131
+ def defines(self) -> Iterable[str]:
132
+ yield None
133
+
134
+ # # User interface
135
+
136
+ def __shaderflow_ui__(self) -> None:
137
+ """Basic info of a Module"""
138
+ # Todo: Make automatic Imgui methods
139
+
140
+ # Module - self.__ui__ must be implemented
141
+ if not getattr(self.__ui__, "__isabstractmethod__", False):
142
+ self.__ui__()
143
+
144
+ # Module - self.ui must be implemented
145
+ if not getattr(self.ui, "__isabstractmethod__", False):
146
+ self.ui()
147
+
148
+ @abstractmethod
149
+ def __ui__(self) -> None:
150
+ """Internal method for self.ui"""
151
+ pass
152
+
153
+ @abstractmethod
154
+ def ui(self) -> None:
155
+ """
156
+ Draw the UI for this module
157
+ """
158
+ pass
159
+
160
+ # Avoid circular reference on Module importing partial Scene
161
+ if TYPE_CHECKING:
162
+ from ShaderFlow.Scene import ShaderScene