1 # SPDX-License-Identifier: GPL-2.0-or-later
3 """Translate complex shaders to exported POV textures."""
7 # WARNING!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
8 def write_nodes(pov_mat_name
, ntree
, file):
9 """Translate Blender node trees to pov and write them to file."""
10 # such function local inlined import are official guidelines
11 # of Blender Foundation to lighten addons footprint at startup
13 from .render
import string_strip_hyphen
16 scene
= bpy
.context
.scene
17 for node
in ntree
.nodes
:
18 pov_node_name
= string_strip_hyphen(bpy
.path
.clean_name(node
.name
)) + "_%s" % pov_mat_name
19 if node
.bl_idname
== "PovrayFinishNode" and node
.outputs
["Finish"].is_linked
:
20 file.write("#declare %s = finish {\n" % pov_node_name
)
21 emission
= node
.inputs
["Emission"].default_value
22 if node
.inputs
["Emission"].is_linked
:
24 file.write(" emission %.4g\n" % emission
)
25 for link
in ntree
.links
:
26 if link
.to_node
== node
:
28 if link
.from_node
.bl_idname
== "PovrayDiffuseNode":
33 if link
.from_node
.inputs
["Intensity"].is_linked
:
36 intensity
= link
.from_node
.inputs
["Intensity"].default_value
37 if link
.from_node
.inputs
["Albedo"].is_linked
:
40 if link
.from_node
.inputs
["Albedo"].default_value
:
42 file.write(" diffuse %s %.4g\n" % (albedo
, intensity
))
43 if link
.from_node
.inputs
["Brilliance"].is_linked
:
46 brilliance
= link
.from_node
.inputs
["Brilliance"].default_value
47 file.write(" brilliance %.4g\n" % brilliance
)
48 if link
.from_node
.inputs
["Crand"].is_linked
:
51 crand
= link
.from_node
.inputs
["Crand"].default_value
53 file.write(" crand %.4g\n" % crand
)
55 if link
.from_node
.bl_idname
== "PovraySubsurfaceNode":
56 if scene
.povray
.sslt_enable
:
59 if link
.from_node
.inputs
["Translucency"].is_linked
:
62 r
, g
, b
, a
= link
.from_node
.inputs
["Translucency"].default_value
[:]
63 if link
.from_node
.inputs
["Energy"].is_linked
:
66 energy
= link
.from_node
.inputs
["Energy"].default_value
68 " subsurface { translucency <%.4g,%.4g,%.4g>*%s }\n"
72 if link
.from_node
.bl_idname
in {"PovraySpecularNode", "PovrayPhongNode"}:
78 highlight
= "specular"
79 if link
.from_node
.inputs
["Intensity"].is_linked
:
82 intensity
= link
.from_node
.inputs
["Intensity"].default_value
84 if link
.from_node
.inputs
["Albedo"].is_linked
:
87 if link
.from_node
.inputs
["Albedo"].default_value
:
89 if link
.from_node
.bl_idname
in {"PovrayPhongNode"}:
91 file.write(" %s %s %.4g\n" % (highlight
, albedo
, intensity
))
93 if link
.from_node
.bl_idname
in {"PovraySpecularNode"}:
94 if link
.from_node
.inputs
["Roughness"].is_linked
:
97 roughness
= link
.from_node
.inputs
["Roughness"].default_value
98 file.write(" roughness %.6g\n" % roughness
)
100 if link
.from_node
.bl_idname
in {"PovrayPhongNode"}:
101 if link
.from_node
.inputs
["Size"].is_linked
:
104 phong_size
= link
.from_node
.inputs
["Size"].default_value
105 file.write(" phong_size %s\n" % phong_size
)
107 if link
.from_node
.inputs
["Metallic"].is_linked
:
110 metallic
= link
.from_node
.inputs
["Metallic"].default_value
111 file.write(" metallic %.4g\n" % metallic
)
113 if link
.from_node
.bl_idname
in {"PovrayMirrorNode"}:
114 file.write(" reflection {\n")
121 if link
.from_node
.inputs
["Color"].is_linked
:
124 color
= link
.from_node
.inputs
["Color"].default_value
[:]
125 file.write(" <%.4g,%.4g,%.4g>\n" % (color
[0], color
[1], color
[2]))
127 if link
.from_node
.inputs
["Exponent"].is_linked
:
130 exponent
= link
.from_node
.inputs
["Exponent"].default_value
131 file.write(" exponent %.4g\n" % exponent
)
133 if link
.from_node
.inputs
["Falloff"].is_linked
:
136 falloff
= link
.from_node
.inputs
["Falloff"].default_value
137 file.write(" falloff %.4g\n" % falloff
)
139 if link
.from_node
.inputs
["Metallic"].is_linked
:
142 metallic
= link
.from_node
.inputs
["Metallic"].default_value
143 file.write(" metallic %.4g" % metallic
)
145 if link
.from_node
.inputs
["Fresnel"].is_linked
:
148 if link
.from_node
.inputs
["Fresnel"].default_value
:
151 if link
.from_node
.inputs
["Conserve energy"].is_linked
:
154 if link
.from_node
.inputs
["Conserve energy"].default_value
:
155 conserve
= "conserve_energy"
157 file.write(" %s}\n %s\n" % (fresnel
, conserve
))
159 if link
.from_node
.bl_idname
== "PovrayAmbientNode":
161 if link
.from_node
.inputs
["Ambient"].is_linked
:
164 ambient
= link
.from_node
.inputs
["Ambient"].default_value
[:]
165 file.write(" ambient <%.4g,%.4g,%.4g>\n" % ambient
)
167 if link
.from_node
.bl_idname
in {"PovrayIridescenceNode"}:
168 file.write(" irid {\n")
172 if link
.from_node
.inputs
["Amount"].is_linked
:
175 amount
= link
.from_node
.inputs
["Amount"].default_value
176 file.write(" %.4g\n" % amount
)
178 if link
.from_node
.inputs
["Thickness"].is_linked
:
181 exponent
= link
.from_node
.inputs
["Thickness"].default_value
182 file.write(" thickness %.4g\n" % thickness
)
184 if link
.from_node
.inputs
["Turbulence"].is_linked
:
187 falloff
= link
.from_node
.inputs
["Turbulence"].default_value
188 file.write(" turbulence %.4g}\n" % turbulence
)
192 for node
in ntree
.nodes
:
193 pov_node_name
= string_strip_hyphen(bpy
.path
.clean_name(node
.name
)) + "_%s" % pov_mat_name
194 if node
.bl_idname
== "PovrayTransformNode" and node
.outputs
["Transform"].is_linked
:
195 tx
= node
.inputs
["Translate x"].default_value
196 ty
= node
.inputs
["Translate y"].default_value
197 tz
= node
.inputs
["Translate z"].default_value
198 rx
= node
.inputs
["Rotate x"].default_value
199 ry
= node
.inputs
["Rotate y"].default_value
200 rz
= node
.inputs
["Rotate z"].default_value
201 sx
= node
.inputs
["Scale x"].default_value
202 sy
= node
.inputs
["Scale y"].default_value
203 sz
= node
.inputs
["Scale z"].default_value
205 "#declare %s = transform {\n"
206 " translate<%.4g,%.4g,%.4g>\n"
207 " rotate<%.4g,%.4g,%.4g>\n"
208 " scale<%.4g,%.4g,%.4g>}\n" % (pov_node_name
, tx
, ty
, tz
, rx
, ry
, rz
, sx
, sy
, sz
)
211 for node
in ntree
.nodes
:
212 pov_node_name
= string_strip_hyphen(bpy
.path
.clean_name(node
.name
)) + "_%s" % pov_mat_name
213 if node
.bl_idname
== "PovrayColorImageNode" and node
.outputs
["Pigment"].is_linked
:
214 declare_nodes
.append(node
.name
)
216 file.write("#declare %s = pigment { color rgb 0.8}\n" % pov_node_name
)
218 im
= bpy
.data
.images
[node
.image
]
219 if im
.filepath
and path
.exists(bpy
.path
.abspath(im
.filepath
)): # (os.path)
221 for link
in ntree
.links
:
223 link
.from_node
.bl_idname
== "PovrayTransformNode"
224 and link
.to_node
== node
227 string_strip_hyphen(bpy
.path
.clean_name(link
.from_node
.name
))
228 + "_%s" % pov_mat_name
230 transform
= "transform {%s}" % pov_trans_name
232 if node
.map_type
== "uv_mapping":
234 filepath
= bpy
.path
.abspath(im
.filepath
)
235 file.write("#declare %s = pigment {%s image_map {\n" % (pov_node_name
, uv
))
237 if node
.premultiplied
:
243 ' "%s"\n gamma %.6g\n premultiplied %s\n'
244 % (filepath
, node
.inputs
["Gamma"].default_value
, premul
)
246 file.write(" %s\n" % once
)
247 if node
.map_type
!= "uv_mapping":
248 file.write(" map_type %s\n" % node
.map_type
)
250 " interpolate %s\n filter all %.4g\n transmit all %.4g\n"
253 node
.inputs
["Filter"].default_value
,
254 node
.inputs
["Transmit"].default_value
,
258 file.write(" %s\n" % transform
)
261 for node
in ntree
.nodes
:
262 pov_node_name
= string_strip_hyphen(bpy
.path
.clean_name(node
.name
)) + "_%s" % pov_mat_name
263 if node
.bl_idname
== "PovrayImagePatternNode" and node
.outputs
["Pattern"].is_linked
:
264 declare_nodes
.append(node
.name
)
266 im
= bpy
.data
.images
[node
.image
]
267 if im
.filepath
and path
.exists(bpy
.path
.abspath(im
.filepath
)):
269 for link
in ntree
.links
:
271 link
.from_node
.bl_idname
== "PovrayTransformNode"
272 and link
.to_node
== node
275 string_strip_hyphen(bpy
.path
.clean_name(link
.from_node
.name
))
276 + "_%s" % pov_mat_name
278 transform
= "transform {%s}" % pov_trans_name
280 if node
.map_type
== "uv_mapping":
282 filepath
= bpy
.path
.abspath(im
.filepath
)
283 file.write("#macro %s() %s image_pattern {\n" % (pov_node_name
, uv
))
285 if node
.premultiplied
:
291 ' "%s"\n gamma %.6g\n premultiplied %s\n'
292 % (filepath
, node
.inputs
["Gamma"].default_value
, premul
)
294 file.write(" %s\n" % once
)
295 if node
.map_type
!= "uv_mapping":
296 file.write(" map_type %s\n" % node
.map_type
)
297 file.write(" interpolate %s\n" % node
.interpolate
)
299 file.write(" %s\n" % transform
)
302 for node
in ntree
.nodes
:
303 pov_node_name
= string_strip_hyphen(bpy
.path
.clean_name(node
.name
)) + "_%s" % pov_mat_name
304 if node
.bl_idname
== "PovrayBumpMapNode" and node
.outputs
["Normal"].is_linked
:
306 im
= bpy
.data
.images
[node
.image
]
307 if im
.filepath
and path
.exists(bpy
.path
.abspath(im
.filepath
)):
309 for link
in ntree
.links
:
311 link
.from_node
.bl_idname
== "PovrayTransformNode"
312 and link
.to_node
== node
315 string_strip_hyphen(bpy
.path
.clean_name(link
.from_node
.name
))
316 + "_%s" % pov_mat_name
318 transform
= "transform {%s}" % pov_trans_name
320 if node
.map_type
== "uv_mapping":
322 filepath
= bpy
.path
.abspath(im
.filepath
)
323 file.write("#declare %s = normal {%s bump_map {\n" % (pov_node_name
, uv
))
327 file.write(' "%s"\n' % filepath
)
328 file.write(" %s\n" % once
)
329 if node
.map_type
!= "uv_mapping":
330 file.write(" map_type %s\n" % node
.map_type
)
331 bump_size
= node
.inputs
["Normal"].default_value
332 if node
.inputs
["Normal"].is_linked
:
335 " interpolate %s\n bump_size %.4g\n" % (node
.interpolate
, bump_size
)
338 file.write(" %s\n" % transform
)
340 declare_nodes
.append(node
.name
)
342 for node
in ntree
.nodes
:
343 pov_node_name
= string_strip_hyphen(bpy
.path
.clean_name(node
.name
)) + "_%s" % pov_mat_name
344 if node
.bl_idname
== "PovrayPigmentNode" and node
.outputs
["Pigment"].is_linked
:
345 declare_nodes
.append(node
.name
)
346 r
, g
, b
= node
.inputs
["Color"].default_value
[:]
347 f
= node
.inputs
["Filter"].default_value
348 t
= node
.inputs
["Transmit"].default_value
349 if node
.inputs
["Color"].is_linked
:
352 "#declare %s = pigment{color srgbft <%.4g,%.4g,%.4g,%.4g,%.4g>}\n"
353 % (pov_node_name
, r
, g
, b
, f
, t
)
356 for node
in ntree
.nodes
:
357 pov_node_name
= string_strip_hyphen(bpy
.path
.clean_name(node
.name
)) + "_%s" % pov_mat_name
358 if node
.bl_idname
== "PovrayTextureNode" and node
.outputs
["Texture"].is_linked
:
359 declare_nodes
.append(node
.name
)
360 r
, g
, b
= node
.inputs
["Pigment"].default_value
[:]
361 pov_col_name
= "color rgb <%.4g,%.4g,%.4g>" % (r
, g
, b
)
362 if node
.inputs
["Pigment"].is_linked
:
363 for link
in ntree
.links
:
364 if link
.to_node
== node
and link
.to_socket
.name
== "Pigment":
366 string_strip_hyphen(bpy
.path
.clean_name(link
.from_node
.name
))
367 + "_%s" % pov_mat_name
369 file.write("#declare %s = texture{\n pigment{%s}\n" % (pov_node_name
, pov_col_name
))
370 if node
.inputs
["Normal"].is_linked
:
371 for link
in ntree
.links
:
374 and link
.to_socket
.name
== "Normal"
375 and link
.from_node
.name
in declare_nodes
378 string_strip_hyphen(bpy
.path
.clean_name(link
.from_node
.name
))
379 + "_%s" % pov_mat_name
381 file.write(" normal{%s}\n" % pov_nor_name
)
382 if node
.inputs
["Finish"].is_linked
:
383 for link
in ntree
.links
:
384 if link
.to_node
== node
and link
.to_socket
.name
== "Finish":
386 string_strip_hyphen(bpy
.path
.clean_name(link
.from_node
.name
))
387 + "_%s" % pov_mat_name
389 file.write(" finish{%s}\n" % pov_fin_name
)
391 declare_nodes
.append(node
.name
)
393 for i
in range(0, len(ntree
.nodes
)):
394 for node
in ntree
.nodes
:
395 if node
.bl_idname
in {"ShaderNodeGroup", "ShaderTextureMapNode"}:
396 for output
in node
.outputs
:
398 output
.name
== "Texture"
400 and (node
.name
not in declare_nodes
)
403 for link
in ntree
.links
:
404 if link
.to_node
== node
and link
.to_socket
.name
not in {
411 if link
.from_node
.name
not in declare_nodes
:
415 string_strip_hyphen(bpy
.path
.clean_name(node
.name
))
416 + "_%s" % pov_mat_name
420 for link
in ntree
.links
:
423 and link
.from_node
.bl_idname
== "PovrayMappingNode"
424 and link
.from_node
.warp_type
!= "NONE"
426 w_type
= link
.from_node
.warp_type
427 if w_type
== "uv_mapping":
431 if w_type
== "toroidal":
434 % link
.from_node
.warp_tor_major_radius
436 orient
= link
.from_node
.warp_orientation
437 exp
= link
.from_node
.warp_dist_exp
438 warp
= "warp{%s orientation %s dist_exp %.4g %s}" % (
444 if link
.from_node
.warp_type
== "planar":
445 warp
= "warp{%s %s %.4g}" % (w_type
, orient
, exp
)
446 if link
.from_node
.warp_type
== "cubic":
447 warp
= "warp{%s}" % w_type
448 file.write("#declare %s = texture {%s\n" % (pov_node_name
, uv
))
449 pattern
= node
.inputs
[0].default_value
451 if node
.inputs
[0].is_linked
:
452 for link
in ntree
.links
:
455 and link
.from_node
.bl_idname
== "ShaderPatternNode"
457 # ------------ advanced ------------------------- #
459 pattern
= lfn
.pattern
460 if pattern
== "agate":
461 advanced
= "agate_turb %.4g" % lfn
.agate_turb
462 if pattern
== "crackle":
463 advanced
= "form <%.4g,%.4g,%.4g>" % (
468 advanced
+= " metric %.4g" % lfn
.crackle_metric
469 if lfn
.crackle_solid
:
471 if pattern
in {"spiral1", "spiral2"}:
472 advanced
= "%.4g" % lfn
.spiral_arms
473 if pattern
in {"tiling"}:
474 advanced
= "%.4g" % lfn
.tiling_number
475 if pattern
in {"gradient"}:
476 advanced
= "%s" % lfn
.gradient_orient
479 and link
.from_node
.bl_idname
== "PovrayImagePatternNode"
483 bpy
.path
.clean_name(link
.from_node
.name
)
485 + "_%s" % pov_mat_name
487 pattern
= "%s()" % pov_macro_name
488 file.write(" %s %s %s\n" % (pattern
, advanced
, warp
))
491 for link
in ntree
.links
:
494 and link
.from_node
.bl_idname
== "PovrayMultiplyNode"
496 if link
.from_node
.amount_x
> 1:
497 repeat
+= "warp{repeat %.4g * x}" % link
.from_node
.amount_x
498 if link
.from_node
.amount_y
> 1:
499 repeat
+= " warp{repeat %.4g * y}" % link
.from_node
.amount_y
500 if link
.from_node
.amount_z
> 1:
501 repeat
+= " warp{repeat %.4g * z}" % link
.from_node
.amount_z
504 for link
in ntree
.links
:
507 and link
.from_node
.bl_idname
== "PovrayTransformNode"
511 bpy
.path
.clean_name(link
.from_node
.name
)
513 + "_%s" % pov_mat_name
515 transform
= "transform {%s}" % pov_trans_name
525 for link
in ntree
.links
:
528 and link
.from_node
.bl_idname
== "PovrayModifierNode"
531 if link
.from_node
.inputs
["Turb X"].is_linked
:
534 x
= link
.from_node
.inputs
["Turb X"].default_value
536 if link
.from_node
.inputs
["Turb Y"].is_linked
:
539 y
= link
.from_node
.inputs
["Turb Y"].default_value
541 if link
.from_node
.inputs
["Turb Z"].is_linked
:
544 z
= link
.from_node
.inputs
["Turb Z"].default_value
546 if link
.from_node
.inputs
["Octaves"].is_linked
:
549 d
= link
.from_node
.inputs
["Octaves"].default_value
551 if link
.from_node
.inputs
["Lambda"].is_linked
:
554 e
= link
.from_node
.inputs
["Lambda"].default_value
556 if link
.from_node
.inputs
["Omega"].is_linked
:
559 f
= link
.from_node
.inputs
["Omega"].default_value
561 if link
.from_node
.inputs
["Frequency"].is_linked
:
564 g
= link
.from_node
.inputs
["Frequency"].default_value
566 if link
.from_node
.inputs
["Phase"].is_linked
:
569 h
= link
.from_node
.inputs
["Phase"].default_value
571 turb
= "turbulence <%.4g,%.4g,%.4g>" % (x
, y
, z
)
572 octv
= "octaves %s" % d
573 lmbd
= "lambda %.4g" % e
574 omg
= "omega %.4g" % f
575 freq
= "frequency %.4g" % g
576 pha
= "phase %.4g" % h
586 file.write(" texture_map {\n")
587 if node
.inputs
["Color ramp"].is_linked
:
588 for link
in ntree
.links
:
591 and link
.from_node
.bl_idname
== "ShaderNodeValToRGB"
593 els
= link
.from_node
.color_ramp
.elements
597 pov_in_mat_name
= string_strip_hyphen(
598 bpy
.path
.clean_name(link
.from_node
.name
)
599 ) + "_%s_%s" % (n
, pov_mat_name
)
601 for ilink
in ntree
.links
:
603 ilink
.to_node
== node
604 and ilink
.to_socket
.name
== str(n
)
613 + "_%s" % pov_mat_name
616 r
, g
, b
, a
= el
.color
[:]
618 " #declare %s = texture{"
620 "color srgbt <%.4g,%.4g,%.4g,%.4g>}};\n"
621 % (pov_in_mat_name
, r
, g
, b
, 1 - a
)
624 " [%s %s]\n" % (el
.position
, pov_in_mat_name
)
627 els
= [[0, 0, 0, 0], [1, 1, 1, 1]]
628 for t
in range(0, 2):
629 pov_in_mat_name
= string_strip_hyphen(
630 bpy
.path
.clean_name(link
.from_node
.name
)
631 ) + "_%s_%s" % (t
, pov_mat_name
)
633 for ilink
in ntree
.links
:
634 if ilink
.to_node
== node
and ilink
.to_socket
.name
== str(t
):
638 bpy
.path
.clean_name(ilink
.from_node
.name
)
640 + "_%s" % pov_mat_name
643 r
, g
, b
= els
[t
][1], els
[t
][2], els
[t
][3]
652 " #declare %s = texture{pigment{color rgb <%.4g,%.4g,%.4g>}};\n"
653 % (pov_in_mat_name
, r
, g
, b
)
657 " texture{pigment{color rgb <%.4g,%.4g,%.4g>}}\n"
667 file.write(" [%s %s]\n" % (els
[t
][0], pov_in_mat_name
))
670 file.write(" texture{%s}\n" % pov_in_mat_name
)
679 if pattern
== "brick":
681 "brick_size <%.4g, %.4g, %.4g> mortar %.4g \n"
689 file.write(" %s %s" % (repeat
, transform
))
692 " %s %s %s %s %s %s" % (turb
, octv
, lmbd
, omg
, freq
, pha
)
695 declare_nodes
.append(node
.name
)
697 for link
in ntree
.links
:
698 if link
.to_node
.bl_idname
== "PovrayOutputNode" and link
.from_node
.name
in declare_nodes
:
699 pov_mat_node_name
= (
700 string_strip_hyphen(bpy
.path
.clean_name(link
.from_node
.name
)) + "_%s" % pov_mat_name
702 file.write("#declare %s = %s\n" % (pov_mat_name
, pov_mat_node_name
))