//========= Copyright 1996-2005, Valve Corporation, All rights reserved. ============// // // Purpose: // // $NoKeywords: $ // //=============================================================================// #ifndef STUDIO_H #define STUDIO_H #ifdef _WIN32 #pragma once #endif #include "basetypes.h" #include "vector2d.h" #include "vector.h" #include "vector4d.h" #include "compressed_vector.h" #include "tier0/dbg.h" #include "mathlib.h" #include "utlvector.h" #ifndef offsetof #define offsetof(s,m) (size_t)&(((s *)0)->m) #endif //----------------------------------------------------------------------------- // forward declarations //----------------------------------------------------------------------------- class IMaterial; class IMesh; struct virtualmodel_t; namespace OptimizedModel { struct StripHeader_t; } /* ============================================================================== STUDIO MODELS Studio models are position independent, so the cache manager can move them. ============================================================================== */ #define STUDIO_VERSION 44 #ifndef _XBOX #define MAXSTUDIOTRIANGLES 65536 // TODO: tune this #define MAXSTUDIOVERTS 65536 // TODO: tune this #define MAXSTUDIOFLEXVERTS 10000 // max number of verts that can be flexed per mesh. TODO: tune this #else #define MAXSTUDIOTRIANGLES 25000 #define MAXSTUDIOVERTS 10000 #define MAXSTUDIOFLEXVERTS 1000 #endif #define MAXSTUDIOSKINS 32 // total textures #define MAXSTUDIOBONES 128 // total bones actually used #define MAXSTUDIOBLENDS 32 #define MAXSTUDIOFLEXDESC 1024 // maximum number of low level flexes (actual morph targets) #define MAXSTUDIOFLEXCTRL 64 // maximum number of flexcontrollers (input sliders) #define MAXSTUDIOPOSEPARAM 24 #define MAXSTUDIOBONECTRLS 4 #define MAXSTUDIOANIMBLOCKS 256 #define MAXSTUDIOBONEBITS 7 // NOTE: MUST MATCH MAXSTUDIOBONES // NOTE!!! : Changing this number also changes the vtx file format!!!!! #define MAX_NUM_BONES_PER_VERT 3 //Adrian - Remove this when we completely phase out the old event system. #define NEW_EVENT_STYLE ( 1 << 10 ) struct mstudiodata_t { int count; int offset; }; #define STUDIO_PROC_AXISINTERP 1 #define STUDIO_PROC_QUATINTERP 2 #define STUDIO_PROC_AIMATBONE 3 #define STUDIO_PROC_AIMATATTACH 4 struct mstudioaxisinterpbone_t { int control;// local transformation of this bone used to calc 3 point blend int axis; // axis to check Vector pos[6]; // X+, X-, Y+, Y-, Z+, Z- Quaternion quat[6];// X+, X-, Y+, Y-, Z+, Z- private: // No copy constructors allowed mstudioaxisinterpbone_t(const mstudioaxisinterpbone_t& vOther); }; struct mstudioquatinterpinfo_t { float inv_tolerance; // 1 / radian angle of trigger influence Quaternion trigger; // angle to match Vector pos; // new position Quaternion quat; // new angle private: // No copy constructors allowed mstudioquatinterpinfo_t(const mstudioquatinterpinfo_t& vOther); }; struct mstudioquatinterpbone_t { int control;// local transformation to check int numtriggers; int triggerindex; inline mstudioquatinterpinfo_t *pTrigger( int i ) const { return (mstudioquatinterpinfo_t *)(((byte *)this) + triggerindex) + i; }; private: // No copy constructors allowed mstudioquatinterpbone_t(const mstudioquatinterpbone_t& vOther); }; struct mstudioaimatbone_t { int parent; int aim; // Might be bone or attach Vector aimvector; Vector upvector; Vector basepos; private: // No copy constructors allowed mstudioaimatbone_t(const mstudioaimatbone_t& vOther); }; // bones struct mstudiobone_t { int sznameindex; inline char * const pszName( void ) const { return ((char *)this) + sznameindex; } int parent; // parent bone int bonecontroller[6]; // bone controller index, -1 == none // default values Vector pos; Quaternion quat; RadianEuler rot; // compression scale 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 inline void *pProcedure( ) const { if (procindex == 0) return NULL; else return (void *)(((byte *)this) + procindex); }; int surfacepropidx; // index into string tablefor property name inline char * const pszSurfaceProp( void ) const { return ((char *)this) + surfacepropidx; } int contents; // See BSPFlags.h for the contents flags int unused[8]; // remove as appropriate private: // No copy constructors allowed mstudiobone_t(const mstudiobone_t& vOther); }; #define BONE_CALCULATE_MASK 0x1F #define BONE_PHYSICALLY_SIMULATED 0x01 // bone is physically simulated when physics are active #define BONE_PHYSICS_PROCEDURAL 0x02 // procedural when physics is active #define BONE_ALWAYS_PROCEDURAL 0x04 // bone is always procedurally animated #define BONE_SCREEN_ALIGN_SPHERE 0x08 // bone aligns to the screen, not constrained in motion. #define BONE_SCREEN_ALIGN_CYLINDER 0x10 // bone aligns to the screen, constrained by it's own axis. #define BONE_USED_MASK 0x0007FF00 #define BONE_USED_BY_ANYTHING 0x0007FF00 #define BONE_USED_BY_HITBOX 0x00000100 // bone (or child) is used by a hit box #define BONE_USED_BY_ATTACHMENT 0x00000200 // bone (or child) is used by an attachment point #define BONE_USED_BY_VERTEX_MASK 0x0003FC00 #define BONE_USED_BY_VERTEX_LOD0 0x00000400 // bone (or child) is used by the toplevel model via skinned vertex #define BONE_USED_BY_VERTEX_LOD1 0x00000800 #define BONE_USED_BY_VERTEX_LOD2 0x00001000 #define BONE_USED_BY_VERTEX_LOD3 0x00002000 #define BONE_USED_BY_VERTEX_LOD4 0x00004000 #define BONE_USED_BY_VERTEX_LOD5 0x00008000 #define BONE_USED_BY_VERTEX_LOD6 0x00010000 #define BONE_USED_BY_VERTEX_LOD7 0x00020000 #define BONE_USED_BY_BONE_MERGE 0x00040000 // bone is available for bone merge to occur against it #define BONE_USED_BY_VERTEX_AT_LOD(lod) ( BONE_USED_BY_VERTEX_LOD0 << (lod) ) #define BONE_USED_BY_ANYTHING_AT_LOD(lod) ( ( BONE_USED_BY_ANYTHING & ~BONE_USED_BY_VERTEX_MASK ) | BONE_USED_BY_VERTEX_AT_LOD(lod) ) #define MAX_NUM_LODS 8 #define BONE_TYPE_MASK 0x00F00000 #define BONE_FIXED_ALIGNMENT 0x00100000 // bone can't spin 360 degrees, all interpolation is normalized around a fixed orientation #define BONE_HAS_SAVEFRAME_POS 0x00200000 #define BONE_HAS_SAVEFRAME_ROT 0x00400000 // bone controllers struct mstudiobonecontroller_t { int bone; // -1 == 0 int type; // X, Y, Z, XR, YR, ZR, M float start; float end; int rest; // byte index value at rest int inputfield; // 0-3 user set controller, 4 mouth int unused[8]; }; // intersection boxes 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]; char* pszHitboxName() { if( szhitboxnameindex == 0 ) return ""; return ((char*)this) + szhitboxnameindex; } mstudiobbox_t() {} private: // No copy constructors allowed mstudiobbox_t(const mstudiobbox_t& vOther); }; // 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; } }; struct mstudiomodelgrouplookup_t { int modelgroup; int indexwithingroup; }; // events struct mstudioevent_t { float cycle; int event; int type; inline const char * pszOptions( void ) const { return options; } char options[64]; int szeventindex; inline char * const pszEventName( void ) const { return ((char *)this) + szeventindex; } }; #define ATTACHMENT_FLAG_WORLD_ALIGN 0x10000 // attachment struct mstudioattachment_t { int sznameindex; inline char * const pszName( void ) const { return ((char *)this) + sznameindex; } unsigned int flags; int localbone; matrix3x4_t local; // attachment point int unused[8]; }; #define IK_SELF 1 #define IK_WORLD 2 #define IK_GROUND 3 #define IK_RELEASE 4 #define IK_ATTACHMENT 5 #define IK_UNLATCH 6 struct mstudioikerror_t { Vector pos; Quaternion q; mstudioikerror_t() {} private: // No copy constructors allowed mstudioikerror_t(const mstudioikerror_t& vOther); }; union mstudioanimvalue_t; struct mstudiocompressedikerror_t { float scale[6]; short offset[6]; inline mstudioanimvalue_t *pAnimvalue( int i ) const { if (offset[i] > 0) return (mstudioanimvalue_t *)(((byte *)this) + offset[i]); else return NULL; }; private: // No copy constructors allowed mstudiocompressedikerror_t(const mstudiocompressedikerror_t& vOther); }; struct mstudioikrule_t { int index; int type; int chain; int bone; int slot; // iktarget slot. Usually same as chain. float height; float radius; float floor; Vector pos; Quaternion q; int compressedikerrorindex; inline mstudiocompressedikerror_t *pCompressedError() const { return (mstudiocompressedikerror_t *)(((byte *)this) + compressedikerrorindex); }; int unused2; int iStart; int ikerrorindex; inline mstudioikerror_t *pError( int i ) const { return (ikerrorindex) ? (mstudioikerror_t *)(((byte *)this) + ikerrorindex) + (i - iStart) : NULL; }; float start; // beginning of influence float peak; // start of full influence float tail; // end of full influence float end; // end of all influence int unused3; float contact; // frame footstep makes ground concact int unused4; int unused5; int unused6; int unused7; int unused8; int szattachmentindex; // name of world attachment inline char * const pszAttachment( void ) const { return ((char *)this) + szattachmentindex; } int unused[7]; mstudioikrule_t() {} private: // No copy constructors allowed mstudioikrule_t(const mstudioikrule_t& vOther); }; struct mstudioiklock_t { int chain; float flPosWeight; float flLocalQWeight; int flags; int unused[4]; }; // 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 #define STUDIO_ANIM_RAWROT 0x02 #define STUDIO_ANIM_ANIMPOS 0x04 #define STUDIO_ANIM_ANIMROT 0x08 #define STUDIO_ANIM_DELTA 0x10 // 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 *pQuat( void ) const { return (Quaternion48 *)(pData()); }; inline Vector48 *pPos( void ) const { return (Vector48 *)(pData() + ((flags & STUDIO_ANIM_RAWROT) != 0) * sizeof( *pQuat() )); }; short nextoffset; inline mstudioanim_t *pNext( void ) const { if (nextoffset != 0) return (mstudioanim_t *)(((byte *)this) + nextoffset); else return NULL; }; }; struct mstudiomovement_t { int endframe; int motionflags; float v0; // velocity at start of block float v1; // velocity at end of block float angle; // YAW rotation at end of this blocks movement Vector vector; // movement vector relative to this blocks initial angle Vector position; // relative to start of animation??? private: // No copy constructors allowed mstudiomovement_t(const mstudiomovement_t& vOther); }; struct studiohdr_t; // used for piecewise loading of animation data struct mstudioanimblock_t { int datastart; int dataend; }; 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; }; Vector unused1; // per animation bounding box Vector unused2; int animblock; int animindex; mstudioanim_t *pAnim( void ) const; 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 unused[7]; // remove as appropriate private: // No copy constructors allowed mstudioanimdesc_t(const mstudioanimdesc_t& vOther); }; struct mstudioikrule_t; struct mstudioautolayer_t { //private: short iSequence; short iPose; //public: int flags; float start; // beginning of influence float peak; // start of full influence float tail; // end of full influence float end; // end of all influence }; // sequence descriptions struct mstudioseqdesc_t { int baseptr; inline studiohdr_t *pStudiohdr( void ) const { return (studiohdr_t *)(((byte *)this) + 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; // Index into array of shorts which is groupsize[0] x groupsize[1] in length int animindexindex; inline int anim( int x, int y ) const { if ( x >= groupsize[0] ) { x = groupsize[0] - 1; } if ( y >= groupsize[1] ) { y = groupsize[ 1 ] - 1; } int offset = y * groupsize[0] + x; short *blends = (short *)(((byte *)this) + animindexindex); int value = (int)blends[ offset ]; return value; } 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 unused[8]; // remove/add as appropriate (grow back to 8 ints on version change!) private: // No copy constructors allowed mstudioseqdesc_t(const mstudioseqdesc_t& vOther); }; struct mstudioposeparamdesc_t { int sznameindex; inline char * const pszName( void ) const { return ((char *)this) + sznameindex; } int flags; // ???? float start; // starting value float end; // ending value float loop; // looping range, 0 for no looping, 360 for rotations, etc. }; struct mstudioflexdesc_t { int szFACSindex; inline char * const pszFACS( void ) const { return ((char *)this) + szFACSindex; } }; struct mstudioflexcontroller_t { int sztypeindex; inline char * const pszType( void ) const { return ((char *)this) + sztypeindex; } int sznameindex; inline char * const pszName( void ) const { return ((char *)this) + sznameindex; } mutable int link; // remapped at load time to master list float min; float max; }; //#if _XBOX // these are the on-disk format vertex anims struct dstudiovertanim_t { short index; byte speed; // 255/max_length_in_flex byte side; // 255/left_right Vector48 delta; Vector48 ndelta; private: // No copy constructors allowed dstudiovertanim_t(const dstudiovertanim_t& vOther); }; //#endif const float g_VertAnimFixedPointScale = 1.0f / 4096.0f; const float g_VertAnimFixedPointScaleInv = 1.0f / g_VertAnimFixedPointScale; // this is the memory image of vertex anims (16-bit fixed point) struct mstudiovertanim_t { short index; byte speed; // 255/max_length_in_flex byte side; // 255/left_right private: // JasonM changing this type a lot, to prefer fixed point 16 bit... union { short delta[3]; float16 flDelta[3]; }; union { short ndelta[3]; float16 flNDelta[3]; }; public: inline Vector GetDeltaFixed() { return Vector( delta[0]*g_VertAnimFixedPointScale, delta[1]*g_VertAnimFixedPointScale, delta[2]*g_VertAnimFixedPointScale ); } inline Vector GetNDeltaFixed() { return Vector( ndelta[0]*g_VertAnimFixedPointScale, ndelta[1]*g_VertAnimFixedPointScale, ndelta[2]*g_VertAnimFixedPointScale ); } inline void GetDeltaFixed4DAligned( Vector4DAligned *vFillIn ) { vFillIn->Set( delta[0]*g_VertAnimFixedPointScale, delta[1]*g_VertAnimFixedPointScale, delta[2]*g_VertAnimFixedPointScale, 0.0f ); } inline void GetNDeltaFixed4DAligned( Vector4DAligned *vFillIn ) { vFillIn->Set( ndelta[0]*g_VertAnimFixedPointScale, ndelta[1]*g_VertAnimFixedPointScale, ndelta[2]*g_VertAnimFixedPointScale, 0.0f ); } inline Vector GetDeltaFloat() { return Vector (flDelta[0].GetFloat(), flDelta[1].GetFloat(), flDelta[2].GetFloat()); } inline Vector GetNDeltaFloat() { return Vector (flNDelta[0].GetFloat(), flNDelta[1].GetFloat(), flNDelta[2].GetFloat()); } inline void SetDeltaFixed( const Vector vInput ) { delta[0] = vInput.x * g_VertAnimFixedPointScaleInv; delta[1] = vInput.y * g_VertAnimFixedPointScaleInv; delta[2] = vInput.z * g_VertAnimFixedPointScaleInv; } inline void SetNDeltaFixed( const Vector vInputNormal ) { ndelta[0] = vInputNormal.x * g_VertAnimFixedPointScaleInv; ndelta[1] = vInputNormal.y * g_VertAnimFixedPointScaleInv; ndelta[2] = vInputNormal.z * g_VertAnimFixedPointScaleInv; } // Ick...can also force fp16 data into this structure for writing to file in legacy format... inline void SetDeltaFloat( const Vector vInput ) { flDelta[0].SetFloat( vInput.x ); flDelta[1].SetFloat( vInput.y ); flDelta[2].SetFloat( vInput.z ); } inline void SetNDeltaFloat( const Vector vInputNormal ) { flNDelta[0].SetFloat( vInputNormal.x ); flNDelta[1].SetFloat( vInputNormal.y ); flNDelta[2].SetFloat( vInputNormal.z ); } private: // No copy constructors allowed mstudiovertanim_t(const mstudiovertanim_t& vOther); }; struct mstudioflex_t { int flexdesc; // input value float target0; // zero float target1; // one float target2; // one float target3; // zero int numverts; int vertindex; inline mstudiovertanim_t *pVertanim( int i ) const { return (mstudiovertanim_t *)(((byte *)this) + vertindex) + i; }; int flexpair; // second flex desc int unused[7]; }; struct mstudioflexop_t { int op; union { int index; float value; } d; }; struct mstudioflexrule_t { int flex; int numops; int opindex; inline mstudioflexop_t *iFlexOp( int i ) const { return (mstudioflexop_t *)(((byte *)this) + opindex) + i; }; }; // 16 bytes struct mstudioboneweight_t { float weight[MAX_NUM_BONES_PER_VERT]; char bone[MAX_NUM_BONES_PER_VERT]; byte numbones; // byte material; // short firstref; // short lastref; }; // 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); }; // 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 mutable void *clientmaterial; // gary, replace with client material pointer if used int unused[10]; }; // eyeball struct mstudioeyeball_t { int sznameindex; inline char * const pszName( void ) const { return ((char *)this) + sznameindex; } int bone; Vector org; float zoffset; float radius; Vector up; Vector forward; int texture; int iris_material; float iris_scale; int glint_material; // !!! int upperflexdesc[3]; // index of raiser, neutral, and lowerer flexdesc that is set by flex controllers int lowerflexdesc[3]; float uppertarget[3]; // angle (radians) of raised, neutral, and lowered lid positions float lowertarget[3]; int upperlidflexdesc; // index of flex desc that actual lid flexes look to int lowerlidflexdesc; int unused[12]; private: // No copy constructors allowed mstudioeyeball_t(const mstudioeyeball_t& vOther); }; // ikinfo struct mstudioiklink_t { int bone; Vector kneeDir; // ideal bending direction (per link, if applicable) Vector unused0; // unused private: // No copy constructors allowed mstudioiklink_t(const mstudioiklink_t& vOther); }; struct mstudioikchain_t { int sznameindex; inline char * const pszName( void ) const { return ((char *)this) + sznameindex; } int linktype; int numlinks; int linkindex; inline mstudioiklink_t *pLink( int i ) const { return (mstudioiklink_t *)(((byte *)this) + linkindex) + i; }; // FIXME: add unused entries }; struct mstudioiface_t { unsigned short a, b, c; // Indices to vertices }; struct mstudiomodel_t; struct mstudio_modelvertexdata_t { Vector *Position( int i ) const; Vector *Normal( int i ) const; Vector4D *TangentS( int i ) const; Vector2D *Texcoord( int i ) const; mstudioboneweight_t *BoneWeights( int i ) const; mstudiovertex_t *Vertex( int i ) const; bool HasTangentData( void ) const; // base of external vertex data stores void *pVertexData; void *pTangentData; }; struct mstudio_meshvertexdata_t { Vector *Position( int i ) const; Vector *Normal( int i ) const; Vector4D *TangentS( int i ) const; Vector2D *Texcoord( int i ) const; mstudioboneweight_t *BoneWeights( int i ) const; mstudiovertex_t *Vertex( int i ) const; bool HasTangentData( void ) const; // indirection to this mesh's model's vertex data const mstudio_modelvertexdata_t *modelvertexdata; // used for fixup calcs when culling top level lods // expected number of mesh verts at desired lod int numLODVertexes[MAX_NUM_LODS]; }; struct mstudiomesh_t { int material; int modelindex; mstudiomodel_t *pModel() const; int numvertices; // number of unique vertices/normals/texcoords int vertexoffset; // vertex mstudiovertex_t const mstudio_meshvertexdata_t *GetVertexData(); int numflexes; // vertex animation int flexindex; inline mstudioflex_t *pFlex( int i ) const { return (mstudioflex_t *)(((byte *)this) + flexindex) + i; }; // special codes for material operations int materialtype; int materialparam; // a unique ordinal for this mesh int meshid; Vector center; mstudio_meshvertexdata_t vertexdata; int unused[8]; // remove as appropriate 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; inline mstudiomesh_t *pMesh( int i ) const { return (mstudiomesh_t *)(((byte *)this) + meshindex) + i; }; // cache purposes int numvertices; // number of unique vertices/normals/texcoords int vertexindex; // vertex Vector int tangentsindex; // tangents Vector const mstudio_modelvertexdata_t *GetVertexData(); int numattachments; int attachmentindex; int numeyeballs; int eyeballindex; inline mstudioeyeball_t *pEyeball( int i ) { return (mstudioeyeball_t *)(((byte *)this) + eyeballindex) + i; }; mstudio_modelvertexdata_t vertexdata; int unused[8]; // remove as appropriate }; inline bool mstudio_modelvertexdata_t::HasTangentData( void ) const { return (pTangentData != NULL); } inline mstudiovertex_t *mstudio_modelvertexdata_t::Vertex( int i ) const { mstudiomodel_t *modelptr = (mstudiomodel_t *)((byte *)this - offsetof(mstudiomodel_t,vertexdata)); return (mstudiovertex_t *)((byte *)pVertexData + modelptr->vertexindex) + i; } inline Vector *mstudio_modelvertexdata_t::Position( int i ) const { return &Vertex(i)->m_vecPosition; } inline Vector *mstudio_modelvertexdata_t::Normal( int i ) const { return &Vertex(i)->m_vecNormal; } inline Vector4D *mstudio_modelvertexdata_t::TangentS( int i ) const { // NOTE: The tangents vector is 16-bytes in a separate array // because it only exists on the high end, and if I leave it out // of the mstudiovertex_t, the vertex is 64-bytes (good for low end) mstudiomodel_t *modelptr = (mstudiomodel_t *)((byte *)this - offsetof(mstudiomodel_t,vertexdata)); return (Vector4D *)((byte *)pTangentData + modelptr->tangentsindex) + i; } inline Vector2D *mstudio_modelvertexdata_t::Texcoord( int i ) const { return &Vertex(i)->m_vecTexCoord; } inline mstudioboneweight_t *mstudio_modelvertexdata_t::BoneWeights( int i ) const { return &Vertex(i)->m_BoneWeights; } inline mstudiomodel_t *mstudiomesh_t::pModel() const { return (mstudiomodel_t *)(((byte *)this) + modelindex); } inline bool mstudio_meshvertexdata_t::HasTangentData( void ) const { return modelvertexdata->HasTangentData(); } inline const mstudio_meshvertexdata_t *mstudiomesh_t::GetVertexData() { // get this mesh's model's vertex data vertexdata.modelvertexdata = this->pModel()->GetVertexData(); return &vertexdata; } inline Vector *mstudio_meshvertexdata_t::Position( int i ) const { mstudiomesh_t *meshptr = (mstudiomesh_t *)((byte *)this - offsetof(mstudiomesh_t,vertexdata)); return modelvertexdata->Position( meshptr->vertexoffset + i ); }; inline Vector *mstudio_meshvertexdata_t::Normal( int i ) const { mstudiomesh_t *meshptr = (mstudiomesh_t *)((byte *)this - offsetof(mstudiomesh_t,vertexdata)); return modelvertexdata->Normal( meshptr->vertexoffset + i ); }; inline Vector4D *mstudio_meshvertexdata_t::TangentS( int i ) const { mstudiomesh_t *meshptr = (mstudiomesh_t *)((byte *)this - offsetof(mstudiomesh_t,vertexdata)); return modelvertexdata->TangentS( meshptr->vertexoffset + i ); } inline Vector2D *mstudio_meshvertexdata_t::Texcoord( int i ) const { mstudiomesh_t *meshptr = (mstudiomesh_t *)((byte *)this - offsetof(mstudiomesh_t,vertexdata)); return modelvertexdata->Texcoord( meshptr->vertexoffset + i ); }; inline mstudioboneweight_t *mstudio_meshvertexdata_t::BoneWeights( int i ) const { mstudiomesh_t *meshptr = (mstudiomesh_t *)((byte *)this - offsetof(mstudiomesh_t,vertexdata)); return modelvertexdata->BoneWeights( meshptr->vertexoffset + i ); }; inline mstudiovertex_t *mstudio_meshvertexdata_t::Vertex( int i ) const { mstudiomesh_t *meshptr = (mstudiomesh_t *)((byte *)this - offsetof(mstudiomesh_t,vertexdata)); return modelvertexdata->Vertex( meshptr->vertexoffset + i ); } // a group of studio model data enum studiomeshgroupflags_t { MESHGROUP_IS_FLEXED = 0x1, MESHGROUP_IS_HWSKINNED = 0x2, MESHGROUP_IS_DELTA_FLEXED = 0x4 }; // ---------------------------------------------------------- // runtime stuff // ---------------------------------------------------------- struct studiomeshgroup_t { IMesh *m_pMesh; int m_NumStrips; int m_Flags; // see studiomeshgroupflags_t OptimizedModel::StripHeader_t *m_pStripData; unsigned short *m_pGroupIndexToMeshIndex; int m_NumVertices; int *m_pUniqueTris; // for performance measurements unsigned short *m_pIndices; #ifndef _XBOX bool m_MeshNeedsRestore; #endif short m_ColorMeshID; inline unsigned short MeshIndex( int i ) const { return m_pGroupIndexToMeshIndex[m_pIndices[i]]; } }; // studio model data struct studiomeshdata_t { int m_NumGroup; studiomeshgroup_t* m_pMeshGroup; }; struct studioloddata_t { // not needed - this is really the same as studiohwdata_t.m_NumStudioMeshes //int m_NumMeshes; studiomeshdata_t *m_pMeshData; // there are studiohwdata_t.m_NumStudioMeshes of these. float m_SwitchPoint; // one of these for each lod since we can switch to simpler materials on lower lods. int numMaterials; IMaterial **ppMaterials; /* will have studiohdr_t.numtextures elements allocated */ // hack - this needs to go away. int *pMaterialFlags; /* will have studiohdr_t.numtextures elements allocated */ }; struct studiohwdata_t { int m_RootLOD; // calced and clamped, nonzero for lod culling int m_NumLODs; studioloddata_t *m_pLODs; int m_NumStudioMeshes; }; // ---------------------------------------------------------- // ---------------------------------------------------------- // body part index struct mstudiobodyparts_t { int sznameindex; inline char * const pszName( void ) const { return ((char *)this) + sznameindex; } int nummodels; int base; int modelindex; // index into models array inline mstudiomodel_t *pModel( int i ) const { return (mstudiomodel_t *)(((byte *)this) + modelindex) + i; }; }; struct mstudiomouth_t { int bone; Vector forward; int flexdesc; private: // No copy constructors allowed mstudiomouth_t(const mstudiomouth_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; }; }; // ---------------------------------------------------------- // Purpose: Load time results on model compositing // ---------------------------------------------------------- class virtualgroup_t { public: virtualgroup_t( void ) { cache = NULL; }; // tool dependant. In engine this is a model_t, in tool it's a direct pointer void *cache; // converts cache entry into a usable studiohdr_t * const studiohdr_t *GetStudioHdr( void ) const; CUtlVector< int > boneMap; // maps global bone to local bone CUtlVector< int > masterBone; // maps local bone to global bone CUtlVector< int > masterSeq; // maps local sequence to master sequence CUtlVector< int > masterAnim; // maps local animation to master animation CUtlVector< int > masterAttachment; // maps local attachment to global CUtlVector< int > masterPose; // maps local pose parameter to global CUtlVector< int > masterNode; // maps local transition nodes to global }; struct virtualsequence_t { #ifdef _XBOX short flags; short activity; short group; short index; #else int flags; int activity; int group; int index; #endif }; struct virtualgeneric_t { #ifdef _XBOX short group; short index; #else int group; int index; #endif }; struct virtualmodel_t { void AppendSequences( int group, const studiohdr_t *pStudioHdr ); void AppendAnimations( int group, const studiohdr_t *pStudioHdr ); void AppendAttachments( int ground, const studiohdr_t *pStudioHdr ); void AppendPoseParameters( int group, const studiohdr_t *pStudioHdr ); void AppendBonemap( int group, const studiohdr_t *pStudioHdr ); void AppendNodes( int group, const studiohdr_t *pStudioHdr ); void AppendTransitions( int group, const studiohdr_t *pStudioHdr ); void AppendIKLocks( int group, const studiohdr_t *pStudioHdr ); void AppendModels( int group, const studiohdr_t *pStudioHdr ); void UpdateAutoplaySequences( const studiohdr_t *pStudioHdr ); virtualgroup_t *pAnimGroup( int animation ) { return &m_group[ m_anim[ animation ].group ]; }; virtualgroup_t *pSeqGroup( int sequence ) { return &m_group[ m_seq[ sequence ].group ]; }; CUtlVector< virtualsequence_t > m_seq; CUtlVector< virtualgeneric_t > m_anim; CUtlVector< virtualgeneric_t > m_attachment; CUtlVector< virtualgeneric_t > m_pose; CUtlVector< virtualgroup_t > m_group; CUtlVector< virtualgeneric_t > m_node; CUtlVector< CUtlVector< byte > > m_transition; CUtlVector< virtualgeneric_t > m_iklock; CUtlVector< unsigned short > m_autoplaySequences; }; // ---------------------------------------------------------- // Studio Model Vertex Data File // Position independent flat data for cache manager // ---------------------------------------------------------- // little-endian "IDSV" #define MODEL_VERTEX_FILE_ID (('V'<<24)+('S'<<16)+('D'<<8)+'I') #define MODEL_VERTEX_FILE_VERSION 4 struct vertexFileHeader_t { int id; // MODEL_VERTEX_FILE_ID int version; // MODEL_VERTEX_FILE_VERSION long 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; }; // This flag is set if no hitbox information was specified #define STUDIOHDR_FLAGS_AUTOGENERATED_HITBOX ( 1 << 0 ) // NOTE: This flag is set at loadtime, not mdl build time so that we don't have to rebuild // models when we change materials. #define STUDIOHDR_FLAGS_USES_ENV_CUBEMAP ( 1 << 1 ) // Use this when there are translucent parts to the model but we're not going to sort it #define STUDIOHDR_FLAGS_FORCE_OPAQUE ( 1 << 2 ) // Use this when we want to render the opaque parts during the opaque pass // and the translucent parts during the translucent pass #define STUDIOHDR_FLAGS_TRANSLUCENT_TWOPASS ( 1 << 3 ) // This is set any time the .qc files has $staticprop in it // Means there's no bones and no transforms #define STUDIOHDR_FLAGS_STATIC_PROP ( 1 << 4 ) // NOTE: This flag is set at loadtime, not mdl build time so that we don't have to rebuild // models when we change materials. #define STUDIOHDR_FLAGS_USES_FB_TEXTURE ( 1 << 5 ) // This flag is set by studiomdl.exe if a separate "$shadowlod" entry was present // for the .mdl (the shadow lod is the last entry in the lod list if present) #define STUDIOHDR_FLAGS_HASSHADOWLOD ( 1 << 6 ) // NOTE: This flag is set at loadtime, not mdl build time so that we don't have to rebuild // models when we change materials. #define STUDIOHDR_FLAGS_USES_BUMPMAPPING ( 1 << 7 ) // NOTE: This flag is set when we should use the actual materials on the shadow LOD // instead of overriding them with the default one (necessary for translucent shadows) #define STUDIOHDR_FLAGS_USE_SHADOWLOD_MATERIALS ( 1 << 8 ) // NOTE: This flag is set when we should use the actual materials on the shadow LOD // instead of overriding them with the default one (necessary for translucent shadows) #define STUDIOHDR_FLAGS_OBSOLETE ( 1 << 9 ) #define STUDIOHDR_FLAGS_UNUSED ( 1 << 10 ) // NOTE: This flag is set at mdl build time #define STUDIOHDR_FLAGS_NO_FORCED_FADE ( 1 << 11 ) // NOTE: The npc will lengthen the viseme check to always include two phonemes #define STUDIOHDR_FLAGS_FORCE_PHONEME_CROSSFADE ( 1 << 12 ) // This flag is set when the .qc has $constantdirectionallight in it // If set, we use constantdirectionallightdot to calculate light intensity // rather than the normal directional dot product // only valid if STUDIOHDR_FLAGS_STATIC_PROP is also set #define STUDIOHDR_FLAGS_CONSTANT_DIRECTIONAL_LIGHT_DOT ( 1 << 13 ) // Flag to mark delta flexes as already converted from disk format to memory format #define STUDIOHDR_FLAGS_FLEXES_CONVERTED ( 1 << 14 ) // Flag to mark delta flexes as already converted from disk format to memory format #define STUDIOHDR_FLAGS_AMBIENT_BOOST ( 1 << 15 ) struct studiohdr_t { int id; int version; long 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]; int 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; inline mstudiobone_t *pBone( int i ) const { Assert( i >= 0 && i < numbones); return (mstudiobone_t *)(((byte *)this) + boneindex) + i; }; int RemapSeqBone( int iSequence, int iLocalBone ) const; // maps local sequence bone to global bone int RemapAnimBone( int iAnim, int iLocalBone ) const; // maps local animations bone to global bone int numbonecontrollers; // bone controllers int bonecontrollerindex; inline mstudiobonecontroller_t *pBonecontroller( int i ) const { Assert( i >= 0 && i < numbonecontrollers); return (mstudiobonecontroller_t *)(((byte *)this) + bonecontrollerindex) + i; }; int numhitboxsets; int hitboxsetindex; // Look up hitbox set by index mstudiohitboxset_t *pHitboxSet( int i ) const { Assert( i >= 0 && i < numhitboxsets); return (mstudiohitboxset_t *)(((byte *)this) + hitboxsetindex ) + i; }; // Calls through to hitbox to determine size of specified set inline mstudiobbox_t *pHitbox( int i, int set ) const { mstudiohitboxset_t const *s = pHitboxSet( set ); if ( !s ) return NULL; return s->pHitbox( i ); }; // Calls through to set to get hitbox count for set inline int iHitboxCount( int set ) const { mstudiohitboxset_t const *s = pHitboxSet( set ); if ( !s ) return 0; return s->numhitboxes; }; // file local animations? and sequences //private: int numlocalanim; // animations/poses int localanimindex; // animation descriptions inline mstudioanimdesc_t *pLocalAnimdesc( int i ) const { if (i < 0 || i >= numlocalanim) i = 0; return (mstudioanimdesc_t *)(((byte *)this) + localanimindex) + i; }; int numlocalseq; // sequences int localseqindex; inline mstudioseqdesc_t *pLocalSeqdesc( int i ) const { if (i < 0 || i >= numlocalseq) i = 0; return (mstudioseqdesc_t *)(((byte *)this) + localseqindex) + i; }; //public: bool SequencesAvailable() const; int GetNumSeq() const; mstudioanimdesc_t &pAnimdesc( int i ) const; mstudioseqdesc_t &pSeqdesc( int i ) const; int iRelativeAnim( int baseseq, int relanim ) const; // maps seq local anim reference to global anim index int iRelativeSeq( int baseseq, int relseq ) const; // maps seq local seq reference to global seq index //private: mutable int activitylistversion; // initialization flag - have the sequences been indexed? mutable int eventsindexed; //public: int GetSequenceActivity( int iSequence ); void SetSequenceActivity( int iSequence, int iActivity ); int GetActivityListVersion( void ) const; void SetActivityListVersion( int version ) const; int GetEventListVersion( void ) const; void SetEventListVersion( int version ) const; // raw textures int numtextures; int textureindex; inline mstudiotexture_t *pTexture( int i ) const { return (mstudiotexture_t *)(((byte *)this) + textureindex) + i; }; // raw textures search paths int numcdtextures; int cdtextureindex; inline char *pCdtexture( int i ) const { return (((char *)this) + *((int *)(((byte *)this) + cdtextureindex) + i)); }; // replaceable textures tables int numskinref; int numskinfamilies; int skinindex; inline short *pSkinref( int i ) const { return (short *)(((byte *)this) + skinindex) + i; }; int numbodyparts; int bodypartindex; inline mstudiobodyparts_t *pBodypart( int i ) const { return (mstudiobodyparts_t *)(((byte *)this) + bodypartindex) + i; }; // queryable attachable points //private: int numlocalattachments; int localattachmentindex; inline mstudioattachment_t *pLocalAttachment( int i ) const { Assert( i >= 0 && i < numlocalattachments); return (mstudioattachment_t *)(((byte *)this) + localattachmentindex) + i; }; //public: int GetNumAttachments( void ) const; const mstudioattachment_t &pAttachment( int i ) const; int GetAttachmentBone( int i ) const; // used on my tools in hlmv, not persistant void SetAttachmentBone( int iAttachment, int iBone ); // animation node to animation node transition graph //private: int numlocalnodes; int localnodeindex; int localnodenameindex; inline char *pszLocalNodeName( int iNode ) const { Assert( iNode >= 0 && iNode < numlocalnodes); return (((char *)this) + *((int *)(((byte *)this) + localnodenameindex) + iNode)); } inline byte *pLocalTransition( int i ) const { Assert( i >= 0 && i < (numlocalnodes * numlocalnodes)); return (byte *)(((byte *)this) + localnodeindex) + i; }; //public: int EntryNode( int iSequence ) const; int ExitNode( int iSequence ) const; char *pszNodeName( int iNode ) const; int GetTransition( int iFrom, int iTo ) const; int numflexdesc; int flexdescindex; inline mstudioflexdesc_t *pFlexdesc( int i ) const { Assert( i >= 0 && i < numflexdesc); return (mstudioflexdesc_t *)(((byte *)this) + flexdescindex) + i; }; int numflexcontrollers; int flexcontrollerindex; inline mstudioflexcontroller_t *pFlexcontroller( int i ) const { Assert( i >= 0 && i < numflexcontrollers); return (mstudioflexcontroller_t *)(((byte *)this) + flexcontrollerindex) + i; }; int numflexrules; int flexruleindex; inline mstudioflexrule_t *pFlexRule( int i ) const { Assert( i >= 0 && i < numflexrules); return (mstudioflexrule_t *)(((byte *)this) + flexruleindex) + i; }; int numikchains; int ikchainindex; inline mstudioikchain_t *pIKChain( int i ) const { Assert( i >= 0 && i < numikchains); return (mstudioikchain_t *)(((byte *)this) + ikchainindex) + i; }; int nummouths; int mouthindex; inline mstudiomouth_t *pMouth( int i ) const { Assert( i >= 0 && i < nummouths); return (mstudiomouth_t *)(((byte *)this) + mouthindex) + i; }; //private: int numlocalposeparameters; int localposeparamindex; inline mstudioposeparamdesc_t *pLocalPoseParameter( int i ) const { Assert( i >= 0 && i < numlocalposeparameters); return (mstudioposeparamdesc_t *)(((byte *)this) + localposeparamindex) + i; }; //public: int GetNumPoseParameters( void ) const; const mstudioposeparamdesc_t &pPoseParameter( int i ) const; int GetSharedPoseParameter( int iSequence, int iLocalPose ) const; int surfacepropindex; inline char * const pszSurfaceProp( void ) const { return ((char *)this) + surfacepropindex; } // Key values int keyvalueindex; int keyvaluesize; inline const char * KeyValueText( void ) const { return keyvaluesize != 0 ? ((char *)this) + keyvalueindex : NULL; } int numlocalikautoplaylocks; int localikautoplaylockindex; inline mstudioiklock_t *pLocalIKAutoplayLock( int i ) const { Assert( i >= 0 && i < numlocalikautoplaylocks); return (mstudioiklock_t *)(((byte *)this) + localikautoplaylockindex) + i; }; int GetNumIKAutoplayLocks( void ) const; const mstudioiklock_t &pIKAutoplayLock( int i ) const; int CountAutoplaySequences() const; int CopyAutoplaySequences( unsigned short *pOut, int outCount ) const; int GetAutoplayList( unsigned short **pOut ) const; // The collision model mass that jay wanted float mass; int contents; // external animations, models, etc. int numincludemodels; int includemodelindex; inline mstudiomodelgroup_t *pModelGroup( int i ) const { Assert( i >= 0 && i < numincludemodels); return (mstudiomodelgroup_t *)(((byte *)this) + includemodelindex) + i; }; // implementation specific call to get a named model const studiohdr_t *FindModel( void **cache, char const *modelname ) const; // implementation specific back pointer to virtual data mutable void *virtualModel; virtualmodel_t *GetVirtualModel( void ) const; // for demand loaded animation blocks int szanimblocknameindex; inline char * const pszAnimBlockName( void ) const { return ((char *)this) + szanimblocknameindex; } int numanimblocks; int animblockindex; inline mstudioanimblock_t *pAnimBlock( int i ) const { Assert( i > 0 && i < numanimblocks); return (mstudioanimblock_t *)(((byte *)this) + animblockindex) + i; }; mutable void *animblockModel; byte * GetAnimBlock( int i ) const; int bonetablebynameindex; inline const byte *GetBoneTableSortedByName() const { return (byte *)this + bonetablebynameindex; } // used by tools only that don't cache, but persist mdl's peer data // engine uses virtualModel to back link to cache pointers void *pVertexBase; void *pIndexBase; // if STUDIOHDR_FLAGS_CONSTANT_DIRECTIONAL_LIGHT_DOT is set, // this value is used to calculate directional components of lighting // on static props byte constdirectionallightdot; // set during load of mdl data to track *desired* lod configuration (not actual) // the *actual* clamped root lod is found in studiohwdata // this is stored here as a global store to ensure the staged loading matches the rendering byte rootLOD; byte unused[2]; int zeroframecacheindex; byte *pZeroframeCache( int i ) const { if (zeroframecacheindex) return (byte *)this + ((int *)(((byte *)this) + zeroframecacheindex))[i]; else return NULL; } int unused2[6]; // remove as appropriate studiohdr_t() {} private: // No copy constructors allowed studiohdr_t(const studiohdr_t& vOther); friend struct virtualmodel_t; }; //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- class IDataCache; class IMDLCache; class CStudioHdr { public: CStudioHdr( void ); CStudioHdr( IMDLCache *modelcache ); CStudioHdr( const studiohdr_t *pStudioHdr, IMDLCache *datacache ); void SetModelCache( IMDLCache *modelcache ); void Init( const studiohdr_t *pStudioHdr ); public: inline bool IsReadyForAccess( void ) { return (m_nFrameUnlockCounter == *m_pFrameUnlockCounter); }; inline bool IsVirtual( void ) { return (m_pVModel != NULL); }; inline bool IsValid( void ) { return (m_pStudioHdr != NULL); }; inline virtualmodel_t *GetVirtualModel( void ) const { return m_pVModel; }; inline const studiohdr_t *GetRenderHdr( void ) const { return m_pStudioHdr; }; const studiohdr_t *pSeqStudioHdr( int sequence ) const; const studiohdr_t *pAnimStudioHdr( int animation )const; private: mutable const studiohdr_t *m_pStudioHdr; mutable virtualmodel_t *m_pVModel; void ResetVModel( const virtualmodel_t *pVModel ) const; const studiohdr_t *GroupStudioHdr( int group ) const; mutable CUtlVector< const studiohdr_t * > m_pStudioHdrCache; int m_nFrameUnlockCounter; int *m_pFrameUnlockCounter; public: inline int numbones( void ) const { return m_pStudioHdr->numbones; }; inline mstudiobone_t *pBone( int i ) const { return m_pStudioHdr->pBone( i ); }; int RemapAnimBone( int iAnim, int iLocalBone ) const; // maps local animations bone to global bone int RemapSeqBone( int iSequence, int iLocalBone ) const; // maps local sequence bone to global bone bool SequencesAvailable() const; int GetNumSeq( void ) const; mstudioanimdesc_t &pAnimdesc( int i ) const; mstudioseqdesc_t &pSeqdesc( int iSequence ) const; int iRelativeAnim( int baseseq, int relanim ) const; // maps seq local anim reference to global anim index int iRelativeSeq( int baseseq, int relseq ) const; // maps seq local seq reference to global seq index int GetSequenceActivity( int iSequence ); void SetSequenceActivity( int iSequence, int iActivity ); int GetActivityListVersion( void ) const; void SetActivityListVersion( int version ); int GetEventListVersion( void ) const; void SetEventListVersion( int version ); int GetNumAttachments( void ) const; const mstudioattachment_t &pAttachment( int i ) const; int GetAttachmentBone( int i ) const; // used on my tools in hlmv, not persistant void SetAttachmentBone( int iAttachment, int iBone ); int EntryNode( int iSequence ) const; int ExitNode( int iSequence ) const; char *pszNodeName( int iNode ) const; // FIXME: where should this one be? int GetTransition( int iFrom, int iTo ) const; int GetNumPoseParameters( void ) const; const mstudioposeparamdesc_t &pPoseParameter( int i ) const; int GetSharedPoseParameter( int iSequence, int iLocalPose ) const; int GetNumIKAutoplayLocks( void ) const; const mstudioiklock_t &pIKAutoplayLock( int i ) const; inline int CountAutoplaySequences() const { return m_pStudioHdr->CountAutoplaySequences(); }; inline int CopyAutoplaySequences( unsigned short *pOut, int outCount ) const { return m_pStudioHdr->CopyAutoplaySequences( pOut, outCount ); }; inline int GetAutoplayList( unsigned short **pOut ) const { return m_pStudioHdr->GetAutoplayList( pOut ); }; inline int GetNumBoneControllers( void ) const { return m_pStudioHdr->numbonecontrollers; }; inline mstudiobonecontroller_t *pBonecontroller( int i ) const { return m_pStudioHdr->pBonecontroller( i ); }; inline int numikchains() const { return m_pStudioHdr->numikchains; }; inline int GetNumIKChains( void ) const { return m_pStudioHdr->numikchains; }; inline mstudioikchain_t *pIKChain( int i ) const { return m_pStudioHdr->pIKChain( i ); }; inline int numflexrules() const { return m_pStudioHdr->numflexrules; }; inline mstudioflexrule_t *pFlexRule( int i ) const { return m_pStudioHdr->pFlexRule( i ); }; inline int numflexdesc() const{ return m_pStudioHdr->numflexdesc; }; inline mstudioflexdesc_t *pFlexdesc( int i ) const { return m_pStudioHdr->pFlexdesc( i ); }; inline int numflexcontrollers() const{ return m_pStudioHdr->numflexcontrollers; }; inline mstudioflexcontroller_t *pFlexcontroller( int i ) const { return m_pStudioHdr->pFlexcontroller( i ); }; inline const char *name() const { return m_pStudioHdr->name; }; // deprecated -- remove after full xbox merge inline const char *pszName() const { return m_pStudioHdr->pszName(); }; inline int numbonecontrollers() const { return m_pStudioHdr->numbonecontrollers; }; inline int numhitboxsets() const { return m_pStudioHdr->numhitboxsets; }; inline mstudiohitboxset_t *pHitboxSet( int i ) const { return m_pStudioHdr->pHitboxSet( i ); }; inline mstudiobbox_t *pHitbox( int i, int set ) const { return m_pStudioHdr->pHitbox( i, set ); }; inline int iHitboxCount( int set ) const { return m_pStudioHdr->iHitboxCount( set ); }; inline int numbodyparts() const { return m_pStudioHdr->numbodyparts; }; inline mstudiobodyparts_t *pBodypart( int i ) const { return m_pStudioHdr->pBodypart( i ); }; inline int numskinfamilies() const { return m_pStudioHdr->numskinfamilies; } inline Vector eyeposition() const { return m_pStudioHdr->eyeposition; }; inline int flags() const { return m_pStudioHdr->flags; }; inline char *const pszSurfaceProp( void ) const { return m_pStudioHdr->pszSurfaceProp(); }; inline float mass() const { return m_pStudioHdr->mass; }; inline int contents() const { return m_pStudioHdr->contents; } inline const byte *GetBoneTableSortedByName() const { return m_pStudioHdr->GetBoneTableSortedByName(); }; inline Vector illumposition() const { return m_pStudioHdr->illumposition; }; inline Vector hull_min() const { return m_pStudioHdr->hull_min; }; // ideal movement hull size inline Vector hull_max() const { return m_pStudioHdr->hull_max; }; inline Vector view_bbmin() const { return m_pStudioHdr->view_bbmin; }; // clipping bounding box inline Vector view_bbmax() const { return m_pStudioHdr->view_bbmax; }; inline int numtextures() const { return m_pStudioHdr->numtextures; }; inline byte *pZeroframeCache( int i ) const { return m_pStudioHdr->pZeroframeCache( i ); }; public: int IsSequenceLooping( int iSequence ); float GetSequenceCycleRate( int iSequence ); void RunFlexRules( const float *src, float *dest ); }; /* class CModelAccess { public: CModelAccess(CStudioHdr *pSemaphore) : m_pStudioHdr(pSemaphore) { m_pStudioHdr->IncrementAccess(); } ~CModelAccess() { m_pStudioHdr->DecrementAccess(); } private: CStudioHdr *m_pStudioHdr; }; #define ENABLE_MODEL_ACCESS( a ) \ CModelAccess ModelAccess##__LINE__( a->m_pStudioHdr ) */ //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- struct flexweight_t { int key; float weight; float influence; }; //----------------------------------------------------------------------------- // Purpose: A markov group basically indexes another flex setting and includes a // weighting factor which is used to factor how likely the specified member // is to be picked //----------------------------------------------------------------------------- struct flexmarkovgroup_t { int settingnumber; int weight; }; #define FS_NORMAL 0 #define FS_MARKOV 1 struct flexsetting_t { int nameindex; inline char *pszName( void ) const { return (char *)(((byte *)this) + nameindex); } inline bool IsMarkov( void ) const { return type == FS_MARKOV ? true : false; } // FS_NORMAL or FS_MARKOV int type; // Number of flex settings for FS_NORMAL or Number of // Markov group members for FS_MARKOV int numsettings; int index; // For FS_MARKOV only, the client .dll writes the current index into here so that // it can retain which markov group is being followed. This is reset every time the expression // starts to play back during scene playback int currentindex; // Index of start of contiguous array of flexweight_t or // flexmarkovgroup_t structures int settingindex; //----------------------------------------------------------------------------- // Purpose: Retrieves the specified markov group header if the entry is a markov entry // Input : member - // Output : flexmarkovgroup_t * //----------------------------------------------------------------------------- inline flexmarkovgroup_t *pMarkovGroup( int member ) const { // type must be FS_MARKOV to return this pointer if ( !IsMarkov() ) return NULL; if ( member < 0 || member >= numsettings ) return NULL; flexmarkovgroup_t *group = ( flexmarkovgroup_t * )(((byte *)this) + settingindex ) + member; return group; }; //----------------------------------------------------------------------------- // Purpose: // Input : member - // Output : int //----------------------------------------------------------------------------- inline int GetMarkovSetting( int member ) const { flexmarkovgroup_t *group = pMarkovGroup( member ); if ( !group ) return -1; return group->settingnumber; }; //----------------------------------------------------------------------------- // Purpose: Retrieves a pointer to the flexweight_t, including resolving // any markov chain hierarchy. Because of this possibility, we return // the number of settings in the weights array returned. We'll generally // call this function with i == 0 // Input : *base - // i - // **weights - // Output : int //----------------------------------------------------------------------------- inline int psetting( byte *base, int i, flexweight_t **weights ) const; }; struct flexsettinghdr_t { int id; int version; inline const char * pszName( void ) const { return name; } char name[64]; int length; int numflexsettings; int flexsettingindex; inline flexsetting_t *pSetting( int i ) const { return (flexsetting_t *)(((byte *)this) + flexsettingindex) + i; }; int nameindex; // look up flex settings by "index" int numindexes; int indexindex; inline flexsetting_t *pIndexedSetting( int index ) const { if ( index < 0 || index >= numindexes ) { return NULL; } int i = *((int *)(((byte *)this) + indexindex) + index); if (i == -1) { return NULL; } return pSetting( i ); } // index names of "flexcontrollers" int numkeys; int keynameindex; inline char *pLocalName( int i ) const { return (char *)(((byte *)this) + *((int *)(((byte *)this) + keynameindex) + i)); }; int keymappingindex; inline int *pLocalToGlobal( int i ) const { return (int *)(((byte *)this) + keymappingindex) + i; }; inline int LocalToGlobal( int i ) const { return *pLocalToGlobal( i ); }; //----------------------------------------------------------------------------- // Purpose: Same as pSetting( int i ) above, except it translates away any markov groups first // Input : i - index to retrieve // Output : flexsetting_t * - a non-markov underlying flexsetting_t //----------------------------------------------------------------------------- inline flexsetting_t *pTranslatedSetting( int i ) const { flexsetting_t *setting = (flexsetting_t *)(((byte *)this) + flexsettingindex) + i; // If this one is not a markov setting, return it if ( !setting->IsMarkov() ) { return setting; } int newindex = setting->GetMarkovSetting( setting->currentindex ); // Ack, this should never happen (the markov references something that is gone) // Since there was a problem, // just return this setting anyway, sigh. if ( newindex == -1 ) { return setting; } // Otherwise, recurse on the translated index // NOTE: It's theoretically possible to have an infinite recursion if two markov // groups reference each other. The faceposer shouldn't create such groups, // so I don't think this will ever actually occur -- ywb return pTranslatedSetting( newindex ); } }; //----------------------------------------------------------------------------- // Purpose: Retrieves a pointer to the flexweight_t, including resolving // any markov chain hierarchy. Because of this possibility, we return // the number of settings in the weights array returned. We'll generally // call this function with i == 0 // Input : *base - flexsettinghdr_t * pointer // i - index of flex setting to retrieve // **weights - destination for weights array starting at index i. // Output : int //----------------------------------------------------------------------------- inline int flexsetting_t::psetting( byte *base, int i, flexweight_t **weights ) const { // Assume failure to find index *weights = NULL; // Recurse if this is a markov setting if ( IsMarkov() ) { // Find the current redirected index int settingnum = GetMarkovSetting( currentindex ); if ( settingnum == -1 ) { // Couldn't find currentindex in the markov list for this flex setting return -1; } // Follow the markov link instead flexsetting_t *setting = ( (flexsettinghdr_t *)base)->pSetting( settingnum ); if ( !setting ) { return -1; } // Recurse ( could support more than one level of markov chains this way ) return setting->psetting( base, i, weights ); } // Grab array pointer *weights = (flexweight_t *)(((byte *)this) + settingindex) + i; // Return true number of settings return numsettings; }; #define STUDIO_CONST 1 // get float #define STUDIO_FETCH1 2 // get Flexcontroller value #define STUDIO_FETCH2 3 // get flex weight #define STUDIO_ADD 4 #define STUDIO_SUB 5 #define STUDIO_MUL 6 #define STUDIO_DIV 7 #define STUDIO_NEG 8 // not implemented #define STUDIO_EXP 9 // not implemented #define STUDIO_OPEN 10 // only used in token parsing #define STUDIO_CLOSE 11 #define STUDIO_COMMA 12 // only used in token parsing #define STUDIO_MAX 13 #define STUDIO_MIN 14 // motion flags #define STUDIO_X 0x00000001 #define STUDIO_Y 0x00000002 #define STUDIO_Z 0x00000004 #define STUDIO_XR 0x00000008 #define STUDIO_YR 0x00000010 #define STUDIO_ZR 0x00000020 #define STUDIO_LX 0x00000040 #define STUDIO_LY 0x00000080 #define STUDIO_LZ 0x00000100 #define STUDIO_LXR 0x00000200 #define STUDIO_LYR 0x00000400 #define STUDIO_LZR 0x00000800 #define STUDIO_LINEAR 0x00001000 #define STUDIO_TYPES 0x0003FFFF #define STUDIO_RLOOP 0x00040000 // controller that wraps shortest distance // sequence and autolayer flags #define STUDIO_LOOPING 0x0001 // ending frame should be the same as the starting frame #define STUDIO_SNAP 0x0002 // do not interpolate between previous animation and this one #define STUDIO_DELTA 0x0004 // this sequence "adds" to the base sequences, not slerp blends #define STUDIO_AUTOPLAY 0x0008 // temporary flag that forces the sequence to always play #define STUDIO_POST 0x0010 // #define STUDIO_ALLZEROS 0x0020 // this animation/sequence has no real animation data // 0x0040 // 0x0080 #define STUDIO_REALTIME 0x0100 // cycle index is taken from a real-time clock, not the animations cycle index #define STUDIO_LOCAL 0x0200 // sequence has a local context sequence #define STUDIO_HIDDEN 0x0400 // don't show in default selection views #define STUDIO_OVERRIDE 0x0800 // a forward declared sequence (empty) #define STUDIO_ACTIVITY 0x1000 // Has been updated at runtime to activity index #define STUDIO_EVENT 0x2000 // Has been updated at runtime to event index #define STUDIO_WORLD 0x4000 // sequence blends in worldspace // autolayer flags // 0x0001 // 0x0002 // 0x0004 // 0x0008 #define STUDIO_AL_POST 0x0010 // // 0x0020 #define STUDIO_AL_SPLINE 0x0040 // convert layer ramp in/out curve is a spline instead of linear #define STUDIO_AL_XFADE 0x0080 // pre-bias the ramp curve to compense for a non-1 weight, assuming a second layer is also going to accumulate // 0x0100 #define STUDIO_AL_NOBLEND 0x0200 // animation always blends at 1.0 (ignores weight) // 0x0400 // 0x0800 #define STUDIO_AL_LOCAL 0x1000 // layer is a local context sequence // 0x2000 #define STUDIO_AL_POSE 0x4000 // layer blends using a pose parameter instead of cycle // Insert this code anywhere that you need to allow for conversion from an old STUDIO_VERSION // to a new one. // If we only support the current version, this function should be empty. inline void Studio_ConvertStudioHdrToNewVersion( studiohdr_t *pStudioHdr ) { COMPILE_TIME_ASSERT( STUDIO_VERSION == 44 ); // put this to make sure this code is updated upon changing version. int version = pStudioHdr->version; if ( version == STUDIO_VERSION ) { return; } } // must be run to fixup with specified rootLOD inline void Studio_SetRootLOD( studiohdr_t *pStudioHdr, int rootLOD ) { // run the lod fixups that culls higher detail lods // vertexes are external, fixups ensure relative offsets and counts are cognizant of shrinking data // indexes are built in lodN..lod0 order so higher detail lod data can be truncated at load // the fixup lookup arrays are filled (or replicated) to ensure all slots valid int vertexindex = 0; int tangentsindex = 0; int bodyPartID; for ( bodyPartID = 0; bodyPartID < pStudioHdr->numbodyparts; bodyPartID++ ) { mstudiobodyparts_t *pBodyPart = pStudioHdr->pBodypart( bodyPartID ); int modelID; for ( modelID = 0; modelID < pBodyPart->nummodels; modelID++ ) { mstudiomodel_t *pModel = pBodyPart->pModel( modelID ); int totalMeshVertexes = 0; int meshID; for ( meshID = 0; meshID < pModel->nummeshes; meshID++ ) { mstudiomesh_t *pMesh = pModel->pMesh( meshID ); // get the fixup, vertexes are reduced pMesh->numvertices = pMesh->vertexdata.numLODVertexes[rootLOD]; pMesh->vertexoffset = totalMeshVertexes; totalMeshVertexes += pMesh->numvertices; } // stay in sync pModel->numvertices = totalMeshVertexes; pModel->vertexindex = vertexindex; pModel->tangentsindex = tangentsindex; vertexindex += totalMeshVertexes*sizeof(mstudiovertex_t); tangentsindex += totalMeshVertexes*sizeof(Vector4D); } } // track the set desired configuration pStudioHdr->rootLOD = rootLOD; } // Determines reduced allocation requirements for vertexes inline int Studio_VertexDataSize( const vertexFileHeader_t *pVvdHdr, int rootLOD, bool bNeedsTangentS ) { // the quantity of vertexes necessary for root lod and all lower detail lods // add one extra vertex to each section // the extra vertex allows prefetch hints to read ahead 1 vertex without faulting int numVertexes = pVvdHdr->numLODVertexes[rootLOD] + 1; int dataLength = pVvdHdr->vertexDataStart + numVertexes*sizeof(mstudiovertex_t); if (bNeedsTangentS) { dataLength += numVertexes*sizeof(Vector4D); } // allocate this much return dataLength; } // Load the minimum qunatity of verts and run fixups inline int Studio_LoadVertexes( const vertexFileHeader_t *pTempVvdHdr, vertexFileHeader_t *pNewVvdHdr, int rootLOD, bool bNeedsTangentS ) { int i; int target; int numVertexes; vertexFileFixup_t *pFixupTable; numVertexes = pTempVvdHdr->numLODVertexes[rootLOD]; // copy all data up to start of vertexes memcpy((void*)pNewVvdHdr, (void*)pTempVvdHdr, pTempVvdHdr->vertexDataStart); for ( i = 0; i < rootLOD; i++) { pNewVvdHdr->numLODVertexes[i] = pNewVvdHdr->numLODVertexes[rootLOD]; } // fixup data starts if (bNeedsTangentS) { // tangent data follows possibly reduced vertex data pNewVvdHdr->tangentDataStart = pNewVvdHdr->vertexDataStart + numVertexes*sizeof(mstudiovertex_t); } else { // no tangent data will be available, mark for identification pNewVvdHdr->tangentDataStart = 0; } if (!pNewVvdHdr->numFixups) { // fixups not required // transfer vertex data memcpy( (byte *)pNewVvdHdr+pNewVvdHdr->vertexDataStart, (byte *)pTempVvdHdr+pTempVvdHdr->vertexDataStart, numVertexes*sizeof(mstudiovertex_t) ); if (bNeedsTangentS) { // transfer tangent data to cache memory memcpy( (byte *)pNewVvdHdr+pNewVvdHdr->tangentDataStart, (byte *)pTempVvdHdr+pTempVvdHdr->tangentDataStart, numVertexes*sizeof(Vector4D) ); } return numVertexes; } // fixups required // re-establish mesh ordered vertexes into cache memory, according to table target = 0; pFixupTable = (vertexFileFixup_t *)((byte *)pTempVvdHdr + pTempVvdHdr->fixupTableStart); for (i=0; i<pTempVvdHdr->numFixups; i++) { if (pFixupTable[i].lod < rootLOD) { // working bottom up, skip over copying higher detail lods continue; } // copy vertexes memcpy( (mstudiovertex_t *)((byte *)pNewVvdHdr+pNewVvdHdr->vertexDataStart) + target, (mstudiovertex_t *)((byte *)pTempVvdHdr+pTempVvdHdr->vertexDataStart) + pFixupTable[i].sourceVertexID, pFixupTable[i].numVertexes*sizeof(mstudiovertex_t) ); if (bNeedsTangentS) { // copy tangents memcpy( (Vector4D *)((byte *)pNewVvdHdr+pNewVvdHdr->tangentDataStart) + target, (Vector4D *)((byte *)pTempVvdHdr+pTempVvdHdr->tangentDataStart) + pFixupTable[i].sourceVertexID, pFixupTable[i].numVertexes*sizeof(Vector4D) ); } // data is placed consecutively target += pFixupTable[i].numVertexes; } pNewVvdHdr->numFixups = 0; return target; } #endif // STUDIO_H
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#1 | 5821 | Knut Wikstrom |
Added Valve Source code. This is NOT to be commited to other than new code from Valve. |