-
Notifications
You must be signed in to change notification settings - Fork 1
MESH
MESH files contain 3D model data. MESH files on all platforms contain vertex coordinates. Only Gamecube MESH files contain texture coordinate and normal vector data, while PS2 MESH files do not. It is possible that this data is stored in SKIN files instead.
This file format typically ends in ".TMESH".
struct Mesh_Z {
TransformationHeader transform;
uint32_t num_vertices; // Number of vertices
Vector3 vertices[num_vertices]; // Vertex data
uint32_t num_texcoords; // Number of texture coordinates (Always 0 on Playstation 2)
Vector2 texcoords[num_texcoords]; // Texture coordinate data
uint32_t num_normals; // Number of normals (Always 0 on Playstation 2)
Vector3 normals[num_normals]; // Normal data
uint32_t num_strips; // Number of strips
Strip strips[num_strips]; // Strip data (Note that they are not equally sized)
// if `transform.subtype` is 4:
int32_t vertex_groups[num_strips]; // Vertex groups applied per-strip (face groups?)
// end if
uint32_t num_stripdata; // Number of StripData elements (should be equal to num_strips on Gamecube, or 0 on Playstation2)
StripData stripdata[num_stripdata]; // StripData (Note that they are not equally sized)
uint32_t num_materials; // The number of materials that this mesh refers to
uint32_t materials[num_materials]; // Material IDs; referred to by CRC32 hash of filename
uint32_t num_sphere_shapes;
MeshSphere sphere_shapes[sphere_shapes]; // Unknown data; each element is 16 bytes
uint32_t num_cuboid_shapes;
MeshCuboid cuboid_shapes[num_cuboid_shapes]; // Unknown data; each element is 80 bytes
uint32_t num_cylinder_shapes;
MeshCylinder cylinder_shapes[num_cylinder_shapes]; // Unknown data; each element is 36 bytes
// Possibly GCN only?
// uint32_t unk_footer4_num; // Always 0 (until proven otherwise); might be another shape type?
// uint32_t strip_order_num;
// uint32_t strip_order[strip_order_num]; // Order of triangle strips; unsure of purpose
}
struct ElementData {
uint16_t texcoord_id;
uint16_t normal_id;
};
If Mesh.transform.subtype
is 4, then num_cuboid_shapes
, num_sphere_shapes
, and num_cylinder_shapes
will be 0. My assumption is that a subtype of 4 indicates that the mesh will generate a convex hull from the mesh's vertices.
struct MeshSphere {
Vector3 center;
float radius;
}
A spherical collision shape.
struct MeshCuboid {
Mat4x4 transform;
char junk[16];
}
A cuboid collision shape. The base cuboid is a rectangular prism with the extents {-1, 0, -1}
to {1, 1, 1}
, and MeshCuboid.transform
transforms this cuboid accordingly.
struct MeshCylinder {
Vector3 position;
float height;
Vector3 normal;
char junk[4];
float radius;
}
A cylindrical collision shape. The cylinder has its base position at MeshCylinder.position
, and extends towards the direction specified by MeshCylinder.normal
.
The model is divided into strips; a strip is just a list of elements that are drawn as a triangle strip; for example, consider a strip with elements [0, 1, 3, 5, 8]
. Every 3 elements of the strip will be drawn as a triangle on screen; in this case, triangles will be drawn for (0, 1, 3)
, (1, 3, 5)
, and (3, 5, 8)
. The order that the triangles will be drawn will differ for every other triangle; this is because, on screen, the front of a triangle is drawn in clock-wise order, but drawing triangle strips would reverse the order every other triangle.
If tri_order
is 1
, then the triangles would be drawn in the order [(0, 3, 1), (1, 3, 5), (3, 8, 5)]
. If tri_order
is 2
, then the triangles would be drawn in the oder [(0, 1, 3), (1, 5, 3), (3, 5, 8)]
.
struct Strip {
uint32_t num_elements; // The number of vertices in this strip
uint16_t vertex_id[num_elements]; // List of vertex IDs
uint32_t material; // material index.
uint32_t tri_order; // The order that the triangles will be built in (1 or 2)
};
If TMesh.transform.subtype
is 0, Strip.material
this is always less than TMesh.num_materials
.
struct StripData {
uint32_t num_elements; // Number of elements (should be equivalent to corresponding Strip.num_elements
ElementData elements[num_elements]; // Contains texture coordinate and normal information
};
Assume you have the following data:
Vector3 vertices[]; // size of these is ignored for now
Vector3 normals[];
Vector2 texcoords[];
uint32_t num_strips;
Strip strips[num_strips];
StripData stripdata[num_strips];
To build a 3d model out of this data, the following process must be performed:
for (uint32_t i = 0; i < num_strips; ++i) {
Strip *strip = &strips[i];
StripData *data = &stripdata[i];
uint32_t triorder = strip->tri_order;
for (uint32_t j = 0; j < strip->num_elements-2; ++j) {
// The order that the vertices will be built will depend on the tri_order of the strip
uint32_t index0 = j;
uint32_t index1, index2;
if (j % 2 == 0) {
index1 = 3 - triorder + j;
index2 = triorder + j;
} else {
index1 = triorder + j;
index2 = 3 - triorder + j;
}
// Build face
Vector3 face_vert[3] = {
vertices[strip->vertex_id[index0]],
vertices[strip->vertex_id[index1]],
vertices[strip->vertex_id[index2]],
};
Vector2 face_texcoords[3] = {
texcoords[data->elements[index0].texcoord_id],
texcoords[data->elements[index1].texcoord_id],
texcoords[data->elements[index2].texcoord_id],
};
Vector3 face_normals[3] = {
normals[data->elements[index0].normal_id],
normals[data->elements[index1].normal_id],
normals[data->elements[index2].normal_id],
};
}
}
struct MeshPs2_Z {
Mesh_Z super;
uint32_t num_submeshes;
SubMeshPs2_Z submeshes[num_submeshes];
}
struct SubMeshPs2_Z {
uint8_t material_index; // index into super.materials
uint32_t num_values;
float unk[4][num_values];
}