procfunc 0.30.0__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.
Files changed (76) hide show
  1. procfunc/__init__.py +87 -0
  2. procfunc/color.py +57 -0
  3. procfunc/compute_graph/__init__.py +28 -0
  4. procfunc/compute_graph/compute_graph.py +115 -0
  5. procfunc/compute_graph/node.py +200 -0
  6. procfunc/compute_graph/operators_info.py +92 -0
  7. procfunc/compute_graph/proxy.py +173 -0
  8. procfunc/compute_graph/util.py +282 -0
  9. procfunc/context.py +115 -0
  10. procfunc/control.py +174 -0
  11. procfunc/nodes/__init__.py +66 -0
  12. procfunc/nodes/bindings_util.py +196 -0
  13. procfunc/nodes/bpy_node_info.py +280 -0
  14. procfunc/nodes/compositor.py +2242 -0
  15. procfunc/nodes/execute/construct_nodes.py +571 -0
  16. procfunc/nodes/execute/construct_special_cases.py +246 -0
  17. procfunc/nodes/execute/execute.py +548 -0
  18. procfunc/nodes/execute/infer_runtime_data_type.py +195 -0
  19. procfunc/nodes/execute/util.py +247 -0
  20. procfunc/nodes/func.py +1417 -0
  21. procfunc/nodes/geo.py +4240 -0
  22. procfunc/nodes/manifest.json +8769 -0
  23. procfunc/nodes/math.py +644 -0
  24. procfunc/nodes/node_function.py +160 -0
  25. procfunc/nodes/shader.py +2359 -0
  26. procfunc/nodes/types.py +347 -0
  27. procfunc/ops/__init__.py +35 -0
  28. procfunc/ops/_util.py +275 -0
  29. procfunc/ops/addons.py +59 -0
  30. procfunc/ops/attr.py +426 -0
  31. procfunc/ops/collection.py +90 -0
  32. procfunc/ops/curve.py +18 -0
  33. procfunc/ops/file.py +126 -0
  34. procfunc/ops/manifest.json +39149 -0
  35. procfunc/ops/mesh.py +1510 -0
  36. procfunc/ops/modifier.py +603 -0
  37. procfunc/ops/object.py +258 -0
  38. procfunc/ops/primitives/__init__.py +31 -0
  39. procfunc/ops/primitives/camera.py +45 -0
  40. procfunc/ops/primitives/curve.py +71 -0
  41. procfunc/ops/primitives/light.py +114 -0
  42. procfunc/ops/primitives/mesh.py +358 -0
  43. procfunc/ops/uv.py +271 -0
  44. procfunc/random.py +247 -0
  45. procfunc/tracer/__init__.py +43 -0
  46. procfunc/tracer/decorator.py +121 -0
  47. procfunc/tracer/patch.py +494 -0
  48. procfunc/tracer/proxy.py +127 -0
  49. procfunc/tracer/trace.py +222 -0
  50. procfunc/transforms/__init__.py +49 -0
  51. procfunc/transforms/cleanup.py +214 -0
  52. procfunc/transforms/convert.py +20 -0
  53. procfunc/transforms/distribution.py +191 -0
  54. procfunc/transforms/extract_materials.py +116 -0
  55. procfunc/transforms/infer_distribution.py +326 -0
  56. procfunc/transforms/parameters.py +15 -0
  57. procfunc/transforms/util.py +35 -0
  58. procfunc/transpiler/__init__.py +24 -0
  59. procfunc/transpiler/bpy_to_computegraph.py +1348 -0
  60. procfunc/transpiler/codegen.py +919 -0
  61. procfunc/transpiler/identifiers.py +595 -0
  62. procfunc/transpiler/main.py +299 -0
  63. procfunc/types.py +380 -0
  64. procfunc/util/__init__.py +0 -0
  65. procfunc/util/bpy_info.py +145 -0
  66. procfunc/util/camera.py +0 -0
  67. procfunc/util/keyframe.py +70 -0
  68. procfunc/util/log.py +96 -0
  69. procfunc/util/manifest.py +121 -0
  70. procfunc/util/pytree.py +343 -0
  71. procfunc/util/teardown.py +37 -0
  72. procfunc-0.30.0.dist-info/METADATA +120 -0
  73. procfunc-0.30.0.dist-info/RECORD +76 -0
  74. procfunc-0.30.0.dist-info/WHEEL +5 -0
  75. procfunc-0.30.0.dist-info/licenses/LICENSE.md +11 -0
  76. procfunc-0.30.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,2359 @@
1
+ """
2
+ Auto-generated Shader Node bindings for Blender
3
+ """
4
+
5
+ import logging
6
+ from typing import Any, Literal, NamedTuple
7
+
8
+ import procfunc as pf
9
+ from procfunc import types as pt
10
+ from procfunc.nodes import types as nt
11
+ from procfunc.nodes.bindings_util import (
12
+ raise_error_or_warn,
13
+ raise_explicit_noise_vector_error,
14
+ raise_io_error,
15
+ raise_shader_normal_error,
16
+ )
17
+
18
+ TBlendType = Literal[
19
+ "MIX",
20
+ "DARKEN",
21
+ "MULTIPLY",
22
+ "BURN",
23
+ "LIGHTEN",
24
+ "SCREEN",
25
+ "DODGE",
26
+ "ADD",
27
+ "OVERLAY",
28
+ "SOFT_LIGHT",
29
+ "LINEAR_LIGHT",
30
+ "DIFFERENCE",
31
+ "EXCLUSION",
32
+ "SUBTRACT",
33
+ "DIVIDE",
34
+ "HUE",
35
+ "SATURATION",
36
+ "COLOR",
37
+ "VALUE",
38
+ ]
39
+ TRenderTarget = Literal["ALL", "EEVEE", "CYCLES"]
40
+
41
+ logger = logging.getLogger(__name__)
42
+
43
+
44
+ def add_shader(
45
+ shader_0: nt.ProcNode[nt.Shader] | None = None,
46
+ shader_1: nt.ProcNode[nt.Shader] | None = None,
47
+ ) -> nt.ProcNode[nt.Shader]:
48
+ """
49
+ Uses a AddShader Shader Node.
50
+
51
+ See: https://docs.blender.org/manual/en/4.2/render/shader_nodes/shader/add.html
52
+ """
53
+ return nt.ProcNode.from_nodetype(
54
+ node_type="ShaderNodeAddShader",
55
+ inputs={("Shader", 0): shader_0, ("Shader", 1): shader_1},
56
+ attrs={},
57
+ )
58
+
59
+
60
+ class AmbientOcclusionResult(NamedTuple):
61
+ color: nt.ProcNode[pt.Color]
62
+ ao: nt.ProcNode[float]
63
+
64
+
65
+ def ambient_occlusion(
66
+ color: nt.SocketOrVal[pt.Color] = (1, 1, 1, 1),
67
+ distance: nt.SocketOrVal[float] = 1.0,
68
+ normal: nt.SocketOrVal[pt.Vector] = None,
69
+ inside: bool = False,
70
+ only_local: bool = False,
71
+ samples: int = 16,
72
+ ) -> AmbientOcclusionResult:
73
+ """
74
+ Uses a AmbientOcclusion Shader Node.
75
+
76
+ Args:
77
+ normal: Avoid using this input. We recommend using the shader's Displacement output instead, which supports bumpmapping OR real mesh render/export.
78
+
79
+ See: https://docs.blender.org/manual/en/4.2/render/shader_nodes/input/ao.html
80
+ """
81
+ if normal is not None:
82
+ raise_shader_normal_error("ambient_occlusion", logger=logger)
83
+
84
+ res = nt.ProcNode.from_nodetype(
85
+ node_type="ShaderNodeAmbientOcclusion",
86
+ inputs={"Color": color, "Distance": distance, "Normal": normal},
87
+ attrs={"inside": inside, "only_local": only_local, "samples": samples},
88
+ )
89
+ return AmbientOcclusionResult(
90
+ color=res._output_socket("color"), ao=res._output_socket("ao")
91
+ )
92
+
93
+
94
+ class AttributeResult(NamedTuple):
95
+ color: nt.ProcNode[pt.Color]
96
+ vector: nt.ProcNode[pt.Vector]
97
+ fac: nt.ProcNode[float]
98
+ alpha: nt.ProcNode[float]
99
+
100
+
101
+ def attribute(
102
+ attribute_name: str = "",
103
+ attribute_type: Literal[
104
+ "GEOMETRY", "OBJECT", "INSTANCER", "VIEW_LAYER"
105
+ ] = "GEOMETRY",
106
+ ) -> AttributeResult:
107
+ """
108
+ Uses a Attribute Shader Node.
109
+
110
+ See: https://docs.blender.org/manual/en/4.2/render/shader_nodes/input/attribute.html
111
+ """
112
+ res = nt.ProcNode.from_nodetype(
113
+ node_type="ShaderNodeAttribute",
114
+ inputs={},
115
+ attrs={"attribute_name": attribute_name, "attribute_type": attribute_type},
116
+ )
117
+ return AttributeResult(
118
+ color=res._output_socket("color"),
119
+ vector=res._output_socket("vector"),
120
+ fac=res._output_socket("fac"),
121
+ alpha=res._output_socket("alpha"),
122
+ )
123
+
124
+
125
+ def background(
126
+ color: nt.SocketOrVal[pt.Color] = (0.8, 0.8, 0.8, 1),
127
+ strength: nt.SocketOrVal[float] = 1.0,
128
+ ) -> nt.ProcNode[nt.Shader]:
129
+ """
130
+ Uses a Background Shader Node.
131
+
132
+ See: https://docs.blender.org/manual/en/4.2/render/shader_nodes/shader/background.html
133
+ """
134
+ return nt.ProcNode.from_nodetype(
135
+ node_type="ShaderNodeBackground",
136
+ inputs={"Color": color, "Strength": strength},
137
+ attrs={},
138
+ )
139
+
140
+
141
+ def bevel(
142
+ radius: nt.SocketOrVal[float] = 0.05,
143
+ normal: nt.SocketOrVal[pt.Vector] = None,
144
+ samples: int = 4,
145
+ ) -> nt.ProcNode[pt.Vector]:
146
+ """
147
+ Uses a Bevel Shader Node.
148
+
149
+ Args:
150
+ normal: Avoid using this input. We recommend using the shader's Displacement output instead, which supports bumpmapping OR real mesh render/export.
151
+
152
+ See: https://docs.blender.org/manual/en/4.2/render/shader_nodes/input/bevel.html
153
+ """
154
+ if normal is not None:
155
+ raise_shader_normal_error("bevel", logger=logger)
156
+
157
+ return nt.ProcNode.from_nodetype(
158
+ node_type="ShaderNodeBevel",
159
+ inputs={"Radius": radius, "Normal": normal},
160
+ attrs={"samples": samples},
161
+ )
162
+
163
+
164
+ def blackbody(temperature: nt.SocketOrVal[float] = 1500.0) -> nt.ProcNode[pt.Color]:
165
+ """
166
+ Uses a Blackbody Shader Node.
167
+
168
+ See: https://docs.blender.org/manual/en/4.2/render/shader_nodes/converter/blackbody.html
169
+ """
170
+ return nt.ProcNode.from_nodetype(
171
+ node_type="ShaderNodeBlackbody",
172
+ inputs={"Temperature": temperature},
173
+ attrs={},
174
+ )
175
+
176
+
177
+ def bright_contrast(
178
+ color: nt.SocketOrVal[pt.Color] = (1, 1, 1, 1),
179
+ bright: nt.SocketOrVal[float] = 0.0,
180
+ contrast: nt.SocketOrVal[float] = 0.0,
181
+ ) -> nt.ProcNode[pt.Color]:
182
+ """
183
+ Uses a BrightContrast Shader Node.
184
+
185
+ See: https://docs.blender.org/manual/en/4.2/render/shader_nodes/color/bright_contrast.html
186
+ """
187
+ return nt.ProcNode.from_nodetype(
188
+ node_type="ShaderNodeBrightContrast",
189
+ inputs={"Color": color, "Bright": bright, "Contrast": contrast},
190
+ attrs={},
191
+ )
192
+
193
+
194
+ def anisotropic_bsdf(
195
+ color: nt.SocketOrVal[pt.Color] = (0.8, 0.8, 0.8, 1),
196
+ roughness: nt.SocketOrVal[float] = 0.5,
197
+ anisotropy: nt.SocketOrVal[float] = 0.0,
198
+ rotation: nt.SocketOrVal[float] = 0.0,
199
+ normal: nt.SocketOrVal[pt.Vector] = None,
200
+ tangent: nt.SocketOrVal[pt.Vector] = (0, 0, 0),
201
+ distribution: Literal[
202
+ "BECKMANN", "GGX", "ASHIKHMIN_SHIRLEY", "MULTI_GGX"
203
+ ] = "MULTI_GGX",
204
+ ) -> nt.ProcNode[nt.Shader]:
205
+ """
206
+ Uses a BsdfAnisotropic Shader Node.
207
+
208
+ Args:
209
+ normal: Avoid using this input. We recommend using the shader's Displacement output instead, which supports bumpmapping OR real mesh render/export.
210
+
211
+ See: https://docs.blender.org/manual/en/4.2/render/shader_nodes/shader/principled.html
212
+ """
213
+
214
+ if normal is not None:
215
+ raise_shader_normal_error("anisotropic_bsdf", logger=logger)
216
+
217
+ return nt.ProcNode.from_nodetype(
218
+ node_type="ShaderNodeBsdfAnisotropic",
219
+ inputs={
220
+ "Color": color,
221
+ "Roughness": roughness,
222
+ "Anisotropy": anisotropy,
223
+ "Rotation": rotation,
224
+ "Normal": normal,
225
+ "Tangent": tangent,
226
+ },
227
+ attrs={"distribution": distribution},
228
+ )
229
+
230
+
231
+ def diffuse_bsdf(
232
+ color: nt.SocketOrVal[pt.Color] = (0.8, 0.8, 0.8, 1),
233
+ roughness: nt.SocketOrVal[float] = 0.0,
234
+ normal: nt.SocketOrVal[pt.Vector] = None,
235
+ ) -> nt.ProcNode[nt.Shader]:
236
+ """
237
+ Uses a BsdfDiffuse Shader Node.
238
+
239
+ Args:
240
+ normal: Avoid using this input. We recommend using the shader's Displacement output instead, which supports bumpmapping OR real mesh render/export.
241
+
242
+ See: https://docs.blender.org/manual/en/4.2/render/shader_nodes/shader/diffuse.html
243
+ """
244
+
245
+ if normal is not None:
246
+ raise_shader_normal_error("diffuse_bsdf", logger=logger)
247
+
248
+ return nt.ProcNode.from_nodetype(
249
+ node_type="ShaderNodeBsdfDiffuse",
250
+ inputs={"Color": color, "Roughness": roughness, "Normal": normal},
251
+ attrs={},
252
+ )
253
+
254
+
255
+ def glass_bsdf(
256
+ color: nt.SocketOrVal[pt.Color] = (1, 1, 1, 1),
257
+ roughness: nt.SocketOrVal[float] = 0.0,
258
+ ior: nt.SocketOrVal[float] = 1.5,
259
+ normal: nt.SocketOrVal[pt.Vector] = None,
260
+ distribution: Literal["BECKMANN", "GGX", "MULTI_GGX"] = "MULTI_GGX",
261
+ ) -> nt.ProcNode[nt.Shader]:
262
+ """
263
+ Uses a BsdfGlass Shader Node.
264
+
265
+ Args:
266
+ normal: Avoid using this input. We recommend using the shader's Displacement output instead, which supports bumpmapping OR real mesh render/export.
267
+
268
+ See: https://docs.blender.org/manual/en/4.2/render/shader_nodes/shader/glass.html
269
+ """
270
+
271
+ if normal is not None:
272
+ raise_shader_normal_error("glass_bsdf", logger=logger)
273
+
274
+ return nt.ProcNode.from_nodetype(
275
+ node_type="ShaderNodeBsdfGlass",
276
+ inputs={"Color": color, "Roughness": roughness, "IOR": ior, "Normal": normal},
277
+ attrs={"distribution": distribution},
278
+ )
279
+
280
+
281
+ def hair_bsdf(
282
+ color: nt.SocketOrVal[pt.Color] = (0.8, 0.8, 0.8, 1),
283
+ offset: nt.SocketOrVal[float] = 0.0,
284
+ roughness_u: nt.SocketOrVal[float] = 0.1,
285
+ roughness_v: nt.SocketOrVal[float] = 1.0,
286
+ tangent: nt.SocketOrVal[pt.Vector] = (0, 0, 0),
287
+ component: Literal["Reflection", "Transmission"] = "Reflection",
288
+ ) -> nt.ProcNode[nt.Shader]:
289
+ """
290
+ Uses a BsdfHair Shader Node.
291
+
292
+ See: https://docs.blender.org/manual/en/4.2/render/shader_nodes/shader/hair.html
293
+ """
294
+ return nt.ProcNode.from_nodetype(
295
+ node_type="ShaderNodeBsdfHair",
296
+ inputs={
297
+ "Color": color,
298
+ "Offset": offset,
299
+ "RoughnessU": roughness_u,
300
+ "RoughnessV": roughness_v,
301
+ "Tangent": tangent,
302
+ },
303
+ attrs={"component": component},
304
+ )
305
+
306
+
307
+ def principled_hair_bsdf(
308
+ color: nt.SocketOrVal[pt.Color] = (0.017513, 0.005763, 0.002059, 1),
309
+ roughness: nt.SocketOrVal[float] = 0.3,
310
+ radial_roughness: nt.SocketOrVal[float] = 0.3,
311
+ coat: nt.SocketOrVal[float] = 0.0,
312
+ ior: nt.SocketOrVal[float] = 1.55,
313
+ offset: nt.SocketOrVal[float] = 0.034907,
314
+ random_roughness: nt.SocketOrVal[float] = 0.0,
315
+ random: nt.SocketOrVal[float] = 0.0,
316
+ model: Literal["CHIANG", "HUANG"] = "CHIANG",
317
+ parametrization: Literal["ABSORPTION", "MELANIN", "COLOR"] = "COLOR",
318
+ ) -> nt.ProcNode[nt.Shader]:
319
+ """
320
+ Uses a BsdfHairPrincipled Shader Node.
321
+
322
+ See: https://docs.blender.org/manual/en/4.2/render/shader_nodes/shader/hair_principled.html
323
+ """
324
+ return nt.ProcNode.from_nodetype(
325
+ node_type="ShaderNodeBsdfHairPrincipled",
326
+ inputs={
327
+ "Color": color,
328
+ "Roughness": roughness,
329
+ "Radial Roughness": radial_roughness,
330
+ "Coat": coat,
331
+ "IOR": ior,
332
+ "Offset": offset,
333
+ "Random Roughness": random_roughness,
334
+ "Random": random,
335
+ },
336
+ attrs={"model": model, "parametrization": parametrization},
337
+ )
338
+
339
+
340
+ TSubsurfaceMethod = Literal["BURLEY", "RANDOM_WALK", "RANDOM_WALK_SKIN"]
341
+
342
+
343
+ def principled_bsdf(
344
+ base_color: nt.SocketOrVal[pt.Color] = (0.8, 0.8, 0.8, 1),
345
+ metallic: nt.SocketOrVal[float] = 0.0,
346
+ roughness: nt.SocketOrVal[float] = 0.5,
347
+ ior: nt.SocketOrVal[float] = 1.5,
348
+ alpha: nt.SocketOrVal[float] = 1.0,
349
+ normal: nt.SocketOrVal[pt.Vector] = (0.0, 0.0, 0.0),
350
+ # subsurface scattering
351
+ subsurface_method: TSubsurfaceMethod = "RANDOM_WALK",
352
+ subsurface_weight: nt.SocketOrVal[float] = 0.0,
353
+ subsurface_radius: nt.SocketOrVal[pt.Vector] = (1, 0.2, 0.1),
354
+ subsurface_scale: nt.SocketOrVal[float] = 0.05,
355
+ subsurface_ior: nt.SocketOrVal[float] | None = None,
356
+ subsurface_anisotropy: nt.SocketOrVal[float] | None = None,
357
+ # specular
358
+ distribution: Literal["GGX", "MULTI_GGX"] = "MULTI_GGX",
359
+ specular_ior_level: nt.SocketOrVal[float] = 0.5,
360
+ specular_tint: nt.SocketOrVal[pt.Color] = (1, 1, 1, 1),
361
+ anisotropic: nt.SocketOrVal[float] = 0.0,
362
+ anisotropic_rotation: nt.SocketOrVal[float] = 0.0,
363
+ tangent: nt.SocketOrVal[pt.Vector] = (0, 0, 0),
364
+ transmission_weight: nt.SocketOrVal[float] = 0.0,
365
+ coat_weight: nt.SocketOrVal[float] = 0.0,
366
+ coat_roughness: nt.SocketOrVal[float] = 0.03,
367
+ coat_ior: nt.SocketOrVal[float] = 1.5,
368
+ coat_tint: nt.SocketOrVal[pt.Color] = (1, 1, 1, 1),
369
+ coat_normal: nt.SocketOrVal[pt.Vector] = (0.0, 0.0, 0.0),
370
+ sheen_weight: nt.SocketOrVal[float] = 0.0,
371
+ sheen_roughness: nt.SocketOrVal[float] = 0.5,
372
+ sheen_tint: nt.SocketOrVal[pt.Color] = (1, 1, 1, 1),
373
+ emission_color: nt.SocketOrVal[pt.Color] = (1, 1, 1, 1),
374
+ emission_strength: nt.SocketOrVal[float] = 0.0,
375
+ thin_film_thickness: nt.SocketOrVal[float] = 0.0,
376
+ thin_film_ior: nt.SocketOrVal[float] = 1.33,
377
+ ) -> nt.ProcNode[nt.Shader]:
378
+ """
379
+ Uses a BsdfPrincipled Shader Node.
380
+
381
+ Args:
382
+ normal: Avoid using this input. We recommend using the shader's Displacement output instead, which supports bumpmapping OR real mesh render/export.
383
+ subsurface_ior: Only supported if subsurface_method is RANDOM_WALK_SKIN.
384
+ distribution: configurs specular shading
385
+
386
+ See: https://docs.blender.org/manual/en/4.2/render/shader_nodes/shader/principled.html
387
+ """
388
+
389
+ if normal is not None:
390
+ raise_shader_normal_error("principled_bsdf", logger=logger)
391
+
392
+ inputs = {
393
+ "Base Color": base_color,
394
+ "Metallic": metallic,
395
+ "Roughness": roughness,
396
+ "IOR": ior,
397
+ "Alpha": alpha,
398
+ "Normal": normal,
399
+ "Subsurface Weight": subsurface_weight,
400
+ "Subsurface Radius": subsurface_radius,
401
+ "Subsurface Scale": subsurface_scale,
402
+ "Subsurface Anisotropy": subsurface_anisotropy,
403
+ "Specular IOR Level": specular_ior_level,
404
+ "Specular Tint": specular_tint,
405
+ "Anisotropic": anisotropic,
406
+ "Anisotropic Rotation": anisotropic_rotation,
407
+ "Tangent": tangent,
408
+ "Transmission Weight": transmission_weight,
409
+ "Coat Weight": coat_weight,
410
+ "Coat Roughness": coat_roughness,
411
+ "Coat IOR": coat_ior,
412
+ "Coat Tint": coat_tint,
413
+ "Coat Normal": coat_normal,
414
+ "Sheen Weight": sheen_weight,
415
+ "Sheen Roughness": sheen_roughness,
416
+ "Sheen Tint": sheen_tint,
417
+ "Emission Color": emission_color,
418
+ "Emission Strength": emission_strength,
419
+ "Thin Film Thickness": thin_film_thickness,
420
+ "Thin Film IOR": thin_film_ior,
421
+ }
422
+
423
+ if subsurface_ior is not None:
424
+ assert subsurface_method == "RANDOM_WALK_SKIN"
425
+ inputs["Subsurface IOR"] = subsurface_ior
426
+ if subsurface_anisotropy is not None:
427
+ assert subsurface_method in ["RANDOM_WALK", "RANDOM_WALK_SKIN"]
428
+ inputs["Subsurface Anisotropy"] = subsurface_anisotropy
429
+
430
+ return nt.ProcNode.from_nodetype(
431
+ node_type="ShaderNodeBsdfPrincipled",
432
+ inputs=inputs,
433
+ attrs={"distribution": distribution, "subsurface_method": subsurface_method},
434
+ )
435
+
436
+
437
+ def ray_portal_bsdf(
438
+ color: nt.SocketOrVal[pt.Color] = (1, 1, 1, 1),
439
+ position: nt.SocketOrVal[pt.Vector] = (0, 0, 0),
440
+ direction: nt.SocketOrVal[pt.Vector] = (0, 0, 0),
441
+ ) -> nt.ProcNode[nt.Shader]:
442
+ """
443
+ Uses a BsdfRayPortal Shader Node.
444
+
445
+ See: https://docs.blender.org/manual/en/4.2/render/shader_nodes/shader/ray_portal.html
446
+ """
447
+ return nt.ProcNode.from_nodetype(
448
+ node_type="ShaderNodeBsdfRayPortal",
449
+ inputs={"Color": color, "Position": position, "Direction": direction},
450
+ attrs={},
451
+ )
452
+
453
+
454
+ def refraction_bsdf(
455
+ color: nt.SocketOrVal[pt.Color] = (1, 1, 1, 1),
456
+ roughness: nt.SocketOrVal[float] = 0.0,
457
+ ior: nt.SocketOrVal[float] = 1.45,
458
+ normal: nt.SocketOrVal[pt.Vector] = None,
459
+ distribution: Literal["BECKMANN", "GGX"] = "BECKMANN",
460
+ ) -> nt.ProcNode[nt.Shader]:
461
+ """
462
+ Uses a BsdfRefraction Shader Node.
463
+
464
+ Args:
465
+ normal: Avoid using this input. We recommend using the shader's Displacement output instead, which supports bumpmapping OR real mesh render/export.
466
+
467
+ See: https://docs.blender.org/manual/en/4.2/render/shader_nodes/shader/refraction.html
468
+ """
469
+
470
+ if normal is not None:
471
+ raise_shader_normal_error("refraction_bsdf", logger=logger)
472
+
473
+ return nt.ProcNode.from_nodetype(
474
+ node_type="ShaderNodeBsdfRefraction",
475
+ inputs={"Color": color, "Roughness": roughness, "IOR": ior, "Normal": normal},
476
+ attrs={"distribution": distribution},
477
+ )
478
+
479
+
480
+ def sheen_bsdf(
481
+ color: nt.SocketOrVal[pt.Color] = (0.8, 0.8, 0.8, 1),
482
+ roughness: nt.SocketOrVal[float] = 0.5,
483
+ normal: nt.SocketOrVal[pt.Vector] = None,
484
+ distribution: Literal["ASHIKHMIN", "MICROFIBER"] = "MICROFIBER",
485
+ ) -> nt.ProcNode[nt.Shader]:
486
+ """
487
+ Uses a BsdfSheen Shader Node.
488
+
489
+ Args:
490
+ normal: Avoid using this input. We recommend using the shader's Displacement output instead, which supports bumpmapping OR real mesh render/export.
491
+
492
+ See: https://docs.blender.org/manual/en/4.2/render/shader_nodes/shader/sheen.html
493
+ """
494
+
495
+ if normal is not None:
496
+ raise_shader_normal_error("sheen_bsdf", logger=logger)
497
+
498
+ return nt.ProcNode.from_nodetype(
499
+ node_type="ShaderNodeBsdfSheen",
500
+ inputs={"Color": color, "Roughness": roughness, "Normal": normal},
501
+ attrs={"distribution": distribution},
502
+ )
503
+
504
+
505
+ def toon_bsdf(
506
+ color: nt.SocketOrVal[pt.Color] = (0.8, 0.8, 0.8, 1),
507
+ size: nt.SocketOrVal[float] = 0.5,
508
+ smooth: nt.SocketOrVal[float] = 0.0,
509
+ normal: nt.SocketOrVal[pt.Vector] = None,
510
+ component: Literal["DIFFUSE", "GLOSSY"] = "DIFFUSE",
511
+ ) -> nt.ProcNode[nt.Shader]:
512
+ """
513
+ Uses a BsdfToon Shader Node.
514
+
515
+ Args:
516
+ normal: Avoid using this input. We recommend using the shader's Displacement output instead, which supports bumpmapping OR real mesh render/export.
517
+
518
+ See: https://docs.blender.org/manual/en/4.2/render/shader_nodes/shader/toon.html
519
+ """
520
+
521
+ if normal is not None:
522
+ raise_shader_normal_error("toon_bsdf", logger=logger)
523
+
524
+ return nt.ProcNode.from_nodetype(
525
+ node_type="ShaderNodeBsdfToon",
526
+ inputs={"Color": color, "Size": size, "Smooth": smooth, "Normal": normal},
527
+ attrs={"component": component},
528
+ )
529
+
530
+
531
+ def translucent_bsdf(
532
+ color: nt.SocketOrVal[pt.Color] = (0.8, 0.8, 0.8, 1),
533
+ normal: nt.SocketOrVal[pt.Vector] = None,
534
+ ) -> nt.ProcNode[nt.Shader]:
535
+ """
536
+ Uses a BsdfTranslucent Shader Node.
537
+
538
+ Args:
539
+ normal: Avoid using this input. We recommend using the shader's Displacement output instead, which supports bumpmapping OR real mesh render/export.
540
+
541
+ See: https://docs.blender.org/manual/en/4.2/render/shader_nodes/shader/translucent.html
542
+ """
543
+
544
+ if normal is not None:
545
+ raise_shader_normal_error("translucent_bsdf", logger=logger)
546
+
547
+ return nt.ProcNode.from_nodetype(
548
+ node_type="ShaderNodeBsdfTranslucent",
549
+ inputs={"Color": color, "Normal": normal},
550
+ attrs={},
551
+ )
552
+
553
+
554
+ def transparent_bsdf(
555
+ color: nt.SocketOrVal[pt.Color] = (1, 1, 1, 1),
556
+ ) -> nt.ProcNode[nt.Shader]:
557
+ """
558
+ Uses a BsdfTransparent Shader Node.
559
+
560
+ See: https://docs.blender.org/manual/en/4.2/render/shader_nodes/shader/transparent.html
561
+ """
562
+ return nt.ProcNode.from_nodetype(
563
+ node_type="ShaderNodeBsdfTransparent",
564
+ inputs={"Color": color},
565
+ attrs={},
566
+ )
567
+
568
+
569
+ def bump(
570
+ strength: nt.SocketOrVal[float] = 1.0,
571
+ distance: nt.SocketOrVal[float] = 1.0,
572
+ height: nt.SocketOrVal[float] = 1.0,
573
+ normal: nt.SocketOrVal[pt.Vector] = None,
574
+ invert: bool = False,
575
+ ) -> nt.ProcNode[pt.Vector]:
576
+ """
577
+ Uses a Bump Shader Node.
578
+
579
+ See: https://docs.blender.org/manual/en/4.2/render/shader_nodes/vector/bump.html
580
+ """
581
+
582
+ msg = (
583
+ "Using the Bump shader node! We recommend using the shader's Displacement output instead"
584
+ ", as it has more capabilities e.g. mesh-based displacement "
585
+ "To suppress this warning, set pf.context.globals.warn_mode_avoid_normal_bump = 'ignore'"
586
+ )
587
+ raise_error_or_warn(msg, pf.context.globals.warn_mode_avoid_normal_bump, logger)
588
+
589
+ return nt.ProcNode.from_nodetype(
590
+ node_type="ShaderNodeBump",
591
+ inputs={
592
+ "Strength": strength,
593
+ "Distance": distance,
594
+ "Height": height,
595
+ "Normal": normal,
596
+ },
597
+ attrs={"invert": invert},
598
+ )
599
+
600
+
601
+ class CameraDataResult(NamedTuple):
602
+ view_vector: nt.ProcNode[pt.Vector]
603
+ view_z_depth: nt.ProcNode[float]
604
+ view_distance: nt.ProcNode[float]
605
+
606
+
607
+ def camera_data() -> CameraDataResult:
608
+ """
609
+ Uses a CameraData Shader Node.
610
+
611
+ See: https://docs.blender.org/manual/en/4.2/render/shader_nodes/input/camera_data.html
612
+ """
613
+ res = nt.ProcNode.from_nodetype(
614
+ node_type="ShaderNodeCameraData",
615
+ inputs={},
616
+ attrs={},
617
+ )
618
+
619
+ return CameraDataResult(
620
+ view_vector=res._output_socket("view_vector"),
621
+ view_z_depth=res._output_socket("view_z_depth"),
622
+ view_distance=res._output_socket("view_distance"),
623
+ )
624
+
625
+
626
+ def displacement(
627
+ height: nt.SocketOrVal[float] = 0.0,
628
+ midlevel: nt.SocketOrVal[float] = 0.5,
629
+ scale: nt.SocketOrVal[float] = 1.0,
630
+ normal: nt.SocketOrVal[pt.Vector] = None,
631
+ space: Literal["OBJECT", "WORLD"] = "OBJECT",
632
+ ) -> nt.ProcNode[pt.Vector]:
633
+ """
634
+ Uses a Displacement Shader Node.
635
+
636
+ See: https://docs.blender.org/manual/en/4.2/render/shader_nodes/vector/displacement.html
637
+ """
638
+ return nt.ProcNode.from_nodetype(
639
+ node_type="ShaderNodeDisplacement",
640
+ inputs={
641
+ "Height": height,
642
+ "Midlevel": midlevel,
643
+ "Scale": scale,
644
+ "Normal": normal,
645
+ },
646
+ attrs={"space": space},
647
+ )
648
+
649
+
650
+ def eevee_specular(
651
+ base_color: nt.SocketOrVal[pt.Color] = (0.8, 0.8, 0.8, 1),
652
+ specular: nt.SocketOrVal[pt.Color] = (0.03, 0.03, 0.03, 1),
653
+ roughness: nt.SocketOrVal[float] = 0.2,
654
+ emissive_color: nt.SocketOrVal[pt.Color] = (0, 0, 0, 1),
655
+ transparency: nt.SocketOrVal[float] = 0.0,
656
+ normal: nt.SocketOrVal[pt.Vector] = None,
657
+ clear_coat: nt.SocketOrVal[float] = 0.0,
658
+ clear_coat_roughness: nt.SocketOrVal[float] = 0.0,
659
+ clear_coat_normal: nt.SocketOrVal[pt.Vector] = None,
660
+ ) -> nt.ProcNode[nt.Shader]:
661
+ """
662
+ Uses a EeveeSpecular Shader Node.
663
+
664
+ See: https://docs.blender.org/manual/en/4.2/render/shader_nodes/shader/specular_bsdf.html
665
+ """
666
+ return nt.ProcNode.from_nodetype(
667
+ node_type="ShaderNodeEeveeSpecular",
668
+ inputs={
669
+ "Base Color": base_color,
670
+ "Specular": specular,
671
+ "Roughness": roughness,
672
+ "Emissive Color": emissive_color,
673
+ "Transparency": transparency,
674
+ "Normal": normal,
675
+ "Clear Coat": clear_coat,
676
+ "Clear Coat Roughness": clear_coat_roughness,
677
+ "Clear Coat Normal": clear_coat_normal,
678
+ },
679
+ attrs={},
680
+ )
681
+
682
+
683
+ def emission(
684
+ color: nt.SocketOrVal[pt.Color] = (1, 1, 1, 1),
685
+ strength: nt.SocketOrVal[float] = 1.0,
686
+ ) -> nt.ProcNode[nt.Shader]:
687
+ """
688
+ Uses a Emission Shader Node.
689
+
690
+ See: https://docs.blender.org/manual/en/4.2/render/shader_nodes/shader/emission.html
691
+ """
692
+ return nt.ProcNode.from_nodetype(
693
+ node_type="ShaderNodeEmission",
694
+ inputs={"Color": color, "Strength": strength},
695
+ attrs={},
696
+ )
697
+
698
+
699
+ def fresnel(
700
+ ior: nt.SocketOrVal[float] = 1.5, normal: nt.SocketOrVal[pt.Vector] = None
701
+ ) -> nt.ProcNode[float]:
702
+ """
703
+ Uses a Fresnel Shader Node.
704
+
705
+ Args:
706
+ normal: Avoid using this input. We recommend using the shader's Displacement output instead, which supports bumpmapping OR real mesh render/export.
707
+
708
+ See: https://docs.blender.org/manual/en/4.2/render/shader_nodes/input/fresnel.html
709
+ """
710
+ if normal is not None:
711
+ raise_shader_normal_error("fresnel", logger=logger)
712
+
713
+ return nt.ProcNode.from_nodetype(
714
+ node_type="ShaderNodeFresnel",
715
+ inputs={"IOR": ior, "Normal": normal},
716
+ attrs={},
717
+ )
718
+
719
+
720
+ def gamma(
721
+ color: nt.SocketOrVal[pt.Color] = (1, 1, 1, 1), gamma: nt.SocketOrVal[float] = 1.0
722
+ ) -> nt.ProcNode[pt.Color]:
723
+ """
724
+ Uses a Gamma Shader Node.
725
+
726
+ See: https://docs.blender.org/manual/en/4.2/render/shader_nodes/color/gamma.html
727
+ """
728
+ return nt.ProcNode.from_nodetype(
729
+ node_type="ShaderNodeGamma",
730
+ inputs={"Color": color, "Gamma": gamma},
731
+ attrs={},
732
+ )
733
+
734
+
735
+ class HairInfoResult(NamedTuple):
736
+ is_strand: nt.ProcNode[float]
737
+ intercept: nt.ProcNode[float]
738
+ length: nt.ProcNode[float]
739
+ thickness: nt.ProcNode[float]
740
+ tangent_normal: nt.ProcNode[pt.Vector]
741
+ random: nt.ProcNode[float]
742
+
743
+
744
+ def hair_info() -> HairInfoResult:
745
+ """
746
+ Uses a HairInfo Shader Node.
747
+
748
+ See: https://docs.blender.org/manual/en/4.2/render/shader_nodes/input/hair_info.html
749
+ """
750
+ res = nt.ProcNode.from_nodetype(
751
+ node_type="ShaderNodeHairInfo",
752
+ inputs={},
753
+ attrs={},
754
+ )
755
+
756
+ return HairInfoResult(
757
+ is_strand=res._output_socket("is_strand"),
758
+ intercept=res._output_socket("intercept"),
759
+ length=res._output_socket("length"),
760
+ thickness=res._output_socket("thickness"),
761
+ tangent_normal=res._output_socket("tangent_normal"),
762
+ random=res._output_socket("random"),
763
+ )
764
+
765
+
766
+ def holdout() -> nt.ProcNode[nt.Shader]:
767
+ """
768
+ Uses a Holdout Shader Node.
769
+
770
+ See: https://docs.blender.org/manual/en/4.2/render/shader_nodes/shader/holdout.html
771
+ """
772
+ return nt.ProcNode.from_nodetype(node_type="ShaderNodeHoldout", inputs={}, attrs={})
773
+
774
+
775
+ def hue_saturation(
776
+ hue: nt.SocketOrVal[float] = 0.5,
777
+ saturation: nt.SocketOrVal[float] = 1.0,
778
+ value: nt.SocketOrVal[float] = 1.0,
779
+ fac: nt.SocketOrVal[float] = 1.0,
780
+ color: nt.SocketOrVal[pt.Color] = (0.8, 0.8, 0.8, 1),
781
+ ) -> nt.ProcNode[pt.Color]:
782
+ """
783
+ Uses a HueSaturation Shader Node.
784
+
785
+ See: https://docs.blender.org/manual/en/4.2/render/shader_nodes/color/hue_saturation.html
786
+ """
787
+ return nt.ProcNode.from_nodetype(
788
+ node_type="ShaderNodeHueSaturation",
789
+ inputs={
790
+ "Hue": hue,
791
+ "Saturation": saturation,
792
+ "Value": value,
793
+ "Fac": fac,
794
+ "Color": color,
795
+ },
796
+ attrs={},
797
+ )
798
+
799
+
800
+ def invert(
801
+ fac: nt.SocketOrVal[float] = 1.0, color: nt.SocketOrVal[pt.Color] = (0, 0, 0, 1)
802
+ ) -> nt.ProcNode[pt.Color]:
803
+ """
804
+ Uses a Invert Shader Node.
805
+
806
+ See: https://docs.blender.org/manual/en/4.2/render/shader_nodes/color/invert_color.html
807
+ """
808
+ return nt.ProcNode.from_nodetype(
809
+ node_type="ShaderNodeInvert",
810
+ inputs={"Fac": fac, "Color": color},
811
+ attrs={},
812
+ )
813
+
814
+
815
+ class LayerWeightResult(NamedTuple):
816
+ fresnel: nt.ProcNode[float]
817
+ facing: nt.ProcNode[float]
818
+
819
+
820
+ def layer_weight(
821
+ blend: nt.SocketOrVal[float] = 0.5,
822
+ normal: nt.SocketOrVal[pt.Vector] = (0.0, 0.0, 0.0),
823
+ ) -> LayerWeightResult:
824
+ """
825
+ Uses a LayerWeight Shader Node.
826
+
827
+ Args:
828
+ normal: Avoid using this input. We recommend using the shader's Displacement output instead, which supports bumpmapping OR real mesh render/export.
829
+
830
+ See: https://docs.blender.org/manual/en/4.2/render/shader_nodes/input/layer_weight.html
831
+ """
832
+ if normal != (0.0, 0.0, 0.0):
833
+ raise_shader_normal_error("layer_weight", logger=logger)
834
+
835
+ res = nt.ProcNode.from_nodetype(
836
+ node_type="ShaderNodeLayerWeight",
837
+ inputs={"Blend": blend, "Normal": normal},
838
+ attrs={},
839
+ )
840
+ return LayerWeightResult(
841
+ fresnel=res._output_socket("fresnel"),
842
+ facing=res._output_socket("facing"),
843
+ )
844
+
845
+
846
+ class LightFalloffResult(NamedTuple):
847
+ quadratic: nt.ProcNode[float]
848
+ linear: nt.ProcNode[float]
849
+ constant: nt.ProcNode[float]
850
+
851
+
852
+ def light_falloff(
853
+ strength: nt.SocketOrVal[float] = 100.0, smooth: nt.SocketOrVal[float] = 0.0
854
+ ) -> LightFalloffResult:
855
+ """
856
+ Uses a LightFalloff Shader Node.
857
+
858
+ See: https://docs.blender.org/manual/en/4.2/render/shader_nodes/color/light_falloff.html
859
+ """
860
+ res = nt.ProcNode.from_nodetype(
861
+ node_type="ShaderNodeLightFalloff",
862
+ inputs={"Strength": strength, "Smooth": smooth},
863
+ attrs={},
864
+ )
865
+ return LightFalloffResult(
866
+ quadratic=res._output_socket("quadratic"),
867
+ linear=res._output_socket("linear"),
868
+ constant=res._output_socket("constant"),
869
+ )
870
+
871
+
872
+ class LightPathResult(NamedTuple):
873
+ is_camera_ray: nt.ProcNode[float]
874
+ is_shadow_ray: nt.ProcNode[float]
875
+ is_diffuse_ray: nt.ProcNode[float]
876
+ is_glossy_ray: nt.ProcNode[float]
877
+ is_singular_ray: nt.ProcNode[float]
878
+ is_reflection_ray: nt.ProcNode[float]
879
+ is_transmission_ray: nt.ProcNode[float]
880
+ ray_length: nt.ProcNode[float]
881
+ ray_depth: nt.ProcNode[float]
882
+ diffuse_depth: nt.ProcNode[float]
883
+ glossy_depth: nt.ProcNode[float]
884
+ transparent_depth: nt.ProcNode[float]
885
+ transmission_depth: nt.ProcNode[float]
886
+
887
+
888
+ def light_path() -> LightPathResult:
889
+ """
890
+ Uses a LightPath Shader Node.
891
+
892
+ See: https://docs.blender.org/manual/en/4.2/render/shader_nodes/input/light_path.html
893
+ """
894
+ res = nt.ProcNode.from_nodetype(
895
+ node_type="ShaderNodeLightPath",
896
+ inputs={},
897
+ attrs={},
898
+ )
899
+
900
+ return LightPathResult(
901
+ is_camera_ray=res._output_socket("is_camera_ray"),
902
+ is_shadow_ray=res._output_socket("is_shadow_ray"),
903
+ is_diffuse_ray=res._output_socket("is_diffuse_ray"),
904
+ is_glossy_ray=res._output_socket("is_glossy_ray"),
905
+ is_singular_ray=res._output_socket("is_singular_ray"),
906
+ is_reflection_ray=res._output_socket("is_reflection_ray"),
907
+ is_transmission_ray=res._output_socket("is_transmission_ray"),
908
+ ray_length=res._output_socket("ray_length"),
909
+ ray_depth=res._output_socket("ray_depth"),
910
+ diffuse_depth=res._output_socket("diffuse_depth"),
911
+ glossy_depth=res._output_socket("glossy_depth"),
912
+ transparent_depth=res._output_socket("transparent_depth"),
913
+ transmission_depth=res._output_socket("transmission_depth"),
914
+ )
915
+
916
+
917
+ def mapping(
918
+ vector: nt.SocketOrVal[pt.Vector] = (0, 0, 0),
919
+ location: nt.SocketOrVal[pt.Vector] = (0, 0, 0),
920
+ rotation: nt.SocketOrVal[pt.Vector] = (0, 0, 0),
921
+ scale: nt.SocketOrVal[pt.Vector] = (1, 1, 1),
922
+ vector_type: Literal["POINT", "TEXTURE", "VECTOR", "NORMAL"] = "POINT",
923
+ ) -> nt.ProcNode[pt.Vector]:
924
+ """
925
+ Uses a Mapping Shader Node.
926
+
927
+ See: https://docs.blender.org/manual/en/4.2/render/shader_nodes/vector/mapping.html
928
+ """
929
+ return nt.ProcNode.from_nodetype(
930
+ node_type="ShaderNodeMapping",
931
+ inputs={
932
+ "Vector": vector,
933
+ "Location": location,
934
+ "Rotation": rotation,
935
+ "Scale": scale,
936
+ },
937
+ attrs={"vector_type": vector_type},
938
+ )
939
+
940
+
941
+ def mix_shader(
942
+ factor: nt.SocketOrVal[float] = 0.5,
943
+ a: nt.ProcNode[nt.Shader] | None = None,
944
+ b: nt.ProcNode[nt.Shader] | None = None,
945
+ ) -> nt.ProcNode[nt.Shader]:
946
+ """
947
+ Uses a MixShader Shader Node.
948
+
949
+ See: https://docs.blender.org/manual/en/4.2/render/shader_nodes/shader/mix.html
950
+ """
951
+ return nt.ProcNode.from_nodetype(
952
+ node_type="ShaderNodeMixShader",
953
+ inputs={"Fac": factor, ("Shader", 0): a, ("Shader", 1): b},
954
+ attrs={},
955
+ )
956
+
957
+
958
+ class NormalResult(NamedTuple):
959
+ normal: nt.ProcNode[pt.Vector]
960
+ dot: nt.ProcNode[float]
961
+
962
+
963
+ def normal(normal: nt.SocketOrVal[pt.Vector] = (0, 0, 1)) -> NormalResult:
964
+ """
965
+ Uses a Normal Shader Node.
966
+
967
+ See: https://docs.blender.org/manual/en/4.2/render/shader_nodes/vector/normal.html
968
+ """
969
+ res = nt.ProcNode.from_nodetype(
970
+ node_type="ShaderNodeNormal",
971
+ inputs={"Normal": normal},
972
+ attrs={},
973
+ )
974
+ return NormalResult(
975
+ normal=res._output_socket("normal"),
976
+ dot=res._output_socket("dot"),
977
+ )
978
+
979
+
980
+ def normal_map(
981
+ strength: nt.SocketOrVal[float] = 1.0,
982
+ color: nt.SocketOrVal[pt.Color] = (0.5, 0.5, 1, 1),
983
+ space: Literal[
984
+ "TANGENT", "OBJECT", "WORLD", "BLENDER_OBJECT", "BLENDER_WORLD"
985
+ ] = "TANGENT",
986
+ uv_map: str = "",
987
+ ) -> nt.ProcNode[pt.Vector]:
988
+ """
989
+ Uses a NormalMap Shader Node.
990
+
991
+ This node is discouraged. We recommend using the shader's Displacement output instead, which supports bumpmapping OR real mesh render/export.
992
+
993
+ See: https://docs.blender.org/manual/en/4.2/render/shader_nodes/vector/normal_map.html
994
+ """
995
+ raise_shader_normal_error("normal_map", logger=logger)
996
+
997
+ return nt.ProcNode.from_nodetype(
998
+ node_type="ShaderNodeNormalMap",
999
+ inputs={"Strength": strength, "Color": color},
1000
+ attrs={"space": space, "uv_map": uv_map},
1001
+ )
1002
+
1003
+
1004
+ class ObjectInfoResult(NamedTuple):
1005
+ location: nt.ProcNode[pt.Vector]
1006
+ color: nt.ProcNode[pt.Color]
1007
+ alpha: nt.ProcNode[float]
1008
+ object_index: nt.ProcNode[int]
1009
+ material_index: nt.ProcNode[int]
1010
+ random: nt.ProcNode[float]
1011
+
1012
+
1013
+ def object_info() -> ObjectInfoResult:
1014
+ """
1015
+ Uses a ObjectInfo Shader Node.
1016
+
1017
+ See: https://docs.blender.org/manual/en/4.2/render/shader_nodes/input/object_info.html
1018
+ """
1019
+ res = nt.ProcNode.from_nodetype(
1020
+ node_type="ShaderNodeObjectInfo",
1021
+ inputs={},
1022
+ attrs={},
1023
+ )
1024
+ return ObjectInfoResult(
1025
+ location=res._output_socket("location"),
1026
+ color=res._output_socket("color"),
1027
+ alpha=res._output_socket("alpha"),
1028
+ object_index=res._output_socket("object_index"),
1029
+ material_index=res._output_socket("material_index"),
1030
+ random=res._output_socket("random"),
1031
+ )
1032
+
1033
+
1034
+ # NOTE: procfunc expects python code to `return LightResult()` instead
1035
+ '''
1036
+
1037
+ def output_aov(
1038
+ color: t.SocketOrVal[pt.Color] = (0, 0, 0, 1),
1039
+ value: t.SocketOrVal[float] = 0.0,
1040
+ aov_name: str = "",
1041
+ ) -> t.ProcNode:
1042
+ """
1043
+ Uses a OutputAOV Shader Node.
1044
+
1045
+ See: https://docs.blender.org/manual/en/4.2/render/shader_nodes/output/aov.html
1046
+ """
1047
+
1048
+ raise_io_error("output_aov", logger=logger)
1049
+
1050
+ return t.ProcNode.from_nodetype(
1051
+ node_type="ShaderNodeOutputAOV",
1052
+ inputs={"Color": color, "Value": value},
1053
+ attrs={"aov_name": aov_name},
1054
+ )
1055
+
1056
+ def output_light(
1057
+ surface: t.ProcNode[t.Shader] = None,
1058
+ is_active_output: bool = True,
1059
+ target: TRenderTarget = "ALL",
1060
+ ) -> t.ProcNode:
1061
+ """
1062
+ Uses a OutputLight Shader Node.
1063
+
1064
+ See: https://docs.blender.org/manual/en/4.2/render/shader_nodes/output/light.html
1065
+ """
1066
+
1067
+ raise_io_error("output_light", logger=logger)
1068
+
1069
+ return t.ProcNode.from_nodetype(
1070
+ node_type="ShaderNodeOutputLight",
1071
+ inputs={"Surface": surface},
1072
+ attrs={"is_active_output": is_active_output, "target": target},
1073
+ )
1074
+
1075
+ def output_line_style(
1076
+ color: t.SocketOrVal[pt.Color] = (1, 0, 1, 1),
1077
+ color_fac: t.SocketOrVal[float] = 1.0,
1078
+ alpha: t.SocketOrVal[float] = 1.0,
1079
+ alpha_fac: t.SocketOrVal[float] = 1.0,
1080
+ blend_type: TBlendType = "MIX",
1081
+ is_active_output: bool = True,
1082
+ target: TRenderTarget = "ALL",
1083
+ use_alpha: bool = False,
1084
+ ) -> t.ProcNode:
1085
+ """
1086
+ Uses a OutputLineStyle Shader Node.
1087
+
1088
+ See: https://docs.blender.org/manual/en/4.2/render/shader_nodes/output/aov.html
1089
+ """
1090
+
1091
+ raise_io_error("output_line_style", logger=logger)
1092
+
1093
+ return t.ProcNode.from_nodetype(
1094
+ node_type="ShaderNodeOutputLineStyle",
1095
+ inputs={
1096
+ "Color": color,
1097
+ "Color Fac": color_fac,
1098
+ "Alpha": alpha,
1099
+ "Alpha Fac": alpha_fac,
1100
+ },
1101
+ attrs={
1102
+ "blend_type": blend_type,
1103
+ "is_active_output": is_active_output,
1104
+ "target": target,
1105
+ "use_alpha": use_alpha,
1106
+ },
1107
+ )
1108
+
1109
+ def output_material(
1110
+ surface: t.ProcNode[t.Shader] = None,
1111
+ volume: t.ProcNode[t.Shader] = None,
1112
+ displacement: t.SocketOrVal[pt.Vector] = (0, 0, 0),
1113
+ thickness: t.SocketOrVal[float] = 0.0,
1114
+ is_active_output: bool = True,
1115
+ target: TRenderTarget = "ALL",
1116
+ ) -> t.ProcNode:
1117
+ """
1118
+ Uses a OutputMaterial Shader Node.
1119
+
1120
+ See: https://docs.blender.org/manual/en/4.2/render/shader_nodes/output/material.html
1121
+ """
1122
+
1123
+ return t.ProcNode.from_nodetype(
1124
+ node_type="ShaderNodeOutputMaterial",
1125
+ inputs={
1126
+ "Surface": surface,
1127
+ "Volume": volume,
1128
+ "Displacement": displacement,
1129
+ "Thickness": thickness,
1130
+ },
1131
+ attrs={"is_active_output": is_active_output, "target": target},
1132
+ )
1133
+ '''
1134
+
1135
+ '''
1136
+ def output_world(
1137
+ surface: t.ProcNode[t.Shader] = None,
1138
+ volume: t.ProcNode[t.Shader] = None,
1139
+ is_active_output: bool = True,
1140
+ target: TRenderTarget = "ALL",
1141
+ ) -> t.ProcNode:
1142
+ """
1143
+ Uses a OutputWorld Shader Node.
1144
+
1145
+ See: https://docs.blender.org/manual/en/4.2/render/shader_nodes/output/world.html
1146
+ """
1147
+ return t.ProcNode.from_nodetype(
1148
+ node_type="ShaderNodeOutputWorld",
1149
+ inputs={"Surface": surface, "Volume": volume},
1150
+ attrs={"is_active_output": is_active_output, "target": target},
1151
+ )
1152
+ '''
1153
+
1154
+
1155
+ class ParticleInfoResult(NamedTuple):
1156
+ index: nt.ProcNode[int]
1157
+ random: nt.ProcNode[float]
1158
+ age: nt.ProcNode[float]
1159
+ lifetime: nt.ProcNode[float]
1160
+ location: nt.ProcNode[pt.Vector]
1161
+ size: nt.ProcNode[float]
1162
+ velocity: nt.ProcNode[pt.Vector]
1163
+ angular_velocity: nt.ProcNode[pt.Vector]
1164
+
1165
+
1166
+ def particle_info() -> ParticleInfoResult:
1167
+ """
1168
+ Uses a ParticleInfo Shader Node.
1169
+
1170
+ See: https://docs.blender.org/manual/en/4.2/render/shader_nodes/input/particle_info.html
1171
+ """
1172
+ res = nt.ProcNode.from_nodetype(
1173
+ node_type="ShaderNodeParticleInfo",
1174
+ inputs={},
1175
+ attrs={},
1176
+ )
1177
+ return ParticleInfoResult(
1178
+ index=res._output_socket("index"),
1179
+ random=res._output_socket("random"),
1180
+ age=res._output_socket("age"),
1181
+ lifetime=res._output_socket("lifetime"),
1182
+ location=res._output_socket("location"),
1183
+ size=res._output_socket("size"),
1184
+ velocity=res._output_socket("velocity"),
1185
+ angular_velocity=res._output_socket("angular_velocity"),
1186
+ )
1187
+
1188
+
1189
+ class PointInfoResult(NamedTuple):
1190
+ position: nt.ProcNode[pt.Vector]
1191
+ radius: nt.ProcNode[float]
1192
+ random: nt.ProcNode[float]
1193
+
1194
+
1195
+ def point_info() -> PointInfoResult:
1196
+ """
1197
+ Uses a PointInfo Shader Node.
1198
+
1199
+ See: https://docs.blender.org/manual/en/4.2/render/shader_nodes/input/point_info.html
1200
+ """
1201
+ res = nt.ProcNode.from_nodetype(
1202
+ node_type="ShaderNodePointInfo",
1203
+ inputs={},
1204
+ attrs={},
1205
+ )
1206
+ return PointInfoResult(
1207
+ position=res._output_socket("position"),
1208
+ radius=res._output_socket("radius"),
1209
+ random=res._output_socket("random"),
1210
+ )
1211
+
1212
+
1213
+ def rgb() -> nt.ProcNode[pt.Color]:
1214
+ """
1215
+ Uses a RGB Shader Node.
1216
+
1217
+ See: https://docs.blender.org/manual/en/4.2/render/shader_nodes/input/rgb.html
1218
+ """
1219
+ return nt.ProcNode.from_nodetype(node_type="ShaderNodeRGB", inputs={}, attrs={})
1220
+
1221
+
1222
+ def rgb_to_bw(
1223
+ color: nt.SocketOrVal[pt.Color] = (0.5, 0.5, 0.5, 1),
1224
+ ) -> nt.ProcNode[float]:
1225
+ """
1226
+ Uses a RGBToBW Shader Node.
1227
+
1228
+ See: https://docs.blender.org/manual/en/4.2/render/shader_nodes/converter/rgb_to_bw.html
1229
+ """
1230
+ return nt.ProcNode.from_nodetype(
1231
+ node_type="ShaderNodeRGBToBW",
1232
+ inputs={"Color": color},
1233
+ attrs={},
1234
+ )
1235
+
1236
+
1237
+ def script(
1238
+ bytecode: str = "",
1239
+ bytecode_hash: str = "",
1240
+ filepath: str = "",
1241
+ mode: Literal["INTERNAL", "EXTERNAL"] = "INTERNAL",
1242
+ script: Any = None,
1243
+ use_auto_update: bool = False,
1244
+ ) -> nt.ProcNode[nt.Shader]:
1245
+ """
1246
+ Uses a Script Shader Node.
1247
+
1248
+ See: https://docs.blender.org/manual/en/4.2/render/shader_nodes/osl.html
1249
+ """
1250
+ return nt.ProcNode.from_nodetype(
1251
+ node_type="ShaderNodeScript",
1252
+ inputs={},
1253
+ attrs={
1254
+ "bytecode": bytecode,
1255
+ "bytecode_hash": bytecode_hash,
1256
+ "filepath": filepath,
1257
+ "mode": mode,
1258
+ "script": script,
1259
+ "use_auto_update": use_auto_update,
1260
+ },
1261
+ )
1262
+
1263
+
1264
+ def shader_to_rgb(
1265
+ shader: nt.ProcNode[nt.Shader] | None = None,
1266
+ ) -> nt.ProcNode[pt.Color]:
1267
+ """
1268
+ Uses a ShaderToRGB Shader Node.
1269
+
1270
+ See: https://docs.blender.org/manual/en/4.2/render/shader_nodes/converter/shader_to_rgb.html
1271
+ """
1272
+ return nt.ProcNode.from_nodetype(
1273
+ node_type="ShaderNodeShaderToRGB",
1274
+ inputs={"Shader": shader},
1275
+ attrs={},
1276
+ )
1277
+
1278
+
1279
+ def squeeze(
1280
+ value: nt.SocketOrVal[float] = 0.0,
1281
+ width: nt.SocketOrVal[float] = 1.0,
1282
+ center: nt.SocketOrVal[float] = 0.0,
1283
+ ) -> nt.ProcNode[float]:
1284
+ """
1285
+ Uses a Squeeze Shader Node.
1286
+
1287
+ See: https://docs.blender.org/manual/en/4.2/render/shader_nodes/converter/map_range.html
1288
+ """
1289
+ return nt.ProcNode.from_nodetype(
1290
+ node_type="ShaderNodeSqueeze",
1291
+ inputs={"Value": value, "Width": width, "Center": center},
1292
+ attrs={},
1293
+ )
1294
+
1295
+
1296
+ def subsurface_scattering(
1297
+ color: nt.SocketOrVal[pt.Color] = (0.8, 0.8, 0.8, 1),
1298
+ scale: nt.SocketOrVal[float] = 0.05,
1299
+ radius: nt.SocketOrVal[pt.Vector] = (1, 0.2, 0.1),
1300
+ ior: nt.SocketOrVal[float] = 1.4,
1301
+ roughness: nt.SocketOrVal[float] = 1.0,
1302
+ anisotropy: nt.SocketOrVal[float] = 0.0,
1303
+ normal: nt.SocketOrVal[pt.Vector] = (0.0, 0.0, 0.0),
1304
+ falloff: Literal["BURLEY", "RANDOM_WALK", "RANDOM_WALK_SKIN"] = "RANDOM_WALK",
1305
+ ) -> nt.ProcNode[nt.Shader]:
1306
+ """
1307
+ Uses a SubsurfaceScattering Shader Node.
1308
+
1309
+ Args:
1310
+ normal: Avoid using this input. We recommend using the shader's Displacement output instead, which supports bumpmapping OR real mesh render/export.
1311
+
1312
+ See: https://docs.blender.org/manual/en/4.2/render/shader_nodes/shader/volume_scatter.html
1313
+ """
1314
+
1315
+ if normal != (0.0, 0.0, 0.0):
1316
+ raise_shader_normal_error("subsurface_scattering", logger=logger)
1317
+
1318
+ return nt.ProcNode.from_nodetype(
1319
+ node_type="ShaderNodeSubsurfaceScattering",
1320
+ inputs={
1321
+ "Color": color,
1322
+ "Scale": scale,
1323
+ "Radius": radius,
1324
+ "IOR": ior,
1325
+ "Roughness": roughness,
1326
+ "Anisotropy": anisotropy,
1327
+ "Normal": normal,
1328
+ },
1329
+ attrs={"falloff": falloff},
1330
+ )
1331
+
1332
+
1333
+ def tangent(
1334
+ axis: Literal["X", "Y", "Z"] = "Z",
1335
+ direction_type: Literal["RADIAL", "UV_MAP"] = "RADIAL",
1336
+ uv_map: str = "",
1337
+ ) -> nt.ProcNode[pt.Vector]:
1338
+ """
1339
+ Uses a Tangent Shader Node.
1340
+
1341
+ See: https://docs.blender.org/manual/en/4.2/render/shader_nodes/input/tangent.html
1342
+ """
1343
+
1344
+ if uv_map != "" and direction_type != "UV_MAP":
1345
+ raise ValueError("uv_map is only available when direction_type is UV_MAP")
1346
+
1347
+ return nt.ProcNode.from_nodetype(
1348
+ node_type="ShaderNodeTangent",
1349
+ inputs={},
1350
+ attrs={"axis": axis, "direction_type": direction_type, "uv_map": uv_map},
1351
+ )
1352
+
1353
+
1354
+ class TextureResult(NamedTuple):
1355
+ fac: nt.ProcNode[float]
1356
+ color: nt.ProcNode[pt.Color]
1357
+
1358
+
1359
+ def brick(
1360
+ vector: nt.SocketOrVal[pt.Vector],
1361
+ color1: nt.SocketOrVal[pt.Color] = (0.8, 0.8, 0.8, 1),
1362
+ color2: nt.SocketOrVal[pt.Color] = (0.2, 0.2, 0.2, 1),
1363
+ mortar: nt.SocketOrVal[pt.Color] = (0, 0, 0, 1),
1364
+ scale: nt.SocketOrVal[float] = 5.0,
1365
+ mortar_size: nt.SocketOrVal[float] = 0.02,
1366
+ mortar_smooth: nt.SocketOrVal[float] = 0.1,
1367
+ bias: nt.SocketOrVal[float] = 0.0,
1368
+ brick_width: nt.SocketOrVal[float] = 0.5,
1369
+ row_height: nt.SocketOrVal[float] = 0.25,
1370
+ offset: float = 0.5,
1371
+ offset_frequency: int = 2,
1372
+ squash: float = 1.0,
1373
+ squash_frequency: int = 2,
1374
+ ) -> TextureResult:
1375
+ """
1376
+ Uses a TexBrick Shader Node.
1377
+
1378
+ Infinigen requires an explicit `vector` input - node will not default to using texture coordinate or world coordinate like blender
1379
+
1380
+ See: https://docs.blender.org/manual/en/4.2/render/shader_nodes/textures/brick.html
1381
+ """
1382
+ if vector is None:
1383
+ raise_explicit_noise_vector_error("brick", logger=logger)
1384
+ res = nt.ProcNode.from_nodetype(
1385
+ node_type="ShaderNodeTexBrick",
1386
+ inputs={
1387
+ "Vector": vector,
1388
+ "Color1": color1,
1389
+ "Color2": color2,
1390
+ "Mortar": mortar,
1391
+ "Scale": scale,
1392
+ "Mortar Size": mortar_size,
1393
+ "Mortar Smooth": mortar_smooth,
1394
+ "Bias": bias,
1395
+ "Brick Width": brick_width,
1396
+ "Row Height": row_height,
1397
+ },
1398
+ attrs={
1399
+ "offset": offset,
1400
+ "offset_frequency": offset_frequency,
1401
+ "squash": squash,
1402
+ "squash_frequency": squash_frequency,
1403
+ },
1404
+ )
1405
+ return TextureResult(
1406
+ fac=res._output_socket("fac"),
1407
+ color=res._output_socket("color"),
1408
+ )
1409
+
1410
+
1411
+ def checker(
1412
+ vector: nt.SocketOrVal[pt.Vector],
1413
+ color1: nt.SocketOrVal[pt.Color] = (0.8, 0.8, 0.8, 1),
1414
+ color2: nt.SocketOrVal[pt.Color] = (0.2, 0.2, 0.2, 1),
1415
+ scale: nt.SocketOrVal[float] = 5.0,
1416
+ ) -> TextureResult:
1417
+ """
1418
+ Uses a TexChecker Shader Node.
1419
+
1420
+ Infinigen requires an explicit `vector` input - node will not default to using texture coordinate or world coordinate like blender
1421
+
1422
+ See: https://docs.blender.org/manual/en/4.2/render/shader_nodes/textures/checker.html
1423
+ """
1424
+ if vector is None:
1425
+ raise_explicit_noise_vector_error("checker", logger=logger)
1426
+ res = nt.ProcNode.from_nodetype(
1427
+ node_type="ShaderNodeTexChecker",
1428
+ inputs={"Vector": vector, "Color1": color1, "Color2": color2, "Scale": scale},
1429
+ attrs={},
1430
+ )
1431
+ return TextureResult(
1432
+ fac=res._output_socket("fac"),
1433
+ color=res._output_socket("color"),
1434
+ )
1435
+
1436
+
1437
+ class CoordResult(NamedTuple):
1438
+ generated: nt.ProcNode[pt.Vector]
1439
+ normal: nt.ProcNode[pt.Vector]
1440
+ uv: nt.ProcNode[pt.Vector]
1441
+ object: nt.ProcNode[pt.Vector]
1442
+ camera: nt.ProcNode[pt.Vector]
1443
+ window: nt.ProcNode[pt.Vector]
1444
+
1445
+
1446
+ def coord(from_instancer: bool = False, object: Any = None) -> CoordResult:
1447
+ """
1448
+ Uses a TexCoord Shader Node.
1449
+
1450
+ See: https://docs.blender.org/manual/en/4.2/render/shader_nodes/input/texture_coordinate.html
1451
+ """
1452
+ res = nt.ProcNode.from_nodetype(
1453
+ node_type="ShaderNodeTexCoord",
1454
+ inputs={},
1455
+ attrs={"from_instancer": from_instancer, "object": object},
1456
+ )
1457
+ return CoordResult(
1458
+ generated=res._output_socket("generated"),
1459
+ normal=res._output_socket("normal"),
1460
+ uv=res._output_socket("uv"),
1461
+ object=res._output_socket("object"),
1462
+ camera=res._output_socket("camera"),
1463
+ window=res._output_socket("window"),
1464
+ )
1465
+
1466
+
1467
+ class GeometryResult(NamedTuple):
1468
+ position: nt.ProcNode[pt.Vector]
1469
+ normal: nt.ProcNode[pt.Vector]
1470
+ tangent: nt.ProcNode[pt.Vector]
1471
+ true_normal: nt.ProcNode[pt.Vector]
1472
+ incoming: nt.ProcNode[pt.Vector]
1473
+ parametric: nt.ProcNode[pt.Vector]
1474
+ backfacing: nt.ProcNode[float]
1475
+ pointiness: nt.ProcNode[float]
1476
+ random_per_island: nt.ProcNode[float]
1477
+
1478
+
1479
+ def geometry() -> GeometryResult:
1480
+ res = nt.ProcNode.from_nodetype(
1481
+ node_type="ShaderNodeNewGeometry",
1482
+ inputs={},
1483
+ attrs={},
1484
+ )
1485
+ return GeometryResult(
1486
+ position=res._output_socket("position"),
1487
+ normal=res._output_socket("normal"),
1488
+ tangent=res._output_socket("tangent"),
1489
+ true_normal=res._output_socket("true_normal"),
1490
+ incoming=res._output_socket("incoming"),
1491
+ parametric=res._output_socket("parametric"),
1492
+ backfacing=res._output_socket("backfacing"),
1493
+ pointiness=res._output_socket("pointiness"),
1494
+ random_per_island=res._output_socket("random_per_island"),
1495
+ )
1496
+
1497
+
1498
+ TTextureInterpolationType = Literal["Linear", "Closest", "Cubic", "Smart"] # TODO
1499
+
1500
+
1501
+ def environment(
1502
+ vector: nt.SocketOrVal[pt.Vector],
1503
+ image: Any = None,
1504
+ interpolation: TTextureInterpolationType = "Linear",
1505
+ projection: Literal["EQUIRECTANGULAR", "MIRROR_BALL"] = "EQUIRECTANGULAR",
1506
+ ) -> nt.ProcNode[pt.Color]:
1507
+ """
1508
+ Uses a TexEnvironment Shader Node.
1509
+
1510
+ Infinigen requires an explicit `vector` input - node will not default to using texture coordinate or world coordinate like blender
1511
+
1512
+ See: https://docs.blender.org/manual/en/4.2/render/shader_nodes/textures/environment.html
1513
+ """
1514
+ if vector is None:
1515
+ raise_explicit_noise_vector_error("environment", logger=logger)
1516
+ return nt.ProcNode.from_nodetype(
1517
+ node_type="ShaderNodeTexEnvironment",
1518
+ inputs={"Vector": vector},
1519
+ attrs={
1520
+ "image": image,
1521
+ "interpolation": interpolation,
1522
+ "projection": projection,
1523
+ },
1524
+ )
1525
+
1526
+
1527
+ def gradient(
1528
+ vector: nt.SocketOrVal[pt.Vector],
1529
+ gradient_type: Literal[
1530
+ "LINEAR",
1531
+ "QUADRATIC",
1532
+ "EASING",
1533
+ "DIAGONAL",
1534
+ "SPHERICAL",
1535
+ "QUADRATIC_SPHERE",
1536
+ "RADIAL",
1537
+ ] = "LINEAR",
1538
+ ) -> TextureResult:
1539
+ """
1540
+ Uses a TexGradient Shader Node.
1541
+
1542
+ Infinigen requires an explicit `vector` input - node will not default to using texture coordinate or world coordinate like blender
1543
+
1544
+ See: https://docs.blender.org/manual/en/4.2/render/shader_nodes/textures/gradient.html
1545
+ """
1546
+ if vector is None:
1547
+ raise_explicit_noise_vector_error("gradient", logger=logger)
1548
+ res = nt.ProcNode.from_nodetype(
1549
+ node_type="ShaderNodeTexGradient",
1550
+ inputs={"Vector": vector},
1551
+ attrs={"gradient_type": gradient_type},
1552
+ )
1553
+ return TextureResult(
1554
+ fac=res._output_socket("fac"),
1555
+ color=res._output_socket("color"),
1556
+ )
1557
+
1558
+
1559
+ def ies(
1560
+ vector: nt.SocketOrVal[pt.Vector],
1561
+ strength: nt.SocketOrVal[float] = 1.0,
1562
+ filepath: str = "",
1563
+ ies: Any = None,
1564
+ mode: Literal["INTERNAL", "EXTERNAL"] = "INTERNAL",
1565
+ ) -> nt.ProcNode[float]:
1566
+ """
1567
+ Uses a TexIES Shader Node.
1568
+
1569
+ Infinigen requires an explicit `vector` input - node will not default to using texture coordinate or world coordinate like blender
1570
+
1571
+ See: https://docs.blender.org/manual/en/4.2/render/shader_nodes/textures/ies.html
1572
+ """
1573
+ if vector is None:
1574
+ raise_explicit_noise_vector_error("ies", logger=logger)
1575
+ return nt.ProcNode.from_nodetype(
1576
+ node_type="ShaderNodeTexIES",
1577
+ inputs={"Vector": vector, "Strength": strength},
1578
+ attrs={"filepath": filepath, "ies": ies, "mode": mode},
1579
+ )
1580
+
1581
+
1582
+ def image(
1583
+ vector: nt.SocketOrVal[pt.Vector],
1584
+ extension: Literal["REPEAT", "EXTEND", "CLIP", "MIRROR"] = "REPEAT",
1585
+ image: Any = None,
1586
+ interpolation: TTextureInterpolationType = "Linear",
1587
+ projection: Literal["FLAT", "BOX", "SPHERE", "CUBE"] = "FLAT",
1588
+ projection_blend: float = 0.0,
1589
+ ) -> TextureResult:
1590
+ """
1591
+ Uses a TexImage Shader Node.
1592
+
1593
+ Infinigen requires an explicit `vector` input - node will not default to using texture coordinate or world coordinate like blender
1594
+
1595
+ See: https://docs.blender.org/manual/en/4.2/render/shader_nodes/textures/image.html
1596
+ """
1597
+ if vector is None:
1598
+ raise_explicit_noise_vector_error("image", logger=logger)
1599
+ res = nt.ProcNode.from_nodetype(
1600
+ node_type="ShaderNodeTexImage",
1601
+ inputs={"Vector": vector},
1602
+ attrs={
1603
+ "extension": extension,
1604
+ "image": image,
1605
+ "interpolation": interpolation,
1606
+ "projection": projection,
1607
+ "projection_blend": projection_blend,
1608
+ },
1609
+ )
1610
+ return TextureResult(
1611
+ color=res._output_socket("color"),
1612
+ fac=res._output_socket("alpha"),
1613
+ )
1614
+
1615
+
1616
+ def magic(
1617
+ vector: nt.SocketOrVal[pt.Vector],
1618
+ scale: nt.SocketOrVal[float] = 5.0,
1619
+ distortion: nt.SocketOrVal[float] = 1.0,
1620
+ turbulence_depth: int = 2,
1621
+ ) -> TextureResult:
1622
+ """
1623
+ Uses a TexMagic Shader Node.
1624
+
1625
+ Infinigen requires an explicit `vector` input - node will not default to using texture coordinate or world coordinate like blender
1626
+
1627
+ See: https://docs.blender.org/manual/en/4.2/render/shader_nodes/textures/magic.html
1628
+ """
1629
+ if vector is None:
1630
+ raise_explicit_noise_vector_error("magic", logger=logger)
1631
+ res = nt.ProcNode.from_nodetype(
1632
+ node_type="ShaderNodeTexMagic",
1633
+ inputs={"Vector": vector, "Scale": scale, "Distortion": distortion},
1634
+ attrs={"turbulence_depth": turbulence_depth},
1635
+ )
1636
+ return TextureResult(
1637
+ fac=res._output_socket("fac"),
1638
+ color=res._output_socket("color"),
1639
+ )
1640
+
1641
+
1642
+ '''
1643
+ def musgrave(
1644
+ vector: nt.SocketOrVal[pt.Vector],
1645
+ scale: nt.SocketOrVal[float] = 5.0,
1646
+ detail: nt.SocketOrVal[float] = 2.0,
1647
+ dimension: nt.SocketOrVal[float] = 2.0,
1648
+ lacunarity: nt.SocketOrVal[float] = 2.0,
1649
+ offset: nt.SocketOrVal[float] = 0.0,
1650
+ gain: nt.SocketOrVal[float] = 1.0,
1651
+ musgrave_type: str = "FBM",
1652
+ noise_dimensions: str = "4D",
1653
+ w: nt.SocketOrVal[float] = None,
1654
+ ) -> TextureResult:
1655
+ """
1656
+
1657
+ Musgrave texture imitation using NoiseTexture for backwards compatibility.
1658
+
1659
+ Converts old Musgrave parameters to new Noise node parameters:
1660
+ - roughness = lacunarity ** (-dimension)
1661
+ - detail = detail - 1
1662
+ - musgrave_type -> noise_type
1663
+ - musgrave_dimensions -> noise_dimensions
1664
+
1665
+ See: https://docs.blender.org/manual/en/4.2/render/shader_nodes/textures/musgrave.html
1666
+ """
1667
+ if vector is None:
1668
+ raise_explicit_noise_vector_error("musgrave", logger=logger)
1669
+ # Convert dimension/lacunarity to roughness
1670
+ if isinstance(lacunarity, nt.ProcNode) or isinstance(dimension, nt.ProcNode):
1671
+ roughness = func.math(
1672
+ lacunarity, func.math(0, dimension, operation="SUBTRACT"), operation="POWER"
1673
+ )
1674
+ else:
1675
+ roughness = lacunarity ** (-dimension)
1676
+ if isinstance(detail, nt.ProcNode):
1677
+ detail_adjusted = func.math(detail, 1, operation="SUBTRACT")
1678
+ else:
1679
+ detail_adjusted = detail - 1
1680
+
1681
+ logger.warning(
1682
+ f"Skipping important musgrave args: {musgrave_type=} {noise_dimensions=}"
1683
+ )
1684
+ res = noise(
1685
+ vector=vector,
1686
+ scale=scale,
1687
+ detail=detail_adjusted,
1688
+ roughness=roughness,
1689
+ lacunarity=lacunarity,
1690
+ distortion=offset, # offset maps to distortion
1691
+ noise_dimensions=noise_dimensions,
1692
+ # noise_node_type=musgrave_type,
1693
+ normalize=False,
1694
+ w=w,
1695
+ )
1696
+
1697
+ return res
1698
+ '''
1699
+
1700
+ TNoiseType = Literal[
1701
+ "MULTIFRACTAL",
1702
+ "FBM",
1703
+ "RIDGED_MULTIFRACTAL",
1704
+ "HYBRID_MULTIFRACTAL",
1705
+ "HETERO_TERRAIN",
1706
+ ]
1707
+ TNoiseDimensions = Literal["1D", "2D", "3D", "4D"]
1708
+
1709
+
1710
+ def noise(
1711
+ vector: nt.SocketOrVal[pt.Vector] = (0.0, 0.0, 0.0),
1712
+ scale: nt.SocketOrVal[float] = 5.0,
1713
+ detail: nt.SocketOrVal[float] = 2.0,
1714
+ roughness: nt.SocketOrVal[float] = 0.5,
1715
+ lacunarity: nt.SocketOrVal[float] = 2.0,
1716
+ offset: nt.SocketOrVal[float] = 0.0,
1717
+ gain: nt.SocketOrVal[float] = 1.0,
1718
+ distortion: nt.SocketOrVal[float] = 0.0,
1719
+ noise_dimensions: TNoiseDimensions = "3D",
1720
+ noise_type: TNoiseType = "FBM",
1721
+ normalize: bool = True,
1722
+ w: nt.SocketOrVal[float] = 0.0,
1723
+ ) -> TextureResult:
1724
+ """
1725
+
1726
+ Uses a TexNoise Shader Node.
1727
+
1728
+ Args:
1729
+ - offset: Only supported for RIDGED_MULTIFRACTAL, HYBRID_MULTIFRACTAL, HETERO_TERRAIN noise types
1730
+ - gain: Only supported for RIDGED_MULTIFRACTAL and HYBRID_MULTIFRACTAL noise types
1731
+ - distortion: Only supported for RIDGED_MULTIFRACTAL, HYBRID_MULTIFRACTAL, HETERO_TERRAIN noise types
1732
+ - w: Only supported for 1D and 4D noise dimensions
1733
+
1734
+ See: https://docs.blender.org/manual/en/4.2/render/shader_nodes/textures/noise.html
1735
+ """
1736
+
1737
+ inputs = {
1738
+ "Scale": scale,
1739
+ "Detail": detail,
1740
+ "Roughness": roughness,
1741
+ "Lacunarity": lacunarity,
1742
+ "Distortion": distortion,
1743
+ }
1744
+
1745
+ if w != 0.0:
1746
+ assert noise_dimensions in ["4D", "1D"]
1747
+ inputs["W"] = w
1748
+ elif noise_dimensions == "1D":
1749
+ raise_explicit_noise_vector_error("noise", logger=logger)
1750
+
1751
+ if vector == (0.0, 0.0, 0.0):
1752
+ assert noise_dimensions in ["2D", "3D", "4D"]
1753
+ inputs["Vector"] = vector
1754
+ elif noise_dimensions in ["2D", "3D"]:
1755
+ raise_explicit_noise_vector_error("noise", logger=logger)
1756
+
1757
+ _extra_args_modes = ["RIDGED_MULTIFRACTAL", "HYBRID_MULTIFRACTAL"] # noqa: F841
1758
+ if offset != 0.0:
1759
+ inputs["Offset"] = offset
1760
+ if gain != 1.0:
1761
+ inputs["Gain"] = gain
1762
+
1763
+ res = nt.ProcNode.from_nodetype(
1764
+ node_type="ShaderNodeTexNoise",
1765
+ inputs=inputs,
1766
+ attrs={
1767
+ "noise_dimensions": noise_dimensions,
1768
+ "noise_type": noise_type,
1769
+ "normalize": normalize,
1770
+ },
1771
+ )
1772
+ return TextureResult(
1773
+ fac=res._output_socket("fac"),
1774
+ color=res._output_socket("color"),
1775
+ )
1776
+
1777
+
1778
+ class PointDensityResult(NamedTuple):
1779
+ color: nt.ProcNode[pt.Color]
1780
+ density: nt.ProcNode[float]
1781
+
1782
+
1783
+ def point_density(
1784
+ vector: nt.SocketOrVal[pt.Vector],
1785
+ interpolation: Literal["Closest", "Linear", "Cubic"] = "Linear",
1786
+ object: Any = None,
1787
+ particle_color_source: Literal[
1788
+ "PARTICLE_AGE", "PARTICLE_SPEED", "PARTICLE_VELOCITY"
1789
+ ] = "PARTICLE_AGE",
1790
+ particle_system: Any = None,
1791
+ point_source: Literal["OBJECT", "PARTICLE_SYSTEM"] = "PARTICLE_SYSTEM",
1792
+ radius: float = 0.3,
1793
+ resolution: int = 100,
1794
+ space: Literal["OBJECT", "WORLD"] = "OBJECT",
1795
+ vertex_attribute_name: str = "",
1796
+ vertex_color_source: Literal[
1797
+ "VERTEX_COLOR", "VERTEX_NORMAL", "VERTEX_WEIGHT"
1798
+ ] = "VERTEX_COLOR",
1799
+ ) -> PointDensityResult:
1800
+ """
1801
+ Uses a TexPointDensity Shader Node.
1802
+
1803
+ Infinigen requires an explicit `vector` input - node will not default to using texture coordinate or world coordinate like blender
1804
+
1805
+ TODO: vertex_attribute_name and vertex_color_source are only available for point_source OBJECT
1806
+
1807
+ See: https://docs.blender.org/manual/en/4.2/render/shader_nodes/textures/point_density.html
1808
+ """
1809
+ if vector is None:
1810
+ raise_explicit_noise_vector_error("point_density", logger=logger)
1811
+ res = nt.ProcNode.from_nodetype(
1812
+ node_type="ShaderNodeTexPointDensity",
1813
+ inputs={"Vector": vector},
1814
+ attrs={
1815
+ "interpolation": interpolation,
1816
+ "object": object,
1817
+ "particle_color_source": particle_color_source,
1818
+ "particle_system": particle_system,
1819
+ "point_source": point_source,
1820
+ "radius": radius,
1821
+ "resolution": resolution,
1822
+ "space": space,
1823
+ "vertex_attribute_name": vertex_attribute_name,
1824
+ "vertex_color_source": vertex_color_source,
1825
+ },
1826
+ )
1827
+ return PointDensityResult(
1828
+ color=res._output_socket("color"),
1829
+ density=res._output_socket("density"),
1830
+ )
1831
+
1832
+
1833
+ def sky(
1834
+ air_density: float = 1.0,
1835
+ altitude: float = 0.0,
1836
+ dust_density: float = 1.0,
1837
+ ground_albedo: float = 0.3,
1838
+ ozone_density: float = 1.0,
1839
+ sky_type: Literal["NISHITA", "HOSEK_WILKIE", "PREETHAM"] = "NISHITA",
1840
+ sun_direction: tuple = (0.0, 0.0, 1.0),
1841
+ sun_disc: bool = True,
1842
+ sun_elevation: float = 0.261799,
1843
+ sun_intensity: float = 1.0,
1844
+ sun_rotation: float = 0.0,
1845
+ sun_size: float = 0.009512,
1846
+ turbidity: float = 2.2,
1847
+ ) -> nt.ProcNode[pt.Color]:
1848
+ """
1849
+ Uses a TexSky Shader Node.
1850
+
1851
+ See: https://docs.blender.org/manual/en/4.2/render/shader_nodes/textures/sky.html
1852
+ """
1853
+ return nt.ProcNode.from_nodetype(
1854
+ node_type="ShaderNodeTexSky",
1855
+ inputs={},
1856
+ attrs={
1857
+ "air_density": air_density,
1858
+ "altitude": altitude,
1859
+ "dust_density": dust_density,
1860
+ "ground_albedo": ground_albedo,
1861
+ "ozone_density": ozone_density,
1862
+ "sky_type": sky_type,
1863
+ "sun_direction": sun_direction,
1864
+ "sun_disc": sun_disc,
1865
+ "sun_elevation": sun_elevation,
1866
+ "sun_intensity": sun_intensity,
1867
+ "sun_rotation": sun_rotation,
1868
+ "sun_size": sun_size,
1869
+ "turbidity": turbidity,
1870
+ },
1871
+ )
1872
+
1873
+
1874
+ class VoronoiResult(NamedTuple):
1875
+ color: nt.ProcNode[pt.Color]
1876
+ distance: nt.ProcNode[float]
1877
+ position: nt.ProcNode[pt.Vector]
1878
+ w: nt.ProcNode[float] | None
1879
+
1880
+
1881
+ TDistanceMetric = Literal["EUCLIDEAN", "MANHATTAN", "CHEBYCHEV", "MINKOWSKI"]
1882
+
1883
+
1884
+ def voronoi(
1885
+ vector: nt.SocketOrVal[pt.Vector],
1886
+ scale: nt.SocketOrVal[float] = 5.0,
1887
+ detail: nt.SocketOrVal[float] = 0.0,
1888
+ roughness: nt.SocketOrVal[float] = 0.5,
1889
+ lacunarity: nt.SocketOrVal[float] = 2.0,
1890
+ randomness: nt.SocketOrVal[float] = 1.0,
1891
+ exponent: nt.SocketOrVal[float] = 0.0,
1892
+ distance: TDistanceMetric = "EUCLIDEAN",
1893
+ feature: Literal["F1", "F2"] = "F1",
1894
+ normalize: bool = False,
1895
+ voronoi_dimensions: TNoiseDimensions = "3D",
1896
+ w: nt.SocketOrVal[float] = 0.0,
1897
+ ) -> VoronoiResult:
1898
+ """
1899
+
1900
+ Uses a TexVoronoi Shader Node.
1901
+
1902
+ Args:
1903
+ exponent: Only supported for Minkowski distance.
1904
+
1905
+ See: https://docs.blender.org/manual/en/4.2/render/shader_nodes/textures/voronoi.html
1906
+ """
1907
+
1908
+ if vector == (0.0, 0.0, 0.0):
1909
+ raise_explicit_noise_vector_error("voronoi", logger=logger)
1910
+
1911
+ inputs = {
1912
+ "Vector": vector,
1913
+ "Scale": scale,
1914
+ "Detail": detail,
1915
+ "Roughness": roughness,
1916
+ "Lacunarity": lacunarity,
1917
+ "Randomness": randomness,
1918
+ }
1919
+
1920
+ if exponent != 0.0:
1921
+ assert distance == "MINKOWSKI", (
1922
+ f"exponent is only supported for Minkowski distance, got {distance=}"
1923
+ )
1924
+ inputs["Exponent"] = exponent
1925
+ if w != 0.0:
1926
+ assert voronoi_dimensions in ["4D", "1D"]
1927
+ inputs["W"] = w
1928
+
1929
+ res = nt.ProcNode.from_nodetype(
1930
+ node_type="ShaderNodeTexVoronoi",
1931
+ inputs=inputs,
1932
+ attrs={
1933
+ "distance": distance,
1934
+ "feature": feature,
1935
+ "normalize": normalize,
1936
+ "voronoi_dimensions": voronoi_dimensions,
1937
+ },
1938
+ )
1939
+
1940
+ w = res._output_socket("w") if voronoi_dimensions == "4D" else None
1941
+
1942
+ return VoronoiResult(
1943
+ distance=res._output_socket("distance"),
1944
+ color=res._output_socket("color"),
1945
+ position=res._output_socket("position"),
1946
+ w=w,
1947
+ )
1948
+
1949
+
1950
+ def voronoi_distance(
1951
+ vector: nt.SocketOrVal[pt.Vector],
1952
+ scale: nt.SocketOrVal[float] = 5.0,
1953
+ detail: nt.SocketOrVal[float] = 0.0,
1954
+ roughness: nt.SocketOrVal[float] = 0.5,
1955
+ lacunarity: nt.SocketOrVal[float] = 2.0,
1956
+ randomness: nt.SocketOrVal[float] = 1.0,
1957
+ normalize: bool = False,
1958
+ voronoi_dimensions: TNoiseDimensions = "3D",
1959
+ w: nt.SocketOrVal[float] = 0.0,
1960
+ ) -> nt.ProcNode[float]:
1961
+ if vector == (0.0, 0.0, 0.0):
1962
+ raise_explicit_noise_vector_error("voronoi_distance", logger=logger)
1963
+
1964
+ inputs = {
1965
+ "Vector": vector,
1966
+ "Scale": scale,
1967
+ "Detail": detail,
1968
+ "Roughness": roughness,
1969
+ "Lacunarity": lacunarity,
1970
+ "Randomness": randomness,
1971
+ }
1972
+
1973
+ if w != 0.0:
1974
+ assert voronoi_dimensions in ["4D", "1D"]
1975
+ inputs["W"] = w
1976
+
1977
+ return nt.ProcNode.from_nodetype(
1978
+ node_type="ShaderNodeTexVoronoi",
1979
+ inputs=inputs,
1980
+ attrs={
1981
+ "feature": "DISTANCE_TO_EDGE",
1982
+ "normalize": normalize,
1983
+ "voronoi_dimensions": voronoi_dimensions,
1984
+ },
1985
+ )
1986
+
1987
+
1988
+ def voronoi_smooth_f1(
1989
+ vector: nt.SocketOrVal[pt.Vector],
1990
+ scale: nt.SocketOrVal[float] = 5.0,
1991
+ detail: nt.SocketOrVal[float] = 0.0,
1992
+ roughness: nt.SocketOrVal[float] = 0.5,
1993
+ lacunarity: nt.SocketOrVal[float] = 2.0,
1994
+ smoothness: nt.SocketOrVal[float] = 0.5,
1995
+ randomness: nt.SocketOrVal[float] = 1.0,
1996
+ distance: TDistanceMetric = "EUCLIDEAN",
1997
+ normalize: bool = False,
1998
+ voronoi_dimensions: TNoiseDimensions = "3D",
1999
+ w: nt.SocketOrVal[float] = 0.0,
2000
+ ) -> VoronoiResult:
2001
+ if vector == (0.0, 0.0, 0.0) and voronoi_dimensions != "1D":
2002
+ raise_explicit_noise_vector_error("voronoi_smooth_f1", logger=logger)
2003
+ elif w == 0.0 and voronoi_dimensions != "1D":
2004
+ raise_explicit_noise_vector_error("voronoi_smooth_f1", logger=logger)
2005
+
2006
+ inputs = {
2007
+ "Vector": vector,
2008
+ "Scale": scale,
2009
+ "Detail": detail,
2010
+ "Roughness": roughness,
2011
+ "Lacunarity": lacunarity,
2012
+ "Randomness": randomness,
2013
+ "Smoothness": smoothness,
2014
+ }
2015
+
2016
+ if w != 0.0:
2017
+ assert voronoi_dimensions in ["4D", "1D"]
2018
+ inputs["W"] = w
2019
+
2020
+ res = nt.ProcNode.from_nodetype(
2021
+ node_type="ShaderNodeTexVoronoi",
2022
+ inputs=inputs,
2023
+ attrs={
2024
+ "distance": distance,
2025
+ "feature": "SMOOTH_F1",
2026
+ "normalize": normalize,
2027
+ "voronoi_dimensions": voronoi_dimensions,
2028
+ },
2029
+ )
2030
+
2031
+ w = res._output_socket("w") if voronoi_dimensions == "4D" else None
2032
+
2033
+ return VoronoiResult(
2034
+ distance=res._output_socket("distance"),
2035
+ color=res._output_socket("color"),
2036
+ position=res._output_socket("position"),
2037
+ w=w,
2038
+ )
2039
+
2040
+
2041
+ def voronoi_n_spheres_distance(
2042
+ vector: nt.SocketOrVal[pt.Vector],
2043
+ scale: nt.SocketOrVal[float] = 5.0,
2044
+ randomness: nt.SocketOrVal[float] = 1.0,
2045
+ normalize: bool = False,
2046
+ ) -> nt.ProcNode[float]:
2047
+ if vector is None:
2048
+ raise_explicit_noise_vector_error("voronoi_spheres_distance", logger=logger)
2049
+
2050
+ return nt.ProcNode.from_nodetype(
2051
+ node_type="ShaderNodeTexVoronoi",
2052
+ inputs={
2053
+ "Vector": vector,
2054
+ "Scale": scale,
2055
+ "Randomness": randomness,
2056
+ },
2057
+ attrs={
2058
+ "feature": "N_SPHERE_RADIUS",
2059
+ "normalize": normalize,
2060
+ },
2061
+ )
2062
+
2063
+
2064
+ def wave(
2065
+ vector: nt.SocketOrVal[pt.Vector],
2066
+ scale: nt.SocketOrVal[float] = 5.0,
2067
+ distortion: nt.SocketOrVal[float] = 0.0,
2068
+ detail: nt.SocketOrVal[float] = 2.0,
2069
+ detail_scale: nt.SocketOrVal[float] = 1.0,
2070
+ detail_roughness: nt.SocketOrVal[float] = 0.5,
2071
+ phase_offset: nt.SocketOrVal[float] = 0.0,
2072
+ bands_direction: Literal["X", "Y", "Z", "SPHERICAL"] = "X",
2073
+ rings_direction: Literal["X", "Y", "Z", "SPHERICAL"] = "X",
2074
+ wave_profile: Literal["SIN", "SAW", "TRI"] = "SIN",
2075
+ wave_type: Literal["BANDS", "RINGS"] = "BANDS",
2076
+ ) -> TextureResult:
2077
+ """
2078
+
2079
+ Uses a TexWave Shader Node.
2080
+
2081
+ TODO: bands_direction and rings_direction are only available for wave_type BANDS or RINGS respectively
2082
+
2083
+ See: https://docs.blender.org/manual/en/4.2/render/shader_nodes/textures/wave.html
2084
+ """
2085
+ if vector is None:
2086
+ raise_explicit_noise_vector_error("wave", logger=logger)
2087
+ res = nt.ProcNode.from_nodetype(
2088
+ node_type="ShaderNodeTexWave",
2089
+ inputs={
2090
+ "Vector": vector,
2091
+ "Scale": scale,
2092
+ "Distortion": distortion,
2093
+ "Detail": detail,
2094
+ "Detail Scale": detail_scale,
2095
+ "Detail Roughness": detail_roughness,
2096
+ "Phase Offset": phase_offset,
2097
+ },
2098
+ attrs={
2099
+ "bands_direction": bands_direction,
2100
+ "rings_direction": rings_direction,
2101
+ "wave_profile": wave_profile,
2102
+ "wave_type": wave_type,
2103
+ },
2104
+ )
2105
+ return TextureResult(
2106
+ fac=res._output_socket("fac"),
2107
+ color=res._output_socket("color"),
2108
+ )
2109
+
2110
+
2111
+ def white_noise(
2112
+ vector: nt.SocketOrVal[pt.Vector] | None = None,
2113
+ noise_dimensions: TNoiseDimensions = "3D",
2114
+ w: nt.SocketOrVal[float] = None,
2115
+ ) -> TextureResult:
2116
+ """
2117
+
2118
+ Uses a TexWhiteNoise Shader Node.
2119
+
2120
+ See: https://docs.blender.org/manual/en/4.2/render/shader_nodes/textures/white_noise.html
2121
+ """
2122
+
2123
+ if noise_dimensions == "1D" and w is None:
2124
+ raise ValueError("w is required for 1D white noise")
2125
+ if noise_dimensions in ["2D", "3D", "4D"] and vector is None:
2126
+ raise_explicit_noise_vector_error("white_noise", logger=logger)
2127
+ if noise_dimensions in ["2D", "3D"] and w is not None:
2128
+ raise ValueError("w is not supported for 2D or 3D white noise")
2129
+
2130
+ res = nt.ProcNode.from_nodetype(
2131
+ node_type="ShaderNodeTexWhiteNoise",
2132
+ inputs={
2133
+ "Vector": vector,
2134
+ "W": w,
2135
+ },
2136
+ attrs={"noise_dimensions": noise_dimensions},
2137
+ )
2138
+ return TextureResult(
2139
+ fac=res._output_socket("value"),
2140
+ color=res._output_socket("color"),
2141
+ )
2142
+
2143
+
2144
+ def uv_along_stroke(use_tips: bool = False) -> nt.ProcNode[pt.Vector]:
2145
+ """
2146
+ Uses a UVAlongStroke Shader Node.
2147
+
2148
+ See: https://docs.blender.org/manual/en/4.2/render/shader_nodes/input/uv_map.html
2149
+ """
2150
+ return nt.ProcNode.from_nodetype(
2151
+ node_type="ShaderNodeUVAlongStroke",
2152
+ inputs={},
2153
+ attrs={"use_tips": use_tips},
2154
+ )
2155
+
2156
+
2157
+ def uv_map(from_instancer: bool = False, uv_map: str = "") -> nt.ProcNode[pt.Vector]:
2158
+ """
2159
+ Uses a UVMap Shader Node.
2160
+
2161
+ See: https://docs.blender.org/manual/en/4.2/render/shader_nodes/input/uv_map.html
2162
+ """
2163
+ return nt.ProcNode.from_nodetype(
2164
+ node_type="ShaderNodeUVMap",
2165
+ inputs={},
2166
+ attrs={"from_instancer": from_instancer, "uv_map": uv_map},
2167
+ )
2168
+
2169
+
2170
+ class ColorRampResult(NamedTuple):
2171
+ color: nt.ProcNode[pt.Color]
2172
+ alpha: nt.ProcNode[float]
2173
+
2174
+
2175
+ TRampInterpolationType = Literal["EASE", "CARDINAL", "LINEAR", "B_SPLINE", "CONSTANT"]
2176
+
2177
+
2178
+ # Manual
2179
+ def color_ramp(
2180
+ fac: nt.SocketOrVal[float] = 0.5,
2181
+ points: list[tuple[float, pt.Color]] | None = None,
2182
+ mode: Literal["RGB", "HSV", "HSL"] = "RGB",
2183
+ interpolation: TRampInterpolationType = "LINEAR",
2184
+ ) -> ColorRampResult:
2185
+ """
2186
+ Uses a ValToRGB (ColorRamp) Shader Node with points support.
2187
+
2188
+ See: https://docs.blender.org/manual/en/4.2/render/shader_nodes/converter/color_ramp.html
2189
+ """
2190
+
2191
+ res = nt.ProcNode.from_nodetype(
2192
+ node_type="ShaderNodeValToRGB",
2193
+ inputs={"Fac": fac},
2194
+ attrs={
2195
+ "points": points,
2196
+ "color_mode": mode,
2197
+ "interpolation": interpolation,
2198
+ },
2199
+ )
2200
+ return ColorRampResult(
2201
+ color=res._output_socket("Color"),
2202
+ alpha=res._output_socket("Alpha"),
2203
+ )
2204
+
2205
+
2206
+ def value() -> nt.ProcNode[float]:
2207
+ """
2208
+ Uses a Value Shader Node.
2209
+
2210
+ See: https://docs.blender.org/manual/en/4.2/render/shader_nodes/input/value.html
2211
+ """
2212
+
2213
+ raise_io_error("value", logger=logger)
2214
+
2215
+ return nt.ProcNode.from_nodetype(node_type="ShaderNodeValue", inputs={}, attrs={})
2216
+
2217
+
2218
+ def vector_displacement(
2219
+ vector: nt.SocketOrVal[pt.Color] = (0.8, 0.8, 0.8, 1),
2220
+ midlevel: nt.SocketOrVal[float] = 0.0,
2221
+ scale: nt.SocketOrVal[float] = 1.0,
2222
+ space: Literal["TANGENT", "OBJECT", "WORLD"] = "TANGENT",
2223
+ ) -> nt.ProcNode[pt.Vector]:
2224
+ """
2225
+ Uses a VectorDisplacement Shader Node.
2226
+
2227
+ See: https://docs.blender.org/manual/en/4.2/render/shader_nodes/vector/vector_displacement.html
2228
+ """
2229
+ return nt.ProcNode.from_nodetype(
2230
+ node_type="ShaderNodeVectorDisplacement",
2231
+ inputs={"Vector": vector, "Midlevel": midlevel, "Scale": scale},
2232
+ attrs={"space": space},
2233
+ )
2234
+
2235
+
2236
+ def vertex_color(layer_name: str = "") -> nt.ProcNode[pt.Color]:
2237
+ """
2238
+ Uses a VertexColor Shader Node.
2239
+
2240
+ See: https://docs.blender.org/manual/en/4.2/render/shader_nodes/input/vertex_color.html
2241
+ """
2242
+ return nt.ProcNode.from_nodetype(
2243
+ node_type="ShaderNodeVertexColor",
2244
+ inputs={},
2245
+ attrs={"layer_name": layer_name},
2246
+ )
2247
+
2248
+
2249
+ def volume_absorption(
2250
+ color: nt.SocketOrVal[pt.Color] = (0.8, 0.8, 0.8, 1),
2251
+ density: nt.SocketOrVal[float] = 1.0,
2252
+ ) -> nt.ProcNode[nt.Shader]:
2253
+ """
2254
+ Uses a VolumeAbsorption Shader Node.
2255
+
2256
+ See: https://docs.blender.org/manual/en/4.2/render/shader_nodes/shader/volume_absorption.html
2257
+ """
2258
+ return nt.ProcNode.from_nodetype(
2259
+ node_type="ShaderNodeVolumeAbsorption",
2260
+ inputs={"Color": color, "Density": density},
2261
+ attrs={},
2262
+ )
2263
+
2264
+
2265
+ def volume_info() -> nt.ProcNode:
2266
+ """
2267
+ Uses a VolumeInfo Shader Node.
2268
+
2269
+ See: https://docs.blender.org/manual/en/4.2/render/shader_nodes/input/volume_info.html
2270
+ """
2271
+ return nt.ProcNode.from_nodetype(
2272
+ node_type="ShaderNodeVolumeInfo",
2273
+ inputs={},
2274
+ attrs={},
2275
+ )
2276
+
2277
+
2278
+ def volume_principled(
2279
+ color: nt.SocketOrVal[pt.Color] = (0.5, 0.5, 0.5, 1),
2280
+ color_attribute: nt.SocketOrVal[str] = "",
2281
+ density: nt.SocketOrVal[float] = 1.0,
2282
+ density_attribute: nt.SocketOrVal[str] = "density",
2283
+ anisotropy: nt.SocketOrVal[float] = 0.0,
2284
+ absorption_color: nt.SocketOrVal[pt.Color] = (0, 0, 0, 1),
2285
+ emission_strength: nt.SocketOrVal[float] = 0.0,
2286
+ emission_color: nt.SocketOrVal[pt.Color] = (1, 1, 1, 1),
2287
+ blackbody_intensity: nt.SocketOrVal[float] = 0.0,
2288
+ blackbody_tint: nt.SocketOrVal[pt.Color] = (1, 1, 1, 1),
2289
+ temperature: nt.SocketOrVal[float] = 1000.0,
2290
+ temperature_attribute: nt.SocketOrVal[str] = "temperature",
2291
+ ) -> nt.ProcNode[nt.Shader]:
2292
+ """
2293
+ Uses a VolumePrincipled Shader Node.
2294
+
2295
+ See: https://docs.blender.org/manual/en/4.2/render/shader_nodes/shader/volume_principled.html
2296
+ """
2297
+ return nt.ProcNode.from_nodetype(
2298
+ node_type="ShaderNodeVolumePrincipled",
2299
+ inputs={
2300
+ "Color": color,
2301
+ "Color Attribute": color_attribute,
2302
+ "Density": density,
2303
+ "Density Attribute": density_attribute,
2304
+ "Anisotropy": anisotropy,
2305
+ "Absorption Color": absorption_color,
2306
+ "Emission Strength": emission_strength,
2307
+ "Emission Color": emission_color,
2308
+ "Blackbody Intensity": blackbody_intensity,
2309
+ "Blackbody Tint": blackbody_tint,
2310
+ "Temperature": temperature,
2311
+ "Temperature Attribute": temperature_attribute,
2312
+ },
2313
+ attrs={},
2314
+ )
2315
+
2316
+
2317
+ def volume_scatter(
2318
+ color: nt.SocketOrVal[pt.Color] = (0.8, 0.8, 0.8, 1),
2319
+ density: nt.SocketOrVal[float] = 1.0,
2320
+ anisotropy: nt.SocketOrVal[float] = 0.0,
2321
+ ) -> nt.ProcNode[nt.Shader]:
2322
+ """
2323
+ Uses a VolumeScatter Shader Node.
2324
+
2325
+ See: https://docs.blender.org/manual/en/4.2/render/shader_nodes/shader/volume_scatter.html
2326
+ """
2327
+ return nt.ProcNode.from_nodetype(
2328
+ node_type="ShaderNodeVolumeScatter",
2329
+ inputs={"Color": color, "Density": density, "Anisotropy": anisotropy},
2330
+ attrs={},
2331
+ )
2332
+
2333
+
2334
+ def wavelength(wavelength: nt.SocketOrVal[float] = 500.0) -> nt.ProcNode[pt.Color]:
2335
+ """
2336
+ Uses a Wavelength Shader Node.
2337
+
2338
+ See: https://docs.blender.org/manual/en/4.2/render/shader_nodes/converter/wavelength.html
2339
+ """
2340
+ return nt.ProcNode.from_nodetype(
2341
+ node_type="ShaderNodeWavelength",
2342
+ inputs={"Wavelength": wavelength},
2343
+ attrs={},
2344
+ )
2345
+
2346
+
2347
+ def wireframe(
2348
+ size: nt.SocketOrVal[float] = 0.01, use_pixel_size: bool = False
2349
+ ) -> nt.ProcNode[float]:
2350
+ """
2351
+ Uses a Wireframe Shader Node.
2352
+
2353
+ See: https://docs.blender.org/manual/en/4.2/render/shader_nodes/input/wireframe.html
2354
+ """
2355
+ return nt.ProcNode.from_nodetype(
2356
+ node_type="ShaderNodeWireframe",
2357
+ inputs={"Size": size},
2358
+ attrs={"use_pixel_size": use_pixel_size},
2359
+ )