From 788fcd96771d07a86e245de493b555ae26708f71 Mon Sep 17 00:00:00 2001 From: modeco80 Date: Sun, 5 Jan 2025 20:36:48 -0500 Subject: [PATCH] hexpat: Add hexpat and MRP Python script for .msh files Documentation is always nice. --- hexpat/mesh_researcher_pro_msh.py | 96 +++++++++++++++++++++++++++++++ hexpat/msh.hexpat | 81 ++++++++++++++++++++++++++ 2 files changed, 177 insertions(+) create mode 100644 hexpat/mesh_researcher_pro_msh.py create mode 100644 hexpat/msh.hexpat diff --git a/hexpat/mesh_researcher_pro_msh.py b/hexpat/mesh_researcher_pro_msh.py new file mode 100644 index 0000000..7a4f45b --- /dev/null +++ b/hexpat/mesh_researcher_pro_msh.py @@ -0,0 +1,96 @@ +# +# EuropaTools +# +# (C) 2021-2022 modeco80 +# +# SPDX-License-Identifier: GPL-3.0-or-later +# + +# This is a Model Researcher Pro script which can be used +# to automatically display .msh meshes inside of it. +# +# While I haven't written a native exporter yet, this should *not* +# be considered a replacement for that, especially given the issues: +# - python (its slow as balls) +# - Model Researcher is Windows-only +# - Generally this is just a quick hack and it shows in code quality. + +import mrp + +bf = mrp.get_bfile() + +# read '\r\n' terminated string +# why the fuck did they do this +def Storage_ReadLine(): + ret = "" + rntest = b"" + while True: + a = bf.reads("1s")[0] + + if a == b'\r' or a == b'\n': + rntest += a + if rntest == b'\r\n': + return ret + else: + ret += a.decode('ascii') + +def ReadMeshBlock(readCallback): + count = bf.readShort() + data = None + if count != 0: + data = readCallback(count) + return (count, data) + +def ReadVec3(count): + return bf.read(count * (3 * 4)) + +def ReadVertexColors(count): + return bf.read(count * 4) + +def ReadFaces(count): + return bf.read(count * (3 * 2)) + +# main + +# Make sure this is a MESH and it's a 2.0 version +# 1.2 version is actually text-format and I can't be bothered +# to support it. +if Storage_ReadLine() != 'MESH': + raise ValueError('not a Europa msh') +if Storage_ReadLine() != '2.0': + raise ValueError('not a Europa msh 2.0 (this parses only binary version 2.0)') + +mesh_count = bf.readShort() +for i in range(0, mesh_count): + mtlFilename = Storage_ReadLine() + + # read some pieces + verts = ReadMeshBlock(ReadVec3) + vertcolors = ReadMeshBlock(ReadVertexColors) + normals = ReadMeshBlock(ReadVec3) + + # Need to manually ReadMeshBlock() here + # since we need vert count to read the UV block + uvCount = bf.readShort() + uv = None + if uvCount != 0: + print(f' {uvCount} uvs') + uv = [] + for i in range(0, uvCount): + uv_bytes = bf.read(verts[0] * (2 * 4)) + uv.append(uv_bytes) + + faces = ReadMeshBlock(ReadFaces) + + # Add it and set everything up, add verts, faces, + # and normals/UV if provided + mesh = mrp.create_mesh(mtlFilename) + mesh.set_vertices(verts[1], tp="Float") + mesh.set_faces(faces[1], tp="Short") + if normals[0] != 0: + mesh.set_normals(normals[1], tp="Float") + if uvCount != 0: + mesh.set_uvs(uv[0], tp="Float") + print(f'Read mesh {mtlFilename}') + +mrp.render("All") diff --git a/hexpat/msh.hexpat b/hexpat/msh.hexpat new file mode 100644 index 0000000..eeef44d --- /dev/null +++ b/hexpat/msh.hexpat @@ -0,0 +1,81 @@ +// +// EuropaTools +// +// (C) 2021-2025 modeco80 +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +import std.io; +import std.mem; + +// Fundamental Types +struct Vec3 { + float x; + float y; + float z; + + // std::print("Vec3: {:4.4f}, {:4.4f}, {:4.4f}", + // x, + // y, + // z + //); +}; + +struct Uvf { + float u; + float v; +}; + +struct Face { + u16 faceX; + u16 faceY; + u16 faceZ; +}; + +// Europa .MSH types + +// a generic block +struct tMeshBlock { + u16 count; + + // count == 0 means no items in block + if(count != 0) { + std::print("Block with {:6d} items", count); + Item items[count]; + } +}; + +// Used for vertex colors. +struct VertexColor { + u8 r; + u8 g; + u8 b; + u8 a; +}; + +struct UvBlockEntry { + Uvf uvs[parent.parent.vertexBlock.count]; +}; + +struct MeshPiece { + char mtl_filename[while(std::mem::read_unsigned($-1, 1) != '\n')]; + tMeshBlock vertexBlock; + tMeshBlock vertexColorBlock; + tMeshBlock normalBlock; + tMeshBlock uvBlock; + tMeshBlock faceBlock; +}; + +struct MshFile { + // This should just be read as lines in any real code. + // I just do this to feel something + char magic[while(std::mem::read_unsigned($-1, 1) != '\n')]; + char version[while(std::mem::read_unsigned($, 1) != '\n')]; + char dummy; // fuckkk dude + + // There is a meshpiece per material change I guess. + tMeshBlock meshPieceBlock; +}; + +MshFile msh @ 0; \ No newline at end of file