FBX IO: Vertex position access with attributes
[blender-addons.git] / archimesh / achm_tools.py
blob7df86abfb1c580381196dd71c0ec82a5ecc028e0
1 # SPDX-FileCopyrightText: 2016-2023 Blender Foundation
3 # SPDX-License-Identifier: GPL-2.0-or-later
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 boolean_modifier = myobject.modifiers.new("", 'BOOLEAN')
178 boolean_modifier.operation = 'DIFFERENCE'
179 boolean_modifier.object = bolobject
182 # --------------------------------------------------------------------
183 # Set material to object
184 # --------------------------------------------------------------------
185 def set_material(myobject, mymaterial):
186 bpy.context.view_layer.objects.active = myobject
187 if bpy.context.view_layer.objects.active.name == myobject.name:
188 myobject.data.materials.append(mymaterial)
191 # --------------------------------------------------------------------
192 # Set material to selected faces
193 # --------------------------------------------------------------------
194 def set_material_faces(myobject, idx):
195 bpy.context.view_layer.objects.active = myobject
196 myobject.select_set(True)
197 bpy.context.object.active_material_index = idx
198 if bpy.context.view_layer.objects.active.name == myobject.name:
199 bpy.ops.object.mode_set(mode='EDIT')
200 bpy.ops.object.material_slot_assign()
201 # Deselect
202 bpy.ops.mesh.select_all(action='DESELECT')
203 bpy.ops.object.mode_set(mode='OBJECT')
206 # --------------------------------------------------------------------
207 # Select faces
208 # --------------------------------------------------------------------
209 def select_faces(myobject, selface, clear):
210 myobject.select_set(True)
211 bpy.context.view_layer.objects.active = myobject
212 if bpy.context.view_layer.objects.active.name == myobject.name:
213 # deselect everything
214 if clear:
215 bpy.ops.object.mode_set(mode='EDIT')
216 bpy.ops.mesh.select_all(action='DESELECT')
218 # reselect the originally selected face
219 bpy.ops.object.mode_set(mode='OBJECT')
220 myobject.data.polygons[selface].select = True
223 # --------------------------------------------------------------------
224 # Select vertices
225 # --------------------------------------------------------------------
226 def select_vertices(myobject, selvertices, clear=True):
227 myobject.select_set(True)
228 bpy.context.view_layer.objects.active = myobject
229 if bpy.context.view_layer.objects.active.name == myobject.name:
230 # deselect everything
231 if clear:
232 bpy.ops.object.mode_set(mode='EDIT')
233 bpy.ops.mesh.select_all(action='DESELECT')
235 # Select Vertices
236 bpy.ops.object.mode_set(mode='EDIT', toggle=False)
237 sel_mode = bpy.context.tool_settings.mesh_select_mode
239 bpy.context.tool_settings.mesh_select_mode = [True, False, False]
240 bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
242 for i in selvertices:
243 myobject.data.vertices[i].select = True
245 bpy.ops.object.mode_set(mode='EDIT', toggle=False)
246 bpy.context.tool_settings.mesh_select_mode = sel_mode
247 bpy.ops.object.mode_set(mode='OBJECT')
250 # --------------------------------------------------------------------
251 # Mark Seam
252 # --------------------------------------------------------------------
253 def mark_seam(myobject):
254 # noinspection PyBroadException
255 try:
256 myobject.select_set(True)
257 bpy.context.view_layer.objects.active = myobject
258 if bpy.context.view_layer.objects.active.name == myobject.name:
259 bpy.ops.object.mode_set(mode='EDIT', toggle=False)
260 bpy.ops.mesh.mark_seam()
261 bpy.ops.object.mode_set(mode='OBJECT')
262 except:
263 bpy.ops.object.mode_set(mode='OBJECT')
266 # --------------------------------------------------------------------
267 # Unwrap mesh
268 # --------------------------------------------------------------------
269 def unwrap_mesh(myobject, allfaces=True):
270 # noinspection PyBroadException
271 try:
272 myobject.select_set(True)
273 bpy.context.view_layer.objects.active = myobject
274 if bpy.context.view_layer.objects.active.name == myobject.name:
275 # Unwrap
276 bpy.ops.object.mode_set(mode='EDIT', toggle=False)
277 if allfaces is True:
278 bpy.ops.mesh.select_all(action='DESELECT')
279 bpy.ops.mesh.select_all()
280 bpy.ops.uv.unwrap()
281 bpy.ops.object.mode_set(mode='OBJECT')
282 except:
283 bpy.ops.object.mode_set(mode='OBJECT')
286 # --------------------------------------------------------------------
287 # Get Node Index(multilanguage support)
288 # --------------------------------------------------------------------
289 def get_node_index(nodes, datatype):
290 idx = 0
291 for m in nodes:
292 if m.type == datatype:
293 return idx
294 idx += 1
296 # by default
297 return 1
300 # --------------------------------------------------------------------
301 # Create cycles diffuse material
302 # --------------------------------------------------------------------
303 def create_diffuse_material(matname, replace, r, g, b, rv=0.8, gv=0.8, bv=0.8, mix=0.1, twosides=False):
304 # Avoid duplicate materials
305 if replace is False:
306 matlist = bpy.data.materials
307 for m in matlist:
308 if m.name == matname:
309 return m
310 # Create material
311 mat = bpy.data.materials.new(matname)
312 mat.diffuse_color = (rv, gv, bv, 1.0) # viewport color
313 mat.use_nodes = True
314 nodes = mat.node_tree.nodes
316 # support for multilanguage
317 node = nodes.new('ShaderNodeBsdfDiffuse')
318 node.name = 'Diffuse BSDF'
319 node.inputs[0].default_value = [r, g, b, 1]
320 node.location = 200, 320
322 node = nodes.new('ShaderNodeBsdfGlossy')
323 node.name = 'Glossy_0'
324 node.location = 200, 0
326 node = nodes.new('ShaderNodeMixShader')
327 node.name = 'Mix_0'
328 node.inputs[0].default_value = mix
329 node.location = 500, 160
331 node = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')]
332 node.location = 1100, 160
334 # Connect nodes
335 outn = nodes['Diffuse BSDF'].outputs[0]
336 inn = nodes['Mix_0'].inputs[1]
337 mat.node_tree.links.new(outn, inn)
339 outn = nodes['Glossy_0'].outputs[0]
340 inn = nodes['Mix_0'].inputs[2]
341 mat.node_tree.links.new(outn, inn)
343 if twosides is False:
344 outn = nodes['Mix_0'].outputs[0]
345 inn = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')].inputs[0]
346 mat.node_tree.links.new(outn, inn)
348 if twosides is True:
349 node = nodes.new('ShaderNodeNewGeometry')
350 node.name = 'Input_1'
351 node.location = -80, -70
353 node = nodes.new('ShaderNodeBsdfDiffuse')
354 node.name = 'Diffuse_1'
355 node.inputs[0].default_value = [0.30, 0.30, 0.30, 1]
356 node.location = 200, -280
358 node = nodes.new('ShaderNodeMixShader')
359 node.name = 'Mix_1'
360 node.inputs[0].default_value = mix
361 node.location = 800, -70
363 outn = nodes['Input_1'].outputs[6]
364 inn = nodes['Mix_1'].inputs[0]
365 mat.node_tree.links.new(outn, inn)
367 outn = nodes['Diffuse_1'].outputs[0]
368 inn = nodes['Mix_1'].inputs[2]
369 mat.node_tree.links.new(outn, inn)
371 outn = nodes['Mix_0'].outputs[0]
372 inn = nodes['Mix_1'].inputs[1]
373 mat.node_tree.links.new(outn, inn)
375 outn = nodes['Mix_1'].outputs[0]
376 inn = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')].inputs[0]
377 mat.node_tree.links.new(outn, inn)
379 return mat
382 # --------------------------------------------------------------------
383 # Create cycles translucent material
384 # --------------------------------------------------------------------
385 def create_translucent_material(matname, replace, r, g, b, rv=0.8, gv=0.8, bv=0.8, mix=0.1):
386 # Avoid duplicate materials
387 if replace is False:
388 matlist = bpy.data.materials
389 for m in matlist:
390 if m.name == matname:
391 return m
392 # Create material
393 mat = bpy.data.materials.new(matname)
394 mat.diffuse_color = (rv, gv, bv, 1.0) # viewport color
395 mat.use_nodes = True
396 nodes = mat.node_tree.nodes
398 # support for multilanguage
399 node = nodes.new('ShaderNodeBsdfDiffuse')
400 node.name = 'Diffuse BSDF'
401 node.inputs[0].default_value = [r, g, b, 1]
402 node.location = 200, 320
404 node = nodes.new('ShaderNodeBsdfTranslucent')
405 node.name = 'Translucent_0'
406 node.location = 200, 0
408 node = nodes.new('ShaderNodeMixShader')
409 node.name = 'Mix_0'
410 node.inputs[0].default_value = mix
411 node.location = 500, 160
413 node = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')]
414 node.location = 1100, 160
416 # Connect nodes
417 outn = nodes['Diffuse BSDF'].outputs[0]
418 inn = nodes['Mix_0'].inputs[1]
419 mat.node_tree.links.new(outn, inn)
421 outn = nodes['Translucent_0'].outputs[0]
422 inn = nodes['Mix_0'].inputs[2]
423 mat.node_tree.links.new(outn, inn)
425 outn = nodes['Mix_0'].outputs[0]
426 inn = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')].inputs[0]
427 mat.node_tree.links.new(outn, inn)
429 return mat
432 # --------------------------------------------------------------------
433 # Create cycles glass material
434 # --------------------------------------------------------------------
435 def create_glass_material(matname, replace, rv=0.333, gv=0.342, bv=0.9):
436 # Avoid duplicate materials
437 if replace is False:
438 matlist = bpy.data.materials
439 for m in matlist:
440 if m.name == matname:
441 return m
442 # Create material
443 mat = bpy.data.materials.new(matname)
444 mat.use_nodes = True
445 mat.diffuse_color = (rv, gv, bv, 1.0)
446 nodes = mat.node_tree.nodes
448 # support for multilanguage
449 node = nodes[get_node_index(nodes, 'BSDF_DIFFUSE')]
450 mat.node_tree.nodes.remove(node) # remove not used
452 node = nodes.new('ShaderNodeLightPath')
453 node.name = 'Light_0'
454 node.location = 10, 160
456 node = nodes.new('ShaderNodeBsdfRefraction')
457 node.name = 'Refraction_0'
458 node.inputs[2].default_value = 1 # IOR 1.0
459 node.location = 250, 400
461 node = nodes.new('ShaderNodeBsdfGlossy')
462 node.name = 'Glossy_0'
463 node.distribution = 'SHARP'
464 node.location = 250, 100
466 node = nodes.new('ShaderNodeBsdfTransparent')
467 node.name = 'Transparent_0'
468 node.location = 500, 10
470 node = nodes.new('ShaderNodeMixShader')
471 node.name = 'Mix_0'
472 node.inputs[0].default_value = 0.035
473 node.location = 500, 160
475 node = nodes.new('ShaderNodeMixShader')
476 node.name = 'Mix_1'
477 node.inputs[0].default_value = 0.1
478 node.location = 690, 290
480 node = nodes.new('ShaderNodeOutputMaterial')
481 node.name = 'OUTPUT_MATERIAL'
482 node.location = 920, 290
484 # Connect nodes
485 outn = nodes['Light_0'].outputs[1]
486 inn = nodes['Mix_1'].inputs[0]
487 mat.node_tree.links.new(outn, inn)
489 outn = nodes['Refraction_0'].outputs[0]
490 inn = nodes['Mix_0'].inputs[1]
491 mat.node_tree.links.new(outn, inn)
493 outn = nodes['Glossy_0'].outputs[0]
494 inn = nodes['Mix_0'].inputs[2]
495 mat.node_tree.links.new(outn, inn)
497 outn = nodes['Mix_0'].outputs[0]
498 inn = nodes['Mix_1'].inputs[1]
499 mat.node_tree.links.new(outn, inn)
501 outn = nodes['Transparent_0'].outputs[0]
502 inn = nodes['Mix_1'].inputs[2]
503 mat.node_tree.links.new(outn, inn)
505 outn = nodes['Mix_1'].outputs[0]
506 inn = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')].inputs[0]
507 mat.node_tree.links.new(outn, inn)
509 return mat
512 # ---------------------------------------------
513 # Create cycles transparents material
514 # --------------------------------------------------------------------
515 def create_transparent_material(matname, replace, r=1, g=1, b=1, alpha=0):
516 # Avoid duplicate materials
517 if replace is False:
518 matlist = bpy.data.materials
519 for m in matlist:
520 if m.name == matname:
521 return m
522 # Create material
523 mat = bpy.data.materials.new(matname)
524 mat.use_nodes = True
525 mat.diffuse_color = (r, g, b, 1.0)
526 nodes = mat.node_tree.nodes
528 # support for multilanguage
529 node = nodes[get_node_index(nodes, 'BSDF_DIFFUSE')]
530 mat.node_tree.nodes.remove(node) # remove not used
532 node = nodes.new('ShaderNodeBsdfTransparent')
533 node.name = 'Transparent_0'
534 node.location = 250, 160
535 node.inputs[0].default_value = [r, g, b, alpha]
537 node = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')]
538 node.location = 700, 160
540 # Connect nodes
541 outn = nodes['Transparent_0'].outputs[0]
542 inn = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')].inputs[0]
543 mat.node_tree.links.new(outn, inn)
545 return mat
548 # --------------------------------------------------------------------
549 # Create cycles glossy material
550 # --------------------------------------------------------------------
551 def create_glossy_material(matname, replace, r, g, b, rv=0.578, gv=0.555, bv=0.736, rvalue=0.2):
552 # Avoid duplicate materials
553 if replace is False:
554 matlist = bpy.data.materials
555 for m in matlist:
556 if m.name == matname:
557 return m
558 # Create material
559 mat = bpy.data.materials.new(matname)
560 mat.use_nodes = True
561 mat.diffuse_color = (rv, gv, bv, 1.0)
562 nodes = mat.node_tree.nodes
564 # support for multilanguage
565 node = nodes[get_node_index(nodes, 'BSDF_DIFFUSE')]
566 mat.node_tree.nodes.remove(node) # remove not used
568 node = nodes.new('ShaderNodeBsdfGlossy')
569 node.name = 'Glossy_0'
570 node.inputs[0].default_value = [r, g, b, 1]
571 node.inputs[1].default_value = rvalue
572 node.location = 200, 160
574 node = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')]
575 node.location = 700, 160
577 # Connect nodes
578 outn = nodes['Glossy_0'].outputs[0]
579 inn = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')].inputs[0]
580 mat.node_tree.links.new(outn, inn)
582 return mat
585 # --------------------------------------------------------------------
586 # Create cycles emission material
587 # --------------------------------------------------------------------
588 def create_emission_material(matname, replace, r, g, b, energy):
589 # Avoid duplicate materials
590 if replace is False:
591 matlist = bpy.data.materials
592 for m in matlist:
593 if m.name == matname:
594 return m
595 # Create material
596 mat = bpy.data.materials.new(matname)
597 mat.use_nodes = True
598 nodes = mat.node_tree.nodes
600 # support for multilanguage
601 node = nodes[get_node_index(nodes, 'BSDF_DIFFUSE')]
602 mat.node_tree.nodes.remove(node) # remove not used
604 node = nodes.new('ShaderNodeEmission')
605 node.name = 'Emission_0'
606 node.inputs[0].default_value = [r, g, b, 1]
607 node.inputs[1].default_value = energy
608 node.location = 200, 160
610 node = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')]
611 node.location = 700, 160
613 # Connect nodes
614 outn = nodes['Emission_0'].outputs[0]
615 inn = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')].inputs[0]
616 mat.node_tree.links.new(outn, inn)
618 return mat
621 # --------------------------------------------------------------------
622 # Create cycles glass material
623 # --------------------------------------------------------------------
624 def create_old_glass_material(matname, replace, rv=0.352716, gv=0.760852, bv=0.9):
625 # Avoid duplicate materials
626 if replace is False:
627 matlist = bpy.data.materials
628 for m in matlist:
629 if m.name == matname:
630 return m
631 # Create material
632 mat = bpy.data.materials.new(matname)
633 mat.use_nodes = True
634 mat.diffuse_color = (rv, gv, bv, 1.0)
635 nodes = mat.node_tree.nodes
637 # support for multilanguage
638 node = nodes[get_node_index(nodes, 'BSDF_DIFFUSE')]
639 mat.node_tree.nodes.remove(node) # remove not used
641 node = nodes.new('ShaderNodeLightPath')
642 node.name = 'Light_0'
643 node.location = 10, 160
645 node = nodes.new('ShaderNodeBsdfGlass')
646 node.name = 'Glass_0'
647 node.location = 250, 300
649 node = nodes.new('ShaderNodeBsdfTransparent')
650 node.name = 'Transparent_0'
651 node.location = 250, 0
653 node = nodes.new('ShaderNodeMixShader')
654 node.name = 'Mix_0'
655 node.inputs[0].default_value = 0.1
656 node.location = 500, 160
658 node = nodes.new('ShaderNodeMixShader')
659 node.name = 'Mix_1'
660 node.inputs[0].default_value = 0.1
661 node.location = 690, 290
663 node = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')]
664 node.location = 920, 290
666 # Connect nodes
667 outn = nodes['Light_0'].outputs[1]
668 inn = nodes['Mix_0'].inputs[0]
669 mat.node_tree.links.new(outn, inn)
671 outn = nodes['Light_0'].outputs[2]
672 inn = nodes['Mix_1'].inputs[0]
673 mat.node_tree.links.new(outn, inn)
675 outn = nodes['Glass_0'].outputs[0]
676 inn = nodes['Mix_0'].inputs[1]
677 mat.node_tree.links.new(outn, inn)
679 outn = nodes['Transparent_0'].outputs[0]
680 inn = nodes['Mix_0'].inputs[2]
681 mat.node_tree.links.new(outn, inn)
683 outn = nodes['Mix_0'].outputs[0]
684 inn = nodes['Mix_1'].inputs[1]
685 mat.node_tree.links.new(outn, inn)
687 outn = nodes['Mix_1'].outputs[0]
688 inn = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')].inputs[0]
689 mat.node_tree.links.new(outn, inn)
691 return mat
694 # --------------------------------------------------------------------
695 # Create cycles brick texture material
696 # --------------------------------------------------------------------
697 def create_brick_material(matname, replace, r, g, b, rv=0.8, gv=0.636, bv=0.315):
698 # Avoid duplicate materials
699 if replace is False:
700 matlist = bpy.data.materials
701 for m in matlist:
702 if m.name == matname:
703 return m
704 # Create material
705 mat = bpy.data.materials.new(matname)
706 mat.use_nodes = True
707 mat.diffuse_color = (rv, gv, bv, 1.0)
708 nodes = mat.node_tree.nodes
710 # support for multilanguage
711 principled_node = nodes[get_node_index(nodes, 'BSDF_PRINCIPLED')]
713 principled_node.inputs[0].default_value = [r, g, b, 1]
714 principled_node.location = 500, 160
716 output_node = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')]
717 output_node.location = 700, 160
719 brick_node = nodes.new('ShaderNodeTexBrick')
720 brick_node.name = 'Brick_0'
721 brick_node.inputs[3].default_value = [0.407, 0.411, 0.394, 1] # mortar color
722 brick_node.inputs[4].default_value = 3 # scale
723 brick_node.inputs[5].default_value = 0.001 # mortar
724 brick_node.inputs[7].default_value = 0.60 # size_w
725 brick_node.inputs[8].default_value = 0.30 # size_h
726 brick_node.location = 300, 160
728 rgb_node = nodes.new('ShaderNodeRGB')
729 rgb_node.name = 'RGB_0'
730 rgb_node.outputs[0].default_value = [r, g, b, 1]
731 rgb_node.location = 70, 160
733 # Connect nodes
734 outn = rgb_node.outputs['Color']
735 inn = brick_node.inputs['Color1']
736 mat.node_tree.links.new(outn, inn)
738 inn = brick_node.inputs['Color2']
739 mat.node_tree.links.new(outn, inn)
741 outn = brick_node.outputs['Color']
742 inn = principled_node.inputs['Base Color']
743 mat.node_tree.links.new(outn, inn)
745 return mat
748 # --------------------------------------------------------------------
749 # Create cycles fabric texture material
750 # --------------------------------------------------------------------
751 def create_fabric_material(matname, replace, r, g, b, rv=0.8, gv=0.636, bv=0.315):
752 # Avoid duplicate materials
753 if replace is False:
754 matlist = bpy.data.materials
755 for m in matlist:
756 if m.name == matname:
757 return m
758 # Create material
759 mat = bpy.data.materials.new(matname)
760 mat.use_nodes = True
761 mat.diffuse_color = (rv, gv, bv, 1.0)
762 nodes = mat.node_tree.nodes
764 # support for multilanguage
765 node = nodes.new('ShaderNodeBsdfDiffuse')
766 node.name = 'Diffuse BSDF'
767 node.inputs[0].default_value = [r, g, b, 1]
768 node.location = 810, 270
770 node = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')]
771 node.location = 1210, 320
773 node = nodes.new('ShaderNodeTexCoord')
774 node.name = 'UVCoordinates'
775 node.location = 26, 395
777 node = nodes.new('ShaderNodeMapping')
778 node.name = 'UVMapping'
779 node.location = 266, 380
780 node.inputs['Scale'].default_value[0] = 1000
781 node.inputs['Scale'].default_value[1] = 1000
782 node.inputs['Scale'].default_value[2] = 1000
784 # ===========================================================================
785 # Image texture
786 # ===========================================================================
787 # Load image file.
789 realpath = path.join(path.dirname(__file__), "images", "fabric_diffuse.png")
790 print("Loading: " + realpath)
791 try:
792 img = bpy.data.images.load(realpath)
793 except:
794 raise NameError("Cannot load image %s" % realpath)
796 # Create image texture from image
797 ctex = bpy.data.textures.new('ColorTex', type='IMAGE')
798 ctex.image = img
800 node = nodes.new('ShaderNodeTexImage')
801 node.name = 'Image1'
802 node.image = ctex.image
803 node.location = 615, 350
805 node = nodes.new('ShaderNodeBsdfTransparent')
806 node.name = 'Transparent1'
807 node.location = 810, 395
808 node.inputs[0].default_value = [r, g, b, 1]
810 node = nodes.new('ShaderNodeAddShader')
811 node.name = 'Add1'
812 node.location = 1040, 356
814 # Connect nodes
815 outn = nodes['UVCoordinates'].outputs['UV']
816 inn = nodes['UVMapping'].inputs['Vector']
817 mat.node_tree.links.new(outn, inn)
819 outn = nodes['UVMapping'].outputs['Vector']
820 inn = nodes['Image1'].inputs['Vector']
821 mat.node_tree.links.new(outn, inn)
823 outn = nodes['Image1'].outputs['Color']
824 inn = nodes['Diffuse BSDF'].inputs['Color']
825 mat.node_tree.links.new(outn, inn)
827 outn = nodes['Transparent1'].outputs['BSDF']
828 inn = nodes['Add1'].inputs[0]
829 mat.node_tree.links.new(outn, inn)
831 outn = nodes['Diffuse BSDF'].outputs['BSDF']
832 inn = nodes['Add1'].inputs[1]
833 mat.node_tree.links.new(outn, inn)
835 outn = nodes['Add1'].outputs['Shader']
836 inn = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')].inputs[0]
837 mat.node_tree.links.new(outn, inn)
839 return mat
842 # --------------------------------------------------------------------
843 # Copy bin file
844 # --------------------------------------------------------------------
845 def copy_binfile(fromfile, tofile):
846 with open(fromfile, 'rb') as f1:
847 with open(tofile, 'wb') as f2:
848 while True:
849 mybytes = f1.read(1024)
850 if mybytes:
851 f2.write(mybytes)
852 else:
853 break
856 # --------------------------------------------------------------------
857 # Parent object (keep positions)
858 # --------------------------------------------------------------------
859 def parentobject(parentobj, childobj):
860 # noinspection PyBroadException
861 try:
862 bpy.ops.object.select_all(action='DESELECT')
863 bpy.context.view_layer.objects.active = parentobj
864 parentobj.select_set(True)
865 childobj.select_set(True)
866 bpy.ops.object.parent_set(type='OBJECT', keep_transform=False)
867 return True
868 except:
869 return False
872 # ------------------------------------------------------------------------------
873 # Create control box
875 # objName: Object name
876 # x: size x axis
877 # y: size y axis
878 # z: size z axis
879 # tube: True create a tube, False only sides
880 # ------------------------------------------------------------------------------
881 def create_control_box(objname, x, y, z, tube=True):
882 myvertex = [(-x / 2, 0, 0.0),
883 (-x / 2, y, 0.0),
884 (x / 2, y, 0.0),
885 (x / 2, 0, 0.0),
886 (-x / 2, 0, z),
887 (-x / 2, y, z),
888 (x / 2, y, z),
889 (x / 2, 0, z)]
891 if tube is True:
892 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)]
893 else:
894 myfaces = [(0, 4, 5, 1), (2, 6, 7, 3)]
896 mesh = bpy.data.meshes.new(objname)
897 myobject = bpy.data.objects.new(objname, mesh)
899 myobject.location = bpy.context.scene.cursor.location
900 bpy.context.collection.objects.link(myobject)
902 mesh.from_pydata(myvertex, [], myfaces)
903 mesh.update(calc_edges=True)
905 return myobject
908 # ------------------------------------------------------------------------------
909 # Remove all children objects
910 # ------------------------------------------------------------------------------
911 def remove_children(myobject):
912 # Remove children
913 for child in myobject.children:
914 # noinspection PyBroadException
915 try:
916 # noinspection PyBroadException
917 try:
918 # remove child relationship
919 for grandchild in child.children:
920 grandchild.parent = None
921 # remove modifiers
922 for mod in child.modifiers:
923 bpy.ops.object.modifier_remove(name=mod.name)
924 except:
925 pass
926 # clear child data
927 if child.type == 'MESH':
928 old = child.data
929 child.select_set(True)
930 bpy.ops.object.delete()
931 bpy.data.meshes.remove(old)
932 if child.type == 'CURVE':
933 child.select_set(True)
934 bpy.ops.object.delete()
935 except:
936 pass
939 # --------------------------------------------------------------------
940 # Get all parents
941 # --------------------------------------------------------------------
942 def get_allparents(myobj):
943 obj = myobj
944 mylist = []
945 while obj.parent is not None:
946 mylist.append(obj)
947 objp = obj.parent
948 obj = objp
950 mylist.append(obj)
952 return mylist
955 # --------------------------------------------------------------------
956 # Verify all faces are in vertice group to avoid Blander crash
958 # Review the faces array and remove any vertex out of the range
959 # this avoid any bug that can appear avoiding Blender crash
960 # --------------------------------------------------------------------
961 def check_mesh_errors(myvertices, myfaces):
962 vmax = len(myvertices)
964 f = 0
965 for face in myfaces:
966 for v in face:
967 if v < 0 or v > vmax:
968 print("Face=" + str(f) + "->removed vertex=" + str(v))
969 myfaces[f].remove(v)
970 f += 1
972 return myfaces