Sun position: store previous settings for HDRI sun syncing operator
[blender-addons.git] / archimesh / achm_tools.py
blob0527d33019f823801e2597e7fa42e399f4f697c3
1 # ##### BEGIN GPL LICENSE BLOCK #####
3 # This program is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU General Public License
5 # as published by the Free Software Foundation; either version 2
6 # of the License, or (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software Foundation,
15 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 # ##### END GPL LICENSE BLOCK #####
19 # <pep8 compliant>
21 # ----------------------------------------------------------
22 # support routines and general functions
23 # Author: Antonio Vazquez (antonioya)
25 # ----------------------------------------------------------
26 # noinspection PyUnresolvedReferences
27 import bpy
28 from os import path
31 # --------------------------------------------------------------------
32 # Get length Blender units
33 # --------------------------------------------------------------------
34 def get_blendunits(units):
35 if bpy.context.scene.unit_settings.system == "IMPERIAL":
36 return units * 0.3048
37 else:
38 return units
41 # --------------------------------------------------------------------
42 # Set normals
43 # True= faces to inside
44 # False= faces to outside
45 # --------------------------------------------------------------------
46 def set_normals(myobject, direction=False):
47 bpy.context.view_layer.objects.active = myobject
48 # go edit mode
49 bpy.ops.object.mode_set(mode='EDIT')
50 # select all faces
51 bpy.ops.mesh.select_all(action='SELECT')
52 # recalculate outside normals
53 bpy.ops.mesh.normals_make_consistent(inside=direction)
54 # go object mode again
55 bpy.ops.object.editmode_toggle()
58 # --------------------------------------------------------------------
59 # Remove doubles
60 # --------------------------------------------------------------------
61 def remove_doubles(myobject):
62 bpy.context.view_layer.objects.active = myobject
63 # go edit mode
64 bpy.ops.object.mode_set(mode='EDIT')
65 # select all faces
66 bpy.ops.mesh.select_all(action='SELECT')
67 # remove
68 bpy.ops.mesh.remove_doubles()
69 # go object mode again
70 bpy.ops.object.editmode_toggle()
73 # --------------------------------------------------------------------
74 # Set shade smooth
75 # --------------------------------------------------------------------
76 def set_smooth(myobject):
77 # deactivate others
78 for o in bpy.data.objects:
79 if o.select_get() is True:
80 o.select_set(False)
82 myobject.select_set(True)
83 bpy.context.view_layer.objects.active = myobject
84 if bpy.context.view_layer.objects.active.name == myobject.name:
85 bpy.ops.object.shade_smooth()
88 # --------------------------------------------------------------------
89 # Add modifier (subdivision)
90 # --------------------------------------------------------------------
91 def set_modifier_subsurf(myobject):
92 bpy.context.view_layer.objects.active = myobject
93 if bpy.context.view_layer.objects.active.name == myobject.name:
94 bpy.ops.object.modifier_add(type='SUBSURF')
95 for mod in myobject.modifiers:
96 if mod.type == 'SUBSURF':
97 mod.levels = 2
100 # --------------------------------------------------------------------
101 # Add modifier (mirror)
102 # --------------------------------------------------------------------
103 def set_modifier_mirror(myobject, axis="Y"):
104 bpy.ops.object.select_all(action='DESELECT')
105 myobject.select_set(True)
106 bpy.context.view_layer.objects.active = myobject
107 if bpy.context.view_layer.objects.active.name == myobject.name:
108 bpy.ops.object.modifier_add(type='MIRROR')
109 for mod in myobject.modifiers:
110 if mod.type == 'MIRROR':
111 if axis == "X":
112 mod.use_axis[0] = True
113 else:
114 mod.use__axis[0] = False
116 if axis == "Y":
117 mod.use_axis[1] = True
118 else:
119 mod.use_axis[1] = False
121 if axis == "Z":
122 mod.use_axis[2] = True
123 else:
124 mod.use_axis[2] = False
126 mod.use_clip = True
129 # --------------------------------------------------------------------
130 # Add modifier (array)
131 # --------------------------------------------------------------------
132 def set_modifier_array(myobject, axis, move, repeat, fix=False, fixmove=0, zmove=0):
133 bpy.ops.object.select_all(action='DESELECT')
134 myobject.select_set(True)
135 bpy.context.view_layer.objects.active = myobject
136 if bpy.context.view_layer.objects.active.name == myobject.name:
137 bpy.ops.object.modifier_add(type='ARRAY')
138 for mod in myobject.modifiers:
139 if mod.type == 'ARRAY':
140 if mod.name == "Array":
141 mod.name = "Array_" + axis
142 mod.count = repeat
143 mod.use_constant_offset = fix
144 if axis == "X":
145 mod.relative_offset_displace[0] = move
146 mod.constant_offset_displace[0] = fixmove
147 mod.relative_offset_displace[1] = 0.0
148 mod.constant_offset_displace[1] = 0.0
149 mod.relative_offset_displace[2] = 0.0
150 mod.constant_offset_displace[2] = zmove
152 if axis == "Y":
153 mod.relative_offset_displace[0] = 0.0
154 mod.constant_offset_displace[0] = 0.0
155 mod.relative_offset_displace[1] = move
156 mod.constant_offset_displace[1] = fixmove
157 mod.relative_offset_displace[2] = 0.0
158 mod.constant_offset_displace[2] = 0.0
161 # --------------------------------------------------------------------
162 # Add modifier (curve)
163 # --------------------------------------------------------------------
164 def set_modifier_curve(myobject, mycurve):
165 bpy.context.view_layer.objects.active = myobject
166 if bpy.context.view_layer.objects.active.name == myobject.name:
167 bpy.ops.object.modifier_add(type='CURVE')
168 for mod in myobject.modifiers:
169 if mod.type == 'CURVE':
170 mod.deform_axis = 'POS_X'
171 mod.object = mycurve
174 # --------------------------------------------------------------------
175 # Add modifier (solidify)
176 # --------------------------------------------------------------------
177 def set_modifier_solidify(myobject, width):
178 bpy.context.view_layer.objects.active = myobject
179 if bpy.context.view_layer.objects.active.name == myobject.name:
180 bpy.ops.object.modifier_add(type='SOLIDIFY')
181 for mod in myobject.modifiers:
182 if mod.type == 'SOLIDIFY':
183 mod.thickness = width
184 mod.use_even_offset = True
185 mod.use_quality_normals = True
186 break
189 # --------------------------------------------------------------------
190 # Add modifier (boolean)
191 # --------------------------------------------------------------------
192 def set_modifier_boolean(myobject, bolobject):
193 bpy.context.view_layer.objects.active = myobject
194 if bpy.context.view_layer.objects.active.name == myobject.name:
195 bpy.ops.object.modifier_add(type='BOOLEAN')
196 mod = myobject.modifiers[len(myobject.modifiers) - 1]
197 mod.operation = 'DIFFERENCE'
198 mod.object = bolobject
201 # --------------------------------------------------------------------
202 # Set material to object
203 # --------------------------------------------------------------------
204 def set_material(myobject, mymaterial):
205 bpy.context.view_layer.objects.active = myobject
206 if bpy.context.view_layer.objects.active.name == myobject.name:
207 myobject.data.materials.append(mymaterial)
210 # --------------------------------------------------------------------
211 # Set material to selected faces
212 # --------------------------------------------------------------------
213 def set_material_faces(myobject, idx):
214 bpy.context.view_layer.objects.active = myobject
215 myobject.select_set(True)
216 bpy.context.object.active_material_index = idx
217 if bpy.context.view_layer.objects.active.name == myobject.name:
218 bpy.ops.object.mode_set(mode='EDIT')
219 bpy.ops.object.material_slot_assign()
220 # Deselect
221 bpy.ops.mesh.select_all(action='DESELECT')
222 bpy.ops.object.mode_set(mode='OBJECT')
225 # --------------------------------------------------------------------
226 # Select faces
227 # --------------------------------------------------------------------
228 def select_faces(myobject, selface, clear):
229 myobject.select_set(True)
230 bpy.context.view_layer.objects.active = myobject
231 if bpy.context.view_layer.objects.active.name == myobject.name:
232 # deselect everything
233 if clear:
234 bpy.ops.object.mode_set(mode='EDIT')
235 bpy.ops.mesh.select_all(action='DESELECT')
237 # reselect the originally selected face
238 bpy.ops.object.mode_set(mode='OBJECT')
239 myobject.data.polygons[selface].select = True
242 # --------------------------------------------------------------------
243 # Select vertices
244 # --------------------------------------------------------------------
245 def select_vertices(myobject, selvertices, clear=True):
246 myobject.select_set(True)
247 bpy.context.view_layer.objects.active = myobject
248 if bpy.context.view_layer.objects.active.name == myobject.name:
249 # deselect everything
250 if clear:
251 bpy.ops.object.mode_set(mode='EDIT')
252 bpy.ops.mesh.select_all(action='DESELECT')
254 # Select Vertices
255 bpy.ops.object.mode_set(mode='EDIT', toggle=False)
256 sel_mode = bpy.context.tool_settings.mesh_select_mode
258 bpy.context.tool_settings.mesh_select_mode = [True, False, False]
259 bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
261 for i in selvertices:
262 myobject.data.vertices[i].select = True
264 bpy.ops.object.mode_set(mode='EDIT', toggle=False)
265 bpy.context.tool_settings.mesh_select_mode = sel_mode
266 bpy.ops.object.mode_set(mode='OBJECT')
269 # --------------------------------------------------------------------
270 # Mark Seam
271 # --------------------------------------------------------------------
272 def mark_seam(myobject):
273 # noinspection PyBroadException
274 try:
275 myobject.select_set(True)
276 bpy.context.view_layer.objects.active = myobject
277 if bpy.context.view_layer.objects.active.name == myobject.name:
278 bpy.ops.object.mode_set(mode='EDIT', toggle=False)
279 bpy.ops.mesh.mark_seam()
280 bpy.ops.object.mode_set(mode='OBJECT')
281 except:
282 bpy.ops.object.mode_set(mode='OBJECT')
285 # --------------------------------------------------------------------
286 # Unwrap mesh
287 # --------------------------------------------------------------------
288 def unwrap_mesh(myobject, allfaces=True):
289 # noinspection PyBroadException
290 try:
291 myobject.select_set(True)
292 bpy.context.view_layer.objects.active = myobject
293 if bpy.context.view_layer.objects.active.name == myobject.name:
294 # Unwrap
295 bpy.ops.object.mode_set(mode='EDIT', toggle=False)
296 if allfaces is True:
297 bpy.ops.mesh.select_all(action='DESELECT')
298 bpy.ops.mesh.select_all()
299 bpy.ops.uv.unwrap()
300 bpy.ops.object.mode_set(mode='OBJECT')
301 except:
302 bpy.ops.object.mode_set(mode='OBJECT')
305 # --------------------------------------------------------------------
306 # Get Node Index(multilanguage support)
307 # --------------------------------------------------------------------
308 def get_node_index(nodes, datatype):
309 idx = 0
310 for m in nodes:
311 if m.type == datatype:
312 return idx
313 idx += 1
315 # by default
316 return 1
319 # --------------------------------------------------------------------
320 # Create cycles diffuse material
321 # --------------------------------------------------------------------
322 def create_diffuse_material(matname, replace, r, g, b, rv=0.8, gv=0.8, bv=0.8, mix=0.1, twosides=False):
323 # Avoid duplicate materials
324 if replace is False:
325 matlist = bpy.data.materials
326 for m in matlist:
327 if m.name == matname:
328 return m
329 # Create material
330 mat = bpy.data.materials.new(matname)
331 mat.diffuse_color = (rv, gv, bv, 1.0) # viewport color
332 mat.use_nodes = True
333 nodes = mat.node_tree.nodes
335 # support for multilanguage
336 node = nodes.new('ShaderNodeBsdfDiffuse')
337 node.name = 'Diffuse BSDF'
338 node.inputs[0].default_value = [r, g, b, 1]
339 node.location = 200, 320
341 node = nodes.new('ShaderNodeBsdfGlossy')
342 node.name = 'Glossy_0'
343 node.location = 200, 0
345 node = nodes.new('ShaderNodeMixShader')
346 node.name = 'Mix_0'
347 node.inputs[0].default_value = mix
348 node.location = 500, 160
350 node = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')]
351 node.location = 1100, 160
353 # Connect nodes
354 outn = nodes['Diffuse BSDF'].outputs[0]
355 inn = nodes['Mix_0'].inputs[1]
356 mat.node_tree.links.new(outn, inn)
358 outn = nodes['Glossy_0'].outputs[0]
359 inn = nodes['Mix_0'].inputs[2]
360 mat.node_tree.links.new(outn, inn)
362 if twosides is False:
363 outn = nodes['Mix_0'].outputs[0]
364 inn = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')].inputs[0]
365 mat.node_tree.links.new(outn, inn)
367 if twosides is True:
368 node = nodes.new('ShaderNodeNewGeometry')
369 node.name = 'Input_1'
370 node.location = -80, -70
372 node = nodes.new('ShaderNodeBsdfDiffuse')
373 node.name = 'Diffuse_1'
374 node.inputs[0].default_value = [0.30, 0.30, 0.30, 1]
375 node.location = 200, -280
377 node = nodes.new('ShaderNodeMixShader')
378 node.name = 'Mix_1'
379 node.inputs[0].default_value = mix
380 node.location = 800, -70
382 outn = nodes['Input_1'].outputs[6]
383 inn = nodes['Mix_1'].inputs[0]
384 mat.node_tree.links.new(outn, inn)
386 outn = nodes['Diffuse_1'].outputs[0]
387 inn = nodes['Mix_1'].inputs[2]
388 mat.node_tree.links.new(outn, inn)
390 outn = nodes['Mix_0'].outputs[0]
391 inn = nodes['Mix_1'].inputs[1]
392 mat.node_tree.links.new(outn, inn)
394 outn = nodes['Mix_1'].outputs[0]
395 inn = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')].inputs[0]
396 mat.node_tree.links.new(outn, inn)
398 return mat
401 # --------------------------------------------------------------------
402 # Create cycles translucent material
403 # --------------------------------------------------------------------
404 def create_translucent_material(matname, replace, r, g, b, rv=0.8, gv=0.8, bv=0.8, mix=0.1):
405 # Avoid duplicate materials
406 if replace is False:
407 matlist = bpy.data.materials
408 for m in matlist:
409 if m.name == matname:
410 return m
411 # Create material
412 mat = bpy.data.materials.new(matname)
413 mat.diffuse_color = (rv, gv, bv, 1.0) # viewport color
414 mat.use_nodes = True
415 nodes = mat.node_tree.nodes
417 # support for multilanguage
418 node = nodes.new('ShaderNodeBsdfDiffuse')
419 node.name = 'Diffuse BSDF'
420 node.inputs[0].default_value = [r, g, b, 1]
421 node.location = 200, 320
423 node = nodes.new('ShaderNodeBsdfTranslucent')
424 node.name = 'Translucent_0'
425 node.location = 200, 0
427 node = nodes.new('ShaderNodeMixShader')
428 node.name = 'Mix_0'
429 node.inputs[0].default_value = mix
430 node.location = 500, 160
432 node = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')]
433 node.location = 1100, 160
435 # Connect nodes
436 outn = nodes['Diffuse BSDF'].outputs[0]
437 inn = nodes['Mix_0'].inputs[1]
438 mat.node_tree.links.new(outn, inn)
440 outn = nodes['Translucent_0'].outputs[0]
441 inn = nodes['Mix_0'].inputs[2]
442 mat.node_tree.links.new(outn, inn)
444 outn = nodes['Mix_0'].outputs[0]
445 inn = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')].inputs[0]
446 mat.node_tree.links.new(outn, inn)
448 return mat
451 # --------------------------------------------------------------------
452 # Create cycles glass material
453 # --------------------------------------------------------------------
454 def create_glass_material(matname, replace, rv=0.333, gv=0.342, bv=0.9):
455 # Avoid duplicate materials
456 if replace is False:
457 matlist = bpy.data.materials
458 for m in matlist:
459 if m.name == matname:
460 return m
461 # Create material
462 mat = bpy.data.materials.new(matname)
463 mat.use_nodes = True
464 mat.diffuse_color = (rv, gv, bv, 1.0)
465 nodes = mat.node_tree.nodes
467 # support for multilanguage
468 node = nodes[get_node_index(nodes, 'BSDF_DIFFUSE')]
469 mat.node_tree.nodes.remove(node) # remove not used
471 node = nodes.new('ShaderNodeLightPath')
472 node.name = 'Light_0'
473 node.location = 10, 160
475 node = nodes.new('ShaderNodeBsdfRefraction')
476 node.name = 'Refraction_0'
477 node.inputs[2].default_value = 1 # IOR 1.0
478 node.location = 250, 400
480 node = nodes.new('ShaderNodeBsdfGlossy')
481 node.name = 'Glossy_0'
482 node.distribution = 'SHARP'
483 node.location = 250, 100
485 node = nodes.new('ShaderNodeBsdfTransparent')
486 node.name = 'Transparent_0'
487 node.location = 500, 10
489 node = nodes.new('ShaderNodeMixShader')
490 node.name = 'Mix_0'
491 node.inputs[0].default_value = 0.035
492 node.location = 500, 160
494 node = nodes.new('ShaderNodeMixShader')
495 node.name = 'Mix_1'
496 node.inputs[0].default_value = 0.1
497 node.location = 690, 290
499 node = nodes.new('ShaderNodeOutputMaterial')
500 node.name = 'OUTPUT_MATERIAL'
501 node.location = 920, 290
503 # Connect nodes
504 outn = nodes['Light_0'].outputs[1]
505 inn = nodes['Mix_1'].inputs[0]
506 mat.node_tree.links.new(outn, inn)
508 outn = nodes['Refraction_0'].outputs[0]
509 inn = nodes['Mix_0'].inputs[1]
510 mat.node_tree.links.new(outn, inn)
512 outn = nodes['Glossy_0'].outputs[0]
513 inn = nodes['Mix_0'].inputs[2]
514 mat.node_tree.links.new(outn, inn)
516 outn = nodes['Mix_0'].outputs[0]
517 inn = nodes['Mix_1'].inputs[1]
518 mat.node_tree.links.new(outn, inn)
520 outn = nodes['Transparent_0'].outputs[0]
521 inn = nodes['Mix_1'].inputs[2]
522 mat.node_tree.links.new(outn, inn)
524 outn = nodes['Mix_1'].outputs[0]
525 inn = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')].inputs[0]
526 mat.node_tree.links.new(outn, inn)
528 return mat
531 # ---------------------------------------------
532 # Create cycles transparents material
533 # --------------------------------------------------------------------
534 def create_transparent_material(matname, replace, r=1, g=1, b=1, alpha=0):
535 # Avoid duplicate materials
536 if replace is False:
537 matlist = bpy.data.materials
538 for m in matlist:
539 if m.name == matname:
540 return m
541 # Create material
542 mat = bpy.data.materials.new(matname)
543 mat.use_nodes = True
544 mat.diffuse_color = (r, g, b, 1.0)
545 nodes = mat.node_tree.nodes
547 # support for multilanguage
548 node = nodes[get_node_index(nodes, 'BSDF_DIFFUSE')]
549 mat.node_tree.nodes.remove(node) # remove not used
551 node = nodes.new('ShaderNodeBsdfTransparent')
552 node.name = 'Transparent_0'
553 node.location = 250, 160
554 node.inputs[0].default_value = [r, g, b, alpha]
556 node = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')]
557 node.location = 700, 160
559 # Connect nodes
560 outn = nodes['Transparent_0'].outputs[0]
561 inn = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')].inputs[0]
562 mat.node_tree.links.new(outn, inn)
564 return mat
567 # --------------------------------------------------------------------
568 # Create cycles glossy material
569 # --------------------------------------------------------------------
570 def create_glossy_material(matname, replace, r, g, b, rv=0.578, gv=0.555, bv=0.736, rvalue=0.2):
571 # Avoid duplicate materials
572 if replace is False:
573 matlist = bpy.data.materials
574 for m in matlist:
575 if m.name == matname:
576 return m
577 # Create material
578 mat = bpy.data.materials.new(matname)
579 mat.use_nodes = True
580 mat.diffuse_color = (rv, gv, bv, 1.0)
581 nodes = mat.node_tree.nodes
583 # support for multilanguage
584 node = nodes[get_node_index(nodes, 'BSDF_DIFFUSE')]
585 mat.node_tree.nodes.remove(node) # remove not used
587 node = nodes.new('ShaderNodeBsdfGlossy')
588 node.name = 'Glossy_0'
589 node.inputs[0].default_value = [r, g, b, 1]
590 node.inputs[1].default_value = rvalue
591 node.location = 200, 160
593 node = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')]
594 node.location = 700, 160
596 # Connect nodes
597 outn = nodes['Glossy_0'].outputs[0]
598 inn = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')].inputs[0]
599 mat.node_tree.links.new(outn, inn)
601 return mat
604 # --------------------------------------------------------------------
605 # Create cycles emission material
606 # --------------------------------------------------------------------
607 def create_emission_material(matname, replace, r, g, b, energy):
608 # Avoid duplicate materials
609 if replace is False:
610 matlist = bpy.data.materials
611 for m in matlist:
612 if m.name == matname:
613 return m
614 # Create material
615 mat = bpy.data.materials.new(matname)
616 mat.use_nodes = True
617 nodes = mat.node_tree.nodes
619 # support for multilanguage
620 node = nodes[get_node_index(nodes, 'BSDF_DIFFUSE')]
621 mat.node_tree.nodes.remove(node) # remove not used
623 node = nodes.new('ShaderNodeEmission')
624 node.name = 'Emission_0'
625 node.inputs[0].default_value = [r, g, b, 1]
626 node.inputs[1].default_value = energy
627 node.location = 200, 160
629 node = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')]
630 node.location = 700, 160
632 # Connect nodes
633 outn = nodes['Emission_0'].outputs[0]
634 inn = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')].inputs[0]
635 mat.node_tree.links.new(outn, inn)
637 return mat
640 # --------------------------------------------------------------------
641 # Create cycles glass material
642 # --------------------------------------------------------------------
643 def create_old_glass_material(matname, replace, rv=0.352716, gv=0.760852, bv=0.9):
644 # Avoid duplicate materials
645 if replace is False:
646 matlist = bpy.data.materials
647 for m in matlist:
648 if m.name == matname:
649 return m
650 # Create material
651 mat = bpy.data.materials.new(matname)
652 mat.use_nodes = True
653 mat.diffuse_color = (rv, gv, bv, 1.0)
654 nodes = mat.node_tree.nodes
656 # support for multilanguage
657 node = nodes[get_node_index(nodes, 'BSDF_DIFFUSE')]
658 mat.node_tree.nodes.remove(node) # remove not used
660 node = nodes.new('ShaderNodeLightPath')
661 node.name = 'Light_0'
662 node.location = 10, 160
664 node = nodes.new('ShaderNodeBsdfGlass')
665 node.name = 'Glass_0'
666 node.location = 250, 300
668 node = nodes.new('ShaderNodeBsdfTransparent')
669 node.name = 'Transparent_0'
670 node.location = 250, 0
672 node = nodes.new('ShaderNodeMixShader')
673 node.name = 'Mix_0'
674 node.inputs[0].default_value = 0.1
675 node.location = 500, 160
677 node = nodes.new('ShaderNodeMixShader')
678 node.name = 'Mix_1'
679 node.inputs[0].default_value = 0.1
680 node.location = 690, 290
682 node = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')]
683 node.location = 920, 290
685 # Connect nodes
686 outn = nodes['Light_0'].outputs[1]
687 inn = nodes['Mix_0'].inputs[0]
688 mat.node_tree.links.new(outn, inn)
690 outn = nodes['Light_0'].outputs[2]
691 inn = nodes['Mix_1'].inputs[0]
692 mat.node_tree.links.new(outn, inn)
694 outn = nodes['Glass_0'].outputs[0]
695 inn = nodes['Mix_0'].inputs[1]
696 mat.node_tree.links.new(outn, inn)
698 outn = nodes['Transparent_0'].outputs[0]
699 inn = nodes['Mix_0'].inputs[2]
700 mat.node_tree.links.new(outn, inn)
702 outn = nodes['Mix_0'].outputs[0]
703 inn = nodes['Mix_1'].inputs[1]
704 mat.node_tree.links.new(outn, inn)
706 outn = nodes['Mix_1'].outputs[0]
707 inn = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')].inputs[0]
708 mat.node_tree.links.new(outn, inn)
710 return mat
713 # --------------------------------------------------------------------
714 # Create cycles brick texture material
715 # --------------------------------------------------------------------
716 def create_brick_material(matname, replace, r, g, b, rv=0.8, gv=0.636, bv=0.315):
717 # Avoid duplicate materials
718 if replace is False:
719 matlist = bpy.data.materials
720 for m in matlist:
721 if m.name == matname:
722 return m
723 # Create material
724 mat = bpy.data.materials.new(matname)
725 mat.use_nodes = True
726 mat.diffuse_color = (rv, gv, bv, 1.0)
727 nodes = mat.node_tree.nodes
729 # support for multilanguage
730 node = nodes[get_node_index(nodes, 'BSDF_DIFFUSE')]
731 node.name = 'Diffuse BSDF'
732 node.label = 'Diffuse BSDF'
734 node.inputs[0].default_value = [r, g, b, 1]
735 node.location = 500, 160
737 node = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')]
738 node.location = 700, 160
740 node = nodes.new('ShaderNodeTexBrick')
741 node.name = 'Brick_0'
742 node.inputs[3].default_value = [0.407, 0.411, 0.394, 1] # mortar color
743 node.inputs[4].default_value = 3 # scale
744 node.inputs[5].default_value = 0.001 # mortar
745 node.inputs[7].default_value = 0.60 # size_w
746 node.inputs[8].default_value = 0.30 # size_h
747 node.location = 300, 160
749 node = nodes.new('ShaderNodeRGB')
750 node.name = 'RGB_0'
751 node.outputs[0].default_value = [r, g, b, 1]
752 node.location = 70, 160
754 # Connect nodes
755 outn = nodes['RGB_0'].outputs['Color']
756 inn = nodes['Brick_0'].inputs['Color1']
757 mat.node_tree.links.new(outn, inn)
759 inn = nodes['Brick_0'].inputs['Color2']
760 mat.node_tree.links.new(outn, inn)
762 outn = nodes['Brick_0'].outputs['Color']
763 inn = nodes['Diffuse BSDF'].inputs['Color']
764 mat.node_tree.links.new(outn, inn)
766 return mat
769 # --------------------------------------------------------------------
770 # Create cycles fabric texture material
771 # --------------------------------------------------------------------
772 def create_fabric_material(matname, replace, r, g, b, rv=0.8, gv=0.636, bv=0.315):
773 # Avoid duplicate materials
774 if replace is False:
775 matlist = bpy.data.materials
776 for m in matlist:
777 if m.name == matname:
778 return m
779 # Create material
780 mat = bpy.data.materials.new(matname)
781 mat.use_nodes = True
782 mat.diffuse_color = (rv, gv, bv, 1.0)
783 nodes = mat.node_tree.nodes
785 # support for multilanguage
786 node = nodes.new('ShaderNodeBsdfDiffuse')
787 node.name = 'Diffuse BSDF'
788 node.inputs[0].default_value = [r, g, b, 1]
789 node.location = 810, 270
791 node = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')]
792 node.location = 1210, 320
794 node = nodes.new('ShaderNodeTexCoord')
795 node.name = 'UVCoordinates'
796 node.location = 26, 395
798 node = nodes.new('ShaderNodeMapping')
799 node.name = 'UVMapping'
800 node.location = 266, 380
801 node.inputs['Scale'].default_value[0] = 1000
802 node.inputs['Scale'].default_value[1] = 1000
803 node.inputs['Scale'].default_value[2] = 1000
805 # ===========================================================================
806 # Image texture
807 # ===========================================================================
808 # Load image file.
810 realpath = path.join(path.dirname(__file__), "images", "fabric_diffuse.png")
811 print("Loading: " + realpath)
812 try:
813 img = bpy.data.images.load(realpath)
814 except:
815 raise NameError("Cannot load image %s" % realpath)
817 # Create image texture from image
818 ctex = bpy.data.textures.new('ColorTex', type='IMAGE')
819 ctex.image = img
821 node = nodes.new('ShaderNodeTexImage')
822 node.name = 'Image1'
823 node.image = ctex.image
824 node.location = 615, 350
826 node = nodes.new('ShaderNodeBsdfTransparent')
827 node.name = 'Transparent1'
828 node.location = 810, 395
829 node.inputs[0].default_value = [r, g, b, 1]
831 node = nodes.new('ShaderNodeAddShader')
832 node.name = 'Add1'
833 node.location = 1040, 356
835 # Connect nodes
836 outn = nodes['UVCoordinates'].outputs['UV']
837 inn = nodes['UVMapping'].inputs['Vector']
838 mat.node_tree.links.new(outn, inn)
840 outn = nodes['UVMapping'].outputs['Vector']
841 inn = nodes['Image1'].inputs['Vector']
842 mat.node_tree.links.new(outn, inn)
844 outn = nodes['Image1'].outputs['Color']
845 inn = nodes['Diffuse BSDF'].inputs['Color']
846 mat.node_tree.links.new(outn, inn)
848 outn = nodes['Transparent1'].outputs['BSDF']
849 inn = nodes['Add1'].inputs[0]
850 mat.node_tree.links.new(outn, inn)
852 outn = nodes['Diffuse BSDF'].outputs['BSDF']
853 inn = nodes['Add1'].inputs[1]
854 mat.node_tree.links.new(outn, inn)
856 outn = nodes['Add1'].outputs['Shader']
857 inn = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')].inputs[0]
858 mat.node_tree.links.new(outn, inn)
860 return mat
863 # --------------------------------------------------------------------
864 # Copy bin file
865 # --------------------------------------------------------------------
866 def copy_binfile(fromfile, tofile):
867 with open(fromfile, 'rb') as f1:
868 with open(tofile, 'wb') as f2:
869 while True:
870 mybytes = f1.read(1024)
871 if mybytes:
872 f2.write(mybytes)
873 else:
874 break
877 # --------------------------------------------------------------------
878 # Parent object (keep positions)
879 # --------------------------------------------------------------------
880 def parentobject(parentobj, childobj):
881 # noinspection PyBroadException
882 try:
883 bpy.ops.object.select_all(action='DESELECT')
884 bpy.context.view_layer.objects.active = parentobj
885 parentobj.select_set(True)
886 childobj.select_set(True)
887 bpy.ops.object.parent_set(type='OBJECT', keep_transform=False)
888 return True
889 except:
890 return False
893 # ------------------------------------------------------------------------------
894 # Create control box
896 # objName: Object name
897 # x: size x axis
898 # y: size y axis
899 # z: size z axis
900 # tube: True create a tube, False only sides
901 # ------------------------------------------------------------------------------
902 def create_control_box(objname, x, y, z, tube=True):
903 myvertex = [(-x / 2, 0, 0.0),
904 (-x / 2, y, 0.0),
905 (x / 2, y, 0.0),
906 (x / 2, 0, 0.0),
907 (-x / 2, 0, z),
908 (-x / 2, y, z),
909 (x / 2, y, z),
910 (x / 2, 0, z)]
912 if tube is True:
913 myfaces = [(0, 1, 2, 3), (0, 4, 5, 1), (1, 5, 6, 2), (3, 7, 4, 0), (2, 6, 7, 3), (5, 4, 7, 6)]
914 else:
915 myfaces = [(0, 4, 5, 1), (2, 6, 7, 3)]
917 mesh = bpy.data.meshes.new(objname)
918 myobject = bpy.data.objects.new(objname, mesh)
920 myobject.location = bpy.context.scene.cursor.location
921 bpy.context.collection.objects.link(myobject)
923 mesh.from_pydata(myvertex, [], myfaces)
924 mesh.update(calc_edges=True)
926 return myobject
929 # ------------------------------------------------------------------------------
930 # Remove all children objects
931 # ------------------------------------------------------------------------------
932 def remove_children(myobject):
933 # Remove children
934 for child in myobject.children:
935 # noinspection PyBroadException
936 try:
937 # noinspection PyBroadException
938 try:
939 # remove child relationship
940 for grandchild in child.children:
941 grandchild.parent = None
942 # remove modifiers
943 for mod in child.modifiers:
944 bpy.ops.object.modifier_remove(name=mod.name)
945 except:
946 pass
947 # clear child data
948 if child.type == 'MESH':
949 old = child.data
950 child.select_set(True)
951 bpy.ops.object.delete()
952 bpy.data.meshes.remove(old)
953 if child.type == 'CURVE':
954 child.select_set(True)
955 bpy.ops.object.delete()
956 except:
957 pass
960 # --------------------------------------------------------------------
961 # Get all parents
962 # --------------------------------------------------------------------
963 def get_allparents(myobj):
964 obj = myobj
965 mylist = []
966 while obj.parent is not None:
967 mylist.append(obj)
968 objp = obj.parent
969 obj = objp
971 mylist.append(obj)
973 return mylist
976 # --------------------------------------------------------------------
977 # Verify all faces are in vertice group to avoid Blander crash
979 # Review the faces array and remove any vertex out of the range
980 # this avoid any bug that can appear avoiding Blender crash
981 # --------------------------------------------------------------------
982 def check_mesh_errors(myvertices, myfaces):
983 vmax = len(myvertices)
985 f = 0
986 for face in myfaces:
987 for v in face:
988 if v < 0 or v > vmax:
989 print("Face=" + str(f) + "->removed vertex=" + str(v))
990 myfaces[f].remove(v)
991 f += 1
993 return myfaces