Cleanup: trailing space
[blender-addons.git] / archimesh / achm_tools.py
blob1eff2cda781e2b238bc862de00e0a56e1fcfa451
1 # SPDX-License-Identifier: GPL-2.0-or-later
3 # <pep8 compliant>
5 # ----------------------------------------------------------
6 # support routines and general functions
7 # Author: Antonio Vazquez (antonioya)
9 # ----------------------------------------------------------
10 # noinspection PyUnresolvedReferences
11 import bpy
12 from os import path
15 # --------------------------------------------------------------------
16 # Get length Blender units
17 # --------------------------------------------------------------------
18 def get_blendunits(units):
19 if bpy.context.scene.unit_settings.system == "IMPERIAL":
20 return units * 0.3048
21 else:
22 return units
25 # --------------------------------------------------------------------
26 # Set normals
27 # True= faces to inside
28 # False= faces to outside
29 # --------------------------------------------------------------------
30 def set_normals(myobject, direction=False):
31 bpy.context.view_layer.objects.active = myobject
32 # go edit mode
33 bpy.ops.object.mode_set(mode='EDIT')
34 # select all faces
35 bpy.ops.mesh.select_all(action='SELECT')
36 # recalculate outside normals
37 bpy.ops.mesh.normals_make_consistent(inside=direction)
38 # go object mode again
39 bpy.ops.object.editmode_toggle()
42 # --------------------------------------------------------------------
43 # Remove doubles
44 # --------------------------------------------------------------------
45 def remove_doubles(myobject):
46 bpy.context.view_layer.objects.active = myobject
47 # go edit mode
48 bpy.ops.object.mode_set(mode='EDIT')
49 # select all faces
50 bpy.ops.mesh.select_all(action='SELECT')
51 # remove
52 bpy.ops.mesh.remove_doubles()
53 # go object mode again
54 bpy.ops.object.editmode_toggle()
57 # --------------------------------------------------------------------
58 # Set shade smooth
59 # --------------------------------------------------------------------
60 def set_smooth(myobject):
61 # deactivate others
62 for o in bpy.data.objects:
63 if o.select_get() is True:
64 o.select_set(False)
66 myobject.select_set(True)
67 bpy.context.view_layer.objects.active = myobject
68 if bpy.context.view_layer.objects.active.name == myobject.name:
69 bpy.ops.object.shade_smooth()
72 # --------------------------------------------------------------------
73 # Add modifier (subdivision)
74 # --------------------------------------------------------------------
75 def set_modifier_subsurf(myobject):
76 bpy.context.view_layer.objects.active = myobject
77 if bpy.context.view_layer.objects.active.name == myobject.name:
78 bpy.ops.object.modifier_add(type='SUBSURF')
79 for mod in myobject.modifiers:
80 if mod.type == 'SUBSURF':
81 mod.levels = 2
84 # --------------------------------------------------------------------
85 # Add modifier (mirror)
86 # --------------------------------------------------------------------
87 def set_modifier_mirror(myobject, axis="Y"):
88 bpy.ops.object.select_all(action='DESELECT')
89 myobject.select_set(True)
90 bpy.context.view_layer.objects.active = myobject
91 if bpy.context.view_layer.objects.active.name == myobject.name:
92 bpy.ops.object.modifier_add(type='MIRROR')
93 for mod in myobject.modifiers:
94 if mod.type == 'MIRROR':
95 if axis == "X":
96 mod.use_axis[0] = True
97 else:
98 mod.use__axis[0] = False
100 if axis == "Y":
101 mod.use_axis[1] = True
102 else:
103 mod.use_axis[1] = False
105 if axis == "Z":
106 mod.use_axis[2] = True
107 else:
108 mod.use_axis[2] = False
110 mod.use_clip = True
113 # --------------------------------------------------------------------
114 # Add modifier (array)
115 # --------------------------------------------------------------------
116 def set_modifier_array(myobject, axis, move, repeat, fix=False, fixmove=0, zmove=0):
117 bpy.ops.object.select_all(action='DESELECT')
118 myobject.select_set(True)
119 bpy.context.view_layer.objects.active = myobject
120 if bpy.context.view_layer.objects.active.name == myobject.name:
121 bpy.ops.object.modifier_add(type='ARRAY')
122 for mod in myobject.modifiers:
123 if mod.type == 'ARRAY':
124 if mod.name == "Array":
125 mod.name = "Array_" + axis
126 mod.count = repeat
127 mod.use_constant_offset = fix
128 if axis == "X":
129 mod.relative_offset_displace[0] = move
130 mod.constant_offset_displace[0] = fixmove
131 mod.relative_offset_displace[1] = 0.0
132 mod.constant_offset_displace[1] = 0.0
133 mod.relative_offset_displace[2] = 0.0
134 mod.constant_offset_displace[2] = zmove
136 if axis == "Y":
137 mod.relative_offset_displace[0] = 0.0
138 mod.constant_offset_displace[0] = 0.0
139 mod.relative_offset_displace[1] = move
140 mod.constant_offset_displace[1] = fixmove
141 mod.relative_offset_displace[2] = 0.0
142 mod.constant_offset_displace[2] = 0.0
145 # --------------------------------------------------------------------
146 # Add modifier (curve)
147 # --------------------------------------------------------------------
148 def set_modifier_curve(myobject, mycurve):
149 bpy.context.view_layer.objects.active = myobject
150 if bpy.context.view_layer.objects.active.name == myobject.name:
151 bpy.ops.object.modifier_add(type='CURVE')
152 for mod in myobject.modifiers:
153 if mod.type == 'CURVE':
154 mod.deform_axis = 'POS_X'
155 mod.object = mycurve
158 # --------------------------------------------------------------------
159 # Add modifier (solidify)
160 # --------------------------------------------------------------------
161 def set_modifier_solidify(myobject, width):
162 bpy.context.view_layer.objects.active = myobject
163 if bpy.context.view_layer.objects.active.name == myobject.name:
164 bpy.ops.object.modifier_add(type='SOLIDIFY')
165 for mod in myobject.modifiers:
166 if mod.type == 'SOLIDIFY':
167 mod.thickness = width
168 mod.use_even_offset = True
169 mod.use_quality_normals = True
170 break
173 # --------------------------------------------------------------------
174 # Add modifier (boolean)
175 # --------------------------------------------------------------------
176 def set_modifier_boolean(myobject, bolobject):
177 bpy.context.view_layer.objects.active = myobject
178 if bpy.context.view_layer.objects.active.name == myobject.name:
179 bpy.ops.object.modifier_add(type='BOOLEAN')
180 mod = myobject.modifiers[len(myobject.modifiers) - 1]
181 mod.operation = 'DIFFERENCE'
182 mod.object = bolobject
185 # --------------------------------------------------------------------
186 # Set material to object
187 # --------------------------------------------------------------------
188 def set_material(myobject, mymaterial):
189 bpy.context.view_layer.objects.active = myobject
190 if bpy.context.view_layer.objects.active.name == myobject.name:
191 myobject.data.materials.append(mymaterial)
194 # --------------------------------------------------------------------
195 # Set material to selected faces
196 # --------------------------------------------------------------------
197 def set_material_faces(myobject, idx):
198 bpy.context.view_layer.objects.active = myobject
199 myobject.select_set(True)
200 bpy.context.object.active_material_index = idx
201 if bpy.context.view_layer.objects.active.name == myobject.name:
202 bpy.ops.object.mode_set(mode='EDIT')
203 bpy.ops.object.material_slot_assign()
204 # Deselect
205 bpy.ops.mesh.select_all(action='DESELECT')
206 bpy.ops.object.mode_set(mode='OBJECT')
209 # --------------------------------------------------------------------
210 # Select faces
211 # --------------------------------------------------------------------
212 def select_faces(myobject, selface, clear):
213 myobject.select_set(True)
214 bpy.context.view_layer.objects.active = myobject
215 if bpy.context.view_layer.objects.active.name == myobject.name:
216 # deselect everything
217 if clear:
218 bpy.ops.object.mode_set(mode='EDIT')
219 bpy.ops.mesh.select_all(action='DESELECT')
221 # reselect the originally selected face
222 bpy.ops.object.mode_set(mode='OBJECT')
223 myobject.data.polygons[selface].select = True
226 # --------------------------------------------------------------------
227 # Select vertices
228 # --------------------------------------------------------------------
229 def select_vertices(myobject, selvertices, clear=True):
230 myobject.select_set(True)
231 bpy.context.view_layer.objects.active = myobject
232 if bpy.context.view_layer.objects.active.name == myobject.name:
233 # deselect everything
234 if clear:
235 bpy.ops.object.mode_set(mode='EDIT')
236 bpy.ops.mesh.select_all(action='DESELECT')
238 # Select Vertices
239 bpy.ops.object.mode_set(mode='EDIT', toggle=False)
240 sel_mode = bpy.context.tool_settings.mesh_select_mode
242 bpy.context.tool_settings.mesh_select_mode = [True, False, False]
243 bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
245 for i in selvertices:
246 myobject.data.vertices[i].select = True
248 bpy.ops.object.mode_set(mode='EDIT', toggle=False)
249 bpy.context.tool_settings.mesh_select_mode = sel_mode
250 bpy.ops.object.mode_set(mode='OBJECT')
253 # --------------------------------------------------------------------
254 # Mark Seam
255 # --------------------------------------------------------------------
256 def mark_seam(myobject):
257 # noinspection PyBroadException
258 try:
259 myobject.select_set(True)
260 bpy.context.view_layer.objects.active = myobject
261 if bpy.context.view_layer.objects.active.name == myobject.name:
262 bpy.ops.object.mode_set(mode='EDIT', toggle=False)
263 bpy.ops.mesh.mark_seam()
264 bpy.ops.object.mode_set(mode='OBJECT')
265 except:
266 bpy.ops.object.mode_set(mode='OBJECT')
269 # --------------------------------------------------------------------
270 # Unwrap mesh
271 # --------------------------------------------------------------------
272 def unwrap_mesh(myobject, allfaces=True):
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 # Unwrap
279 bpy.ops.object.mode_set(mode='EDIT', toggle=False)
280 if allfaces is True:
281 bpy.ops.mesh.select_all(action='DESELECT')
282 bpy.ops.mesh.select_all()
283 bpy.ops.uv.unwrap()
284 bpy.ops.object.mode_set(mode='OBJECT')
285 except:
286 bpy.ops.object.mode_set(mode='OBJECT')
289 # --------------------------------------------------------------------
290 # Get Node Index(multilanguage support)
291 # --------------------------------------------------------------------
292 def get_node_index(nodes, datatype):
293 idx = 0
294 for m in nodes:
295 if m.type == datatype:
296 return idx
297 idx += 1
299 # by default
300 return 1
303 # --------------------------------------------------------------------
304 # Create cycles diffuse material
305 # --------------------------------------------------------------------
306 def create_diffuse_material(matname, replace, r, g, b, rv=0.8, gv=0.8, bv=0.8, mix=0.1, twosides=False):
307 # Avoid duplicate materials
308 if replace is False:
309 matlist = bpy.data.materials
310 for m in matlist:
311 if m.name == matname:
312 return m
313 # Create material
314 mat = bpy.data.materials.new(matname)
315 mat.diffuse_color = (rv, gv, bv, 1.0) # viewport color
316 mat.use_nodes = True
317 nodes = mat.node_tree.nodes
319 # support for multilanguage
320 node = nodes.new('ShaderNodeBsdfDiffuse')
321 node.name = 'Diffuse BSDF'
322 node.inputs[0].default_value = [r, g, b, 1]
323 node.location = 200, 320
325 node = nodes.new('ShaderNodeBsdfGlossy')
326 node.name = 'Glossy_0'
327 node.location = 200, 0
329 node = nodes.new('ShaderNodeMixShader')
330 node.name = 'Mix_0'
331 node.inputs[0].default_value = mix
332 node.location = 500, 160
334 node = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')]
335 node.location = 1100, 160
337 # Connect nodes
338 outn = nodes['Diffuse BSDF'].outputs[0]
339 inn = nodes['Mix_0'].inputs[1]
340 mat.node_tree.links.new(outn, inn)
342 outn = nodes['Glossy_0'].outputs[0]
343 inn = nodes['Mix_0'].inputs[2]
344 mat.node_tree.links.new(outn, inn)
346 if twosides is False:
347 outn = nodes['Mix_0'].outputs[0]
348 inn = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')].inputs[0]
349 mat.node_tree.links.new(outn, inn)
351 if twosides is True:
352 node = nodes.new('ShaderNodeNewGeometry')
353 node.name = 'Input_1'
354 node.location = -80, -70
356 node = nodes.new('ShaderNodeBsdfDiffuse')
357 node.name = 'Diffuse_1'
358 node.inputs[0].default_value = [0.30, 0.30, 0.30, 1]
359 node.location = 200, -280
361 node = nodes.new('ShaderNodeMixShader')
362 node.name = 'Mix_1'
363 node.inputs[0].default_value = mix
364 node.location = 800, -70
366 outn = nodes['Input_1'].outputs[6]
367 inn = nodes['Mix_1'].inputs[0]
368 mat.node_tree.links.new(outn, inn)
370 outn = nodes['Diffuse_1'].outputs[0]
371 inn = nodes['Mix_1'].inputs[2]
372 mat.node_tree.links.new(outn, inn)
374 outn = nodes['Mix_0'].outputs[0]
375 inn = nodes['Mix_1'].inputs[1]
376 mat.node_tree.links.new(outn, inn)
378 outn = nodes['Mix_1'].outputs[0]
379 inn = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')].inputs[0]
380 mat.node_tree.links.new(outn, inn)
382 return mat
385 # --------------------------------------------------------------------
386 # Create cycles translucent material
387 # --------------------------------------------------------------------
388 def create_translucent_material(matname, replace, r, g, b, rv=0.8, gv=0.8, bv=0.8, mix=0.1):
389 # Avoid duplicate materials
390 if replace is False:
391 matlist = bpy.data.materials
392 for m in matlist:
393 if m.name == matname:
394 return m
395 # Create material
396 mat = bpy.data.materials.new(matname)
397 mat.diffuse_color = (rv, gv, bv, 1.0) # viewport color
398 mat.use_nodes = True
399 nodes = mat.node_tree.nodes
401 # support for multilanguage
402 node = nodes.new('ShaderNodeBsdfDiffuse')
403 node.name = 'Diffuse BSDF'
404 node.inputs[0].default_value = [r, g, b, 1]
405 node.location = 200, 320
407 node = nodes.new('ShaderNodeBsdfTranslucent')
408 node.name = 'Translucent_0'
409 node.location = 200, 0
411 node = nodes.new('ShaderNodeMixShader')
412 node.name = 'Mix_0'
413 node.inputs[0].default_value = mix
414 node.location = 500, 160
416 node = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')]
417 node.location = 1100, 160
419 # Connect nodes
420 outn = nodes['Diffuse BSDF'].outputs[0]
421 inn = nodes['Mix_0'].inputs[1]
422 mat.node_tree.links.new(outn, inn)
424 outn = nodes['Translucent_0'].outputs[0]
425 inn = nodes['Mix_0'].inputs[2]
426 mat.node_tree.links.new(outn, inn)
428 outn = nodes['Mix_0'].outputs[0]
429 inn = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')].inputs[0]
430 mat.node_tree.links.new(outn, inn)
432 return mat
435 # --------------------------------------------------------------------
436 # Create cycles glass material
437 # --------------------------------------------------------------------
438 def create_glass_material(matname, replace, rv=0.333, gv=0.342, bv=0.9):
439 # Avoid duplicate materials
440 if replace is False:
441 matlist = bpy.data.materials
442 for m in matlist:
443 if m.name == matname:
444 return m
445 # Create material
446 mat = bpy.data.materials.new(matname)
447 mat.use_nodes = True
448 mat.diffuse_color = (rv, gv, bv, 1.0)
449 nodes = mat.node_tree.nodes
451 # support for multilanguage
452 node = nodes[get_node_index(nodes, 'BSDF_DIFFUSE')]
453 mat.node_tree.nodes.remove(node) # remove not used
455 node = nodes.new('ShaderNodeLightPath')
456 node.name = 'Light_0'
457 node.location = 10, 160
459 node = nodes.new('ShaderNodeBsdfRefraction')
460 node.name = 'Refraction_0'
461 node.inputs[2].default_value = 1 # IOR 1.0
462 node.location = 250, 400
464 node = nodes.new('ShaderNodeBsdfGlossy')
465 node.name = 'Glossy_0'
466 node.distribution = 'SHARP'
467 node.location = 250, 100
469 node = nodes.new('ShaderNodeBsdfTransparent')
470 node.name = 'Transparent_0'
471 node.location = 500, 10
473 node = nodes.new('ShaderNodeMixShader')
474 node.name = 'Mix_0'
475 node.inputs[0].default_value = 0.035
476 node.location = 500, 160
478 node = nodes.new('ShaderNodeMixShader')
479 node.name = 'Mix_1'
480 node.inputs[0].default_value = 0.1
481 node.location = 690, 290
483 node = nodes.new('ShaderNodeOutputMaterial')
484 node.name = 'OUTPUT_MATERIAL'
485 node.location = 920, 290
487 # Connect nodes
488 outn = nodes['Light_0'].outputs[1]
489 inn = nodes['Mix_1'].inputs[0]
490 mat.node_tree.links.new(outn, inn)
492 outn = nodes['Refraction_0'].outputs[0]
493 inn = nodes['Mix_0'].inputs[1]
494 mat.node_tree.links.new(outn, inn)
496 outn = nodes['Glossy_0'].outputs[0]
497 inn = nodes['Mix_0'].inputs[2]
498 mat.node_tree.links.new(outn, inn)
500 outn = nodes['Mix_0'].outputs[0]
501 inn = nodes['Mix_1'].inputs[1]
502 mat.node_tree.links.new(outn, inn)
504 outn = nodes['Transparent_0'].outputs[0]
505 inn = nodes['Mix_1'].inputs[2]
506 mat.node_tree.links.new(outn, inn)
508 outn = nodes['Mix_1'].outputs[0]
509 inn = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')].inputs[0]
510 mat.node_tree.links.new(outn, inn)
512 return mat
515 # ---------------------------------------------
516 # Create cycles transparents material
517 # --------------------------------------------------------------------
518 def create_transparent_material(matname, replace, r=1, g=1, b=1, alpha=0):
519 # Avoid duplicate materials
520 if replace is False:
521 matlist = bpy.data.materials
522 for m in matlist:
523 if m.name == matname:
524 return m
525 # Create material
526 mat = bpy.data.materials.new(matname)
527 mat.use_nodes = True
528 mat.diffuse_color = (r, g, b, 1.0)
529 nodes = mat.node_tree.nodes
531 # support for multilanguage
532 node = nodes[get_node_index(nodes, 'BSDF_DIFFUSE')]
533 mat.node_tree.nodes.remove(node) # remove not used
535 node = nodes.new('ShaderNodeBsdfTransparent')
536 node.name = 'Transparent_0'
537 node.location = 250, 160
538 node.inputs[0].default_value = [r, g, b, alpha]
540 node = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')]
541 node.location = 700, 160
543 # Connect nodes
544 outn = nodes['Transparent_0'].outputs[0]
545 inn = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')].inputs[0]
546 mat.node_tree.links.new(outn, inn)
548 return mat
551 # --------------------------------------------------------------------
552 # Create cycles glossy material
553 # --------------------------------------------------------------------
554 def create_glossy_material(matname, replace, r, g, b, rv=0.578, gv=0.555, bv=0.736, rvalue=0.2):
555 # Avoid duplicate materials
556 if replace is False:
557 matlist = bpy.data.materials
558 for m in matlist:
559 if m.name == matname:
560 return m
561 # Create material
562 mat = bpy.data.materials.new(matname)
563 mat.use_nodes = True
564 mat.diffuse_color = (rv, gv, bv, 1.0)
565 nodes = mat.node_tree.nodes
567 # support for multilanguage
568 node = nodes[get_node_index(nodes, 'BSDF_DIFFUSE')]
569 mat.node_tree.nodes.remove(node) # remove not used
571 node = nodes.new('ShaderNodeBsdfGlossy')
572 node.name = 'Glossy_0'
573 node.inputs[0].default_value = [r, g, b, 1]
574 node.inputs[1].default_value = rvalue
575 node.location = 200, 160
577 node = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')]
578 node.location = 700, 160
580 # Connect nodes
581 outn = nodes['Glossy_0'].outputs[0]
582 inn = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')].inputs[0]
583 mat.node_tree.links.new(outn, inn)
585 return mat
588 # --------------------------------------------------------------------
589 # Create cycles emission material
590 # --------------------------------------------------------------------
591 def create_emission_material(matname, replace, r, g, b, energy):
592 # Avoid duplicate materials
593 if replace is False:
594 matlist = bpy.data.materials
595 for m in matlist:
596 if m.name == matname:
597 return m
598 # Create material
599 mat = bpy.data.materials.new(matname)
600 mat.use_nodes = True
601 nodes = mat.node_tree.nodes
603 # support for multilanguage
604 node = nodes[get_node_index(nodes, 'BSDF_DIFFUSE')]
605 mat.node_tree.nodes.remove(node) # remove not used
607 node = nodes.new('ShaderNodeEmission')
608 node.name = 'Emission_0'
609 node.inputs[0].default_value = [r, g, b, 1]
610 node.inputs[1].default_value = energy
611 node.location = 200, 160
613 node = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')]
614 node.location = 700, 160
616 # Connect nodes
617 outn = nodes['Emission_0'].outputs[0]
618 inn = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')].inputs[0]
619 mat.node_tree.links.new(outn, inn)
621 return mat
624 # --------------------------------------------------------------------
625 # Create cycles glass material
626 # --------------------------------------------------------------------
627 def create_old_glass_material(matname, replace, rv=0.352716, gv=0.760852, bv=0.9):
628 # Avoid duplicate materials
629 if replace is False:
630 matlist = bpy.data.materials
631 for m in matlist:
632 if m.name == matname:
633 return m
634 # Create material
635 mat = bpy.data.materials.new(matname)
636 mat.use_nodes = True
637 mat.diffuse_color = (rv, gv, bv, 1.0)
638 nodes = mat.node_tree.nodes
640 # support for multilanguage
641 node = nodes[get_node_index(nodes, 'BSDF_DIFFUSE')]
642 mat.node_tree.nodes.remove(node) # remove not used
644 node = nodes.new('ShaderNodeLightPath')
645 node.name = 'Light_0'
646 node.location = 10, 160
648 node = nodes.new('ShaderNodeBsdfGlass')
649 node.name = 'Glass_0'
650 node.location = 250, 300
652 node = nodes.new('ShaderNodeBsdfTransparent')
653 node.name = 'Transparent_0'
654 node.location = 250, 0
656 node = nodes.new('ShaderNodeMixShader')
657 node.name = 'Mix_0'
658 node.inputs[0].default_value = 0.1
659 node.location = 500, 160
661 node = nodes.new('ShaderNodeMixShader')
662 node.name = 'Mix_1'
663 node.inputs[0].default_value = 0.1
664 node.location = 690, 290
666 node = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')]
667 node.location = 920, 290
669 # Connect nodes
670 outn = nodes['Light_0'].outputs[1]
671 inn = nodes['Mix_0'].inputs[0]
672 mat.node_tree.links.new(outn, inn)
674 outn = nodes['Light_0'].outputs[2]
675 inn = nodes['Mix_1'].inputs[0]
676 mat.node_tree.links.new(outn, inn)
678 outn = nodes['Glass_0'].outputs[0]
679 inn = nodes['Mix_0'].inputs[1]
680 mat.node_tree.links.new(outn, inn)
682 outn = nodes['Transparent_0'].outputs[0]
683 inn = nodes['Mix_0'].inputs[2]
684 mat.node_tree.links.new(outn, inn)
686 outn = nodes['Mix_0'].outputs[0]
687 inn = nodes['Mix_1'].inputs[1]
688 mat.node_tree.links.new(outn, inn)
690 outn = nodes['Mix_1'].outputs[0]
691 inn = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')].inputs[0]
692 mat.node_tree.links.new(outn, inn)
694 return mat
697 # --------------------------------------------------------------------
698 # Create cycles brick texture material
699 # --------------------------------------------------------------------
700 def create_brick_material(matname, replace, r, g, b, rv=0.8, gv=0.636, bv=0.315):
701 # Avoid duplicate materials
702 if replace is False:
703 matlist = bpy.data.materials
704 for m in matlist:
705 if m.name == matname:
706 return m
707 # Create material
708 mat = bpy.data.materials.new(matname)
709 mat.use_nodes = True
710 mat.diffuse_color = (rv, gv, bv, 1.0)
711 nodes = mat.node_tree.nodes
713 # support for multilanguage
714 node = nodes[get_node_index(nodes, 'BSDF_DIFFUSE')]
715 node.name = 'Diffuse BSDF'
716 node.label = 'Diffuse BSDF'
718 node.inputs[0].default_value = [r, g, b, 1]
719 node.location = 500, 160
721 node = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')]
722 node.location = 700, 160
724 node = nodes.new('ShaderNodeTexBrick')
725 node.name = 'Brick_0'
726 node.inputs[3].default_value = [0.407, 0.411, 0.394, 1] # mortar color
727 node.inputs[4].default_value = 3 # scale
728 node.inputs[5].default_value = 0.001 # mortar
729 node.inputs[7].default_value = 0.60 # size_w
730 node.inputs[8].default_value = 0.30 # size_h
731 node.location = 300, 160
733 node = nodes.new('ShaderNodeRGB')
734 node.name = 'RGB_0'
735 node.outputs[0].default_value = [r, g, b, 1]
736 node.location = 70, 160
738 # Connect nodes
739 outn = nodes['RGB_0'].outputs['Color']
740 inn = nodes['Brick_0'].inputs['Color1']
741 mat.node_tree.links.new(outn, inn)
743 inn = nodes['Brick_0'].inputs['Color2']
744 mat.node_tree.links.new(outn, inn)
746 outn = nodes['Brick_0'].outputs['Color']
747 inn = nodes['Diffuse BSDF'].inputs['Color']
748 mat.node_tree.links.new(outn, inn)
750 return mat
753 # --------------------------------------------------------------------
754 # Create cycles fabric texture material
755 # --------------------------------------------------------------------
756 def create_fabric_material(matname, replace, r, g, b, rv=0.8, gv=0.636, bv=0.315):
757 # Avoid duplicate materials
758 if replace is False:
759 matlist = bpy.data.materials
760 for m in matlist:
761 if m.name == matname:
762 return m
763 # Create material
764 mat = bpy.data.materials.new(matname)
765 mat.use_nodes = True
766 mat.diffuse_color = (rv, gv, bv, 1.0)
767 nodes = mat.node_tree.nodes
769 # support for multilanguage
770 node = nodes.new('ShaderNodeBsdfDiffuse')
771 node.name = 'Diffuse BSDF'
772 node.inputs[0].default_value = [r, g, b, 1]
773 node.location = 810, 270
775 node = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')]
776 node.location = 1210, 320
778 node = nodes.new('ShaderNodeTexCoord')
779 node.name = 'UVCoordinates'
780 node.location = 26, 395
782 node = nodes.new('ShaderNodeMapping')
783 node.name = 'UVMapping'
784 node.location = 266, 380
785 node.inputs['Scale'].default_value[0] = 1000
786 node.inputs['Scale'].default_value[1] = 1000
787 node.inputs['Scale'].default_value[2] = 1000
789 # ===========================================================================
790 # Image texture
791 # ===========================================================================
792 # Load image file.
794 realpath = path.join(path.dirname(__file__), "images", "fabric_diffuse.png")
795 print("Loading: " + realpath)
796 try:
797 img = bpy.data.images.load(realpath)
798 except:
799 raise NameError("Cannot load image %s" % realpath)
801 # Create image texture from image
802 ctex = bpy.data.textures.new('ColorTex', type='IMAGE')
803 ctex.image = img
805 node = nodes.new('ShaderNodeTexImage')
806 node.name = 'Image1'
807 node.image = ctex.image
808 node.location = 615, 350
810 node = nodes.new('ShaderNodeBsdfTransparent')
811 node.name = 'Transparent1'
812 node.location = 810, 395
813 node.inputs[0].default_value = [r, g, b, 1]
815 node = nodes.new('ShaderNodeAddShader')
816 node.name = 'Add1'
817 node.location = 1040, 356
819 # Connect nodes
820 outn = nodes['UVCoordinates'].outputs['UV']
821 inn = nodes['UVMapping'].inputs['Vector']
822 mat.node_tree.links.new(outn, inn)
824 outn = nodes['UVMapping'].outputs['Vector']
825 inn = nodes['Image1'].inputs['Vector']
826 mat.node_tree.links.new(outn, inn)
828 outn = nodes['Image1'].outputs['Color']
829 inn = nodes['Diffuse BSDF'].inputs['Color']
830 mat.node_tree.links.new(outn, inn)
832 outn = nodes['Transparent1'].outputs['BSDF']
833 inn = nodes['Add1'].inputs[0]
834 mat.node_tree.links.new(outn, inn)
836 outn = nodes['Diffuse BSDF'].outputs['BSDF']
837 inn = nodes['Add1'].inputs[1]
838 mat.node_tree.links.new(outn, inn)
840 outn = nodes['Add1'].outputs['Shader']
841 inn = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')].inputs[0]
842 mat.node_tree.links.new(outn, inn)
844 return mat
847 # --------------------------------------------------------------------
848 # Copy bin file
849 # --------------------------------------------------------------------
850 def copy_binfile(fromfile, tofile):
851 with open(fromfile, 'rb') as f1:
852 with open(tofile, 'wb') as f2:
853 while True:
854 mybytes = f1.read(1024)
855 if mybytes:
856 f2.write(mybytes)
857 else:
858 break
861 # --------------------------------------------------------------------
862 # Parent object (keep positions)
863 # --------------------------------------------------------------------
864 def parentobject(parentobj, childobj):
865 # noinspection PyBroadException
866 try:
867 bpy.ops.object.select_all(action='DESELECT')
868 bpy.context.view_layer.objects.active = parentobj
869 parentobj.select_set(True)
870 childobj.select_set(True)
871 bpy.ops.object.parent_set(type='OBJECT', keep_transform=False)
872 return True
873 except:
874 return False
877 # ------------------------------------------------------------------------------
878 # Create control box
880 # objName: Object name
881 # x: size x axis
882 # y: size y axis
883 # z: size z axis
884 # tube: True create a tube, False only sides
885 # ------------------------------------------------------------------------------
886 def create_control_box(objname, x, y, z, tube=True):
887 myvertex = [(-x / 2, 0, 0.0),
888 (-x / 2, y, 0.0),
889 (x / 2, y, 0.0),
890 (x / 2, 0, 0.0),
891 (-x / 2, 0, z),
892 (-x / 2, y, z),
893 (x / 2, y, z),
894 (x / 2, 0, z)]
896 if tube is True:
897 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)]
898 else:
899 myfaces = [(0, 4, 5, 1), (2, 6, 7, 3)]
901 mesh = bpy.data.meshes.new(objname)
902 myobject = bpy.data.objects.new(objname, mesh)
904 myobject.location = bpy.context.scene.cursor.location
905 bpy.context.collection.objects.link(myobject)
907 mesh.from_pydata(myvertex, [], myfaces)
908 mesh.update(calc_edges=True)
910 return myobject
913 # ------------------------------------------------------------------------------
914 # Remove all children objects
915 # ------------------------------------------------------------------------------
916 def remove_children(myobject):
917 # Remove children
918 for child in myobject.children:
919 # noinspection PyBroadException
920 try:
921 # noinspection PyBroadException
922 try:
923 # remove child relationship
924 for grandchild in child.children:
925 grandchild.parent = None
926 # remove modifiers
927 for mod in child.modifiers:
928 bpy.ops.object.modifier_remove(name=mod.name)
929 except:
930 pass
931 # clear child data
932 if child.type == 'MESH':
933 old = child.data
934 child.select_set(True)
935 bpy.ops.object.delete()
936 bpy.data.meshes.remove(old)
937 if child.type == 'CURVE':
938 child.select_set(True)
939 bpy.ops.object.delete()
940 except:
941 pass
944 # --------------------------------------------------------------------
945 # Get all parents
946 # --------------------------------------------------------------------
947 def get_allparents(myobj):
948 obj = myobj
949 mylist = []
950 while obj.parent is not None:
951 mylist.append(obj)
952 objp = obj.parent
953 obj = objp
955 mylist.append(obj)
957 return mylist
960 # --------------------------------------------------------------------
961 # Verify all faces are in vertice group to avoid Blander crash
963 # Review the faces array and remove any vertex out of the range
964 # this avoid any bug that can appear avoiding Blender crash
965 # --------------------------------------------------------------------
966 def check_mesh_errors(myvertices, myfaces):
967 vmax = len(myvertices)
969 f = 0
970 for face in myfaces:
971 for v in face:
972 if v < 0 or v > vmax:
973 print("Face=" + str(f) + "->removed vertex=" + str(v))
974 myfaces[f].remove(v)
975 f += 1
977 return myfaces