Sun Position: replace DMS label by the Enter Coordinates field
[blender-addons.git] / archimesh / achm_tools.py
blob81f0a7bd0d978b26a4c7b27d2b803d6d0a629c30
1 # SPDX-License-Identifier: GPL-2.0-or-later
3 # ----------------------------------------------------------
4 # support routines and general functions
5 # Author: Antonio Vazquez (antonioya)
7 # ----------------------------------------------------------
8 # noinspection PyUnresolvedReferences
9 import bpy
10 from os import path
13 # --------------------------------------------------------------------
14 # Get length Blender units
15 # --------------------------------------------------------------------
16 def get_blendunits(units):
17 if bpy.context.scene.unit_settings.system == "IMPERIAL":
18 return units * 0.3048
19 else:
20 return units
23 # --------------------------------------------------------------------
24 # Set normals
25 # True= faces to inside
26 # False= faces to outside
27 # --------------------------------------------------------------------
28 def set_normals(myobject, direction=False):
29 bpy.context.view_layer.objects.active = myobject
30 # go edit mode
31 bpy.ops.object.mode_set(mode='EDIT')
32 # select all faces
33 bpy.ops.mesh.select_all(action='SELECT')
34 # recalculate outside normals
35 bpy.ops.mesh.normals_make_consistent(inside=direction)
36 # go object mode again
37 bpy.ops.object.editmode_toggle()
40 # --------------------------------------------------------------------
41 # Remove doubles
42 # --------------------------------------------------------------------
43 def remove_doubles(myobject):
44 bpy.context.view_layer.objects.active = myobject
45 # go edit mode
46 bpy.ops.object.mode_set(mode='EDIT')
47 # select all faces
48 bpy.ops.mesh.select_all(action='SELECT')
49 # remove
50 bpy.ops.mesh.remove_doubles()
51 # go object mode again
52 bpy.ops.object.editmode_toggle()
55 # --------------------------------------------------------------------
56 # Set shade smooth
57 # --------------------------------------------------------------------
58 def set_smooth(myobject):
59 # deactivate others
60 for o in bpy.data.objects:
61 if o.select_get() is True:
62 o.select_set(False)
64 myobject.select_set(True)
65 bpy.context.view_layer.objects.active = myobject
66 if bpy.context.view_layer.objects.active.name == myobject.name:
67 bpy.ops.object.shade_smooth()
70 # --------------------------------------------------------------------
71 # Add modifier (subdivision)
72 # --------------------------------------------------------------------
73 def set_modifier_subsurf(myobject):
74 bpy.context.view_layer.objects.active = myobject
75 if bpy.context.view_layer.objects.active.name == myobject.name:
76 bpy.ops.object.modifier_add(type='SUBSURF')
77 for mod in myobject.modifiers:
78 if mod.type == 'SUBSURF':
79 mod.levels = 2
82 # --------------------------------------------------------------------
83 # Add modifier (mirror)
84 # --------------------------------------------------------------------
85 def set_modifier_mirror(myobject, axis="Y"):
86 bpy.ops.object.select_all(action='DESELECT')
87 myobject.select_set(True)
88 bpy.context.view_layer.objects.active = myobject
89 if bpy.context.view_layer.objects.active.name == myobject.name:
90 bpy.ops.object.modifier_add(type='MIRROR')
91 for mod in myobject.modifiers:
92 if mod.type == 'MIRROR':
93 if axis == "X":
94 mod.use_axis[0] = True
95 else:
96 mod.use__axis[0] = False
98 if axis == "Y":
99 mod.use_axis[1] = True
100 else:
101 mod.use_axis[1] = False
103 if axis == "Z":
104 mod.use_axis[2] = True
105 else:
106 mod.use_axis[2] = False
108 mod.use_clip = True
111 # --------------------------------------------------------------------
112 # Add modifier (array)
113 # --------------------------------------------------------------------
114 def set_modifier_array(myobject, axis, move, repeat, fix=False, fixmove=0, zmove=0):
115 bpy.ops.object.select_all(action='DESELECT')
116 myobject.select_set(True)
117 bpy.context.view_layer.objects.active = myobject
118 if bpy.context.view_layer.objects.active.name == myobject.name:
119 bpy.ops.object.modifier_add(type='ARRAY')
120 for mod in myobject.modifiers:
121 if mod.type == 'ARRAY':
122 if mod.name == "Array":
123 mod.name = "Array_" + axis
124 mod.count = repeat
125 mod.use_constant_offset = fix
126 if axis == "X":
127 mod.relative_offset_displace[0] = move
128 mod.constant_offset_displace[0] = fixmove
129 mod.relative_offset_displace[1] = 0.0
130 mod.constant_offset_displace[1] = 0.0
131 mod.relative_offset_displace[2] = 0.0
132 mod.constant_offset_displace[2] = zmove
134 if axis == "Y":
135 mod.relative_offset_displace[0] = 0.0
136 mod.constant_offset_displace[0] = 0.0
137 mod.relative_offset_displace[1] = move
138 mod.constant_offset_displace[1] = fixmove
139 mod.relative_offset_displace[2] = 0.0
140 mod.constant_offset_displace[2] = 0.0
143 # --------------------------------------------------------------------
144 # Add modifier (curve)
145 # --------------------------------------------------------------------
146 def set_modifier_curve(myobject, mycurve):
147 bpy.context.view_layer.objects.active = myobject
148 if bpy.context.view_layer.objects.active.name == myobject.name:
149 bpy.ops.object.modifier_add(type='CURVE')
150 for mod in myobject.modifiers:
151 if mod.type == 'CURVE':
152 mod.deform_axis = 'POS_X'
153 mod.object = mycurve
156 # --------------------------------------------------------------------
157 # Add modifier (solidify)
158 # --------------------------------------------------------------------
159 def set_modifier_solidify(myobject, width):
160 bpy.context.view_layer.objects.active = myobject
161 if bpy.context.view_layer.objects.active.name == myobject.name:
162 bpy.ops.object.modifier_add(type='SOLIDIFY')
163 for mod in myobject.modifiers:
164 if mod.type == 'SOLIDIFY':
165 mod.thickness = width
166 mod.use_even_offset = True
167 mod.use_quality_normals = True
168 break
171 # --------------------------------------------------------------------
172 # Add modifier (boolean)
173 # --------------------------------------------------------------------
174 def set_modifier_boolean(myobject, bolobject):
175 boolean_modifier = myobject.modifiers.new("", 'BOOLEAN')
176 boolean_modifier.operation = 'DIFFERENCE'
177 boolean_modifier.object = bolobject
180 # --------------------------------------------------------------------
181 # Set material to object
182 # --------------------------------------------------------------------
183 def set_material(myobject, mymaterial):
184 bpy.context.view_layer.objects.active = myobject
185 if bpy.context.view_layer.objects.active.name == myobject.name:
186 myobject.data.materials.append(mymaterial)
189 # --------------------------------------------------------------------
190 # Set material to selected faces
191 # --------------------------------------------------------------------
192 def set_material_faces(myobject, idx):
193 bpy.context.view_layer.objects.active = myobject
194 myobject.select_set(True)
195 bpy.context.object.active_material_index = idx
196 if bpy.context.view_layer.objects.active.name == myobject.name:
197 bpy.ops.object.mode_set(mode='EDIT')
198 bpy.ops.object.material_slot_assign()
199 # Deselect
200 bpy.ops.mesh.select_all(action='DESELECT')
201 bpy.ops.object.mode_set(mode='OBJECT')
204 # --------------------------------------------------------------------
205 # Select faces
206 # --------------------------------------------------------------------
207 def select_faces(myobject, selface, clear):
208 myobject.select_set(True)
209 bpy.context.view_layer.objects.active = myobject
210 if bpy.context.view_layer.objects.active.name == myobject.name:
211 # deselect everything
212 if clear:
213 bpy.ops.object.mode_set(mode='EDIT')
214 bpy.ops.mesh.select_all(action='DESELECT')
216 # reselect the originally selected face
217 bpy.ops.object.mode_set(mode='OBJECT')
218 myobject.data.polygons[selface].select = True
221 # --------------------------------------------------------------------
222 # Select vertices
223 # --------------------------------------------------------------------
224 def select_vertices(myobject, selvertices, clear=True):
225 myobject.select_set(True)
226 bpy.context.view_layer.objects.active = myobject
227 if bpy.context.view_layer.objects.active.name == myobject.name:
228 # deselect everything
229 if clear:
230 bpy.ops.object.mode_set(mode='EDIT')
231 bpy.ops.mesh.select_all(action='DESELECT')
233 # Select Vertices
234 bpy.ops.object.mode_set(mode='EDIT', toggle=False)
235 sel_mode = bpy.context.tool_settings.mesh_select_mode
237 bpy.context.tool_settings.mesh_select_mode = [True, False, False]
238 bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
240 for i in selvertices:
241 myobject.data.vertices[i].select = True
243 bpy.ops.object.mode_set(mode='EDIT', toggle=False)
244 bpy.context.tool_settings.mesh_select_mode = sel_mode
245 bpy.ops.object.mode_set(mode='OBJECT')
248 # --------------------------------------------------------------------
249 # Mark Seam
250 # --------------------------------------------------------------------
251 def mark_seam(myobject):
252 # noinspection PyBroadException
253 try:
254 myobject.select_set(True)
255 bpy.context.view_layer.objects.active = myobject
256 if bpy.context.view_layer.objects.active.name == myobject.name:
257 bpy.ops.object.mode_set(mode='EDIT', toggle=False)
258 bpy.ops.mesh.mark_seam()
259 bpy.ops.object.mode_set(mode='OBJECT')
260 except:
261 bpy.ops.object.mode_set(mode='OBJECT')
264 # --------------------------------------------------------------------
265 # Unwrap mesh
266 # --------------------------------------------------------------------
267 def unwrap_mesh(myobject, allfaces=True):
268 # noinspection PyBroadException
269 try:
270 myobject.select_set(True)
271 bpy.context.view_layer.objects.active = myobject
272 if bpy.context.view_layer.objects.active.name == myobject.name:
273 # Unwrap
274 bpy.ops.object.mode_set(mode='EDIT', toggle=False)
275 if allfaces is True:
276 bpy.ops.mesh.select_all(action='DESELECT')
277 bpy.ops.mesh.select_all()
278 bpy.ops.uv.unwrap()
279 bpy.ops.object.mode_set(mode='OBJECT')
280 except:
281 bpy.ops.object.mode_set(mode='OBJECT')
284 # --------------------------------------------------------------------
285 # Get Node Index(multilanguage support)
286 # --------------------------------------------------------------------
287 def get_node_index(nodes, datatype):
288 idx = 0
289 for m in nodes:
290 if m.type == datatype:
291 return idx
292 idx += 1
294 # by default
295 return 1
298 # --------------------------------------------------------------------
299 # Create cycles diffuse material
300 # --------------------------------------------------------------------
301 def create_diffuse_material(matname, replace, r, g, b, rv=0.8, gv=0.8, bv=0.8, mix=0.1, twosides=False):
302 # Avoid duplicate materials
303 if replace is False:
304 matlist = bpy.data.materials
305 for m in matlist:
306 if m.name == matname:
307 return m
308 # Create material
309 mat = bpy.data.materials.new(matname)
310 mat.diffuse_color = (rv, gv, bv, 1.0) # viewport color
311 mat.use_nodes = True
312 nodes = mat.node_tree.nodes
314 # support for multilanguage
315 node = nodes.new('ShaderNodeBsdfDiffuse')
316 node.name = 'Diffuse BSDF'
317 node.inputs[0].default_value = [r, g, b, 1]
318 node.location = 200, 320
320 node = nodes.new('ShaderNodeBsdfGlossy')
321 node.name = 'Glossy_0'
322 node.location = 200, 0
324 node = nodes.new('ShaderNodeMixShader')
325 node.name = 'Mix_0'
326 node.inputs[0].default_value = mix
327 node.location = 500, 160
329 node = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')]
330 node.location = 1100, 160
332 # Connect nodes
333 outn = nodes['Diffuse BSDF'].outputs[0]
334 inn = nodes['Mix_0'].inputs[1]
335 mat.node_tree.links.new(outn, inn)
337 outn = nodes['Glossy_0'].outputs[0]
338 inn = nodes['Mix_0'].inputs[2]
339 mat.node_tree.links.new(outn, inn)
341 if twosides is False:
342 outn = nodes['Mix_0'].outputs[0]
343 inn = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')].inputs[0]
344 mat.node_tree.links.new(outn, inn)
346 if twosides is True:
347 node = nodes.new('ShaderNodeNewGeometry')
348 node.name = 'Input_1'
349 node.location = -80, -70
351 node = nodes.new('ShaderNodeBsdfDiffuse')
352 node.name = 'Diffuse_1'
353 node.inputs[0].default_value = [0.30, 0.30, 0.30, 1]
354 node.location = 200, -280
356 node = nodes.new('ShaderNodeMixShader')
357 node.name = 'Mix_1'
358 node.inputs[0].default_value = mix
359 node.location = 800, -70
361 outn = nodes['Input_1'].outputs[6]
362 inn = nodes['Mix_1'].inputs[0]
363 mat.node_tree.links.new(outn, inn)
365 outn = nodes['Diffuse_1'].outputs[0]
366 inn = nodes['Mix_1'].inputs[2]
367 mat.node_tree.links.new(outn, inn)
369 outn = nodes['Mix_0'].outputs[0]
370 inn = nodes['Mix_1'].inputs[1]
371 mat.node_tree.links.new(outn, inn)
373 outn = nodes['Mix_1'].outputs[0]
374 inn = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')].inputs[0]
375 mat.node_tree.links.new(outn, inn)
377 return mat
380 # --------------------------------------------------------------------
381 # Create cycles translucent material
382 # --------------------------------------------------------------------
383 def create_translucent_material(matname, replace, r, g, b, rv=0.8, gv=0.8, bv=0.8, mix=0.1):
384 # Avoid duplicate materials
385 if replace is False:
386 matlist = bpy.data.materials
387 for m in matlist:
388 if m.name == matname:
389 return m
390 # Create material
391 mat = bpy.data.materials.new(matname)
392 mat.diffuse_color = (rv, gv, bv, 1.0) # viewport color
393 mat.use_nodes = True
394 nodes = mat.node_tree.nodes
396 # support for multilanguage
397 node = nodes.new('ShaderNodeBsdfDiffuse')
398 node.name = 'Diffuse BSDF'
399 node.inputs[0].default_value = [r, g, b, 1]
400 node.location = 200, 320
402 node = nodes.new('ShaderNodeBsdfTranslucent')
403 node.name = 'Translucent_0'
404 node.location = 200, 0
406 node = nodes.new('ShaderNodeMixShader')
407 node.name = 'Mix_0'
408 node.inputs[0].default_value = mix
409 node.location = 500, 160
411 node = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')]
412 node.location = 1100, 160
414 # Connect nodes
415 outn = nodes['Diffuse BSDF'].outputs[0]
416 inn = nodes['Mix_0'].inputs[1]
417 mat.node_tree.links.new(outn, inn)
419 outn = nodes['Translucent_0'].outputs[0]
420 inn = nodes['Mix_0'].inputs[2]
421 mat.node_tree.links.new(outn, inn)
423 outn = nodes['Mix_0'].outputs[0]
424 inn = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')].inputs[0]
425 mat.node_tree.links.new(outn, inn)
427 return mat
430 # --------------------------------------------------------------------
431 # Create cycles glass material
432 # --------------------------------------------------------------------
433 def create_glass_material(matname, replace, rv=0.333, gv=0.342, bv=0.9):
434 # Avoid duplicate materials
435 if replace is False:
436 matlist = bpy.data.materials
437 for m in matlist:
438 if m.name == matname:
439 return m
440 # Create material
441 mat = bpy.data.materials.new(matname)
442 mat.use_nodes = True
443 mat.diffuse_color = (rv, gv, bv, 1.0)
444 nodes = mat.node_tree.nodes
446 # support for multilanguage
447 node = nodes[get_node_index(nodes, 'BSDF_DIFFUSE')]
448 mat.node_tree.nodes.remove(node) # remove not used
450 node = nodes.new('ShaderNodeLightPath')
451 node.name = 'Light_0'
452 node.location = 10, 160
454 node = nodes.new('ShaderNodeBsdfRefraction')
455 node.name = 'Refraction_0'
456 node.inputs[2].default_value = 1 # IOR 1.0
457 node.location = 250, 400
459 node = nodes.new('ShaderNodeBsdfGlossy')
460 node.name = 'Glossy_0'
461 node.distribution = 'SHARP'
462 node.location = 250, 100
464 node = nodes.new('ShaderNodeBsdfTransparent')
465 node.name = 'Transparent_0'
466 node.location = 500, 10
468 node = nodes.new('ShaderNodeMixShader')
469 node.name = 'Mix_0'
470 node.inputs[0].default_value = 0.035
471 node.location = 500, 160
473 node = nodes.new('ShaderNodeMixShader')
474 node.name = 'Mix_1'
475 node.inputs[0].default_value = 0.1
476 node.location = 690, 290
478 node = nodes.new('ShaderNodeOutputMaterial')
479 node.name = 'OUTPUT_MATERIAL'
480 node.location = 920, 290
482 # Connect nodes
483 outn = nodes['Light_0'].outputs[1]
484 inn = nodes['Mix_1'].inputs[0]
485 mat.node_tree.links.new(outn, inn)
487 outn = nodes['Refraction_0'].outputs[0]
488 inn = nodes['Mix_0'].inputs[1]
489 mat.node_tree.links.new(outn, inn)
491 outn = nodes['Glossy_0'].outputs[0]
492 inn = nodes['Mix_0'].inputs[2]
493 mat.node_tree.links.new(outn, inn)
495 outn = nodes['Mix_0'].outputs[0]
496 inn = nodes['Mix_1'].inputs[1]
497 mat.node_tree.links.new(outn, inn)
499 outn = nodes['Transparent_0'].outputs[0]
500 inn = nodes['Mix_1'].inputs[2]
501 mat.node_tree.links.new(outn, inn)
503 outn = nodes['Mix_1'].outputs[0]
504 inn = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')].inputs[0]
505 mat.node_tree.links.new(outn, inn)
507 return mat
510 # ---------------------------------------------
511 # Create cycles transparents material
512 # --------------------------------------------------------------------
513 def create_transparent_material(matname, replace, r=1, g=1, b=1, alpha=0):
514 # Avoid duplicate materials
515 if replace is False:
516 matlist = bpy.data.materials
517 for m in matlist:
518 if m.name == matname:
519 return m
520 # Create material
521 mat = bpy.data.materials.new(matname)
522 mat.use_nodes = True
523 mat.diffuse_color = (r, g, b, 1.0)
524 nodes = mat.node_tree.nodes
526 # support for multilanguage
527 node = nodes[get_node_index(nodes, 'BSDF_DIFFUSE')]
528 mat.node_tree.nodes.remove(node) # remove not used
530 node = nodes.new('ShaderNodeBsdfTransparent')
531 node.name = 'Transparent_0'
532 node.location = 250, 160
533 node.inputs[0].default_value = [r, g, b, alpha]
535 node = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')]
536 node.location = 700, 160
538 # Connect nodes
539 outn = nodes['Transparent_0'].outputs[0]
540 inn = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')].inputs[0]
541 mat.node_tree.links.new(outn, inn)
543 return mat
546 # --------------------------------------------------------------------
547 # Create cycles glossy material
548 # --------------------------------------------------------------------
549 def create_glossy_material(matname, replace, r, g, b, rv=0.578, gv=0.555, bv=0.736, rvalue=0.2):
550 # Avoid duplicate materials
551 if replace is False:
552 matlist = bpy.data.materials
553 for m in matlist:
554 if m.name == matname:
555 return m
556 # Create material
557 mat = bpy.data.materials.new(matname)
558 mat.use_nodes = True
559 mat.diffuse_color = (rv, gv, bv, 1.0)
560 nodes = mat.node_tree.nodes
562 # support for multilanguage
563 node = nodes[get_node_index(nodes, 'BSDF_DIFFUSE')]
564 mat.node_tree.nodes.remove(node) # remove not used
566 node = nodes.new('ShaderNodeBsdfGlossy')
567 node.name = 'Glossy_0'
568 node.inputs[0].default_value = [r, g, b, 1]
569 node.inputs[1].default_value = rvalue
570 node.location = 200, 160
572 node = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')]
573 node.location = 700, 160
575 # Connect nodes
576 outn = nodes['Glossy_0'].outputs[0]
577 inn = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')].inputs[0]
578 mat.node_tree.links.new(outn, inn)
580 return mat
583 # --------------------------------------------------------------------
584 # Create cycles emission material
585 # --------------------------------------------------------------------
586 def create_emission_material(matname, replace, r, g, b, energy):
587 # Avoid duplicate materials
588 if replace is False:
589 matlist = bpy.data.materials
590 for m in matlist:
591 if m.name == matname:
592 return m
593 # Create material
594 mat = bpy.data.materials.new(matname)
595 mat.use_nodes = True
596 nodes = mat.node_tree.nodes
598 # support for multilanguage
599 node = nodes[get_node_index(nodes, 'BSDF_DIFFUSE')]
600 mat.node_tree.nodes.remove(node) # remove not used
602 node = nodes.new('ShaderNodeEmission')
603 node.name = 'Emission_0'
604 node.inputs[0].default_value = [r, g, b, 1]
605 node.inputs[1].default_value = energy
606 node.location = 200, 160
608 node = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')]
609 node.location = 700, 160
611 # Connect nodes
612 outn = nodes['Emission_0'].outputs[0]
613 inn = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')].inputs[0]
614 mat.node_tree.links.new(outn, inn)
616 return mat
619 # --------------------------------------------------------------------
620 # Create cycles glass material
621 # --------------------------------------------------------------------
622 def create_old_glass_material(matname, replace, rv=0.352716, gv=0.760852, bv=0.9):
623 # Avoid duplicate materials
624 if replace is False:
625 matlist = bpy.data.materials
626 for m in matlist:
627 if m.name == matname:
628 return m
629 # Create material
630 mat = bpy.data.materials.new(matname)
631 mat.use_nodes = True
632 mat.diffuse_color = (rv, gv, bv, 1.0)
633 nodes = mat.node_tree.nodes
635 # support for multilanguage
636 node = nodes[get_node_index(nodes, 'BSDF_DIFFUSE')]
637 mat.node_tree.nodes.remove(node) # remove not used
639 node = nodes.new('ShaderNodeLightPath')
640 node.name = 'Light_0'
641 node.location = 10, 160
643 node = nodes.new('ShaderNodeBsdfGlass')
644 node.name = 'Glass_0'
645 node.location = 250, 300
647 node = nodes.new('ShaderNodeBsdfTransparent')
648 node.name = 'Transparent_0'
649 node.location = 250, 0
651 node = nodes.new('ShaderNodeMixShader')
652 node.name = 'Mix_0'
653 node.inputs[0].default_value = 0.1
654 node.location = 500, 160
656 node = nodes.new('ShaderNodeMixShader')
657 node.name = 'Mix_1'
658 node.inputs[0].default_value = 0.1
659 node.location = 690, 290
661 node = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')]
662 node.location = 920, 290
664 # Connect nodes
665 outn = nodes['Light_0'].outputs[1]
666 inn = nodes['Mix_0'].inputs[0]
667 mat.node_tree.links.new(outn, inn)
669 outn = nodes['Light_0'].outputs[2]
670 inn = nodes['Mix_1'].inputs[0]
671 mat.node_tree.links.new(outn, inn)
673 outn = nodes['Glass_0'].outputs[0]
674 inn = nodes['Mix_0'].inputs[1]
675 mat.node_tree.links.new(outn, inn)
677 outn = nodes['Transparent_0'].outputs[0]
678 inn = nodes['Mix_0'].inputs[2]
679 mat.node_tree.links.new(outn, inn)
681 outn = nodes['Mix_0'].outputs[0]
682 inn = nodes['Mix_1'].inputs[1]
683 mat.node_tree.links.new(outn, inn)
685 outn = nodes['Mix_1'].outputs[0]
686 inn = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')].inputs[0]
687 mat.node_tree.links.new(outn, inn)
689 return mat
692 # --------------------------------------------------------------------
693 # Create cycles brick texture material
694 # --------------------------------------------------------------------
695 def create_brick_material(matname, replace, r, g, b, rv=0.8, gv=0.636, bv=0.315):
696 # Avoid duplicate materials
697 if replace is False:
698 matlist = bpy.data.materials
699 for m in matlist:
700 if m.name == matname:
701 return m
702 # Create material
703 mat = bpy.data.materials.new(matname)
704 mat.use_nodes = True
705 mat.diffuse_color = (rv, gv, bv, 1.0)
706 nodes = mat.node_tree.nodes
708 # support for multilanguage
709 principled_node = nodes[get_node_index(nodes, 'BSDF_PRINCIPLED')]
711 principled_node.inputs[0].default_value = [r, g, b, 1]
712 principled_node.location = 500, 160
714 output_node = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')]
715 output_node.location = 700, 160
717 brick_node = nodes.new('ShaderNodeTexBrick')
718 brick_node.name = 'Brick_0'
719 brick_node.inputs[3].default_value = [0.407, 0.411, 0.394, 1] # mortar color
720 brick_node.inputs[4].default_value = 3 # scale
721 brick_node.inputs[5].default_value = 0.001 # mortar
722 brick_node.inputs[7].default_value = 0.60 # size_w
723 brick_node.inputs[8].default_value = 0.30 # size_h
724 brick_node.location = 300, 160
726 rgb_node = nodes.new('ShaderNodeRGB')
727 rgb_node.name = 'RGB_0'
728 rgb_node.outputs[0].default_value = [r, g, b, 1]
729 rgb_node.location = 70, 160
731 # Connect nodes
732 outn = rgb_node.outputs['Color']
733 inn = brick_node.inputs['Color1']
734 mat.node_tree.links.new(outn, inn)
736 inn = brick_node.inputs['Color2']
737 mat.node_tree.links.new(outn, inn)
739 outn = brick_node.outputs['Color']
740 inn = principled_node.inputs['Base Color']
741 mat.node_tree.links.new(outn, inn)
743 return mat
746 # --------------------------------------------------------------------
747 # Create cycles fabric texture material
748 # --------------------------------------------------------------------
749 def create_fabric_material(matname, replace, r, g, b, rv=0.8, gv=0.636, bv=0.315):
750 # Avoid duplicate materials
751 if replace is False:
752 matlist = bpy.data.materials
753 for m in matlist:
754 if m.name == matname:
755 return m
756 # Create material
757 mat = bpy.data.materials.new(matname)
758 mat.use_nodes = True
759 mat.diffuse_color = (rv, gv, bv, 1.0)
760 nodes = mat.node_tree.nodes
762 # support for multilanguage
763 node = nodes.new('ShaderNodeBsdfDiffuse')
764 node.name = 'Diffuse BSDF'
765 node.inputs[0].default_value = [r, g, b, 1]
766 node.location = 810, 270
768 node = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')]
769 node.location = 1210, 320
771 node = nodes.new('ShaderNodeTexCoord')
772 node.name = 'UVCoordinates'
773 node.location = 26, 395
775 node = nodes.new('ShaderNodeMapping')
776 node.name = 'UVMapping'
777 node.location = 266, 380
778 node.inputs['Scale'].default_value[0] = 1000
779 node.inputs['Scale'].default_value[1] = 1000
780 node.inputs['Scale'].default_value[2] = 1000
782 # ===========================================================================
783 # Image texture
784 # ===========================================================================
785 # Load image file.
787 realpath = path.join(path.dirname(__file__), "images", "fabric_diffuse.png")
788 print("Loading: " + realpath)
789 try:
790 img = bpy.data.images.load(realpath)
791 except:
792 raise NameError("Cannot load image %s" % realpath)
794 # Create image texture from image
795 ctex = bpy.data.textures.new('ColorTex', type='IMAGE')
796 ctex.image = img
798 node = nodes.new('ShaderNodeTexImage')
799 node.name = 'Image1'
800 node.image = ctex.image
801 node.location = 615, 350
803 node = nodes.new('ShaderNodeBsdfTransparent')
804 node.name = 'Transparent1'
805 node.location = 810, 395
806 node.inputs[0].default_value = [r, g, b, 1]
808 node = nodes.new('ShaderNodeAddShader')
809 node.name = 'Add1'
810 node.location = 1040, 356
812 # Connect nodes
813 outn = nodes['UVCoordinates'].outputs['UV']
814 inn = nodes['UVMapping'].inputs['Vector']
815 mat.node_tree.links.new(outn, inn)
817 outn = nodes['UVMapping'].outputs['Vector']
818 inn = nodes['Image1'].inputs['Vector']
819 mat.node_tree.links.new(outn, inn)
821 outn = nodes['Image1'].outputs['Color']
822 inn = nodes['Diffuse BSDF'].inputs['Color']
823 mat.node_tree.links.new(outn, inn)
825 outn = nodes['Transparent1'].outputs['BSDF']
826 inn = nodes['Add1'].inputs[0]
827 mat.node_tree.links.new(outn, inn)
829 outn = nodes['Diffuse BSDF'].outputs['BSDF']
830 inn = nodes['Add1'].inputs[1]
831 mat.node_tree.links.new(outn, inn)
833 outn = nodes['Add1'].outputs['Shader']
834 inn = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')].inputs[0]
835 mat.node_tree.links.new(outn, inn)
837 return mat
840 # --------------------------------------------------------------------
841 # Copy bin file
842 # --------------------------------------------------------------------
843 def copy_binfile(fromfile, tofile):
844 with open(fromfile, 'rb') as f1:
845 with open(tofile, 'wb') as f2:
846 while True:
847 mybytes = f1.read(1024)
848 if mybytes:
849 f2.write(mybytes)
850 else:
851 break
854 # --------------------------------------------------------------------
855 # Parent object (keep positions)
856 # --------------------------------------------------------------------
857 def parentobject(parentobj, childobj):
858 # noinspection PyBroadException
859 try:
860 bpy.ops.object.select_all(action='DESELECT')
861 bpy.context.view_layer.objects.active = parentobj
862 parentobj.select_set(True)
863 childobj.select_set(True)
864 bpy.ops.object.parent_set(type='OBJECT', keep_transform=False)
865 return True
866 except:
867 return False
870 # ------------------------------------------------------------------------------
871 # Create control box
873 # objName: Object name
874 # x: size x axis
875 # y: size y axis
876 # z: size z axis
877 # tube: True create a tube, False only sides
878 # ------------------------------------------------------------------------------
879 def create_control_box(objname, x, y, z, tube=True):
880 myvertex = [(-x / 2, 0, 0.0),
881 (-x / 2, y, 0.0),
882 (x / 2, y, 0.0),
883 (x / 2, 0, 0.0),
884 (-x / 2, 0, z),
885 (-x / 2, y, z),
886 (x / 2, y, z),
887 (x / 2, 0, z)]
889 if tube is True:
890 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)]
891 else:
892 myfaces = [(0, 4, 5, 1), (2, 6, 7, 3)]
894 mesh = bpy.data.meshes.new(objname)
895 myobject = bpy.data.objects.new(objname, mesh)
897 myobject.location = bpy.context.scene.cursor.location
898 bpy.context.collection.objects.link(myobject)
900 mesh.from_pydata(myvertex, [], myfaces)
901 mesh.update(calc_edges=True)
903 return myobject
906 # ------------------------------------------------------------------------------
907 # Remove all children objects
908 # ------------------------------------------------------------------------------
909 def remove_children(myobject):
910 # Remove children
911 for child in myobject.children:
912 # noinspection PyBroadException
913 try:
914 # noinspection PyBroadException
915 try:
916 # remove child relationship
917 for grandchild in child.children:
918 grandchild.parent = None
919 # remove modifiers
920 for mod in child.modifiers:
921 bpy.ops.object.modifier_remove(name=mod.name)
922 except:
923 pass
924 # clear child data
925 if child.type == 'MESH':
926 old = child.data
927 child.select_set(True)
928 bpy.ops.object.delete()
929 bpy.data.meshes.remove(old)
930 if child.type == 'CURVE':
931 child.select_set(True)
932 bpy.ops.object.delete()
933 except:
934 pass
937 # --------------------------------------------------------------------
938 # Get all parents
939 # --------------------------------------------------------------------
940 def get_allparents(myobj):
941 obj = myobj
942 mylist = []
943 while obj.parent is not None:
944 mylist.append(obj)
945 objp = obj.parent
946 obj = objp
948 mylist.append(obj)
950 return mylist
953 # --------------------------------------------------------------------
954 # Verify all faces are in vertice group to avoid Blander crash
956 # Review the faces array and remove any vertex out of the range
957 # this avoid any bug that can appear avoiding Blender crash
958 # --------------------------------------------------------------------
959 def check_mesh_errors(myvertices, myfaces):
960 vmax = len(myvertices)
962 f = 0
963 for face in myfaces:
964 for v in face:
965 if v < 0 or v > vmax:
966 print("Face=" + str(f) + "->removed vertex=" + str(v))
967 myfaces[f].remove(v)
968 f += 1
970 return myfaces