2023-11-12 15:09:22 +00:00
|
|
|
import bpy
|
|
|
|
import os
|
|
|
|
import xml.etree.ElementTree as ET
|
|
|
|
|
2023-11-12 18:10:30 +00:00
|
|
|
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")
|
2023-11-12 15:09:22 +00:00
|
|
|
|
|
|
|
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 = '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)
|
|
|
|
|
2023-11-12 18:10:30 +00:00
|
|
|
custom_print("Input Directory:", input_directory)
|
|
|
|
custom_print("Output Directory:", output_directory)
|
|
|
|
custom_print("Texture Directory:", texture_directory)
|
2023-11-12 15:09:22 +00:00
|
|
|
|
|
|
|
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')
|
|
|
|
|
2023-11-12 18:10:30 +00:00
|
|
|
# custom_print the result
|
|
|
|
custom_print(f"The value of 'materials' is: {materials_value}")
|
2023-11-12 15:09:22 +00:00
|
|
|
|
|
|
|
# return the materials_value
|
|
|
|
return materials_value
|
|
|
|
|
|
|
|
except Exception as e:
|
2023-11-12 18:10:30 +00:00
|
|
|
custom_print(f"Error processing XML file: {e}")
|
2023-11-12 15:09:22 +00:00
|
|
|
return None
|
|
|
|
|
|
|
|
def get_diffuse_texture_name(xml_file_path, material_name):
|
|
|
|
"""
|
|
|
|
Extracts the name of the texture in <diffuse_texture> 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 <diffuse_texture> 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}.dds"
|
|
|
|
else:
|
2023-11-12 18:10:30 +00:00
|
|
|
custom_print(f"Diffuse texture not found for material '{material_name}' in the XML file.")
|
2023-11-12 15:09:22 +00:00
|
|
|
return None
|
|
|
|
else:
|
2023-11-12 18:10:30 +00:00
|
|
|
custom_print(f"Material '{material_name}' not found in the XML file.")
|
2023-11-12 15:09:22 +00:00
|
|
|
return None
|
|
|
|
except FileNotFoundError:
|
2023-11-12 18:10:30 +00:00
|
|
|
custom_print(f"File not found: {xml_file_path}")
|
2023-11-12 15:09:22 +00:00
|
|
|
return None
|
|
|
|
except Exception as e:
|
2023-11-12 18:10:30 +00:00
|
|
|
custom_print(f"Error while processing {xml_file_path}: {e}")
|
2023-11-12 15:09:22 +00:00
|
|
|
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:
|
2023-11-12 18:10:30 +00:00
|
|
|
get_material_config_path = material_config.replace("units/", f"{input_directory}/") + ".material_config"
|
2023-11-12 15:09:22 +00:00
|
|
|
diffuse_texture_name = get_diffuse_texture_name(get_material_config_path, material_name)
|
|
|
|
else:
|
2023-11-12 18:10:30 +00:00
|
|
|
custom_print("Material config is None. Handle this case accordingly.")
|
2023-11-12 15:09:22 +00:00
|
|
|
|
|
|
|
# CRASHES WHEN IT CANT FIND THE FILES TO LAZY TO FIX IT JUST HAVE THE FILES
|
|
|
|
# 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:
|
2023-11-12 18:10:30 +00:00
|
|
|
custom_print(f"Texture not found for material {material_name}: {texture_path}")
|
2023-11-12 15:09:22 +00:00
|
|
|
else:
|
2023-11-12 18:10:30 +00:00
|
|
|
custom_print(f"No diffuse texture found for material {material_name}")
|
2023-11-12 15:09:22 +00:00
|
|
|
|
|
|
|
|
|
|
|
# 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 .gltf to the output directory
|
|
|
|
bpy.ops.export_scene.gltf(filepath=output_path, export_format='GLTF_EMBEDDED', use_selection=True)
|
|
|
|
|
|
|
|
# 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:
|
2023-11-12 18:10:30 +00:00
|
|
|
custom_print("No objects selected.")
|
2023-11-12 15:09:22 +00:00
|
|
|
|
|
|
|
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)
|