1 # SPDX-License-Identifier: GPL-2.0-or-later
3 """"Nodes based User interface for shaders exported to POV textures."""
6 from bpy
.utils
import register_class
, unregister_class
7 from bpy
.types
import NodeSocket
, Operator
8 from bpy
.props
import (
12 import nodeitems_utils
13 from nodeitems_utils
import NodeCategory
, NodeItem
15 # ---------------------------------------------------------------- #
17 # ---------------------------------------------------------------- #
18 # -------- Parent node class
19 class ObjectNodeTree(bpy
.types
.NodeTree
):
20 """Povray Material Nodes"""
22 bl_idname
= "ObjectNodeTree"
23 bl_label
= "Povray Object Nodes"
27 def poll(cls
, context
):
28 return context
.scene
.render
.engine
== "POVRAY_RENDER"
31 def get_from_context(cls
, context
):
32 ob
= context
.active_object
33 if ob
and ob
.type != 'LIGHT':
34 ma
= ob
.active_material
36 nt_name
= ma
.node_tree
38 return nt_name
, ma
, ma
39 return (None, None, None)
45 # -------- Sockets classes
46 class PovraySocketUniversal(NodeSocket
):
47 bl_idname
= "PovraySocketUniversal"
48 bl_label
= "Povray Socket"
49 value_unlimited
: bpy
.props
.FloatProperty(default
=0.0)
50 value_0_1
: bpy
.props
.FloatProperty(min=0.0, max=1.0, default
=0.0)
51 value_0_10
: bpy
.props
.FloatProperty(min=0.0, max=10.0, default
=0.0)
52 value_000001_10
: bpy
.props
.FloatProperty(min=0.000001, max=10.0, default
=0.0)
53 value_1_9
: bpy
.props
.IntProperty(min=1, max=9, default
=1)
54 value_0_255
: bpy
.props
.IntProperty(min=0, max=255, default
=0)
55 percent
: bpy
.props
.FloatProperty(min=0.0, max=100.0, default
=0.0)
57 def draw(self
, context
, layout
, node
, text
):
58 space
= context
.space_data
59 tree
= space
.edit_tree
64 # inps = link.to_node.inputs
66 inp
for inp
in link
.to_node
.inputs
if link
.from_node
== node
and inp
.is_linked
68 for inp
in linked_inps
:
69 if inp
.bl_idname
== "PovraySocketFloat_0_1":
70 if (prop
:= "value_0_1") not in value
:
72 if inp
.bl_idname
== "PovraySocketFloat_000001_10":
73 if (prop
:= "value_000001_10") not in value
:
75 if inp
.bl_idname
== "PovraySocketFloat_0_10":
76 if (prop
:= "value_0_10") not in value
:
78 if inp
.bl_idname
== "PovraySocketInt_1_9":
79 if (prop
:= "value_1_9") not in value
:
81 if inp
.bl_idname
== "PovraySocketInt_0_255":
82 if (prop
:= "value_0_255") not in value
:
84 if inp
.bl_idname
== "PovraySocketFloatUnlimited":
85 if (prop
:= "value_unlimited") not in value
:
88 layout
.prop(self
, "%s" % value
[0], text
=text
)
90 layout
.prop(self
, "percent", text
="Percent")
92 layout
.prop(self
, "percent", text
=text
)
94 def draw_color(self
, context
, node
):
98 class PovraySocketFloat_0_1(NodeSocket
):
99 bl_idname
= "PovraySocketFloat_0_1"
100 bl_label
= "Povray Socket"
101 default_value
: bpy
.props
.FloatProperty(
102 description
="Input node Value_0_1", min=0, max=1, default
=0
105 def draw(self
, context
, layout
, node
, text
):
107 layout
.label(text
=text
)
109 layout
.prop(self
, "default_value", text
=text
, slider
=True)
111 def draw_color(self
, context
, node
):
112 return (0.5, 0.7, 0.7, 1)
115 class PovraySocketFloat_0_10(NodeSocket
):
116 bl_idname
= "PovraySocketFloat_0_10"
117 bl_label
= "Povray Socket"
118 default_value
: bpy
.props
.FloatProperty(
119 description
="Input node Value_0_10", min=0, max=10, default
=0
122 def draw(self
, context
, layout
, node
, text
):
123 if node
.bl_idname
== "ShaderNormalMapNode" and node
.inputs
[2].is_linked
:
124 layout
.label(text
="")
125 self
.hide_value
= True
127 layout
.label(text
=text
)
129 layout
.prop(self
, "default_value", text
=text
, slider
=True)
131 def draw_color(self
, context
, node
):
132 return (0.65, 0.65, 0.65, 1)
135 class PovraySocketFloat_10(NodeSocket
):
136 bl_idname
= "PovraySocketFloat_10"
137 bl_label
= "Povray Socket"
138 default_value
: bpy
.props
.FloatProperty(
139 description
="Input node Value_10", min=-10, max=10, default
=0
142 def draw(self
, context
, layout
, node
, text
):
143 if node
.bl_idname
== "ShaderNormalMapNode" and node
.inputs
[2].is_linked
:
144 layout
.label(text
="")
145 self
.hide_value
= True
147 layout
.label(text
=text
)
149 layout
.prop(self
, "default_value", text
=text
, slider
=True)
151 def draw_color(self
, context
, node
):
152 return (0.65, 0.65, 0.65, 1)
155 class PovraySocketFloatPositive(NodeSocket
):
156 bl_idname
= "PovraySocketFloatPositive"
157 bl_label
= "Povray Socket"
158 default_value
: bpy
.props
.FloatProperty(
159 description
="Input Node Value Positive", min=0.0, default
=0
162 def draw(self
, context
, layout
, node
, text
):
164 layout
.label(text
=text
)
166 layout
.prop(self
, "default_value", text
=text
, slider
=True)
168 def draw_color(self
, context
, node
):
169 return (0.045, 0.005, 0.136, 1)
172 class PovraySocketFloat_000001_10(NodeSocket
):
173 bl_idname
= "PovraySocketFloat_000001_10"
174 bl_label
= "Povray Socket"
175 default_value
: bpy
.props
.FloatProperty(min=0.000001, max=10, default
=0.000001)
177 def draw(self
, context
, layout
, node
, text
):
178 if self
.is_output
or self
.is_linked
:
179 layout
.label(text
=text
)
181 layout
.prop(self
, "default_value", text
=text
, slider
=True)
183 def draw_color(self
, context
, node
):
187 class PovraySocketFloatUnlimited(NodeSocket
):
188 bl_idname
= "PovraySocketFloatUnlimited"
189 bl_label
= "Povray Socket"
190 default_value
: bpy
.props
.FloatProperty(default
=0.0)
192 def draw(self
, context
, layout
, node
, text
):
194 layout
.label(text
=text
)
196 layout
.prop(self
, "default_value", text
=text
, slider
=True)
198 def draw_color(self
, context
, node
):
199 return (0.7, 0.7, 1, 1)
202 class PovraySocketInt_1_9(NodeSocket
):
203 bl_idname
= "PovraySocketInt_1_9"
204 bl_label
= "Povray Socket"
205 default_value
: bpy
.props
.IntProperty(
206 description
="Input node Value_1_9", min=1, max=9, default
=6
209 def draw(self
, context
, layout
, node
, text
):
211 layout
.label(text
=text
)
213 layout
.prop(self
, "default_value", text
=text
)
215 def draw_color(self
, context
, node
):
216 return (1, 0.7, 0.7, 1)
219 class PovraySocketInt_0_256(NodeSocket
):
220 bl_idname
= "PovraySocketInt_0_256"
221 bl_label
= "Povray Socket"
222 default_value
: bpy
.props
.IntProperty(min=0, max=255, default
=0)
224 def draw(self
, context
, layout
, node
, text
):
226 layout
.label(text
=text
)
228 layout
.prop(self
, "default_value", text
=text
)
230 def draw_color(self
, context
, node
):
231 return (0.5, 0.5, 0.5, 1)
234 class PovraySocketPattern(NodeSocket
):
235 bl_idname
= "PovraySocketPattern"
236 bl_label
= "Povray Socket"
238 default_value
: bpy
.props
.EnumProperty(
240 description
="Select the pattern",
242 ("boxed", "Boxed", ""),
243 ("brick", "Brick", ""),
244 ("cells", "Cells", ""),
245 ("checker", "Checker", ""),
246 ("granite", "Granite", ""),
247 ("leopard", "Leopard", ""),
248 ("marble", "Marble", ""),
249 ("onion", "Onion", ""),
250 ("planar", "Planar", ""),
251 ("quilted", "Quilted", ""),
252 ("ripples", "Ripples", ""),
253 ("radial", "Radial", ""),
254 ("spherical", "Spherical", ""),
255 ("spotted", "Spotted", ""),
256 ("waves", "Waves", ""),
257 ("wood", "Wood", ""),
258 ("wrinkles", "Wrinkles", ""),
263 def draw(self
, context
, layout
, node
, text
):
264 if self
.is_output
or self
.is_linked
:
265 layout
.label(text
="Pattern")
267 layout
.prop(self
, "default_value", text
=text
)
269 def draw_color(self
, context
, node
):
273 class PovraySocketColor(NodeSocket
):
274 bl_idname
= "PovraySocketColor"
275 bl_label
= "Povray Socket"
277 default_value
: FloatVectorProperty(
282 default
=(0.0, 0.0, 0.0),
283 options
={"ANIMATABLE"},
287 def draw(self
, context
, layout
, node
, text
):
288 if self
.is_output
or self
.is_linked
:
289 layout
.label(text
=text
)
291 layout
.prop(self
, "default_value", text
=text
)
293 def draw_color(self
, context
, node
):
297 class PovraySocketColorRGBFT(NodeSocket
):
298 bl_idname
= "PovraySocketColorRGBFT"
299 bl_label
= "Povray Socket"
301 default_value
: FloatVectorProperty(
306 default
=(0.0, 0.0, 0.0),
307 options
={"ANIMATABLE"},
310 f
: bpy
.props
.FloatProperty(default
=0.0, min=0.0, max=1.0)
311 t
: bpy
.props
.FloatProperty(default
=0.0, min=0.0, max=1.0)
313 def draw(self
, context
, layout
, node
, text
):
314 if self
.is_output
or self
.is_linked
:
315 layout
.label(text
=text
)
317 layout
.prop(self
, "default_value", text
=text
)
319 def draw_color(self
, context
, node
):
323 class PovraySocketTexture(NodeSocket
):
324 bl_idname
= "PovraySocketTexture"
325 bl_label
= "Povray Socket"
326 default_value
: bpy
.props
.IntProperty()
328 def draw(self
, context
, layout
, node
, text
):
329 layout
.label(text
=text
)
331 def draw_color(self
, context
, node
):
335 class PovraySocketTransform(NodeSocket
):
336 bl_idname
= "PovraySocketTransform"
337 bl_label
= "Povray Socket"
338 default_value
: bpy
.props
.IntProperty(min=0, max=255, default
=0)
340 def draw(self
, context
, layout
, node
, text
):
341 layout
.label(text
=text
)
343 def draw_color(self
, context
, node
):
344 return (99 / 255, 99 / 255, 199 / 255, 1)
347 class PovraySocketNormal(NodeSocket
):
348 bl_idname
= "PovraySocketNormal"
349 bl_label
= "Povray Socket"
350 default_value
: bpy
.props
.IntProperty(min=0, max=255, default
=0)
352 def draw(self
, context
, layout
, node
, text
):
353 layout
.label(text
=text
)
355 def draw_color(self
, context
, node
):
356 return (0.65, 0.65, 0.65, 1)
359 class PovraySocketSlope(NodeSocket
):
360 bl_idname
= "PovraySocketSlope"
361 bl_label
= "Povray Socket"
362 default_value
: bpy
.props
.FloatProperty(min=0.0, max=1.0)
363 height
: bpy
.props
.FloatProperty(min=0.0, max=10.0)
364 slope
: bpy
.props
.FloatProperty(min=-10.0, max=10.0)
366 def draw(self
, context
, layout
, node
, text
):
367 if self
.is_output
or self
.is_linked
:
368 layout
.label(text
=text
)
370 layout
.prop(self
, "default_value", text
="")
371 layout
.prop(self
, "height", text
="")
372 layout
.prop(self
, "slope", text
="")
374 def draw_color(self
, context
, node
):
378 class PovraySocketMap(NodeSocket
):
379 bl_idname
= "PovraySocketMap"
380 bl_label
= "Povray Socket"
381 default_value
: bpy
.props
.StringProperty()
383 def draw(self
, context
, layout
, node
, text
):
384 layout
.label(text
=text
)
386 def draw_color(self
, context
, node
):
387 return (0.2, 0, 0.2, 1)
390 class PovrayPatternNode(Operator
):
391 bl_idname
= "pov.patternnode"
396 def execute(self
, context
):
397 space
= context
.space_data
398 tree
= space
.edit_tree
399 for node
in tree
.nodes
:
402 tmap
= tree
.nodes
.new("ShaderNodeValToRGB")
403 tmap
.label
= "Pattern"
404 for inp
in tmap
.inputs
:
405 tmap
.inputs
.remove(inp
)
406 for outp
in tmap
.outputs
:
407 tmap
.outputs
.remove(outp
)
408 pattern
= tmap
.inputs
.new("PovraySocketPattern", "Pattern")
409 pattern
.hide_value
= True
410 mapping
= tmap
.inputs
.new("NodeSocketVector", "Mapping")
411 mapping
.hide_value
= True
412 transform
= tmap
.inputs
.new("NodeSocketVector", "Transform")
413 transform
.hide_value
= True
414 modifier
= tmap
.inputs
.new("NodeSocketVector", "Modifier")
415 modifier
.hide_value
= True
416 for i
in range(0, 2):
417 tmap
.inputs
.new("NodeSocketShader", "%s" % (i
+ 1))
418 tmap
.outputs
.new("NodeSocketShader", "Material")
419 tmap
.outputs
.new("NodeSocketColor", "Color")
420 tree
.nodes
.active
= tmap
422 aNode
= tree
.nodes
.active
424 v2d
= context
.region
.view2d
425 x
, y
= v2d
.region_to_view(self
.x
, self
.y
)
426 aNode
.location
= (x
, y
)
428 def modal(self
, context
, event
):
429 if event
.type == "MOUSEMOVE":
430 self
.x
= event
.mouse_region_x
431 self
.y
= event
.mouse_region_y
432 self
.execute(context
)
433 return {"RUNNING_MODAL"}
434 if event
.type == "LEFTMOUSE":
436 if event
.type in ("RIGHTMOUSE", "ESC"):
439 return {"RUNNING_MODAL"}
441 def invoke(self
, context
, event
):
442 context
.window_manager
.modal_handler_add(self
)
443 return {"RUNNING_MODAL"}
446 class UpdatePreviewMaterial(Operator
):
447 """Operator update preview material"""
449 bl_idname
= "node.updatepreview"
450 bl_label
= "Update preview"
452 def execute(self
, context
):
453 scene
= context
.view_layer
455 for obj
in scene
.objects
:
457 scene
.objects
.active
= ob
459 scene
.objects
.active
= ob
461 def modal(self
, context
, event
):
462 if event
.type == "RIGHTMOUSE":
463 self
.execute(context
)
465 return {"PASS_THROUGH"}
467 def invoke(self
, context
, event
):
468 context
.window_manager
.modal_handler_add(self
)
469 return {"RUNNING_MODAL"}
472 class UpdatePreviewKey(Operator
):
473 """Operator update preview keymap"""
475 bl_idname
= "wm.updatepreviewkey"
476 bl_label
= "Activate RMB"
479 def poll(cls
, context
):
480 conf
= context
.window_manager
.keyconfigs
.active
481 mapstr
= "Node Editor"
482 map = conf
.keymaps
[mapstr
]
484 map.keymap_items
["node.updatepreview"]
486 except BaseException
as e
:
488 print("An exception occurred: {}".format(e
))
491 def execute(self
, context
):
492 conf
= context
.window_manager
.keyconfigs
.active
493 mapstr
= "Node Editor"
494 map = conf
.keymaps
[mapstr
]
495 map.keymap_items
.new("node.updatepreview", type="RIGHTMOUSE", value
="PRESS")
499 class PovrayShaderNodeCategory(NodeCategory
):
501 def poll(cls
, context
):
502 return context
.space_data
.tree_type
== "ObjectNodeTree"
505 class PovrayTextureNodeCategory(NodeCategory
):
507 def poll(cls
, context
):
508 return context
.space_data
.tree_type
== "TextureNodeTree"
511 class PovraySceneNodeCategory(NodeCategory
):
513 def poll(cls
, context
):
514 return context
.space_data
.tree_type
== "CompositorNodeTree"
518 PovrayShaderNodeCategory("SHADEROUTPUT", "Output", items
=[NodeItem("PovrayOutputNode")]),
519 PovrayShaderNodeCategory("SIMPLE", "Simple texture", items
=[NodeItem("PovrayTextureNode")]),
520 PovrayShaderNodeCategory(
524 NodeItem("PovrayBumpMapNode"),
525 NodeItem("PovrayColorImageNode"),
526 NodeItem("ShaderNormalMapNode"),
527 NodeItem("PovraySlopeNode"),
528 NodeItem("ShaderTextureMapNode"),
529 NodeItem("ShaderNodeValToRGB"),
532 PovrayShaderNodeCategory(
535 items
=[NodeItem("PovrayImagePatternNode"), NodeItem("ShaderPatternNode")],
537 PovrayShaderNodeCategory("COLOR", "Color", items
=[NodeItem("PovrayPigmentNode")]),
538 PovrayShaderNodeCategory(
542 NodeItem("PovrayMappingNode"),
543 NodeItem("PovrayMultiplyNode"),
544 NodeItem("PovrayModifierNode"),
545 NodeItem("PovrayTransformNode"),
546 NodeItem("PovrayValueNode"),
549 PovrayShaderNodeCategory(
553 NodeItem("PovrayFinishNode"),
554 NodeItem("PovrayDiffuseNode"),
555 NodeItem("PovraySpecularNode"),
556 NodeItem("PovrayPhongNode"),
557 NodeItem("PovrayAmbientNode"),
558 NodeItem("PovrayMirrorNode"),
559 NodeItem("PovrayIridescenceNode"),
560 NodeItem("PovraySubsurfaceNode"),
563 PovrayShaderNodeCategory(
567 NodeItem("ShaderNodeAddShader"),
568 NodeItem("ShaderNodeAmbientOcclusion"),
569 NodeItem("ShaderNodeAttribute"),
570 NodeItem("ShaderNodeBackground"),
571 NodeItem("ShaderNodeBlackbody"),
572 NodeItem("ShaderNodeBrightContrast"),
573 NodeItem("ShaderNodeBsdfAnisotropic"),
574 NodeItem("ShaderNodeBsdfDiffuse"),
575 NodeItem("ShaderNodeBsdfGlass"),
576 NodeItem("ShaderNodeBsdfGlossy"),
577 NodeItem("ShaderNodeBsdfHair"),
578 NodeItem("ShaderNodeBsdfRefraction"),
579 NodeItem("ShaderNodeBsdfToon"),
580 NodeItem("ShaderNodeBsdfTranslucent"),
581 NodeItem("ShaderNodeBsdfTransparent"),
582 NodeItem("ShaderNodeBsdfVelvet"),
583 NodeItem("ShaderNodeBump"),
584 NodeItem("ShaderNodeCameraData"),
585 NodeItem("ShaderNodeCombineHSV"),
586 NodeItem("ShaderNodeCombineRGB"),
587 NodeItem("ShaderNodeCombineXYZ"),
588 NodeItem("ShaderNodeEmission"),
589 NodeItem("ShaderNodeExtendedMaterial"),
590 NodeItem("ShaderNodeFresnel"),
591 NodeItem("ShaderNodeGamma"),
592 NodeItem("ShaderNodeGeometry"),
593 NodeItem("ShaderNodeGroup"),
594 NodeItem("ShaderNodeHairInfo"),
595 NodeItem("ShaderNodeHoldout"),
596 NodeItem("ShaderNodeHueSaturation"),
597 NodeItem("ShaderNodeInvert"),
598 NodeItem("ShaderNodeLampData"),
599 NodeItem("ShaderNodeLayerWeight"),
600 NodeItem("ShaderNodeLightFalloff"),
601 NodeItem("ShaderNodeLightPath"),
602 NodeItem("ShaderNodeMapping"),
603 NodeItem("ShaderNodeMaterial"),
604 NodeItem("ShaderNodeMath"),
605 NodeItem("ShaderNodeMixRGB"),
606 NodeItem("ShaderNodeMixShader"),
607 NodeItem("ShaderNodeNewGeometry"),
608 NodeItem("ShaderNodeNormal"),
609 NodeItem("ShaderNodeNormalMap"),
610 NodeItem("ShaderNodeObjectInfo"),
611 NodeItem("ShaderNodeOutput"),
612 NodeItem("ShaderNodeOutputLamp"),
613 NodeItem("ShaderNodeOutputLineStyle"),
614 NodeItem("ShaderNodeOutputMaterial"),
615 NodeItem("ShaderNodeOutputWorld"),
616 NodeItem("ShaderNodeParticleInfo"),
617 NodeItem("ShaderNodeRGB"),
618 NodeItem("ShaderNodeRGBCurve"),
619 NodeItem("ShaderNodeRGBToBW"),
620 NodeItem("ShaderNodeScript"),
621 NodeItem("ShaderNodeSeparateHSV"),
622 NodeItem("ShaderNodeSeparateRGB"),
623 NodeItem("ShaderNodeSeparateXYZ"),
624 NodeItem("ShaderNodeSqueeze"),
625 NodeItem("ShaderNodeSubsurfaceScattering"),
626 NodeItem("ShaderNodeTangent"),
627 NodeItem("ShaderNodeTexBrick"),
628 NodeItem("ShaderNodeTexChecker"),
629 NodeItem("ShaderNodeTexCoord"),
630 NodeItem("ShaderNodeTexEnvironment"),
631 NodeItem("ShaderNodeTexGradient"),
632 NodeItem("ShaderNodeTexImage"),
633 NodeItem("ShaderNodeTexMagic"),
634 NodeItem("ShaderNodeTexMusgrave"),
635 NodeItem("ShaderNodeTexNoise"),
636 NodeItem("ShaderNodeTexPointDensity"),
637 NodeItem("ShaderNodeTexSky"),
638 NodeItem("ShaderNodeTexVoronoi"),
639 NodeItem("ShaderNodeTexWave"),
640 NodeItem("ShaderNodeTexture"),
641 NodeItem("ShaderNodeUVAlongStroke"),
642 NodeItem("ShaderNodeUVMap"),
643 NodeItem("ShaderNodeValToRGB"),
644 NodeItem("ShaderNodeValue"),
645 NodeItem("ShaderNodeVectorCurve"),
646 NodeItem("ShaderNodeVectorMath"),
647 NodeItem("ShaderNodeVectorTransform"),
648 NodeItem("ShaderNodeVolumeAbsorption"),
649 NodeItem("ShaderNodeVolumeScatter"),
650 NodeItem("ShaderNodeWavelength"),
651 NodeItem("ShaderNodeWireframe"),
654 PovrayTextureNodeCategory(
657 items
=[NodeItem("TextureNodeValToRGB"), NodeItem("TextureOutputNode")],
659 PovraySceneNodeCategory("ISOSURFACE", "Isosurface", items
=[NodeItem("IsoPropsNode")]),
660 PovraySceneNodeCategory("FOG", "Fog", items
=[NodeItem("PovrayFogNode")]),
662 # -------- end nodes init
667 PovraySocketUniversal
,
668 PovraySocketFloat_0_1
,
669 PovraySocketFloat_0_10
,
670 PovraySocketFloat_10
,
671 PovraySocketFloatPositive
,
672 PovraySocketFloat_000001_10
,
673 PovraySocketFloatUnlimited
,
675 PovraySocketInt_0_256
,
678 PovraySocketColorRGBFT
,
680 PovraySocketTransform
,
684 # PovrayShaderNodeCategory, # XXX SOMETHING BROKEN from 2.8 ?
685 # PovrayTextureNodeCategory, # XXX SOMETHING BROKEN from 2.8 ?
686 # PovraySceneNodeCategory, # XXX SOMETHING BROKEN from 2.8 ?
688 UpdatePreviewMaterial
,
694 nodeitems_utils
.register_node_categories("POVRAYNODES", node_categories
)
700 for cls
in reversed(classes
):
701 unregister_class(cls
)
702 nodeitems_utils
.unregister_node_categories("POVRAYNODES")