13 hours ago13 hr ๐งต [REQUEST] Noesis plugin for XMX/LDMX โ mesh corruption (index/stride issue)Hi,I'm reverse engineering a proprietary XMX / LDMX model format and I'm getting consistent mesh corruption when parsing it.I'd like help identifying the correct vertex layout and eventually building a Noesis plugin (.fmt or Python) to export to FBX/OBJ.๐ฆ Format info (confirmed)Header:4C 44 4D 58 โ "LDMX"Chunks found:EMAN (likely "NAME", reversed)MNXT (textures)VPRG (vertex block)INDX (index buffer)๐น Vertex dataFloat data detected:E0 FE 48 BE โ -0.196 48 E7 E7 BE โ -0.453 80 09 A7 3C โ 0.020Likely:Position (float3)followed by normals / UVs / etc.โ Unknown:vertex stride (suspected 32 / 36 / 40 bytes)๐น Index buffer58 44 4E 49 โ "INDX"Appears to be uint16 (little endian)Example:01 00 02 00 03 00โ ๏ธ ProblemMesh is corrupted:degenerate triangles like:22 00 22 00 22 00 25 00 25 00 25 00Result:collapsed facesbroken topology๐ง Suspected causesWrong vertex strideIncorrect index offsetVertex count mismatchPossible mixed endianness๐ TexturesPaths inside file:d:\FS\tex_zb01.zip/:xxxx.ddsโ DDS inside ZIP archives๐ฏ What I needHelp identifying correct vertex layout / strideEventually a Noesis plugin that:reads vertices correctlyfixes meshexports to FBX/OBJ๐งช I can provideSample filesHex dumpsTest resultsโ QuestionHas anyone seen a similar format or can help determine the correct vertex layout / stride?Best result, but it's not entirely correct; it always reads shitty padding or interspersed garbage. One or more triangles always come out wrong, but it's the best versionโthe one where the mesh turns out almost perfect...Almost all the files to be converted are located at: https://github.com/randalfcastro-tech/xmx-testing from inc_noesis import * import noesis import rapi import struct # ========================= # REGISTER # ========================= def registerNoesisTypes(): handle = noesis.register("XMX REAL FINAL STABLE", ".xmx") noesis.setHandlerTypeCheck(handle, xmxCheckType) noesis.setHandlerLoadModel(handle, xmxLoadModel) return 1 def xmxCheckType(data): return 1 if data[:4] == b"LDMX" else 0 # ========================= # FIND REAL REGION # ========================= def findRealMeshRegion(data): vprg = data.find(b"VPRG") if vprg == -1: return -1, -1 xtrv = data.find(b"XTRV", vprg) xdni = data.find(b"XDNI", xtrv) return xtrv, xdni # ========================= # VERTICES (REAL) # ========================= def parseVertices(data): xtrv, xdni = findRealMeshRegion(data) if xtrv == -1 or xdni == -1: print(" Missing real mesh blocks") return None start = xtrv + 4 end = xdni bs = NoeBitStream(data) bs.seek(start) verts = [] norms = [] uvs = [] stride = 32 MAX_VERTS = 200000 while bs.tell() + stride <= end and len(verts) < MAX_VERTS: try: x = bs.readFloat() y = bs.readFloat() z = bs.readFloat() nx = bs.readFloat() ny = bs.readFloat() nz = bs.readFloat() u = bs.readFloat() v = bs.readFloat() # sanity check (evita basura) if abs(x) > 10000 or abs(y) > 10000 or abs(z) > 10000: break verts.append((x,y,z)) norms.append((nx,ny,nz)) uvs.append((u,1.0-v)) except: break print(" Vertices:", len(verts)) return verts, norms, uvs # ========================= # INDICES (REAL) # ========================= def parseIndices(data): _, xdni = findRealMeshRegion(data) if xdni == -1: print(" No XDNI") return None bs = NoeBitStream(data) bs.seek(xdni + 4) indices = [] MAX_INDICES = 500000 while bs.tell() + 2 <= len(data) and len(indices) < MAX_INDICES: indices.append(bs.readUShort()) print(" Indices:", len(indices)) return indices # ========================= # STRIP โ TRIANGLES # ========================= RESTART = 0xFFFF def stripToTriangles(indices, vertCount): tris = [] flip = False for i in range(len(indices)-2): a = indices[i] b = indices[i+1] c = indices[i+2] if a == RESTART or b == RESTART or c == RESTART: flip = False continue if a == b or b == c or a == c: flip = not flip continue if a >= vertCount or b >= vertCount or c >= vertCount: continue if flip: tris.extend([b, a, c]) else: tris.extend([a, b, c]) flip = not flip return tris # ========================= # BUILD # ========================= def buildMesh(verts, norms, uvs, indices): tris = stripToTriangles(indices, len(verts)) # sanitizar clean = [] maxVert = len(verts) for i in tris: if not isinstance(i, int): continue if i < 0 or i >= maxVert: continue clean.append(i) clean = clean[:(len(clean)//3)*3] if len(clean) < 3: print(" No valid triangles") return vb = b''.join([struct.pack("3f", *v) for v in verts]) nb = b''.join([struct.pack("3f", *n) for n in norms]) ub = b''.join([struct.pack("2f", *u) for u in uvs]) rapi.rpgBindPositionBuffer(vb, noesis.RPGEODATA_FLOAT, 12) rapi.rpgBindNormalBuffer(nb, noesis.RPGEODATA_FLOAT, 12) rapi.rpgBindUV1Buffer(ub, noesis.RPGEODATA_FLOAT, 8) try: ib = struct.pack("%dH" % len(clean), *clean) except Exception as e: print(" PACK ERROR:", e) return rapi.rpgCommitTriangles( ib, noesis.RPGEODATA_USHORT, len(clean), noesis.RPGEO_TRIANGLE, 1 ) # ========================= # MAIN # ========================= def xmxLoadModel(data, mdlList): ctx = rapi.rpgCreateContext() parsed = parseVertices(data) if not parsed: return 0 verts, norms, uvs = parsed indices = parseIndices(data) if not indices: return 0 print(" Building mesh") buildMesh(verts, norms, uvs, indices) mdl = rapi.rpgConstructModel() mdlList.append(mdl) return 1 fmt_parser_xmx.py Edited 13 hours ago13 hr by Ralp1670
13 hours ago13 hr Supporter degenerated triangles: just a wild guess (without checking the details): maybe the indices of those models with that problem are DWords, not shorts?
12 hours ago12 hr Author 15 minutes ago, shak-otay said:degenerated triangles: just a wild guess (without checking the details): maybe the indices of those models with that problem are DWords, not shorts?๐ NOT DWORD (uint32)โ These models correctly use UINT16.
11 hours ago11 hr Supporter 1 hour ago, Ralp1670 said:No, son.Your disrespect forces me to leave the thread. Good luck.
11 hours ago11 hr Author 6 minutes ago, shak-otay said:Your disrespect forces me to leave the thread. Good luck.6 minutes ago, shak-otay said:Your disrespect forces me to leave the thread. Good luck.Friend, I thought you would ask questions or present arguments, but you are taking things for granted that simply aren't true; even if it sounds like a clever answer, it isn't backed by factsโand Iโve tried to be as polite as possible...
9 hours ago9 hr Localization 4 hours ago, Ralp1670 said:I'm reverse engineering a proprietary XMX / LDMX model format and I'm getting consistent mesh corruption when parsing it.Can you tell us which game (?) uses this .xmx format?
5 hours ago5 hr Supporter Since there is FFFF strip terminator it could be some xbox / 360 game. But now it's a history. Also i can't see any samples.
Create an account or sign in to comment