diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /public/studio.h | |
| download | archived-source-engine-2018-hl2-src-master.tar.xz archived-source-engine-2018-hl2-src-master.zip | |
Diffstat (limited to 'public/studio.h')
| -rw-r--r-- | public/studio.h | 3229 |
1 files changed, 3229 insertions, 0 deletions
diff --git a/public/studio.h b/public/studio.h new file mode 100644 index 0000000..bc70313 --- /dev/null +++ b/public/studio.h @@ -0,0 +1,3229 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//===========================================================================// + +#ifndef STUDIO_H +#define STUDIO_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "basetypes.h" +#include "mathlib/vector2d.h" +#include "mathlib/vector.h" +#include "mathlib/vector4d.h" +#include "mathlib/compressed_vector.h" +#include "tier0/dbg.h" +#include "tier0/threadtools.h" +#include "mathlib/mathlib.h" +#include "utlvector.h" +#include "utlhash.h" +#include "datamap.h" +#include "generichash.h" +#include "localflexcontroller.h" +#include "utlsymbol.h" + +#define STUDIO_ENABLE_PERF_COUNTERS + +#define STUDIO_SEQUENCE_ACTIVITY_LOOKUPS_ARE_SLOW 0 +// If this is set to 1, then the activity->sequence mapping inside +// the CStudioHdr will not be initialized until the first call to +// SelectWeightedSequence() or HaveSequenceForActivity(). If set +// to zero, the mapping will be initialized from CStudioHdr::Init() +// (itself called from the constructor). +// As of June 4 2007, this was set to 1 because physics, among other +// systems, extemporaneously declares CStudioHdrs inside local function +// scopes without querying their activity/sequence mapping at all. +#define STUDIO_SEQUENCE_ACTIVITY_LAZY_INITIALIZE 1 + +//----------------------------------------------------------------------------- +// forward declarations +//----------------------------------------------------------------------------- + +class IMaterial; +class IMesh; +class IMorph; +struct virtualmodel_t; +struct vertexFileHeader_t; +struct thinModelVertices_t; + +namespace OptimizedModel +{ + struct StripHeader_t; +} + + +/* +============================================================================== + +STUDIO MODELS + +Studio models are position independent, so the cache manager can move them. +============================================================================== +*/ + +#define STUDIO_VERSION 48 + +#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 MAXSTUDIOFLEXDESC 1024 // maximum number of low level flexes (actual morph targets) +#define MAXSTUDIOFLEXCTRL 96 // 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 +#define STUDIO_PROC_JIGGLE 5 + +struct mstudioaxisinterpbone_t +{ + DECLARE_BYTESWAP_DATADESC(); + 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- + + mstudioaxisinterpbone_t(){} +private: + // No copy constructors allowed + mstudioaxisinterpbone_t(const mstudioaxisinterpbone_t& vOther); +}; + + +struct mstudioquatinterpinfo_t +{ + DECLARE_BYTESWAP_DATADESC(); + float inv_tolerance; // 1 / radian angle of trigger influence + Quaternion trigger; // angle to match + Vector pos; // new position + Quaternion quat; // new angle + + mstudioquatinterpinfo_t(){} +private: + // No copy constructors allowed + mstudioquatinterpinfo_t(const mstudioquatinterpinfo_t& vOther); +}; + +struct mstudioquatinterpbone_t +{ + DECLARE_BYTESWAP_DATADESC(); + int control;// local transformation to check + int numtriggers; + int triggerindex; + inline mstudioquatinterpinfo_t *pTrigger( int i ) const { return (mstudioquatinterpinfo_t *)(((byte *)this) + triggerindex) + i; }; + + mstudioquatinterpbone_t(){} +private: + // No copy constructors allowed + mstudioquatinterpbone_t(const mstudioquatinterpbone_t& vOther); +}; + + +#define JIGGLE_IS_FLEXIBLE 0x01 +#define JIGGLE_IS_RIGID 0x02 +#define JIGGLE_HAS_YAW_CONSTRAINT 0x04 +#define JIGGLE_HAS_PITCH_CONSTRAINT 0x08 +#define JIGGLE_HAS_ANGLE_CONSTRAINT 0x10 +#define JIGGLE_HAS_LENGTH_CONSTRAINT 0x20 +#define JIGGLE_HAS_BASE_SPRING 0x40 +#define JIGGLE_IS_BOING 0x80 // simple squash and stretch sinusoid "boing" + +struct mstudiojigglebone_t +{ + DECLARE_BYTESWAP_DATADESC(); + + int flags; + + // general params + float length; // how from from bone base, along bone, is tip + float tipMass; + + // flexible params + float yawStiffness; + float yawDamping; + float pitchStiffness; + float pitchDamping; + float alongStiffness; + float alongDamping; + + // angle constraint + float angleLimit; // maximum deflection of tip in radians + + // yaw constraint + float minYaw; // in radians + float maxYaw; // in radians + float yawFriction; + float yawBounce; + + // pitch constraint + float minPitch; // in radians + float maxPitch; // in radians + float pitchFriction; + float pitchBounce; + + // base spring + float baseMass; + float baseStiffness; + float baseDamping; + float baseMinLeft; + float baseMaxLeft; + float baseLeftFriction; + float baseMinUp; + float baseMaxUp; + float baseUpFriction; + float baseMinForward; + float baseMaxForward; + float baseForwardFriction; + + // boing + float boingImpactSpeed; + float boingImpactAngle; + float boingDampingRate; + float boingFrequency; + float boingAmplitude; + +private: + // No copy constructors allowed + //mstudiojigglebone_t(const mstudiojigglebone_t& vOther); +}; + +struct mstudioaimatbone_t +{ + DECLARE_BYTESWAP_DATADESC(); + + int parent; + int aim; // Might be bone or attach + Vector aimvector; + Vector upvector; + Vector basepos; + + mstudioaimatbone_t() {} +private: + // No copy constructors allowed + mstudioaimatbone_t(const mstudioaimatbone_t& vOther); +}; + +// bones +struct mstudiobone_t +{ + DECLARE_BYTESWAP_DATADESC(); + 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 + + mstudiobone_t(){} +private: + // No copy constructors allowed + mstudiobone_t(const mstudiobone_t& vOther); +}; + +struct mstudiolinearbone_t +{ + DECLARE_BYTESWAP_DATADESC(); + + int numbones; + + int flagsindex; + inline int flags( int i ) const { Assert( i >= 0 && i < numbones); return *((int *)(((byte *)this) + flagsindex) + i); }; + inline int *pflags( int i ) { Assert( i >= 0 && i < numbones); return ((int *)(((byte *)this) + flagsindex) + i); }; + + int parentindex; + inline int parent( int i ) const { Assert( i >= 0 && i < numbones); return *((int *)(((byte *)this) + parentindex) + i); }; + + int posindex; + inline Vector pos( int i ) const { Assert( i >= 0 && i < numbones); return *((Vector *)(((byte *)this) + posindex) + i); }; + + int quatindex; + inline Quaternion quat( int i ) const { Assert( i >= 0 && i < numbones); return *((Quaternion *)(((byte *)this) + quatindex) + i); }; + + int rotindex; + inline RadianEuler rot( int i ) const { Assert( i >= 0 && i < numbones); return *((RadianEuler *)(((byte *)this) + rotindex) + i); }; + + int posetoboneindex; + inline matrix3x4_t poseToBone( int i ) const { Assert( i >= 0 && i < numbones); return *((matrix3x4_t *)(((byte *)this) + posetoboneindex) + i); }; + + int posscaleindex; + inline Vector posscale( int i ) const { Assert( i >= 0 && i < numbones); return *((Vector *)(((byte *)this) + posscaleindex) + i); }; + + int rotscaleindex; + inline Vector rotscale( int i ) const { Assert( i >= 0 && i < numbones); return *((Vector *)(((byte *)this) + rotscaleindex) + i); }; + + int qalignmentindex; + inline Quaternion qalignment( int i ) const { Assert( i >= 0 && i < numbones); return *((Quaternion *)(((byte *)this) + qalignmentindex) + i); }; + + int unused[6]; + + mstudiolinearbone_t(){} +private: + // No copy constructors allowed + mstudiolinearbone_t(const mstudiolinearbone_t& vOther); +}; + + +//----------------------------------------------------------------------------- +// The component of the bone used by mstudioboneflexdriver_t +//----------------------------------------------------------------------------- +enum StudioBoneFlexComponent_t +{ + STUDIO_BONE_FLEX_INVALID = -1, // Invalid + STUDIO_BONE_FLEX_TX = 0, // Translate X + STUDIO_BONE_FLEX_TY = 1, // Translate Y + STUDIO_BONE_FLEX_TZ = 2 // Translate Z +}; + + +//----------------------------------------------------------------------------- +// Component is one of Translate X, Y or Z [0,2] (StudioBoneFlexComponent_t) +//----------------------------------------------------------------------------- +struct mstudioboneflexdrivercontrol_t +{ + DECLARE_BYTESWAP_DATADESC(); + + int m_nBoneComponent; // Bone component that drives flex, StudioBoneFlexComponent_t + int m_nFlexControllerIndex; // Flex controller to drive + float m_flMin; // Min value of bone component mapped to 0 on flex controller + float m_flMax; // Max value of bone component mapped to 1 on flex controller + + mstudioboneflexdrivercontrol_t(){} +private: + // No copy constructors allowed + mstudioboneflexdrivercontrol_t( const mstudioboneflexdrivercontrol_t &vOther ); +}; + + +//----------------------------------------------------------------------------- +// Drive flex controllers from bone components +//----------------------------------------------------------------------------- +struct mstudioboneflexdriver_t +{ + DECLARE_BYTESWAP_DATADESC(); + + int m_nBoneIndex; // Bone to drive flex controller + int m_nControlCount; // Number of flex controllers being driven + int m_nControlIndex; // Index into data where controllers are (relative to this) + + inline mstudioboneflexdrivercontrol_t *pBoneFlexDriverControl( int i ) const + { + Assert( i >= 0 && i < m_nControlCount ); + return (mstudioboneflexdrivercontrol_t *)(((byte *)this) + m_nControlIndex) + i; + } + + int unused[3]; + + mstudioboneflexdriver_t(){} +private: + // No copy constructors allowed + mstudioboneflexdriver_t( const mstudioboneflexdriver_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 // Vector48 +#define BONE_HAS_SAVEFRAME_ROT 0x00400000 // Quaternion64 + +// bone controllers +struct mstudiobonecontroller_t +{ + DECLARE_BYTESWAP_DATADESC(); + 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 +{ + DECLARE_BYTESWAP_DATADESC(); + int bone; + int group; // intersection group + Vector bbmin; // bounding box + Vector bbmax; + int szhitboxnameindex; // offset to the name of the hitbox. + int unused[8]; + + const char* pszHitboxName() + { + if( szhitboxnameindex == 0 ) + return ""; + + return ((const char*)this) + szhitboxnameindex; + } + + mstudiobbox_t() {} + +private: + // No copy constructors allowed + mstudiobbox_t(const mstudiobbox_t& vOther); +}; + +// demand loaded sequence groups +struct mstudiomodelgroup_t +{ + DECLARE_BYTESWAP_DATADESC(); + 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 +{ + DECLARE_BYTESWAP_DATADESC(); + 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 +{ + DECLARE_BYTESWAP_DATADESC(); + 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 +{ + DECLARE_BYTESWAP_DATADESC(); + Vector pos; + Quaternion q; + + mstudioikerror_t() {} + +private: + // No copy constructors allowed + mstudioikerror_t(const mstudioikerror_t& vOther); +}; + +union mstudioanimvalue_t; + +struct mstudiocompressedikerror_t +{ + DECLARE_BYTESWAP_DATADESC(); + 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; }; + mstudiocompressedikerror_t(){} + +private: + // No copy constructors allowed + mstudiocompressedikerror_t(const mstudiocompressedikerror_t& vOther); +}; + +struct mstudioikrule_t +{ + DECLARE_BYTESWAP_DATADESC(); + 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 + + float unused3; // + float contact; // frame footstep makes ground concact + float drop; // how far down the foot should drop when reaching for IK + float top; // top of the foot box + + 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 +{ + DECLARE_BYTESWAP_DATADESC(); + int chain; + float flPosWeight; + float flLocalQWeight; + int flags; + + int unused[4]; +}; + + +struct mstudiolocalhierarchy_t +{ + DECLARE_BYTESWAP_DATADESC(); + int iBone; // bone being adjusted + int iNewParent; // the bones new parent + + float start; // beginning of influence + float peak; // start of full influence + float tail; // end of full influence + float end; // end of all influence + + int iStart; // first frame + + int localanimindex; + inline mstudiocompressedikerror_t *pLocalAnim() const { return (mstudiocompressedikerror_t *)(((byte *)this) + localanimindex); }; + + int unused[4]; +}; + + + +// animation frames +union mstudioanimvalue_t +{ + struct + { + byte valid; + byte total; + } num; + short value; +}; + +struct mstudioanim_valueptr_t +{ + DECLARE_BYTESWAP_DATADESC(); + 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 +{ + DECLARE_BYTESWAP_DATADESC(); + 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() ) ); }; + + short nextoffset; + inline mstudioanim_t *pNext( void ) const { if (nextoffset != 0) return (mstudioanim_t *)(((byte *)this) + nextoffset); else return NULL; }; +}; + +struct mstudiomovement_t +{ + DECLARE_BYTESWAP_DATADESC(); + 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??? + + mstudiomovement_t(){} +private: + // No copy constructors allowed + mstudiomovement_t(const mstudiomovement_t& vOther); +}; + +struct studiohdr_t; + +// used for piecewise loading of animation data +struct mstudioanimblock_t +{ + DECLARE_BYTESWAP_DATADESC(); + int datastart; + int dataend; +}; + +struct mstudioanimsections_t +{ + DECLARE_BYTESWAP_DATADESC(); + int animblock; + int animindex; +}; + +struct mstudioanimdesc_t +{ + DECLARE_BYTESWAP_DATADESC(); + 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 mstudioikrule_t; + +struct mstudioautolayer_t +{ + DECLARE_BYTESWAP_DATADESC(); +//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 +}; + +struct mstudioactivitymodifier_t +{ + DECLARE_BYTESWAP_DATADESC(); + + int sznameindex; + inline char *pszName() { return (sznameindex) ? (char *)(((byte *)this) + sznameindex ) : NULL; } +}; + +// sequence descriptions +struct mstudioseqdesc_t +{ + DECLARE_BYTESWAP_DATADESC(); + 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 cycleposeindex; // index of pose parameter to use as cycle index + + int activitymodifierindex; + int numactivitymodifiers; + inline mstudioactivitymodifier_t *pActivityModifier( int i ) const { Assert( i >= 0 && i < numactivitymodifiers); return activitymodifierindex != 0 ? (mstudioactivitymodifier_t *)(((byte *)this) + activitymodifierindex) + i : NULL; }; + + int unused[5]; // 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); +}; + + +struct mstudioposeparamdesc_t +{ + DECLARE_BYTESWAP_DATADESC(); + 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 +{ + DECLARE_BYTESWAP_DATADESC(); + int szFACSindex; + inline char * const pszFACS( void ) const { return ((char *)this) + szFACSindex; } +}; + + + +struct mstudioflexcontroller_t +{ + DECLARE_BYTESWAP_DATADESC(); + 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 localToGlobal; // remapped at load time to master list + float min; + float max; +}; + + +enum FlexControllerRemapType_t +{ + FLEXCONTROLLER_REMAP_PASSTHRU = 0, + FLEXCONTROLLER_REMAP_2WAY, // Control 0 -> ramps from 1-0 from 0->0.5. Control 1 -> ramps from 0-1 from 0.5->1 + FLEXCONTROLLER_REMAP_NWAY, // StepSize = 1 / (control count-1) Control n -> ramps from 0-1-0 from (n-1)*StepSize to n*StepSize to (n+1)*StepSize. A second control is needed to specify amount to use + FLEXCONTROLLER_REMAP_EYELID +}; + + +class CStudioHdr; +struct mstudioflexcontrollerui_t +{ + DECLARE_BYTESWAP_DATADESC(); + int sznameindex; + inline char * const pszName( void ) const { return ((char *)this) + sznameindex; } + + // These are used like a union to save space + // Here are the possible configurations for a UI controller + // + // SIMPLE NON-STEREO: 0: control 1: unused 2: unused + // STEREO: 0: left 1: right 2: unused + // NWAY NON-STEREO: 0: control 1: unused 2: value + // NWAY STEREO: 0: left 1: right 2: value + + int szindex0; + int szindex1; + int szindex2; + + inline const mstudioflexcontroller_t *pController( void ) const + { + return !stereo ? (mstudioflexcontroller_t *)( (char *)this + szindex0 ) : NULL; + } + inline char * const pszControllerName( void ) const { return !stereo ? pController()->pszName() : NULL; } + inline int controllerIndex( const CStudioHdr &cStudioHdr ) const; + + inline const mstudioflexcontroller_t *pLeftController( void ) const + { + return stereo ? (mstudioflexcontroller_t *)( (char *)this + szindex0 ) : NULL; + } + inline char * const pszLeftName( void ) const { return stereo ? pLeftController()->pszName() : NULL; } + inline int leftIndex( const CStudioHdr &cStudioHdr ) const; + + inline const mstudioflexcontroller_t *pRightController( void ) const + { + return stereo ? (mstudioflexcontroller_t *)( (char *)this + szindex1 ): NULL; + } + inline char * const pszRightName( void ) const { return stereo ? pRightController()->pszName() : NULL; } + inline int rightIndex( const CStudioHdr &cStudioHdr ) const; + + inline const mstudioflexcontroller_t *pNWayValueController( void ) const + { + return remaptype == FLEXCONTROLLER_REMAP_NWAY ? (mstudioflexcontroller_t *)( (char *)this + szindex2 ) : NULL; + } + inline char * const pszNWayValueName( void ) const { return remaptype == FLEXCONTROLLER_REMAP_NWAY ? pNWayValueController()->pszName() : NULL; } + inline int nWayValueIndex( const CStudioHdr &cStudioHdr ) const; + + // Number of controllers this ui description contains, 1, 2 or 3 + inline int Count() const { return ( stereo ? 2 : 1 ) + ( remaptype == FLEXCONTROLLER_REMAP_NWAY ? 1 : 0 ); } + inline const mstudioflexcontroller_t *pController( int index ) const; + + unsigned char remaptype; // See the FlexControllerRemapType_t enum + bool stereo; // Is this a stereo control? + byte unused[2]; +}; + + +// this is the memory image of vertex anims (16-bit fixed point) +struct mstudiovertanim_t +{ + DECLARE_BYTESWAP_DATADESC(); + unsigned short index; + byte speed; // 255/max_length_in_flex + byte side; // 255/left_right + +protected: + // 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 void ConvertToFixed( float flVertAnimFixedPointScale ) + { + delta[0] = flDelta[0].GetFloat() / flVertAnimFixedPointScale; + delta[1] = flDelta[1].GetFloat() / flVertAnimFixedPointScale; + delta[2] = flDelta[2].GetFloat() / flVertAnimFixedPointScale; + ndelta[0] = flNDelta[0].GetFloat() / flVertAnimFixedPointScale; + ndelta[1] = flNDelta[1].GetFloat() / flVertAnimFixedPointScale; + ndelta[2] = flNDelta[2].GetFloat() / flVertAnimFixedPointScale; + } + + inline Vector GetDeltaFixed( float flVertAnimFixedPointScale ) + { + return Vector( delta[0] * flVertAnimFixedPointScale, delta[1] * flVertAnimFixedPointScale, delta[2] * flVertAnimFixedPointScale ); + } + inline Vector GetNDeltaFixed( float flVertAnimFixedPointScale ) + { + return Vector( ndelta[0] * flVertAnimFixedPointScale, ndelta[1] * flVertAnimFixedPointScale, ndelta[2] * flVertAnimFixedPointScale ); + } + inline void GetDeltaFixed4DAligned( Vector4DAligned *vFillIn, float flVertAnimFixedPointScale ) + { + vFillIn->Set( delta[0] * flVertAnimFixedPointScale, delta[1] * flVertAnimFixedPointScale, delta[2] * flVertAnimFixedPointScale, 0.0f ); + } + inline void GetNDeltaFixed4DAligned( Vector4DAligned *vFillIn, float flVertAnimFixedPointScale ) + { + vFillIn->Set( ndelta[0] * flVertAnimFixedPointScale, ndelta[1] * flVertAnimFixedPointScale, ndelta[2] * flVertAnimFixedPointScale, 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, float flVertAnimFixedPointScale ) + { + delta[0] = vInput.x / flVertAnimFixedPointScale; + delta[1] = vInput.y / flVertAnimFixedPointScale; + delta[2] = vInput.z / flVertAnimFixedPointScale; + } + inline void SetNDeltaFixed( const Vector& vInputNormal, float flVertAnimFixedPointScale ) + { + ndelta[0] = vInputNormal.x / flVertAnimFixedPointScale; + ndelta[1] = vInputNormal.y / flVertAnimFixedPointScale; + ndelta[2] = vInputNormal.z / flVertAnimFixedPointScale; + } + + // 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 ); + } + + class CSortByIndex + { + public: + bool operator()(const mstudiovertanim_t &left, const mstudiovertanim_t & right)const + { + return left.index < right.index; + } + }; + friend class CSortByIndex; + + mstudiovertanim_t(){} +//private: +// No copy constructors allowed, but it's needed for std::sort() +// mstudiovertanim_t(const mstudiovertanim_t& vOther); +}; + + +// this is the memory image of vertex anims (16-bit fixed point) +struct mstudiovertanim_wrinkle_t : public mstudiovertanim_t +{ + DECLARE_BYTESWAP_DATADESC(); + + short wrinkledelta; + + inline void SetWrinkleFixed( float flWrinkle, float flVertAnimFixedPointScale ) + { + int nWrinkleDeltaInt = flWrinkle / flVertAnimFixedPointScale; + wrinkledelta = clamp( nWrinkleDeltaInt, -32767, 32767 ); + } + + inline Vector4D GetDeltaFixed( float flVertAnimFixedPointScale ) + { + return Vector4D( delta[0] * flVertAnimFixedPointScale, delta[1] * flVertAnimFixedPointScale, delta[2] * flVertAnimFixedPointScale, wrinkledelta * flVertAnimFixedPointScale ); + } + + inline void GetDeltaFixed4DAligned( Vector4DAligned *vFillIn, float flVertAnimFixedPointScale ) + { + vFillIn->Set( delta[0] * flVertAnimFixedPointScale, delta[1] * flVertAnimFixedPointScale, delta[2] * flVertAnimFixedPointScale, wrinkledelta * flVertAnimFixedPointScale ); + } + + inline float GetWrinkleDeltaFixed( float flVertAnimFixedPointScale ) + { + return wrinkledelta * flVertAnimFixedPointScale; + } +}; + + +enum StudioVertAnimType_t +{ + STUDIO_VERT_ANIM_NORMAL = 0, + STUDIO_VERT_ANIM_WRINKLE, +}; + +struct mstudioflex_t +{ + DECLARE_BYTESWAP_DATADESC(); + 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 { Assert( vertanimtype == STUDIO_VERT_ANIM_NORMAL ); return (mstudiovertanim_t *)(((byte *)this) + vertindex) + i; }; + inline mstudiovertanim_wrinkle_t *pVertanimWrinkle( int i ) const { Assert( vertanimtype == STUDIO_VERT_ANIM_WRINKLE ); return (mstudiovertanim_wrinkle_t *)(((byte *)this) + vertindex) + i; }; + + inline byte *pBaseVertanim( ) const { return ((byte *)this) + vertindex; }; + inline int VertAnimSizeBytes() const { return ( vertanimtype == STUDIO_VERT_ANIM_NORMAL ) ? sizeof(mstudiovertanim_t) : sizeof(mstudiovertanim_wrinkle_t); } + + int flexpair; // second flex desc + unsigned char vertanimtype; // See StudioVertAnimType_t + unsigned char unusedchar[3]; + int unused[6]; +}; + + +struct mstudioflexop_t +{ + DECLARE_BYTESWAP_DATADESC(); + int op; + union + { + int index; + float value; + } d; +}; + +struct mstudioflexrule_t +{ + DECLARE_BYTESWAP_DATADESC(); + 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 +{ + DECLARE_BYTESWAP_DATADESC(); + 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 +{ + DECLARE_BYTESWAP_DATADESC(); + 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 +{ + DECLARE_BYTESWAP_DATADESC(); + 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 +{ + DECLARE_BYTESWAP_DATADESC(); + 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 unused1; + float iris_scale; + int unused2; + + 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[4]; // These were used before, so not guaranteed to be 0 + bool m_bNonFACS; // Never used before version 44 + char unused3[3]; + int unused4[7]; + + mstudioeyeball_t(){} +private: + // No copy constructors allowed + mstudioeyeball_t(const mstudioeyeball_t& vOther); +}; + + +// ikinfo +struct mstudioiklink_t +{ + DECLARE_BYTESWAP_DATADESC(); + int bone; + Vector kneeDir; // ideal bending direction (per link, if applicable) + Vector unused0; // unused + + mstudioiklink_t(){} +private: + // No copy constructors allowed + mstudioiklink_t(const mstudioiklink_t& vOther); +}; + +struct mstudioikchain_t +{ + DECLARE_BYTESWAP_DATADESC(); + 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 +{ + DECLARE_BYTESWAP_DATADESC(); + 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; + int GetGlobalVertexIndex( int i ) const; + int GetGlobalTangentIndex( int i ) const; + + // base of external vertex data stores + const void *pVertexData; + const void *pTangentData; +}; + +struct mstudio_meshvertexdata_t +{ + DECLARE_BYTESWAP_DATADESC(); + 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; + int GetModelVertexIndex( int i ) const; + int GetGlobalVertexIndex( int i ) 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 +{ + DECLARE_BYTESWAP_DATADESC(); + int material; + + int modelindex; + mstudiomodel_t *pModel() const; + + int numvertices; // number of unique vertices/normals/texcoords + int vertexoffset; // vertex mstudiovertex_t + + // Access thin/fat mesh vertex data (only one will return a non-NULL result) + const mstudio_meshvertexdata_t *GetVertexData( void *pModelData = NULL ); + const thinModelVertices_t *GetThinVertexData( void *pModelData = NULL ); + + 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 + + mstudiomesh_t(){} +private: + // No copy constructors allowed + mstudiomesh_t(const mstudiomesh_t& vOther); +}; + +// studio models +struct mstudiomodel_t +{ + DECLARE_BYTESWAP_DATADESC(); + 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 + + // These functions are defined in application-specific code: + const vertexFileHeader_t *CacheVertexData( void *pModelData ); + + // Access thin/fat mesh vertex data (only one will return a non-NULL result) + const mstudio_modelvertexdata_t *GetVertexData( void *pModelData = NULL ); + const thinModelVertices_t *GetThinVertexData( void *pModelData = NULL ); + + 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 int mstudio_modelvertexdata_t::GetGlobalVertexIndex( int i ) const +{ + mstudiomodel_t *modelptr = (mstudiomodel_t *)((byte *)this - offsetof(mstudiomodel_t, vertexdata)); + Assert( ( modelptr->vertexindex % sizeof( mstudiovertex_t ) ) == 0 ); + return ( i + ( modelptr->vertexindex / sizeof( mstudiovertex_t ) ) ); +} + +inline int mstudio_modelvertexdata_t::GetGlobalTangentIndex( int i ) const +{ + mstudiomodel_t *modelptr = (mstudiomodel_t *)((byte *)this - offsetof(mstudiomodel_t, vertexdata)); + Assert( ( modelptr->tangentsindex % sizeof( Vector4D ) ) == 0 ); + return ( i + ( modelptr->tangentsindex / sizeof( Vector4D ) ) ); +} + +inline mstudiovertex_t *mstudio_modelvertexdata_t::Vertex( int i ) const +{ + return (mstudiovertex_t *)pVertexData + GetGlobalVertexIndex( 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) + return (Vector4D *)pTangentData + GetGlobalTangentIndex( 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( void *pModelData ) +{ + // get this mesh's model's vertex data (allow for mstudiomodel_t::GetVertexData + // returning NULL if the data has been converted to 'thin' vertices) + this->pModel()->GetVertexData( pModelData ); + vertexdata.modelvertexdata = &( this->pModel()->vertexdata ); + + if ( !vertexdata.modelvertexdata->pVertexData ) + return NULL; + + return &vertexdata; +} + +inline const thinModelVertices_t * mstudiomesh_t::GetThinVertexData( void *pModelData ) +{ + // get this mesh's model's thin vertex data + return this->pModel()->GetThinVertexData( pModelData ); +} + +inline int mstudio_meshvertexdata_t::GetModelVertexIndex( int i ) const +{ + mstudiomesh_t *meshptr = (mstudiomesh_t *)((byte *)this - offsetof(mstudiomesh_t,vertexdata)); + return meshptr->vertexoffset + i; +} + +inline int mstudio_meshvertexdata_t::GetGlobalVertexIndex( int i ) const +{ + return modelvertexdata->GetGlobalVertexIndex( GetModelVertexIndex( i ) ); +} + +inline Vector *mstudio_meshvertexdata_t::Position( int i ) const +{ + return modelvertexdata->Position( GetModelVertexIndex( i ) ); +}; + +inline Vector *mstudio_meshvertexdata_t::Normal( int i ) const +{ + return modelvertexdata->Normal( GetModelVertexIndex( i ) ); +}; + +inline Vector4D *mstudio_meshvertexdata_t::TangentS( int i ) const +{ + return modelvertexdata->TangentS( GetModelVertexIndex( i ) ); +} + +inline Vector2D *mstudio_meshvertexdata_t::Texcoord( int i ) const +{ + return modelvertexdata->Texcoord( GetModelVertexIndex( i ) ); +}; + +inline mstudioboneweight_t *mstudio_meshvertexdata_t::BoneWeights( int i ) const +{ + return modelvertexdata->BoneWeights( GetModelVertexIndex( i ) ); +}; + +inline mstudiovertex_t *mstudio_meshvertexdata_t::Vertex( int i ) const +{ + return modelvertexdata->Vertex( GetModelVertexIndex( 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; + bool m_MeshNeedsRestore; + short m_ColorMeshID; + IMorph *m_pMorph; + + 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 */ + + // For decals on hardware morphing, we must actually do hardware skinning + // For this to work, we have to hope that the total # of bones used by + // hw flexed verts is < than the max possible for the dx level we're running under + int *m_pHWMorphDecalBoneRemap; + int m_nDecalBoneCount; +}; + +struct studiohwdata_t +{ + int m_RootLOD; // calced and clamped, nonzero for lod culling + int m_NumLODs; + studioloddata_t *m_pLODs; + int m_NumStudioMeshes; + + inline float LODMetric( float unitSphereSize ) const { return ( unitSphereSize != 0.0f ) ? (100.0f / unitSphereSize) : 0.0f; } + inline int GetLODForMetric( float lodMetric ) const + { + if ( !m_NumLODs ) + return 0; + + // shadow lod is specified on the last lod with a negative switch + // never consider shadow lod as viable candidate + int numLODs = (m_pLODs[m_NumLODs-1].m_SwitchPoint < 0.0f) ? m_NumLODs-1 : m_NumLODs; + + for ( int i = m_RootLOD; i < numLODs-1; i++ ) + { + if ( m_pLODs[i+1].m_SwitchPoint > lodMetric ) + return i; + } + + return numLODs-1; + } +}; + +// ---------------------------------------------------------- +// ---------------------------------------------------------- + +// body part index +struct mstudiobodyparts_t +{ + DECLARE_BYTESWAP_DATADESC(); + 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 +{ + DECLARE_BYTESWAP_DATADESC(); + int bone; + Vector forward; + int flexdesc; + + mstudiomouth_t(){} +private: + // No copy constructors allowed + mstudiomouth_t(const mstudiomouth_t& vOther); +}; + +struct mstudiohitboxset_t +{ + DECLARE_BYTESWAP_DATADESC(); + 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; }; +}; + + +//----------------------------------------------------------------------------- +// Src bone transforms are transformations that will convert .dmx or .smd-based animations into .mdl-based animations +// NOTE: The operation you should apply is: pretransform * bone transform * posttransform +//----------------------------------------------------------------------------- +struct mstudiosrcbonetransform_t +{ + DECLARE_BYTESWAP_DATADESC(); + + int sznameindex; + inline const char *pszName( void ) const { return ((char *)this) + sznameindex; } + matrix3x4_t pretransform; + matrix3x4_t posttransform; +}; + + +// ---------------------------------------------------------- +// 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 ]; } // Note: user must manage mutex for this + virtualgroup_t *pSeqGroup( int sequence ) + { + // Check for out of range access that is causing crashes on some servers. + // Perhaps caused by sourcemod bugs. Typical sequence in these cases is ~292 + // when the count is 234. Using unsigned math allows for free range + // checking against zero. + if ( (unsigned)sequence >= (unsigned)m_seq.Count() ) + { + Assert( 0 ); + return 0; + } + return &m_group[ m_seq[ sequence ].group ]; + } // Note: user must manage mutex for this + + CThreadFastMutex m_Lock; + + 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< virtualgeneric_t > m_iklock; + CUtlVector< unsigned short > m_autoplaySequences; +}; + +// 'thin' vertex data, used to do model decals (see Studio_CreateThinVertexes()) +struct thinModelVertices_t +{ + void Init( int numBoneInfluences, Vector *positions, unsigned short *normals, float *boneWeights, char *boneIndices ) + { + Assert( positions != NULL ); + Assert( normals != NULL ); + Assert( ( numBoneInfluences >= 0 ) && ( numBoneInfluences <= 3 ) ); + Assert( numBoneInfluences > 0 ? !!boneIndices : !boneIndices ); + Assert( numBoneInfluences > 1 ? !!boneWeights : !boneWeights ); + m_numBoneInfluences = numBoneInfluences; + m_vecPositions = positions; + m_vecNormals = normals; + m_boneWeights = boneWeights; + m_boneIndices = boneIndices; + } + + void SetPosition( int vertIndex, const Vector & position ) + { + Assert( m_vecPositions ); + m_vecPositions[ vertIndex ] = position; + } + + void SetNormal( int vertIndex, const Vector & normal ) + { + Assert( m_vecNormals ); + unsigned int packedNormal; + PackNormal_UBYTE4( normal.x, normal.y, normal.z, &packedNormal ); + m_vecNormals[ vertIndex ] = (unsigned short)( 0x0000FFFF & packedNormal ); + } + + void SetBoneWeights( int vertIndex, const mstudioboneweight_t & boneWeights ) + { + Assert( ( m_numBoneInfluences >= 1 ) && ( m_numBoneInfluences <= 3 ) ); + Assert( ( boneWeights.numbones >= 1 ) && ( boneWeights.numbones <= m_numBoneInfluences ) ); + int numStoredWeights = max( 0, ( m_numBoneInfluences - 1 ) ); + float *pBaseWeight = m_boneWeights + vertIndex*numStoredWeights; + char *pBaseIndex = m_boneIndices + vertIndex*m_numBoneInfluences; + for ( int i = 0; i < m_numBoneInfluences; i++ ) + { + pBaseIndex[i] = boneWeights.bone[i]; + } + for ( int i = 0; i < numStoredWeights; i++ ) + { + pBaseWeight[i] = boneWeights.weight[i]; + } + } + + void GetMeshPosition( mstudiomesh_t *pMesh, int meshIndex, Vector *pPosition ) const + { + Assert( pMesh ); + GetPosition( pMesh->vertexdata.GetGlobalVertexIndex( meshIndex ), pPosition ); + } + + void GetMeshNormal( mstudiomesh_t *pMesh, int meshIndex, Vector *pNormal ) const + { + Assert( pMesh ); + GetNormal( pMesh->vertexdata.GetGlobalVertexIndex( meshIndex ), pNormal ); + } + + void GetMeshBoneWeights( mstudiomesh_t *pMesh, int meshIndex, mstudioboneweight_t *pBoneWeights ) const + { + Assert( pMesh ); + GetBoneWeights( pMesh->vertexdata.GetGlobalVertexIndex( meshIndex ), pBoneWeights ); + } + + void GetModelPosition( mstudiomodel_t *pModel, int modelIndex, Vector *pPosition ) const + { + Assert( pModel ); + GetPosition( pModel->vertexdata.GetGlobalVertexIndex( modelIndex ), pPosition ); + } + + void GetModelNormal( mstudiomodel_t *pModel, int modelIndex, Vector *pNormal ) const + { + Assert( pModel ); + GetNormal( pModel->vertexdata.GetGlobalVertexIndex( modelIndex ), pNormal ); + } + + void GetModelBoneWeights( mstudiomodel_t *pModel, int modelIndex, mstudioboneweight_t *pBoneWeights ) const + { + Assert( pModel ); + GetBoneWeights( pModel->vertexdata.GetGlobalVertexIndex( modelIndex ), pBoneWeights ); + } + +private: + void GetPosition( int vertIndex, Vector *pPosition ) const + { + Assert( pPosition ); + Assert( m_vecPositions ); + *pPosition = m_vecPositions[ vertIndex ]; + } + + void GetNormal( int vertIndex, Vector *pNormal ) const + { + Assert( pNormal ); + Assert( m_vecNormals ); + unsigned int packedNormal = 0x0000FFFF & m_vecNormals[ vertIndex ]; + UnpackNormal_UBYTE4( &packedNormal, pNormal->Base() ); + } + + void GetBoneWeights( int vertIndex, mstudioboneweight_t *pBoneWeights ) const + { + Assert( pBoneWeights ); + Assert( ( m_numBoneInfluences <= 1 ) || ( m_boneWeights != NULL ) ); + Assert( ( m_numBoneInfluences <= 0 ) || ( m_boneIndices != NULL ) ); + int numStoredWeights = max( 0, ( m_numBoneInfluences - 1 ) ); + float *pBaseWeight = m_boneWeights + vertIndex*numStoredWeights; + char *pBaseIndex = m_boneIndices + vertIndex*m_numBoneInfluences; + float sum = 0.0f; + for (int i = 0;i < MAX_NUM_BONES_PER_VERT;i++) + { + if ( i < ( m_numBoneInfluences - 1 ) ) + pBoneWeights->weight[i] = pBaseWeight[i]; + else + pBoneWeights->weight[i] = 1.0f - sum; + sum += pBoneWeights->weight[i]; + + pBoneWeights->bone[i] = ( i < m_numBoneInfluences ) ? pBaseIndex[i] : 0; + } + + // Treat 'zero weights' as '100% binding to bone zero': + pBoneWeights->numbones = m_numBoneInfluences ? m_numBoneInfluences : 1; + } + + int m_numBoneInfluences;// Number of bone influences per vertex, N + float *m_boneWeights; // This array stores (N-1) weights per vertex (unless N is zero) + char *m_boneIndices; // This array stores N indices per vertex + Vector *m_vecPositions; + unsigned short *m_vecNormals; // Normals are compressed into 16 bits apiece (see PackNormal_UBYTE4() ) +}; + +// ---------------------------------------------------------- +// 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 +// this id (IDCV) is used once the vertex data has been compressed (see CMDLCache::CreateThinVertexes) +#define MODEL_VERTEX_FILE_THIN_ID (('V'<<24)+('C'<<16)+('D'<<8)+'I') + +struct vertexFileHeader_t +{ + DECLARE_BYTESWAP_DATADESC(); + int id; // MODEL_VERTEX_FILE_ID + int version; // MODEL_VERTEX_FILE_VERSION + int 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 + +public: + + // Accessor to fat vertex data + const mstudiovertex_t *GetVertexData() const + { + if ( ( id == MODEL_VERTEX_FILE_ID ) && ( vertexDataStart != 0 ) ) + return ( mstudiovertex_t * ) ( vertexDataStart + (byte *)this ); + else + return NULL; + } + // Accessor to (fat) tangent vertex data (tangents aren't stored in compressed data) + const Vector4D *GetTangentData() const + { + if ( ( id == MODEL_VERTEX_FILE_ID ) && ( tangentDataStart != 0 ) ) + return ( Vector4D * ) ( tangentDataStart + (byte *)this ); + else + return NULL; + } + // Accessor to thin vertex data + const thinModelVertices_t *GetThinVertexData() const + { + if ( ( id == MODEL_VERTEX_FILE_THIN_ID ) && ( vertexDataStart != 0 ) ) + return ( thinModelVertices_t * ) ( vertexDataStart + (byte *)this ); + else + return NULL; + } +}; + +// model vertex data accessor (defined here so vertexFileHeader_t can be used) +inline const mstudio_modelvertexdata_t * mstudiomodel_t::GetVertexData( void *pModelData ) +{ + const vertexFileHeader_t * pVertexHdr = CacheVertexData( pModelData ); + if ( !pVertexHdr ) + { + vertexdata.pVertexData = NULL; + vertexdata.pTangentData = NULL; + return NULL; + } + + vertexdata.pVertexData = pVertexHdr->GetVertexData(); + vertexdata.pTangentData = pVertexHdr->GetTangentData(); + + if ( !vertexdata.pVertexData ) + return NULL; + + return &vertexdata; +} + +// model thin vertex data accessor (defined here so vertexFileHeader_t can be used) +inline const thinModelVertices_t * mstudiomodel_t::GetThinVertexData( void *pModelData ) +{ + const vertexFileHeader_t * pVertexHdr = CacheVertexData( pModelData ); + if ( !pVertexHdr ) + return NULL; + + return pVertexHdr->GetThinVertexData(); +} + +// apply sequentially to lod sorted vertex and tangent pools to re-establish mesh order +struct vertexFileFixup_t +{ + DECLARE_BYTESWAP_DATADESC(); + 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 0x00000001 + +// 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 0x00000002 + +// Use this when there are translucent parts to the model but we're not going to sort it +#define STUDIOHDR_FLAGS_FORCE_OPAQUE 0x00000004 + +// 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 0x00000008 + +// 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 0x00000010 + +// 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 0x00000020 + +// 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 0x00000040 + +// 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 0x00000080 + +// 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 0x00000100 + +// 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 0x00000200 + +#define STUDIOHDR_FLAGS_UNUSED 0x00000400 + +// NOTE: This flag is set at mdl build time +#define STUDIOHDR_FLAGS_NO_FORCED_FADE 0x00000800 + +// NOTE: The npc will lengthen the viseme check to always include two phonemes +#define STUDIOHDR_FLAGS_FORCE_PHONEME_CROSSFADE 0x00001000 + +// 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 0x00002000 + +// Flag to mark delta flexes as already converted from disk format to memory format +#define STUDIOHDR_FLAGS_FLEXES_CONVERTED 0x00004000 + +// Indicates the studiomdl was built in preview mode +#define STUDIOHDR_FLAGS_BUILT_IN_PREVIEW_MODE 0x00008000 + +// Ambient boost (runtime flag) +#define STUDIOHDR_FLAGS_AMBIENT_BOOST 0x00010000 + +// Don't cast shadows from this model (useful on first-person models) +#define STUDIOHDR_FLAGS_DO_NOT_CAST_SHADOWS 0x00020000 + +// alpha textures should cast shadows in vrad on this model (ONLY prop_static!) +#define STUDIOHDR_FLAGS_CAST_TEXTURE_SHADOWS 0x00040000 + + +// flagged on load to indicate no animation events on this model +#define STUDIOHDR_FLAGS_VERT_ANIM_FIXED_POINT_SCALE 0x00200000 + +// NOTE! Next time we up the .mdl file format, remove studiohdr2_t +// and insert all fields in this structure into studiohdr_t. +struct studiohdr2_t +{ + // NOTE: For forward compat, make sure any methods in this struct + // are also available in studiohdr_t so no leaf code ever directly references + // a studiohdr2_t structure + DECLARE_BYTESWAP_DATADESC(); + int numsrcbonetransform; + int srcbonetransformindex; + + int illumpositionattachmentindex; + inline int IllumPositionAttachmentIndex() const { return illumpositionattachmentindex; } + + float flMaxEyeDeflection; + inline float MaxEyeDeflection() const { return flMaxEyeDeflection != 0.0f ? flMaxEyeDeflection : 0.866f; } // default to cos(30) if not set + + int linearboneindex; + inline mstudiolinearbone_t *pLinearBones() const { return (linearboneindex) ? (mstudiolinearbone_t *)(((byte *)this) + linearboneindex) : NULL; } + + int sznameindex; + inline char *pszName() { return (sznameindex) ? (char *)(((byte *)this) + sznameindex ) : NULL; } + + int m_nBoneFlexDriverCount; + int m_nBoneFlexDriverIndex; + inline mstudioboneflexdriver_t *pBoneFlexDriver( int i ) const { Assert( i >= 0 && i < m_nBoneFlexDriverCount ); return (mstudioboneflexdriver_t *)(((byte *)this) + m_nBoneFlexDriverIndex) + i; } + + int reserved[56]; +}; + +struct studiohdr_t +{ + DECLARE_BYTESWAP_DATADESC(); + int id; + int version; + + int checksum; // this has to be the same in the phy and vtx files to load! + + inline const char * pszName( void ) const { if (studiohdr2index && pStudioHdr2()->pszName()) return pStudioHdr2()->pszName(); else 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 ); + void SetActivityListVersion( int version ) const; + int GetEventListVersion( void ); + void SetEventListVersion( int version ); + + // raw textures + int numtextures; + int textureindex; + inline mstudiotexture_t *pTexture( int i ) const { Assert( i >= 0 && i < numtextures ); 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 ); + // 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 ); + int ExitNode( int iSequence ); + char *pszNodeName( int iNode ); + 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( LocalFlexController_t i ) const { Assert( numflexcontrollers == 0 || ( 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 ); + 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 ); + 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; + + // set in the mdl data to specify that lod configuration should only allow first numAllowRootLODs + // to be set as root LOD: + // numAllowedRootLODs = 0 means no restriction, any lod can be set as root lod. + // numAllowedRootLODs = N means that lod0 - lod(N-1) can be set as root lod, but not lodN or lower. + byte numAllowedRootLODs; + + byte unused[1]; + + int unused4; // zero out if version < 47 + + int numflexcontrollerui; + int flexcontrolleruiindex; + mstudioflexcontrollerui_t *pFlexControllerUI( int i ) const { Assert( i >= 0 && i < numflexcontrollerui); return (mstudioflexcontrollerui_t *)(((byte *)this) + flexcontrolleruiindex) + i; } + + float flVertAnimFixedPointScale; + inline float VertAnimFixedPointScale() const { return ( flags & STUDIOHDR_FLAGS_VERT_ANIM_FIXED_POINT_SCALE ) ? flVertAnimFixedPointScale : 1.0f / 4096.0f; } + + int unused3[1]; + + // FIXME: Remove when we up the model version. Move all fields of studiohdr2_t into studiohdr_t. + int studiohdr2index; + studiohdr2_t* pStudioHdr2() const { return (studiohdr2_t *)( ( (byte *)this ) + studiohdr2index ); } + + // Src bone transforms are transformations that will convert .dmx or .smd-based animations into .mdl-based animations + int NumSrcBoneTransforms() const { return studiohdr2index ? pStudioHdr2()->numsrcbonetransform : 0; } + const mstudiosrcbonetransform_t* SrcBoneTransform( int i ) const { Assert( i >= 0 && i < NumSrcBoneTransforms()); return (mstudiosrcbonetransform_t *)(((byte *)this) + pStudioHdr2()->srcbonetransformindex) + i; } + + inline int IllumPositionAttachmentIndex() const { return studiohdr2index ? pStudioHdr2()->IllumPositionAttachmentIndex() : 0; } + + inline float MaxEyeDeflection() const { return studiohdr2index ? pStudioHdr2()->MaxEyeDeflection() : 0.866f; } // default to cos(30) if not set + + inline mstudiolinearbone_t *pLinearBones() const { return studiohdr2index ? pStudioHdr2()->pLinearBones() : NULL; } + + inline int BoneFlexDriverCount() const { return studiohdr2index ? pStudioHdr2()->m_nBoneFlexDriverCount : 0; } + inline const mstudioboneflexdriver_t* BoneFlexDriver( int i ) const { Assert( i >= 0 && i < BoneFlexDriverCount() ); return studiohdr2index ? pStudioHdr2()->pBoneFlexDriver( i ) : NULL; } + + // NOTE: No room to add stuff? Up the .mdl file format version + // [and move all fields in studiohdr2_t into studiohdr_t and kill studiohdr2_t], + // or add your stuff to studiohdr2_t. See NumSrcBoneTransforms/SrcBoneTransform for the pattern to use. + int unused2[1]; + + 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( const studiohdr_t *pStudioHdr, IMDLCache *mdlcache = NULL ); + ~CStudioHdr() { Term(); } + + void Init( const studiohdr_t *pStudioHdr, IMDLCache *mdlcache = NULL ); + void Term(); + +public: + inline bool IsVirtual( void ) { return (m_pVModel != NULL); }; + inline bool IsValid( void ) { return (m_pStudioHdr != NULL); }; + inline bool IsReadyForAccess( void ) const { 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 studiohdr_t *pAnimStudioHdr( int animation ); + +private: + mutable const studiohdr_t *m_pStudioHdr; + mutable virtualmodel_t *m_pVModel; + + const virtualmodel_t * ResetVModel( const virtualmodel_t *pVModel ) const; + const studiohdr_t *GroupStudioHdr( int group ); + mutable CUtlVector< const studiohdr_t * > m_pStudioHdrCache; + + mutable int m_nFrameUnlockCounter; + int * m_pFrameUnlockCounter; + CThreadFastMutex m_FrameUnlockCounterMutex; + +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 ); + mstudioseqdesc_t &pSeqdesc( int iSequence ); + 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 ); + void SetActivityListVersion( int version ); + int GetEventListVersion( void ); + void SetEventListVersion( int version ); + + int GetNumAttachments( void ) const; + const mstudioattachment_t &pAttachment( int i ); + int GetAttachmentBone( int i ); + // used on my tools in hlmv, not persistant + void SetAttachmentBone( int iAttachment, int iBone ); + + int EntryNode( int iSequence ); + int ExitNode( int iSequence ); + char *pszNodeName( int iNode ); + // FIXME: where should this one be? + int GetTransition( int iFrom, int iTo ) const; + + int GetNumPoseParameters( void ) const; + const mstudioposeparamdesc_t &pPoseParameter( int i ); + int GetSharedPoseParameter( int iSequence, int iLocalPose ) const; + + int GetNumIKAutoplayLocks( void ) const; + const mstudioiklock_t &pIKAutoplayLock( int i ); + + 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 LocalFlexController_t numflexcontrollers() const{ return (LocalFlexController_t)m_pStudioHdr->numflexcontrollers; }; + inline mstudioflexcontroller_t *pFlexcontroller( LocalFlexController_t i ) const { return m_pStudioHdr->pFlexcontroller( i ); }; + + inline int numflexcontrollerui() const{ return m_pStudioHdr->numflexcontrollerui; }; + inline mstudioflexcontrollerui_t *pFlexcontrollerUI( int i ) const { return m_pStudioHdr->pFlexControllerUI( 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 int IllumPositionAttachmentIndex() const { return m_pStudioHdr->IllumPositionAttachmentIndex(); } + + inline float MaxEyeDeflection() const { return m_pStudioHdr->MaxEyeDeflection(); } + + inline mstudiolinearbone_t *pLinearBones() const { return m_pStudioHdr->pLinearBones(); } + + inline int BoneFlexDriverCount() const { return m_pStudioHdr->BoneFlexDriverCount(); } + inline const mstudioboneflexdriver_t *BoneFlexDriver( int i ) const { return m_pStudioHdr->BoneFlexDriver( i ); } + + inline float VertAnimFixedPointScale() const { return m_pStudioHdr->VertAnimFixedPointScale(); } + +public: + int IsSequenceLooping( int iSequence ); + float GetSequenceCycleRate( int iSequence ); + + void RunFlexRules( const float *src, float *dest ); + + +public: + inline int boneFlags( int iBone ) const { return m_boneFlags[ iBone ]; } + inline int boneParent( int iBone ) const { return m_boneParent[ iBone ]; } + +private: + CUtlVector< int > m_boneFlags; + CUtlVector< int > m_boneParent; + +public: + + // This class maps an activity to sequences allowed for that activity, accelerating the resolution + // of SelectWeightedSequence(), especially on PowerPC. Iterating through every sequence + // attached to a model turned out to be a very destructive cache access pattern on 360. + // + // I've encapsulated this behavior inside a nested class for organizational reasons; there is + // no particular programmatic or efficiency benefit to it. It just makes clearer what particular + // code in the otherwise very complicated StudioHdr class has to do with this particular + // optimization, and it lets you collapse the whole definition down to a single line in Visual + // Studio. + class CActivityToSequenceMapping /* final */ + { + public: + // A tuple of a sequence and its corresponding weight. Lists of these correspond to activities. + struct SequenceTuple + { + short seqnum; + short weight; // the absolute value of the weight from the sequence header + CUtlSymbol *pActivityModifiers; // list of activity modifier symbols + int iNumActivityModifiers; + }; + + // The type of the hash's stored data, a composite of both key and value + // (because that's how CUtlHash works): + // key: an int, the activity # + // values: an index into the m_pSequenceTuples array, a count of the + // total sequences present for an activity, and the sum of their + // weights. + // Note this struct is 128-bits wide, exactly coincident to a PowerPC + // cache line and VMX register. Please consider very carefully the + // performance implications before adding any additional fields to this. + // You could probably do away with totalWeight if you really had to. + struct HashValueType + { + // KEY (hashed) + int activityIdx; + + // VALUE (not hashed) + int startingIdx; + int count; + int totalWeight; + + HashValueType(int _actIdx, int _stIdx, int _ct, int _tW) : + activityIdx(_actIdx), startingIdx(_stIdx), count(_ct), totalWeight(_tW) {} + + // default constructor (ought not to be actually used) + HashValueType() : activityIdx(-1), startingIdx(-1), count(-1), totalWeight(-1) + { AssertMsg(false, "Don't use default HashValueType()!"); } + + + class HashFuncs + { + public: + // dummy constructor (gndn) + HashFuncs( int ) {} + + // COMPARE + // compare two entries for uniqueness. We should never have two different + // entries for the same activity, so we only compare the activity index; + // this allows us to use the utlhash as a dict by constructing dummy entries + // as hash lookup keys. + bool operator()( const HashValueType &lhs, const HashValueType &rhs ) const + { + return lhs.activityIdx == rhs.activityIdx; + } + + // HASH + // We only hash on the activity index; everything else is data. + unsigned int operator()( const HashValueType &item ) const + { + return HashInt( item.activityIdx ); + } + }; + }; + + typedef CUtlHash<HashValueType, HashValueType::HashFuncs, HashValueType::HashFuncs> ActivityToValueIdxHash; + + // These must be here because IFM does not compile/link studio.cpp (?!?) + + // ctor + CActivityToSequenceMapping( void ) + : m_pSequenceTuples(NULL), m_iSequenceTuplesCount(0), m_ActToSeqHash(8,0,0), m_expectedPStudioHdr(NULL), m_expectedVModel(NULL) +#if STUDIO_SEQUENCE_ACTIVITY_LAZY_INITIALIZE + , m_bIsInitialized(false) +#endif + {}; + + // dtor -- not virtual because this class has no inheritors + ~CActivityToSequenceMapping() + { + if ( m_pSequenceTuples != NULL ) + { + if ( m_pSequenceTuples->pActivityModifiers != NULL ) + { + delete[] m_pSequenceTuples->pActivityModifiers; + } + delete[] m_pSequenceTuples; + } + } + + /// Get the list of sequences for an activity. Returns the pointer to the + /// first sequence tuple. Output parameters are a count of sequences present, + /// and the total weight of all the sequences. (it would be more LHS-friendly + /// to return these on registers, if only C++ offered more than one return + /// value....) + const SequenceTuple *GetSequences( int forActivity, int *outSequenceCount, int *outTotalWeight ); + + /// The number of sequences available for an activity. + int NumSequencesForActivity( int forActivity ); + +#if STUDIO_SEQUENCE_ACTIVITY_LAZY_INITIALIZE + inline bool IsInitialized( void ) { return m_bIsInitialized; } +#endif + + private: + + /// Allocate my internal array. (It is freed in the destructor.) Also, + /// build the hash of activities to sequences and populate m_pSequenceTuples. + void Initialize( CStudioHdr *pstudiohdr ); + + /// Force Initialize() to occur again, even if it has already occured. + void Reinitialize( CStudioHdr *pstudiohdr ); + + /// A more efficient version of the old SelectWeightedSequence() function in animation.cpp. + int SelectWeightedSequence( CStudioHdr *pstudiohdr, int activity, int curSequence ); + + // selects the sequence with the most matching modifiers + int SelectWeightedSequenceFromModifiers( CStudioHdr *pstudiohdr, int activity, CUtlSymbol *pActivityModifiers, int iModifierCount ); + + // Actually a big array, into which the hash values index. + SequenceTuple *m_pSequenceTuples; + unsigned int m_iSequenceTuplesCount; // (size of the whole array) +#if STUDIO_SEQUENCE_ACTIVITY_LAZY_INITIALIZE + bool m_bIsInitialized; +#endif + + // we don't store an outer pointer because we can't initialize it at construction time + // (warning c4355) -- there are ways around this but it's easier to just pass in a + // pointer to the CStudioHdr when we need it, since this class isn't supposed to + // export its interface outside the studio header anyway. + // CStudioHdr * const m_pOuter; + + ActivityToValueIdxHash m_ActToSeqHash; + + // we store these so we can know if the contents of the studiohdr have changed + // from underneath our feet (this is an emergency data integrity check) + const void *m_expectedPStudioHdr; + const void *m_expectedVModel; + + // double-check that the data I point to hasn't changed + bool ValidateAgainst( const CStudioHdr * RESTRICT pstudiohdr ); + void SetValidationPair( const CStudioHdr *RESTRICT pstudiohdr ); + + friend class CStudioHdr; + }; + + CActivityToSequenceMapping m_ActivityToSequence; + + /// A more efficient version of the old SelectWeightedSequence() function in animation.cpp. + /// Returns -1 on failure to find a sequence + inline int SelectWeightedSequence( int activity, int curSequence ) + { +#if STUDIO_SEQUENCE_ACTIVITY_LAZY_INITIALIZE + // We lazy-initialize the header on demand here, because CStudioHdr::Init() is + // called from the constructor, at which time the this pointer is illegitimate. + if ( !m_ActivityToSequence.IsInitialized() ) + { + m_ActivityToSequence.Initialize(this); + } +#endif + return m_ActivityToSequence.SelectWeightedSequence( this, activity, curSequence ); + } + + inline int SelectWeightedSequenceFromModifiers( int activity, CUtlSymbol *pActivityModifiers, int iModifierCount ) + { +#if STUDIO_SEQUENCE_ACTIVITY_LAZY_INITIALIZE + // We lazy-initialize the header on demand here, because CStudioHdr::Init() is + // called from the constructor, at which time the this pointer is illegitimate. + if ( !m_ActivityToSequence.IsInitialized() ) + { + m_ActivityToSequence.Initialize( this ); + } +#endif + return m_ActivityToSequence.SelectWeightedSequenceFromModifiers( this, activity, pActivityModifiers, iModifierCount ); + } + + /// True iff there is at least one sequence for the given activity. + inline bool HaveSequenceForActivity( int activity ) + { +#if STUDIO_SEQUENCE_ACTIVITY_LAZY_INITIALIZE + if ( !m_ActivityToSequence.IsInitialized() ) + { + m_ActivityToSequence.Initialize(this); + } +#endif + return (m_ActivityToSequence.NumSequencesForActivity( activity ) > 0); + } + + // Force this CStudioHdr's activity-to-sequence mapping to be reinitialized + inline void ReinitializeSequenceMapping(void) + { + m_ActivityToSequence.Reinitialize(this); + } + +#ifdef STUDIO_ENABLE_PERF_COUNTERS +public: + inline void ClearPerfCounters( void ) + { + m_nPerfAnimatedBones = 0; + m_nPerfUsedBones = 0; + m_nPerfAnimationLayers = 0; + }; + + // timing info + mutable int m_nPerfAnimatedBones; + mutable int m_nPerfUsedBones; + mutable int m_nPerfAnimationLayers; +#endif + + +}; + +/* +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 +{ + DECLARE_BYTESWAP_DATADESC(); + int key; + float weight; + float influence; +}; + +struct flexsetting_t +{ + DECLARE_BYTESWAP_DATADESC(); + int nameindex; + + inline char *pszName( void ) const + { + return (char *)(((byte *)this) + nameindex); + } + + // Leaving this for legacy support + int obsolete1; + + // Number of flex settings + int numsettings; + int index; + + // OBSOLETE: + int obsolete2; + + // Index of start of contiguous array of flexweight_t structures + int settingindex; + + //----------------------------------------------------------------------------- + // 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 +{ + DECLARE_BYTESWAP_DATADESC(); + 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: Retrieves a pointer to the flexweight_t. +// 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 +{ + // Grab array pointer + *weights = (flexweight_t *)(((byte *)this) + settingindex) + i; + // Return true number of settings + return numsettings; +}; + + +//----------------------------------------------------------------------------- +// For a given flex controller ui struct, these return the index of the +// studiohdr_t flex controller that correspond to the the left and right +// flex controllers if the ui controller is a stereo control. +// nWayValueIndex returns the index of the flex controller that is the value +// flex controller for an NWAY combination +// If these functions are called and the ui controller isn't of the type +// specified then -1 is returned +//----------------------------------------------------------------------------- +inline int mstudioflexcontrollerui_t::controllerIndex( const CStudioHdr &cStudioHdr ) const +{ + return !stereo ? pController() - cStudioHdr.pFlexcontroller( (LocalFlexController_t)0 ) : -1; +} + + +inline int mstudioflexcontrollerui_t::rightIndex( const CStudioHdr &cStudioHdr ) const +{ + return stereo ? pRightController() - cStudioHdr.pFlexcontroller( (LocalFlexController_t)0 ) : -1; +} + + +inline int mstudioflexcontrollerui_t::leftIndex( const CStudioHdr &cStudioHdr ) const +{ + return stereo ? pLeftController() - cStudioHdr.pFlexcontroller((LocalFlexController_t) 0 ) : -1; +} + + +inline int mstudioflexcontrollerui_t::nWayValueIndex( const CStudioHdr &cStudioHdr ) const +{ + return remaptype == FLEXCONTROLLER_REMAP_NWAY ? pNWayValueController() - cStudioHdr.pFlexcontroller( (LocalFlexController_t)0 ) : -1; +} + + +inline const mstudioflexcontroller_t *mstudioflexcontrollerui_t::pController( int index ) const +{ + if ( index < 0 || index > Count() ) + return NULL; + + if ( remaptype == FLEXCONTROLLER_REMAP_NWAY ) + { + if ( stereo ) + return (mstudioflexcontroller_t *)( ( char * ) this ) + *( &szindex0 + index ); + + if ( index == 0 ) + return pController(); + + if ( index == 1 ) + return pNWayValueController(); + + return NULL; + } + + if ( index > 1 ) + return NULL; + + if ( stereo ) + return (mstudioflexcontroller_t *)( ( char * ) this ) + *( &szindex0 + index ); + + if ( index > 0 ) + return NULL; + + return pController(); +} + + +#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 +#define STUDIO_2WAY_0 15 // Fetch a value from a 2 Way slider for the 1st value RemapVal( 0.0, 0.5, 0.0, 1.0 ) +#define STUDIO_2WAY_1 16 // Fetch a value from a 2 Way slider for the 2nd value RemapVal( 0.5, 1.0, 0.0, 1.0 ) +#define STUDIO_NWAY 17 // Fetch a value from a 2 Way slider for the 2nd value RemapVal( 0.5, 1.0, 0.0, 1.0 ) +#define STUDIO_COMBO 18 // Perform a combo operation (essentially multiply the last N values on the stack) +#define STUDIO_DOMINATE 19 // Performs a combination domination operation +#define STUDIO_DME_LOWER_EYELID 20 // +#define STUDIO_DME_UPPER_EYELID 21 // + +// 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 +#define STUDIO_CYCLEPOSE 0x0080 // cycle index is taken from a pose parameter index +#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 parent 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 bool Studio_ConvertStudioHdrToNewVersion( studiohdr_t *pStudioHdr ) +{ + COMPILE_TIME_ASSERT( STUDIO_VERSION == 48 ); // put this to make sure this code is updated upon changing version. + + int version = pStudioHdr->version; + if ( version == STUDIO_VERSION ) + return true; + + bool bResult = true; + if (version < 46) + { + // some of the anim index data is incompatible + for (int i = 0; i < pStudioHdr->numlocalanim; i++) + { + mstudioanimdesc_t *pAnim = (mstudioanimdesc_t *)pStudioHdr->pLocalAnimdesc( i ); + + // old ANI files that used sections (v45 only) are not compatible + if ( pAnim->sectionframes != 0 ) + { + // zero most everything out + memset( &(pAnim->numframes), 0, (byte *)(pAnim + 1) - (byte *)&(pAnim->numframes) ); + + pAnim->numframes = 1; + pAnim->animblock = -1; // disable animation fetching + bResult = false; + } + } + } + + if (version < 47) + { + // used to contain zeroframe cache data + if (pStudioHdr->unused4 != 0) + { + pStudioHdr->unused4 = 0; + bResult = false; + } + for (int i = 0; i < pStudioHdr->numlocalanim; i++) + { + mstudioanimdesc_t *pAnim = (mstudioanimdesc_t *)pStudioHdr->pLocalAnimdesc( i ); + pAnim->zeroframeindex = 0; + pAnim->zeroframespan = 0; + } + } + else if (version == 47) + { + for (int i = 0; i < pStudioHdr->numlocalanim; i++) + { + mstudioanimdesc_t *pAnim = (mstudioanimdesc_t *)pStudioHdr->pLocalAnimdesc( i ); + if (pAnim->zeroframeindex != 0) + { + pAnim->zeroframeindex = 0; + pAnim->zeroframespan = 0; + bResult = false; + } + } + } + + // for now, just slam the version number since they're compatible + pStudioHdr->version = STUDIO_VERSION; + + return bResult; +} + +// must be run to fixup with specified rootLOD +inline void Studio_SetRootLOD( studiohdr_t *pStudioHdr, int rootLOD ) +{ + // honor studiohdr restriction of root lod in case requested root lod exceeds restriction. + if ( pStudioHdr->numAllowedRootLODs > 0 && + rootLOD >= pStudioHdr->numAllowedRootLODs ) + { + rootLOD = pStudioHdr->numAllowedRootLODs - 1; + } + + Assert( rootLOD >= 0 && rootLOD < MAX_NUM_LODS ); + Clamp( rootLOD, 0, MAX_NUM_LODS - 1 ); + + // 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 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 quantity 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 |