import bpy import os import xml.etree.ElementTree as ET def custom_print(*args): message = ' '.join(map(str, args)) for window in bpy.context.window_manager.windows: screen = window.screen for area in screen.areas: if area.type == 'CONSOLE': override = {'window': window, 'screen': screen, 'area': area} bpy.ops.console.scrollback_append(override, text=message, type="OUTPUT") for material in bpy.data.materials: bpy.data.materials.remove(material, do_unlink=True) for mesh in bpy.data.meshes: bpy.data.meshes.remove(mesh, do_unlink=True) for image in bpy.data.images: bpy.data.images.remove(image, do_unlink=True) # Set the input and output directories wherethefilesare = 'output' # Set where the files are located from PD2ModelParser.exe wheretosave = 'done' # Where you want to save the fixed files. wherethetexturesare = 'converted_textures' # Set where all the files are located # Get the parent directory of the python file dir_path = os.path.dirname(os.path.realpath(__file__)) parent_dir = os.path.abspath(os.path.join(dir_path, os.pardir)) # Make valid folder paths using os.path.join input_directory = os.path.join(parent_dir, wherethefilesare) output_directory = os.path.join(parent_dir, wheretosave) texture_directory = os.path.join(parent_dir, wherethetexturesare) custom_print("Input Directory:", input_directory) custom_print("Output Directory:", output_directory) custom_print("Texture Directory:", texture_directory) def get_object_filepath(file_path): # Replace the extension with .object object_file_path = os.path.splitext(file_path)[0] + '.object' return object_file_path def set_diffuse_texture_as_base_color(material, texture_path): # Create an image texture node texture_node = material.node_tree.nodes.new(type='ShaderNodeTexImage') texture_node.image = bpy.data.images.load(texture_path) # Get the principled BSDF shader node principled_bsdf = material.node_tree.nodes.get("Principled BSDF") # Connect the texture node to the base color input of the principled BSDF shader node material.node_tree.links.new(principled_bsdf.inputs["Base Color"], texture_node.outputs["Color"]) def process_xml_file(file_path): try: # Open the file and read its contents with open(file_path, 'r') as file: file_contents = file.read() # Parse the XML string root = ET.fromstring(file_contents) # Find the value of the 'materials' attribute materials_value = root.find('.//diesel').get('materials') # custom_print the result custom_print(f"The value of 'materials' is: {materials_value}") # return the materials_value return materials_value except Exception as e: custom_print(f"Error processing XML file: {e}") return None def get_diffuse_texture_name(xml_file_path, material_name): """ Extracts the name of the texture in for a given material name in an XML file. Parameters: - xml_file_path (str): The path to the XML file. - material_name (str): The name of the material to search for. Returns: - str: The name of the texture if the material is found, else None. """ try: # Parse the XML file tree = ET.parse(xml_file_path) root = tree.getroot() # Find the material with the specified name target_material = root.find(f".//material[@name='{material_name}']") if target_material is not None: # Find the 'diffuse_texture' element within the material diffuse_texture_element = target_material.find("diffuse_texture") if diffuse_texture_element is not None: # Get the value of the 'file' attribute in and extract the texture name diffuse_texture_path = diffuse_texture_element.get("file") texture_name = diffuse_texture_path.split("/")[-1] # Extracting the texture name from the file path return f"{texture_name}.png" else: custom_print(f"Diffuse texture not found for material '{material_name}' in the XML file.") return None else: custom_print(f"Material '{material_name}' not found in the XML file.") return None except FileNotFoundError: custom_print(f"File not found: {xml_file_path}") return None except Exception as e: custom_print(f"Error while processing {xml_file_path}: {e}") return None # Main function to process the object def process_gltf(file_path): # Get the filename without extension file_name = os.path.splitext(os.path.basename(file_path))[0] # Clear existing mesh objects in the scene bpy.ops.object.select_all(action='SELECT') bpy.ops.object.delete() # Import the GLTF file bpy.ops.import_scene.gltf(filepath=file_path) # Deselect all objects bpy.ops.object.select_all(action='DESELECT') # Find the mesh with the highest poly count max_poly_count = 0 object_with_max_poly_count = None for obj in bpy.context.scene.objects: if obj.type == 'MESH' and len(obj.data.polygons) > max_poly_count: max_poly_count = len(obj.data.polygons) object_with_max_poly_count = obj # Select the parent (root) object of the object with the highest poly count if object_with_max_poly_count: root_object = object_with_max_poly_count.parent if root_object: root_object.select_set(True) # Set the 3D cursor as the pivot point for the transformation bpy.context.scene.tool_settings.transform_pivot_point = 'CURSOR' # Set the location of the selected object to the 3D cursor root_object.location = bpy.context.scene.cursor.location # Set the transform rotation mode to XYZ Euler root_object.rotation_mode = 'XYZ' # Add the specified rotation to the object root_object.rotation_euler[0] = -1.5708 # X-axis rotation, -90 degrees in radians root_object.rotation_euler[1] = 0 # Y-axis rotation, 0 degrees in radians root_object.rotation_euler[2] = 0 # Z-axis rotation, 0 degrees in radians # Select all objects in the scene bpy.ops.object.select_all(action='SELECT') # Apply all transforms to the selected objects bpy.ops.object.transform_apply(location=True, rotation=True, scale=True) # Select meshes that start with the name "g_" bpy.ops.object.select_all(action='DESELECT') for obj in bpy.context.scene.objects: if obj.type == 'MESH' and obj.name.startswith("g_"): obj.select_set(True) # Invert the selection and delete the unselected objects bpy.ops.object.select_all(action='INVERT') bpy.ops.object.delete() # Join remaining objects together bpy.ops.object.select_all(action='DESELECT') #Mesh objects MSH_OBJS = [m for m in bpy.context.scene.objects if m.type == 'MESH'] # Check if there are any objects in the list if MSH_OBJS: for OBJS in MSH_OBJS: # Select all mesh objects OBJS.select_set(state=True) # Makes one active bpy.context.view_layer.objects.active = OBJS # Joins objects bpy.ops.object.join() # Rename the object to the filename bpy.ops.object.select_all(action='DESELECT') bpy.ops.object.select_by_type(type='MESH') bpy.context.selected_objects[0].name = file_name # Get mat config object_file_path = get_object_filepath(file_path) material_config = process_xml_file(object_file_path) # Loop each material slot get the name for material_slot in bpy.context.selected_objects[0].material_slots: # Get the material in the current slot material = material_slot.material # Get the material name material_name = material.name # Initialize the variable with a default value diffuse_texture_name = None # Get the diffuse texture name using get_diffuse_texture_name() # Makes this not hardcoded if material_config is not None: get_material_config_path = material_config.replace("units/", f"{input_directory}/") + ".material_config" diffuse_texture_name = get_diffuse_texture_name(get_material_config_path, material_name) else: custom_print("Material config is None. Handle this case accordingly.") # CRASHES WHEN IT CANT FIND THE FILES TO LAZY TO FIX IT SO IT JUST SKIPS IT # Check if the texture exists if diffuse_texture_name: # Construct the full path to the texture texture_path = os.path.join(texture_directory, diffuse_texture_name) # Check if the texture file exists before trying to load it if os.path.exists(texture_path): # Set the diffuse texture as the base color set_diffuse_texture_as_base_color(material, texture_path) else: custom_print(f"Texture not found for material {material_name}: {texture_path}") else: custom_print(f"No diffuse texture found for material {material_name}") # Ensure the output directory exists output_path = os.path.join(output_directory, os.path.relpath(file_path, start=input_directory)) os.makedirs(os.path.dirname(output_path), exist_ok=True) # Select all objects in the scene bpy.ops.object.select_all(action='SELECT') # Export as to the output directory bpy.ops.export_scene.fbx( filepath=output_path.replace('.gltf', '.fbx'), check_existing=True, filter_glob="*.fbx", use_selection=True, bake_space_transform=False, object_types={'MESH'}, mesh_smooth_type='EDGE', use_mesh_edges=True, add_leaf_bones=False, path_mode='COPY', ) # Remove materials and meshes for material in bpy.data.materials: bpy.data.materials.remove(material, do_unlink=True) for mesh in bpy.data.meshes: bpy.data.meshes.remove(mesh, do_unlink=True) for image in bpy.data.images: bpy.data.images.remove(image, do_unlink=True) else: custom_print("No objects selected.") for material in bpy.data.materials: bpy.data.materials.remove(material, do_unlink=True) for mesh in bpy.data.meshes: bpy.data.meshes.remove(mesh, do_unlink=True) for image in bpy.data.images: bpy.data.images.remove(image, do_unlink=True) # Recursive function to process all .gltf files in a directory def process_directory(directory): for root, dirs, files in os.walk(directory): for file in files: if file.lower().endswith('.gltf'): file_path = os.path.join(root, file) process_gltf(file_path) # Start processing from the input directory process_directory(input_directory)