forked from LeenkxTeam/LNXSDK
948 lines
39 KiB
Python
948 lines
39 KiB
Python
import bpy, math, time
|
|
|
|
from . import cache
|
|
from .. utility import *
|
|
|
|
def assemble():
|
|
|
|
configure_world()
|
|
|
|
configure_lights()
|
|
|
|
configure_meshes()
|
|
|
|
def init(self, prev_container):
|
|
|
|
store_existing(prev_container)
|
|
|
|
set_settings()
|
|
|
|
configure_world()
|
|
|
|
configure_lights()
|
|
|
|
configure_meshes(self)
|
|
|
|
print("Config mesh catch omitted: REMEMBER TO SET IT BACK NAXELA")
|
|
# try:
|
|
# configure_meshes(self)
|
|
# except Exception as e:
|
|
|
|
# print("An error occured during mesh configuration. See error below:")
|
|
|
|
# print(f"{type(e).__name__} at line {e.__traceback__.tb_lineno} of {__file__}: {e}")
|
|
|
|
# if not bpy.context.scene.TLM_SceneProperties.tlm_verbose:
|
|
# print("Turn on verbose mode to get more detail.")
|
|
|
|
def configure_world():
|
|
pass
|
|
|
|
def configure_lights():
|
|
pass
|
|
|
|
def configure_meshes(self):
|
|
|
|
if bpy.context.scene.TLM_SceneProperties.tlm_verbose:
|
|
print("Configuring meshes: Start")
|
|
|
|
if bpy.context.scene.TLM_SceneProperties.tlm_verbose:
|
|
print("Configuring meshes: Material restore")
|
|
for obj in bpy.context.scene.objects:
|
|
if obj.type == 'MESH' and obj.name in bpy.context.view_layer.objects:
|
|
if obj.TLM_ObjectProperties.tlm_mesh_lightmap_use:
|
|
cache.backup_material_restore(obj)
|
|
|
|
if bpy.context.scene.TLM_SceneProperties.tlm_verbose:
|
|
print("Configuring meshes: Material rename check")
|
|
for obj in bpy.context.scene.objects:
|
|
if obj.type == 'MESH' and obj.name in bpy.context.view_layer.objects:
|
|
if obj.TLM_ObjectProperties.tlm_mesh_lightmap_use:
|
|
cache.backup_material_rename(obj)
|
|
|
|
for mat in bpy.data.materials:
|
|
if mat.users < 1:
|
|
bpy.data.materials.remove(mat)
|
|
|
|
for mat in bpy.data.materials:
|
|
if mat.name.startswith("."):
|
|
if "_Original" in mat.name:
|
|
bpy.data.materials.remove(mat)
|
|
|
|
#for image in bpy.data.images:
|
|
# if image.name.endswith("_baked"):
|
|
# bpy.data.images.remove(image, do_unlink=True)
|
|
|
|
iterNum = 0
|
|
currentIterNum = 0
|
|
|
|
scene = bpy.context.scene
|
|
|
|
if bpy.context.scene.TLM_SceneProperties.tlm_verbose:
|
|
print("Object: Setting UV, converting modifiers and prepare channels")
|
|
|
|
#OBJECT: Set UV, CONVERT AND PREPARE
|
|
for obj in bpy.context.scene.objects:
|
|
if obj.type == 'MESH' and obj.name in bpy.context.view_layer.objects:
|
|
|
|
hidden = False
|
|
|
|
#We check if the object is hidden
|
|
if obj.hide_get():
|
|
hidden = True
|
|
if obj.hide_viewport:
|
|
hidden = True
|
|
if obj.hide_render:
|
|
hidden = True
|
|
|
|
#We check if the object's collection is hidden
|
|
collections = obj.users_collection
|
|
|
|
for collection in collections:
|
|
|
|
if collection.hide_viewport:
|
|
hidden = True
|
|
if collection.hide_render:
|
|
hidden = True
|
|
|
|
try:
|
|
if collection.name in bpy.context.scene.view_layers[0].layer_collection.children:
|
|
if bpy.context.scene.view_layers[0].layer_collection.children[collection.name].hide_viewport:
|
|
hidden = True
|
|
except:
|
|
print("Error: Could not find collection: " + collection.name)
|
|
|
|
|
|
#Additional check for zero poly meshes
|
|
mesh = obj.data
|
|
if (len(mesh.polygons)) < 1:
|
|
print("Found an object with zero polygons. Skipping object: " + obj.name)
|
|
obj.TLM_ObjectProperties.tlm_mesh_lightmap_use = False
|
|
|
|
if obj.TLM_ObjectProperties.tlm_mesh_lightmap_use and not hidden:
|
|
|
|
print("Preparing: UV initiation for object: " + obj.name)
|
|
|
|
if len(obj.data.vertex_colors) < 1:
|
|
obj.data.vertex_colors.new(name="TLM")
|
|
|
|
if scene.TLM_SceneProperties.tlm_reset_uv:
|
|
|
|
uv_layers = obj.data.uv_layers
|
|
uv_channel = "UVMap_Lightmap"
|
|
for uvlayer in uv_layers:
|
|
if uvlayer.name == uv_channel:
|
|
uv_layers.remove(uvlayer)
|
|
|
|
if scene.TLM_SceneProperties.tlm_apply_on_unwrap:
|
|
if bpy.context.scene.TLM_SceneProperties.tlm_verbose:
|
|
print("Applying transform to: " + obj.name)
|
|
bpy.context.view_layer.objects.active = obj
|
|
obj.select_set(True)
|
|
bpy.ops.object.transform_apply(location=False, rotation=True, scale=True)
|
|
|
|
if scene.TLM_SceneProperties.tlm_apply_modifiers:
|
|
if bpy.context.scene.TLM_SceneProperties.tlm_verbose:
|
|
print("Applying modifiers to: " + obj.name)
|
|
bpy.context.view_layer.objects.active = obj
|
|
obj.select_set(True)
|
|
bpy.ops.object.convert(target='MESH')
|
|
|
|
for slot in obj.material_slots:
|
|
material = slot.material
|
|
skipIncompatibleMaterials(material)
|
|
|
|
obj.hide_select = False #Remember to toggle this back
|
|
for slot in obj.material_slots:
|
|
if "." + slot.name + '_Original' in bpy.data.materials:
|
|
if bpy.context.scene.TLM_SceneProperties.tlm_verbose:
|
|
print("The material: " + slot.name + " shifted to " + "." + slot.name + '_Original')
|
|
slot.material = bpy.data.materials["." + slot.name + '_Original']
|
|
|
|
#ATLAS UV PROJECTING
|
|
print("PREPARE: ATLAS")
|
|
for atlasgroup in scene.TLM_AtlasList:
|
|
|
|
print("Adding UV Projection for Atlas group: " + atlasgroup.name)
|
|
|
|
atlas = atlasgroup.name
|
|
atlas_items = []
|
|
|
|
bpy.ops.object.select_all(action='DESELECT')
|
|
|
|
#Atlas: Set UV, CONVERT AND PREPARE
|
|
for obj in bpy.context.scene.objects:
|
|
|
|
if obj.TLM_ObjectProperties.tlm_atlas_pointer == atlasgroup.name:
|
|
|
|
hidden = False
|
|
|
|
#We check if the object is hidden
|
|
if obj.hide_get():
|
|
hidden = True
|
|
if obj.hide_viewport:
|
|
hidden = True
|
|
if obj.hide_render:
|
|
hidden = True
|
|
|
|
#We check if the object's collection is hidden
|
|
collections = obj.users_collection
|
|
|
|
for collection in collections:
|
|
|
|
if collection.hide_viewport:
|
|
hidden = True
|
|
if collection.hide_render:
|
|
hidden = True
|
|
|
|
try:
|
|
if collection.name in bpy.context.scene.view_layers[0].layer_collection.children:
|
|
if bpy.context.scene.view_layers[0].layer_collection.children[collection.name].hide_viewport:
|
|
hidden = True
|
|
except:
|
|
print("Error: Could not find collection: " + collection.name)
|
|
|
|
if obj.TLM_ObjectProperties.tlm_mesh_lightmap_unwrap_mode == "AtlasGroupA" and not hidden:
|
|
|
|
uv_layers = obj.data.uv_layers
|
|
|
|
if not obj.TLM_ObjectProperties.tlm_use_default_channel:
|
|
uv_channel = obj.TLM_ObjectProperties.tlm_uv_channel
|
|
else:
|
|
uv_channel = "UVMap_Lightmap"
|
|
|
|
if not uv_channel in uv_layers:
|
|
if bpy.context.scene.TLM_SceneProperties.tlm_verbose:
|
|
print("UV map created for object: " + obj.name)
|
|
uvmap = uv_layers.new(name=uv_channel)
|
|
uv_layers.active_index = len(uv_layers) - 1
|
|
else:
|
|
print("Existing UV map found for object: " + obj.name)
|
|
for i in range(0, len(uv_layers)):
|
|
if uv_layers[i].name == 'UVMap_Lightmap':
|
|
uv_layers.active_index = i
|
|
break
|
|
|
|
atlas_items.append(obj)
|
|
obj.select_set(True)
|
|
|
|
if atlasgroup.tlm_atlas_lightmap_unwrap_mode == "SmartProject":
|
|
if bpy.context.scene.TLM_SceneProperties.tlm_verbose:
|
|
print("Atlasgroup Smart Project for: " + str(atlas_items))
|
|
for obj in atlas_items:
|
|
print("Applying Smart Project to: ")
|
|
print(obj.name + ": Active UV: " + obj.data.uv_layers[obj.data.uv_layers.active_index].name)
|
|
|
|
|
|
if len(atlas_items) > 0:
|
|
bpy.context.view_layer.objects.active = atlas_items[0]
|
|
|
|
bpy.ops.object.mode_set(mode='EDIT')
|
|
bpy.ops.mesh.select_all(action='SELECT')
|
|
#API changes in 2.91 causes errors:
|
|
if (2, 91, 0) > bpy.app.version:
|
|
bpy.ops.uv.smart_project(angle_limit=45.0, island_margin=obj.TLM_ObjectProperties.tlm_mesh_unwrap_margin, user_area_weight=1.0, use_aspect=True, stretch_to_bounds=False)
|
|
else:
|
|
angle = math.radians(45.0)
|
|
bpy.ops.uv.smart_project(angle_limit=angle, island_margin=obj.TLM_ObjectProperties.tlm_mesh_unwrap_margin, area_weight=1.0, correct_aspect=True, scale_to_bounds=False)
|
|
bpy.ops.mesh.select_all(action='DESELECT')
|
|
bpy.ops.object.mode_set(mode='OBJECT')
|
|
print("Smart project done.")
|
|
elif atlasgroup.tlm_atlas_lightmap_unwrap_mode == "Lightmap":
|
|
|
|
bpy.ops.object.mode_set(mode='EDIT')
|
|
bpy.ops.uv.lightmap_pack('EXEC_SCREEN', PREF_CONTEXT='ALL_FACES', PREF_MARGIN_DIV=atlasgroup.tlm_atlas_unwrap_margin)
|
|
bpy.ops.mesh.select_all(action='DESELECT')
|
|
bpy.ops.object.mode_set(mode='OBJECT')
|
|
|
|
elif atlasgroup.tlm_atlas_lightmap_unwrap_mode == "Xatlas":
|
|
|
|
if bpy.context.scene.TLM_SceneProperties.tlm_verbose:
|
|
print("Using Xatlas on Atlas Group: " + atlas)
|
|
|
|
for obj in atlas_items:
|
|
obj.select_set(True)
|
|
if len(atlas_items) > 0:
|
|
bpy.context.view_layer.objects.active = atlas_items[0]
|
|
|
|
bpy.ops.object.mode_set(mode='EDIT')
|
|
|
|
Unwrap_Lightmap_Group_Xatlas_2_headless_call(obj)
|
|
|
|
bpy.ops.object.mode_set(mode='OBJECT')
|
|
|
|
else:
|
|
if bpy.context.scene.TLM_SceneProperties.tlm_verbose:
|
|
print("Copied Existing UV Map for Atlas Group: " + atlas)
|
|
|
|
if atlasgroup.tlm_use_uv_packer:
|
|
bpy.ops.object.select_all(action='DESELECT')
|
|
for obj in atlas_items:
|
|
obj.select_set(True)
|
|
if len(atlas_items) > 0:
|
|
bpy.context.view_layer.objects.active = atlas_items[0]
|
|
bpy.ops.object.mode_set(mode='EDIT')
|
|
bpy.ops.mesh.select_all(action='SELECT')
|
|
|
|
bpy.context.scene.UVPackerProps.uvp_padding = atlasgroup.tlm_uv_packer_padding
|
|
bpy.context.scene.UVPackerProps.uvp_engine = atlasgroup.tlm_uv_packer_packing_engine
|
|
|
|
#print(x)
|
|
|
|
print("!!!!!!!!!!!!!!!!!!!!! Using UV Packer on: " + obj.name)
|
|
|
|
if uv_layers.active == "UVMap_Lightmap":
|
|
print("YES")
|
|
else:
|
|
print("NO")
|
|
uv_layers.active_index = len(uv_layers) - 1
|
|
|
|
if uv_layers.active == "UVMap_Lightmap":
|
|
print("YES")
|
|
else:
|
|
print("NO")
|
|
uv_layers.active_index = len(uv_layers) - 1
|
|
|
|
bpy.ops.uvpackeroperator.packbtn()
|
|
|
|
# if bpy.context.scene.UVPackerProps.uvp_engine == "OP0":
|
|
# time.sleep(1)
|
|
# else:
|
|
# time.sleep(2)
|
|
time.sleep(2)
|
|
|
|
#FIX THIS! MAKE A SEPARATE CALL. THIS IS A THREADED ASYNC
|
|
|
|
bpy.ops.mesh.select_all(action='DESELECT')
|
|
bpy.ops.object.mode_set(mode='OBJECT')
|
|
|
|
#print(x)
|
|
|
|
for obj in bpy.context.scene.objects:
|
|
if obj.type == 'MESH' and obj.name in bpy.context.view_layer.objects:
|
|
if obj.TLM_ObjectProperties.tlm_mesh_lightmap_use:
|
|
iterNum = iterNum + 1
|
|
|
|
#OBJECT UV PROJECTING
|
|
print("PREPARE: OBJECTS")
|
|
for obj in bpy.context.scene.objects:
|
|
if obj.name in bpy.context.view_layer.objects: #Possible fix for view layer error
|
|
if obj.type == 'MESH' and obj.name in bpy.context.view_layer.objects:
|
|
|
|
hidden = False
|
|
|
|
#We check if the object is hidden
|
|
if obj.hide_get():
|
|
hidden = True
|
|
if obj.hide_viewport:
|
|
hidden = True
|
|
if obj.hide_render:
|
|
hidden = True
|
|
|
|
#We check if the object's collection is hidden
|
|
collections = obj.users_collection
|
|
|
|
for collection in collections:
|
|
|
|
if collection.hide_viewport:
|
|
hidden = True
|
|
if collection.hide_render:
|
|
hidden = True
|
|
|
|
try:
|
|
if collection.name in bpy.context.scene.view_layers[0].layer_collection.children:
|
|
if bpy.context.scene.view_layers[0].layer_collection.children[collection.name].hide_viewport:
|
|
hidden = True
|
|
except:
|
|
print("Error: Could not find collection: " + collection.name)
|
|
|
|
if obj.TLM_ObjectProperties.tlm_mesh_lightmap_use and not hidden:
|
|
|
|
objWasHidden = False
|
|
|
|
#For some reason, a Blender bug might prevent invisible objects from being smart projected
|
|
#We will turn the object temporarily visible
|
|
obj.hide_viewport = False
|
|
obj.hide_set(False)
|
|
|
|
currentIterNum = currentIterNum + 1
|
|
|
|
#Configure selection
|
|
bpy.ops.object.select_all(action='DESELECT')
|
|
bpy.context.view_layer.objects.active = obj
|
|
obj.select_set(True)
|
|
|
|
obs = bpy.context.view_layer.objects
|
|
active = obs.active
|
|
|
|
#Provide material if none exists
|
|
print("Preprocessing material for: " + obj.name)
|
|
preprocess_material(obj, scene)
|
|
|
|
#UV Layer management here
|
|
if not obj.TLM_ObjectProperties.tlm_mesh_lightmap_unwrap_mode == "AtlasGroupA":
|
|
|
|
print("Managing layer for Obj: " + obj.name)
|
|
|
|
uv_layers = obj.data.uv_layers
|
|
|
|
if not obj.TLM_ObjectProperties.tlm_use_default_channel:
|
|
uv_channel = obj.TLM_ObjectProperties.tlm_uv_channel
|
|
else:
|
|
uv_channel = "UVMap_Lightmap"
|
|
|
|
if not uv_channel in uv_layers:
|
|
if bpy.context.scene.TLM_SceneProperties.tlm_verbose:
|
|
print("UV map created for obj: " + obj.name)
|
|
uvmap = uv_layers.new(name=uv_channel)
|
|
uv_layers.active_index = len(uv_layers) - 1
|
|
|
|
#If lightmap
|
|
if obj.TLM_ObjectProperties.tlm_mesh_lightmap_unwrap_mode == "Lightmap":
|
|
bpy.ops.uv.lightmap_pack('EXEC_SCREEN', PREF_CONTEXT='ALL_FACES', PREF_MARGIN_DIV=obj.TLM_ObjectProperties.tlm_mesh_unwrap_margin)
|
|
|
|
#If smart project
|
|
elif obj.TLM_ObjectProperties.tlm_mesh_lightmap_unwrap_mode == "SmartProject":
|
|
|
|
if bpy.context.scene.TLM_SceneProperties.tlm_verbose:
|
|
print("Smart Project B")
|
|
bpy.ops.object.select_all(action='DESELECT')
|
|
obj.select_set(True)
|
|
bpy.ops.object.mode_set(mode='EDIT')
|
|
bpy.ops.mesh.select_all(action='SELECT')
|
|
#API changes in 2.91 causes errors:
|
|
if (2, 91, 0) > bpy.app.version:
|
|
bpy.ops.uv.smart_project(angle_limit=45.0, island_margin=obj.TLM_ObjectProperties.tlm_mesh_unwrap_margin, user_area_weight=1.0, use_aspect=True, stretch_to_bounds=False)
|
|
else:
|
|
angle = math.radians(45.0)
|
|
bpy.ops.uv.smart_project(angle_limit=angle, island_margin=obj.TLM_ObjectProperties.tlm_mesh_unwrap_margin, area_weight=1.0, correct_aspect=True, scale_to_bounds=False)
|
|
|
|
bpy.ops.mesh.select_all(action='DESELECT')
|
|
bpy.ops.object.mode_set(mode='OBJECT')
|
|
|
|
elif obj.TLM_ObjectProperties.tlm_mesh_lightmap_unwrap_mode == "Xatlas":
|
|
|
|
Unwrap_Lightmap_Group_Xatlas_2_headless_call(obj)
|
|
|
|
elif obj.TLM_ObjectProperties.tlm_mesh_lightmap_unwrap_mode == "AtlasGroupA":
|
|
|
|
if bpy.context.scene.TLM_SceneProperties.tlm_verbose:
|
|
print("ATLAS GROUP: " + obj.TLM_ObjectProperties.tlm_atlas_pointer)
|
|
|
|
else: #if copy existing
|
|
|
|
if bpy.context.scene.TLM_SceneProperties.tlm_verbose:
|
|
print("Copied Existing UV Map for object: " + obj.name)
|
|
|
|
if obj.TLM_ObjectProperties.tlm_use_uv_packer:
|
|
bpy.ops.object.select_all(action='DESELECT')
|
|
obj.select_set(True)
|
|
bpy.context.view_layer.objects.active = obj
|
|
bpy.ops.object.mode_set(mode='EDIT')
|
|
bpy.ops.mesh.select_all(action='SELECT')
|
|
|
|
bpy.context.scene.UVPackerProps.uvp_padding = obj.TLM_ObjectProperties.tlm_uv_packer_padding
|
|
bpy.context.scene.UVPackerProps.uvp_engine = obj.TLM_ObjectProperties.tlm_uv_packer_packing_engine
|
|
|
|
#print(x)
|
|
|
|
print("!!!!!!!!!!!!!!!!!!!!! Using UV Packer on: " + obj.name)
|
|
|
|
if uv_layers.active == "UVMap_Lightmap":
|
|
print("YES")
|
|
else:
|
|
print("NO")
|
|
uv_layers.active_index = len(uv_layers) - 1
|
|
|
|
if uv_layers.active == "UVMap_Lightmap":
|
|
print("YES")
|
|
else:
|
|
print("NO")
|
|
uv_layers.active_index = len(uv_layers) - 1
|
|
|
|
bpy.ops.uvpackeroperator.packbtn()
|
|
|
|
if bpy.context.scene.UVPackerProps.uvp_engine == "OP0":
|
|
time.sleep(1)
|
|
else:
|
|
time.sleep(2)
|
|
|
|
#FIX THIS! MAKE A SEPARATE CALL. THIS IS A THREADED ASYNC
|
|
|
|
bpy.ops.mesh.select_all(action='DESELECT')
|
|
bpy.ops.object.mode_set(mode='OBJECT')
|
|
|
|
#print(x)
|
|
|
|
else:
|
|
if bpy.context.scene.TLM_SceneProperties.tlm_verbose:
|
|
print("Existing UV map found for obj: " + obj.name)
|
|
for i in range(0, len(uv_layers)):
|
|
if uv_layers[i].name == uv_channel:
|
|
uv_layers.active_index = i
|
|
break
|
|
|
|
#print(x)
|
|
|
|
#Sort out nodes
|
|
for slot in obj.material_slots:
|
|
|
|
nodetree = slot.material.node_tree
|
|
|
|
outputNode = nodetree.nodes[0] #Presumed to be material output node
|
|
|
|
if(outputNode.type != "OUTPUT_MATERIAL"):
|
|
for node in nodetree.nodes:
|
|
if node.type == "OUTPUT_MATERIAL":
|
|
outputNode = node
|
|
break
|
|
|
|
mainNode = outputNode.inputs[0].links[0].from_node
|
|
|
|
if mainNode.type not in ['BSDF_PRINCIPLED','BSDF_DIFFUSE','GROUP']:
|
|
|
|
#TODO! FIND THE PRINCIPLED PBR
|
|
self.report({'INFO'}, "The primary material node is not supported. Seeking first principled.")
|
|
|
|
if len(find_node_by_type(nodetree.nodes, Node_Types.pbr_node)) > 0:
|
|
mainNode = find_node_by_type(nodetree.nodes, Node_Types.pbr_node)[0]
|
|
else:
|
|
self.report({'INFO'}, "No principled found. Seeking diffuse")
|
|
if len(find_node_by_type(nodetree.nodes, Node_Types.diffuse)) > 0:
|
|
mainNode = find_node_by_type(nodetree.nodes, Node_Types.diffuse)[0]
|
|
else:
|
|
self.report({'INFO'}, "No supported nodes. Continuing anyway.")
|
|
|
|
if mainNode.type == 'GROUP':
|
|
if mainNode.node_tree != "Leenkx PBR":
|
|
if bpy.context.scene.TLM_SceneProperties.tlm_verbose:
|
|
print("The material group is not supported!")
|
|
|
|
if (mainNode.type == "ShaderNodeMixRGB"):
|
|
if bpy.context.scene.TLM_SceneProperties.tlm_verbose:
|
|
print("Mix shader found")
|
|
|
|
#Skip for now
|
|
slot.material.TLM_ignore = True
|
|
|
|
if (mainNode.type == "BSDF_PRINCIPLED"):
|
|
if bpy.context.scene.TLM_SceneProperties.tlm_verbose:
|
|
print("BSDF_Principled")
|
|
if scene.TLM_EngineProperties.tlm_directional_mode == "None":
|
|
if bpy.context.scene.TLM_SceneProperties.tlm_verbose:
|
|
print("Directional mode")
|
|
if not len(mainNode.inputs[22].links) == 0:
|
|
if bpy.context.scene.TLM_SceneProperties.tlm_verbose:
|
|
print("NOT LEN 0")
|
|
ninput = mainNode.inputs[22].links[0]
|
|
noutput = mainNode.inputs[22].links[0].from_node
|
|
nodetree.links.remove(noutput.outputs[0].links[0])
|
|
|
|
#Clamp metallic
|
|
if bpy.context.scene.TLM_SceneProperties.tlm_metallic_clamp == "limit":
|
|
|
|
MainMetNodeSocket = mainNode.inputs.get("Metallic")
|
|
if not len(MainMetNodeSocket.links) == 0:
|
|
|
|
print("Creating new clamp node")
|
|
|
|
nodes = nodetree.nodes
|
|
MetClampNode = nodes.new('ShaderNodeClamp')
|
|
MetClampNode.location = (-200,150)
|
|
MetClampNode.inputs[2].default_value = 0.9
|
|
minput = mainNode.inputs.get("Metallic").links[0] #Metal input socket
|
|
moutput = mainNode.inputs.get("Metallic").links[0].from_socket #Output socket
|
|
|
|
nodetree.links.remove(minput)
|
|
|
|
nodetree.links.new(moutput, MetClampNode.inputs[0]) #minput node to clamp node
|
|
nodetree.links.new(MetClampNode.outputs[0], MainMetNodeSocket) #clamp node to metinput
|
|
|
|
elif mainNode.type == "PRINCIPLED_BSDF" and MainMetNodeSocket.links[0].from_node.type == "CLAMP":
|
|
|
|
pass
|
|
|
|
else:
|
|
|
|
print("New clamp node NOT made")
|
|
|
|
if mainNode.inputs[4].default_value > 0.9:
|
|
mainNode.inputs[4].default_value = 0.9
|
|
|
|
elif bpy.context.scene.TLM_SceneProperties.tlm_metallic_clamp == "zero":
|
|
|
|
MainMetNodeSocket = mainNode.inputs[4]
|
|
if not len(MainMetNodeSocket.links) == 0:
|
|
nodes = nodetree.nodes
|
|
MetClampNode = nodes.new('ShaderNodeClamp')
|
|
MetClampNode.location = (-200,150)
|
|
MetClampNode.inputs[2].default_value = 0.0
|
|
minput = mainNode.inputs[4].links[0] #Metal input socket
|
|
moutput = mainNode.inputs[4].links[0].from_socket #Output socket
|
|
|
|
nodetree.links.remove(minput)
|
|
|
|
nodetree.links.new(moutput, MetClampNode.inputs[0]) #minput node to clamp node
|
|
nodetree.links.new(MetClampNode.outputs[0], MainMetNodeSocket) #clamp node to metinput
|
|
else:
|
|
mainNode.inputs[4].default_value = 0.0
|
|
|
|
else: #Skip
|
|
pass
|
|
|
|
if (mainNode.type == "BSDF_DIFFUSE"):
|
|
if bpy.context.scene.TLM_SceneProperties.tlm_verbose:
|
|
print("BSDF_Diffuse")
|
|
|
|
# if (mainNode.type == "BSDF_DIFFUSE"):
|
|
# if bpy.context.scene.TLM_SceneProperties.tlm_verbose:
|
|
# print("BSDF_Diffuse")
|
|
|
|
#TODO FIX THIS PART!
|
|
#THIS IS USED IN CASES WHERE FOR SOME REASON THE USER FORGETS TO CONNECT SOMETHING INTO THE OUTPUT MATERIAL
|
|
for slot in obj.material_slots:
|
|
|
|
nodetree = bpy.data.materials[slot.name].node_tree
|
|
nodes = nodetree.nodes
|
|
|
|
#First search to get the first output material type
|
|
for node in nodetree.nodes:
|
|
if node.type == "OUTPUT_MATERIAL":
|
|
mainNode = node
|
|
break
|
|
|
|
#Fallback to get search
|
|
if not mainNode.type == "OUTPUT_MATERIAL":
|
|
mainNode = nodetree.nodes.get("Material Output")
|
|
|
|
#Last resort to first node in list
|
|
if not mainNode.type == "OUTPUT_MATERIAL":
|
|
mainNode = nodetree.nodes[0].inputs[0].links[0].from_node
|
|
|
|
# for node in nodes:
|
|
# if "LM" in node.name:
|
|
# nodetree.links.new(node.outputs[0], mainNode.inputs[0])
|
|
|
|
# for node in nodes:
|
|
# if "Lightmap" in node.name:
|
|
# nodes.remove(node)
|
|
|
|
def preprocess_material(obj, scene):
|
|
if len(obj.material_slots) == 0:
|
|
single = False
|
|
number = 0
|
|
while single == False:
|
|
matname = obj.name + ".00" + str(number)
|
|
if matname in bpy.data.materials:
|
|
single = False
|
|
number = number + 1
|
|
else:
|
|
mat = bpy.data.materials.new(name=matname)
|
|
mat.use_nodes = True
|
|
obj.data.materials.append(mat)
|
|
single = True
|
|
|
|
#We copy the existing material slots to an ordered array, which corresponds to the slot index
|
|
matArray = []
|
|
for slot in obj.material_slots:
|
|
matArray.append(slot.name)
|
|
|
|
obj["TLM_PrevMatArray"] = matArray
|
|
|
|
#We check and safeguard against NoneType
|
|
for slot in obj.material_slots:
|
|
if slot.material is None:
|
|
matName = obj.name + ".00" + str(0)
|
|
bpy.data.materials.new(name=matName)
|
|
slot.material = bpy.data.materials[matName]
|
|
slot.material.use_nodes = True
|
|
|
|
for slot in obj.material_slots:
|
|
|
|
cache.backup_material_copy(slot)
|
|
|
|
mat = slot.material
|
|
if mat.users > 1:
|
|
copymat = mat.copy()
|
|
slot.material = copymat
|
|
|
|
#SOME ATLAS EXCLUSION HERE?
|
|
ob = obj
|
|
for slot in ob.material_slots:
|
|
#If temporary material already exists
|
|
if slot.material.name.endswith('_temp'):
|
|
continue
|
|
n = slot.material.name + '_' + ob.name + '_temp'
|
|
if not n in bpy.data.materials:
|
|
slot.material = slot.material.copy()
|
|
slot.material.name = n
|
|
|
|
#Add images for baking
|
|
img_name = obj.name + '_baked'
|
|
#Resolution is object lightmap resolution divided by global scaler
|
|
|
|
if scene.TLM_EngineProperties.tlm_setting_supersample == "2x":
|
|
supersampling_scale = 2
|
|
elif scene.TLM_EngineProperties.tlm_setting_supersample == "4x":
|
|
supersampling_scale = 4
|
|
else:
|
|
supersampling_scale = 1
|
|
|
|
|
|
if (obj.TLM_ObjectProperties.tlm_mesh_lightmap_unwrap_mode == "AtlasGroupA" and obj.TLM_ObjectProperties.tlm_atlas_pointer != ""):
|
|
|
|
atlas_image_name = obj.TLM_ObjectProperties.tlm_atlas_pointer + "_baked"
|
|
|
|
res = int(scene.TLM_AtlasList[obj.TLM_ObjectProperties.tlm_atlas_pointer].tlm_atlas_lightmap_resolution) / int(scene.TLM_EngineProperties.tlm_resolution_scale) * int(supersampling_scale)
|
|
|
|
#If image not in bpy.data.images or if size changed, make a new image
|
|
if atlas_image_name not in bpy.data.images or bpy.data.images[atlas_image_name].size[0] != res or bpy.data.images[atlas_image_name].size[1] != res:
|
|
img = bpy.data.images.new(img_name, int(res), int(res), alpha=True, float_buffer=True)
|
|
|
|
num_pixels = len(img.pixels)
|
|
result_pixel = list(img.pixels)
|
|
|
|
for i in range(0,num_pixels,4):
|
|
|
|
if scene.TLM_SceneProperties.tlm_override_bg_color:
|
|
result_pixel[i+0] = scene.TLM_SceneProperties.tlm_override_color[0]
|
|
result_pixel[i+1] = scene.TLM_SceneProperties.tlm_override_color[1]
|
|
result_pixel[i+2] = scene.TLM_SceneProperties.tlm_override_color[2]
|
|
else:
|
|
result_pixel[i+0] = 0.0
|
|
result_pixel[i+1] = 0.0
|
|
result_pixel[i+2] = 0.0
|
|
result_pixel[i+3] = 1.0
|
|
|
|
img.pixels = result_pixel
|
|
|
|
img.name = atlas_image_name
|
|
else:
|
|
img = bpy.data.images[atlas_image_name]
|
|
|
|
for slot in obj.material_slots:
|
|
mat = slot.material
|
|
mat.use_nodes = True
|
|
nodes = mat.node_tree.nodes
|
|
|
|
if "Baked Image" in nodes:
|
|
img_node = nodes["Baked Image"]
|
|
else:
|
|
img_node = nodes.new('ShaderNodeTexImage')
|
|
img_node.name = 'Baked Image'
|
|
img_node.location = (100, 100)
|
|
img_node.image = img
|
|
img_node.select = True
|
|
nodes.active = img_node
|
|
|
|
#We need to save this file first in Blender 3.3 due to new filmic option?
|
|
image = img
|
|
saveDir = os.path.join(os.path.dirname(bpy.data.filepath), bpy.context.scene.TLM_EngineProperties.tlm_lightmap_savedir)
|
|
bakemap_path = os.path.join(saveDir, image.name)
|
|
filepath_ext = ".hdr"
|
|
image.filepath_raw = bakemap_path + filepath_ext
|
|
image.file_format = "HDR"
|
|
if bpy.context.scene.TLM_SceneProperties.tlm_verbose:
|
|
print("Saving to: " + image.filepath_raw)
|
|
image.save()
|
|
|
|
else:
|
|
|
|
res = int(obj.TLM_ObjectProperties.tlm_mesh_lightmap_resolution) / int(scene.TLM_EngineProperties.tlm_resolution_scale) * int(supersampling_scale)
|
|
|
|
#If image not in bpy.data.images or if size changed, make a new image
|
|
if img_name not in bpy.data.images or bpy.data.images[img_name].size[0] != res or bpy.data.images[img_name].size[1] != res:
|
|
img = bpy.data.images.new(img_name, int(res), int(res), alpha=True, float_buffer=True)
|
|
|
|
num_pixels = len(img.pixels)
|
|
result_pixel = list(img.pixels)
|
|
|
|
for i in range(0,num_pixels,4):
|
|
if scene.TLM_SceneProperties.tlm_override_bg_color:
|
|
result_pixel[i+0] = scene.TLM_SceneProperties.tlm_override_color[0]
|
|
result_pixel[i+1] = scene.TLM_SceneProperties.tlm_override_color[1]
|
|
result_pixel[i+2] = scene.TLM_SceneProperties.tlm_override_color[2]
|
|
else:
|
|
result_pixel[i+0] = 0.0
|
|
result_pixel[i+1] = 0.0
|
|
result_pixel[i+2] = 0.0
|
|
result_pixel[i+3] = 1.0
|
|
|
|
img.pixels = result_pixel
|
|
|
|
img.name = img_name
|
|
else:
|
|
img = bpy.data.images[img_name]
|
|
|
|
for slot in obj.material_slots:
|
|
mat = slot.material
|
|
mat.use_nodes = True
|
|
nodes = mat.node_tree.nodes
|
|
|
|
if "Baked Image" in nodes:
|
|
img_node = nodes["Baked Image"]
|
|
else:
|
|
img_node = nodes.new('ShaderNodeTexImage')
|
|
img_node.name = 'Baked Image'
|
|
img_node.location = (100, 100)
|
|
img_node.image = img
|
|
img_node.select = True
|
|
nodes.active = img_node
|
|
|
|
#We need to save this file first in Blender 3.3 due to new filmic option?
|
|
image = img
|
|
saveDir = os.path.join(os.path.dirname(bpy.data.filepath), bpy.context.scene.TLM_EngineProperties.tlm_lightmap_savedir)
|
|
bakemap_path = os.path.join(saveDir, image.name)
|
|
filepath_ext = ".hdr"
|
|
image.filepath_raw = bakemap_path + filepath_ext
|
|
image.file_format = "HDR"
|
|
if bpy.context.scene.TLM_SceneProperties.tlm_verbose:
|
|
print("Saving to: " + image.filepath_raw)
|
|
image.save()
|
|
|
|
def set_settings():
|
|
|
|
scene = bpy.context.scene
|
|
cycles = scene.cycles
|
|
scene.render.engine = "CYCLES"
|
|
sceneProperties = scene.TLM_SceneProperties
|
|
engineProperties = scene.TLM_EngineProperties
|
|
cycles.device = scene.TLM_EngineProperties.tlm_mode
|
|
|
|
print(bpy.app.version)
|
|
|
|
if bpy.app.version[0] == 3:
|
|
if cycles.device == "GPU":
|
|
scene.cycles.tile_size = 256
|
|
else:
|
|
scene.cycles.tile_size = 32
|
|
else:
|
|
if cycles.device == "GPU":
|
|
scene.render.tile_x = 256
|
|
scene.render.tile_y = 256
|
|
else:
|
|
scene.render.tile_x = 32
|
|
scene.render.tile_y = 32
|
|
|
|
if engineProperties.tlm_quality == "0":
|
|
cycles.samples = 32
|
|
cycles.max_bounces = 1
|
|
cycles.diffuse_bounces = 1
|
|
cycles.glossy_bounces = 1
|
|
cycles.transparent_max_bounces = 1
|
|
cycles.transmission_bounces = 1
|
|
cycles.volume_bounces = 1
|
|
cycles.caustics_reflective = False
|
|
cycles.caustics_refractive = False
|
|
elif engineProperties.tlm_quality == "1":
|
|
cycles.samples = 64
|
|
cycles.max_bounces = 2
|
|
cycles.diffuse_bounces = 2
|
|
cycles.glossy_bounces = 2
|
|
cycles.transparent_max_bounces = 2
|
|
cycles.transmission_bounces = 2
|
|
cycles.volume_bounces = 2
|
|
cycles.caustics_reflective = False
|
|
cycles.caustics_refractive = False
|
|
elif engineProperties.tlm_quality == "2":
|
|
cycles.samples = 512
|
|
cycles.max_bounces = 2
|
|
cycles.diffuse_bounces = 2
|
|
cycles.glossy_bounces = 2
|
|
cycles.transparent_max_bounces = 2
|
|
cycles.transmission_bounces = 2
|
|
cycles.volume_bounces = 2
|
|
cycles.caustics_reflective = False
|
|
cycles.caustics_refractive = False
|
|
elif engineProperties.tlm_quality == "3":
|
|
cycles.samples = 1024
|
|
cycles.max_bounces = 256
|
|
cycles.diffuse_bounces = 256
|
|
cycles.glossy_bounces = 256
|
|
cycles.transparent_max_bounces = 256
|
|
cycles.transmission_bounces = 256
|
|
cycles.volume_bounces = 256
|
|
cycles.caustics_reflective = False
|
|
cycles.caustics_refractive = False
|
|
elif engineProperties.tlm_quality == "4":
|
|
cycles.samples = 2048
|
|
cycles.max_bounces = 512
|
|
cycles.diffuse_bounces = 512
|
|
cycles.glossy_bounces = 512
|
|
cycles.transparent_max_bounces = 512
|
|
cycles.transmission_bounces = 512
|
|
cycles.volume_bounces = 512
|
|
cycles.caustics_reflective = True
|
|
cycles.caustics_refractive = True
|
|
else: #Custom
|
|
pass
|
|
|
|
def store_existing(prev_container):
|
|
|
|
scene = bpy.context.scene
|
|
cycles = scene.cycles
|
|
|
|
selected = []
|
|
|
|
for obj in bpy.context.scene.objects:
|
|
if obj.select_get():
|
|
selected.append(obj.name)
|
|
|
|
prev_container["settings"] = [
|
|
cycles.samples,
|
|
cycles.max_bounces,
|
|
cycles.diffuse_bounces,
|
|
cycles.glossy_bounces,
|
|
cycles.transparent_max_bounces,
|
|
cycles.transmission_bounces,
|
|
cycles.volume_bounces,
|
|
cycles.caustics_reflective,
|
|
cycles.caustics_refractive,
|
|
cycles.device,
|
|
scene.render.engine,
|
|
bpy.context.view_layer.objects.active,
|
|
selected,
|
|
[scene.render.resolution_x, scene.render.resolution_y]
|
|
]
|
|
|
|
def skipIncompatibleMaterials(material):
|
|
node_tree = material.node_tree
|
|
nodes = material.node_tree.nodes
|
|
|
|
#ADD OR MIX SHADER? CUSTOM/GROUP?
|
|
#IF Principled has emissive or transparency?
|
|
|
|
SkipMatList = ["EMISSION",
|
|
"BSDF_TRANSPARENT",
|
|
"BACKGROUND",
|
|
"BSDF_HAIR",
|
|
"BSDF_HAIR_PRINCIPLED",
|
|
"HOLDOUT",
|
|
"PRINCIPLED_VOLUME",
|
|
"BSDF_REFRACTION",
|
|
"EEVEE_SPECULAR",
|
|
"BSDF_TRANSLUCENT",
|
|
"VOLUME_ABSORPTION",
|
|
"VOLUME_SCATTER"]
|
|
|
|
#Find output node
|
|
outputNode = nodes[0]
|
|
if(outputNode.type != "OUTPUT_MATERIAL"):
|
|
for node in node_tree.nodes:
|
|
if node.type == "OUTPUT_MATERIAL":
|
|
outputNode = node
|
|
break
|
|
|
|
#Find mainnode
|
|
mainNode = outputNode.inputs[0].links[0].from_node
|
|
|
|
if mainNode.type in SkipMatList:
|
|
material.TLM_ignore = True
|
|
print("Ignored material: " + material.name)
|
|
|
|
def packUVPack():
|
|
|
|
|
|
|
|
pass
|