139 lines
5.0 KiB
Python
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/{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)
|