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.
- ShaderFlow/Common/Imgui.py +0 -0
- ShaderFlow/Common/Notes.py +134 -0
- ShaderFlow/Common/__init__.py +0 -0
- ShaderFlow/Message.py +147 -0
- ShaderFlow/Module.py +162 -0
- ShaderFlow/Modules/Audio.py +431 -0
- ShaderFlow/Modules/Bouncing.py +94 -0
- ShaderFlow/Modules/Camera.py +451 -0
- ShaderFlow/Modules/Depth.py +0 -0
- ShaderFlow/Modules/Dynamics.py +288 -0
- ShaderFlow/Modules/Frametimer.py +79 -0
- ShaderFlow/Modules/Keyboard.py +43 -0
- ShaderFlow/Modules/Noise.py +80 -0
- ShaderFlow/Modules/OpticalFlow.py +1 -0
- ShaderFlow/Modules/Piano.py +362 -0
- ShaderFlow/Modules/Spectrogram.py +315 -0
- ShaderFlow/Modules/Video.py +234 -0
- ShaderFlow/Modules/Waveform.py +100 -0
- ShaderFlow/Modules/__init__.py +0 -0
- ShaderFlow/Resources/Images/ShaderFlow.png +0 -0
- ShaderFlow/Resources/Images/ShaderFlow.svg +1 -0
- ShaderFlow/Resources/Scenes/Examples/Demo.py +284 -0
- ShaderFlow/Resources/Scenes/Examples/GLSL/Bars.frag +20 -0
- ShaderFlow/Resources/Scenes/Examples/GLSL/Bouncing.frag +50 -0
- ShaderFlow/Resources/Scenes/Examples/GLSL/Life/Simulation.glsl +49 -0
- ShaderFlow/Resources/Scenes/Examples/GLSL/Life/Visuals.glsl +31 -0
- ShaderFlow/Resources/Scenes/Examples/GLSL/MotionBlur.frag +20 -0
- ShaderFlow/Resources/Scenes/Examples/GLSL/Multipass.frag +43 -0
- ShaderFlow/Resources/Scenes/Examples/GLSL/RayMarch.frag +59 -0
- ShaderFlow/Resources/Scenes/Examples/GLSL/ShaderToy.frag +65 -0
- ShaderFlow/Resources/Scenes/Examples/GLSL/Video.frag +5 -0
- ShaderFlow/Resources/Scenes/Examples/GLSL/Visualizer.frag +75 -0
- ShaderFlow/Resources/Scenes/Examples/GLSL/Waveform.frag +18 -0
- ShaderFlow/Resources/Shaders/Fragment/Default.glsl +65 -0
- ShaderFlow/Resources/Shaders/Fragment/Final.glsl +7 -0
- ShaderFlow/Resources/Shaders/Fragment/Loading.frag +40 -0
- ShaderFlow/Resources/Shaders/Fragment/Missing.glsl +23 -0
- ShaderFlow/Resources/Shaders/Include/Camera.glsl +166 -0
- ShaderFlow/Resources/Shaders/Include/Complex.glsl +0 -0
- ShaderFlow/Resources/Shaders/Include/ShaderFlow.glsl +444 -0
- ShaderFlow/Resources/Shaders/Vertex/Default.glsl +21 -0
- ShaderFlow/Scene.py +1124 -0
- ShaderFlow/Shader.py +391 -0
- ShaderFlow/Texture.py +528 -0
- ShaderFlow/Variable.py +128 -0
- ShaderFlow/__init__.py +11 -0
- ShaderFlow/__main__.py +22 -0
- shaderflow-0.4.0.dev1.dist-info/METADATA +79 -0
- shaderflow-0.4.0.dev1.dist-info/RECORD +51 -0
- shaderflow-0.4.0.dev1.dist-info/WHEEL +5 -0
- shaderflow-0.4.0.dev1.dist-info/entry_points.txt +2 -0
ShaderFlow/Shader.py
ADDED
|
@@ -0,0 +1,391 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import contextlib
|
|
4
|
+
import errno
|
|
5
|
+
import itertools
|
|
6
|
+
import os
|
|
7
|
+
import re
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Any, Iterable, List, Self, Set, Tuple, Union
|
|
10
|
+
|
|
11
|
+
import _moderngl
|
|
12
|
+
import imgui
|
|
13
|
+
import moderngl
|
|
14
|
+
import numpy
|
|
15
|
+
import rich
|
|
16
|
+
import watchdog
|
|
17
|
+
import watchdog.observers
|
|
18
|
+
from attr import Factory, define
|
|
19
|
+
from rich.panel import Panel
|
|
20
|
+
from rich.syntax import Syntax
|
|
21
|
+
|
|
22
|
+
import Broken
|
|
23
|
+
from Broken import BrokenPath, denum, log
|
|
24
|
+
from Broken.Loaders import LoaderString
|
|
25
|
+
from ShaderFlow import SHADERFLOW
|
|
26
|
+
from ShaderFlow.Message import ShaderMessage
|
|
27
|
+
from ShaderFlow.Module import ShaderModule
|
|
28
|
+
from ShaderFlow.Texture import ShaderTexture
|
|
29
|
+
from ShaderFlow.Variable import ShaderVariable, ShaderVariableDirection
|
|
30
|
+
|
|
31
|
+
WATCHDOG = watchdog.observers.Observer()
|
|
32
|
+
WATCHDOG.start()
|
|
33
|
+
|
|
34
|
+
@define
|
|
35
|
+
class ShaderDumper:
|
|
36
|
+
shader: ShaderObject # Fixme: Extending a parent class with refactored functionality
|
|
37
|
+
"""Parent ShaderObject instance"""
|
|
38
|
+
|
|
39
|
+
error: str
|
|
40
|
+
"""str(_moderngl.Error) exception"""
|
|
41
|
+
|
|
42
|
+
fragment: str
|
|
43
|
+
"""Potentially faulty Fragment shader"""
|
|
44
|
+
|
|
45
|
+
vertex: str
|
|
46
|
+
"""Potentially faulty Vertex shader"""
|
|
47
|
+
|
|
48
|
+
context: int = 5
|
|
49
|
+
"""Number of lines to show before and after the faulty line"""
|
|
50
|
+
|
|
51
|
+
_parser = re.compile(r"^0\((\d+)\)\s*:\s*error\s* (\w+):\s(.*)", re.MULTILINE)
|
|
52
|
+
|
|
53
|
+
@property
|
|
54
|
+
def code(self) -> str:
|
|
55
|
+
"""Simple heuristic to choose what shader cause the error"""
|
|
56
|
+
if ("fragment_shader" in self.error):
|
|
57
|
+
return self.fragment
|
|
58
|
+
elif ("vertex_shader" in self.error):
|
|
59
|
+
return self.vertex
|
|
60
|
+
raise RuntimeError(f"Cannot determine shader from error: {self.error}")
|
|
61
|
+
|
|
62
|
+
@property
|
|
63
|
+
def lines(self) -> List[str]:
|
|
64
|
+
return self.code.splitlines()
|
|
65
|
+
|
|
66
|
+
def dump(self):
|
|
67
|
+
directory = Broken.PROJECT.DIRECTORIES.DUMP
|
|
68
|
+
log.error(f"{self.shader.who} Dumping shaders to {directory}")
|
|
69
|
+
(directory/f"{self.shader.uuid}.frag").write_text(self.fragment, encoding="utf-8")
|
|
70
|
+
(directory/f"{self.shader.uuid}.vert").write_text(self.vertex, encoding="utf-8")
|
|
71
|
+
(directory/f"{self.shader.uuid}-error.md" ).write_text(self.error, encoding="utf-8")
|
|
72
|
+
|
|
73
|
+
# Visual only: Print highlighted code panels of all errors
|
|
74
|
+
for match in ShaderDumper._parser.finditer(self.error):
|
|
75
|
+
lineno, errno, message = match.groups()
|
|
76
|
+
lineno = int(lineno)
|
|
77
|
+
start = max(0, lineno - self.context - 1)
|
|
78
|
+
end = min(len(self.lines), lineno + self.context)
|
|
79
|
+
code = []
|
|
80
|
+
|
|
81
|
+
for i, line in enumerate(self.lines[start:end]):
|
|
82
|
+
div = (">" if (i == lineno) else "|")
|
|
83
|
+
code.append(f"({i+start+1:3d}) {div} {line}")
|
|
84
|
+
|
|
85
|
+
rich.print(Panel(
|
|
86
|
+
Syntax(code='\n'.join(code), lexer="glsl"),
|
|
87
|
+
title=f"({errno} at Module #{self.shader.uuid}, Line {lineno}): {message}",
|
|
88
|
+
))
|
|
89
|
+
|
|
90
|
+
@define
|
|
91
|
+
class ShaderObject(ShaderModule):
|
|
92
|
+
version: int = 330
|
|
93
|
+
"""OpenGL Version to use for the shader. Must be <= than the Window Backend version"""
|
|
94
|
+
|
|
95
|
+
program: moderngl.Program = None
|
|
96
|
+
"""ModernGL 'Compiled Shaders' object"""
|
|
97
|
+
|
|
98
|
+
vao: moderngl.VertexArray = None
|
|
99
|
+
"""Buffer object for the vertices of the shader"""
|
|
100
|
+
|
|
101
|
+
vbo: moderngl.Buffer = None
|
|
102
|
+
"""Buffer object for the vertices of the shader"""
|
|
103
|
+
|
|
104
|
+
texture: ShaderTexture = None
|
|
105
|
+
"""ShaderTexture Module that this Shader renders to in layers and temporal"""
|
|
106
|
+
|
|
107
|
+
clear: bool = False
|
|
108
|
+
"""Clear the Final Texture before rendering"""
|
|
109
|
+
|
|
110
|
+
instances: int = 1
|
|
111
|
+
"""Number of gl_InstanceID's to render"""
|
|
112
|
+
|
|
113
|
+
vertices: List[float] = Factory(list)
|
|
114
|
+
"""Vertices of the shader. More often than not, a Fullscreen Quad"""
|
|
115
|
+
|
|
116
|
+
vertex_variables: Set[ShaderVariable] = Factory(set)
|
|
117
|
+
"""Variables metaprogramming that will be added to the Vertex Shader"""
|
|
118
|
+
|
|
119
|
+
fragment_variables: Set[ShaderVariable] = Factory(set)
|
|
120
|
+
"""Variables metaprogramming that will be added to the Fragment Shader"""
|
|
121
|
+
|
|
122
|
+
def __post__(self):
|
|
123
|
+
"""Set default values for some variables"""
|
|
124
|
+
self.texture = ShaderTexture(scene=self.scene, name=self.name, track=True)
|
|
125
|
+
self.fragment_variable("out vec4 fragColor")
|
|
126
|
+
self.vertex_variable("in vec2 vertex_position")
|
|
127
|
+
self.vertex_variable("in vec2 vertex_gluv")
|
|
128
|
+
self.passthrough("flat int instance")
|
|
129
|
+
self.passthrough("vec2 gluv")
|
|
130
|
+
self.passthrough("vec2 stuv")
|
|
131
|
+
self.passthrough("vec2 astuv")
|
|
132
|
+
self.passthrough("vec2 agluv")
|
|
133
|
+
self.passthrough("vec2 fragCoord")
|
|
134
|
+
self.passthrough("vec2 glxy")
|
|
135
|
+
self.passthrough("vec2 stxy")
|
|
136
|
+
|
|
137
|
+
# Add a fullscreen center-(0, 0) uv rectangle
|
|
138
|
+
for x, y in itertools.product((-1, 1), (-1, 1)):
|
|
139
|
+
self.add_vertice(x=x, y=y, u=x, v=y)
|
|
140
|
+
|
|
141
|
+
# Load default vertex and fragment shaders
|
|
142
|
+
self.vertex = (SHADERFLOW.RESOURCES.VERTEX/ "Default.glsl")
|
|
143
|
+
self.fragment = (SHADERFLOW.RESOURCES.FRAGMENT/"Default.glsl")
|
|
144
|
+
|
|
145
|
+
def add_vertice(self, x: float=0, y: float=0, u: float=0, v: float=0) -> Self:
|
|
146
|
+
self.vertices.extend((x, y, u, v))
|
|
147
|
+
return self
|
|
148
|
+
|
|
149
|
+
def clear_vertices(self) -> Self:
|
|
150
|
+
self.vertices = []
|
|
151
|
+
|
|
152
|
+
def vertex_variable(self, variable: ShaderVariable) -> Self:
|
|
153
|
+
self.vertex_variables.add(ShaderVariable.smart(variable))
|
|
154
|
+
|
|
155
|
+
def fragment_variable(self, variable: ShaderVariable) -> Self:
|
|
156
|
+
self.fragment_variables.add(ShaderVariable.smart(variable))
|
|
157
|
+
|
|
158
|
+
def common_variable(self, variable: ShaderVariable) -> Self:
|
|
159
|
+
self.vertex_variable(variable)
|
|
160
|
+
self.fragment_variable(variable)
|
|
161
|
+
|
|
162
|
+
def passthrough(self, variable: ShaderVariable) -> Self:
|
|
163
|
+
variable = ShaderVariable.smart(variable)
|
|
164
|
+
self.vertex_variable(variable.copy(direction="out"))
|
|
165
|
+
self.fragment_variable(variable.copy(direction="in"))
|
|
166
|
+
|
|
167
|
+
@property
|
|
168
|
+
def vao_definition(self) -> Tuple[str]:
|
|
169
|
+
"""("2f 2f", "render_vertex", "coords_vertex")"""
|
|
170
|
+
sizes, names = [], []
|
|
171
|
+
for variable in self.vertex_variables:
|
|
172
|
+
if variable.direction == ShaderVariableDirection.In.value:
|
|
173
|
+
sizes.append(variable.size_string)
|
|
174
|
+
names.append(variable.name)
|
|
175
|
+
return (" ".join(sizes), *names)
|
|
176
|
+
|
|
177
|
+
def _build_shader(self, content: str, variables: Iterable[ShaderVariable]) -> str:
|
|
178
|
+
"""Build the final shader from the contents provided"""
|
|
179
|
+
shader = []
|
|
180
|
+
|
|
181
|
+
@contextlib.contextmanager
|
|
182
|
+
def section(name: str=""):
|
|
183
|
+
shader.append("\n\n// " + "-"*96 + "|")
|
|
184
|
+
shader.append(f"// ShaderFlow Section: ({name})\n")
|
|
185
|
+
yield
|
|
186
|
+
|
|
187
|
+
shader.append(f"#version {self.version}")
|
|
188
|
+
|
|
189
|
+
# Add variable definitions
|
|
190
|
+
with section("Variables"):
|
|
191
|
+
for variable in variables:
|
|
192
|
+
shader.append(variable.declaration)
|
|
193
|
+
|
|
194
|
+
with section("Include - ShaderFlow"):
|
|
195
|
+
shader.append(SHADERFLOW.RESOURCES.SHADERS_INCLUDE/"ShaderFlow.glsl")
|
|
196
|
+
|
|
197
|
+
# Add all modules includes to the shader
|
|
198
|
+
for module in self.scene.modules:
|
|
199
|
+
for defines in module.defines():
|
|
200
|
+
shader.append(defines)
|
|
201
|
+
|
|
202
|
+
for include in filter(None, module.includes()):
|
|
203
|
+
with section(f"Include - {module.who}"):
|
|
204
|
+
shader.append(include)
|
|
205
|
+
|
|
206
|
+
# Add shader content itself
|
|
207
|
+
with section("Content"):
|
|
208
|
+
shader.append(content)
|
|
209
|
+
|
|
210
|
+
return '\n'.join(map(LoaderString, shader))
|
|
211
|
+
|
|
212
|
+
# # Vertex shader content
|
|
213
|
+
|
|
214
|
+
def _watchshader(self, path: Path) -> None:
|
|
215
|
+
|
|
216
|
+
@define(eq=False)
|
|
217
|
+
class Handler(watchdog.events.FileSystemEventHandler):
|
|
218
|
+
shader: ShaderObject
|
|
219
|
+
def on_modified(self, event):
|
|
220
|
+
if self.shader.scene.rendering:
|
|
221
|
+
return
|
|
222
|
+
self.shader.scene.scheduler.once(self.shader.compile)
|
|
223
|
+
|
|
224
|
+
# Add the Shader Path to the watchdog for changes. Only ignore 'File Too Long'
|
|
225
|
+
# exceptions when non-path strings as we can't get max len easily per system
|
|
226
|
+
try:
|
|
227
|
+
if (path := BrokenPath(path).valid()):
|
|
228
|
+
WATCHDOG.schedule(Handler(self), path)
|
|
229
|
+
except OSError as error:
|
|
230
|
+
if error.errno != errno.ENAMETOOLONG:
|
|
231
|
+
raise error
|
|
232
|
+
|
|
233
|
+
_vertex: Union[Path, str] = ""
|
|
234
|
+
"""The 'User Content' of the Vertex Shader, interted after the Metaprogramming.
|
|
235
|
+
A Path value will be watched for changes and shaders will be automatically reloaded"""
|
|
236
|
+
|
|
237
|
+
def make_vertex(self, content: str) -> Self:
|
|
238
|
+
return self._build_shader(LoaderString(content), self.vertex_variables)
|
|
239
|
+
|
|
240
|
+
@property
|
|
241
|
+
def vertex(self) -> str:
|
|
242
|
+
return self.make_vertex(self._vertex)
|
|
243
|
+
|
|
244
|
+
@vertex.setter
|
|
245
|
+
def vertex(self, value: Union[Path, str]):
|
|
246
|
+
self._watchshader(value)
|
|
247
|
+
self._vertex = value
|
|
248
|
+
|
|
249
|
+
# # Fragment shader content
|
|
250
|
+
|
|
251
|
+
_fragment: Union[Path, str] = ""
|
|
252
|
+
"""The 'User Content' of the Fragment Shader, interted after the Metaprogramming.
|
|
253
|
+
A Path value will be watched for changes and shaders will be automatically reloaded"""
|
|
254
|
+
|
|
255
|
+
def make_fragment(self, content: str) -> Self:
|
|
256
|
+
return self._build_shader(LoaderString(content), self.fragment_variables)
|
|
257
|
+
|
|
258
|
+
@property
|
|
259
|
+
def fragment(self) -> str:
|
|
260
|
+
return self.make_fragment(self._fragment)
|
|
261
|
+
|
|
262
|
+
@fragment.setter
|
|
263
|
+
def fragment(self, value: Union[Path, str]):
|
|
264
|
+
self._watchshader(value)
|
|
265
|
+
self._fragment = value
|
|
266
|
+
|
|
267
|
+
# # Uniforms
|
|
268
|
+
|
|
269
|
+
def set_uniform(self, name: str, value: Any=None) -> None:
|
|
270
|
+
if (self.program is None):
|
|
271
|
+
raise RuntimeError(f"Shader {self.who} hasn't been compiled yet")
|
|
272
|
+
if (value is not None) and (uniform := self.program.get(name, None)):
|
|
273
|
+
uniform.value = denum(value)
|
|
274
|
+
|
|
275
|
+
def get_uniform(self, name: str) -> Any | None:
|
|
276
|
+
return self.program.get(name, None)
|
|
277
|
+
|
|
278
|
+
# # Rendering
|
|
279
|
+
|
|
280
|
+
def _full_pipeline(self) -> Iterable[ShaderVariable]:
|
|
281
|
+
for module in self.scene.modules:
|
|
282
|
+
yield from module.pipeline()
|
|
283
|
+
|
|
284
|
+
def compile(self, _vertex: str=None, _fragment: str=None) -> Self:
|
|
285
|
+
log.info(f"{self.who} Compiling shaders")
|
|
286
|
+
|
|
287
|
+
# Add pipeline variable definitions
|
|
288
|
+
for variable in self._full_pipeline():
|
|
289
|
+
self.common_variable(variable)
|
|
290
|
+
|
|
291
|
+
# Metaprogram either injected or proper shaders
|
|
292
|
+
fragment = self.make_fragment(_fragment or self._fragment)
|
|
293
|
+
vertex = self.make_vertex(_vertex or self._vertex)
|
|
294
|
+
|
|
295
|
+
try:
|
|
296
|
+
self.program = self.scene.opengl.program(vertex, fragment)
|
|
297
|
+
except _moderngl.Error as error:
|
|
298
|
+
ShaderDumper(
|
|
299
|
+
shader=self,
|
|
300
|
+
error=str(error),
|
|
301
|
+
vertex=vertex,
|
|
302
|
+
fragment=fragment
|
|
303
|
+
).dump()
|
|
304
|
+
|
|
305
|
+
if (_vertex or _fragment):
|
|
306
|
+
raise RuntimeError(log.error("Recursion on Missing Texture Shader Loading"))
|
|
307
|
+
|
|
308
|
+
log.error(f"{self.who} Error compiling shaders, loading missing texture shader")
|
|
309
|
+
self.compile(
|
|
310
|
+
_vertex =LoaderString(SHADERFLOW.RESOURCES.VERTEX/"Default.glsl"),
|
|
311
|
+
_fragment=LoaderString(SHADERFLOW.RESOURCES.FRAGMENT/"Missing.glsl")
|
|
312
|
+
)
|
|
313
|
+
|
|
314
|
+
# Render the vertices that are defined on the shader
|
|
315
|
+
self.vbo = self.scene.opengl.buffer(numpy.array(self.vertices, dtype="f4"))
|
|
316
|
+
self.vao = self.scene.opengl.vertex_array(
|
|
317
|
+
self.program, [(self.vbo, *self.vao_definition)],
|
|
318
|
+
skip_errors=True
|
|
319
|
+
)
|
|
320
|
+
|
|
321
|
+
return self
|
|
322
|
+
|
|
323
|
+
# # Module
|
|
324
|
+
|
|
325
|
+
def update(self) -> None:
|
|
326
|
+
if (self.program is None):
|
|
327
|
+
self.compile()
|
|
328
|
+
self.render()
|
|
329
|
+
|
|
330
|
+
SKIP_GPU: bool = (os.environ.get("SKIP_GPU", "0") == "1")
|
|
331
|
+
"""Do not render shaders, useful for benchmarking raw Python performance"""
|
|
332
|
+
|
|
333
|
+
def render_fbo(self, fbo: moderngl.Framebuffer, clear: bool=True) -> None:
|
|
334
|
+
if self.SKIP_GPU:
|
|
335
|
+
return
|
|
336
|
+
fbo.use()
|
|
337
|
+
clear or fbo.clear()
|
|
338
|
+
self.vao.render(
|
|
339
|
+
moderngl.TRIANGLE_STRIP,
|
|
340
|
+
instances=self.instances
|
|
341
|
+
)
|
|
342
|
+
|
|
343
|
+
def use_pipeline(self, pipeline: Iterable[ShaderVariable]) -> None:
|
|
344
|
+
for index, variable in enumerate(pipeline):
|
|
345
|
+
# if variable not in self.fragment_variables:
|
|
346
|
+
# self.load_shaders()
|
|
347
|
+
if (variable.type == "sampler2D"):
|
|
348
|
+
self.set_uniform(variable.name, index)
|
|
349
|
+
variable.value.use(index)
|
|
350
|
+
continue
|
|
351
|
+
self.set_uniform(variable.name, variable.value)
|
|
352
|
+
|
|
353
|
+
def render(self) -> None:
|
|
354
|
+
|
|
355
|
+
# Optimization: Final shader doesn't need the full pipeline
|
|
356
|
+
if self.texture.final:
|
|
357
|
+
self.use_pipeline(self.scene.shader.texture.pipeline())
|
|
358
|
+
self.set_uniform("iFlip", self.scene.rendering)
|
|
359
|
+
self.render_fbo(self.texture.fbo(), clear=False)
|
|
360
|
+
return
|
|
361
|
+
|
|
362
|
+
self.use_pipeline(self._full_pipeline())
|
|
363
|
+
|
|
364
|
+
# Optimization: Only the iLayer uniform changes
|
|
365
|
+
for layer, box in enumerate(self.texture.row(0)):
|
|
366
|
+
self.set_uniform("iLayer", layer)
|
|
367
|
+
self.render_fbo(fbo=box.fbo, clear=box.clear)
|
|
368
|
+
|
|
369
|
+
self.texture.roll()
|
|
370
|
+
|
|
371
|
+
def handle(self, message: ShaderMessage) -> None:
|
|
372
|
+
if isinstance(message, ShaderMessage.Shader.Compile):
|
|
373
|
+
self.compile()
|
|
374
|
+
|
|
375
|
+
elif isinstance(message, ShaderMessage.Shader.Render):
|
|
376
|
+
self.render()
|
|
377
|
+
|
|
378
|
+
# Fixme: Should this be on a proper User Interface class?
|
|
379
|
+
if self.texture.final:
|
|
380
|
+
self.scene._render_ui()
|
|
381
|
+
|
|
382
|
+
def __ui__(self) -> None:
|
|
383
|
+
if imgui.button("Reload"):
|
|
384
|
+
self.compile()
|
|
385
|
+
imgui.same_line()
|
|
386
|
+
if imgui.button("Dump"):
|
|
387
|
+
self.dump_shaders()
|
|
388
|
+
if imgui.tree_node("Pipeline"):
|
|
389
|
+
for variable in self._full_pipeline():
|
|
390
|
+
imgui.text(f"{variable.name.ljust(16)}: {variable.value}")
|
|
391
|
+
imgui.tree_pop()
|