struct RwMeshCache { RwUInt32 lengthOfMeshesArray; /**< Number of meshes in the object */ RwResEntry *meshes[1]; /**< The RwMeshCache structure will be * allocated sufficiently big that this tail * array can hold lengthOfMeshesArray pointers to * \ref RwResEntry's */ }; struct RwResEntry { RwLLLink link; RwInt32 size; /* RwResEntry + pad + data(+junk) */ void *owner; /* RpGeometry */ RwResEntry **ownerRef; /* points back into RwMeshCache */ RwResEntryDestroyNotify destroyNotify; }; struct rwPS2AllClusterQuickInfo { u_long128 *data; RwUInt32 stride; }; struct rwPS2AllFieldRec { int numVerts; int morphNumVerts; int dataoffset; int morphDataoffset; short skip; short morphSkip; short reverse; unsigned char vuoffset; unsigned char pad[1]; }; struct rwPS2AllResEntryHeader { RwInt32 refCnt; RwInt32 clrCnt; u_long128 *data; /* pointer to instanced data */ RwUInt32 numVerts; RwUInt32 objIdentifier; RwUInt32 meshIdentifier; int batchSize; int numBatches; int batchesPerTag; int morphStart; int morphFinish; int morphNum; rwPS2AllClusterQuickInfo clquickinfo[CL_MAXCL + FMADD]; /* 10 + 2 */ rwPS2AllFieldRec fieldRec[CL_MAXCL + FMADD]; /* 10 + 2 */ }; /* memory layout: RwResEntry rwPS2AllResEntryHeader [pad] to 0x40 if has broken out attribs, else to 0x80 data RwResEntry includes junk bytes at the end of 'data' used for alignment. These are written together with the data when the instance data is stream out. When it is streamed in there will be even more junk bytes due to alignment again. All meshes begin with the same 12 byte header: int32 platform - always 4 (PS2) uint32 size - size of instance data (including trailing junk) bool32 needNoPatching - no DMAref tags that need patching Format of instanced data (a DMA chain): - DMAref pointers are relative to the beginning of the instanced data and are patched to absolute pointers when loaded into memory - FLUSH; MASKPATH3 was used at least until version 3.4 FLUSH; FLUSH is used by 3.6 and possibly earlier - the same goes for MARK and NOP - unpack-data is always rounded up to a multiple of 16 bytes - when using broken out clusters this means vertexCount*attribSize has to be an exact multiple of 16 since we can't pad with zeros in the (contiguous) broken out vertex data; thus the vertex count may have to be adjusted for rounding. no broken out clusters ====================== DMAret [FLUSH; MSKPATH3 || FLUSH; FLUSH] { foreach batch { foreach cluster { MARK/0; STMOD; STCYCL; UNPACK unpack-data } ITOP; MSCALF/MSCNT; // if first/not-first 0/FLUSH; 0/MASKPATH3 || 0/FLUSH; 0/FLUSH // if not-last/last } } broken out clusters =================== foreach batch { foreach broken out cluster { DMAref [STCYCL; UNPACK] -> unpack-data DMAcnt (empty) } DMAcnt/ret { foreach cluster { MARK/0; STMOD; STCYCL; UNPACK unpack-data } ITOP; MSCALF/MSCNT; // if first/not-first 0/FLUSH; 0/MASKPATH3 || 0/FLUSH; 0/FLUSH // if not-last/last } } custom Bully pipeline ===================== foreach batch { DMAcnt { 0; 0; STMOD; STCYCL UNPACK 0x69388000 // vertices unpack-data UNPACK 0x65388001 // uv unpack-data UNPACK 0x6F38C002 // color1 unpack-data UNPACK 0x6F38C003 // color2 unpack-data ITOP; MSCALF/MSCNT; 0; 0 } } DMAret (empty) [0; FLUSH] */