diff options
Diffstat (limited to 'mp/src/public/studio.h')
| -rw-r--r-- | mp/src/public/studio.h | 3118 |
1 files changed, 3118 insertions, 0 deletions
diff --git a/mp/src/public/studio.h b/mp/src/public/studio.h new file mode 100644 index 00000000..7770c2af --- /dev/null +++ b/mp/src/public/studio.h @@ -0,0 +1,3118 @@ +//========= 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"
+
+
+#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);
+};
+
+
+#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
+};
+
+// 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 unused[7]; // remove/add as appropriate (grow back to 8 ints on version change!)
+
+ mstudioseqdesc_t(){}
+private:
+ // No copy constructors allowed
+ mstudioseqdesc_t(const mstudioseqdesc_t& vOther);
+};
+
+
+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];
+};
+
+
+// these are the on-disk format vertex anims
+struct dstudiovertanim_t
+{
+ unsigned short index;
+ byte speed; // 255/max_length_in_flex
+ byte side; // 255/left_right
+ Vector48 delta;
+ Vector48 ndelta;
+
+private:
+ // No copy constructors allowed
+ dstudiovertanim_t(const dstudiovertanim_t& vOther);
+};
+
+
+struct dstudiovertanim_wrinkle_t : public dstudiovertanim_t
+{
+ short wrinkledelta; // Encodes a range from -1 to 1. NOTE: -32768 == -32767 == -1.0f, 32767 = 1.0f
+
+private:
+ // No copy constructors allowed
+ dstudiovertanim_wrinkle_t( const dstudiovertanim_t& vOther );
+};
+
+const float g_VertAnimFixedPointScale = 1.0f / 4096.0f;
+const float g_VertAnimFixedPointScaleInv = 1.0f / g_VertAnimFixedPointScale;
+
+// this is the memory image of vertex anims (16-bit fixed point)
+struct mstudiovertanim_t
+{
+ 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 Vector GetDeltaFixed()
+ {
+ return Vector( delta[0]*g_VertAnimFixedPointScale, delta[1]*g_VertAnimFixedPointScale, delta[2]*g_VertAnimFixedPointScale );
+ }
+ inline Vector GetNDeltaFixed()
+ {
+ return Vector( ndelta[0]*g_VertAnimFixedPointScale, ndelta[1]*g_VertAnimFixedPointScale, ndelta[2]*g_VertAnimFixedPointScale );
+ }
+ inline void GetDeltaFixed4DAligned( Vector4DAligned *vFillIn )
+ {
+ vFillIn->Set( delta[0]*g_VertAnimFixedPointScale, delta[1]*g_VertAnimFixedPointScale, delta[2]*g_VertAnimFixedPointScale, 0.0f );
+ }
+ inline void GetNDeltaFixed4DAligned( Vector4DAligned *vFillIn )
+ {
+ vFillIn->Set( ndelta[0]*g_VertAnimFixedPointScale, ndelta[1]*g_VertAnimFixedPointScale, ndelta[2]*g_VertAnimFixedPointScale, 0.0f );
+ }
+ inline Vector GetDeltaFloat()
+ {
+ return Vector (flDelta[0].GetFloat(), flDelta[1].GetFloat(), flDelta[2].GetFloat());
+ }
+ inline Vector GetNDeltaFloat()
+ {
+ return Vector (flNDelta[0].GetFloat(), flNDelta[1].GetFloat(), flNDelta[2].GetFloat());
+ }
+ inline void SetDeltaFixed( const Vector& vInput )
+ {
+ delta[0] = vInput.x * g_VertAnimFixedPointScaleInv;
+ delta[1] = vInput.y * g_VertAnimFixedPointScaleInv;
+ delta[2] = vInput.z * g_VertAnimFixedPointScaleInv;
+ }
+ inline void SetNDeltaFixed( const Vector& vInputNormal )
+ {
+ ndelta[0] = vInputNormal.x * g_VertAnimFixedPointScaleInv;
+ ndelta[1] = vInputNormal.y * g_VertAnimFixedPointScaleInv;
+ ndelta[2] = vInputNormal.z * g_VertAnimFixedPointScaleInv;
+ }
+
+ // Ick...can also force fp16 data into this structure for writing to file in legacy format...
+ inline void SetDeltaFloat( const Vector& vInput )
+ {
+ flDelta[0].SetFloat( vInput.x );
+ flDelta[1].SetFloat( vInput.y );
+ flDelta[2].SetFloat( vInput.z );
+ }
+ inline void SetNDeltaFloat( const Vector& vInputNormal )
+ {
+ flNDelta[0].SetFloat( vInputNormal.x );
+ flNDelta[1].SetFloat( vInputNormal.y );
+ flNDelta[2].SetFloat( vInputNormal.z );
+ }
+
+ mstudiovertanim_t(){}
+private:
+ // No copy constructors allowed
+ 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 )
+ {
+ int nWrinkleDeltaInt = flWrinkle * g_VertAnimFixedPointScaleInv;
+ wrinkledelta = clamp( nWrinkleDeltaInt, -32767, 32767 );
+ }
+
+ inline Vector4D GetDeltaFixed()
+ {
+ return Vector4D( delta[0]*g_VertAnimFixedPointScale, delta[1]*g_VertAnimFixedPointScale, delta[2]*g_VertAnimFixedPointScale, wrinkledelta*g_VertAnimFixedPointScale );
+ }
+
+ inline void GetDeltaFixed4DAligned( Vector4DAligned *vFillIn )
+ {
+ vFillIn->Set( delta[0]*g_VertAnimFixedPointScale, delta[1]*g_VertAnimFixedPointScale, delta[2]*g_VertAnimFixedPointScale, wrinkledelta*g_VertAnimFixedPointScale );
+ }
+};
+
+
+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
+ long checksum; // same as studiohdr_t, ensures sync
+ int numLODs; // num of valid lods
+ int numLODVertexes[MAX_NUM_LODS]; // num verts for desired root lod
+ int numFixups; // num of vertexFileFixup_t
+ int fixupTableStart; // offset from base to fixup table
+ int vertexDataStart; // offset from base to vertex block
+ int tangentDataStart; // offset from base to tangent block
+
+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 ( 1 << 0 )
+
+// NOTE: This flag is set at loadtime, not mdl build time so that we don't have to rebuild
+// models when we change materials.
+#define STUDIOHDR_FLAGS_USES_ENV_CUBEMAP ( 1 << 1 )
+
+// Use this when there are translucent parts to the model but we're not going to sort it
+#define STUDIOHDR_FLAGS_FORCE_OPAQUE ( 1 << 2 )
+
+// Use this when we want to render the opaque parts during the opaque pass
+// and the translucent parts during the translucent pass
+#define STUDIOHDR_FLAGS_TRANSLUCENT_TWOPASS ( 1 << 3 )
+
+// This is set any time the .qc files has $staticprop in it
+// Means there's no bones and no transforms
+#define STUDIOHDR_FLAGS_STATIC_PROP ( 1 << 4 )
+
+// NOTE: This flag is set at loadtime, not mdl build time so that we don't have to rebuild
+// models when we change materials.
+#define STUDIOHDR_FLAGS_USES_FB_TEXTURE ( 1 << 5 )
+
+// This flag is set by studiomdl.exe if a separate "$shadowlod" entry was present
+// for the .mdl (the shadow lod is the last entry in the lod list if present)
+#define STUDIOHDR_FLAGS_HASSHADOWLOD ( 1 << 6 )
+
+// NOTE: This flag is set at loadtime, not mdl build time so that we don't have to rebuild
+// models when we change materials.
+#define STUDIOHDR_FLAGS_USES_BUMPMAPPING ( 1 << 7 )
+
+// NOTE: This flag is set when we should use the actual materials on the shadow LOD
+// instead of overriding them with the default one (necessary for translucent shadows)
+#define STUDIOHDR_FLAGS_USE_SHADOWLOD_MATERIALS ( 1 << 8 )
+
+// NOTE: This flag is set when we should use the actual materials on the shadow LOD
+// instead of overriding them with the default one (necessary for translucent shadows)
+#define STUDIOHDR_FLAGS_OBSOLETE ( 1 << 9 )
+
+#define STUDIOHDR_FLAGS_UNUSED ( 1 << 10 )
+
+// NOTE: This flag is set at mdl build time
+#define STUDIOHDR_FLAGS_NO_FORCED_FADE ( 1 << 11 )
+
+// NOTE: The npc will lengthen the viseme check to always include two phonemes
+#define STUDIOHDR_FLAGS_FORCE_PHONEME_CROSSFADE ( 1 << 12 )
+
+// This flag is set when the .qc has $constantdirectionallight in it
+// If set, we use constantdirectionallightdot to calculate light intensity
+// rather than the normal directional dot product
+// only valid if STUDIOHDR_FLAGS_STATIC_PROP is also set
+#define STUDIOHDR_FLAGS_CONSTANT_DIRECTIONAL_LIGHT_DOT ( 1 << 13 )
+
+// Flag to mark delta flexes as already converted from disk format to memory format
+#define STUDIOHDR_FLAGS_FLEXES_CONVERTED ( 1 << 14 )
+
+// Indicates the studiomdl was built in preview mode
+#define STUDIOHDR_FLAGS_BUILT_IN_PREVIEW_MODE ( 1 << 15 )
+
+// Ambient boost (runtime flag)
+#define STUDIOHDR_FLAGS_AMBIENT_BOOST ( 1 << 16 )
+
+// Don't cast shadows from this model (useful on first-person models)
+#define STUDIOHDR_FLAGS_DO_NOT_CAST_SHADOWS ( 1 << 17 )
+
+// alpha textures should cast shadows in vrad on this model (ONLY prop_static!)
+#define STUDIOHDR_FLAGS_CAST_TEXTURE_SHADOWS ( 1 << 18 )
+
+
+// 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 reserved[58];
+};
+
+struct studiohdr_t
+{
+ DECLARE_BYTESWAP_DATADESC();
+ int id;
+ int version;
+
+ long checksum; // this has to be the same in the phy and vtx files to load!
+
+ inline const char * pszName( void ) const { 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; }
+
+ int unused3[2];
+
+ // 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; }
+
+ // 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(); }
+
+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
+ };
+
+ // 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 )
+ {
+ 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 );
+
+ // 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 );
+ }
+
+ /// 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;
+ }
+
+ // 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
|