hexpat: Add hexpat and MRP Python script for .msh files
Documentation is always nice.
This commit is contained in:
parent
73e16a8b72
commit
788fcd9677
2 changed files with 177 additions and 0 deletions
96
hexpat/mesh_researcher_pro_msh.py
Normal file
96
hexpat/mesh_researcher_pro_msh.py
Normal file
|
@ -0,0 +1,96 @@
|
|||
#
|
||||
# EuropaTools
|
||||
#
|
||||
# (C) 2021-2022 modeco80 <lily.modeco80@protonmail.ch>
|
||||
#
|
||||
# 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")
|
81
hexpat/msh.hexpat
Normal file
81
hexpat/msh.hexpat
Normal file
|
@ -0,0 +1,81 @@
|
|||
//
|
||||
// EuropaTools
|
||||
//
|
||||
// (C) 2021-2025 modeco80 <lily.modeco80@protonmail.ch>
|
||||
//
|
||||
// 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<Item> {
|
||||
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<Vec3> vertexBlock;
|
||||
tMeshBlock<VertexColor> vertexColorBlock;
|
||||
tMeshBlock<Vec3> normalBlock;
|
||||
tMeshBlock<UvBlockEntry> uvBlock;
|
||||
tMeshBlock<Face> 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<MeshPiece> meshPieceBlock;
|
||||
};
|
||||
|
||||
MshFile msh @ 0;
|
Loading…
Reference in a new issue