MDL
MDL is the model format for the Stories games. They are the relocatable chunk version of Rsl DFF files after they have been preprocessed by their respective ModelInfo class. Hence the format differs for different types of ModelInfos and also for each platform and game. The structures below are for the PS2 files. Mobile LCS doesn't make use of mdl files but still has the code to read and write them.
For the most part the structures are almost the same as their RW counterparts. The actual geometry however is most notably different. The DFF files from mobile LCS contain PS2 geometry that is converted to OpenGL geometry at runtime.
The main structure at the beginning of the data depends on the ModelInfo type:
struct SimpleModel { RslElement *element[N]; };
Where N is the number of Elements (Atomics) of the object (stored in ModelInfo).
struct ElementGroupModel { RslElementGroup *elementgroup; };
struct PedModel { CColModel *colModel; RslElementGroup *elementgroup; };
struct VehicleModel { RslElementGroup *elementgroup; int32 numExtras; RslElement **extras; RslMaterial *primaryMaterials[NUMPRIM]; RslMaterial *secondaryMaterials[NUMSEC]; };
Where NUMPRIM and NUMSEC are 25 in LCS and 30 and 27 (possibly less?) respectively in VCS.
The Rsl structures are:
struct RslV3 { float32 x, y, z; }; struct RslMatrix { RslV3 right; uint32 flags; RslV3 up; uint32 pad1; RslV3 at; uint32 pad2; RslV3 pos; uint32 pad3; }; struct RslLLLink { RslLLLink *next; RslLLLink *prev; }; struct RslLinkList { RslLLLink link; }; struct RslObject { uint8 type; uint8 subType; uint8 flags; uint8 privateFlags; void *parent; }; struct RslObjectHasNode { RslObject object; RslLLLink lNode; void (*sync)(); }; struct RslRasterPS2 { uint8 *data; uint32 flags; }; struct RslRasterPSP { uint32 unk1; uint8 *data; uint16 flags1; uint8 width; // log of width uint8 height; // log of height uint32 flags2; }; struct RslPs2StreamRaster { uint32 width; uint32 height; uint32 depth; uint32 mipmaps; uint32 unused; }; union RslRaster { RslRasterPS2 ps2; RslRasterPSP psp; }; struct RslTexList { RslObject object; RslLinkList texturesInDict; RslLLLink lInInstance; }; struct RslTexture { RslRaster *raster; RslTexList *dict; RslLLLink lInDictionary; char name[32]; char mask[32]; }; struct RslNode { RslObject object; RslLinkList objectList; RslMatrix modelling; RslMatrix ltm; RslNode *child; RslNode *next; RslNode *root; // RwHAnimNodeExtension #ifdef LCS int32 nodeId; #else int32 unknown1; int32 unknown2; #endif RslTAnimTree *hier; // R* Node name char *name; // R* Visibility int32 hierId; }; struct rslNodeList { RslNode **frames; int32 numNodes; }; struct RslElementGroup { RslObject object; RslLinkList atomicList; }; struct RslElement { RslObjectHasNode object; RslGeometry *geometry; RslElementGroup *clump; RslLLLink inElementGroupLink; RslElementCallBackRender renderCallBack; // CVisibilityComponents int16 modelInfoId; uint16 visIdFlag; // RpSkin RslTAnimTree *hier; }; struct RslMaterialList { RslMaterial **materials; int32 numMaterials; int32 space; }; struct RslGeometry { RslObject object; int16 refCount; int16 pad1; RslMaterialList matList; RslSkin *skin; }; struct RslMatFXEnv { RslNode *frame; union { char *texname; RslTexture *texture; }; float32 intensity; }; struct RslMatFX { union { RslMatFXEnv env; }; int32 effectType; }; struct RslMaterial { union { char *texname; RslTexture *texture; }; RGBA color; uint32 refCount; RslMatFX *matfx; }; struct RslTAnimNodeInfo { int8 id; int8 index; int8 flags; RslNode *frame; }; struct RslTAnimTree { int32 flags; int32 numNodes; void *pCurrentAnim; float32 currentTime; void *pNextFrame; void (*pAnimCallBack)(); void *pAnimCallBackData; float32 animCallBackTime; void (*pAnimLoopCallBack)(); void *pAnimLoopCallBackData; float32 *pMatrixArray; void *pMatrixArrayUnaligned; RslTAnimNodeInfo *pNodeInfo; #ifdef LCS RslNode *parentNode; int32 maxKeyFrameSize; int32 currentKeyFrameSize; void (*keyFrameToMatrixCB)(); void (*keyFrameBlendCB)(); void (*keyFrameInterpolateCB)(); void (*keyFrameAddCB)(); RslTAnimTree *parentTree; int32 offsetInParent; int32 rootParentOffset; #endif }; struct RslSkin { uint32 numBones; uint32 numUsedBones; // == numBones uint8 *usedBones; // NULL float32 *invMatrices; int32 numWeights; // 0 uint8 *indices; // NULL float32 *weights; // NULL uint32 unk1; // 0 uint32 unk2; // 0 uint32 unk3; // 0 uint32 unk4; // 0 uint32 unk5; // 0 void *data; // NULL }; struct sPs2Geometry { float32 bound[4]; uint32 size; // and numMeshes int32 flags; uint32 unk1; uint32 unk2; uint32 unk3; uint32 unk4; float32 scale[3]; float32 pos[3]; }; struct sPs2GeometryMesh { float32 bound[4]; float32 uvScale[2]; int32 unknown; uint32 dmaPacket; uint16 numTriangles; int16 matID; int16 min[3]; // bounding box int16 max[3]; };
TODO: describe the geometry format.
In the file, textures are just file names. After loading they are replaced by actual RslTexture structs.
Tool
- Source code for stories format conversion – by The Hero
- GTAForums: PS2 MDL Importer – by AK-73
External link
- GTAForums: An attempt at making an MDL viewer – post by J-Fox