678 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			678 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
import bpy.ops as O
 | 
						|
import bpy, os, re, sys, importlib, struct, platform, subprocess, threading, string, bmesh, shutil, glob, uuid
 | 
						|
from io import StringIO
 | 
						|
from threading  import Thread
 | 
						|
from queue import Queue, Empty
 | 
						|
from dataclasses import dataclass
 | 
						|
from dataclasses import field
 | 
						|
from typing import List
 | 
						|
 | 
						|
###########################################################
 | 
						|
###########################################################
 | 
						|
# This set of utility functions are courtesy of LorenzWieseke
 | 
						|
#
 | 
						|
# Modified by Naxela
 | 
						|
#   
 | 
						|
# https://github.com/Naxela/The_Lightmapper/tree/Lightmap-to-GLB
 | 
						|
###########################################################
 | 
						|
 | 
						|
class Node_Types:
 | 
						|
    output_node = 'OUTPUT_MATERIAL'
 | 
						|
    ao_node = 'AMBIENT_OCCLUSION'
 | 
						|
    image_texture = 'TEX_IMAGE'
 | 
						|
    pbr_node = 'BSDF_PRINCIPLED'
 | 
						|
    diffuse = 'BSDF_DIFFUSE'
 | 
						|
    mapping = 'MAPPING'
 | 
						|
    normal_map = 'NORMAL_MAP'
 | 
						|
    bump_map = 'BUMP'
 | 
						|
    attr_node = 'ATTRIBUTE'
 | 
						|
 | 
						|
class Shader_Node_Types:
 | 
						|
    emission = "ShaderNodeEmission"
 | 
						|
    image_texture = "ShaderNodeTexImage"
 | 
						|
    mapping = "ShaderNodeMapping"
 | 
						|
    normal = "ShaderNodeNormalMap"
 | 
						|
    ao = "ShaderNodeAmbientOcclusion"
 | 
						|
    uv = "ShaderNodeUVMap"
 | 
						|
    mix = "ShaderNodeMixRGB"
 | 
						|
 | 
						|
def select_object(self,obj):
 | 
						|
    C = bpy.context
 | 
						|
    try:
 | 
						|
        O.object.select_all(action='DESELECT')
 | 
						|
        C.view_layer.objects.active = obj
 | 
						|
        obj.select_set(True)
 | 
						|
    except:
 | 
						|
        self.report({'INFO'},"Object not in View Layer")
 | 
						|
 | 
						|
 | 
						|
def select_obj_by_mat(self,mat):
 | 
						|
    D = bpy.data
 | 
						|
    for obj in D.objects:
 | 
						|
        if obj.type == "MESH":
 | 
						|
            object_materials = [
 | 
						|
                slot.material for slot in obj.material_slots]
 | 
						|
            if mat in object_materials:
 | 
						|
                select_object(self,obj)
 | 
						|
 | 
						|
 | 
						|
def save_image(image):
 | 
						|
 | 
						|
    filePath = bpy.data.filepath
 | 
						|
    path = os.path.dirname(filePath)
 | 
						|
 | 
						|
    try:
 | 
						|
        os.mkdir(path + "/tex")
 | 
						|
    except FileExistsError:
 | 
						|
        pass
 | 
						|
 | 
						|
    try:
 | 
						|
        os.mkdir(path + "/tex/" + str(image.size[0]))
 | 
						|
    except FileExistsError:
 | 
						|
        pass
 | 
						|
 | 
						|
    if image.file_format == "JPEG" :
 | 
						|
        file_ending = ".jpg"
 | 
						|
    elif image.file_format == "PNG" :
 | 
						|
        file_ending = ".png"
 | 
						|
    
 | 
						|
    savepath = path + "/tex/" + \
 | 
						|
        str(image.size[0]) + "/" + image.name + file_ending
 | 
						|
 | 
						|
    image.filepath_raw = savepath
 | 
						|
    
 | 
						|
    image.save()
 | 
						|
 | 
						|
def get_file_size(filepath):
 | 
						|
    size = "Unpack Files"
 | 
						|
    try:
 | 
						|
        path = bpy.path.abspath(filepath)
 | 
						|
        size = os.path.getsize(path)
 | 
						|
        size /= 1024
 | 
						|
    except:
 | 
						|
        if bpy.context.scene.TLM_SceneProperties.tlm_verbose:
 | 
						|
            print("error getting file path for " + filepath)
 | 
						|
        
 | 
						|
    return (size)
 | 
						|
 | 
						|
 | 
						|
def scale_image(image, newSize):
 | 
						|
    if (image.org_filepath != ''):
 | 
						|
        image.filepath = image.org_filepath
 | 
						|
 | 
						|
    image.org_filepath = image.filepath
 | 
						|
    image.scale(newSize[0], newSize[1])
 | 
						|
    save_image(image)
 | 
						|
 | 
						|
 | 
						|
def check_only_one_pbr(self,material):
 | 
						|
    check_ok = True
 | 
						|
    # get pbr shader
 | 
						|
    nodes = material.node_tree.nodes
 | 
						|
    pbr_node_type = Node_Types.pbr_node
 | 
						|
    pbr_nodes = find_node_by_type(nodes,pbr_node_type)
 | 
						|
 | 
						|
    # check only one pbr node
 | 
						|
    if len(pbr_nodes) == 0:
 | 
						|
        self.report({'INFO'}, 'No PBR Shader Found')
 | 
						|
        check_ok = False
 | 
						|
 | 
						|
    if len(pbr_nodes) > 1:
 | 
						|
        self.report({'INFO'}, 'More than one PBR Node found ! Clean before Baking.')
 | 
						|
        check_ok = False
 | 
						|
 | 
						|
    return check_ok
 | 
						|
 | 
						|
# is material already the baked one
 | 
						|
def check_is_org_material(self,material):     
 | 
						|
    check_ok = True   
 | 
						|
    if "_Bake" in material.name:
 | 
						|
        self.report({'INFO'}, 'Change back to org. Material')
 | 
						|
        check_ok = False
 | 
						|
    
 | 
						|
    return check_ok
 | 
						|
 | 
						|
 | 
						|
def clean_empty_materials(self):
 | 
						|
    for obj in bpy.context.scene.objects:
 | 
						|
        for slot in obj.material_slots:
 | 
						|
            mat = slot.material
 | 
						|
            if mat is None:
 | 
						|
                if bpy.context.scene.TLM_SceneProperties.tlm_verbose:
 | 
						|
                    print("Removed Empty Materials from " + obj.name)
 | 
						|
                bpy.ops.object.select_all(action='DESELECT')
 | 
						|
                obj.select_set(True)
 | 
						|
                bpy.ops.object.material_slot_remove()
 | 
						|
 | 
						|
def get_pbr_inputs(pbr_node):
 | 
						|
 | 
						|
    base_color_input = pbr_node.inputs["Base Color"]
 | 
						|
    metallic_input = pbr_node.inputs["Metallic"]
 | 
						|
    specular_input = pbr_node.inputs["Specular"]
 | 
						|
    roughness_input = pbr_node.inputs["Roughness"]
 | 
						|
    normal_input = pbr_node.inputs["Normal"]
 | 
						|
 | 
						|
    pbr_inputs = {"base_color_input":base_color_input, "metallic_input":metallic_input,"specular_input":specular_input,"roughness_input":roughness_input,"normal_input":normal_input}
 | 
						|
    return pbr_inputs    
 | 
						|
 | 
						|
def find_node_by_type(nodes, node_type):
 | 
						|
    nodes_found = [n for n in nodes if n.type == node_type]
 | 
						|
    return nodes_found
 | 
						|
 | 
						|
def find_node_by_type_recusivly(material, note_to_start, node_type, del_nodes_inbetween=False):
 | 
						|
    nodes = material.node_tree.nodes
 | 
						|
    if note_to_start.type == node_type:
 | 
						|
        return note_to_start
 | 
						|
 | 
						|
    for input in note_to_start.inputs:
 | 
						|
        for link in input.links:
 | 
						|
            current_node = link.from_node
 | 
						|
            if (del_nodes_inbetween and note_to_start.type != Node_Types.normal_map and note_to_start.type != Node_Types.bump_map):
 | 
						|
                nodes.remove(note_to_start)
 | 
						|
            return find_node_by_type_recusivly(material, current_node, node_type, del_nodes_inbetween)
 | 
						|
 | 
						|
 | 
						|
def find_node_by_name_recusivly(node, idname):
 | 
						|
    if node.bl_idname == idname:
 | 
						|
        return node
 | 
						|
 | 
						|
    for input in node.inputs:
 | 
						|
        for link in input.links:
 | 
						|
            current_node = link.from_node
 | 
						|
            return find_node_by_name_recusivly(current_node, idname)
 | 
						|
 | 
						|
def make_link(material, socket1, socket2):
 | 
						|
    links = material.node_tree.links
 | 
						|
    links.new(socket1, socket2)
 | 
						|
 | 
						|
 | 
						|
def add_gamma_node(material, pbrInput):
 | 
						|
    nodeToPrincipledOutput = pbrInput.links[0].from_socket
 | 
						|
 | 
						|
    gammaNode = material.node_tree.nodes.new("ShaderNodeGamma")
 | 
						|
    gammaNode.inputs[1].default_value = 2.2
 | 
						|
    gammaNode.name = "Gamma Bake"
 | 
						|
 | 
						|
    # link in gamma
 | 
						|
    make_link(material, nodeToPrincipledOutput, gammaNode.inputs["Color"])
 | 
						|
    make_link(material, gammaNode.outputs["Color"], pbrInput)
 | 
						|
 | 
						|
 | 
						|
def remove_gamma_node(material, pbrInput):
 | 
						|
    nodes = material.node_tree.nodes
 | 
						|
    gammaNode = nodes.get("Gamma Bake")
 | 
						|
    nodeToPrincipledOutput = gammaNode.inputs[0].links[0].from_socket
 | 
						|
 | 
						|
    make_link(material, nodeToPrincipledOutput, pbrInput)
 | 
						|
    material.node_tree.nodes.remove(gammaNode)
 | 
						|
 | 
						|
def apply_ao_toggle(self,context): 
 | 
						|
    all_materials = bpy.data.materials
 | 
						|
    ao_toggle = context.scene.toggle_ao
 | 
						|
    for mat in all_materials:
 | 
						|
        nodes = mat.node_tree.nodes
 | 
						|
        ao_node = nodes.get("AO Bake")
 | 
						|
        if ao_node is not None:
 | 
						|
            if ao_toggle:
 | 
						|
                emission_setup(mat,ao_node.outputs["Color"])
 | 
						|
            else:
 | 
						|
                pbr_node = find_node_by_type(nodes,Node_Types.pbr_node)[0]   
 | 
						|
                remove_node(mat,"Emission Bake")
 | 
						|
                reconnect_PBR(mat, pbr_node)
 | 
						|
        
 | 
						|
 | 
						|
def emission_setup(material, node_output):
 | 
						|
    nodes = material.node_tree.nodes
 | 
						|
    emission_node = add_node(material,Shader_Node_Types.emission,"Emission Bake")
 | 
						|
 | 
						|
    # link emission to whatever goes into current pbrInput
 | 
						|
    emission_input = emission_node.inputs[0]
 | 
						|
    make_link(material, node_output, emission_input)
 | 
						|
 | 
						|
    # link emission to materialOutput
 | 
						|
    surface_input = nodes.get("Material Output").inputs[0]
 | 
						|
    emission_output = emission_node.outputs[0]
 | 
						|
    make_link(material, emission_output, surface_input)
 | 
						|
 | 
						|
def link_pbr_to_output(material,pbr_node):
 | 
						|
    nodes = material.node_tree.nodes
 | 
						|
    surface_input = nodes.get("Material Output").inputs[0]
 | 
						|
    make_link(material,pbr_node.outputs[0],surface_input)
 | 
						|
 | 
						|
    
 | 
						|
def reconnect_PBR(material, pbrNode):
 | 
						|
    nodes = material.node_tree.nodes
 | 
						|
    pbr_output = pbrNode.outputs[0]
 | 
						|
    surface_input = nodes.get("Material Output").inputs[0]
 | 
						|
    make_link(material, pbr_output, surface_input)
 | 
						|
 | 
						|
def mute_all_texture_mappings(material, do_mute):
 | 
						|
    nodes = material.node_tree.nodes
 | 
						|
    for node in nodes:
 | 
						|
        if node.bl_idname == "ShaderNodeMapping":
 | 
						|
            node.mute = do_mute
 | 
						|
 | 
						|
def add_node(material,shader_node_type,node_name):
 | 
						|
    nodes = material.node_tree.nodes
 | 
						|
    new_node = nodes.get(node_name)
 | 
						|
    if new_node is None:
 | 
						|
        new_node = nodes.new(shader_node_type)
 | 
						|
        new_node.name = node_name
 | 
						|
        new_node.label = node_name
 | 
						|
    return new_node
 | 
						|
 | 
						|
def remove_node(material,node_name):
 | 
						|
    nodes = material.node_tree.nodes
 | 
						|
    node = nodes.get(node_name)
 | 
						|
    if node is not None:
 | 
						|
        nodes.remove(node)
 | 
						|
 | 
						|
def lightmap_to_ao(material,lightmap_node):
 | 
						|
        nodes = material.node_tree.nodes
 | 
						|
        # -----------------------AO SETUP--------------------#
 | 
						|
        # 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
 | 
						|
        ao_group = nodes.get('glTF Settings')
 | 
						|
        if ao_group is None:
 | 
						|
            ao_group = nodes.new('ShaderNodeGroup')
 | 
						|
            ao_group.name = 'glTF Settings'
 | 
						|
            ao_group.node_tree = bpy.data.node_groups['glTF Settings']
 | 
						|
 | 
						|
        # create group inputs
 | 
						|
        if ao_group.inputs.get('Occlusion') is None:
 | 
						|
            ao_group.inputs.new('NodeSocketFloat','Occlusion')
 | 
						|
 | 
						|
        # mulitply to control strength
 | 
						|
        mix_node = add_node(material,Shader_Node_Types.mix,"Adjust Lightmap")
 | 
						|
        mix_node.blend_type = "MULTIPLY"
 | 
						|
        mix_node.inputs["Fac"].default_value = 1
 | 
						|
        mix_node.inputs["Color2"].default_value = [3,3,3,1]
 | 
						|
 | 
						|
        # position node
 | 
						|
        ao_group.location = (lightmap_node.location[0]+600,lightmap_node.location[1])
 | 
						|
        mix_node.location = (lightmap_node.location[0]+300,lightmap_node.location[1])
 | 
						|
    
 | 
						|
        make_link(material,lightmap_node.outputs['Color'],mix_node.inputs['Color1'])
 | 
						|
        make_link(material,mix_node.outputs['Color'],ao_group.inputs['Occlusion'])
 | 
						|
 | 
						|
 | 
						|
###########################################################
 | 
						|
###########################################################
 | 
						|
# This utility function is modified from blender_xatlas
 | 
						|
# and calls the object without any explicit object context
 | 
						|
# thus allowing blender_xatlas to pack from background.
 | 
						|
###########################################################
 | 
						|
# Code is courtesy of mattedicksoncom
 | 
						|
# Modified by Naxela
 | 
						|
#
 | 
						|
# https://github.com/mattedicksoncom/blender-xatlas/
 | 
						|
###########################################################
 | 
						|
 | 
						|
def gen_safe_name():
 | 
						|
    genId = uuid.uuid4().hex
 | 
						|
    # genId = "u_" + genId.replace("-","_")
 | 
						|
    return "u_" + genId
 | 
						|
 | 
						|
def Unwrap_Lightmap_Group_Xatlas_2_headless_call(obj):
 | 
						|
 | 
						|
    blender_xatlas = importlib.util.find_spec("blender_xatlas")
 | 
						|
 | 
						|
    if blender_xatlas is not None:
 | 
						|
        import blender_xatlas
 | 
						|
    else:
 | 
						|
        return 0
 | 
						|
 | 
						|
    packOptions = bpy.context.scene.pack_tool
 | 
						|
    chartOptions = bpy.context.scene.chart_tool
 | 
						|
 | 
						|
    sharedProperties = bpy.context.scene.shared_properties
 | 
						|
    #sharedProperties.unwrapSelection
 | 
						|
 | 
						|
    context = bpy.context
 | 
						|
 | 
						|
    #save whatever mode the user was in
 | 
						|
    startingMode = bpy.context.object.mode
 | 
						|
    selected_objects = bpy.context.selected_objects
 | 
						|
 | 
						|
    #check something is actually selected
 | 
						|
    #external function/operator will select them
 | 
						|
    if len(selected_objects) == 0:
 | 
						|
        print("Nothing Selected")
 | 
						|
        self.report({"WARNING"}, "Nothing Selected, please select Something")
 | 
						|
        return {'FINISHED'}
 | 
						|
 | 
						|
    #store the names of objects to be lightmapped
 | 
						|
    rename_dict = dict()
 | 
						|
    safe_dict = dict()
 | 
						|
 | 
						|
    #make sure all the objects have ligthmap uvs
 | 
						|
    for obj in selected_objects:
 | 
						|
        if obj.type == 'MESH':
 | 
						|
            safe_name = gen_safe_name();
 | 
						|
            rename_dict[obj.name] = (obj.name,safe_name)
 | 
						|
            safe_dict[safe_name] = obj.name
 | 
						|
            context.view_layer.objects.active = obj
 | 
						|
            if obj.data.users > 1:
 | 
						|
                obj.data = obj.data.copy() #make single user copy
 | 
						|
            uv_layers = obj.data.uv_layers
 | 
						|
 | 
						|
            #setup the lightmap uvs
 | 
						|
            uvName = "UVMap_Lightmap"
 | 
						|
            if sharedProperties.lightmapUVChoiceType == "NAME":
 | 
						|
                uvName = sharedProperties.lightmapUVName
 | 
						|
            elif sharedProperties.lightmapUVChoiceType == "INDEX":
 | 
						|
                if sharedProperties.lightmapUVIndex < len(uv_layers):
 | 
						|
                    uvName = uv_layers[sharedProperties.lightmapUVIndex].name
 | 
						|
 | 
						|
            if not uvName in uv_layers:
 | 
						|
                uvmap = uv_layers.new(name=uvName)
 | 
						|
                uv_layers.active_index = len(uv_layers) - 1
 | 
						|
            else:
 | 
						|
                for i in range(0, len(uv_layers)):
 | 
						|
                    if uv_layers[i].name == uvName:
 | 
						|
                        uv_layers.active_index = i
 | 
						|
            obj.select_set(True)
 | 
						|
 | 
						|
    #save all the current edges
 | 
						|
    if sharedProperties.packOnly:
 | 
						|
        edgeDict = dict()
 | 
						|
        for obj in selected_objects:
 | 
						|
            if obj.type == 'MESH':
 | 
						|
                tempEdgeDict = dict()
 | 
						|
                tempEdgeDict['object'] = obj.name
 | 
						|
                tempEdgeDict['edges'] = []
 | 
						|
                print(len(obj.data.edges))
 | 
						|
                for i in range(0,len(obj.data.edges)):
 | 
						|
                    setEdge = obj.data.edges[i]
 | 
						|
                    tempEdgeDict['edges'].append(i)
 | 
						|
                edgeDict[obj.name] = tempEdgeDict
 | 
						|
 | 
						|
        bpy.ops.object.mode_set(mode='EDIT')
 | 
						|
        bpy.ops.mesh.select_all(action='SELECT')
 | 
						|
        bpy.ops.mesh.quads_convert_to_tris(quad_method='FIXED', ngon_method='BEAUTY')
 | 
						|
    else:
 | 
						|
        bpy.ops.object.mode_set(mode='EDIT')
 | 
						|
        bpy.ops.mesh.select_all(action='SELECT')
 | 
						|
        bpy.ops.mesh.quads_convert_to_tris(quad_method='FIXED', ngon_method='BEAUTY')
 | 
						|
 | 
						|
    bpy.ops.object.mode_set(mode='OBJECT')
 | 
						|
 | 
						|
    #Create a fake obj export to a string
 | 
						|
    #Will strip this down further later
 | 
						|
    fakeFile = StringIO()
 | 
						|
    blender_xatlas.export_obj_simple.save(
 | 
						|
        rename_dict=rename_dict,
 | 
						|
        context=bpy.context,
 | 
						|
        filepath=fakeFile,
 | 
						|
        mainUVChoiceType=sharedProperties.mainUVChoiceType,
 | 
						|
        uvIndex=sharedProperties.mainUVIndex,
 | 
						|
        uvName=sharedProperties.mainUVName,
 | 
						|
        use_selection=True,
 | 
						|
        use_animation=False,
 | 
						|
        use_mesh_modifiers=True,
 | 
						|
        use_edges=True,
 | 
						|
        use_smooth_groups=False,
 | 
						|
        use_smooth_groups_bitflags=False,
 | 
						|
        use_normals=True,
 | 
						|
        use_uvs=True,
 | 
						|
        use_materials=False,
 | 
						|
        use_triangles=False,
 | 
						|
        use_nurbs=False,
 | 
						|
        use_vertex_groups=False,
 | 
						|
        use_blen_objects=True,
 | 
						|
        group_by_object=False,
 | 
						|
        group_by_material=False,
 | 
						|
        keep_vertex_order=False,
 | 
						|
    )
 | 
						|
 | 
						|
    #print just for reference
 | 
						|
    # print(fakeFile.getvalue())
 | 
						|
 | 
						|
    #get the path to xatlas
 | 
						|
    #file_path = os.path.dirname(os.path.abspath(__file__))
 | 
						|
    scriptsDir = os.path.join(bpy.utils.user_resource('SCRIPTS'), "addons")
 | 
						|
    file_path = os.path.join(scriptsDir, "blender_xatlas")
 | 
						|
    if platform.system() == "Windows":
 | 
						|
        xatlas_path = os.path.join(file_path, "xatlas", "xatlas-blender.exe")
 | 
						|
    elif platform.system() == "Linux":
 | 
						|
        xatlas_path = os.path.join(file_path, "xatlas", "xatlas-blender")
 | 
						|
        #need to set permissions for the process on linux
 | 
						|
        subprocess.Popen(
 | 
						|
            'chmod u+x "' + xatlas_path + '"',
 | 
						|
            shell=True
 | 
						|
        )
 | 
						|
 | 
						|
    #setup the arguments to be passed to xatlas-------------------
 | 
						|
    arguments_string = ""
 | 
						|
    for argumentKey in packOptions.__annotations__.keys():
 | 
						|
        key_string = str(argumentKey)
 | 
						|
        if argumentKey is not None:
 | 
						|
            print(getattr(packOptions,key_string))
 | 
						|
            attrib = getattr(packOptions,key_string)
 | 
						|
            if type(attrib) == bool:
 | 
						|
                if attrib == True:
 | 
						|
                    arguments_string = arguments_string + " -" + str(argumentKey)
 | 
						|
            else:
 | 
						|
                arguments_string = arguments_string + " -" + str(argumentKey) + " " + str(attrib)
 | 
						|
 | 
						|
    for argumentKey in chartOptions.__annotations__.keys():
 | 
						|
        if argumentKey is not None:
 | 
						|
            key_string = str(argumentKey)
 | 
						|
            print(getattr(chartOptions,key_string))
 | 
						|
            attrib = getattr(chartOptions,key_string)
 | 
						|
            if type(attrib) == bool:
 | 
						|
                if attrib == True:
 | 
						|
                    arguments_string = arguments_string + " -" + str(argumentKey)
 | 
						|
            else:
 | 
						|
                arguments_string = arguments_string + " -" + str(argumentKey) + " " + str(attrib)
 | 
						|
 | 
						|
    #add pack only option
 | 
						|
    if sharedProperties.packOnly:
 | 
						|
        arguments_string = arguments_string + " -packOnly"
 | 
						|
 | 
						|
    arguments_string = arguments_string + " -atlasLayout" + " " + sharedProperties.atlasLayout
 | 
						|
 | 
						|
    print(arguments_string)
 | 
						|
    #END setup the arguments to be passed to xatlas-------------------
 | 
						|
 | 
						|
    #RUN xatlas process
 | 
						|
    xatlas_process = subprocess.Popen(
 | 
						|
        r'"{}"'.format(xatlas_path) + ' ' + arguments_string,
 | 
						|
        stdin=subprocess.PIPE,
 | 
						|
        stdout=subprocess.PIPE,
 | 
						|
        shell=True
 | 
						|
    )
 | 
						|
 | 
						|
    print(xatlas_path)
 | 
						|
 | 
						|
    #shove the fake file in stdin
 | 
						|
    stdin = xatlas_process.stdin
 | 
						|
    value = bytes(fakeFile.getvalue() + "\n", 'UTF-8') #The \n is needed to end the input properly
 | 
						|
    stdin.write(value)
 | 
						|
    stdin.flush()
 | 
						|
 | 
						|
    #Get the output from xatlas
 | 
						|
    outObj = ""
 | 
						|
    while True:
 | 
						|
        output = xatlas_process.stdout.readline()
 | 
						|
        if not output:
 | 
						|
                break
 | 
						|
        outObj = outObj + (output.decode().strip() + "\n")
 | 
						|
 | 
						|
    #the objects after xatlas processing
 | 
						|
    # print(outObj)
 | 
						|
 | 
						|
 | 
						|
    #Setup for reading the output
 | 
						|
    @dataclass
 | 
						|
    class uvObject:
 | 
						|
        obName: string = ""
 | 
						|
        uvArray: List[float] = field(default_factory=list)
 | 
						|
        faceArray: List[int] = field(default_factory=list)
 | 
						|
 | 
						|
    convertedObjects = []
 | 
						|
    uvArrayComplete = []
 | 
						|
 | 
						|
 | 
						|
    #search through the out put for STARTOBJ
 | 
						|
    #then start reading the objects
 | 
						|
    obTest = None
 | 
						|
    startRead = False
 | 
						|
    for line in outObj.splitlines():
 | 
						|
 | 
						|
        line_split = line.split()
 | 
						|
 | 
						|
        if not line_split:
 | 
						|
            continue
 | 
						|
 | 
						|
        line_start = line_split[0]  # we compare with this a _lot_
 | 
						|
        # print(line_start)
 | 
						|
        if line_start == "STARTOBJ":
 | 
						|
            print("Start reading the objects----------------------------------------")
 | 
						|
            startRead = True
 | 
						|
            # obTest = uvObject()
 | 
						|
 | 
						|
        if startRead:
 | 
						|
            #if it's a new obj
 | 
						|
            if line_start == 'o':
 | 
						|
                #if there is already an object append it
 | 
						|
                if obTest is not None:
 | 
						|
                    convertedObjects.append(obTest)
 | 
						|
 | 
						|
                obTest = uvObject() #create new uv object
 | 
						|
                obTest.obName = line_split[1]
 | 
						|
 | 
						|
            if obTest is not None:
 | 
						|
                #the uv coords
 | 
						|
                if line_start == 'vt':
 | 
						|
                    newUv = [float(line_split[1]),float(line_split[2])]
 | 
						|
                    obTest.uvArray.append(newUv)
 | 
						|
                    uvArrayComplete.append(newUv)
 | 
						|
 | 
						|
                #the face coords index
 | 
						|
                #faces are 1 indexed
 | 
						|
                if line_start == 'f':
 | 
						|
                    #vert/uv/normal
 | 
						|
                    #only need the uvs
 | 
						|
                    newFace = [
 | 
						|
                        int(line_split[1].split("/")[1]),
 | 
						|
                        int(line_split[2].split("/")[1]),
 | 
						|
                        int(line_split[3].split("/")[1])
 | 
						|
                    ]
 | 
						|
                    obTest.faceArray.append(newFace)
 | 
						|
 | 
						|
    #append the final object
 | 
						|
    convertedObjects.append(obTest)
 | 
						|
    print(convertedObjects)
 | 
						|
 | 
						|
 | 
						|
    #apply the output-------------------------------------------------------------
 | 
						|
    #copy the uvs to the original objects
 | 
						|
    # objIndex = 0
 | 
						|
    print("Applying the UVs----------------------------------------")
 | 
						|
    # print(convertedObjects)
 | 
						|
    for importObject in convertedObjects:
 | 
						|
        bpy.ops.object.select_all(action='DESELECT')
 | 
						|
 | 
						|
        obTest = importObject
 | 
						|
        obTest.obName = safe_dict[obTest.obName] #probably shouldn't just replace it
 | 
						|
        bpy.context.scene.objects[obTest.obName].select_set(True)
 | 
						|
        context.view_layer.objects.active = bpy.context.scene.objects[obTest.obName]
 | 
						|
        bpy.ops.object.mode_set(mode = 'OBJECT')
 | 
						|
 | 
						|
        obj = bpy.context.active_object
 | 
						|
        me = obj.data
 | 
						|
        #convert to bmesh to create the new uvs
 | 
						|
        bm = bmesh.new()
 | 
						|
        bm.from_mesh(me)
 | 
						|
 | 
						|
        uv_layer = bm.loops.layers.uv.verify()
 | 
						|
 | 
						|
        nFaces = len(bm.faces)
 | 
						|
        #need to ensure lookup table for some reason?
 | 
						|
        if hasattr(bm.faces, "ensure_lookup_table"):
 | 
						|
            bm.faces.ensure_lookup_table()
 | 
						|
 | 
						|
        #loop through the faces
 | 
						|
        for faceIndex in range(nFaces):
 | 
						|
            faceGroup = obTest.faceArray[faceIndex]
 | 
						|
 | 
						|
            bm.faces[faceIndex].loops[0][uv_layer].uv = (
 | 
						|
                uvArrayComplete[faceGroup[0] - 1][0],
 | 
						|
                uvArrayComplete[faceGroup[0] - 1][1])
 | 
						|
 | 
						|
            bm.faces[faceIndex].loops[1][uv_layer].uv = (
 | 
						|
                uvArrayComplete[faceGroup[1] - 1][0],
 | 
						|
                uvArrayComplete[faceGroup[1] - 1][1])
 | 
						|
 | 
						|
            bm.faces[faceIndex].loops[2][uv_layer].uv = (
 | 
						|
                uvArrayComplete[faceGroup[2] - 1][0],
 | 
						|
                uvArrayComplete[faceGroup[2] - 1][1])
 | 
						|
 | 
						|
            # objIndex = objIndex + 3
 | 
						|
 | 
						|
        # print(objIndex)
 | 
						|
        #assign the mesh back to the original mesh
 | 
						|
        bm.to_mesh(me)
 | 
						|
    #END apply the output-------------------------------------------------------------
 | 
						|
 | 
						|
 | 
						|
    #Start setting the quads back again-------------------------------------------------------------
 | 
						|
    if sharedProperties.packOnly:
 | 
						|
        bpy.ops.object.mode_set(mode='EDIT')
 | 
						|
        bpy.ops.mesh.select_all(action='DESELECT')
 | 
						|
        bpy.ops.object.mode_set(mode='OBJECT')
 | 
						|
 | 
						|
        for edges in edgeDict:
 | 
						|
            edgeList = edgeDict[edges]
 | 
						|
            currentObject = bpy.context.scene.objects[edgeList['object']]
 | 
						|
            bm = bmesh.new()
 | 
						|
            bm.from_mesh(currentObject.data)
 | 
						|
            if hasattr(bm.edges, "ensure_lookup_table"):
 | 
						|
                bm.edges.ensure_lookup_table()
 | 
						|
 | 
						|
            #assume that all the triangulated edges come after the original edges
 | 
						|
            newEdges = []
 | 
						|
            for edge in range(len(edgeList['edges']), len(bm.edges)):
 | 
						|
                newEdge = bm.edges[edge]
 | 
						|
                newEdge.select = True
 | 
						|
                newEdges.append(newEdge)
 | 
						|
 | 
						|
            bmesh.ops.dissolve_edges(bm, edges=newEdges, use_verts=False, use_face_split=False)
 | 
						|
            bpy.ops.object.mode_set(mode='OBJECT')
 | 
						|
            bm.to_mesh(currentObject.data)
 | 
						|
            bm.free()
 | 
						|
            bpy.ops.object.mode_set(mode='EDIT')
 | 
						|
 | 
						|
    #End setting the quads back again-------------------------------------------------------------
 | 
						|
 | 
						|
    #select the original objects that were selected
 | 
						|
    for objectName in rename_dict:
 | 
						|
        if objectName[0] in bpy.context.scene.objects:
 | 
						|
            current_object = bpy.context.scene.objects[objectName[0]]
 | 
						|
            current_object.select_set(True)
 | 
						|
            context.view_layer.objects.active = current_object
 | 
						|
 | 
						|
    bpy.ops.object.mode_set(mode=startingMode)
 | 
						|
 | 
						|
    print("Finished Xatlas----------------------------------------")
 | 
						|
    return {'FINISHED'}
 | 
						|
 | 
						|
def transfer_assets(copy, source, destination):
 | 
						|
    for filename in glob.glob(os.path.join(source, '*.*')):
 | 
						|
        try:
 | 
						|
            shutil.copy(filename, destination)
 | 
						|
        except shutil.SameFileError:
 | 
						|
            pass
 | 
						|
 | 
						|
def transfer_load():
 | 
						|
    load_folder = bpy.path.abspath(os.path.join(os.path.dirname(bpy.data.filepath), bpy.context.scene.TLM_SceneProperties.tlm_load_folder))
 | 
						|
    lightmap_folder = os.path.join(os.path.dirname(bpy.data.filepath), bpy.context.scene.TLM_EngineProperties.tlm_lightmap_savedir)
 | 
						|
    print(load_folder)
 | 
						|
    print(lightmap_folder)
 | 
						|
    transfer_assets(True, load_folder, lightmap_folder)
 |