Damien Picard
35d087e51c
I introduced the bug in dd91f21836
when I overlooked an error that was
caught in a try... expecpt block, where context was not defined.
Pull Request #104468
971 lines
32 KiB
Python
971 lines
32 KiB
Python
# SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
# ----------------------------------------------------------
|
|
# support routines and general functions
|
|
# Author: Antonio Vazquez (antonioya)
|
|
#
|
|
# ----------------------------------------------------------
|
|
# noinspection PyUnresolvedReferences
|
|
import bpy
|
|
from os import path
|
|
|
|
|
|
# --------------------------------------------------------------------
|
|
# Get length Blender units
|
|
# --------------------------------------------------------------------
|
|
def get_blendunits(units):
|
|
if bpy.context.scene.unit_settings.system == "IMPERIAL":
|
|
return units * 0.3048
|
|
else:
|
|
return units
|
|
|
|
|
|
# --------------------------------------------------------------------
|
|
# Set normals
|
|
# True= faces to inside
|
|
# False= faces to outside
|
|
# --------------------------------------------------------------------
|
|
def set_normals(myobject, direction=False):
|
|
bpy.context.view_layer.objects.active = myobject
|
|
# go edit mode
|
|
bpy.ops.object.mode_set(mode='EDIT')
|
|
# select all faces
|
|
bpy.ops.mesh.select_all(action='SELECT')
|
|
# recalculate outside normals
|
|
bpy.ops.mesh.normals_make_consistent(inside=direction)
|
|
# go object mode again
|
|
bpy.ops.object.editmode_toggle()
|
|
|
|
|
|
# --------------------------------------------------------------------
|
|
# Remove doubles
|
|
# --------------------------------------------------------------------
|
|
def remove_doubles(myobject):
|
|
bpy.context.view_layer.objects.active = myobject
|
|
# go edit mode
|
|
bpy.ops.object.mode_set(mode='EDIT')
|
|
# select all faces
|
|
bpy.ops.mesh.select_all(action='SELECT')
|
|
# remove
|
|
bpy.ops.mesh.remove_doubles()
|
|
# go object mode again
|
|
bpy.ops.object.editmode_toggle()
|
|
|
|
|
|
# --------------------------------------------------------------------
|
|
# Set shade smooth
|
|
# --------------------------------------------------------------------
|
|
def set_smooth(myobject):
|
|
# deactivate others
|
|
for o in bpy.data.objects:
|
|
if o.select_get() is True:
|
|
o.select_set(False)
|
|
|
|
myobject.select_set(True)
|
|
bpy.context.view_layer.objects.active = myobject
|
|
if bpy.context.view_layer.objects.active.name == myobject.name:
|
|
bpy.ops.object.shade_smooth()
|
|
|
|
|
|
# --------------------------------------------------------------------
|
|
# Add modifier (subdivision)
|
|
# --------------------------------------------------------------------
|
|
def set_modifier_subsurf(myobject):
|
|
bpy.context.view_layer.objects.active = myobject
|
|
if bpy.context.view_layer.objects.active.name == myobject.name:
|
|
bpy.ops.object.modifier_add(type='SUBSURF')
|
|
for mod in myobject.modifiers:
|
|
if mod.type == 'SUBSURF':
|
|
mod.levels = 2
|
|
|
|
|
|
# --------------------------------------------------------------------
|
|
# Add modifier (mirror)
|
|
# --------------------------------------------------------------------
|
|
def set_modifier_mirror(myobject, axis="Y"):
|
|
bpy.ops.object.select_all(action='DESELECT')
|
|
myobject.select_set(True)
|
|
bpy.context.view_layer.objects.active = myobject
|
|
if bpy.context.view_layer.objects.active.name == myobject.name:
|
|
bpy.ops.object.modifier_add(type='MIRROR')
|
|
for mod in myobject.modifiers:
|
|
if mod.type == 'MIRROR':
|
|
if axis == "X":
|
|
mod.use_axis[0] = True
|
|
else:
|
|
mod.use__axis[0] = False
|
|
|
|
if axis == "Y":
|
|
mod.use_axis[1] = True
|
|
else:
|
|
mod.use_axis[1] = False
|
|
|
|
if axis == "Z":
|
|
mod.use_axis[2] = True
|
|
else:
|
|
mod.use_axis[2] = False
|
|
|
|
mod.use_clip = True
|
|
|
|
|
|
# --------------------------------------------------------------------
|
|
# Add modifier (array)
|
|
# --------------------------------------------------------------------
|
|
def set_modifier_array(myobject, axis, move, repeat, fix=False, fixmove=0, zmove=0):
|
|
bpy.ops.object.select_all(action='DESELECT')
|
|
myobject.select_set(True)
|
|
bpy.context.view_layer.objects.active = myobject
|
|
if bpy.context.view_layer.objects.active.name == myobject.name:
|
|
bpy.ops.object.modifier_add(type='ARRAY')
|
|
for mod in myobject.modifiers:
|
|
if mod.type == 'ARRAY':
|
|
if mod.name == "Array":
|
|
mod.name = "Array_" + axis
|
|
mod.count = repeat
|
|
mod.use_constant_offset = fix
|
|
if axis == "X":
|
|
mod.relative_offset_displace[0] = move
|
|
mod.constant_offset_displace[0] = fixmove
|
|
mod.relative_offset_displace[1] = 0.0
|
|
mod.constant_offset_displace[1] = 0.0
|
|
mod.relative_offset_displace[2] = 0.0
|
|
mod.constant_offset_displace[2] = zmove
|
|
|
|
if axis == "Y":
|
|
mod.relative_offset_displace[0] = 0.0
|
|
mod.constant_offset_displace[0] = 0.0
|
|
mod.relative_offset_displace[1] = move
|
|
mod.constant_offset_displace[1] = fixmove
|
|
mod.relative_offset_displace[2] = 0.0
|
|
mod.constant_offset_displace[2] = 0.0
|
|
|
|
|
|
# --------------------------------------------------------------------
|
|
# Add modifier (curve)
|
|
# --------------------------------------------------------------------
|
|
def set_modifier_curve(myobject, mycurve):
|
|
bpy.context.view_layer.objects.active = myobject
|
|
if bpy.context.view_layer.objects.active.name == myobject.name:
|
|
bpy.ops.object.modifier_add(type='CURVE')
|
|
for mod in myobject.modifiers:
|
|
if mod.type == 'CURVE':
|
|
mod.deform_axis = 'POS_X'
|
|
mod.object = mycurve
|
|
|
|
|
|
# --------------------------------------------------------------------
|
|
# Add modifier (solidify)
|
|
# --------------------------------------------------------------------
|
|
def set_modifier_solidify(myobject, width):
|
|
bpy.context.view_layer.objects.active = myobject
|
|
if bpy.context.view_layer.objects.active.name == myobject.name:
|
|
bpy.ops.object.modifier_add(type='SOLIDIFY')
|
|
for mod in myobject.modifiers:
|
|
if mod.type == 'SOLIDIFY':
|
|
mod.thickness = width
|
|
mod.use_even_offset = True
|
|
mod.use_quality_normals = True
|
|
break
|
|
|
|
|
|
# --------------------------------------------------------------------
|
|
# Add modifier (boolean)
|
|
# --------------------------------------------------------------------
|
|
def set_modifier_boolean(myobject, bolobject):
|
|
boolean_modifier = myobject.modifiers.new("", 'BOOLEAN')
|
|
boolean_modifier.operation = 'DIFFERENCE'
|
|
boolean_modifier.object = bolobject
|
|
|
|
|
|
# --------------------------------------------------------------------
|
|
# Set material to object
|
|
# --------------------------------------------------------------------
|
|
def set_material(myobject, mymaterial):
|
|
bpy.context.view_layer.objects.active = myobject
|
|
if bpy.context.view_layer.objects.active.name == myobject.name:
|
|
myobject.data.materials.append(mymaterial)
|
|
|
|
|
|
# --------------------------------------------------------------------
|
|
# Set material to selected faces
|
|
# --------------------------------------------------------------------
|
|
def set_material_faces(myobject, idx):
|
|
bpy.context.view_layer.objects.active = myobject
|
|
myobject.select_set(True)
|
|
bpy.context.object.active_material_index = idx
|
|
if bpy.context.view_layer.objects.active.name == myobject.name:
|
|
bpy.ops.object.mode_set(mode='EDIT')
|
|
bpy.ops.object.material_slot_assign()
|
|
# Deselect
|
|
bpy.ops.mesh.select_all(action='DESELECT')
|
|
bpy.ops.object.mode_set(mode='OBJECT')
|
|
|
|
|
|
# --------------------------------------------------------------------
|
|
# Select faces
|
|
# --------------------------------------------------------------------
|
|
def select_faces(myobject, selface, clear):
|
|
myobject.select_set(True)
|
|
bpy.context.view_layer.objects.active = myobject
|
|
if bpy.context.view_layer.objects.active.name == myobject.name:
|
|
# deselect everything
|
|
if clear:
|
|
bpy.ops.object.mode_set(mode='EDIT')
|
|
bpy.ops.mesh.select_all(action='DESELECT')
|
|
|
|
# reselect the originally selected face
|
|
bpy.ops.object.mode_set(mode='OBJECT')
|
|
myobject.data.polygons[selface].select = True
|
|
|
|
|
|
# --------------------------------------------------------------------
|
|
# Select vertices
|
|
# --------------------------------------------------------------------
|
|
def select_vertices(myobject, selvertices, clear=True):
|
|
myobject.select_set(True)
|
|
bpy.context.view_layer.objects.active = myobject
|
|
if bpy.context.view_layer.objects.active.name == myobject.name:
|
|
# deselect everything
|
|
if clear:
|
|
bpy.ops.object.mode_set(mode='EDIT')
|
|
bpy.ops.mesh.select_all(action='DESELECT')
|
|
|
|
# Select Vertices
|
|
bpy.ops.object.mode_set(mode='EDIT', toggle=False)
|
|
sel_mode = bpy.context.tool_settings.mesh_select_mode
|
|
|
|
bpy.context.tool_settings.mesh_select_mode = [True, False, False]
|
|
bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
|
|
|
|
for i in selvertices:
|
|
myobject.data.vertices[i].select = True
|
|
|
|
bpy.ops.object.mode_set(mode='EDIT', toggle=False)
|
|
bpy.context.tool_settings.mesh_select_mode = sel_mode
|
|
bpy.ops.object.mode_set(mode='OBJECT')
|
|
|
|
|
|
# --------------------------------------------------------------------
|
|
# Mark Seam
|
|
# --------------------------------------------------------------------
|
|
def mark_seam(myobject):
|
|
# noinspection PyBroadException
|
|
try:
|
|
myobject.select_set(True)
|
|
bpy.context.view_layer.objects.active = myobject
|
|
if bpy.context.view_layer.objects.active.name == myobject.name:
|
|
bpy.ops.object.mode_set(mode='EDIT', toggle=False)
|
|
bpy.ops.mesh.mark_seam()
|
|
bpy.ops.object.mode_set(mode='OBJECT')
|
|
except:
|
|
bpy.ops.object.mode_set(mode='OBJECT')
|
|
|
|
|
|
# --------------------------------------------------------------------
|
|
# Unwrap mesh
|
|
# --------------------------------------------------------------------
|
|
def unwrap_mesh(myobject, allfaces=True):
|
|
# noinspection PyBroadException
|
|
try:
|
|
myobject.select_set(True)
|
|
bpy.context.view_layer.objects.active = myobject
|
|
if bpy.context.view_layer.objects.active.name == myobject.name:
|
|
# Unwrap
|
|
bpy.ops.object.mode_set(mode='EDIT', toggle=False)
|
|
if allfaces is True:
|
|
bpy.ops.mesh.select_all(action='DESELECT')
|
|
bpy.ops.mesh.select_all()
|
|
bpy.ops.uv.unwrap()
|
|
bpy.ops.object.mode_set(mode='OBJECT')
|
|
except:
|
|
bpy.ops.object.mode_set(mode='OBJECT')
|
|
|
|
|
|
# --------------------------------------------------------------------
|
|
# Get Node Index(multilanguage support)
|
|
# --------------------------------------------------------------------
|
|
def get_node_index(nodes, datatype):
|
|
idx = 0
|
|
for m in nodes:
|
|
if m.type == datatype:
|
|
return idx
|
|
idx += 1
|
|
|
|
# by default
|
|
return 1
|
|
|
|
|
|
# --------------------------------------------------------------------
|
|
# Create cycles diffuse material
|
|
# --------------------------------------------------------------------
|
|
def create_diffuse_material(matname, replace, r, g, b, rv=0.8, gv=0.8, bv=0.8, mix=0.1, twosides=False):
|
|
# Avoid duplicate materials
|
|
if replace is False:
|
|
matlist = bpy.data.materials
|
|
for m in matlist:
|
|
if m.name == matname:
|
|
return m
|
|
# Create material
|
|
mat = bpy.data.materials.new(matname)
|
|
mat.diffuse_color = (rv, gv, bv, 1.0) # viewport color
|
|
mat.use_nodes = True
|
|
nodes = mat.node_tree.nodes
|
|
|
|
# support for multilanguage
|
|
node = nodes.new('ShaderNodeBsdfDiffuse')
|
|
node.name = 'Diffuse BSDF'
|
|
node.inputs[0].default_value = [r, g, b, 1]
|
|
node.location = 200, 320
|
|
|
|
node = nodes.new('ShaderNodeBsdfGlossy')
|
|
node.name = 'Glossy_0'
|
|
node.location = 200, 0
|
|
|
|
node = nodes.new('ShaderNodeMixShader')
|
|
node.name = 'Mix_0'
|
|
node.inputs[0].default_value = mix
|
|
node.location = 500, 160
|
|
|
|
node = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')]
|
|
node.location = 1100, 160
|
|
|
|
# Connect nodes
|
|
outn = nodes['Diffuse BSDF'].outputs[0]
|
|
inn = nodes['Mix_0'].inputs[1]
|
|
mat.node_tree.links.new(outn, inn)
|
|
|
|
outn = nodes['Glossy_0'].outputs[0]
|
|
inn = nodes['Mix_0'].inputs[2]
|
|
mat.node_tree.links.new(outn, inn)
|
|
|
|
if twosides is False:
|
|
outn = nodes['Mix_0'].outputs[0]
|
|
inn = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')].inputs[0]
|
|
mat.node_tree.links.new(outn, inn)
|
|
|
|
if twosides is True:
|
|
node = nodes.new('ShaderNodeNewGeometry')
|
|
node.name = 'Input_1'
|
|
node.location = -80, -70
|
|
|
|
node = nodes.new('ShaderNodeBsdfDiffuse')
|
|
node.name = 'Diffuse_1'
|
|
node.inputs[0].default_value = [0.30, 0.30, 0.30, 1]
|
|
node.location = 200, -280
|
|
|
|
node = nodes.new('ShaderNodeMixShader')
|
|
node.name = 'Mix_1'
|
|
node.inputs[0].default_value = mix
|
|
node.location = 800, -70
|
|
|
|
outn = nodes['Input_1'].outputs[6]
|
|
inn = nodes['Mix_1'].inputs[0]
|
|
mat.node_tree.links.new(outn, inn)
|
|
|
|
outn = nodes['Diffuse_1'].outputs[0]
|
|
inn = nodes['Mix_1'].inputs[2]
|
|
mat.node_tree.links.new(outn, inn)
|
|
|
|
outn = nodes['Mix_0'].outputs[0]
|
|
inn = nodes['Mix_1'].inputs[1]
|
|
mat.node_tree.links.new(outn, inn)
|
|
|
|
outn = nodes['Mix_1'].outputs[0]
|
|
inn = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')].inputs[0]
|
|
mat.node_tree.links.new(outn, inn)
|
|
|
|
return mat
|
|
|
|
|
|
# --------------------------------------------------------------------
|
|
# Create cycles translucent material
|
|
# --------------------------------------------------------------------
|
|
def create_translucent_material(matname, replace, r, g, b, rv=0.8, gv=0.8, bv=0.8, mix=0.1):
|
|
# Avoid duplicate materials
|
|
if replace is False:
|
|
matlist = bpy.data.materials
|
|
for m in matlist:
|
|
if m.name == matname:
|
|
return m
|
|
# Create material
|
|
mat = bpy.data.materials.new(matname)
|
|
mat.diffuse_color = (rv, gv, bv, 1.0) # viewport color
|
|
mat.use_nodes = True
|
|
nodes = mat.node_tree.nodes
|
|
|
|
# support for multilanguage
|
|
node = nodes.new('ShaderNodeBsdfDiffuse')
|
|
node.name = 'Diffuse BSDF'
|
|
node.inputs[0].default_value = [r, g, b, 1]
|
|
node.location = 200, 320
|
|
|
|
node = nodes.new('ShaderNodeBsdfTranslucent')
|
|
node.name = 'Translucent_0'
|
|
node.location = 200, 0
|
|
|
|
node = nodes.new('ShaderNodeMixShader')
|
|
node.name = 'Mix_0'
|
|
node.inputs[0].default_value = mix
|
|
node.location = 500, 160
|
|
|
|
node = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')]
|
|
node.location = 1100, 160
|
|
|
|
# Connect nodes
|
|
outn = nodes['Diffuse BSDF'].outputs[0]
|
|
inn = nodes['Mix_0'].inputs[1]
|
|
mat.node_tree.links.new(outn, inn)
|
|
|
|
outn = nodes['Translucent_0'].outputs[0]
|
|
inn = nodes['Mix_0'].inputs[2]
|
|
mat.node_tree.links.new(outn, inn)
|
|
|
|
outn = nodes['Mix_0'].outputs[0]
|
|
inn = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')].inputs[0]
|
|
mat.node_tree.links.new(outn, inn)
|
|
|
|
return mat
|
|
|
|
|
|
# --------------------------------------------------------------------
|
|
# Create cycles glass material
|
|
# --------------------------------------------------------------------
|
|
def create_glass_material(matname, replace, rv=0.333, gv=0.342, bv=0.9):
|
|
# Avoid duplicate materials
|
|
if replace is False:
|
|
matlist = bpy.data.materials
|
|
for m in matlist:
|
|
if m.name == matname:
|
|
return m
|
|
# Create material
|
|
mat = bpy.data.materials.new(matname)
|
|
mat.use_nodes = True
|
|
mat.diffuse_color = (rv, gv, bv, 1.0)
|
|
nodes = mat.node_tree.nodes
|
|
|
|
# support for multilanguage
|
|
node = nodes[get_node_index(nodes, 'BSDF_DIFFUSE')]
|
|
mat.node_tree.nodes.remove(node) # remove not used
|
|
|
|
node = nodes.new('ShaderNodeLightPath')
|
|
node.name = 'Light_0'
|
|
node.location = 10, 160
|
|
|
|
node = nodes.new('ShaderNodeBsdfRefraction')
|
|
node.name = 'Refraction_0'
|
|
node.inputs[2].default_value = 1 # IOR 1.0
|
|
node.location = 250, 400
|
|
|
|
node = nodes.new('ShaderNodeBsdfGlossy')
|
|
node.name = 'Glossy_0'
|
|
node.distribution = 'SHARP'
|
|
node.location = 250, 100
|
|
|
|
node = nodes.new('ShaderNodeBsdfTransparent')
|
|
node.name = 'Transparent_0'
|
|
node.location = 500, 10
|
|
|
|
node = nodes.new('ShaderNodeMixShader')
|
|
node.name = 'Mix_0'
|
|
node.inputs[0].default_value = 0.035
|
|
node.location = 500, 160
|
|
|
|
node = nodes.new('ShaderNodeMixShader')
|
|
node.name = 'Mix_1'
|
|
node.inputs[0].default_value = 0.1
|
|
node.location = 690, 290
|
|
|
|
node = nodes.new('ShaderNodeOutputMaterial')
|
|
node.name = 'OUTPUT_MATERIAL'
|
|
node.location = 920, 290
|
|
|
|
# Connect nodes
|
|
outn = nodes['Light_0'].outputs[1]
|
|
inn = nodes['Mix_1'].inputs[0]
|
|
mat.node_tree.links.new(outn, inn)
|
|
|
|
outn = nodes['Refraction_0'].outputs[0]
|
|
inn = nodes['Mix_0'].inputs[1]
|
|
mat.node_tree.links.new(outn, inn)
|
|
|
|
outn = nodes['Glossy_0'].outputs[0]
|
|
inn = nodes['Mix_0'].inputs[2]
|
|
mat.node_tree.links.new(outn, inn)
|
|
|
|
outn = nodes['Mix_0'].outputs[0]
|
|
inn = nodes['Mix_1'].inputs[1]
|
|
mat.node_tree.links.new(outn, inn)
|
|
|
|
outn = nodes['Transparent_0'].outputs[0]
|
|
inn = nodes['Mix_1'].inputs[2]
|
|
mat.node_tree.links.new(outn, inn)
|
|
|
|
outn = nodes['Mix_1'].outputs[0]
|
|
inn = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')].inputs[0]
|
|
mat.node_tree.links.new(outn, inn)
|
|
|
|
return mat
|
|
|
|
|
|
# ---------------------------------------------
|
|
# Create cycles transparents material
|
|
# --------------------------------------------------------------------
|
|
def create_transparent_material(matname, replace, r=1, g=1, b=1, alpha=0):
|
|
# Avoid duplicate materials
|
|
if replace is False:
|
|
matlist = bpy.data.materials
|
|
for m in matlist:
|
|
if m.name == matname:
|
|
return m
|
|
# Create material
|
|
mat = bpy.data.materials.new(matname)
|
|
mat.use_nodes = True
|
|
mat.diffuse_color = (r, g, b, 1.0)
|
|
nodes = mat.node_tree.nodes
|
|
|
|
# support for multilanguage
|
|
node = nodes[get_node_index(nodes, 'BSDF_DIFFUSE')]
|
|
mat.node_tree.nodes.remove(node) # remove not used
|
|
|
|
node = nodes.new('ShaderNodeBsdfTransparent')
|
|
node.name = 'Transparent_0'
|
|
node.location = 250, 160
|
|
node.inputs[0].default_value = [r, g, b, alpha]
|
|
|
|
node = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')]
|
|
node.location = 700, 160
|
|
|
|
# Connect nodes
|
|
outn = nodes['Transparent_0'].outputs[0]
|
|
inn = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')].inputs[0]
|
|
mat.node_tree.links.new(outn, inn)
|
|
|
|
return mat
|
|
|
|
|
|
# --------------------------------------------------------------------
|
|
# Create cycles glossy material
|
|
# --------------------------------------------------------------------
|
|
def create_glossy_material(matname, replace, r, g, b, rv=0.578, gv=0.555, bv=0.736, rvalue=0.2):
|
|
# Avoid duplicate materials
|
|
if replace is False:
|
|
matlist = bpy.data.materials
|
|
for m in matlist:
|
|
if m.name == matname:
|
|
return m
|
|
# Create material
|
|
mat = bpy.data.materials.new(matname)
|
|
mat.use_nodes = True
|
|
mat.diffuse_color = (rv, gv, bv, 1.0)
|
|
nodes = mat.node_tree.nodes
|
|
|
|
# support for multilanguage
|
|
node = nodes[get_node_index(nodes, 'BSDF_DIFFUSE')]
|
|
mat.node_tree.nodes.remove(node) # remove not used
|
|
|
|
node = nodes.new('ShaderNodeBsdfGlossy')
|
|
node.name = 'Glossy_0'
|
|
node.inputs[0].default_value = [r, g, b, 1]
|
|
node.inputs[1].default_value = rvalue
|
|
node.location = 200, 160
|
|
|
|
node = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')]
|
|
node.location = 700, 160
|
|
|
|
# Connect nodes
|
|
outn = nodes['Glossy_0'].outputs[0]
|
|
inn = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')].inputs[0]
|
|
mat.node_tree.links.new(outn, inn)
|
|
|
|
return mat
|
|
|
|
|
|
# --------------------------------------------------------------------
|
|
# Create cycles emission material
|
|
# --------------------------------------------------------------------
|
|
def create_emission_material(matname, replace, r, g, b, energy):
|
|
# Avoid duplicate materials
|
|
if replace is False:
|
|
matlist = bpy.data.materials
|
|
for m in matlist:
|
|
if m.name == matname:
|
|
return m
|
|
# Create material
|
|
mat = bpy.data.materials.new(matname)
|
|
mat.use_nodes = True
|
|
nodes = mat.node_tree.nodes
|
|
|
|
# support for multilanguage
|
|
node = nodes[get_node_index(nodes, 'BSDF_DIFFUSE')]
|
|
mat.node_tree.nodes.remove(node) # remove not used
|
|
|
|
node = nodes.new('ShaderNodeEmission')
|
|
node.name = 'Emission_0'
|
|
node.inputs[0].default_value = [r, g, b, 1]
|
|
node.inputs[1].default_value = energy
|
|
node.location = 200, 160
|
|
|
|
node = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')]
|
|
node.location = 700, 160
|
|
|
|
# Connect nodes
|
|
outn = nodes['Emission_0'].outputs[0]
|
|
inn = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')].inputs[0]
|
|
mat.node_tree.links.new(outn, inn)
|
|
|
|
return mat
|
|
|
|
|
|
# --------------------------------------------------------------------
|
|
# Create cycles glass material
|
|
# --------------------------------------------------------------------
|
|
def create_old_glass_material(matname, replace, rv=0.352716, gv=0.760852, bv=0.9):
|
|
# Avoid duplicate materials
|
|
if replace is False:
|
|
matlist = bpy.data.materials
|
|
for m in matlist:
|
|
if m.name == matname:
|
|
return m
|
|
# Create material
|
|
mat = bpy.data.materials.new(matname)
|
|
mat.use_nodes = True
|
|
mat.diffuse_color = (rv, gv, bv, 1.0)
|
|
nodes = mat.node_tree.nodes
|
|
|
|
# support for multilanguage
|
|
node = nodes[get_node_index(nodes, 'BSDF_DIFFUSE')]
|
|
mat.node_tree.nodes.remove(node) # remove not used
|
|
|
|
node = nodes.new('ShaderNodeLightPath')
|
|
node.name = 'Light_0'
|
|
node.location = 10, 160
|
|
|
|
node = nodes.new('ShaderNodeBsdfGlass')
|
|
node.name = 'Glass_0'
|
|
node.location = 250, 300
|
|
|
|
node = nodes.new('ShaderNodeBsdfTransparent')
|
|
node.name = 'Transparent_0'
|
|
node.location = 250, 0
|
|
|
|
node = nodes.new('ShaderNodeMixShader')
|
|
node.name = 'Mix_0'
|
|
node.inputs[0].default_value = 0.1
|
|
node.location = 500, 160
|
|
|
|
node = nodes.new('ShaderNodeMixShader')
|
|
node.name = 'Mix_1'
|
|
node.inputs[0].default_value = 0.1
|
|
node.location = 690, 290
|
|
|
|
node = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')]
|
|
node.location = 920, 290
|
|
|
|
# Connect nodes
|
|
outn = nodes['Light_0'].outputs[1]
|
|
inn = nodes['Mix_0'].inputs[0]
|
|
mat.node_tree.links.new(outn, inn)
|
|
|
|
outn = nodes['Light_0'].outputs[2]
|
|
inn = nodes['Mix_1'].inputs[0]
|
|
mat.node_tree.links.new(outn, inn)
|
|
|
|
outn = nodes['Glass_0'].outputs[0]
|
|
inn = nodes['Mix_0'].inputs[1]
|
|
mat.node_tree.links.new(outn, inn)
|
|
|
|
outn = nodes['Transparent_0'].outputs[0]
|
|
inn = nodes['Mix_0'].inputs[2]
|
|
mat.node_tree.links.new(outn, inn)
|
|
|
|
outn = nodes['Mix_0'].outputs[0]
|
|
inn = nodes['Mix_1'].inputs[1]
|
|
mat.node_tree.links.new(outn, inn)
|
|
|
|
outn = nodes['Mix_1'].outputs[0]
|
|
inn = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')].inputs[0]
|
|
mat.node_tree.links.new(outn, inn)
|
|
|
|
return mat
|
|
|
|
|
|
# --------------------------------------------------------------------
|
|
# Create cycles brick texture material
|
|
# --------------------------------------------------------------------
|
|
def create_brick_material(matname, replace, r, g, b, rv=0.8, gv=0.636, bv=0.315):
|
|
# Avoid duplicate materials
|
|
if replace is False:
|
|
matlist = bpy.data.materials
|
|
for m in matlist:
|
|
if m.name == matname:
|
|
return m
|
|
# Create material
|
|
mat = bpy.data.materials.new(matname)
|
|
mat.use_nodes = True
|
|
mat.diffuse_color = (rv, gv, bv, 1.0)
|
|
nodes = mat.node_tree.nodes
|
|
|
|
# support for multilanguage
|
|
principled_node = nodes[get_node_index(nodes, 'BSDF_PRINCIPLED')]
|
|
|
|
principled_node.inputs[0].default_value = [r, g, b, 1]
|
|
principled_node.location = 500, 160
|
|
|
|
output_node = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')]
|
|
output_node.location = 700, 160
|
|
|
|
brick_node = nodes.new('ShaderNodeTexBrick')
|
|
brick_node.name = 'Brick_0'
|
|
brick_node.inputs[3].default_value = [0.407, 0.411, 0.394, 1] # mortar color
|
|
brick_node.inputs[4].default_value = 3 # scale
|
|
brick_node.inputs[5].default_value = 0.001 # mortar
|
|
brick_node.inputs[7].default_value = 0.60 # size_w
|
|
brick_node.inputs[8].default_value = 0.30 # size_h
|
|
brick_node.location = 300, 160
|
|
|
|
rgb_node = nodes.new('ShaderNodeRGB')
|
|
rgb_node.name = 'RGB_0'
|
|
rgb_node.outputs[0].default_value = [r, g, b, 1]
|
|
rgb_node.location = 70, 160
|
|
|
|
# Connect nodes
|
|
outn = rgb_node.outputs['Color']
|
|
inn = brick_node.inputs['Color1']
|
|
mat.node_tree.links.new(outn, inn)
|
|
|
|
inn = brick_node.inputs['Color2']
|
|
mat.node_tree.links.new(outn, inn)
|
|
|
|
outn = brick_node.outputs['Color']
|
|
inn = principled_node.inputs['Base Color']
|
|
mat.node_tree.links.new(outn, inn)
|
|
|
|
return mat
|
|
|
|
|
|
# --------------------------------------------------------------------
|
|
# Create cycles fabric texture material
|
|
# --------------------------------------------------------------------
|
|
def create_fabric_material(matname, replace, r, g, b, rv=0.8, gv=0.636, bv=0.315):
|
|
# Avoid duplicate materials
|
|
if replace is False:
|
|
matlist = bpy.data.materials
|
|
for m in matlist:
|
|
if m.name == matname:
|
|
return m
|
|
# Create material
|
|
mat = bpy.data.materials.new(matname)
|
|
mat.use_nodes = True
|
|
mat.diffuse_color = (rv, gv, bv, 1.0)
|
|
nodes = mat.node_tree.nodes
|
|
|
|
# support for multilanguage
|
|
node = nodes.new('ShaderNodeBsdfDiffuse')
|
|
node.name = 'Diffuse BSDF'
|
|
node.inputs[0].default_value = [r, g, b, 1]
|
|
node.location = 810, 270
|
|
|
|
node = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')]
|
|
node.location = 1210, 320
|
|
|
|
node = nodes.new('ShaderNodeTexCoord')
|
|
node.name = 'UVCoordinates'
|
|
node.location = 26, 395
|
|
|
|
node = nodes.new('ShaderNodeMapping')
|
|
node.name = 'UVMapping'
|
|
node.location = 266, 380
|
|
node.inputs['Scale'].default_value[0] = 1000
|
|
node.inputs['Scale'].default_value[1] = 1000
|
|
node.inputs['Scale'].default_value[2] = 1000
|
|
|
|
# ===========================================================================
|
|
# Image texture
|
|
# ===========================================================================
|
|
# Load image file.
|
|
|
|
realpath = path.join(path.dirname(__file__), "images", "fabric_diffuse.png")
|
|
print("Loading: " + realpath)
|
|
try:
|
|
img = bpy.data.images.load(realpath)
|
|
except:
|
|
raise NameError("Cannot load image %s" % realpath)
|
|
|
|
# Create image texture from image
|
|
ctex = bpy.data.textures.new('ColorTex', type='IMAGE')
|
|
ctex.image = img
|
|
|
|
node = nodes.new('ShaderNodeTexImage')
|
|
node.name = 'Image1'
|
|
node.image = ctex.image
|
|
node.location = 615, 350
|
|
|
|
node = nodes.new('ShaderNodeBsdfTransparent')
|
|
node.name = 'Transparent1'
|
|
node.location = 810, 395
|
|
node.inputs[0].default_value = [r, g, b, 1]
|
|
|
|
node = nodes.new('ShaderNodeAddShader')
|
|
node.name = 'Add1'
|
|
node.location = 1040, 356
|
|
|
|
# Connect nodes
|
|
outn = nodes['UVCoordinates'].outputs['UV']
|
|
inn = nodes['UVMapping'].inputs['Vector']
|
|
mat.node_tree.links.new(outn, inn)
|
|
|
|
outn = nodes['UVMapping'].outputs['Vector']
|
|
inn = nodes['Image1'].inputs['Vector']
|
|
mat.node_tree.links.new(outn, inn)
|
|
|
|
outn = nodes['Image1'].outputs['Color']
|
|
inn = nodes['Diffuse BSDF'].inputs['Color']
|
|
mat.node_tree.links.new(outn, inn)
|
|
|
|
outn = nodes['Transparent1'].outputs['BSDF']
|
|
inn = nodes['Add1'].inputs[0]
|
|
mat.node_tree.links.new(outn, inn)
|
|
|
|
outn = nodes['Diffuse BSDF'].outputs['BSDF']
|
|
inn = nodes['Add1'].inputs[1]
|
|
mat.node_tree.links.new(outn, inn)
|
|
|
|
outn = nodes['Add1'].outputs['Shader']
|
|
inn = nodes[get_node_index(nodes, 'OUTPUT_MATERIAL')].inputs[0]
|
|
mat.node_tree.links.new(outn, inn)
|
|
|
|
return mat
|
|
|
|
|
|
# --------------------------------------------------------------------
|
|
# Copy bin file
|
|
# --------------------------------------------------------------------
|
|
def copy_binfile(fromfile, tofile):
|
|
with open(fromfile, 'rb') as f1:
|
|
with open(tofile, 'wb') as f2:
|
|
while True:
|
|
mybytes = f1.read(1024)
|
|
if mybytes:
|
|
f2.write(mybytes)
|
|
else:
|
|
break
|
|
|
|
|
|
# --------------------------------------------------------------------
|
|
# Parent object (keep positions)
|
|
# --------------------------------------------------------------------
|
|
def parentobject(parentobj, childobj):
|
|
# noinspection PyBroadException
|
|
try:
|
|
bpy.ops.object.select_all(action='DESELECT')
|
|
bpy.context.view_layer.objects.active = parentobj
|
|
parentobj.select_set(True)
|
|
childobj.select_set(True)
|
|
bpy.ops.object.parent_set(type='OBJECT', keep_transform=False)
|
|
return True
|
|
except:
|
|
return False
|
|
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# Create control box
|
|
#
|
|
# objName: Object name
|
|
# x: size x axis
|
|
# y: size y axis
|
|
# z: size z axis
|
|
# tube: True create a tube, False only sides
|
|
# ------------------------------------------------------------------------------
|
|
def create_control_box(objname, x, y, z, tube=True):
|
|
myvertex = [(-x / 2, 0, 0.0),
|
|
(-x / 2, y, 0.0),
|
|
(x / 2, y, 0.0),
|
|
(x / 2, 0, 0.0),
|
|
(-x / 2, 0, z),
|
|
(-x / 2, y, z),
|
|
(x / 2, y, z),
|
|
(x / 2, 0, z)]
|
|
|
|
if tube is True:
|
|
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)]
|
|
else:
|
|
myfaces = [(0, 4, 5, 1), (2, 6, 7, 3)]
|
|
|
|
mesh = bpy.data.meshes.new(objname)
|
|
myobject = bpy.data.objects.new(objname, mesh)
|
|
|
|
myobject.location = bpy.context.scene.cursor.location
|
|
bpy.context.collection.objects.link(myobject)
|
|
|
|
mesh.from_pydata(myvertex, [], myfaces)
|
|
mesh.update(calc_edges=True)
|
|
|
|
return myobject
|
|
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# Remove all children objects
|
|
# ------------------------------------------------------------------------------
|
|
def remove_children(myobject):
|
|
# Remove children
|
|
for child in myobject.children:
|
|
# noinspection PyBroadException
|
|
try:
|
|
# noinspection PyBroadException
|
|
try:
|
|
# remove child relationship
|
|
for grandchild in child.children:
|
|
grandchild.parent = None
|
|
# remove modifiers
|
|
for mod in child.modifiers:
|
|
bpy.ops.object.modifier_remove(name=mod.name)
|
|
except:
|
|
pass
|
|
# clear child data
|
|
if child.type == 'MESH':
|
|
old = child.data
|
|
child.select_set(True)
|
|
bpy.ops.object.delete()
|
|
bpy.data.meshes.remove(old)
|
|
if child.type == 'CURVE':
|
|
child.select_set(True)
|
|
bpy.ops.object.delete()
|
|
except:
|
|
pass
|
|
|
|
|
|
# --------------------------------------------------------------------
|
|
# Get all parents
|
|
# --------------------------------------------------------------------
|
|
def get_allparents(myobj):
|
|
obj = myobj
|
|
mylist = []
|
|
while obj.parent is not None:
|
|
mylist.append(obj)
|
|
objp = obj.parent
|
|
obj = objp
|
|
|
|
mylist.append(obj)
|
|
|
|
return mylist
|
|
|
|
|
|
# --------------------------------------------------------------------
|
|
# Verify all faces are in vertice group to avoid Blander crash
|
|
#
|
|
# Review the faces array and remove any vertex out of the range
|
|
# this avoid any bug that can appear avoiding Blender crash
|
|
# --------------------------------------------------------------------
|
|
def check_mesh_errors(myvertices, myfaces):
|
|
vmax = len(myvertices)
|
|
|
|
f = 0
|
|
for face in myfaces:
|
|
for v in face:
|
|
if v < 0 or v > vmax:
|
|
print("Face=" + str(f) + "->removed vertex=" + str(v))
|
|
myfaces[f].remove(v)
|
|
f += 1
|
|
|
|
return myfaces
|