Fusion/pdworld2json.py

139 lines
5.0 KiB
Python

import json
import os
from scipy.spatial.transform import Rotation
import xml.etree.ElementTree as ET
import numpy as np
def parse_xml_to_json(xml_file_path, json_file_path, include_unit_id=False):
# Parse the XML file
tree = ET.parse(xml_file_path)
root = tree.getroot()
# Create a list to store the parsed data
world_data = []
# Keep track of unique instances of the "path" attribute
unique_paths = set()
# Iterate through the "unit_data" elements and extract the desired attributes
for unit_data_elem in root.findall('.//unit_data'):
position_str = unit_data_elem.get('position')
rotation_quaternion_str = unit_data_elem.get('rotation')
full_name = unit_data_elem.get('name')
unit_id = unit_data_elem.get('unit_id') if include_unit_id else None
# Replace commas with dots in the position and quaternion strings
position_str = position_str.replace(',', '.')
rotation_quaternion_str = rotation_quaternion_str.replace(',', '.')
# Extract name and path
unit_description = full_name.split('/')[-1]
path = full_name
# Track unique instances of the "path" attribute
unique_paths.add(path)
# Convert position and quaternion to vectors
try:
position_vector = list(map(float, position_str.split()))
# Convert right-handed to left-handed coordinates
position_vector[0] = -position_vector[0]
quaternion = np.array(list(map(float, rotation_quaternion_str.split())))
rotation = Rotation.from_quat(quaternion)
euler_angles = rotation.as_euler('zyx')
# Convert radians to degrees and clamp to 3 decimal places
rotation_vector = np.degrees(euler_angles).round(3).tolist()
rotation_vector[0] = - rotation_vector[0]
# Convert Euler angles to Rotator format
rotator = {
'yaw': rotation_vector[0],
'pitch': rotation_vector[1],
'roll': rotation_vector[2]
}
# Generate Unreal object reference
object_reference = f"/Script/Engine.StaticMesh'/Game/Mods/YourModName/Units/{path}/{unit_description}.{unit_description}'"
# Use 'name' instead of 'unit_id'
unit_data = {
'position': {
'x': position_vector[0],
'y': position_vector[1],
'z': position_vector[2]
},
'rotation_quaternion': rotation_quaternion_str,
'rotation_vector': rotator,
'unit_description': unit_description,
'path': object_reference,
}
if include_unit_id:
unit_data['name'] = unit_id
else:
unit_data['name'] = unit_description
world_data.append(unit_data)
except ValueError:
print(f"Skipping invalid data: {position_str}, {rotation_quaternion_str}")
# Print the unique instances of the "path" attribute
unique_dlc_units = set()
for unique_path in unique_paths:
dlc = unique_path.split('/')[1]
unique_dlc_units.add(dlc)
print(f"Units DLCs in this map: {', '.join(unique_dlc_units)}. Make sure to get them from the game")
# Convert the list to JSON and write it to the output file
with open(json_file_path, 'w') as json_file:
json.dump(world_data, json_file, indent=2)
print(f'Conversion complete. JSON data written to {json_file_path}')
def process_folder(folder_path):
# Ensure the folder exists, create it if it doesn't
full_folder_path = os.path.abspath(folder_path)
if not os.path.exists(full_folder_path):
os.makedirs(full_folder_path)
print(f'Created folder: {full_folder_path}')
# List all files and folders in the current directory
files_and_folders = os.listdir(full_folder_path)
for item in files_and_folders:
item_path = os.path.join(full_folder_path, item)
# If it's a directory, recursively process it
if os.path.isdir(item_path):
print(f'Processing folder: {item_path}')
process_folder(item_path)
# If it's a file with a .continent extension, process it
elif os.path.isfile(item_path) and item.lower().endswith('.continent'):
print(f'Processing file: {item_path}')
# Get the directory of the script using __file__
script_directory = os.path.abspath(os.path.dirname(__file__))
json_file_path = os.path.abspath(os.path.join(script_directory, 'WorldFiles', 'UnrealJSON', f'{os.path.splitext(item)[0]}.json'))
parse_xml_to_json(item_path, json_file_path, include_unit_id=True)
print(f'Created JSON file: {json_file_path}')
# Get The working Dir
root_folder = r'\WorldFiles\DieselWorldCONTINENT'
dir_path = os.path.dirname(os.path.realpath(__file__))
folder_path = dir_path + root_folder
# Start processing
print(f'Starting processing from: {folder_path}')
process_folder(folder_path)