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