1 # SPDX-FileCopyrightText: 2022 Blender Foundation
3 # SPDX-License-Identifier: GPL-2.0-or-later
5 """"Nodes based User interface for shaders exported to POV textures."""
8 from bpy
.utils
import register_class
, unregister_class
9 from bpy
.types
import NodeSocket
, Operator
10 from bpy
.props
import (
14 import nodeitems_utils
15 from nodeitems_utils
import NodeCategory
, NodeItem
17 # ---------------------------------------------------------------- #
19 # ---------------------------------------------------------------- #
20 # -------- Parent node class
21 class ObjectNodeTree(bpy
.types
.NodeTree
):
22 """Povray Material Nodes"""
24 bl_idname
= "ObjectNodeTree"
25 bl_label
= "Povray Object Nodes"
29 def poll(cls
, context
):
30 return context
.scene
.render
.engine
== "POVRAY_RENDER"
33 def get_from_context(cls
, context
):
34 ob
= context
.active_object
35 if ob
and ob
.type != 'LIGHT':
36 ma
= ob
.active_material
38 nt_name
= ma
.node_tree
40 return nt_name
, ma
, ma
41 return (None, None, None)
47 # -------- Sockets classes
48 class PovraySocketUniversal(NodeSocket
):
49 bl_idname
= "PovraySocketUniversal"
50 bl_label
= "Povray Socket"
51 value_unlimited
: bpy
.props
.FloatProperty(default
=0.0)
52 value_0_1
: bpy
.props
.FloatProperty(min=0.0, max=1.0, default
=0.0)
53 value_0_10
: bpy
.props
.FloatProperty(min=0.0, max=10.0, default
=0.0)
54 value_000001_10
: bpy
.props
.FloatProperty(min=0.000001, max=10.0, default
=0.0)
55 value_1_9
: bpy
.props
.IntProperty(min=1, max=9, default
=1)
56 value_0_255
: bpy
.props
.IntProperty(min=0, max=255, default
=0)
57 percent
: bpy
.props
.FloatProperty(min=0.0, max=100.0, default
=0.0)
59 def draw(self
, context
, layout
, node
, text
):
60 space
= context
.space_data
61 tree
= space
.edit_tree
66 # inps = link.to_node.inputs
68 inp
for inp
in link
.to_node
.inputs
if link
.from_node
== node
and inp
.is_linked
70 for inp
in linked_inps
:
71 if inp
.bl_idname
== "PovraySocketFloat_0_1":
72 if (prop
:= "value_0_1") not in value
:
74 if inp
.bl_idname
== "PovraySocketFloat_000001_10":
75 if (prop
:= "value_000001_10") not in value
:
77 if inp
.bl_idname
== "PovraySocketFloat_0_10":
78 if (prop
:= "value_0_10") not in value
:
80 if inp
.bl_idname
== "PovraySocketInt_1_9":
81 if (prop
:= "value_1_9") not in value
:
83 if inp
.bl_idname
== "PovraySocketInt_0_255":
84 if (prop
:= "value_0_255") not in value
:
86 if inp
.bl_idname
== "PovraySocketFloatUnlimited":
87 if (prop
:= "value_unlimited") not in value
:
90 layout
.prop(self
, "%s" % value
[0], text
=text
)
92 layout
.prop(self
, "percent", text
="Percent")
94 layout
.prop(self
, "percent", text
=text
)
96 def draw_color(self
, context
, node
):
100 class PovraySocketFloat_0_1(NodeSocket
):
101 bl_idname
= "PovraySocketFloat_0_1"
102 bl_label
= "Povray Socket"
103 default_value
: bpy
.props
.FloatProperty(
104 description
="Input node Value_0_1", min=0, max=1, default
=0
107 def draw(self
, context
, layout
, node
, text
):
109 layout
.label(text
=text
)
111 layout
.prop(self
, "default_value", text
=text
, slider
=True)
113 def draw_color(self
, context
, node
):
114 return (0.5, 0.7, 0.7, 1)
117 class PovraySocketFloat_0_10(NodeSocket
):
118 bl_idname
= "PovraySocketFloat_0_10"
119 bl_label
= "Povray Socket"
120 default_value
: bpy
.props
.FloatProperty(
121 description
="Input node Value_0_10", min=0, max=10, default
=0
124 def draw(self
, context
, layout
, node
, text
):
125 if node
.bl_idname
== "ShaderNormalMapNode" and node
.inputs
[2].is_linked
:
126 layout
.label(text
="")
127 self
.hide_value
= True
129 layout
.label(text
=text
)
131 layout
.prop(self
, "default_value", text
=text
, slider
=True)
133 def draw_color(self
, context
, node
):
134 return (0.65, 0.65, 0.65, 1)
137 class PovraySocketFloat_10(NodeSocket
):
138 bl_idname
= "PovraySocketFloat_10"
139 bl_label
= "Povray Socket"
140 default_value
: bpy
.props
.FloatProperty(
141 description
="Input node Value_10", min=-10, max=10, default
=0
144 def draw(self
, context
, layout
, node
, text
):
145 if node
.bl_idname
== "ShaderNormalMapNode" and node
.inputs
[2].is_linked
:
146 layout
.label(text
="")
147 self
.hide_value
= True
149 layout
.label(text
=text
)
151 layout
.prop(self
, "default_value", text
=text
, slider
=True)
153 def draw_color(self
, context
, node
):
154 return (0.65, 0.65, 0.65, 1)
157 class PovraySocketFloatPositive(NodeSocket
):
158 bl_idname
= "PovraySocketFloatPositive"
159 bl_label
= "Povray Socket"
160 default_value
: bpy
.props
.FloatProperty(
161 description
="Input Node Value Positive", min=0.0, default
=0
164 def draw(self
, context
, layout
, node
, text
):
166 layout
.label(text
=text
)
168 layout
.prop(self
, "default_value", text
=text
, slider
=True)
170 def draw_color(self
, context
, node
):
171 return (0.045, 0.005, 0.136, 1)
174 class PovraySocketFloat_000001_10(NodeSocket
):
175 bl_idname
= "PovraySocketFloat_000001_10"
176 bl_label
= "Povray Socket"
177 default_value
: bpy
.props
.FloatProperty(min=0.000001, max=10, default
=0.000001)
179 def draw(self
, context
, layout
, node
, text
):
180 if self
.is_output
or self
.is_linked
:
181 layout
.label(text
=text
)
183 layout
.prop(self
, "default_value", text
=text
, slider
=True)
185 def draw_color(self
, context
, node
):
189 class PovraySocketFloatUnlimited(NodeSocket
):
190 bl_idname
= "PovraySocketFloatUnlimited"
191 bl_label
= "Povray Socket"
192 default_value
: bpy
.props
.FloatProperty(default
=0.0)
194 def draw(self
, context
, layout
, node
, text
):
196 layout
.label(text
=text
)
198 layout
.prop(self
, "default_value", text
=text
, slider
=True)
200 def draw_color(self
, context
, node
):
201 return (0.7, 0.7, 1, 1)
204 class PovraySocketInt_1_9(NodeSocket
):
205 bl_idname
= "PovraySocketInt_1_9"
206 bl_label
= "Povray Socket"
207 default_value
: bpy
.props
.IntProperty(
208 description
="Input node Value_1_9", min=1, max=9, default
=6
211 def draw(self
, context
, layout
, node
, text
):
213 layout
.label(text
=text
)
215 layout
.prop(self
, "default_value", text
=text
)
217 def draw_color(self
, context
, node
):
218 return (1, 0.7, 0.7, 1)
221 class PovraySocketInt_0_256(NodeSocket
):
222 bl_idname
= "PovraySocketInt_0_256"
223 bl_label
= "Povray Socket"
224 default_value
: bpy
.props
.IntProperty(min=0, max=255, default
=0)
226 def draw(self
, context
, layout
, node
, text
):
228 layout
.label(text
=text
)
230 layout
.prop(self
, "default_value", text
=text
)
232 def draw_color(self
, context
, node
):
233 return (0.5, 0.5, 0.5, 1)
236 class PovraySocketPattern(NodeSocket
):
237 bl_idname
= "PovraySocketPattern"
238 bl_label
= "Povray Socket"
240 default_value
: bpy
.props
.EnumProperty(
242 description
="Select the pattern",
244 ("boxed", "Boxed", ""),
245 ("brick", "Brick", ""),
246 ("cells", "Cells", ""),
247 ("checker", "Checker", ""),
248 ("granite", "Granite", ""),
249 ("leopard", "Leopard", ""),
250 ("marble", "Marble", ""),
251 ("onion", "Onion", ""),
252 ("planar", "Planar", ""),
253 ("quilted", "Quilted", ""),
254 ("ripples", "Ripples", ""),
255 ("radial", "Radial", ""),
256 ("spherical", "Spherical", ""),
257 ("spotted", "Spotted", ""),
258 ("waves", "Waves", ""),
259 ("wood", "Wood", ""),
260 ("wrinkles", "Wrinkles", ""),
265 def draw(self
, context
, layout
, node
, text
):
266 if self
.is_output
or self
.is_linked
:
267 layout
.label(text
="Pattern")
269 layout
.prop(self
, "default_value", text
=text
)
271 def draw_color(self
, context
, node
):
275 class PovraySocketColor(NodeSocket
):
276 bl_idname
= "PovraySocketColor"
277 bl_label
= "Povray Socket"
279 default_value
: FloatVectorProperty(
284 default
=(0.0, 0.0, 0.0),
285 options
={"ANIMATABLE"},
289 def draw(self
, context
, layout
, node
, text
):
290 if self
.is_output
or self
.is_linked
:
291 layout
.label(text
=text
)
293 layout
.prop(self
, "default_value", text
=text
)
295 def draw_color(self
, context
, node
):
299 class PovraySocketColorRGBFT(NodeSocket
):
300 bl_idname
= "PovraySocketColorRGBFT"
301 bl_label
= "Povray Socket"
303 default_value
: FloatVectorProperty(
308 default
=(0.0, 0.0, 0.0),
309 options
={"ANIMATABLE"},
312 f
: bpy
.props
.FloatProperty(default
=0.0, min=0.0, max=1.0)
313 t
: bpy
.props
.FloatProperty(default
=0.0, min=0.0, max=1.0)
315 def draw(self
, context
, layout
, node
, text
):
316 if self
.is_output
or self
.is_linked
:
317 layout
.label(text
=text
)
319 layout
.prop(self
, "default_value", text
=text
)
321 def draw_color(self
, context
, node
):
325 class PovraySocketTexture(NodeSocket
):
326 bl_idname
= "PovraySocketTexture"
327 bl_label
= "Povray Socket"
328 default_value
: bpy
.props
.IntProperty()
330 def draw(self
, context
, layout
, node
, text
):
331 layout
.label(text
=text
)
333 def draw_color(self
, context
, node
):
337 class PovraySocketTransform(NodeSocket
):
338 bl_idname
= "PovraySocketTransform"
339 bl_label
= "Povray Socket"
340 default_value
: bpy
.props
.IntProperty(min=0, max=255, default
=0)
342 def draw(self
, context
, layout
, node
, text
):
343 layout
.label(text
=text
)
345 def draw_color(self
, context
, node
):
346 return (99 / 255, 99 / 255, 199 / 255, 1)
349 class PovraySocketNormal(NodeSocket
):
350 bl_idname
= "PovraySocketNormal"
351 bl_label
= "Povray Socket"
352 default_value
: bpy
.props
.IntProperty(min=0, max=255, default
=0)
354 def draw(self
, context
, layout
, node
, text
):
355 layout
.label(text
=text
)
357 def draw_color(self
, context
, node
):
358 return (0.65, 0.65, 0.65, 1)
361 class PovraySocketSlope(NodeSocket
):
362 bl_idname
= "PovraySocketSlope"
363 bl_label
= "Povray Socket"
364 default_value
: bpy
.props
.FloatProperty(min=0.0, max=1.0)
365 height
: bpy
.props
.FloatProperty(min=0.0, max=10.0)
366 slope
: bpy
.props
.FloatProperty(min=-10.0, max=10.0)
368 def draw(self
, context
, layout
, node
, text
):
369 if self
.is_output
or self
.is_linked
:
370 layout
.label(text
=text
)
372 layout
.prop(self
, "default_value", text
="")
373 layout
.prop(self
, "height", text
="")
374 layout
.prop(self
, "slope", text
="")
376 def draw_color(self
, context
, node
):
380 class PovraySocketMap(NodeSocket
):
381 bl_idname
= "PovraySocketMap"
382 bl_label
= "Povray Socket"
383 default_value
: bpy
.props
.StringProperty()
385 def draw(self
, context
, layout
, node
, text
):
386 layout
.label(text
=text
)
388 def draw_color(self
, context
, node
):
389 return (0.2, 0, 0.2, 1)
392 class PovrayPatternNode(Operator
):
393 bl_idname
= "pov.patternnode"
398 def execute(self
, context
):
399 space
= context
.space_data
400 tree
= space
.edit_tree
401 for node
in tree
.nodes
:
404 tmap
= tree
.nodes
.new("ShaderNodeValToRGB")
405 tmap
.label
= "Pattern"
406 for inp
in tmap
.inputs
:
407 tmap
.inputs
.remove(inp
)
408 for outp
in tmap
.outputs
:
409 tmap
.outputs
.remove(outp
)
410 pattern
= tmap
.inputs
.new("PovraySocketPattern", "Pattern")
411 pattern
.hide_value
= True
412 mapping
= tmap
.inputs
.new("NodeSocketVector", "Mapping")
413 mapping
.hide_value
= True
414 transform
= tmap
.inputs
.new("NodeSocketVector", "Transform")
415 transform
.hide_value
= True
416 modifier
= tmap
.inputs
.new("NodeSocketVector", "Modifier")
417 modifier
.hide_value
= True
418 for i
in range(0, 2):
419 tmap
.inputs
.new("NodeSocketShader", "%s" % (i
+ 1))
420 tmap
.outputs
.new("NodeSocketShader", "Material")
421 tmap
.outputs
.new("NodeSocketColor", "Color")
422 tree
.nodes
.active
= tmap
424 aNode
= tree
.nodes
.active
426 v2d
= context
.region
.view2d
427 x
, y
= v2d
.region_to_view(self
.x
, self
.y
)
428 aNode
.location
= (x
, y
)
430 def modal(self
, context
, event
):
431 if event
.type == "MOUSEMOVE":
432 self
.x
= event
.mouse_region_x
433 self
.y
= event
.mouse_region_y
434 self
.execute(context
)
435 return {"RUNNING_MODAL"}
436 if event
.type == "LEFTMOUSE":
438 if event
.type in ("RIGHTMOUSE", "ESC"):
441 return {"RUNNING_MODAL"}
443 def invoke(self
, context
, event
):
444 context
.window_manager
.modal_handler_add(self
)
445 return {"RUNNING_MODAL"}
448 class UpdatePreviewMaterial(Operator
):
449 """Operator update preview material"""
451 bl_idname
= "node.updatepreview"
452 bl_label
= "Update preview"
454 def execute(self
, context
):
455 scene
= context
.view_layer
457 for obj
in scene
.objects
:
459 scene
.objects
.active
= ob
461 scene
.objects
.active
= ob
463 def modal(self
, context
, event
):
464 if event
.type == "RIGHTMOUSE":
465 self
.execute(context
)
467 return {"PASS_THROUGH"}
469 def invoke(self
, context
, event
):
470 context
.window_manager
.modal_handler_add(self
)
471 return {"RUNNING_MODAL"}
474 class UpdatePreviewKey(Operator
):
475 """Operator update preview keymap"""
477 bl_idname
= "wm.updatepreviewkey"
478 bl_label
= "Activate RMB"
481 def poll(cls
, context
):
482 conf
= context
.window_manager
.keyconfigs
.active
483 mapstr
= "Node Editor"
484 map = conf
.keymaps
[mapstr
]
486 map.keymap_items
["node.updatepreview"]
488 except BaseException
as e
:
490 print("An exception occurred: {}".format(e
))
493 def execute(self
, context
):
494 conf
= context
.window_manager
.keyconfigs
.active
495 mapstr
= "Node Editor"
496 map = conf
.keymaps
[mapstr
]
497 map.keymap_items
.new("node.updatepreview", type="RIGHTMOUSE", value
="PRESS")
501 class PovrayShaderNodeCategory(NodeCategory
):
503 def poll(cls
, context
):
504 return context
.space_data
.tree_type
== "ObjectNodeTree"
507 class PovrayTextureNodeCategory(NodeCategory
):
509 def poll(cls
, context
):
510 return context
.space_data
.tree_type
== "TextureNodeTree"
513 class PovraySceneNodeCategory(NodeCategory
):
515 def poll(cls
, context
):
516 return context
.space_data
.tree_type
== "CompositorNodeTree"
520 PovrayShaderNodeCategory("SHADEROUTPUT", "Output", items
=[NodeItem("PovrayOutputNode")]),
521 PovrayShaderNodeCategory("SIMPLE", "Simple texture", items
=[NodeItem("PovrayTextureNode")]),
522 PovrayShaderNodeCategory(
526 NodeItem("PovrayBumpMapNode"),
527 NodeItem("PovrayColorImageNode"),
528 NodeItem("ShaderNormalMapNode"),
529 NodeItem("PovraySlopeNode"),
530 NodeItem("ShaderTextureMapNode"),
531 NodeItem("ShaderNodeValToRGB"),
534 PovrayShaderNodeCategory(
537 items
=[NodeItem("PovrayImagePatternNode"), NodeItem("ShaderPatternNode")],
539 PovrayShaderNodeCategory("COLOR", "Color", items
=[NodeItem("PovrayPigmentNode")]),
540 PovrayShaderNodeCategory(
544 NodeItem("PovrayMappingNode"),
545 NodeItem("PovrayMultiplyNode"),
546 NodeItem("PovrayModifierNode"),
547 NodeItem("PovrayTransformNode"),
548 NodeItem("PovrayValueNode"),
551 PovrayShaderNodeCategory(
555 NodeItem("PovrayFinishNode"),
556 NodeItem("PovrayDiffuseNode"),
557 NodeItem("PovraySpecularNode"),
558 NodeItem("PovrayPhongNode"),
559 NodeItem("PovrayAmbientNode"),
560 NodeItem("PovrayMirrorNode"),
561 NodeItem("PovrayIridescenceNode"),
562 NodeItem("PovraySubsurfaceNode"),
565 PovrayShaderNodeCategory(
569 NodeItem("ShaderNodeAddShader"),
570 NodeItem("ShaderNodeAmbientOcclusion"),
571 NodeItem("ShaderNodeAttribute"),
572 NodeItem("ShaderNodeBackground"),
573 NodeItem("ShaderNodeBlackbody"),
574 NodeItem("ShaderNodeBrightContrast"),
575 NodeItem("ShaderNodeBsdfAnisotropic"),
576 NodeItem("ShaderNodeBsdfDiffuse"),
577 NodeItem("ShaderNodeBsdfGlass"),
578 NodeItem("ShaderNodeBsdfGlossy"),
579 NodeItem("ShaderNodeBsdfHair"),
580 NodeItem("ShaderNodeBsdfRefraction"),
581 NodeItem("ShaderNodeBsdfToon"),
582 NodeItem("ShaderNodeBsdfTranslucent"),
583 NodeItem("ShaderNodeBsdfTransparent"),
584 NodeItem("ShaderNodeBsdfVelvet"),
585 NodeItem("ShaderNodeBump"),
586 NodeItem("ShaderNodeCameraData"),
587 NodeItem("ShaderNodeCombineHSV"),
588 NodeItem("ShaderNodeCombineRGB"),
589 NodeItem("ShaderNodeCombineXYZ"),
590 NodeItem("ShaderNodeEmission"),
591 NodeItem("ShaderNodeExtendedMaterial"),
592 NodeItem("ShaderNodeFresnel"),
593 NodeItem("ShaderNodeGamma"),
594 NodeItem("ShaderNodeGeometry"),
595 NodeItem("ShaderNodeGroup"),
596 NodeItem("ShaderNodeHairInfo"),
597 NodeItem("ShaderNodeHoldout"),
598 NodeItem("ShaderNodeHueSaturation"),
599 NodeItem("ShaderNodeInvert"),
600 NodeItem("ShaderNodeLampData"),
601 NodeItem("ShaderNodeLayerWeight"),
602 NodeItem("ShaderNodeLightFalloff"),
603 NodeItem("ShaderNodeLightPath"),
604 NodeItem("ShaderNodeMapping"),
605 NodeItem("ShaderNodeMaterial"),
606 NodeItem("ShaderNodeMath"),
607 NodeItem("ShaderNodeMixRGB"),
608 NodeItem("ShaderNodeMixShader"),
609 NodeItem("ShaderNodeNewGeometry"),
610 NodeItem("ShaderNodeNormal"),
611 NodeItem("ShaderNodeNormalMap"),
612 NodeItem("ShaderNodeObjectInfo"),
613 NodeItem("ShaderNodeOutput"),
614 NodeItem("ShaderNodeOutputLamp"),
615 NodeItem("ShaderNodeOutputLineStyle"),
616 NodeItem("ShaderNodeOutputMaterial"),
617 NodeItem("ShaderNodeOutputWorld"),
618 NodeItem("ShaderNodeParticleInfo"),
619 NodeItem("ShaderNodeRGB"),
620 NodeItem("ShaderNodeRGBCurve"),
621 NodeItem("ShaderNodeRGBToBW"),
622 NodeItem("ShaderNodeScript"),
623 NodeItem("ShaderNodeSeparateHSV"),
624 NodeItem("ShaderNodeSeparateRGB"),
625 NodeItem("ShaderNodeSeparateXYZ"),
626 NodeItem("ShaderNodeSqueeze"),
627 NodeItem("ShaderNodeSubsurfaceScattering"),
628 NodeItem("ShaderNodeTangent"),
629 NodeItem("ShaderNodeTexBrick"),
630 NodeItem("ShaderNodeTexChecker"),
631 NodeItem("ShaderNodeTexCoord"),
632 NodeItem("ShaderNodeTexEnvironment"),
633 NodeItem("ShaderNodeTexGradient"),
634 NodeItem("ShaderNodeTexImage"),
635 NodeItem("ShaderNodeTexMagic"),
636 NodeItem("ShaderNodeTexMusgrave"),
637 NodeItem("ShaderNodeTexNoise"),
638 NodeItem("ShaderNodeTexPointDensity"),
639 NodeItem("ShaderNodeTexSky"),
640 NodeItem("ShaderNodeTexVoronoi"),
641 NodeItem("ShaderNodeTexWave"),
642 NodeItem("ShaderNodeTexture"),
643 NodeItem("ShaderNodeUVAlongStroke"),
644 NodeItem("ShaderNodeUVMap"),
645 NodeItem("ShaderNodeValToRGB"),
646 NodeItem("ShaderNodeValue"),
647 NodeItem("ShaderNodeVectorCurve"),
648 NodeItem("ShaderNodeVectorMath"),
649 NodeItem("ShaderNodeVectorTransform"),
650 NodeItem("ShaderNodeVolumeAbsorption"),
651 NodeItem("ShaderNodeVolumeScatter"),
652 NodeItem("ShaderNodeWavelength"),
653 NodeItem("ShaderNodeWireframe"),
656 PovrayTextureNodeCategory(
659 items
=[NodeItem("TextureNodeValToRGB"), NodeItem("TextureOutputNode")],
661 PovraySceneNodeCategory("ISOSURFACE", "Isosurface", items
=[NodeItem("IsoPropsNode")]),
662 PovraySceneNodeCategory("FOG", "Fog", items
=[NodeItem("PovrayFogNode")]),
664 # -------- end nodes init
669 PovraySocketUniversal
,
670 PovraySocketFloat_0_1
,
671 PovraySocketFloat_0_10
,
672 PovraySocketFloat_10
,
673 PovraySocketFloatPositive
,
674 PovraySocketFloat_000001_10
,
675 PovraySocketFloatUnlimited
,
677 PovraySocketInt_0_256
,
680 PovraySocketColorRGBFT
,
682 PovraySocketTransform
,
686 # PovrayShaderNodeCategory, # XXX SOMETHING BROKEN from 2.8 ?
687 # PovrayTextureNodeCategory, # XXX SOMETHING BROKEN from 2.8 ?
688 # PovraySceneNodeCategory, # XXX SOMETHING BROKEN from 2.8 ?
690 UpdatePreviewMaterial
,
696 nodeitems_utils
.register_node_categories("POVRAYNODES", node_categories
)
702 for cls
in reversed(classes
):
703 unregister_class(cls
)
704 nodeitems_utils
.unregister_node_categories("POVRAYNODES")