nenuzhno-engine_iter1/demos/skinning/mdl_loader.cpp

1062 lines
36 KiB
C++

#include "log.h"
#include "mdl_loader.h"
#include <cstring>
#include <stdint.h>
#include "system/FileSystem.h"
#include <renderer/Model.h>
#include <vec2.hpp>
#include <gtc/packing.hpp>
#include <glm.hpp>
typedef unsigned char byte;
typedef glm::vec2 Vector2D;
typedef glm::vec3 Vector;
typedef glm::quat Quaternion;
typedef glm::vec3 RadianEuler;
typedef glm::mat3x4 matrix3x4_t;
#define IDSTUDIOHEADER (('T'<<24)+('S'<<16)+('D'<<8)+'I')
#define MAX_NUM_LODS 8
#define MAX_NUM_BONES_PER_VERT 3
struct studiohdr_t{
uint32_t id;
uint32_t version;
uint32_t checksum; // this has to be the same in the phy and vtx files to load!
inline const char * pszName( void ) const { return name; }
char name[64];
uint32_t length;
Vector eyeposition; // ideal eye position
Vector illumposition; // illumination center
Vector hull_min; // ideal movement hull size
Vector hull_max;
Vector view_bbmin; // clipping bounding box
Vector view_bbmax;
int flags;
int numbones; // bones
int boneindex;
int numbonecontrollers; // bone controllers
int bonecontrollerindex;
int numhitboxsets;
int hitboxsetindex;
int numlocalanim; // animations/poses
int localanimindex; // animation descriptions
int numlocalseq; // sequences
int localseqindex;
mutable int activitylistversion; // initialization flag - have the sequences been indexed?
mutable int eventsindexed;
int numtextures;
int textureindex;
int numcdtextures;
int cdtextureindex;
int numskinref;
int numskinfamilies;
int skinindex;
int numbodyparts;
int bodypartindex;
int numlocalattachments;
int localattachmentindex;
int numlocalnodes;
int localnodeindex;
int localnodenameindex;
int numflexdesc;
int flexdescindex;
int numflexcontrollers;
int flexcontrollerindex;
int numflexrules;
int flexruleindex;
int numikchains;
int ikchainindex;
int nummouths;
int mouthindex;
int numlocalposeparameters;
int localposeparamindex;
int surfacepropindex;
int keyvalueindex;
int keyvaluesize;
int numlocalikautoplaylocks;
int localikautoplaylockindex;
float mass;
int contents;
int numincludemodels;
int includemodelindex;
//mutable void *virtualModel;
uint32_t virtualModel;
int szanimblocknameindex;
int numanimblocks;
int animblockindex;
uint32_t animblockModel;//void*
int bonetablebynameindex;
//void *pVertexBase;
uint32_t pVertexBase;
//void *pIndexBase;
uint32_t pIndexBase;
byte constdirectionallightdot;
byte rootLOD;
byte numAllowedRootLODs;
byte unused[1];
int unused4; // zero out if version < 47
int numflexcontrollerui;
int flexcontrolleruiindex;
int unused3[2];
int studiohdr2index;
int unused2[1];
studiohdr_t() {}
private:
// No copy constructors allowed
studiohdr_t(const studiohdr_t& vOther);
};
struct mstudio_modelvertexdata_t
{
// base of external vertex data stores
//const void *pVertexData;
//const void *pTangentData;
uint32_t pVertexData;
uint32_t pTangentData;
};
struct mstudio_meshvertexdata_t
{
//const mstudio_modelvertexdata_t *modelvertexdata;
uint32_t modelvertexdata;
int numLODVertexes[MAX_NUM_LODS];
};
struct mstudiomesh_t
{
int material;
int modelindex;
int numvertices; // number of unique vertices/normals/texcoords
int vertexoffset; // vertex mstudiovertex_t
int numflexes; // vertex animation
int flexindex;
int materialtype;
int materialparam;
int meshid;
Vector center;
mstudio_meshvertexdata_t vertexdata;
int unused[8]; // remove as appropriate
mstudiomesh_t(){}
private:
// No copy constructors allowed
mstudiomesh_t(const mstudiomesh_t& vOther);
};
// studio models
struct mstudiomodel_t
{
inline const char * pszName( void ) const { return name; }
char name[64];
int type;
float boundingradius;
int nummeshes;
int meshindex;
int numvertices; // number of unique vertices/normals/texcoords
int vertexindex; // vertex Vector
int tangentsindex; // tangents Vector
int numattachments;
int attachmentindex;
int numeyeballs;
int eyeballindex;
mstudio_modelvertexdata_t vertexdata;
int unused[8]; // remove as appropriate
};
struct mstudiobodyparts_t
{
int sznameindex;
int nummodels;
int base;
int modelindex; // index into models array
};
struct mstudiobone_t
{
int sznameindex;
int parent; // parent bone
int bonecontroller[6]; // bone controller index, -1 == none
Vector pos;
Quaternion quat;
RadianEuler rot;
Vector posscale;
Vector rotscale;
matrix3x4_t poseToBone;
Quaternion qAlignment;
int flags;
int proctype;
int procindex; // procedural rule
mutable int physicsbone; // index into physically simulated bone
int surfacepropidx; // index into string tablefor property name
int contents; // See BSPFlags.h for the contents flags
int unused[8]; // remove as appropriate
mstudiobone_t(){}
private:
// No copy constructors allowed
mstudiobone_t(const mstudiobone_t& vOther);
};
// sequence descriptions
struct mstudioseqdesc_t{
int baseptr;
int szlabelindex;
inline char * const pszLabel( void ) const { return ((char *)this) + szlabelindex; }
int szactivitynameindex;
inline char * const pszActivityName( void ) const { return ((char *)this) + szactivitynameindex; }
int flags; // looping/non-looping flags
int activity; // initialized at loadtime to game DLL values
int actweight;
int numevents;
int eventindex;
// inline mstudioevent_t *pEvent( int i ) const { Assert( i >= 0 && i < numevents); return (mstudioevent_t *)(((byte *)this) + eventindex) + i; };
Vector bbmin; // per sequence bounding box
Vector bbmax;
int numblends;
int animindexindex;
int movementindex; // [blend] float array for blended movement
int groupsize[2];
int paramindex[2]; // X, Y, Z, XR, YR, ZR
float paramstart[2]; // local (0..1) starting value
float paramend[2]; // local (0..1) ending value
int paramparent;
float fadeintime; // ideal cross fate in time (0.2 default)
float fadeouttime; // ideal cross fade out time (0.2 default)
int localentrynode; // transition node at entry
int localexitnode; // transition node at exit
int nodeflags; // transition rules
float entryphase; // used to match entry gait
float exitphase; // used to match exit gait
float lastframe; // frame that should generation EndOfSequence
int nextseq; // auto advancing sequences
int pose; // index of delta animation between end and nextseq
int numikrules;
int numautolayers; //
int autolayerindex;
// inline mstudioautolayer_t *pAutolayer( int i ) const { Assert( i >= 0 && i < numautolayers); return (mstudioautolayer_t *)(((byte *)this) + autolayerindex) + i; };
int weightlistindex;
// inline float *pBoneweight( int i ) const { return ((float *)(((byte *)this) + weightlistindex) + i); };
// inline float weight( int i ) const { return *(pBoneweight( i)); };
// FIXME: make this 2D instead of 2x1D arrays
int posekeyindex;
// float *pPoseKey( int iParam, int iAnim ) const { return (float *)(((byte *)this) + posekeyindex) + iParam * groupsize[0] + iAnim; }
// float poseKey( int iParam, int iAnim ) const { return *(pPoseKey( iParam, iAnim )); }
int numiklocks;
int iklockindex;
// inline mstudioiklock_t *pIKLock( int i ) const { Assert( i >= 0 && i < numiklocks); return (mstudioiklock_t *)(((byte *)this) + iklockindex) + i; };
// Key values
int keyvalueindex;
int keyvaluesize;
// inline const char * KeyValueText( void ) const { return keyvaluesize != 0 ? ((char *)this) + keyvalueindex : NULL; }
int cycleposeindex; // index of pose parameter to use as cycle index
int unused[7]; // remove/add as appropriate (grow back to 8 ints on version change!)
mstudioseqdesc_t(){}
private:
// No copy constructors allowed
mstudioseqdesc_t(const mstudioseqdesc_t& vOther);
};
// animation frames
union mstudioanimvalue_t{
struct {
byte valid;
byte total;
} num;
short value;
};
struct mstudioanim_valueptr_t{
short offset[3];
inline mstudioanimvalue_t *pAnimvalue( int i ) const { if (offset[i] > 0) return (mstudioanimvalue_t *)(((byte *)this) + offset[i]); else return NULL; }
};
#define STUDIO_ANIM_RAWPOS 0x01 // Vector48
#define STUDIO_ANIM_RAWROT 0x02 // Quaternion48
#define STUDIO_ANIM_ANIMPOS 0x04 // mstudioanim_valueptr_t
#define STUDIO_ANIM_ANIMROT 0x08 // mstudioanim_valueptr_t
#define STUDIO_ANIM_DELTA 0x10
#define STUDIO_ANIM_RAWROT2 0x20 // Quaternion64
// per bone per animation DOF and weight pointers
struct mstudioanim_t
{
byte bone;
byte flags; // weighing options
// valid for animating data only
inline byte *pData( void ) const { return (((byte *)this) + sizeof( struct mstudioanim_t )); }
inline mstudioanim_valueptr_t *pRotV( void ) const { return (mstudioanim_valueptr_t *)(pData()); }
inline mstudioanim_valueptr_t *pPosV( void ) const { return (mstudioanim_valueptr_t *)(pData()) + ((flags & STUDIO_ANIM_ANIMROT) != 0); }
// valid if animation unvaring over timeline
//inline Quaternion48 *pQuat48( void ) const { return (Quaternion48 *)(pData()); }
//inline Quaternion64 *pQuat64( void ) const { return (Quaternion64 *)(pData()); }
//inline Vector48 *pPos( void ) const { return (Vector48 *)(pData() + ((flags & STUDIO_ANIM_RAWROT) != 0) * sizeof( *pQuat48() ) + ((flags & STUDIO_ANIM_RAWROT2) != 0) * sizeof( *pQuat64() ) ); }
inline glm::quat Quat() const {
struct t{uint64_t x:21;
uint64_t y:21;
uint64_t z:21;
uint64_t wneg:1;
}raw = *((t*)pData());
glm::quat tmp;
tmp.x = ((int)raw.x - 1048576) * (1 / 1048576.5f);
tmp.y = ((int)raw.y - 1048576) * (1 / 1048576.5f);
tmp.z = ((int)raw.z - 1048576) * (1 / 1048576.5f);
tmp.w = sqrt( 1 - tmp.x * tmp.x - tmp.y * tmp.y - tmp.z * tmp.z );
if (raw.wneg)
tmp.w = -tmp.w;
return tmp;
}
inline glm::vec3 PosVec3() const {
int ofs = ((flags & STUDIO_ANIM_RAWROT2) != 0) * 8 + ((flags & STUDIO_ANIM_RAWROT) != 0) * 6;
return glm::vec3(glm::unpackHalf1x16(*(short*)(pData()+ofs)),glm::unpackHalf1x16(*(short*)(pData()+ofs+2)),glm::unpackHalf1x16(*(short*)(pData()+ofs+4)));
}
short nextoffset;
inline mstudioanim_t *pNext( void ) const { if (nextoffset != 0) return (mstudioanim_t *)(((byte *)this) + nextoffset); else return NULL; }
};
struct mstudioanimdesc_t{
int baseptr;
//inline studiohdr_t *pStudiohdr( void ) const { return (studiohdr_t *)(((byte *)this) + baseptr); }
int sznameindex;
inline char * const pszName( void ) const { return ((char *)this) + sznameindex; }
float fps; // frames per second
int flags; // looping/non-looping flags
int numframes;
// piecewise movement
int nummovements;
int movementindex;
//inline mstudiomovement_t * const pMovement( int i ) const { return (mstudiomovement_t *)(((byte *)this) + movementindex) + i; };
int unused1[6]; // remove as appropriate (and zero if loading older versions)
int animblock;
int animindex; // non-zero when anim data isn't in sections
//mstudioanim_t *pAnimBlock( int block, int index ) const; // returns pointer to a specific anim block (local or external)
//mstudioanim_t *pAnim( int *piFrame, float &flStall ) const; // returns pointer to data and new frame index
//mstudioanim_t *pAnim( int *piFrame ) const; // returns pointer to data and new frame index
int numikrules;
int ikruleindex; // non-zero when IK data is stored in the mdl
int animblockikruleindex; // non-zero when IK data is stored in animblock file
//mstudioikrule_t *pIKRule( int i ) const;
int numlocalhierarchy;
int localhierarchyindex;
//mstudiolocalhierarchy_t *pHierarchy( int i ) const;
int sectionindex;
int sectionframes; // number of frames used in each fast lookup section, zero if not used
//inline mstudioanimsections_t * const pSection( int i ) const { return (mstudioanimsections_t *)(((byte *)this) + sectionindex) + i; }
short zeroframespan; // frames per span
short zeroframecount; // number of spans
int zeroframeindex;
//byte *pZeroFrameData( ) const { if (zeroframeindex) return (((byte *)this) + zeroframeindex); else return NULL; };
mutable float zeroframestalltime; // saved during read stalls
mstudioanimdesc_t(){}
private:
// No copy constructors allowed
mstudioanimdesc_t(const mstudioanimdesc_t& vOther);
};
struct mstudiohitboxset_t{
int sznameindex;
//inline char * const pszName( void ) const { return ((char *)this) + sznameindex; }
int numhitboxes;
int hitboxindex;
//inline mstudiobbox_t *pHitbox( int i ) const { return (mstudiobbox_t *)(((byte *)this) + hitboxindex) + i; };
};
struct mstudiobbox_t{
int bone;
int group; // intersection group
Vector bbmin; // bounding box
Vector bbmax;
int szhitboxnameindex; // offset to the name of the hitbox.
int unused[8];
mstudiobbox_t() {}
private:
// No copy constructors allowed
mstudiobbox_t(const mstudiobbox_t& vOther);
};
// skin info
struct mstudiotexture_t{
int sznameindex;
inline char * const pszName( void ) const { return ((char *)this) + sznameindex; }
int flags;
int used;
int unused1;
//mutable IMaterial *material; // fixme: this needs to go away . .isn't used by the engine, but is used by studiomdl
uint32_t material;
//mutable void *clientmaterial; // gary, replace with client material pointer if used
uint32_t clientmaterial;
int unused[10];
};
// demand loaded sequence groups
struct mstudiomodelgroup_t{
int szlabelindex; // textual name
inline char * const pszLabel( void ) const { return ((char *)this) + szlabelindex; }
int sznameindex; // file name
inline char * const pszName( void ) const { return ((char *)this) + sznameindex; }
};
//vvd
struct mstudioboneweight_t
{
float weight[MAX_NUM_BONES_PER_VERT];
char bone[MAX_NUM_BONES_PER_VERT];
byte numbones;
};
// NOTE: This is exactly 48 bytes
struct mstudiovertex_t
{
mstudioboneweight_t m_BoneWeights;
Vector m_vecPosition;
Vector m_vecNormal;
Vector2D m_vecTexCoord;
mstudiovertex_t() {}
private:
// No copy constructors allowed
mstudiovertex_t(const mstudiovertex_t& vOther);
};
struct vertexFileHeader_t
{
int id; // MODEL_VERTEX_FILE_ID
int version; // MODEL_VERTEX_FILE_VERSION
uint32_t checksum; // same as studiohdr_t, ensures sync
int numLODs; // num of valid lods
int numLODVertexes[MAX_NUM_LODS]; // num verts for desired root lod
int numFixups; // num of vertexFileFixup_t
int fixupTableStart; // offset from base to fixup table
int vertexDataStart; // offset from base to vertex block
int tangentDataStart; // offset from base to tangent block
};
// apply sequentially to lod sorted vertex and tangent pools to re-establish mesh order
struct vertexFileFixup_t
{
int lod; // used to skip culled root lod
int sourceVertexID; // absolute index from start of vertex/tangent blocks
int numVertexes;
};
//vtx
#pragma pack(1)
struct vtxFileHeader_t
{
int version;
int vertCacheSize;
unsigned short maxBonesPerStrip;
unsigned short maxBonesPerTri;
int maxBonesPerVert;
uint32_t checkSum;
int numLODs; // garymcthack - this is also specified in ModelHeader_t and should match
int materialReplacementListOffset;
int numBodyParts;
int bodyPartOffset;
};
struct vtxBodyPartHeader_t
{
int numModels;
int modelOffset;
};
// This maps one to one with models in the mdl file.
// There are a bunch of model LODs stored inside potentially due to the qc $lod command
struct vtxModelHeader_t
{
int numLODs; // garymcthack - this is also specified in FileHeader_t
int lodOffset;
};
struct vtxModelLODHeader_t
{
int numMeshes;
int meshOffset;
float switchPoint;
};
// a collection of locking groups:
// up to 4:
// non-flexed, hardware skinned
// flexed, hardware skinned
// non-flexed, software skinned
// flexed, software skinned
//
// A mesh has a material associated with it.
struct vtxMeshHeader_t
{
int numStripGroups;
int stripGroupHeaderOffset;
unsigned char flags;
};
// a locking group
// a single vertex buffer
// a single index buffer
struct vtxStripGroupHeader_t
{
// These are the arrays of all verts and indices for this mesh. strips index into this.
int numVerts;
int vertOffset;
int numIndices;
int indexOffset;
int numStrips;
int stripOffset;
unsigned char flags;
};
struct vtxVertex_t
{
// these index into the mesh's vert[origMeshVertID]'s bones
unsigned char boneWeightIndex[MAX_NUM_BONES_PER_VERT];
unsigned char numBones;
unsigned short origMeshVertID;
// for sw skinned verts, these are indices into the global list of bones
// for hw skinned verts, these are hardware bone indices
char boneID[MAX_NUM_BONES_PER_VERT];
};
#pragma pack()
Model *MDLLoader::Load(const char *name){
Log("Loading model: %s\n", name);
std::string filePath = name;
loadMDLOut_t mdlData;
if(!LoadMDL(filePath.c_str(),mdlData))
return NULL;
int ext = filePath.find(".mdl");
filePath = filePath.substr(0,ext);
Model *mdl = new Model();
int lod = 0;
LoadVVD((filePath+".vvd").c_str(),mdl,lod);
filePath+=".vtx";
if(!g_fs.FileExists(filePath.c_str()))
filePath = filePath.substr(0,ext)+".dx90.vtx";
LoadVTX(filePath.c_str(),mdl,mdlData,lod);
mdl->vbo.Create();
mdl->vbo.Upload(mdl->vertexFormat[0].stride*mdl->vertexCount,mdl->verts);
mdl->ibo.Create();
mdl->ibo.Upload(mdl->indexSize*mdl->indexCount,mdl->inds);
mdl->submeshes.Resize(mdlData.numMeshes);
for(int i=0;i<mdlData.numMeshes;i++){
mdl->submeshes[i]={(uint32_t)mdlData.meshes[i].indOffs,mdlData.meshes[i].indCount,i};
}
mdl->skeleton.Resize(mdlData.bones.size);
for(int i=0;i<mdlData.bones.size;i++){
mdl->skeleton[i] = mdlData.bones[i];
}
mdl->animations.Resize(mdlData.anims.size);
for(int i=0;i<mdlData.anims.size;i++){
mdl->animations[i].name = mdlData.anims[i].name;
mdl->animations[i].fps = mdlData.anims[i].fps;
mdl->animations[i].frames.Resize(mdlData.anims[i].frames.size);
for(int f=0;f<mdlData.anims[i].frames.size;f++){
mdl->animations[i].frames[f].bones.Resize(mdlData.anims[i].frames[f].bones.size);
for(int b=0;b<mdlData.anims[i].frames[f].bones.size;b++){
mdl->animations[i].frames[f].bones[b] = mdlData.anims[i].frames[f].bones[b];
}
}
}
return mdl;
}
bool MDLLoader::CheckExt(const char *name){
return strstr(name,".mdl")!=0;
}
#if 1
bool MDLLoader::LoadMDL(const char *name, loadMDLOut_t &mdlData){
IFile *mdlfile = g_fs.Open(name);
if(!mdlfile){
return false;
}
uint32_t fileLen = mdlfile->GetLen();
Log("MDL: file len %d\n",fileLen);
char *data = new char[fileLen];
mdlfile->Seek(0);
mdlfile->Read(data,fileLen);
g_fs.Close(mdlfile);
studiohdr_t *mdlHeader = (studiohdr_t*)data;
if(mdlHeader->id != IDSTUDIOHEADER){
Log("MDL: wrongh id(%X, must be %X)\n",mdlHeader->id,IDSTUDIOHEADER);
return false;
}
if(mdlHeader->length != fileLen){
Log("MDL: wrongh length(%d)\n",mdlHeader->length);
return false;
}
if(mdlHeader->numbodyparts != 1)
Log("MDL: %s num bodyparts %d\n", name, mdlHeader->numbodyparts);
Log("MDL: numincludemodels %d\n",mdlHeader->numincludemodels);
if(mdlHeader->numincludemodels){
mstudiomodelgroup_t *incs = (mstudiomodelgroup_t*)(data+mdlHeader->includemodelindex);
for(int i=0; i<mdlHeader->numincludemodels; i++){
Log( "include model %d: label %s, name %s\n",i,incs[i].pszLabel(),incs[i].pszName());
}
}
if(mdlHeader->szanimblocknameindex)
Log("anim block name %s\n",data+mdlHeader->szanimblocknameindex);
Log("MDL: num bones %d\n",mdlHeader->numbones);
mdlData.bones.Resize(mdlHeader->numbones);
mstudiobone_t *bones = (mstudiobone_t*)(data+mdlHeader->boneindex);
for(int i=0; i<mdlHeader->numbones; i++){
mstudiobone_t &b = bones[i];
char *bonename = data+mdlHeader->boneindex+sizeof(mstudiobone_t)*i+b.sznameindex;
Log( "bone %d: name %s, parent %d, pos (%.3f,%.3f,%.3f), rot(%.3f,%.3f,%.3f)\n",i,bonename,b.parent,b.pos.x,b.pos.y,b.pos.z, b.rot.x,b.rot.y,b.rot.z);
mdlData.bones[i]={b.parent,b.pos,b.quat};
}
Log("MDL: numtextures %d, numcdtextures %d\n",mdlHeader->numtextures,mdlHeader->numcdtextures);
mstudiotexture_t *texs = (mstudiotexture_t*)(data+mdlHeader->textureindex);
for(int i=0; i<mdlHeader->numtextures; i++){
Log("tex %d: %s\n",i,texs[i].pszName());
}
Log("MDL: numlocalanim %d, numlocalseq %d, numanimblocks %d\n",mdlHeader->numlocalanim,mdlHeader->numlocalseq,mdlHeader->numanimblocks);
if(mdlHeader->numlocalseq){
mstudioseqdesc_t *seqs = (mstudioseqdesc_t*)(data+mdlHeader->localseqindex);
for(int i=0; i<mdlHeader->numlocalseq; i++){
Log( "seq %d: label %s, activity name %s\n",i,seqs[i].pszLabel(),seqs[i].pszActivityName());
}
}
if(mdlHeader->numlocalanim){
mstudioanimdesc_t *anims = (mstudioanimdesc_t*)(data+mdlHeader->localanimindex);
mdlData.anims.Resize(mdlHeader->numlocalanim);
for(int i=0; i<mdlHeader->numlocalanim; i++){
mstudioanimdesc_t &ad=anims[i];
Log( "animdesc %d: label %s, num frames %d, fps %.3f, flags %d, block %d, index %d, section frames %d\n",i,ad.pszName(),ad.numframes,ad.fps,ad.flags,ad.animblock,ad.animindex,ad.sectionframes);
//if(i<4)
{
mdlData.anims[i].name = ad.pszName();
mdlData.anims[i].fps = ad.fps;
mdlData.anims[i].frames.Resize(ad.numframes);
for(int f=0;f<ad.numframes;f++){
mdlData.anims[i].frames[f].bones.Resize(mdlHeader->numbones);
}
mstudioanim_t *pAnim = (mstudioanim_t*)((char*)(anims+i)+anims[i].animindex);
for(int b=0;b<mdlHeader->numbones;b++){
for(int f=0;f<ad.numframes;f++){
bone_t bone;
//bone.parent = mdlData.bones[b].parent;
bone = mdlData.bones[b];
if(pAnim&&pAnim->bone==b){
//if(f==0/* && i==1*/) Log("anim %p: bone %d, flags %d, next %d\n",pAnim,pAnim->bone,pAnim->flags,pAnim->nextoffset);
if(pAnim->flags&STUDIO_ANIM_RAWPOS){
bone.pos = pAnim->PosVec3();
}
//TODO: STUDIO_ANIM_RAWROT
if(pAnim->flags&STUDIO_ANIM_RAWROT2)
bone.rot = pAnim->Quat();
if(pAnim->flags&STUDIO_ANIM_ANIMROT){
vec3 animAng=bones[b].rot;
mstudioanim_valueptr_t *rotV = pAnim->pRotV();
for(int j=0;j<3;j++){
mstudioanimvalue_t *animVal = rotV->pAnimvalue(j);
if(animVal){
int k = glm::min(f,animVal->num.valid-1);
animAng[j] += animVal[k+1].value*bones[b].rotscale[j];
}
}
bone.rot = glm::quat(animAng);
}
if(pAnim->flags&STUDIO_ANIM_ANIMPOS){
vec3 animPos=bones[b].pos;
mstudioanim_valueptr_t *posV = pAnim->pPosV();
for(int j=0;j<3;j++){
mstudioanimvalue_t *animVal = posV->pAnimvalue(j);
if(animVal){
int k = glm::min(f,animVal->num.valid-1);
//Log("animVal %d: %d num %d %d\n",j,animVal[1].value,animVal->num.total,animVal->num.valid);
animPos[j] += animVal[k+1].value*bones[b].posscale[j];
}
}
bone.pos = animPos;
}
}else{
bone = mdlData.bones[b];
}
mdlData.anims[i].frames[f].bones[b] = bone;
}
if(pAnim&&pAnim->bone==b)
pAnim = pAnim->pNext();
}
}
}
}
mstudiobodyparts_t *bodyparts = (mstudiobodyparts_t*)(data+mdlHeader->bodypartindex);
//for(int bpi = 0; bpi<mdlHeader->numbodyparts; bpi++)
int bpi = 0;
{
mstudiobodyparts_t &bp = bodyparts[bpi];
if(bp.nummodels!=1)
Log("MDL: %s bodyparts[%d] num models %d\n", name, bpi, bp.nummodels);
mstudiomodel_t *bpmodels = (mstudiomodel_t*)((char*)(bodyparts+bpi)+bp.modelindex);
//for(int mi = 0;mi<bp.nummodels; mi++)
int mi = 0;
{
mstudiomodel_t &bpm = bpmodels[mi];
mdlData.numMeshes = bpm.nummeshes;
if(bpm.nummeshes<=0){
Log( " mdl model %d without meshes",bpi);
}else{
Log("MDL: nummeshes %d\n",bpm.nummeshes);
mstudiomesh_t *mdlMeshes = (mstudiomesh_t*)((char*)(bpmodels+mi)+bpm.meshindex);
mdlData.meshes = new loadMDLOut_t::mdlOutMesh_t[bpm.nummeshes];
for(int mshi = 0; mshi<bpm.nummeshes; mshi++){
mstudiomesh_t &msh = mdlMeshes[mshi];
//Log(" mesh %d: material %d, vertices: num %d offset %d\n",mshi,msh.material, msh.numvertices, msh.vertexoffset);
//get materials
mdlData.meshes[mshi].vertOffs=msh.vertexoffset;
mdlData.meshes[mshi].vertCount=msh.numvertices;
}
}
}
}
//g_fs.Close(mdlfile);
delete[] data;
return true;
}
#else
//OLD variant
bool MDLLoader::LoadMDL(const char *name, loadMDLOut_t &mdlData){
IFile *mdlfile = g_fs.Open(name);
if(!mdlfile){
return false;
}
studiohdr_t mdlHeader;
mdlfile->Read(&mdlHeader,sizeof(studiohdr_t));
if(mdlHeader.id != IDSTUDIOHEADER)
return false;
if(mdlHeader.numbodyparts != 1)
Log("MDL: %s num bodyparts %d\n", name, mdlHeader.numbodyparts);
Log("MDL: num bones %d\n",mdlHeader.numbones);
mdlData.bones.Resize(mdlHeader.numbones);
mstudiobone_t *bones = new mstudiobone_t[mdlHeader.numbones];
mdlfile->Seek(mdlHeader.boneindex);
mdlfile->Read(bones,sizeof(mstudiobone_t)*mdlHeader.numbones);
for(int b=0; b<mdlHeader.numbones; b++){
/*char bonename[64];
mdlfile->Seek(mdlHeader.boneindex+sizeof(mstudiobone_t)*b+bones[b].sznameindex);
mdlfile->GetString(bonename,64);
Log( "bone %d: name %s, parent %d, pos (%f,%f,%f), rot(%f,%f,%f)\n",b,bonename,bones[b].parent,bones[b].pos.x,bones[b].pos.y,bones[b].pos.z, bones[b].rot.x,bones[b].rot.y,bones[b].rot.z);
*/
mdlData.bones[b]={bones[b].parent,bones[b].pos,bones[b].quat};
}
delete[] bones;
Log("MDL: numhitboxsets %d\n",mdlHeader.numhitboxsets);
if(mdlHeader.numhitboxsets){
mstudiohitboxset_t *hbsets = new mstudiohitboxset_t[mdlHeader.numhitboxsets];
mdlfile->Seek(mdlHeader.hitboxsetindex);
mdlfile->Read(hbsets,sizeof(mstudiohitboxset_t)*mdlHeader.numhitboxsets);
for(int i=0; i<mdlHeader.numhitboxsets; i++){
char temp[64];
mdlfile->Seek(mdlHeader.hitboxsetindex+sizeof(mstudiohitboxset_t)*i+hbsets[i].sznameindex);
mdlfile->GetString(temp,64);
Log("hitbox std %d: %s num %d\n",i,temp,hbsets[i].numhitboxes);
}
mstudiobbox_t *hitboxes = new mstudiobbox_t[hbsets[0].numhitboxes];
mdlfile->Seek(mdlHeader.hitboxsetindex+hbsets[0].hitboxindex);
mdlfile->Read(hitboxes,sizeof(mstudiobbox_t)*hbsets[0].numhitboxes);
for(int i=0;i<hbsets[0].numhitboxes;i++){
char temp[64]="(null)";
int of = sizeof(mstudiobbox_t)*i+hitboxes[i].szhitboxnameindex;
if(hitboxes[i].szhitboxnameindex){
mdlfile->Seek(mdlHeader.hitboxsetindex+hbsets[0].hitboxindex+of);
mdlfile->GetString(temp,64);
}
Log("bbox %d: %s(%d) bone %d, min(%.2f,%.2f,%.2f) max(%.2f,%.2f,%.2f)\n",i,temp,of,hitboxes[i].bone,hitboxes[i].bbmin.x,hitboxes[i].bbmin.y,hitboxes[i].bbmin.z,hitboxes[i].bbmax.x,hitboxes[i].bbmax.y,hitboxes[i].bbmax.z);
}
delete[] hitboxes;
delete[] hbsets;
}
Log("MDL: numlocalanim %d, numlocalseq %d\n",mdlHeader.numlocalanim,mdlHeader.numlocalseq);
if(mdlHeader.numlocalseq){
mstudioseqdesc_t *seqs = new mstudioseqdesc_t[mdlHeader.numlocalseq];
mdlfile->Seek(mdlHeader.localseqindex);
mdlfile->Read(seqs,sizeof(mstudioseqdesc_t)*mdlHeader.numlocalseq);
for(int i=0; i<mdlHeader.numlocalseq; i++){
char temp[64];
char temp2[64];
mdlfile->Seek(mdlHeader.localseqindex+sizeof(mstudioseqdesc_t)*i+seqs[i].szlabelindex);
mdlfile->GetString(temp,64);
mdlfile->Seek(mdlHeader.localseqindex+sizeof(mstudioseqdesc_t)*i+seqs[i].szactivitynameindex);
mdlfile->GetString(temp2,64);
Log( "seq %d: label %s, activity name %s\n",i,temp,temp2);
}
delete[] seqs;
}
if(mdlHeader.numlocalanim){
mstudioanimdesc_t *anims = new mstudioanimdesc_t[mdlHeader.numlocalanim];
mdlfile->Seek(mdlHeader.localanimindex);
mdlfile->Read(anims,sizeof(mstudioanimdesc_t)*mdlHeader.numlocalanim);
for(int i=0; i<mdlHeader.numlocalanim; i++){
mstudioanimdesc_t &ad=anims[i];
char temp[64];
mdlfile->Seek(mdlHeader.localanimindex+sizeof(mstudioanimdesc_t)*i+ad.sznameindex);
mdlfile->GetString(temp,64);
Log( "animdesc %d: label %s, num franes %d, block %d, index %d, section frames %d\n",i,temp,ad.numframes,ad.animblock,ad.animindex,ad.sectionframes);
}
mstudioanim_t anim0 = {};
mdlfile->Seek(mdlHeader.localanimindex+anims[0].animindex);
mdlfile->Read(&anim0,sizeof(mstudioanim_t));
Log("anim 0: bone %d, flags %d, next %d\n",anim0.bone,anim0.flags,anim0.nextoffset);
delete[] anims;
}
Log("MDL: numtextures %d, numcdtextures %d\n",mdlHeader.numtextures,mdlHeader.numcdtextures);
Log("MDL: numincludemodels %d\n",mdlHeader.numincludemodels);
if(mdlHeader.numincludemodels){
mstudiomodelgroup_t *incs = new mstudiomodelgroup_t[mdlHeader.numincludemodels];
mdlfile->Seek(mdlHeader.includemodelindex);
mdlfile->Read(incs,sizeof(mstudiomodelgroup_t)*mdlHeader.numincludemodels);
for(int i=0; i<mdlHeader.numincludemodels; i++){
char temp[64];
char temp2[64];
mdlfile->Seek(mdlHeader.includemodelindex+sizeof(mstudiomodelgroup_t)*i+incs[i].szlabelindex);
mdlfile->GetString(temp,64);
mdlfile->Seek(mdlHeader.includemodelindex+sizeof(mstudiomodelgroup_t)*i+incs[i].sznameindex);
mdlfile->GetString(temp2,64);
Log( "include model %d: label %s, name %s\n",i,temp,temp2);
}
delete[] incs;
}
mstudiobodyparts_t *bodyparts = new mstudiobodyparts_t[mdlHeader.numbodyparts];
mdlfile->Seek(mdlHeader.bodypartindex);
mdlfile->Read(bodyparts,sizeof(mstudiobodyparts_t)*mdlHeader.numbodyparts);
//for(int bpi = 0; bpi<mdlHeader.numbodyparts; bpi++)
int bpi = 0;
{
mstudiobodyparts_t &bp = bodyparts[bpi];
if(bp.nummodels!=1)
Log("MDL: %s bodyparts[%d] num models %d\n", name, bpi, bp.nummodels);
mstudiomodel_t *bpmodels = new mstudiomodel_t[bp.nummodels];
memset(bpmodels,0,sizeof(mstudiomodel_t));
mdlfile->Seek(mdlHeader.bodypartindex+(sizeof(mstudiobodyparts_t)*bpi)+bp.modelindex);
mdlfile->Read(bpmodels,sizeof(mstudiomodel_t)*bp.nummodels);
//for(int mi = 0;mi<bp.nummodels; mi++)
int mi = 0;
{
mstudiomodel_t &bpm = bpmodels[mi];
mdlData.numMeshes = bpm.nummeshes;
if(bpm.nummeshes<=0){
Log( " mdl model %d without meshes",bpi);
}else{
mstudiomesh_t *mdlMeshes = new mstudiomesh_t[bpm.nummeshes];
mdlfile->Seek(mdlHeader.bodypartindex+
(sizeof(mstudiobodyparts_t)*bpi)+bp.modelindex+
(sizeof(mstudiomodel_t)*mi)+bpm.meshindex);
mdlfile->Read(mdlMeshes,sizeof(mstudiomesh_t)*bpm.nummeshes);
mdlData.meshes = new loadMDLOut_t::mdlOutMesh_t[bpm.nummeshes];
for(int mshi = 0; mshi<bpm.nummeshes; mshi++){
mstudiomesh_t &msh = mdlMeshes[mshi];
//Log(" mesh %d: material %d, vertices: num %d offset %d\n",mshi,msh.material, msh.numvertices, msh.vertexoffset);
//get materials
mdlData.meshes[mshi].vertOffs=msh.vertexoffset;
mdlData.meshes[mshi].vertCount=msh.numvertices;
}
//TODO replace this by small structure
//bpm.meshindex = (int)mdlMeshes;
delete[] mdlMeshes;
}
}
delete[] bpmodels;
}
delete[] bodyparts;
g_fs.Close(mdlfile);
return true;
}
#endif
bool MDLLoader::LoadVVD(const char *name, Model *mdl, int lod)
{
IFile *vvdfile = g_fs.Open(name);
if(!vvdfile){
return NULL;
}
vertexFileHeader_t vvdHeader;
vvdfile->Read(&vvdHeader,sizeof(vertexFileHeader_t));
//Log( "vvd header: version %d, numLODs %d, numLODVertexes[0] %d numFixups %d\n",vvdHeader.version, vvdHeader.numLODs, vvdHeader.numLODVertexes[0],vvdHeader.numFixups);
if(lod >= vvdHeader.numLODs)
lod = vvdHeader.numLODs-1;
int numVerts = vvdHeader.numLODVertexes[lod];
char *verts = new char[sizeof(mstudiovertex_t)*numVerts];
if(!vvdHeader.numFixups){
vvdfile->Seek(vvdHeader.vertexDataStart);
vvdfile->Read(verts,sizeof(mstudiovertex_t)*numVerts);
}else{
//Log("Start fixup vertices %s\n",fileName);
vertexFileFixup_t *fixupTable = new vertexFileFixup_t[vvdHeader.numFixups];
vvdfile->Seek(vvdHeader.fixupTableStart);
vvdfile->Read(fixupTable,sizeof(vertexFileFixup_t)*vvdHeader.numFixups);
int target = 0;
for(int f=0; f<vvdHeader.numFixups; f++){
if(fixupTable[f].lod < lod)
continue;
//Log( "fixup %d, lod %d, id %d, num %d\n", f, fixupTable[f].lod, fixupTable[f].sourceVertexID, fixupTable[f].numVertexes);
vvdfile->Seek(vvdHeader.vertexDataStart + fixupTable[f].sourceVertexID*sizeof(mstudiovertex_t));
vvdfile->Read((verts+target*sizeof(mstudiovertex_t)),fixupTable[f].numVertexes*sizeof(mstudiovertex_t));
target += fixupTable[f].numVertexes;
}
//Log( "read %d fixup verts\n",target);
delete[] fixupTable;
}
Log( "VVD: read %d verts\n",numVerts);
mdl->vertexCount = numVerts;
mdl->vertexFormat.Resize(5);
mdl->vertexFormat[0]=vertAttrib_t(0,3,GL_FLOAT,false,sizeof(mstudiovertex_t),16);//pos
mdl->vertexFormat[1]=vertAttrib_t(1,3,GL_FLOAT,false,sizeof(mstudiovertex_t),28);//norm
mdl->vertexFormat[2]=vertAttrib_t(2,2,GL_FLOAT,false,sizeof(mstudiovertex_t),40);//uv
mdl->vertexFormat[3]=vertAttrib_t(3,3,GL_FLOAT,false,sizeof(mstudiovertex_t),0);//weight
mdl->vertexFormat[4]=vertAttrib_t(4,4,GL_UNSIGNED_BYTE,false,sizeof(mstudiovertex_t),12);//bone ids
mdl->verts = verts;
g_fs.Close(vvdfile);
return true;
}
int CalcVTXIndsCount(vtxFileHeader_t *vtxHeader, int lod){
int numInds = 0;
//for(int bpi=0; bpi<vtxHeader->numBodyParts; pbi++)
int bpi = 0;
{
vtxBodyPartHeader_t *bp = (vtxBodyPartHeader_t*)((char*)vtxHeader+vtxHeader->bodyPartOffset)+bpi;
//for(int mi=0; mi<bp->numModels; mi++)
int mi = 0;
{
vtxModelHeader_t *model = (vtxModelHeader_t*)(((char*)bp)+bp->modelOffset)+mi;
vtxModelLODHeader_t *lodHdr = (vtxModelLODHeader_t*)(((char*)model)+model->lodOffset)+lod;
for(int mshi=0; mshi<lodHdr->numMeshes; mshi++)
{
vtxMeshHeader_t *msh = (vtxMeshHeader_t*)(((char*)lodHdr)+lodHdr->meshOffset)+mshi;
//if(msh->numStripGroups!=1)
// Log("VTX: mesh %d numStripGroups %d\n",mshi,msh->numStripGroups);
for(int sgi=0; sgi<msh->numStripGroups; sgi++)
//int sgi=0;
{
vtxStripGroupHeader_t *sg = ((vtxStripGroupHeader_t*)(((char*)msh)+msh->stripGroupHeaderOffset))+sgi;
numInds += sg->numIndices;
}
}
}
}
return numInds;
}
bool MDLLoader::LoadVTX(const char *name, Model *mdl, loadMDLOut_t &mdlData, int lod){
IFile *vtxfile = g_fs.Open(name);
if(!vtxfile){
return false;
}
char *data = new char[vtxfile->GetLen()];
vtxfile->Seek(0);
vtxfile->Read(data,vtxfile->GetLen());
g_fs.Close(vtxfile);
char *inds = NULL;
int numInds = 0;
vtxFileHeader_t *vtxHeader = (vtxFileHeader_t*)data;
//Log( "vtx header: version %d, numLODs %d, bodyparts num %d offset 0x%x\n",vtxHeader.version, vtxHeader.numLODs, vtxHeader.numBodyParts, vtxHeader.bodyPartOffset);
numInds = CalcVTXIndsCount(vtxHeader, lod);
Log("VTX: inds num %d\n",numInds);
inds = new char[numInds*sizeof(uint32_t)];
int curInd = 0;
//for(int bpi=0; bpi<vtxHeader->numBodyParts; pbi++)
int bpi = 0;
{
vtxBodyPartHeader_t *bp = (vtxBodyPartHeader_t*)(data+vtxHeader->bodyPartOffset)+bpi;
//for(int mi=0; mi<bp->numModels; mi++)
int mi = 0;
{
vtxModelHeader_t *model = (vtxModelHeader_t*)(((char*)bp)+bp->modelOffset)+mi;
vtxModelLODHeader_t *lodHdr = (vtxModelLODHeader_t*)(((char*)model)+model->lodOffset)+lod;
for(int mshi=0; mshi<lodHdr->numMeshes; mshi++)
//int mshi=0;
{
vtxMeshHeader_t *msh = (vtxMeshHeader_t*)(((char*)lodHdr)+lodHdr->meshOffset)+mshi;
//Log("VTX: mesh %d: numStripGroups %d, flags %d\n",mshi,msh->numStripGroups,msh->flags);
mdlData.meshes[mshi].indOffs = curInd;
mdlData.meshes[mshi].indCount = 0;
for(int sgi=0; sgi<msh->numStripGroups; sgi++)
//int sgi=0;
{
vtxStripGroupHeader_t *sg = ((vtxStripGroupHeader_t*)(((char*)msh)+msh->stripGroupHeaderOffset))+sgi;
//Log( " vtx strip group %d: Verts num %d offset 0x%x, Indices num %d offset 0x%x, Strips num %d offset 0x%x, flags %d\n",sgi,sg->numVerts,sg->vertOffset,sg->numIndices,sg->indexOffset,sg->numStrips,sg->stripOffset,sg->flags);
vtxVertex_t *vtxVerts = (vtxVertex_t*)(((char*)sg)+sg->vertOffset);
uint16_t *vtxInds = (uint16_t*)(((char*)sg)+sg->indexOffset);
for(int ind=0; ind<sg->numIndices; ind++){
//int vertOffs = ((mstudiomesh_t*)((mstudiomodel_t*)bodyparts[bph].modelindex)[mh].meshindex)[msh].vertexoffset;
int vertOffs = mdlData.meshes[mshi].vertOffs;
((uint32_t*)inds)[curInd+ind] = vertOffs + vtxVerts[vtxInds[ind]].origMeshVertID;
}
curInd += sg->numIndices;
mdlData.meshes[mshi].indCount += sg->numIndices;
}
}
}
}
delete[] data;
mdl->inds = inds;
mdl->indexCount = numInds;
mdl->indexType = GL_UNSIGNED_INT;
mdl->indexSize = 4;
return true;
}