#include "log.h" #include "mdl_loader.h" #include #include #include "system/FileSystem.h" #include #include #include #include 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;isubmeshes[i]={(uint32_t)mdlData.meshes[i].indOffs,mdlData.meshes[i].indCount,i}; } mdl->skeleton.Resize(mdlData.bones.size); for(int i=0;iskeleton[i] = mdlData.bones[i]; } mdl->animations.Resize(mdlData.anims.size); for(int i=0;ianimations[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;fanimations[i].frames[f].bones.Resize(mdlData.anims[i].frames[f].bones.size); for(int b=0;banimations[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; inumincludemodels; 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; inumbones; 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; inumtextures; 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; inumlocalseq; 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; inumlocalanim; 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;fnumbones); } mstudioanim_t *pAnim = (mstudioanim_t*)((char*)(anims+i)+anims[i].animindex); for(int b=0;bnumbones;b++){ for(int f=0;fbone==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; bpinumbodyparts; 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;miRead(&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; bSeek(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; iSeek(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;iSeek(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; iSeek(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; iSeek(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; iSeek(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; bpiSeek(mdlHeader.bodypartindex+(sizeof(mstudiobodyparts_t)*bpi)+bp.modelindex); mdlfile->Read(bpmodels,sizeof(mstudiomodel_t)*bp.nummodels); //for(int mi = 0;miSeek(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; mshiRead(&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; fSeek(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; bpinumBodyParts; pbi++) int bpi = 0; { vtxBodyPartHeader_t *bp = (vtxBodyPartHeader_t*)((char*)vtxHeader+vtxHeader->bodyPartOffset)+bpi; //for(int mi=0; minumModels; 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; mshinumMeshes; 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; sginumStripGroups; 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; bpinumBodyParts; pbi++) int bpi = 0; { vtxBodyPartHeader_t *bp = (vtxBodyPartHeader_t*)(data+vtxHeader->bodyPartOffset)+bpi; //for(int mi=0; minumModels; 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; mshinumMeshes; 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; sginumStripGroups; 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; indnumIndices; 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; }