Fix #105009: AnimAll: Error when inserting key on string attribute
[blender-addons.git] / archimesh / achm_tools.py
blobba0265e0753428c9530c541eb7a30467bd39481e
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.location = 250, 100
465 node = nodes.new('ShaderNodeBsdfTransparent')
466 node.name = 'Transparent_0'
467 node.location = 500, 10
469 node = nodes.new('ShaderNodeMixShader')
470 node.name = 'Mix_0'
471 node.inputs[0].default_value = 0.035
472 node.location = 500, 160
474 node = nodes.new('ShaderNodeMixShader')
475 node.name = 'Mix_1'
476 node.inputs[0].default_value = 0.1
477 node.location = 690, 290
479 node = nodes.new('ShaderNodeOutputMaterial')
480 node.name = 'OUTPUT_MATERIAL'
481 node.location = 920, 290
483 # Connect nodes
484 outn = nodes['Light_0'].outputs[1]
485 inn = nodes['Mix_1'].inputs[0]
486 mat.node_tree.links.new(outn, inn)
488 outn = nodes['Refraction_0'].outputs[0]
489 inn = nodes['Mix_0'].inputs[1]
490 mat.node_tree.links.new(outn, inn)
492 outn = nodes['Glossy_0'].outputs[0]
493 inn = nodes['Mix_0'].inputs[2]
494 mat.node_tree.links.new(outn, inn)
496 outn = nodes['Mix_0'].outputs[0]
497 inn = nodes['Mix_1'].inputs[1]
498 mat.node_tree.links.new(outn, inn)
500 outn = nodes['Transparent_0'].outputs[0]
501 inn = nodes['Mix_1'].inputs[2]
502 mat.node_tree.links.new(outn, inn)
504 outn = nodes['Mix_1'].outputs[0]
505 inn = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')].inputs[0]
506 mat.node_tree.links.new(outn, inn)
508 return mat
511 # ---------------------------------------------
512 # Create cycles transparents material
513 # --------------------------------------------------------------------
514 def create_transparent_material(matname, replace, r=1, g=1, b=1, alpha=0):
515 # Avoid duplicate materials
516 if replace is False:
517 matlist = bpy.data.materials
518 for m in matlist:
519 if m.name == matname:
520 return m
521 # Create material
522 mat = bpy.data.materials.new(matname)
523 mat.use_nodes = True
524 mat.diffuse_color = (r, g, b, 1.0)
525 nodes = mat.node_tree.nodes
527 # support for multilanguage
528 node = nodes[get_node_index(nodes, 'BSDF_DIFFUSE')]
529 mat.node_tree.nodes.remove(node) # remove not used
531 node = nodes.new('ShaderNodeBsdfTransparent')
532 node.name = 'Transparent_0'
533 node.location = 250, 160
534 node.inputs[0].default_value = [r, g, b, alpha]
536 node = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')]
537 node.location = 700, 160
539 # Connect nodes
540 outn = nodes['Transparent_0'].outputs[0]
541 inn = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')].inputs[0]
542 mat.node_tree.links.new(outn, inn)
544 return mat
547 # --------------------------------------------------------------------
548 # Create cycles glossy material
549 # --------------------------------------------------------------------
550 def create_glossy_material(matname, replace, r, g, b, rv=0.578, gv=0.555, bv=0.736, rvalue=0.2):
551 # Avoid duplicate materials
552 if replace is False:
553 matlist = bpy.data.materials
554 for m in matlist:
555 if m.name == matname:
556 return m
557 # Create material
558 mat = bpy.data.materials.new(matname)
559 mat.use_nodes = True
560 mat.diffuse_color = (rv, gv, bv, 1.0)
561 nodes = mat.node_tree.nodes
563 # support for multilanguage
564 node = nodes[get_node_index(nodes, 'BSDF_DIFFUSE')]
565 mat.node_tree.nodes.remove(node) # remove not used
567 node = nodes.new('ShaderNodeBsdfGlossy')
568 node.name = 'Glossy_0'
569 node.inputs[0].default_value = [r, g, b, 1]
570 node.inputs[1].default_value = rvalue
571 node.location = 200, 160
573 node = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')]
574 node.location = 700, 160
576 # Connect nodes
577 outn = nodes['Glossy_0'].outputs[0]
578 inn = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')].inputs[0]
579 mat.node_tree.links.new(outn, inn)
581 return mat
584 # --------------------------------------------------------------------
585 # Create cycles emission material
586 # --------------------------------------------------------------------
587 def create_emission_material(matname, replace, r, g, b, energy):
588 # Avoid duplicate materials
589 if replace is False:
590 matlist = bpy.data.materials
591 for m in matlist:
592 if m.name == matname:
593 return m
594 # Create material
595 mat = bpy.data.materials.new(matname)
596 mat.use_nodes = True
597 nodes = mat.node_tree.nodes
599 # support for multilanguage
600 node = nodes[get_node_index(nodes, 'BSDF_DIFFUSE')]
601 mat.node_tree.nodes.remove(node) # remove not used
603 node = nodes.new('ShaderNodeEmission')
604 node.name = 'Emission_0'
605 node.inputs[0].default_value = [r, g, b, 1]
606 node.inputs[1].default_value = energy
607 node.location = 200, 160
609 node = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')]
610 node.location = 700, 160
612 # Connect nodes
613 outn = nodes['Emission_0'].outputs[0]
614 inn = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')].inputs[0]
615 mat.node_tree.links.new(outn, inn)
617 return mat
620 # --------------------------------------------------------------------
621 # Create cycles glass material
622 # --------------------------------------------------------------------
623 def create_old_glass_material(matname, replace, rv=0.352716, gv=0.760852, bv=0.9):
624 # Avoid duplicate materials
625 if replace is False:
626 matlist = bpy.data.materials
627 for m in matlist:
628 if m.name == matname:
629 return m
630 # Create material
631 mat = bpy.data.materials.new(matname)
632 mat.use_nodes = True
633 mat.diffuse_color = (rv, gv, bv, 1.0)
634 nodes = mat.node_tree.nodes
636 # support for multilanguage
637 node = nodes[get_node_index(nodes, 'BSDF_DIFFUSE')]
638 mat.node_tree.nodes.remove(node) # remove not used
640 node = nodes.new('ShaderNodeLightPath')
641 node.name = 'Light_0'
642 node.location = 10, 160
644 node = nodes.new('ShaderNodeBsdfGlass')
645 node.name = 'Glass_0'
646 node.location = 250, 300
648 node = nodes.new('ShaderNodeBsdfTransparent')
649 node.name = 'Transparent_0'
650 node.location = 250, 0
652 node = nodes.new('ShaderNodeMixShader')
653 node.name = 'Mix_0'
654 node.inputs[0].default_value = 0.1
655 node.location = 500, 160
657 node = nodes.new('ShaderNodeMixShader')
658 node.name = 'Mix_1'
659 node.inputs[0].default_value = 0.1
660 node.location = 690, 290
662 node = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')]
663 node.location = 920, 290
665 # Connect nodes
666 outn = nodes['Light_0'].outputs[1]
667 inn = nodes['Mix_0'].inputs[0]
668 mat.node_tree.links.new(outn, inn)
670 outn = nodes['Light_0'].outputs[2]
671 inn = nodes['Mix_1'].inputs[0]
672 mat.node_tree.links.new(outn, inn)
674 outn = nodes['Glass_0'].outputs[0]
675 inn = nodes['Mix_0'].inputs[1]
676 mat.node_tree.links.new(outn, inn)
678 outn = nodes['Transparent_0'].outputs[0]
679 inn = nodes['Mix_0'].inputs[2]
680 mat.node_tree.links.new(outn, inn)
682 outn = nodes['Mix_0'].outputs[0]
683 inn = nodes['Mix_1'].inputs[1]
684 mat.node_tree.links.new(outn, inn)
686 outn = nodes['Mix_1'].outputs[0]
687 inn = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')].inputs[0]
688 mat.node_tree.links.new(outn, inn)
690 return mat
693 # --------------------------------------------------------------------
694 # Create cycles brick texture material
695 # --------------------------------------------------------------------
696 def create_brick_material(matname, replace, r, g, b, rv=0.8, gv=0.636, bv=0.315):
697 # Avoid duplicate materials
698 if replace is False:
699 matlist = bpy.data.materials
700 for m in matlist:
701 if m.name == matname:
702 return m
703 # Create material
704 mat = bpy.data.materials.new(matname)
705 mat.use_nodes = True
706 mat.diffuse_color = (rv, gv, bv, 1.0)
707 nodes = mat.node_tree.nodes
709 # support for multilanguage
710 principled_node = nodes[get_node_index(nodes, 'BSDF_PRINCIPLED')]
712 principled_node.inputs[0].default_value = [r, g, b, 1]
713 principled_node.location = 500, 160
715 output_node = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')]
716 output_node.location = 700, 160
718 brick_node = nodes.new('ShaderNodeTexBrick')
719 brick_node.name = 'Brick_0'
720 brick_node.inputs[3].default_value = [0.407, 0.411, 0.394, 1] # mortar color
721 brick_node.inputs[4].default_value = 3 # scale
722 brick_node.inputs[5].default_value = 0.001 # mortar
723 brick_node.inputs[7].default_value = 0.60 # size_w
724 brick_node.inputs[8].default_value = 0.30 # size_h
725 brick_node.location = 300, 160
727 rgb_node = nodes.new('ShaderNodeRGB')
728 rgb_node.name = 'RGB_0'
729 rgb_node.outputs[0].default_value = [r, g, b, 1]
730 rgb_node.location = 70, 160
732 # Connect nodes
733 outn = rgb_node.outputs['Color']
734 inn = brick_node.inputs['Color1']
735 mat.node_tree.links.new(outn, inn)
737 inn = brick_node.inputs['Color2']
738 mat.node_tree.links.new(outn, inn)
740 outn = brick_node.outputs['Color']
741 inn = principled_node.inputs['Base Color']
742 mat.node_tree.links.new(outn, inn)
744 return mat
747 # --------------------------------------------------------------------
748 # Create cycles fabric texture material
749 # --------------------------------------------------------------------
750 def create_fabric_material(matname, replace, r, g, b, rv=0.8, gv=0.636, bv=0.315):
751 # Avoid duplicate materials
752 if replace is False:
753 matlist = bpy.data.materials
754 for m in matlist:
755 if m.name == matname:
756 return m
757 # Create material
758 mat = bpy.data.materials.new(matname)
759 mat.use_nodes = True
760 mat.diffuse_color = (rv, gv, bv, 1.0)
761 nodes = mat.node_tree.nodes
763 # support for multilanguage
764 node = nodes.new('ShaderNodeBsdfDiffuse')
765 node.name = 'Diffuse BSDF'
766 node.inputs[0].default_value = [r, g, b, 1]
767 node.location = 810, 270
769 node = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')]
770 node.location = 1210, 320
772 node = nodes.new('ShaderNodeTexCoord')
773 node.name = 'UVCoordinates'
774 node.location = 26, 395
776 node = nodes.new('ShaderNodeMapping')
777 node.name = 'UVMapping'
778 node.location = 266, 380
779 node.inputs['Scale'].default_value[0] = 1000
780 node.inputs['Scale'].default_value[1] = 1000
781 node.inputs['Scale'].default_value[2] = 1000
783 # ===========================================================================
784 # Image texture
785 # ===========================================================================
786 # Load image file.
788 realpath = path.join(path.dirname(__file__), "images", "fabric_diffuse.png")
789 print("Loading: " + realpath)
790 try:
791 img = bpy.data.images.load(realpath)
792 except:
793 raise NameError("Cannot load image %s" % realpath)
795 # Create image texture from image
796 ctex = bpy.data.textures.new('ColorTex', type='IMAGE')
797 ctex.image = img
799 node = nodes.new('ShaderNodeTexImage')
800 node.name = 'Image1'
801 node.image = ctex.image
802 node.location = 615, 350
804 node = nodes.new('ShaderNodeBsdfTransparent')
805 node.name = 'Transparent1'
806 node.location = 810, 395
807 node.inputs[0].default_value = [r, g, b, 1]
809 node = nodes.new('ShaderNodeAddShader')
810 node.name = 'Add1'
811 node.location = 1040, 356
813 # Connect nodes
814 outn = nodes['UVCoordinates'].outputs['UV']
815 inn = nodes['UVMapping'].inputs['Vector']
816 mat.node_tree.links.new(outn, inn)
818 outn = nodes['UVMapping'].outputs['Vector']
819 inn = nodes['Image1'].inputs['Vector']
820 mat.node_tree.links.new(outn, inn)
822 outn = nodes['Image1'].outputs['Color']
823 inn = nodes['Diffuse BSDF'].inputs['Color']
824 mat.node_tree.links.new(outn, inn)
826 outn = nodes['Transparent1'].outputs['BSDF']
827 inn = nodes['Add1'].inputs[0]
828 mat.node_tree.links.new(outn, inn)
830 outn = nodes['Diffuse BSDF'].outputs['BSDF']
831 inn = nodes['Add1'].inputs[1]
832 mat.node_tree.links.new(outn, inn)
834 outn = nodes['Add1'].outputs['Shader']
835 inn = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')].inputs[0]
836 mat.node_tree.links.new(outn, inn)
838 return mat
841 # --------------------------------------------------------------------
842 # Copy bin file
843 # --------------------------------------------------------------------
844 def copy_binfile(fromfile, tofile):
845 with open(fromfile, 'rb') as f1:
846 with open(tofile, 'wb') as f2:
847 while True:
848 mybytes = f1.read(1024)
849 if mybytes:
850 f2.write(mybytes)
851 else:
852 break
855 # --------------------------------------------------------------------
856 # Parent object (keep positions)
857 # --------------------------------------------------------------------
858 def parentobject(parentobj, childobj):
859 # noinspection PyBroadException
860 try:
861 bpy.ops.object.select_all(action='DESELECT')
862 bpy.context.view_layer.objects.active = parentobj
863 parentobj.select_set(True)
864 childobj.select_set(True)
865 bpy.ops.object.parent_set(type='OBJECT', keep_transform=False)
866 return True
867 except:
868 return False
871 # ------------------------------------------------------------------------------
872 # Create control box
874 # objName: Object name
875 # x: size x axis
876 # y: size y axis
877 # z: size z axis
878 # tube: True create a tube, False only sides
879 # ------------------------------------------------------------------------------
880 def create_control_box(objname, x, y, z, tube=True):
881 myvertex = [(-x / 2, 0, 0.0),
882 (-x / 2, y, 0.0),
883 (x / 2, y, 0.0),
884 (x / 2, 0, 0.0),
885 (-x / 2, 0, z),
886 (-x / 2, y, z),
887 (x / 2, y, z),
888 (x / 2, 0, z)]
890 if tube is True:
891 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)]
892 else:
893 myfaces = [(0, 4, 5, 1), (2, 6, 7, 3)]
895 mesh = bpy.data.meshes.new(objname)
896 myobject = bpy.data.objects.new(objname, mesh)
898 myobject.location = bpy.context.scene.cursor.location
899 bpy.context.collection.objects.link(myobject)
901 mesh.from_pydata(myvertex, [], myfaces)
902 mesh.update(calc_edges=True)
904 return myobject
907 # ------------------------------------------------------------------------------
908 # Remove all children objects
909 # ------------------------------------------------------------------------------
910 def remove_children(myobject):
911 # Remove children
912 for child in myobject.children:
913 # noinspection PyBroadException
914 try:
915 # noinspection PyBroadException
916 try:
917 # remove child relationship
918 for grandchild in child.children:
919 grandchild.parent = None
920 # remove modifiers
921 for mod in child.modifiers:
922 bpy.ops.object.modifier_remove(name=mod.name)
923 except:
924 pass
925 # clear child data
926 if child.type == 'MESH':
927 old = child.data
928 child.select_set(True)
929 bpy.ops.object.delete()
930 bpy.data.meshes.remove(old)
931 if child.type == 'CURVE':
932 child.select_set(True)
933 bpy.ops.object.delete()
934 except:
935 pass
938 # --------------------------------------------------------------------
939 # Get all parents
940 # --------------------------------------------------------------------
941 def get_allparents(myobj):
942 obj = myobj
943 mylist = []
944 while obj.parent is not None:
945 mylist.append(obj)
946 objp = obj.parent
947 obj = objp
949 mylist.append(obj)
951 return mylist
954 # --------------------------------------------------------------------
955 # Verify all faces are in vertice group to avoid Blander crash
957 # Review the faces array and remove any vertex out of the range
958 # this avoid any bug that can appear avoiding Blender crash
959 # --------------------------------------------------------------------
960 def check_mesh_errors(myvertices, myfaces):
961 vmax = len(myvertices)
963 f = 0
964 for face in myfaces:
965 for v in face:
966 if v < 0 or v > vmax:
967 print("Face=" + str(f) + "->removed vertex=" + str(v))
968 myfaces[f].remove(v)
969 f += 1
971 return myfaces