SOLVED
I'll leave the post here for posterity. I'll put the solution in spoilers for anyone who still wants to try to solve it on their own. Most models are now decipherable, though it appears some have extra data that needs to be figure out before a general script may be written to convert them all to GLTF.
ORIGINAL TEXT:
I've been getting more time since settling into my new job, so I decided to return to finishing up my work on the Agartha project from last year. In the original thread I figured out No Cliche's weird custom compression algorithm to crack open the archive files, letting us access the textures and other assets. This was pretty cool in itself, but I wanted to convert the models and animations into GLTF like I had done with the Castlevania prototype.
To that end, I've made quite a bit of progress on saturday! I've mostly got the model format, .o6d, figured out, except for a huge sticking point. Here's an image - disregard the typenames, they're misnomers at the present:
I cannot for the life of me figure out how to compute the size of the first block of vertices. Technically speaking, it seems like the format stores geometry in batches of vertices arranged into triangle strips - no explicit indices are used - so this would be the first triangle trip out of 4.
As indicated in the file, we do have access to the length of the entire mesh block, which include header data and such. Subtracting the header size, we could in theory work backward, subtracting the lengths of the subsequent strips to get the size of the first block but... who does that? it makes no sense from a programming perspective to ever do it like that - there's no way to even figure out where to read the subsequent strips in the first place! So this solution is unacceptable. I cannot use it to create a GLTF translator.
For anyone interested in the files: https://we.tl/t-zjZXaUELBs (the link will expire in a week from writing this, unfortunately)
So does anyone have any ideas? I thought that maybe the size might be included in a bit field or something on the mesh header, but all the values there are fairly static between different files, so that appears not to be the case. If the value is explicitly encoded in the file, then it has to be somewhere at or before the mesh blocks. Worst case scenario I can try diving into the code with Ghidra...
EDIT:
Some basic layout info for anyone wanting to examine the file:
The file is mapped out in chunks. Each chunk has a 4 byte id, followed by a 32 bit length. Chunks ids in the o6d format: O6D!, MDLS, MATS, TEXS.
MDL data:
There appears to be only one of these per file. They are followed by one or more meshes.
Mesh data:
Each mesh has a header, followed by one or more blocks of vertex data. NOTE: how do we known the length of the first block?
Following the mesh data, there is a block of 3D vectors giving the points, and another block of 3D vertices giving the normals.
Texture names follow from there.
Also note, most of the 'unknown' data is the same between files, regardless of complexity. I suspect most of it is actually serialised KAMUI2 structures.
I'll leave the post here for posterity. I'll put the solution in spoilers for anyone who still wants to try to solve it on their own. Most models are now decipherable, though it appears some have extra data that needs to be figure out before a general script may be written to convert them all to GLTF.
In hindsight it was kind of silly that I did not think of this sooner, as I've written tools in the past to process arbitrary geometry into triangle strips as well. The first block of vertices is a 'catch all' pool of primitives - single faces that cannot be included into a triangle strip. In the mesh header, entries [8] and [9] denote the number of triangles and quads in the pool. The length is thus
4*quads + 3*triangles
. Subsequent blocks are all triangle strips, and have their lengths prefixed.ORIGINAL TEXT:
I've been getting more time since settling into my new job, so I decided to return to finishing up my work on the Agartha project from last year. In the original thread I figured out No Cliche's weird custom compression algorithm to crack open the archive files, letting us access the textures and other assets. This was pretty cool in itself, but I wanted to convert the models and animations into GLTF like I had done with the Castlevania prototype.
To that end, I've made quite a bit of progress on saturday! I've mostly got the model format, .o6d, figured out, except for a huge sticking point. Here's an image - disregard the typenames, they're misnomers at the present:
I cannot for the life of me figure out how to compute the size of the first block of vertices. Technically speaking, it seems like the format stores geometry in batches of vertices arranged into triangle strips - no explicit indices are used - so this would be the first triangle trip out of 4.
mesh2[10]
in the image appears to indicate the number of strips that are in addition to the 'main' strip, the red block in the image whose size is mysteriously absent from the file. You can see the orange, yellow and green blocks are all prefixed with their lengths in the image. In the mesh header there is no such value, in fact, no where in the file is 18 even expressed outside of the vertex entries! I've asked everyone I know if they had any thoughts, but no one has managed to figure this one out.As indicated in the file, we do have access to the length of the entire mesh block, which include header data and such. Subtracting the header size, we could in theory work backward, subtracting the lengths of the subsequent strips to get the size of the first block but... who does that? it makes no sense from a programming perspective to ever do it like that - there's no way to even figure out where to read the subsequent strips in the first place! So this solution is unacceptable. I cannot use it to create a GLTF translator.
For anyone interested in the files: https://we.tl/t-zjZXaUELBs (the link will expire in a week from writing this, unfortunately)
So does anyone have any ideas? I thought that maybe the size might be included in a bit field or something on the mesh header, but all the values there are fairly static between different files, so that appears not to be the case. If the value is explicitly encoded in the file, then it has to be somewhere at or before the mesh blocks. Worst case scenario I can try diving into the code with Ghidra...
EDIT:
Some basic layout info for anyone wanting to examine the file:
The file is mapped out in chunks. Each chunk has a 4 byte id, followed by a 32 bit length. Chunks ids in the o6d format: O6D!, MDLS, MATS, TEXS.
MDL data:
There appears to be only one of these per file. They are followed by one or more meshes.
Code:
struct Xform
{
float m4x3[4][3];
float unk[4]; //quaternion?
};
uint32 nbones;
uint32 unk0[3];
uint64 hierarchy[nbones];
Xform xforms[nbones];
uint8 unk1[100];
uint32 npoints;
uint32 nnormals;
uint8 unk2[10];
uint32 nmeshes;
uint32 unk3;
Mesh data:
Each mesh has a header, followed by one or more blocks of vertex data. NOTE: how do we known the length of the first block?
Code:
struct Vertex
{
uint16 point;
uint16 normal;
float u, v;
};
struct Mesh
{
uint32 size;
uint16 unk[18];
uint32 nblocks;
};
Following the mesh data, there is a block of 3D vectors giving the points, and another block of 3D vertices giving the normals.
Texture names follow from there.
Also note, most of the 'unknown' data is the same between files, regardless of complexity. I suspect most of it is actually serialised KAMUI2 structures.
Last edited: