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