(The section about manipulating data has been removed. This is so that people arent encouraged to edit this data. do not edit this data unless you’re prepared to have future updates possibly breaking your games!)
CSGPHS format.
This format can change at any time and any tooling you develop around this format may break suddenly in the future without warning.
The first 6 bytes spell out CSGPHS
.
char magic[6]; // spells out CSGPHS
The next 4 bytes indicates the type of collision mesh.
uint32_t type;
From my testing, I know of only two possible formats: 0
indicates that this uses the box collisions (when CollisionFidelity
is set to Box
) and 6
indicates that this uses convex hulls (when CollisionFidelity
is set to Hull
or Default
).
Fun fact: If you export a MeshPart which has its CollisionFidelity
set to Default
as a file and it only has one convex hull when you view its decomposition geometry, it will appear with its CollisionFidelity
set to Hull
when you re-import it. This tells us that the CollisionFidelity
isn’t serialized with the file, but inferred from CSGPHS.
Type=0, Box
After parsing the type, 5 characters will follow. These characters spell out BLOCK
.
char block[5]; // spells out BLOCK
… and that’s all she wrote for this type.
Type=6, Default and Hull
This type is more complex. It can be split into two parts: Physics Data and Mesh Data.
Physics Data
After parsing the type, the unscaled volume of the collision mesh follows (note: all numbers are relative to the unscaled mesh). This is stored as a float.
float volume;
After volume, the mesh’s center of gravity is described. This consists of three floats.
float cog_x, cog_y, cog_z;
The last data before the actual collision mesh(es) are described is the mesh’s inertia tensor. This is important for physics involving rotations. I think (!) that it’s stored in the following order:
float im_xx, im_xy, im_xz, im_yy, im_yz, im_zz;
(I’m only sure of where 1, 4, and 6 are. I’m just assuming where 2, 3, and 5 are based on what makes sense. I didn’t do enough testing to make sure.)
Mesh Data
Immediately after the physics data is the mesh data. This data is encoded for every convex hull in the mesh.
The first 4 bytes (and the following 16 bytes) is… trash? I don’t know what this part means exactly, but the 4 bytes are always an int with a value of 16 and the 16 bytes after that are all 0.
uint32_t trash1;
char trash1_dump[16]; // {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
After that is… more trash? It has the same format as the first trash, but instead of the 16 bytes being all 0, the final two bytes are 0x80
and 0x3f
:
uint32_t trash2;
char trash2_dump[16]; // {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0x80,0x3f}
After these two trash chunks is the useful data. The next 4 bytes is the number of floats that describe the vertex data.
uint32_t verts_size;
vert_size/3
is basically the number of vertices in the mesh.
The next 4 bytes after that is (what I think is) the size of each component in a vertex. Since floats are 4 bytes, this value is 4.
uint32_t vert_width; // 4 in all the meshes I've tested.
After this is an array of <verts_size>
floats. Each vertex is made up of three components that describe its position: x, y, and z. If there are v_0, v_1 ... v_n
vertices in the mesh, then in memory they will be ordered as x_0, y_0, z_0, x_1, y_1, z_1 ... x_n, y_n, z_n
. A more familiar way of putting this is that each vertex is a Vector3 struct:
struct Vector3
{
float x, y, z;
}
// ...
Vector3* verts = new Vector3[verts_size/3];
After the vertex data comes the face data. 4 bytes indicate the number of ints that describe the faces.
uint32_t faces_size
Since each face is a triangle, faces_size/3
is basically the number of faces in the mesh.
Finally, the rest of the data (for the current convex hull) is an array of <faces_size>
4-byte ints. A face is described by three vertices. Each int is basically an index into the verts
array. You’d be familiar with this scheme if you’ve processed mesh files before.
struct Face
{
uint32_t a, b, c;
}
// ...
Face* faces = new Face[faces_size/3];
… Tada! And that’s it for hull/default-type collision data.