forked from LeenkxTeam/LNXSDK
1732 lines
66 KiB
Python
1732 lines
66 KiB
Python
import bpy, os, time, blf, webbrowser, platform, numpy, bmesh
|
|
import math, subprocess, multiprocessing
|
|
from .. utility import utility
|
|
from .. utility import build
|
|
from .. utility.cycles import cache
|
|
from .. network import server
|
|
|
|
def setObjectLightmapByWeight(minimumRes, maximumRes, objWeight):
|
|
|
|
availableResolutions = [32,64,128,256,512,1024,2048,4096,8192]
|
|
|
|
minRes = minimumRes
|
|
minResIdx = availableResolutions.index(minRes)
|
|
maxRes = maximumRes
|
|
maxResIdx = availableResolutions.index(maxRes)
|
|
|
|
exampleWeight = objWeight
|
|
|
|
if minResIdx == maxResIdx:
|
|
pass
|
|
else:
|
|
|
|
increment = 1.0/(maxResIdx-minResIdx)
|
|
|
|
assortedRange = []
|
|
|
|
for a in numpy.arange(0.0, 1.0, increment):
|
|
assortedRange.append(round(a, 2))
|
|
|
|
assortedRange.append(1.0)
|
|
nearestWeight = min(assortedRange, key=lambda x:abs(x - exampleWeight))
|
|
return (availableResolutions[assortedRange.index(nearestWeight) + minResIdx])
|
|
|
|
class TLM_BuildLightmaps(bpy.types.Operator):
|
|
bl_idname = "tlm.build_lightmaps"
|
|
bl_label = "Build Lightmaps"
|
|
bl_description = "Build Lightmaps"
|
|
bl_options = {'REGISTER', 'UNDO'}
|
|
|
|
def modal(self, context, event):
|
|
|
|
#Add progress bar from 0.15
|
|
|
|
print("MODAL")
|
|
|
|
return {'PASS_THROUGH'}
|
|
|
|
def invoke(self, context, event):
|
|
|
|
if not bpy.app.background:
|
|
|
|
build.prepare_build(self, False)
|
|
|
|
else:
|
|
|
|
print("Running in background mode. Contextual operator not available. Use command 'thelightmapper.addon.build.prepare_build()'")
|
|
|
|
return {'RUNNING_MODAL'}
|
|
|
|
def cancel(self, context):
|
|
pass
|
|
|
|
def draw_callback_px(self, context, event):
|
|
pass
|
|
|
|
class TLM_CleanLightmaps(bpy.types.Operator):
|
|
bl_idname = "tlm.clean_lightmaps"
|
|
bl_label = "Clean Lightmaps"
|
|
bl_description = "Clean Lightmaps"
|
|
bl_options = {'REGISTER', 'UNDO'}
|
|
|
|
def execute(self, context):
|
|
|
|
scene = context.scene
|
|
|
|
filepath = bpy.data.filepath
|
|
dirpath = os.path.join(os.path.dirname(bpy.data.filepath), scene.TLM_EngineProperties.tlm_lightmap_savedir)
|
|
|
|
if not bpy.context.scene.TLM_SceneProperties.tlm_keep_baked_files:
|
|
if os.path.isdir(dirpath):
|
|
for file in os.listdir(dirpath):
|
|
os.remove(os.path.join(dirpath + "/" + file))
|
|
|
|
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)
|
|
|
|
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 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:
|
|
for vertex_layer in obj.data.vertex_colors:
|
|
if vertex_layer.name == "TLM":
|
|
obj.data.vertex_colors.remove(vertex_layer)
|
|
|
|
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)
|
|
|
|
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:
|
|
if obj.TLM_ObjectProperties.tlm_postpack_object:
|
|
|
|
atlas = obj.TLM_ObjectProperties.tlm_postatlas_pointer
|
|
atlas_resize = False
|
|
|
|
for atlasgroup in scene.TLM_PostAtlasList:
|
|
if atlasgroup.name == atlas:
|
|
atlas_resize = True
|
|
|
|
if atlas_resize:
|
|
|
|
bpy.ops.object.select_all(action='DESELECT')
|
|
obj.select_set(True)
|
|
bpy.context.view_layer.objects.active = obj
|
|
|
|
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"
|
|
|
|
for i in range(0, len(uv_layers)):
|
|
if uv_layers[i].name == uv_channel:
|
|
uv_layers.active_index = i
|
|
break
|
|
|
|
bpy.ops.object.mode_set(mode='EDIT')
|
|
bpy.ops.mesh.select_all(action='SELECT')
|
|
bpy.ops.uv.select_all(action='SELECT')
|
|
bpy.ops.uv.pack_islands(rotate=False, margin=0.001)
|
|
bpy.ops.uv.select_all(action='DESELECT')
|
|
bpy.ops.mesh.select_all(action='DESELECT')
|
|
bpy.ops.object.mode_set(mode='OBJECT')
|
|
|
|
if bpy.context.scene.TLM_SceneProperties.tlm_verbose:
|
|
print("Resized for obj: " + obj.name)
|
|
|
|
if "Lightmap" in obj:
|
|
del obj["Lightmap"]
|
|
|
|
if bpy.context.scene.TLM_SceneProperties.tlm_repartition_on_clean:
|
|
|
|
mats = bpy.data.materials
|
|
|
|
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:
|
|
|
|
print("Repartitioning materials")
|
|
|
|
for slt in obj.material_slots:
|
|
print("Repartitioning material: " + str(slt.name))
|
|
part = slt.name.rpartition('.')
|
|
if part[2].isnumeric() and part[0] in mats:
|
|
slt.material = mats.get(part[0])
|
|
|
|
for slt in obj.material_slots:
|
|
if slt.name.endswith(tuple(["001","002","003","004","005","006","007","008","009"])): #Do regex instead
|
|
if not slt.name[:-4] in mats:
|
|
slt.material.name = slt.name[:-4]
|
|
|
|
return {'FINISHED'}
|
|
|
|
class TLM_ExploreLightmaps(bpy.types.Operator):
|
|
bl_idname = "tlm.explore_lightmaps"
|
|
bl_label = "Explore Lightmaps"
|
|
bl_description = "Explore Lightmaps"
|
|
bl_options = {'REGISTER', 'UNDO'}
|
|
|
|
def execute(self, context):
|
|
|
|
scene = context.scene
|
|
cycles = scene.cycles
|
|
|
|
if not bpy.data.is_saved:
|
|
self.report({'INFO'}, "Please save your file first")
|
|
return {"CANCELLED"}
|
|
|
|
filepath = bpy.data.filepath
|
|
dirpath = os.path.join(os.path.dirname(bpy.data.filepath), scene.TLM_EngineProperties.tlm_lightmap_savedir)
|
|
|
|
if platform.system() != "Linux":
|
|
|
|
if os.path.isdir(dirpath):
|
|
webbrowser.open('file://' + dirpath)
|
|
else:
|
|
os.mkdir(dirpath)
|
|
webbrowser.open('file://' + dirpath)
|
|
else:
|
|
|
|
if os.path.isdir(dirpath):
|
|
os.system('xdg-open "%s"' % dirpath)
|
|
#webbrowser.open('file://' + dirpath)
|
|
else:
|
|
os.mkdir(dirpath)
|
|
os.system('xdg-open "%s"' % dirpath)
|
|
#webbrowser.open('file://' + dirpath)
|
|
|
|
return {'FINISHED'}
|
|
|
|
class TLM_EnableSet(bpy.types.Operator):
|
|
"""Enable for set"""
|
|
bl_idname = "tlm.enable_set"
|
|
bl_label = "Enable for set"
|
|
bl_description = "Enable for set"
|
|
bl_options = {'REGISTER', 'UNDO'}
|
|
|
|
def execute(self, context):
|
|
|
|
scene = context.scene
|
|
|
|
weightList = {} #ObjName : [Dimension,Weight]
|
|
max = 0
|
|
|
|
if bpy.context.scene.TLM_SceneProperties.tlm_utility_set == "Scene":
|
|
for obj in bpy.context.scene.objects:
|
|
if obj.type == "MESH":
|
|
|
|
print("Enabling for scene: " + obj.name)
|
|
|
|
bpy.context.view_layer.objects.active = obj
|
|
obj.select_set(True)
|
|
bpy.ops.object.transform_apply(location=False, rotation=True, scale=True)
|
|
|
|
obj.TLM_ObjectProperties.tlm_mesh_lightmap_use = True
|
|
|
|
obj.TLM_ObjectProperties.tlm_mesh_lightmap_unwrap_mode = bpy.context.scene.TLM_SceneProperties.tlm_mesh_lightmap_unwrap_mode
|
|
obj.TLM_ObjectProperties.tlm_mesh_unwrap_margin = bpy.context.scene.TLM_SceneProperties.tlm_mesh_unwrap_margin
|
|
obj.TLM_ObjectProperties.tlm_postpack_object = bpy.context.scene.TLM_SceneProperties.tlm_postpack_object
|
|
|
|
if obj.TLM_ObjectProperties.tlm_mesh_lightmap_unwrap_mode == "AtlasGroupA":
|
|
obj.TLM_ObjectProperties.tlm_atlas_pointer = bpy.context.scene.TLM_SceneProperties.tlm_atlas_pointer
|
|
|
|
obj.TLM_ObjectProperties.tlm_postatlas_pointer = bpy.context.scene.TLM_SceneProperties.tlm_postatlas_pointer
|
|
|
|
if bpy.context.scene.TLM_SceneProperties.tlm_resolution_weight == "Single":
|
|
obj.TLM_ObjectProperties.tlm_mesh_lightmap_resolution = scene.TLM_SceneProperties.tlm_mesh_lightmap_resolution
|
|
elif bpy.context.scene.TLM_SceneProperties.tlm_resolution_weight == "Dimension":
|
|
obj_dimensions = obj.dimensions.x * obj.dimensions.y * obj.dimensions.z
|
|
weightList[obj.name] = [obj_dimensions, 0]
|
|
if obj_dimensions > max:
|
|
max = obj_dimensions
|
|
elif bpy.context.scene.TLM_SceneProperties.tlm_resolution_weight == "Surface":
|
|
bm = bmesh.new()
|
|
bm.from_mesh(obj.data)
|
|
area = sum(f.calc_area() for f in bm.faces)
|
|
weightList[obj.name] = [area, 0]
|
|
if area > max:
|
|
max = area
|
|
elif bpy.context.scene.TLM_SceneProperties.tlm_resolution_weight == "Volume":
|
|
bm = bmesh.new()
|
|
bm.from_mesh(obj.data)
|
|
volume = float( bm.calc_volume())
|
|
weightList[obj.name] = [volume, 0]
|
|
if volume > max:
|
|
max = volume
|
|
|
|
elif bpy.context.scene.TLM_SceneProperties.tlm_utility_set == "Selection":
|
|
for obj in bpy.context.selected_objects:
|
|
if obj.type == "MESH":
|
|
|
|
print("Enabling for selection: " + obj.name)
|
|
|
|
bpy.context.view_layer.objects.active = obj
|
|
obj.select_set(True)
|
|
bpy.ops.object.transform_apply(location=False, rotation=True, scale=True)
|
|
|
|
obj.TLM_ObjectProperties.tlm_mesh_lightmap_use = True
|
|
|
|
obj.TLM_ObjectProperties.tlm_mesh_lightmap_unwrap_mode = bpy.context.scene.TLM_SceneProperties.tlm_mesh_lightmap_unwrap_mode
|
|
obj.TLM_ObjectProperties.tlm_mesh_unwrap_margin = bpy.context.scene.TLM_SceneProperties.tlm_mesh_unwrap_margin
|
|
obj.TLM_ObjectProperties.tlm_postpack_object = bpy.context.scene.TLM_SceneProperties.tlm_postpack_object
|
|
|
|
if obj.TLM_ObjectProperties.tlm_mesh_lightmap_unwrap_mode == "AtlasGroupA":
|
|
obj.TLM_ObjectProperties.tlm_atlas_pointer = bpy.context.scene.TLM_SceneProperties.tlm_atlas_pointer
|
|
|
|
obj.TLM_ObjectProperties.tlm_postatlas_pointer = bpy.context.scene.TLM_SceneProperties.tlm_postatlas_pointer
|
|
|
|
if bpy.context.scene.TLM_SceneProperties.tlm_resolution_weight == "Single":
|
|
obj.TLM_ObjectProperties.tlm_mesh_lightmap_resolution = scene.TLM_SceneProperties.tlm_mesh_lightmap_resolution
|
|
elif bpy.context.scene.TLM_SceneProperties.tlm_resolution_weight == "Dimension":
|
|
obj_dimensions = obj.dimensions.x * obj.dimensions.y * obj.dimensions.z
|
|
weightList[obj.name] = [obj_dimensions, 0]
|
|
if obj_dimensions > max:
|
|
max = obj_dimensions
|
|
elif bpy.context.scene.TLM_SceneProperties.tlm_resolution_weight == "Surface":
|
|
bm = bmesh.new()
|
|
bm.from_mesh(obj.data)
|
|
area = sum(f.calc_area() for f in bm.faces)
|
|
weightList[obj.name] = [area, 0]
|
|
if area > max:
|
|
max = area
|
|
elif bpy.context.scene.TLM_SceneProperties.tlm_resolution_weight == "Volume":
|
|
bm = bmesh.new()
|
|
bm.from_mesh(obj.data)
|
|
volume = float( bm.calc_volume())
|
|
weightList[obj.name] = [volume, 0]
|
|
if volume > max:
|
|
max = volume
|
|
|
|
else: #Enabled
|
|
for obj in bpy.context.scene.objects:
|
|
if obj.type == "MESH":
|
|
if obj.TLM_ObjectProperties.tlm_mesh_lightmap_use:
|
|
|
|
print("Enabling for designated: " + obj.name)
|
|
|
|
bpy.context.view_layer.objects.active = obj
|
|
obj.select_set(True)
|
|
bpy.ops.object.transform_apply(location=False, rotation=True, scale=True)
|
|
|
|
obj.TLM_ObjectProperties.tlm_mesh_lightmap_use = True
|
|
|
|
obj.TLM_ObjectProperties.tlm_mesh_lightmap_unwrap_mode = bpy.context.scene.TLM_SceneProperties.tlm_mesh_lightmap_unwrap_mode
|
|
obj.TLM_ObjectProperties.tlm_mesh_unwrap_margin = bpy.context.scene.TLM_SceneProperties.tlm_mesh_unwrap_margin
|
|
obj.TLM_ObjectProperties.tlm_postpack_object = bpy.context.scene.TLM_SceneProperties.tlm_postpack_object
|
|
|
|
if obj.TLM_ObjectProperties.tlm_mesh_lightmap_unwrap_mode == "AtlasGroupA":
|
|
obj.TLM_ObjectProperties.tlm_atlas_pointer = bpy.context.scene.TLM_SceneProperties.tlm_atlas_pointer
|
|
|
|
obj.TLM_ObjectProperties.tlm_postatlas_pointer = bpy.context.scene.TLM_SceneProperties.tlm_postatlas_pointer
|
|
|
|
if bpy.context.scene.TLM_SceneProperties.tlm_resolution_weight == "Single":
|
|
obj.TLM_ObjectProperties.tlm_mesh_lightmap_resolution = scene.TLM_SceneProperties.tlm_mesh_lightmap_resolution
|
|
elif bpy.context.scene.TLM_SceneProperties.tlm_resolution_weight == "Dimension":
|
|
obj_dimensions = obj.dimensions.x * obj.dimensions.y * obj.dimensions.z
|
|
weightList[obj.name] = [obj_dimensions, 0]
|
|
if obj_dimensions > max:
|
|
max = obj_dimensions
|
|
elif bpy.context.scene.TLM_SceneProperties.tlm_resolution_weight == "Surface":
|
|
bm = bmesh.new()
|
|
bm.from_mesh(obj.data)
|
|
area = sum(f.calc_area() for f in bm.faces)
|
|
weightList[obj.name] = [area, 0]
|
|
if area > max:
|
|
max = area
|
|
elif bpy.context.scene.TLM_SceneProperties.tlm_resolution_weight == "Volume":
|
|
bm = bmesh.new()
|
|
bm.from_mesh(obj.data)
|
|
volume = float( bm.calc_volume())
|
|
weightList[obj.name] = [volume, 0]
|
|
if volume > max:
|
|
max = volume
|
|
|
|
if bpy.context.scene.TLM_SceneProperties.tlm_utility_set == "Scene":
|
|
for obj in bpy.context.scene.objects:
|
|
if obj.type == "MESH":
|
|
|
|
if bpy.context.scene.TLM_SceneProperties.tlm_resolution_weight != "Single":
|
|
for key in weightList:
|
|
weightList[obj.name][1] = weightList[obj.name][0] / max
|
|
a = setObjectLightmapByWeight(int(bpy.context.scene.TLM_SceneProperties.tlm_resolution_min), int(bpy.context.scene.TLM_SceneProperties.tlm_resolution_max), weightList[obj.name][1])
|
|
print(str(a) + "/" + str(weightList[obj.name][1]))
|
|
print("Scale: " + str(weightList[obj.name][0]))
|
|
print("Obj: " + obj.name)
|
|
obj.TLM_ObjectProperties.tlm_mesh_lightmap_resolution = str(a)
|
|
|
|
elif bpy.context.scene.TLM_SceneProperties.tlm_utility_set == "Selection":
|
|
for obj in bpy.context.selected_objects:
|
|
if obj.type == "MESH":
|
|
|
|
if bpy.context.scene.TLM_SceneProperties.tlm_resolution_weight != "Single":
|
|
for key in weightList:
|
|
weightList[obj.name][1] = weightList[obj.name][0] / max
|
|
a = setObjectLightmapByWeight(int(bpy.context.scene.TLM_SceneProperties.tlm_resolution_min), int(bpy.context.scene.TLM_SceneProperties.tlm_resolution_max), weightList[obj.name][1])
|
|
print(str(a) + "/" + str(weightList[obj.name][1]))
|
|
print("Scale: " + str(weightList[obj.name][0]))
|
|
print("Obj: " + obj.name)
|
|
obj.TLM_ObjectProperties.tlm_mesh_lightmap_resolution = str(a)
|
|
|
|
|
|
else: #Enabled
|
|
for obj in bpy.context.scene.objects:
|
|
if obj.type == "MESH":
|
|
if obj.TLM_ObjectProperties.tlm_mesh_lightmap_use:
|
|
|
|
if bpy.context.scene.TLM_SceneProperties.tlm_resolution_weight != "Single":
|
|
for key in weightList:
|
|
weightList[obj.name][1] = weightList[obj.name][0] / max
|
|
a = setObjectLightmapByWeight(int(bpy.context.scene.TLM_SceneProperties.tlm_resolution_min), int(bpy.context.scene.TLM_SceneProperties.tlm_resolution_max), weightList[obj.name][1])
|
|
print(str(a) + "/" + str(weightList[obj.name][1]))
|
|
print("Scale: " + str(weightList[obj.name][0]))
|
|
print("Obj: " + obj.name)
|
|
print("")
|
|
obj.TLM_ObjectProperties.tlm_mesh_lightmap_resolution = str(a)
|
|
|
|
return{'FINISHED'}
|
|
|
|
class TLM_DisableSelection(bpy.types.Operator):
|
|
"""Disable for set"""
|
|
bl_idname = "tlm.disable_selection"
|
|
bl_label = "Disable for set"
|
|
bl_description = "Disable for selection"
|
|
bl_options = {'REGISTER', 'UNDO'}
|
|
|
|
def execute(self, context):
|
|
|
|
scene = context.scene
|
|
|
|
weightList = {} #ObjName : [Dimension,Weight]
|
|
max = 0
|
|
|
|
if bpy.context.scene.TLM_SceneProperties.tlm_utility_set == "Scene":
|
|
for obj in bpy.context.scene.objects:
|
|
if obj.type == "MESH":
|
|
|
|
obj.TLM_ObjectProperties.tlm_mesh_lightmap_use = False
|
|
|
|
elif bpy.context.scene.TLM_SceneProperties.tlm_utility_set == "Selection":
|
|
for obj in bpy.context.selected_objects:
|
|
if obj.type == "MESH":
|
|
|
|
obj.TLM_ObjectProperties.tlm_mesh_lightmap_use = False
|
|
|
|
|
|
else: #Enabled
|
|
for obj in bpy.context.scene.objects:
|
|
if obj.type == "MESH":
|
|
if obj.TLM_ObjectProperties.tlm_mesh_lightmap_use:
|
|
|
|
obj.TLM_ObjectProperties.tlm_mesh_lightmap_use = False
|
|
|
|
|
|
return{'FINISHED'}
|
|
|
|
class TLM_RemoveLightmapUV(bpy.types.Operator):
|
|
"""Remove Lightmap UV for set"""
|
|
bl_idname = "tlm.remove_uv_selection"
|
|
bl_label = "Remove Lightmap UV"
|
|
bl_description = "Remove Lightmap UV for set"
|
|
bl_options = {'REGISTER', 'UNDO'}
|
|
|
|
def execute(self, context):
|
|
|
|
if bpy.context.scene.TLM_SceneProperties.tlm_utility_set == "Scene":
|
|
for obj in bpy.context.scene.objects:
|
|
if obj.type == "MESH":
|
|
|
|
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"
|
|
|
|
for uvlayer in uv_layers:
|
|
if uvlayer.name == uv_channel:
|
|
uv_layers.remove(uvlayer)
|
|
|
|
elif bpy.context.scene.TLM_SceneProperties.tlm_utility_set == "Selection":
|
|
for obj in bpy.context.selected_objects:
|
|
if obj.type == "MESH":
|
|
|
|
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"
|
|
|
|
for uvlayer in uv_layers:
|
|
if uvlayer.name == uv_channel:
|
|
uv_layers.remove(uvlayer)
|
|
|
|
else: #Enabled
|
|
for obj in bpy.context.scene.objects:
|
|
if obj.type == "MESH":
|
|
if obj.TLM_ObjectProperties.tlm_mesh_lightmap_use:
|
|
|
|
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"
|
|
|
|
for uvlayer in uv_layers:
|
|
if uvlayer.name == uv_channel:
|
|
uv_layers.remove(uvlayer)
|
|
|
|
return{'FINISHED'}
|
|
|
|
class TLM_SelectLightmapped(bpy.types.Operator):
|
|
"""Select all objects for lightmapping"""
|
|
bl_idname = "tlm.select_lightmapped_objects"
|
|
bl_label = "Select lightmap objects"
|
|
bl_description = "Remove Lightmap UV for selection"
|
|
bl_options = {'REGISTER', 'UNDO'}
|
|
|
|
def execute(self, context):
|
|
|
|
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:
|
|
|
|
obj.select_set(True)
|
|
|
|
return{'FINISHED'}
|
|
|
|
class TLM_GroupListNewItem(bpy.types.Operator):
|
|
# Add a new item to the list
|
|
bl_idname = "tlm_grouplist.new_item"
|
|
bl_label = "Add a new lightmap group"
|
|
bl_description = "Create a new lightmap group"
|
|
|
|
def execute(self, context):
|
|
scene = context.scene
|
|
scene.TLM_GroupList.add()
|
|
scene.TLM_GroupListItem = len(scene.TLM_GroupList) - 1
|
|
|
|
scene.TLM_GroupList[len(scene.TLM_GroupList) - 1].name = "LightmapGroup"
|
|
|
|
class TLM_AtlasListNewItem(bpy.types.Operator):
|
|
# Add a new item to the list
|
|
bl_idname = "tlm_atlaslist.new_item"
|
|
bl_label = "Add a new item"
|
|
bl_description = "Create a new AtlasGroup"
|
|
|
|
def execute(self, context):
|
|
scene = context.scene
|
|
scene.TLM_AtlasList.add()
|
|
scene.TLM_AtlasListItem = len(scene.TLM_AtlasList) - 1
|
|
|
|
scene.TLM_AtlasList[len(scene.TLM_AtlasList) - 1].name = "AtlasGroup"
|
|
|
|
return{'FINISHED'}
|
|
|
|
class TLM_PostAtlasListNewItem(bpy.types.Operator):
|
|
# Add a new item to the list
|
|
bl_idname = "tlm_postatlaslist.new_item"
|
|
bl_label = "Add a new item"
|
|
bl_description = "Create a new AtlasGroup"
|
|
bl_description = ""
|
|
|
|
def execute(self, context):
|
|
scene = context.scene
|
|
scene.TLM_PostAtlasList.add()
|
|
scene.TLM_PostAtlasListItem = len(scene.TLM_PostAtlasList) - 1
|
|
|
|
scene.TLM_PostAtlasList[len(scene.TLM_PostAtlasList) - 1].name = "AtlasGroup"
|
|
|
|
return{'FINISHED'}
|
|
|
|
class TLM_AtlastListDeleteItem(bpy.types.Operator):
|
|
# Delete the selected item from the list
|
|
bl_idname = "tlm_atlaslist.delete_item"
|
|
bl_label = "Deletes an item"
|
|
bl_description = "Delete an AtlasGroup"
|
|
|
|
@classmethod
|
|
def poll(self, context):
|
|
""" Enable if there's something in the list """
|
|
scene = context.scene
|
|
return len(scene.TLM_AtlasList) > 0
|
|
|
|
def execute(self, context):
|
|
scene = context.scene
|
|
list = scene.TLM_AtlasList
|
|
index = scene.TLM_AtlasListItem
|
|
|
|
for obj in bpy.context.scene.objects:
|
|
|
|
atlasName = scene.TLM_AtlasList[index].name
|
|
|
|
if obj.TLM_ObjectProperties.tlm_atlas_pointer == atlasName:
|
|
obj.TLM_ObjectProperties.tlm_mesh_lightmap_unwrap_mode = "SmartProject"
|
|
|
|
list.remove(index)
|
|
|
|
if index > 0:
|
|
index = index - 1
|
|
|
|
scene.TLM_AtlasListItem = index
|
|
return{'FINISHED'}
|
|
|
|
class TLM_PostAtlastListDeleteItem(bpy.types.Operator):
|
|
# Delete the selected item from the list
|
|
bl_idname = "tlm_postatlaslist.delete_item"
|
|
bl_label = "Deletes an item"
|
|
bl_description = "Delete an AtlasGroup"
|
|
|
|
@classmethod
|
|
def poll(self, context):
|
|
""" Enable if there's something in the list """
|
|
scene = context.scene
|
|
return len(scene.TLM_PostAtlasList) > 0
|
|
|
|
def execute(self, context):
|
|
scene = context.scene
|
|
list = scene.TLM_PostAtlasList
|
|
index = scene.TLM_PostAtlasListItem
|
|
|
|
for obj in bpy.context.scene.objects:
|
|
|
|
atlasName = scene.TLM_PostAtlasList[index].name
|
|
|
|
if obj.TLM_ObjectProperties.tlm_atlas_pointer == atlasName:
|
|
obj.TLM_ObjectProperties.tlm_mesh_lightmap_unwrap_mode = "SmartProject"
|
|
|
|
list.remove(index)
|
|
|
|
if index > 0:
|
|
index = index - 1
|
|
|
|
scene.TLM_PostAtlasListItem = index
|
|
return{'FINISHED'}
|
|
|
|
class TLM_AtlasListMoveItem(bpy.types.Operator):
|
|
# Move an item in the list
|
|
bl_idname = "tlm_atlaslist.move_item"
|
|
bl_label = "Move an item in the list"
|
|
bl_description = "Move an item in the list"
|
|
direction: bpy.props.EnumProperty(
|
|
items=(
|
|
('UP', 'Up', ""),
|
|
('DOWN', 'Down', ""),))
|
|
|
|
def move_index(self):
|
|
# Move index of an item render queue while clamping it
|
|
scene = context.scene
|
|
index = scene.TLM_AtlasListItem
|
|
list_length = len(scene.TLM_AtlasList) - 1
|
|
new_index = 0
|
|
|
|
if self.direction == 'UP':
|
|
new_index = index - 1
|
|
elif self.direction == 'DOWN':
|
|
new_index = index + 1
|
|
|
|
new_index = max(0, min(new_index, list_length))
|
|
scene.TLM_AtlasList.move(index, new_index)
|
|
scene.TLM_AtlasListItem = new_index
|
|
|
|
def execute(self, context):
|
|
scene = context.scene
|
|
list = scene.TLM_AtlasList
|
|
index = scene.TLM_AtlasListItem
|
|
|
|
if self.direction == 'DOWN':
|
|
neighbor = index + 1
|
|
self.move_index()
|
|
|
|
elif self.direction == 'UP':
|
|
neighbor = index - 1
|
|
self.move_index()
|
|
else:
|
|
return{'CANCELLED'}
|
|
return{'FINISHED'}
|
|
|
|
class TLM_PostAtlasListMoveItem(bpy.types.Operator):
|
|
# Move an item in the list
|
|
bl_idname = "tlm_postatlaslist.move_item"
|
|
bl_label = "Move an item in the list"
|
|
bl_description = "Move an item in the list"
|
|
direction: bpy.props.EnumProperty(
|
|
items=(
|
|
('UP', 'Up', ""),
|
|
('DOWN', 'Down', ""),))
|
|
|
|
def move_index(self):
|
|
# Move index of an item render queue while clamping it
|
|
scene = context.scene
|
|
index = scene.TLM_PostAtlasListItem
|
|
list_length = len(scene.TLM_PostAtlasList) - 1
|
|
new_index = 0
|
|
|
|
if self.direction == 'UP':
|
|
new_index = index - 1
|
|
elif self.direction == 'DOWN':
|
|
new_index = index + 1
|
|
|
|
new_index = max(0, min(new_index, list_length))
|
|
scene.TLM_PostAtlasList.move(index, new_index)
|
|
scene.TLM_PostAtlasListItem = new_index
|
|
|
|
def execute(self, context):
|
|
scene = context.scene
|
|
list = scene.TLM_PostAtlasList
|
|
index = scene.TLM_PostAtlasListItem
|
|
|
|
if self.direction == 'DOWN':
|
|
neighbor = index + 1
|
|
self.move_index()
|
|
|
|
elif self.direction == 'UP':
|
|
neighbor = index - 1
|
|
self.move_index()
|
|
else:
|
|
return{'CANCELLED'}
|
|
return{'FINISHED'}
|
|
|
|
class TLM_StartServer(bpy.types.Operator):
|
|
bl_idname = "tlm.start_server"
|
|
bl_label = "Start Network Server"
|
|
bl_description = "Start Network Server"
|
|
bl_options = {'REGISTER', 'UNDO'}
|
|
|
|
def modal(self, context, event):
|
|
|
|
#Add progress bar from 0.15
|
|
|
|
print("MODAL")
|
|
|
|
return {'PASS_THROUGH'}
|
|
|
|
def invoke(self, context, event):
|
|
|
|
server.startServer()
|
|
|
|
return {'RUNNING_MODAL'}
|
|
|
|
class TLM_BuildEnvironmentProbes(bpy.types.Operator):
|
|
bl_idname = "tlm.build_environmentprobe"
|
|
bl_label = "Build Environment Probes"
|
|
bl_description = "Build all environment probes from reflection cubemaps"
|
|
bl_options = {'REGISTER', 'UNDO'}
|
|
|
|
def invoke(self, context, event):
|
|
|
|
for obj in bpy.context.scene.objects:
|
|
|
|
if obj.type == "LIGHT_PROBE":
|
|
if obj.data.type == "CUBEMAP":
|
|
|
|
cam_name = "EnvPCam_" + obj.name
|
|
camera = bpy.data.cameras.new(cam_name)
|
|
camobj_name = "EnvPCamera_" + obj.name
|
|
cam_obj = bpy.data.objects.new(camobj_name, camera)
|
|
bpy.context.collection.objects.link(cam_obj)
|
|
cam_obj.location = obj.location
|
|
camera.angle = math.radians(90)
|
|
|
|
prevResx = bpy.context.scene.render.resolution_x
|
|
prevResy = bpy.context.scene.render.resolution_y
|
|
prevCam = bpy.context.scene.camera
|
|
prevEngine = bpy.context.scene.render.engine
|
|
bpy.context.scene.camera = cam_obj
|
|
|
|
bpy.context.scene.render.engine = bpy.context.scene.TLM_SceneProperties.tlm_environment_probe_engine
|
|
bpy.context.scene.render.resolution_x = int(bpy.context.scene.TLM_SceneProperties.tlm_environment_probe_resolution)
|
|
bpy.context.scene.render.resolution_y = int(bpy.context.scene.TLM_SceneProperties.tlm_environment_probe_resolution)
|
|
|
|
savedir = os.path.dirname(bpy.data.filepath)
|
|
directory = os.path.join(savedir, "Probes")
|
|
|
|
t = 90
|
|
|
|
inverted = bpy.context.scene.TLM_SceneProperties.tlm_invert_direction
|
|
|
|
if inverted:
|
|
|
|
positions = {
|
|
"xp" : (math.radians(t), 0, math.radians(0)),
|
|
"zp" : (math.radians(t), 0, math.radians(t)),
|
|
"xm" : (math.radians(t), 0, math.radians(t*2)),
|
|
"zm" : (math.radians(t), 0, math.radians(-t)),
|
|
"yp" : (math.radians(t*2), 0, math.radians(t)),
|
|
"ym" : (0, 0, math.radians(t))
|
|
}
|
|
|
|
else:
|
|
|
|
positions = {
|
|
"xp" : (math.radians(t), 0, math.radians(t*2)),
|
|
"zp" : (math.radians(t), 0, math.radians(-t)),
|
|
"xm" : (math.radians(t), 0, math.radians(0)),
|
|
"zm" : (math.radians(t), 0, math.radians(t)),
|
|
"yp" : (math.radians(t*2), 0, math.radians(-t)),
|
|
"ym" : (0, 0, math.radians(-t))
|
|
}
|
|
|
|
|
|
|
|
cam = cam_obj
|
|
image_settings = bpy.context.scene.render.image_settings
|
|
image_settings.file_format = "HDR"
|
|
image_settings.color_depth = '32'
|
|
|
|
for val in positions:
|
|
cam.rotation_euler = positions[val]
|
|
|
|
filename = os.path.join(directory, val) + "_" + camobj_name + ".hdr"
|
|
bpy.context.scene.render.filepath = filename
|
|
print("Writing out: " + val)
|
|
bpy.ops.render.render(write_still=True)
|
|
|
|
cmft_path = bpy.path.abspath(os.path.join(os.path.dirname(bpy.data.filepath), bpy.context.scene.TLM_SceneProperties.tlm_cmft_path))
|
|
|
|
output_file_irr = camobj_name + ".hdr"
|
|
|
|
posx = directory + "/" + "xp_" + camobj_name + ".hdr"
|
|
negx = directory + "/" + "xm_" + camobj_name + ".hdr"
|
|
posy = directory + "/" + "yp_" + camobj_name + ".hdr"
|
|
negy = directory + "/" + "ym_" + camobj_name + ".hdr"
|
|
posz = directory + "/" + "zp_" + camobj_name + ".hdr"
|
|
negz = directory + "/" + "zm_" + camobj_name + ".hdr"
|
|
output = directory + "/" + camobj_name
|
|
|
|
if platform.system() == 'Windows':
|
|
envpipe = [cmft_path,
|
|
'--inputFacePosX', posx,
|
|
'--inputFaceNegX', negx,
|
|
'--inputFacePosY', posy,
|
|
'--inputFaceNegY', negy,
|
|
'--inputFacePosZ', posz,
|
|
'--inputFaceNegZ', negz,
|
|
'--output0', output,
|
|
'--output0params',
|
|
'hdr,rgbe,latlong']
|
|
|
|
else:
|
|
envpipe = [cmft_path + '--inputFacePosX' + posx
|
|
+ '--inputFaceNegX' + negx
|
|
+ '--inputFacePosY' + posy
|
|
+ '--inputFaceNegY' + negy
|
|
+ '--inputFacePosZ' + posz
|
|
+ '--inputFaceNegZ' + negz
|
|
+ '--output0' + output
|
|
+ '--output0params' + 'hdr,rgbe,latlong']
|
|
|
|
if bpy.context.scene.TLM_SceneProperties.tlm_verbose:
|
|
print("Calling CMFT with:" + str(envpipe))
|
|
|
|
if bpy.context.scene.TLM_SceneProperties.tlm_create_spherical:
|
|
subprocess.call(envpipe, shell=True)
|
|
|
|
input2 = output + ".hdr"
|
|
output2 = directory + "/" + camobj_name
|
|
|
|
if platform.system() == 'Windows':
|
|
envpipe2 = [cmft_path,
|
|
'--input', input2,
|
|
'--filter', 'shcoeffs',
|
|
'--outputNum', '1',
|
|
'--output0', output2]
|
|
|
|
else:
|
|
envpipe2 = [cmft_path +
|
|
'--input' + input2
|
|
+ '-filter' + 'shcoeffs'
|
|
+ '--outputNum' + '1'
|
|
+ '--output0' + output2]
|
|
|
|
if bpy.context.scene.TLM_SceneProperties.tlm_write_sh:
|
|
subprocess.call(envpipe2, shell=True)
|
|
|
|
if bpy.context.scene.TLM_SceneProperties.tlm_write_radiance:
|
|
|
|
use_opencl = 'false'
|
|
cpu_count = 2
|
|
|
|
# 4096 = 256 face
|
|
# 2048 = 128 face
|
|
# 1024 = 64 face
|
|
target_w = int(512)
|
|
face_size = target_w / 8
|
|
if target_w == 2048:
|
|
mip_count = 9
|
|
elif target_w == 1024:
|
|
mip_count = 8
|
|
else:
|
|
mip_count = 7
|
|
|
|
output_file_rad = directory + "/" + camobj_name + "_rad.hdr"
|
|
|
|
if platform.system() == 'Windows':
|
|
|
|
envpipe3 = [
|
|
cmft_path,
|
|
'--input', input2,
|
|
'--filter', 'radiance',
|
|
'--dstFaceSize', str(face_size),
|
|
'--srcFaceSize', str(face_size),
|
|
'--excludeBase', 'false',
|
|
# '--mipCount', str(mip_count),
|
|
'--glossScale', '8',
|
|
'--glossBias', '3',
|
|
'--lightingModel', 'blinnbrdf',
|
|
'--edgeFixup', 'none',
|
|
'--numCpuProcessingThreads', str(cpu_count),
|
|
'--useOpenCL', use_opencl,
|
|
'--clVendor', 'anyGpuVendor',
|
|
'--deviceType', 'gpu',
|
|
'--deviceIndex', '0',
|
|
'--generateMipChain', 'true',
|
|
'--inputGammaNumerator', '1.0',
|
|
'--inputGammaDenominator', '1.0',
|
|
'--outputGammaNumerator', '1.0',
|
|
'--outputGammaDenominator', '1.0',
|
|
'--outputNum', '1',
|
|
'--output0', output_file_rad,
|
|
'--output0params', 'hdr,rgbe,latlong'
|
|
]
|
|
|
|
subprocess.call(envpipe3)
|
|
|
|
else:
|
|
|
|
envpipe3 = cmft_path + \
|
|
' --input "' + input2 + '"' + \
|
|
' --filter radiance' + \
|
|
' --dstFaceSize ' + str(face_size) + \
|
|
' --srcFaceSize ' + str(face_size) + \
|
|
' --excludeBase false' + \
|
|
' --glossScale 8' + \
|
|
' --glossBias 3' + \
|
|
' --lightingModel blinnbrdf' + \
|
|
' --edgeFixup none' + \
|
|
' --numCpuProcessingThreads ' + str(cpu_count) + \
|
|
' --useOpenCL ' + use_opencl + \
|
|
' --clVendor anyGpuVendor' + \
|
|
' --deviceType gpu' + \
|
|
' --deviceIndex 0' + \
|
|
' --generateMipChain true' + \
|
|
' --inputGammaNumerator ' + '1.0' + \
|
|
' --inputGammaDenominator 1.0' + \
|
|
' --outputGammaNumerator 1.0' + \
|
|
' --outputGammaDenominator 1.0' + \
|
|
' --outputNum 1' + \
|
|
' --output0 "' + output_file_rad + '"' + \
|
|
' --output0params hdr,rgbe,latlong'
|
|
|
|
subprocess.call([envpipe3], shell=True)
|
|
|
|
for obj in bpy.context.scene.objects:
|
|
obj.select_set(False)
|
|
|
|
cam_obj.select_set(True)
|
|
bpy.ops.object.delete()
|
|
bpy.context.scene.render.resolution_x = prevResx
|
|
bpy.context.scene.render.resolution_y = prevResy
|
|
bpy.context.scene.camera = prevCam
|
|
bpy.context.scene.render.engine = prevEngine
|
|
|
|
print("Finished building environment probes")
|
|
|
|
|
|
return {'RUNNING_MODAL'}
|
|
|
|
class TLM_CleanBuildEnvironmentProbes(bpy.types.Operator):
|
|
bl_idname = "tlm.clean_environmentprobe"
|
|
bl_label = "Clean Environment Probes"
|
|
bl_description = "Clean Environment Probes"
|
|
bl_options = {'REGISTER', 'UNDO'}
|
|
|
|
def execute(self, context):
|
|
|
|
scene = context.scene
|
|
|
|
savedir = os.path.dirname(bpy.data.filepath)
|
|
dirpath = os.path.join(savedir, "Probes")
|
|
|
|
if os.path.isdir(dirpath):
|
|
for file in os.listdir(dirpath):
|
|
os.remove(os.path.join(dirpath + "/" + file))
|
|
|
|
return {'FINISHED'}
|
|
|
|
class TLM_MergeAdjacentActors(bpy.types.Operator):
|
|
bl_idname = "tlm.merge_adjacent_actors"
|
|
bl_label = "Merge adjacent actors"
|
|
bl_description = "Merges the adjacent faces/vertices of selected objects"
|
|
bl_options = {'REGISTER', 'UNDO'}
|
|
|
|
def execute(self, context):
|
|
|
|
scene = context.scene
|
|
|
|
return {'FINISHED'}
|
|
|
|
class TLM_PrepareUVMaps(bpy.types.Operator):
|
|
bl_idname = "tlm.prepare_uvmaps"
|
|
bl_label = "Prepare UV maps"
|
|
bl_description = "Prepare UV lightmaps for selected objects"
|
|
bl_options = {'REGISTER', 'UNDO'}
|
|
|
|
def execute(self, context):
|
|
|
|
scene = context.scene
|
|
|
|
return {'FINISHED'}
|
|
|
|
class TLM_LoadLightmaps(bpy.types.Operator):
|
|
bl_idname = "tlm.load_lightmaps"
|
|
bl_label = "Load Lightmaps"
|
|
bl_description = "Load lightmaps from selected folder"
|
|
bl_options = {'REGISTER', 'UNDO'}
|
|
|
|
def execute(self, context):
|
|
|
|
scene = context.scene
|
|
|
|
utility.transfer_load()
|
|
|
|
print("Transfer finished")
|
|
|
|
build.finish_assemble(self, 1, 1)
|
|
|
|
return {'FINISHED'}
|
|
|
|
class TLM_ToggleTexelDensity(bpy.types.Operator):
|
|
bl_idname = "tlm.toggle_texel_density"
|
|
bl_label = "Toggle Texel Density"
|
|
bl_description = "Toggle visualize lightmap texel density for selected objects"
|
|
bl_options = {'REGISTER', 'UNDO'}
|
|
|
|
def execute(self, context):
|
|
|
|
scene = context.scene
|
|
|
|
for obj in bpy.context.selected_objects:
|
|
if obj.type == "MESH":
|
|
uv_layers = obj.data.uv_layers
|
|
|
|
#if the object has a td_vis in the uv maps, toggle off
|
|
#else toggle on
|
|
|
|
if obj.TLM_ObjectProperties.tlm_use_default_channel:
|
|
|
|
for i in range(0, len(uv_layers)):
|
|
if uv_layers[i].name == 'UVMap_Lightmap':
|
|
uv_layers.active_index = i
|
|
break
|
|
else:
|
|
|
|
for i in range(0, len(uv_layers)):
|
|
if uv_layers[i].name == obj.TLM_ObjectProperties.tlm_uv_channel:
|
|
uv_layers.active_index = i
|
|
break
|
|
|
|
#filepath = r"C:\path\to\image.png"
|
|
|
|
#img = bpy.data.images.load(filepath)
|
|
|
|
for area in bpy.context.screen.areas:
|
|
if area.type == 'VIEW_3D':
|
|
space_data = area.spaces.active
|
|
bpy.ops.screen.area_dupli('INVOKE_DEFAULT')
|
|
new_window = context.window_manager.windows[-1]
|
|
|
|
area = new_window.screen.areas[-1]
|
|
area.type = 'VIEW_3D'
|
|
#bg = space_data.background_images.new()
|
|
print(bpy.context.object)
|
|
bpy.ops.object.bake_td_uv_to_vc()
|
|
|
|
#bg.image = img
|
|
break
|
|
|
|
|
|
#set active uv_layer to
|
|
|
|
|
|
print("TLM_Viz_Toggle")
|
|
|
|
return {'FINISHED'}
|
|
|
|
class TLM_DisableSpecularity(bpy.types.Operator):
|
|
bl_idname = "tlm.disable_specularity"
|
|
bl_label = "Disable specularity"
|
|
bl_description = "Disables specularity from set"
|
|
bl_options = {'REGISTER', 'UNDO'}
|
|
|
|
def execute(self, context):
|
|
|
|
if bpy.context.scene.TLM_SceneProperties.tlm_utility_set == "Scene":
|
|
for obj in bpy.context.scene.objects:
|
|
if obj.type == "MESH":
|
|
|
|
for slot in obj.material_slots:
|
|
|
|
mat = slot.material
|
|
|
|
if mat.node_tree:
|
|
|
|
for node in mat.node_tree.nodes:
|
|
|
|
if node.type == "BSDF_PRINCIPLED":
|
|
|
|
for inp in node.inputs:
|
|
|
|
if inp.name == "Specular":
|
|
|
|
inp.default_value = 0.0
|
|
|
|
if inp.links and bpy.context.scene.TLM_SceneProperties.tlm_remove_met_spec_link:
|
|
|
|
mat.node_tree.links.remove(inp.links[0])
|
|
|
|
elif bpy.context.scene.TLM_SceneProperties.tlm_utility_set == "Selection":
|
|
for obj in bpy.context.selected_objects:
|
|
if obj.type == "MESH":
|
|
|
|
for slot in obj.material_slots:
|
|
|
|
mat = slot.material
|
|
|
|
if mat.node_tree:
|
|
|
|
for node in mat.node_tree.nodes:
|
|
|
|
if node.type == "BSDF_PRINCIPLED":
|
|
|
|
for inp in node.inputs:
|
|
|
|
if inp.name == "Specular":
|
|
|
|
inp.default_value = 0.0
|
|
|
|
if inp.links and bpy.context.scene.TLM_SceneProperties.tlm_remove_met_spec_link:
|
|
|
|
mat.node_tree.links.remove(inp.links[0])
|
|
|
|
else: #Enabled
|
|
for obj in bpy.context.scene.objects:
|
|
if obj.type == "MESH":
|
|
if obj.TLM_ObjectProperties.tlm_mesh_lightmap_use:
|
|
|
|
for slot in obj.material_slots:
|
|
|
|
mat = slot.material
|
|
|
|
if mat.node_tree:
|
|
|
|
for node in mat.node_tree.nodes:
|
|
|
|
if node.type == "BSDF_PRINCIPLED":
|
|
|
|
for inp in node.inputs:
|
|
|
|
if inp.name == "Specular":
|
|
|
|
inp.default_value = 0.0
|
|
|
|
if inp.links and bpy.context.scene.TLM_SceneProperties.tlm_remove_met_spec_link:
|
|
|
|
mat.node_tree.links.remove(inp.links[0])
|
|
|
|
return{'FINISHED'}
|
|
|
|
class TLM_DisableMetallic(bpy.types.Operator):
|
|
bl_idname = "tlm.disable_metallic"
|
|
bl_label = "Disable metallic"
|
|
bl_description = "Disables metallic from set"
|
|
bl_options = {'REGISTER', 'UNDO'}
|
|
|
|
def execute(self, context):
|
|
|
|
if bpy.context.scene.TLM_SceneProperties.tlm_utility_set == "Scene":
|
|
for obj in bpy.context.scene.objects:
|
|
if obj.type == "MESH":
|
|
|
|
for slot in obj.material_slots:
|
|
|
|
mat = slot.material
|
|
|
|
for node in mat.node_tree.nodes:
|
|
|
|
if node.type == "BSDF_PRINCIPLED":
|
|
|
|
for inp in node.inputs:
|
|
|
|
if inp.name == "Metallic":
|
|
|
|
inp.default_value = 0.0
|
|
|
|
if inp.links and bpy.context.scene.TLM_SceneProperties.tlm_remove_met_spec_link:
|
|
|
|
mat.node_tree.links.remove(inp.links[0])
|
|
|
|
elif bpy.context.scene.TLM_SceneProperties.tlm_utility_set == "Selection":
|
|
for obj in bpy.context.selected_objects:
|
|
if obj.type == "MESH":
|
|
|
|
for slot in obj.material_slots:
|
|
|
|
mat = slot.material
|
|
|
|
for node in mat.node_tree.nodes:
|
|
|
|
if node.type == "BSDF_PRINCIPLED":
|
|
|
|
for inp in node.inputs:
|
|
|
|
if inp.name == "Metallic":
|
|
|
|
inp.default_value = 0.0
|
|
|
|
if inp.links and bpy.context.scene.TLM_SceneProperties.tlm_remove_met_spec_link:
|
|
|
|
mat.node_tree.links.remove(inp.links[0])
|
|
|
|
else: #Enabled
|
|
for obj in bpy.context.scene.objects:
|
|
if obj.type == "MESH":
|
|
if obj.TLM_ObjectProperties.tlm_mesh_lightmap_use:
|
|
|
|
for slot in obj.material_slots:
|
|
|
|
mat = slot.material
|
|
|
|
for node in mat.node_tree.nodes:
|
|
|
|
if node.type == "BSDF_PRINCIPLED":
|
|
|
|
for inp in node.inputs:
|
|
|
|
if inp.name == "Metallic":
|
|
|
|
inp.default_value = 0.0
|
|
|
|
if inp.links and bpy.context.scene.TLM_SceneProperties.tlm_remove_met_spec_link:
|
|
|
|
mat.node_tree.links.remove(inp.links[0])
|
|
|
|
return{'FINISHED'}
|
|
|
|
class TLM_RemoveEmptyImages(bpy.types.Operator):
|
|
|
|
bl_idname = "tlm.remove_empty_images"
|
|
bl_label = "Remove Empty Images"
|
|
bl_description = "Removes empty images from scene materials"
|
|
bl_options = {'REGISTER', 'UNDO'}
|
|
|
|
def execute(self, context):
|
|
|
|
for mat in bpy.data.materials:
|
|
|
|
nodetree = mat.node_tree
|
|
|
|
if nodetree:
|
|
|
|
for node in nodetree.nodes:
|
|
|
|
if node.name == "Baked Image":
|
|
|
|
print(node.name)
|
|
|
|
nodetree.nodes.remove(node)
|
|
|
|
return{'FINISHED'}
|
|
|
|
|
|
class TLM_PostAtlasSpecialsMenu(bpy.types.Menu):
|
|
bl_label = "Lightmap"
|
|
bl_idname = "TLM_MT_PostAtlasListSpecials"
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
layout.operator("tlm.add_collections_post")
|
|
layout.operator("tlm.add_selected_collections_post")
|
|
|
|
class TLM_AddCollectionsPost(bpy.types.Operator):
|
|
bl_idname = "tlm.add_collections_post"
|
|
bl_label = "Add collections"
|
|
bl_description = "Adds all collections to atlases"
|
|
bl_options = {'REGISTER', 'UNDO'}
|
|
|
|
resolution : bpy.props.EnumProperty(
|
|
items = [('32', '32', 'TODO'),
|
|
('64', '64', 'TODO'),
|
|
('128', '128', 'TODO'),
|
|
('256', '256', 'TODO'),
|
|
('512', '512', 'TODO'),
|
|
('1024', '1024', 'TODO'),
|
|
('2048', '2048', 'TODO'),
|
|
('4096', '4096', 'TODO'),
|
|
('8192', '8192', 'TODO')],
|
|
name = "Atlas Lightmap Resolution",
|
|
description="Atlas lightmap resolution",
|
|
default='256')
|
|
|
|
unwrap_modes = [('Lightmap', 'Lightmap', 'Use Blender Lightmap Pack algorithm'),
|
|
('SmartProject', 'Smart Project', 'Use Blender Smart Project algorithm')]
|
|
|
|
if "blender_xatlas" in bpy.context.preferences.addons.keys():
|
|
unwrap_modes.append(('Xatlas', 'Xatlas', 'Use Xatlas addon packing algorithm'))
|
|
|
|
unwrap : bpy.props.EnumProperty(
|
|
items = unwrap_modes,
|
|
name = "Unwrap Mode",
|
|
description="Atlas unwrapping method",
|
|
default='SmartProject')
|
|
|
|
margin : bpy.props.FloatProperty(
|
|
name="Unwrap Margin",
|
|
default=0.1,
|
|
min=0.0,
|
|
max=1.0,
|
|
subtype='FACTOR')
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
return True
|
|
|
|
def execute(self, context):
|
|
|
|
for collection in bpy.context.scene.collection.children:
|
|
|
|
#Add a new atlas with collection name
|
|
#Traverse before adding
|
|
scene = bpy.context.scene
|
|
scene.TLM_PostAtlasList.add()
|
|
scene.TLM_PostAtlasListItem = len(scene.TLM_PostAtlasList) - 1
|
|
|
|
scene.TLM_PostAtlasList[len(scene.TLM_PostAtlasList) - 1].name = collection.name
|
|
scene.TLM_PostAtlasList[collection.name].tlm_atlas_lightmap_unwrap_mode = self.unwrap
|
|
scene.TLM_PostAtlasList[collection.name].tlm_atlas_lightmap_resolution = self.resolution
|
|
scene.TLM_PostAtlasList[collection.name].tlm_atlas_unwrap_margin = self.margin
|
|
|
|
for obj in collection.objects:
|
|
if obj.type == "MESH":
|
|
obj.TLM_ObjectProperties.tlm_mesh_lightmap_use = True
|
|
obj.TLM_ObjectProperties.tlm_postpack_object = True
|
|
obj.TLM_ObjectProperties.tlm_postatlas_pointer = collection.name
|
|
|
|
return{'FINISHED'}
|
|
|
|
def invoke(self, context, event):
|
|
return context.window_manager.invoke_props_dialog(self)
|
|
|
|
def draw(self, context):
|
|
row = self.layout
|
|
row.prop(self, "unwrap", text="Unwrap mode")
|
|
row.prop(self, "resolution", text="Resolution")
|
|
row.prop(self, "margin", text="Margin")
|
|
|
|
class TLM_AddSelectedCollectionsPost(bpy.types.Operator):
|
|
bl_idname = "tlm.add_selected_collections_post"
|
|
bl_label = "Add selected collections"
|
|
bl_description = "Add the collections of the selected objects"
|
|
bl_options = {'REGISTER', 'UNDO'}
|
|
|
|
resolution : bpy.props.EnumProperty(
|
|
items = [('32', '32', 'TODO'),
|
|
('64', '64', 'TODO'),
|
|
('128', '128', 'TODO'),
|
|
('256', '256', 'TODO'),
|
|
('512', '512', 'TODO'),
|
|
('1024', '1024', 'TODO'),
|
|
('2048', '2048', 'TODO'),
|
|
('4096', '4096', 'TODO'),
|
|
('8192', '8192', 'TODO')],
|
|
name = "Atlas Lightmap Resolution",
|
|
description="Atlas lightmap resolution",
|
|
default='256')
|
|
|
|
unwrap_modes = [('Lightmap', 'Lightmap', 'Use Blender Lightmap Pack algorithm'),
|
|
('SmartProject', 'Smart Project', 'Use Blender Smart Project algorithm')]
|
|
|
|
if "blender_xatlas" in bpy.context.preferences.addons.keys():
|
|
unwrap_modes.append(('Xatlas', 'Xatlas', 'Use Xatlas addon packing algorithm'))
|
|
|
|
unwrap : bpy.props.EnumProperty(
|
|
items = unwrap_modes,
|
|
name = "Unwrap Mode",
|
|
description="Atlas unwrapping method",
|
|
default='SmartProject')
|
|
|
|
margin : bpy.props.FloatProperty(
|
|
name="Unwrap Margin",
|
|
default=0.1,
|
|
min=0.0,
|
|
max=1.0,
|
|
subtype='FACTOR')
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
return True
|
|
|
|
def execute(self, context):
|
|
|
|
collections = []
|
|
|
|
for obj in bpy.context.selected_objects:
|
|
|
|
obj_collection = obj.users_collection[0]
|
|
|
|
if obj_collection.name not in collections:
|
|
|
|
collections.append(obj_collection.name)
|
|
|
|
print("Collections:" + str(collections))
|
|
|
|
for collection in bpy.context.scene.collection.children:
|
|
|
|
if collection.name in collections:
|
|
|
|
#Add a new atlas with collection name
|
|
#Traverse before adding
|
|
scene = bpy.context.scene
|
|
scene.TLM_PostAtlasList.add()
|
|
scene.TLM_PostAtlasListItem = len(scene.TLM_PostAtlasList) - 1
|
|
|
|
scene.TLM_PostAtlasList[len(scene.TLM_PostAtlasList) - 1].name = collection.name
|
|
scene.TLM_PostAtlasList[collection.name].tlm_atlas_lightmap_unwrap_mode = self.unwrap
|
|
scene.TLM_PostAtlasList[collection.name].tlm_atlas_lightmap_resolution = self.resolution
|
|
scene.TLM_PostAtlasList[collection.name].tlm_atlas_unwrap_margin = self.margin
|
|
|
|
for obj in collection.objects:
|
|
if obj.type == "MESH":
|
|
obj.TLM_ObjectProperties.tlm_mesh_lightmap_use = True
|
|
obj.TLM_ObjectProperties.tlm_postpack_object = True
|
|
obj.TLM_ObjectProperties.tlm_postatlas_pointer = collection.name
|
|
|
|
return{'FINISHED'}
|
|
|
|
def invoke(self, context, event):
|
|
return context.window_manager.invoke_props_dialog(self)
|
|
|
|
def draw(self, context):
|
|
row = self.layout
|
|
row.prop(self, "unwrap", text="Unwrap mode")
|
|
row.prop(self, "resolution", text="Resolution")
|
|
row.prop(self, "margin", text="Margin")
|
|
|
|
class TLM_AtlasSpecialsMenu(bpy.types.Menu):
|
|
bl_label = "Lightmap"
|
|
bl_idname = "TLM_MT_AtlasListSpecials"
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
layout.operator("tlm.add_collections")
|
|
layout.operator("tlm.add_selected_collections")
|
|
|
|
class TLM_AddCollections(bpy.types.Operator):
|
|
bl_idname = "tlm.add_collections"
|
|
bl_label = "Add all collections"
|
|
bl_description = "Adds all collections to atlases"
|
|
bl_options = {'REGISTER', 'UNDO'}
|
|
|
|
resolution : bpy.props.EnumProperty(
|
|
items = [('32', '32', 'TODO'),
|
|
('64', '64', 'TODO'),
|
|
('128', '128', 'TODO'),
|
|
('256', '256', 'TODO'),
|
|
('512', '512', 'TODO'),
|
|
('1024', '1024', 'TODO'),
|
|
('2048', '2048', 'TODO'),
|
|
('4096', '4096', 'TODO'),
|
|
('8192', '8192', 'TODO')],
|
|
name = "Atlas Lightmap Resolution",
|
|
description="Atlas lightmap resolution",
|
|
default='256')
|
|
|
|
unwrap_modes = [('Lightmap', 'Lightmap', 'Use Blender Lightmap Pack algorithm'),
|
|
('SmartProject', 'Smart Project', 'Use Blender Smart Project algorithm'),
|
|
('Copy', 'Copy existing', 'Use the existing UV channel')]
|
|
|
|
if "blender_xatlas" in bpy.context.preferences.addons.keys():
|
|
unwrap_modes.append(('Xatlas', 'Xatlas', 'Use Xatlas addon packing algorithm'))
|
|
|
|
unwrap : bpy.props.EnumProperty(
|
|
items = unwrap_modes,
|
|
name = "Unwrap Mode",
|
|
description="Atlas unwrapping method",
|
|
default='SmartProject')
|
|
|
|
margin : bpy.props.FloatProperty(
|
|
name="Unwrap Margin",
|
|
default=0.1,
|
|
min=0.0,
|
|
max=1.0,
|
|
subtype='FACTOR')
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
return True
|
|
|
|
def execute(self, context):
|
|
|
|
for collection in bpy.context.scene.collection.children:
|
|
|
|
#Add a new atlas with collection name
|
|
#Traverse before adding
|
|
scene = bpy.context.scene
|
|
scene.TLM_AtlasList.add()
|
|
scene.TLM_AtlasListItem = len(scene.TLM_AtlasList) - 1
|
|
|
|
scene.TLM_AtlasList[len(scene.TLM_AtlasList) - 1].name = collection.name
|
|
scene.TLM_AtlasList[collection.name].tlm_atlas_lightmap_unwrap_mode = self.unwrap
|
|
scene.TLM_AtlasList[collection.name].tlm_atlas_lightmap_resolution = self.resolution
|
|
scene.TLM_AtlasList[collection.name].tlm_atlas_unwrap_margin = self.margin
|
|
|
|
for obj in collection.objects:
|
|
if obj.type == "MESH":
|
|
obj.TLM_ObjectProperties.tlm_mesh_lightmap_use = True
|
|
obj.TLM_ObjectProperties.tlm_mesh_lightmap_unwrap_mode = "AtlasGroupA"
|
|
obj.TLM_ObjectProperties.tlm_atlas_pointer = collection.name
|
|
|
|
return{'FINISHED'}
|
|
|
|
def invoke(self, context, event):
|
|
return context.window_manager.invoke_props_dialog(self)
|
|
|
|
def draw(self, context):
|
|
row = self.layout
|
|
row.prop(self, "unwrap", text="Unwrap mode")
|
|
row.prop(self, "resolution", text="Resolution")
|
|
row.prop(self, "margin", text="Margin")
|
|
|
|
class TLM_AddSelectedCollections(bpy.types.Operator):
|
|
bl_idname = "tlm.add_selected_collections"
|
|
bl_label = "Add the collections of the selected objects"
|
|
bl_description = "Add the collections of the selected objects"
|
|
bl_options = {'REGISTER', 'UNDO'}
|
|
|
|
resolution : bpy.props.EnumProperty(
|
|
items = [('32', '32', 'TODO'),
|
|
('64', '64', 'TODO'),
|
|
('128', '128', 'TODO'),
|
|
('256', '256', 'TODO'),
|
|
('512', '512', 'TODO'),
|
|
('1024', '1024', 'TODO'),
|
|
('2048', '2048', 'TODO'),
|
|
('4096', '4096', 'TODO'),
|
|
('8192', '8192', 'TODO')],
|
|
name = "Atlas Lightmap Resolution",
|
|
description="Atlas lightmap resolution",
|
|
default='256')
|
|
|
|
unwrap_modes = [('Lightmap', 'Lightmap', 'Use Blender Lightmap Pack algorithm'),
|
|
('SmartProject', 'Smart Project', 'Use Blender Smart Project algorithm'),
|
|
('Copy', 'Copy existing', 'Use the existing UV channel')]
|
|
|
|
if "blender_xatlas" in bpy.context.preferences.addons.keys():
|
|
unwrap_modes.append(('Xatlas', 'Xatlas', 'Use Xatlas addon packing algorithm'))
|
|
|
|
unwrap : bpy.props.EnumProperty(
|
|
items = unwrap_modes,
|
|
name = "Unwrap Mode",
|
|
description="Atlas unwrapping method",
|
|
default='SmartProject')
|
|
|
|
margin : bpy.props.FloatProperty(
|
|
name="Unwrap Margin",
|
|
default=0.1,
|
|
min=0.0,
|
|
max=1.0,
|
|
subtype='FACTOR')
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
return True
|
|
|
|
def execute(self, context):
|
|
|
|
collections = []
|
|
|
|
for obj in bpy.context.selected_objects:
|
|
|
|
obj_collection = obj.users_collection[0]
|
|
|
|
if obj_collection.name not in collections:
|
|
|
|
collections.append(obj_collection.name)
|
|
|
|
print("Collections:" + str(collections))
|
|
|
|
for collection in bpy.context.scene.collection.children:
|
|
|
|
if collection.name in collections:
|
|
|
|
#Add a new atlas with collection name
|
|
#Traverse before adding
|
|
scene = bpy.context.scene
|
|
scene.TLM_AtlasList.add()
|
|
scene.TLM_AtlasListItem = len(scene.TLM_AtlasList) - 1
|
|
|
|
scene.TLM_AtlasList[len(scene.TLM_AtlasList) - 1].name = collection.name
|
|
scene.TLM_AtlasList[collection.name].tlm_atlas_lightmap_unwrap_mode = self.unwrap
|
|
scene.TLM_AtlasList[collection.name].tlm_atlas_lightmap_resolution = self.resolution
|
|
scene.TLM_AtlasList[collection.name].tlm_atlas_unwrap_margin = self.margin
|
|
|
|
for obj in collection.objects:
|
|
if obj.type == "MESH":
|
|
obj.TLM_ObjectProperties.tlm_mesh_lightmap_use = True
|
|
obj.TLM_ObjectProperties.tlm_mesh_lightmap_unwrap_mode = "AtlasGroupA"
|
|
obj.TLM_ObjectProperties.tlm_atlas_pointer = collection.name
|
|
|
|
return{'FINISHED'}
|
|
|
|
def invoke(self, context, event):
|
|
return context.window_manager.invoke_props_dialog(self)
|
|
|
|
def draw(self, context):
|
|
row = self.layout
|
|
row.prop(self, "unwrap", text="Unwrap mode")
|
|
row.prop(self, "resolution", text="Resolution")
|
|
row.prop(self, "margin", text="Margin")
|
|
|
|
#Atlas disable objects
|
|
|
|
class TLM_Reset(bpy.types.Operator):
|
|
bl_idname = "tlm.reset"
|
|
bl_label = "Resets all UI and settings"
|
|
bl_description = "Reset UI and objects"
|
|
bl_options = {'REGISTER', 'UNDO'}
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
return True
|
|
|
|
def execute(self, context):
|
|
self.report({'INFO'}, "YES!")
|
|
return {'FINISHED'}
|
|
|
|
def invoke(self, context, event):
|
|
return context.window_manager.invoke_confirm(self, event)
|
|
|
|
# class TLM_Reset2(bpy.types.Operator):
|
|
# bl_idname = "tlm.reset2"
|
|
# bl_label = "Do you really want to do that?"
|
|
# bl_options = {'REGISTER', 'INTERNAL'}
|
|
|
|
# prop1: bpy.props.BoolProperty()
|
|
# prop2: bpy.props.BoolProperty()
|
|
|
|
# @classmethod
|
|
# def poll(cls, context):
|
|
# return True
|
|
|
|
# def execute(self, context):
|
|
# self.report({'INFO'}, "YES!")
|
|
# return {'FINISHED'}
|
|
|
|
# def invoke(self, context, event):
|
|
# return context.window_manager.invoke_props_dialog(self)
|
|
|
|
# def draw(self, context):
|
|
# row = self.layout
|
|
# row.prop(self, "prop1", text="Property A")
|
|
# row.prop(self, "prop2", text="Property B")
|
|
|
|
def TLM_DoubleResolution():
|
|
pass
|
|
|
|
def TLM_HalfResolution():
|
|
pass
|
|
|
|
def TLM_DivideLMGroups():
|
|
pass
|
|
|
|
class TLM_CalcTexDex(bpy.types.Operator):
|
|
bl_idname = "tlm.calctexdex"
|
|
bl_label = "Calculate Texel Density"
|
|
bl_description = "Calculates Texel Density of selected object"
|
|
bl_options = {'REGISTER', 'UNDO'}
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
return True
|
|
|
|
def execute(self, context):
|
|
return {'FINISHED'}
|
|
|
|
class TLM_AddGLTFNode(bpy.types.Operator):
|
|
bl_idname = "tlm.add_gltf_node"
|
|
bl_label = "Add GLTF Node"
|
|
bl_description = "Add to GLTF node to active material and connect lightmap if present"
|
|
bl_options = {'REGISTER', 'UNDO'}
|
|
|
|
def execute(self, context):
|
|
|
|
scene = context.scene
|
|
cycles = scene.cycles
|
|
material = bpy.context.active_object.active_material
|
|
|
|
nodes = material.node_tree.nodes
|
|
# create group data
|
|
gltf_settings = bpy.data.node_groups.get('glTF Settings')
|
|
if gltf_settings is None:
|
|
bpy.data.node_groups.new('glTF Settings', 'ShaderNodeTree')
|
|
|
|
# add group to node tree
|
|
gltf_settings_node = nodes.get('glTF Settings')
|
|
if gltf_settings_node is None:
|
|
gltf_settings_node = nodes.new('ShaderNodeGroup')
|
|
gltf_settings_node.name = 'glTF Settings'
|
|
gltf_settings_node.node_tree = bpy.data.node_groups['glTF Settings']
|
|
|
|
# create group inputs
|
|
if gltf_settings_node.inputs.get('Occlusion') is None:
|
|
gltf_settings_node.inputs.new('NodeSocketFloat','Occlusion')
|
|
|
|
#return gltf_settings_node
|
|
|
|
return {'FINISHED'}
|
|
|
|
class TLM_ShiftMultiplyLinks(bpy.types.Operator):
|
|
bl_idname = "tlm.shift_multiply_links"
|
|
bl_label = "Shift multiply links"
|
|
bl_description = "Shift multiply links for active material"
|
|
bl_options = {'REGISTER', 'UNDO'}
|
|
|
|
def execute(self, context):
|
|
|
|
scene = context.scene
|
|
cycles = scene.cycles
|
|
material = bpy.context.active_object.active_material
|
|
|
|
nodes = material.node_tree.nodes
|
|
|
|
LM_Node = nodes.get("TLM_Lightmap")
|
|
Multi_Node = nodes.get("Lightmap_Multiplication")
|
|
Base_Node = nodes.get("Lightmap_BasecolorNode_A")
|
|
|
|
material.node_tree.links.remove(LM_Node.outputs[0].links[0])
|
|
material.node_tree.links.remove(Base_Node.outputs[0].links[0])
|
|
|
|
material.node_tree.links.new(LM_Node.outputs[0], Multi_Node.inputs[2])
|
|
material.node_tree.links.new(Base_Node.outputs[0], Multi_Node.inputs[1])
|
|
|
|
return {'FINISHED'}
|