Making Community map playable

Hey folks,
So like @only_a_ptr suggested in discord I'm opening this thread so we (I hope together) try to make community map playable on moderate machine's.
To achieve this we will implement set_default_rendering_distance directive in the TOBJ file but first we need to merge all .meshe's and textures
it to one .mesh and one texture file. To tackle first obstacle merging .meshes in to one we first need to convert all meshes to .mesh.xml file format which
blender can read and to do that I created just simple bash script
Code:
#!/bin/bash

# Directory containing the .mesh files
input_dir="/home/slobodan/mesh"

# Directory to save the converted .mesh.xml files
output_dir="/home/slobodan/xml"


# Check if the output directory exists, create if not
if [ ! -d "$output_dir" ]; then
  mkdir -p "$output_dir"
fi

# Loop through each .mesh file in the input directory
for file in "$input_dir"/*.mesh; do
  # Get the filename without extension
  filename=$(basename "$file" .mesh)
 
  # Convert .mesh to .mesh.xml using OgreXMLConverter
  LD_LIBRARY_PATH=. ./OgreXMLConverter "$file" "$output_dir/$filename.mesh.xml"
 
  echo "Converted $file to $output_dir/$filename.mesh.xml"
done

after that to tackle second problem we need to create python script which will read coordinates of meshes ,
rotation angle and mesh name from community map.tobj file and merge all mesh's in to one in side of blender.
So I created such python script (which is second python script in my life) and I assume it's ugly from experienced
developer stand of point, but that's my best try atm

Python:
import bpy
import math
import os

coordinate_file = "/home/slobodan/.rigsofrods/Community-Map/CommunityMap.tobj"
ogre_directory = "/home/slobodan/old-mesh-xml"

mesh_objects = []

with open(coordinate_file, "r") as file:
    for line_num, line in enumerate(file, start=1):
        parts = line.strip().split(",")

        # Check if the line has enough elements
        if len(parts) < 7:
            print(f"Error: Insufficient values in line {line_num}: {line}")
            continue

        try:
            coordinates = [float(coord) for coord in parts[:3]]  # Extract position coordinates
            rotations = [float(rot) for rot in parts[3:6]]  # Extract rotation values
            object_name = parts[6].strip()  # Extract the object name

            # Check if the .mesh.xml file exists
            ogre_filepath = os.path.join(ogre_directory, f"{object_name}.mesh.xml")
            if not os.path.isfile(ogre_filepath):
                print(f"Error: File '{ogre_filepath}' not found for object '{object_name}'")
                continue

            # Import the .mesh.xml file
            bpy.ops.ogre.import_mesh(filepath=ogre_filepath)

            # Get the imported mesh object
            mesh_object = bpy.context.selected_objects[0]
            mesh_objects.append(mesh_object)

            # Swap the coordinates to match the desired axis orientation
            coordinates = [coordinates[1], -coordinates[0], coordinates[2]]

            # Swap the rotation axes to match the desired axis orientation and rotate around -X axis
            rotations = [math.radians(rotations[1]), -math.radians(rotations[0]), math.radians(rotations[2])]

            # Set the object's location based on the coordinates
            mesh_object.location = coordinates

            # Set the object's rotation based on the rotation values
            mesh_object.rotation_euler = rotations

        except ValueError:
            print(f"Error: Unable to convert values to float in line {line_num}: {line}")

# Select all imported mesh objects
for mesh_object in mesh_objects:
    mesh_object.select_set(True)

# Set the active object to the last imported mesh object
bpy.context.view_layer.objects.active = mesh_objects[-1]

In discord @only_a_ptr also talked about messed up axis when compared which orientation RoR uses vs Blender vs popular games and Autodesk
and I have issue with just that. So when importing mesh in blender with OGRE importer you have to choose in which orientation you want to import
your .mesh file and no matter what Swap Axis mode I choose imported model has been imported in same orientation, if someone could test this wold
be great.

As you may read in discord I have week machine and I could not import all mesh's at once but I could accept @The Jesser suggestion to split them up
which I could do after figuring out why my imported meshes are not at lest at same plane regardless which one. Here is what I get after importing
first 60's object from CommunityMap.tobj file

COMM-MAP.png
Edit: I just tested axis orientation and it seams that they work

Code:
# Swap the rotation axes to match the desired axis orientation and rotate around -X axis
rotations = [math.radians(rotations[0]), -math.radians(rotations[1]), math.radians(rotations[2])]

Im not sure what orientation should we use, when I worked not ror-bot +X was forward, -Y was right and +Z up, I need help with this tnx
 
Last edited:
@Vido Respect for starting this project!

On axes and rotations in Ogre, there's this wikipage: https://wiki.ogre3d.org/Quaternion+and+Rotation+Primer. This is the important bit on axes:
within Ogre, we will use a right-handed coordinate system (see also Right-hand Rule). This means two things:
  • First, we consider +X to point to the right of your monitor (thumb), +Y to point to the top of your monitor (index finger) and +Z to point out of your monitor toward you (middle finger). (You can only do this with your right hand, hence the name.)
  • Next, to find out which direction +45 degrees rotates, point the thumb of your right hand down the axis of rotation (i.e. towards positive infinity). The direction your fingers curl is the positive angle.
It doesn't explicitly say "forward/right/left/up" but it hints that X is right/left and Z is front-back.
Euler Rotations
  • Yaw would be left/right rotation around the Y axis (vertical) on the XZ plane. Yaw is used when driving a car.
  • Pitch is up/down rotation around the X axis (horizontal, pointing right) on the YZ plane. Pitch is used when flying a jet down or up, or when driving up hill or down.
  • Roll is tilt rotation around the Z axis (pointing towards you) on the XY plane. Roll is literally what happens to your car when you take a curve too fast!

I created us a miniature test terrain to work with - .blend and .mesh.xml files are included. Sorry about the mess in filenames and lettercase, I didn't realize you're on Linux.

Your script looks very good, you know Python better than I do, I would have trouble following it without the comments. I also didn't even think of scripting directly in Blender, I intended standalone xml-splatting script, but you took it a step further which is great. I didn't have time to try it but I noticed you use .odef names as mesh names which won't always work, see the scaled cone objects in the test terrain.
1686736855278.png
 

Attachments

Last edited:
Here is improved version of python script which import most stuff correctly but it also imports some with wired orientation

Code:
import bpy
import math
import os

coordinate_file = "/home/slobodan/.rigsofrods/Community-Map/CommunityMap.tobj"
ogre_directory = "/home/slobodan/old-mesh-xml"

mesh_objects = []

def find_mesh_line(odef_filepath):
    with open(odef_filepath, "r") as odef_file:
        for line in odef_file:
            if ".mesh" in line:
                return line.strip()
    return None

with open(coordinate_file, "r") as file:
    for line_num, line in enumerate(file, start=1):
        parts = line.strip().split(",")

        # Check if the line has enough elements
        if len(parts) < 7:
            print(f"Error: Insufficient values in line {line_num}: {line}")
            continue

        try:
            coordinates = [float(coord) for coord in parts[:3]]  # Extract position coordinates
            rotations = [float(rot) for rot in parts[3:6]]  # Extract rotation values
            object_name = parts[6].strip()  # Extract the object name
            object_name += ".odef"  # Append the .odef extension

            # Check if the .odef file exists
            odef_filepath = os.path.join(ogre_directory, object_name)
            if not os.path.isfile(odef_filepath):
                print(f"Error: File '{odef_filepath}' not found for object '{object_name}'")
                continue

            # Find the line containing the .mesh keyword in the .odef file
            mesh_line = find_mesh_line(odef_filepath)
            if not mesh_line:
                print(f"Error: Mesh line not found in '{odef_filepath}' for object '{object_name}'")
                continue

            # Extract the .mesh.xml file path from the line
            mesh_filepath = mesh_line.split("=")[-1].strip().strip('"')
            mesh_filepath = os.path.join(ogre_directory, mesh_filepath.replace(".mesh", ".mesh.xml"))

            # Print the mesh filepath
            print(f"Mesh Filepath: {mesh_filepath}")

            # Import the .mesh.xml file
            bpy.ops.ogre.import_mesh(filepath=mesh_filepath)

            # Get the imported mesh object
            mesh_object = bpy.context.selected_objects[0]
            mesh_objects.append(mesh_object)

            # Swap the coordinates to match the desired axis orientation
            coordinates = [coordinates[0], -coordinates[2], coordinates[1]]

            # Swap the rotation axes to match the desired axis orientation and rotate around -X axis
            rotations = [math.radians(rotations[0]), math.radians(rotations[2]), math.radians(rotations[1])]

            # Set the object's location based on the coordinates
            mesh_object.location = coordinates

            # Set the object's rotation based on the rotation values
            mesh_object.rotation_euler = rotations

            # Open the .odef file and read the content of the second line
            with open(odef_filepath, "r") as odef_file:
                odef_lines = odef_file.readlines()
                if len(odef_lines) > 1:
                    scale_values = odef_lines[1].strip().split(",")
                    if len(scale_values) == 3:
                        scale = [float(value) for value in scale_values]
                        mesh_object.scale = scale
                    else:
                        print("Error: Insufficient values in the second line of .odef file")

            # Set rotation to OGRE default
            mesh_object.rotation_euler.rotate_axis('X', math.radians(-90))

        except ValueError:
            print(f"Error: Unable to convert values to float in line {line_num}: {line}")

# Select all imported mesh objects
for mesh_object in mesh_objects:
    mesh_object.select_set(True)

# Set the active object to the last imported mesh object
bpy.context.view_layer.objects.active = mesh_objects[-1]
 
Back
Top