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 == "ShaderNodeMix"):
 | |
|                             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 or byp.app.version[0] == 4:
 | |
|         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
 |