April 29Apr 29 Hi guys, I was wondering if anyone could help me process these models. The format isn’t documented, but I’ve been analysing the data from them for days. They don’t have a file extension, but internally they share certain similarities. I’ve included the details below, along with this link to some sample models: https://github.com/randalfcastro-tech/Spintires 1. General file structure The file is a hybrid format: Quote [BINARY HEADER] [MATERIAL XML] [BINARY MESH BLOCKS] ├── Nodes (meshes / submeshes) ├── Skeleton (bones) ├── Vertex Buffers ├── Index Buffers ├── Socket Points (optional) ├── Collision (cdt) 2. INITIAL HEADER First bytes: First 4 bytes: block size or pointer Endianness: Little-endian 3. XML BLOCK (Materials) Starts as embedded text: Quote <CombineXMesh> Materials contain: DiffuseMap → base texture NormalMap → relief SpecularMap → specular highlight IllumMap → emission (in some cases) Blending="alpha" → transparency Logical example: MaterialID ├── Diffuse ├── Normal ├── Specular ├── Flags (alpha / opaque) This is then mapped directly to FBX materials. 4. NODES / SUBMESHES Each model is divided into parts: Quote Example: Cabin Chassis Wheel Add-on Support Each node contains: [string name] [padding] [4x4 matrix (64 bytes)] 5. MATRICES (TRANSFORM) Quote Always: -16 floats (4x4 matrix) -64 bytes in total -Little-endian float32 Represent: -position -rotation -scale -or transformation relative to parent 6. SKELETON / BONES (VERY IMPORTANT) Found in advanced models (UAZ, crane): Quote Each bone: -BoneName -4x4 matrix (64 bytes) -Parent relationship (implicit or by order) Example: -BoneChassis -BoneAxleFront -BoneAxleRear -BoneArm1 -BoneArm2 Hierarchy: Parent → Child (implicit by order or XML structure) 7. SOCKET POINTS (LOCATORS) Quote Block: <SocketPoints> Contains: Name (e.g. SteeringWheel) Position (XYZ) In FBX: → these become Nulls / Helpers 8. VERTEX BUFFER (GEOMETRY) Quote This is the core of the format. Typical structure of a vertex - Based on what you found in the files: -Vector3 Position (12 bytes) -Vector3 Normal (12 bytes) -Vector2 UV (8 bytes) Optional: -Bone IDs (4 bytes or more) -Bone Weights (4–16 bytes) -Vertex Colour (4 bytes) 9. STRIDE (KEY) Quote Quote There are two main variants: Static mesh: -Position + Normal + UV & ≈ 32 bytes per vertex Skinned mesh: Position + Normal + UV + BoneIDs + Weights & ≈ 48–64 bytes per vertex 10. INDEX BUFFER (TRIANGLES) Quote Always at the end of each submesh: -uint16 indices -format: ushort (2 bytes) -order: triangle list -limited to 65,535 vertices per mesh Example: 0, 1, 2 2, 3, 0 11. COLLISION (CDT) Quote Separate block: -cdt -cdt_mesh Features: -simplified mesh -no materials -geometry only -used for physics In FBX: → exportable as a separate ‘Collision’ mesh 12. KEY RELATIONSHIPS Quote Mesh ↔ Bone -Vertex weights determine influence Skinning type: -2–4 bones per vertex Mesh ↔ Material -ID in XML -face groups per material Mesh ↔ Node -each submesh has its own matrix Bone hierarchy -defines animation (cranes, wheels, pistons) 13. STRUCTURE FOR NOESIS (.FMT)🧠 Here’s what you need to implement: Quote LOGICAL CLASSES: struct Mesh { string name; Matrix4 transform; vector<Vertex> vertices; vector<uint16> indices; int materialID; }; struct Vertex { float x, y, z; float nx, ny, nz; float u, v; uint8 boneIDs[4]; float boneWeights[4]; }; 🔄 PARSE FLOW: Quote Read header (size/pointer) Read materials XML Iterate through nodes: name matrix Read vertex buffer Detect stride automatically Read index buffer Read bones if present Read sockets Read CDT meshes 🙃"The Spintires/MudRunner .mesh format is a binary container with an XML header: Header: 4 bytes (Size) + XML block (Materials/Sockets). Body: Repeating blocks of: Name: String (possibly preceded by an Int32 length). Transform: 4x4 matrix (64 bytes, Little Endian floats). Counts: Int32 (VertexCount) + Int32 (IndexCount). Vertex Data: Floats. The stride is variable: 32 bytes for static meshes, 40–48 bytes for skinned meshes (contains bone weights). Index Data: Unsigned Shorts (16-bit) that form triangles.Coordinates: Y-axis up (Y-up), but the UVs require a flip along the V-axis ()." I would like a Python script for Noesis that works perfectly and is fully functional, so that I can export all the models.
April 29Apr 29 Supporter 5 hours ago, Ralp1670 said: Hi guys, Hi, Quote I would like a Python script for Noesis that works perfectly and is fully functional, so that I can export all the models. Yes. Of course. But how about doing it step by step? edit: mesh format appears to be simple Edited April 29Apr 29 by shak-otay
April 29Apr 29 Supporter trucks_kraz_carts_crane: edit: uaz469, H2O file and uvs: 0x47973 18582 Vb1 36 12 0xDD7 8047 020000 0x0 255 Bridge is more complex (18 sub meshes): Edited April 29Apr 29 by shak-otay
May 3May 3 Author On 4/29/2026 at 5:40 PM, shak-otay said: trucks_kraz_carts_crane: edit: uaz469, H2O file and uvs: 0x47973 18582 Vb1 36 12 0xDD7 8047 020000 0x0 255 Bridge is more complex (18 sub meshes): Quote from inc_noesis import * def registerNoesisTypes(): handle = noesis.register("Spintires Auto-Detector Pro", ".mesh") noesis.setHandlerTypeCheck(handle, meshCheckType) noesis.setHandlerLoadModel(handle, meshLoadModel) return 1 def meshCheckType(data): return 1 # Attempt to load any .mesh file def meshLoadModel(data, mdlList): rapi.rpgCreateContext() bs = NoeBitStream(data) # 1. START SCAN (Searches for data marker 11 00 00 00) v_start = data.find(b'\x11\x00\x00\x00') if v_start != -1: v_start += 4 else: # Fallback: search after closing the XML file xml_end = data.find(b'>', data.find(b'<CombineXMesh')) v_start = (xml_end + 16) if xml_end != -1 else 0x100 # 2. STRIDE DETECTION (Differentiates simple from complex meshes) # Based on your captures: small files are usually 28, large ones 36/40 stride = 36 if len(data) < 40000: stride = 28 # Correction of the "bytes vs string" error filename = rapi.getInputName().lower() if "wheel" in filename or "tire" in filename: stride = 40 # 3. LOCALIZE FACES (Incremental pattern 0,1,2...) i_start = data.find(b'\x00\x00\x01\x00\x02\x00') try: # 4. LOAD VERTICES # If we find faces, we read up to that point. Otherwise, we read to the end. limit = i_start if i_start != -1 else len(data) v_count = (limit - v_start) // stride if v_count > 0: bs.seek(v_start) v_buffer = bs.readBytes(v_count * stride) # We link Position and UVs (UV always at 12 according to your photos) rapi.rpgBindPositionBufferOfs(v_buffer, noesis.RPGEODATA_FLOAT, stride, 0) rapi.rpgBindUV1BufferOfs(v_buffer, noesis.RPGEODATA_FLOAT, stride, 12) #5. UPLOAD FACES if i_start != -1: bs.seek(i_start) i_count = (len(data) - i_start) // 2 i_buffer = bs.readBytes(i_count *2) rapi.rpgCommitTriangles(i_buffer, noesis.RPGEODATA_USHORT, i_count, noesis.RPGEO_TRIANGLE, 1) else: # If it does not detect faces, it creates points so that the viewer shows the shape rapi.rpgCommitTriangles(None, noesis.RPGEODATA_USHORT, 0, noesis.RPGEO_POINTS, 1) mdl = rapi.rpgConstructModel() mdlList.append(mdl) print("File loaded: Offset", hex(v_start), "| Stride", stride, "| Verts", v_count) return 1 except: return 0 return 0 I can upload more samples... Okay goodman, I did this. It's not perfect; it doesn't read the strides of each model, and it doesn't display the mesh, but it recognizes the format and allows exporting.It allows exporting, but it doesn't export the model because it's missing the offset and stride readings... I changed all the extensions to .mesh since they didn't come with them. If you could give me the perfect script that displays the mesh with Noesis and allows exporting, that would be great. I could also test it with all the files and see which one fails. Perhaps structures like the bridge, because they have several meshes, but even so, it could auto-detect those meshes, the starting offset, and apply the stride, which, as I see in the images, is 28 and 36, although there will surely be 40 or less than 28. The UV position in both images is 12; I don't know if it's constant in other files. Having it detect the offset and be universal would require a well-written Python script by Reshax professionals. Perhaps an expert already has it; well, that would be helpful to me and others as well. 🙂 Edited May 3May 3 by Ralp1670
May 3May 3 Supporter Indentation is lost when you use "quote" instead of "code" tags. edit: counts for uaz469 (FI count= 3x 6194 = 18582): Edited May 3May 3 by shak-otay
May 4May 4 Author 19 hours ago, shak-otay said: Indentation is lost when you use "quote" instead of "code" tags. edit: counts for uaz469 (FI count= 3x 6194 = 18582): I see, the models look good, but my question is, what do you need to create a converter with a built-in parser for the files I uploaded to GitHub? It needs to read the mesh and its UVs; the skeleton isn't necessary. And it needs to work with Noesis, with a filename like fmt_Spintires_mesh.py
May 4May 4 Supporter 2 hours ago, Ralp1670 said: , but my question is, what do you need to create a converter with a built-in parser for the files I uploaded to GitHub? Good start would be to have your code in code tags, as I mentioned already. Quote And it needs to work with Noesis, Everybody's needs are different - I usually use my Make_H2O (or the Make_obj) project for quicker approaches. Here's the H2O file for the models_chinacanyonbridge_02: models_chinacanyonbridge_02_00000.zip
May 4May 4 Author 8 hours ago, shak-otay said: Good start would be to have your code in code tags, as I mentioned already. Everybody's needs are different - I usually use my Make_H2O (or the Make_obj) project for quicker approaches. Here's the H2O file for the models_chinacanyonbridge_02: models_chinacanyonbridge_02_00000.zip 1.02 kB · 0 downloads I completely understand your point, but there's an important nuance here: when code is copied from a quote tag in files Spintires , the indentation is often already lost in the rendered content itself. In other words, it's not just a problem with how it's pasted; the spaces/tabs no longer exist in the text. Quote Maybe this will 👉 Binary mesh format with XML-like header Actual structure: [Binary Header] <CombineXMesh> <Material ... /> </CombineXMesh> [Binary Data] Includes: Vertices (binary) Indices Bones (RootBone_cdt, etc.) Mesh names Data buffers 🧠 3. XML Tags and Sections (Actual Headers) Based on your files: <CombineXMesh> → Main container for engine-optimized meshes <Material> → Defines textures (diffuse, normal, specular) Additional key elements: cdt_mesh 👉 Stands for Collision Data 👉 Low-poly invisible meshes used for physics 👉 Typically: Stride ≈ 12 bytes Should Ignore or separate in the parser 🦴 4. Skeleton Tags (Bones) Extracted from your binaries: BoneChassis / BoneRoot → vehicle center BoneCabin / BoneBody → cabin BoneAxle / BoneSuspension → axles/suspension SteeringWheel → steering wheel BoneExhaust → exhaust (effects) ✔️ These are NOT XML, they are in binary as strings ⚙️ 5. Actual Structure (the important part) The actual hierarchy comes from: Bones Buffers Internal Transformations Logical example: CombineXMesh └── Material └── MeshChunk ├── Vertices (binary) ├── Indices ├── Weights └── Bones 🔢 6. The “12” and binary markers (very important) Actual patterns found: 11 00 00 00 → start of vertex block 12 initial bytes → float X, Y, Z 00 00 01 00 02 00 → start of indices (faces) 02 00 00 00 → index type (16-bit short) ✔️ This is what you really need to parse https://expeditions-guides.saber.games/truck_modding/tags_and_attributes_of_trucks/ ModdingGuide_Mudrunner.pdf Quote The guide lists these tags; if this is what's missing to create a complete Noesis parser, help me out and we'll be doing something great: 1. XML Structure Tags (Headers) These appear at the beginning of the files or in the .xml files that accompany the mesh. <CombineXMesh>: The root container for all 3D data. <Material>: Defines the visual properties of a part of the mesh. <PhysicsModel>: Container for physics and collisions. <Body>: Defines a physical part with mass and gravity. <SocketPoints> / <SocketPoint>: Anchor points for lights, smoke, and accessories. <Truck> / <TruckData>: Specific vehicle configuration. <Driver>: Defines the driver's position and bones. <OcclusionMap> / <OcclusionVolume>: Data for pre-calculated ambient shadows. 2. Data Attributes (Properties) These are the parameters that go within the XML tags. DiffuseMap: Path to the main texture (__d). NormalMap: Path to the bump map (__n). SpecularMap: Path to the shine map (__s). MeshParts: Identifies which parts of the binary use that material. Pos / Dir / Axis: Position and direction coordinates (X, Y, Z). ParentFrame: Indicates which bone that part depends on. _template: Reference to a predefined template. File: Path to an inherited or external file. 3. Geometry Markers (Binary Tags) These are the "invisible tags" we found by analyzing the Hexadecimal. 11 00 00 00: Start marker of a vertex block. 00 00 01 00 02 00: Start marker of the face table (indices). 02 00 00 00: Indicates that the indices are of type Short (2 bytes). Vb1: Internal label of Vertex Buffer. Ib1: Internal label of Index Buffer. cdt_mesh: Indicates that the following block is for collisions, not visual. 4. Standard Bone Names (Skeleton Tags) Used to animate the truck, according to pages 6, 12 and 33 of the guide. BoneChassis: The main frame. BoneAxle1, 2, 3: The wheel axles. BoneCabin: The truck cab. BoneSteering: The steering system. BoneExhaust: The exhaust pipe. BoneSuspension: Shock absorber mounts. SteeringWheel: The physical steering wheel. 5. Special Suffixes (Postfix Tags) MudRunner uses the object name to decide how to treat it. _cdt: The object is invisible and is used for collisions. _cockpit: Mesh that is only visible from the interior camera. _windshield: Windshield glass (for rain/mud effects). _high / _low: Levels of detail (LOD). 6. Map Tags (Terrain)(Geometry): Terrain mesh (Heightmap). (Tint): Soil color map. (Mud): Definition of areas with deformable mud. (Water): Water planes and stream flow. Additional information: In the GitHub files, the most important tag for the script is CombineXMesh 😄 because after that tag is closed in the text, the binary markers like 11 00 00 00 begin to appear. With this, I believe we can begin creating the parser. I think we're conflating two different things here. The real problem isn't how the code looks, but the mesh files I'm working with. When the content is already "flattened" (without spaces or tabs), it's not a matter of how it's copied or using code tags: that information simply no longer exists in the file/text, and it can't be retrieved as is. Therefore, while putting the code in appropriate tags can aid readability, it doesn't solve this case. If the goal is to work with this data (for Noesis or a converter), the correct approach is different: Completely ignore the indentation (it's irrelevant to the parser). Reconstruct the structure from the tags themselves (if it's XML), using a formatter or directly with the parser. Or work from a base/intermediate format (H2O/OBJ, as you mentioned) if that allows for faster results. In other words, it's not a presentation problem, but rather a problem of how the actual content of the file is interpreted. In short: if the file is already flattened, it can't be reverted as is; the structure must be regenerated logically. By the way, does your parser actually need the complete XML hierarchy, or are you trying to rebuild the meshes from already exported data (like H2O/OBJ)? By the way, are you trying to parse those XML files directly to integrate them into Noesis, or are you using an intermediate pipeline like H2O/OBJ as you mentioned? Edited May 4May 4 by Ralp1670
May 4May 4 Author 8 hours ago, shak-otay said: Good start would be to have your code in code tags, as I mentioned already. Everybody's needs are different - I usually use my Make_H2O (or the Make_obj) project for quicker approaches. Here's the H2O file for the models_chinacanyonbridge_02: models_chinacanyonbridge_02_00000.zip 1.02 kB · 0 downloads Here's a working script for when you enter the data for Noesis. It doesn't show the file, but many models have so many pieces that they caused Noesis to crash, so I think it has a submesh limit. But I think that with the information I've provided you'll get it working once and for all. It is much lighter on memory and prevents the program from crashing. 🙂 from inc_noesis import * import struct import you def registerNoesisTypes(): handle = noesis.register("MudRunner - Spintires", ".mesh") noesis.setHandlerTypeCheck(handle, meshCheckType) noesis.setHandlerLoadModel(handle, meshLoadModel) return 1 def meshCheckType(data): return 1 if len(data) > 100 else 0 def meshLoadModel(data, mdlList): #1. CREATE A SINGLE MASTER CONTEXT (Fixes MAX_CONCURRENT error) rapi.rpgCreateContext() rapi.rpgSetOption(noesis.RPGOPT_TRIWINDBACKWARD, 1) # Axes according to technical manual: Y is Up rapi.rpgSetTransform(NoeMat43((NoeVec3((1, 0, 0)), NoeVec3((0, 0, 1)), NoeVec3((0, 1, 0)), NoeVec3((0, 0, 0))))) input_path = rapi.getInputName() log_path = input_path + "_REPORTE_V124.txt" with open(log_path, "w", encoding="utf-8") as log: log.write("TECHNICAL REPORT V124\nFile: " + os.path.basename(input_path) + "\n" + "="*30 + "\n") #2. BONE SCAN (Skeleton) bones = [] bone_tags = [b'BoneChassis', b'BoneAxle', b'BoneCabin', b'BoneSteering', b'BoneRoot', b'BoneExhaust'] for tag in bone_tags: ptr = data.find(tag) while ptr != -1: name_end = data.find(b'\x00', ptr) b_name = data[ptr:name_end].decode("utf-8", "ignore") bones.append(NoeBone(len(bones), b_name, NoeMat43(), None, -1)) ptr = data.find(tag, name_end + 1) #3. SUB-MESH PROCESSING (VISUALS AND COLLISION) cur = 0 pieces = 0 while curr < len(data): tag_pos = data.find(b'mesh', curr) if tag_pos == -1: break try: # MudRunner saves: [i_count][v_count] before the name i_count = struct.unpack("<I", data[tag_pos-12 : tag_pos-8])[0] v_count = struct.unpack("<I", data[tag_pos-8 : tag_pos-4])[0] # Identify type by name (Visual vs Collision) is_collision = b"cdt_mesh" in data[tag_pos:tag_pos+20] stride = 12 if is_collision else 36 if not is_collision and any(x in input_path.lower() for x in ["uaz", "kraz", "truck"]): stride = 40 v_marker = data.find(b'\x11\x00\x00\x00', tag_pos) if v_marker != -1 and v_count > 0: v_start = v_marker + 4 i_start = data.find(b'\x00\x00\x01\x00\x02\x00', v_start) if i_start != -1: v_buffer = data[v_start : i_start - 8] rapi.rpgBindPositionBufferOfs(v_buffer, noesis.RPGEODATA_FLOAT, stride, 0) if not is_collision: rapi.rpgBindUV1BufferOfs(v_buffer, noesis.RPGEODATA_FLOAT, stride, 12) i_buffer = data[i_start : i_start + (i_count * 2)] # Add the part to the master context rapi.rpgCommitTriangles(i_buffer, noesis.RPGEODATA_USHORT, i_count, noesis.RPGEO_TRIANGLE, 1) pieces += 1 log.write("[OK] Piece {0}: V {1} | I {2} | Stride {3}\n".format(pieces, v_count, i_count, stride)) curr = tag_pos + 10 except: curr = tag_pos + 10 # 4. FINAL CONSTRUCTION if pieces > 0: try: mdl = rapi.rpgConstructModel() if bones: mdl.setBones(bones) mdlList.append(mdl) log.write("SUCCESS: Model assembled with {0} pieces.\n".format(pieces)) except: # If the solid fails, we force point rescue mode rapi.rpgCreateContext() rapi.rpgBindPositionBufferOfs(data[0x200:], noesis.RPGEODATA_FLOAT, 36, 0) rapi.rpgCommitTriangles(None, noesis.RPGEODATA_USHORT, 0, noesis.RPGEO_POINTS, 1) mdlList.append(rapi.rpgConstructModel()) log.write("WARNING: Solid construction failure. Loading point cloud.\n") rapi.setPreviewOption("set_cam", "1") return 1 Edited May 4May 4 by Ralp1670
May 4May 4 Supporter Wow, you again killed the indentations! Great. Here's how it should look (does work for uaz469 only). It's your first code with indentations added manually (chose extension .spi, because .mesh appears too frequently): from inc_noesis import * def registerNoesisTypes(): handle = noesis.register("Spintires Auto-Detector Pro", ".spi") noesis.setHandlerTypeCheck(handle, meshCheckType) noesis.setHandlerLoadModel(handle, meshLoadModel) return 1 def meshCheckType(data): return 1 # Attempt to load any .mesh file def meshLoadModel(data, mdlList): rapi.rpgCreateContext() bs = NoeBitStream(data) print("Most parameters set manually, for uaz469 only") # 1. START SCAN (Searches for data marker 11 00 00 00) v_start = data.find(b'\x11\x00\x00\x00') print(hex(v_start)) v_start = 0xDD3# set manually, because first finding is not suiting if v_start != -1: v_start += 4 else: # Fallback: search after closing the XML file xml_end = data.find(b'>', data.find(b'<CombineXMesh')) v_start = (xml_end + 16) if xml_end != -1 else 0x100 # 2. STRIDE DETECTION (Differentiates simple from complex meshes) # Based on your captures: small files are usually 28, large ones 36/40 stride = 36 if len(data) < 40000: stride = 28 # Correction of the "bytes vs string" error filename = rapi.getInputName().lower() print(filename) if "wheel" in filename or "tire" in filename: stride = 40 print("stride", stride) stride= 36#set manually # 3. LOCALIZE FACES (Incremental pattern 0,1,2...) i_start = data.find(b'\x00\x00\x01\x00\x02\x00') print("i_start", hex(i_start)) i_start= 0x47973#same problem as above try: # 4. LOAD VERTICES # If we find faces, we read up to that point. Otherwise, we read to the end. if i_start != -1: limit = i_start else: limit = len(data) v_count = (limit - v_start) // stride print("v_count, calculated", v_count) if v_count > 0: bs.seek(v_start) v_buffer = bs.readBytes(v_count * stride) # We link Position and UVs (UV always at 12 according to your photos) rapi.rpgBindPositionBufferOfs(v_buffer, noesis.RPGEODATA_FLOAT, stride, 0) rapi.rpgBindUV1BufferOfs(v_buffer, noesis.RPGEODATA_FLOAT, stride, 12) #5. UPLOAD FACES if i_start != -1: bs.seek(i_start) i_count = (len(data) - i_start) // 2 print("i_count, FI_start", i_count, hex(bs.tell())) i_count= 18528#correction i_buffer = bs.readBytes(i_count *2) rapi.rpgCommitTriangles(i_buffer, noesis.RPGEODATA_USHORT, i_count, noesis.RPGEO_TRIANGLE, 1) else: # If it does not detect faces, it creates points so that the viewer shows the shape rapi.rpgCommitTriangles(None, noesis.RPGEODATA_USHORT, 0, noesis.RPGEO_POINTS, 1) mdl = rapi.rpgConstructModel() mdlList.append(mdl) print("File loaded: Offset", hex(v_start), "| Stride", stride, "| Verts", v_count) return 1 except: return 1 And here's my Make_H2O tool (tested a few models only, may badly fail on others): Make_H2O_SpinTires.zip
May 4May 4 Author Man, you've written a script for a single file; all the parameters are handwritten! And it only works with one file. I thought it would be a complete script that finds the offsets of vertices and faces, but wow, that's a really well-made Python script, man. Edited May 4May 4 by Ralp1670
May 4May 4 Author 56 minutes ago, shak-otay said: Wow, you again killed the indentations! Great. Here's how it should look (does work for uaz469 only). It's your first code with indentations added manually (chose extension .spi, because .mesh appears too frequently): from inc_noesis import * def registerNoesisTypes(): handle = noesis.register("Spintires Auto-Detector Pro", ".spi") noesis.setHandlerTypeCheck(handle, meshCheckType) noesis.setHandlerLoadModel(handle, meshLoadModel) return 1 def meshCheckType(data): return 1 # Attempt to load any .mesh file def meshLoadModel(data, mdlList): rapi.rpgCreateContext() bs = NoeBitStream(data) print("Most parameters set manually, for uaz469 only") # 1. START SCAN (Searches for data marker 11 00 00 00) v_start = data.find(b'\x11\x00\x00\x00') print(hex(v_start)) v_start = 0xDD3# set manually, because first finding is not suiting if v_start != -1: v_start += 4 else: # Fallback: search after closing the XML file xml_end = data.find(b'>', data.find(b'<CombineXMesh')) v_start = (xml_end + 16) if xml_end != -1 else 0x100 # 2. STRIDE DETECTION (Differentiates simple from complex meshes) # Based on your captures: small files are usually 28, large ones 36/40 stride = 36 if len(data) < 40000: stride = 28 # Correction of the "bytes vs string" error filename = rapi.getInputName().lower() print(filename) if "wheel" in filename or "tire" in filename: stride = 40 print("stride", stride) stride= 36#set manually # 3. LOCALIZE FACES (Incremental pattern 0,1,2...) i_start = data.find(b'\x00\x00\x01\x00\x02\x00') print("i_start", hex(i_start)) i_start= 0x47973#same problem as above try: # 4. LOAD VERTICES # If we find faces, we read up to that point. Otherwise, we read to the end. if i_start != -1: limit = i_start else: limit = len(data) v_count = (limit - v_start) // stride print("v_count, calculated", v_count) if v_count > 0: bs.seek(v_start) v_buffer = bs.readBytes(v_count * stride) # We link Position and UVs (UV always at 12 according to your photos) rapi.rpgBindPositionBufferOfs(v_buffer, noesis.RPGEODATA_FLOAT, stride, 0) rapi.rpgBindUV1BufferOfs(v_buffer, noesis.RPGEODATA_FLOAT, stride, 12) #5. UPLOAD FACES if i_start != -1: bs.seek(i_start) i_count = (len(data) - i_start) // 2 print("i_count, FI_start", i_count, hex(bs.tell())) i_count= 18528#correction i_buffer = bs.readBytes(i_count *2) rapi.rpgCommitTriangles(i_buffer, noesis.RPGEODATA_USHORT, i_count, noesis.RPGEO_TRIANGLE, 1) else: # If it does not detect faces, it creates points so that the viewer shows the shape rapi.rpgCommitTriangles(None, noesis.RPGEODATA_USHORT, 0, noesis.RPGEO_POINTS, 1) mdl = rapi.rpgConstructModel() mdlList.append(mdl) print("File loaded: Offset", hex(v_start), "| Stride", stride, "| Verts", v_count) return 1 except: return 1 And here's my Make_H2O tool (tested a few models only, may badly fail on others): Make_H2O_SpinTires.zip 85.77 kB · 1 download I thought it would automatically find all the submesh offsets, assigning vertices and faces, and also assign strides according to the model, and then display it in Noesis with a skeleton. I added tags so it would display with its names, etc. Great work, my friend. 😅 Edited May 4May 4 by Ralp1670
May 4May 4 Supporter 1) you didn't get the point. It's your script which I made working by adding indentations (which you didn't want to correct for unknown reasons). And I've set the params for one model only because I have my own solution and didn't want to waste more time on this. It was just to show how a working Noesis script would look like. 2) You are on my ignore list. Edited May 4May 4 by shak-otay
May 4May 4 32 minutes ago, Ralp1670 said: WOW, man, you've written a script for a single file; all the parameters are handwritten! And it only works with one file, WOW!!!!!!!! I thought it would be a complete script that finds the offsets of vertices and faces, but wow, that's a really well-made Python script, man. 😅 Your sarcastic attitude isn't in the spirit of this forum. People have given you some assistance, which they don't have to do, so the least you could do is be grateful for the help. Nobody is here to do absolutely everything for you, and trying things yourself will help you to learn for future projects.
May 4May 4 Author I meant it humorously, not sarcastically, and not to offend anyone. If that's the reaction my comments caused, I apologize from the start. The point is that we could accomplish what I mentioned in the last comment, not to offend anyone, because I do value the help and your response.
May 4May 4 Author 1 hour ago, shak-otay said: 1) you didn't get the point. It's your script which I made working by adding indentations (which you didn't want to correct for unknown reasons). And I've set the params for one model only because I have my own solution and didn't want to waste more time on this. It was just to show how a working Noesis script would look like. 2) You are on my ignore list. Hey man, I didn't mean to offend or disrespect you. I appreciate your work and the hours you've dedicated to helping me. I didn't mean it in a bad way; I respect you. I was just expecting something more complex. You could continue helping me; I'm sure that with your help we can automatically calculate the stride detector for each model, just like the faces and vertices. I'm sorry if it sounded offensive, but that wasn't my intention. I sincerely apologize. I thought you'd laugh at the comment, but I see now that it was a misunderstanding.
May 5May 5 8 hours ago, Ralp1670 said: I thought it would automatically find all the submesh offsets, assigning vertices and faces, and also assign strides according to the model, and then display it in Noesis with a skeleton. I added tags so it would display with its names, etc. Great work, my friend. 😅 Man, you are so lucky, because if it were me dealing with the report, you would have been banned already.
Create an account or sign in to comment