#ifndef STUDIO_H
#define STUDIO_H
#include "mathlib/mathlib.h"

#define MAX_NUM_LODS 8

// 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 )

// NOTE:  This flag is set when we need to draw in the additive stage of the deferred rendering
#define STUDIOHDR_FLAGS_NEEDS_DEFERRED_ADDITIVE	( 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 )

// This flag indicates that the model has complex weights, which allows it to have 3< weights per bone.
// PREVIOUSLY USED FOR: STUDIOHDR_FLAGS_FLEXES_CONVERTED
#define STUDIOHDR_FLAGS_COMPLEX_WEIGHTS		( 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 )

// Model has a quad-only Catmull-Clark SubD cage
#define STUDIOHDR_FLAGS_SUBDIVISION_SURFACE		( 1 << 19 )

// flagged on load to indicate no animation events on this model
#define STUDIOHDR_FLAGS_NO_ANIM_EVENTS			( 1 << 20 )

// If flag is set then studiohdr_t.flVertAnimFixedPointScale contains the
// scale value for fixed point vert anim data, if not set then the
// scale value is the default of 1.0 / 4096.0.  Regardless use
// studiohdr_t::VertAnimFixedPointScale() to always retrieve the scale value
#define STUDIOHDR_FLAGS_VERT_ANIM_FIXED_POINT_SCALE	( 1 << 21 )

// unknown purpose but definitely exists
#define STUDIOHDR_FLAGS_RESPAWN_UNK (1 << 23) // 0x800000

// If this flag is present the model has vertex color, and by extension (previously) a VVC (IDVC) file.
#define STUDIOHDR_FLAGS_USES_VERTEX_COLOR (1 << 24) // 0x1000000 

// If this flag is present the model has a secondary UV layer, and by extension (previously) a VVC (IDVC) file.
#define STUDIOHDR_FLAGS_USES_UV2 (1 << 25) // 0x2000000

//-------------------------------------------------------------------------------------
// Forward declarations
//-------------------------------------------------------------------------------------
class CMDLCache;

enum
{
	STUDIODATA_FLAGS_STUDIOMESH_LOADED = 0x0001,
	STUDIODATA_FLAGS_VCOLLISION_LOADED = 0x0002,
	STUDIODATA_ERROR_MODEL = 0x0004,
	STUDIODATA_FLAGS_NO_STUDIOMESH = 0x0008,
	STUDIODATA_FLAGS_NO_VERTEX_DATA = 0x0010,
	//										= 0x0020,	// unused
	STUDIODATA_FLAGS_PHYSICS2COLLISION_LOADED = 0x0040,
	STUDIODATA_FLAGS_VCOLLISION_SCANNED = 0x0080,

	STUDIODATA_FLAGS_COMBINED_PLACEHOLDER = 0x0100,
	STUDIODATA_FLAGS_COMBINED = 0x0200,
	STUDIODATA_FLAGS_COMBINED_UNAVAILABLE = 0x0400,
	STUDIODATA_FLAGS_COMBINED_ASSET = 0x0800,
};

// intersection boxes
struct mstudiobbox_t
{
//	DECLARE_BYTESWAP_DATADESC();
	int					bone;
	int					group;				// intersection group
	Vector3D			bbmin;				// bounding box
	Vector3D			bbmax;
	int					szhitboxnameindex;	// offset to the name of the hitbox.
	int					critoverride;		// overrides the group to be a crit, 0 or 1. might be group override since group 1 is head.
	int					keyvalueindex;		// used for keyvalues, most for titans.

	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);
};

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; };
};

#pragma pack(push, 1)
struct studiohdr_t
{
	int id;                  // 'IDST'
	int version;             // R5 = '54'
	int checksum;
	int sznameindex;		// This has been moved from studiohdr2 to the front of the main header.

	char name[64];

	int length;              // size of data

	Vector3D eyeposition;     // ideal eye position
	Vector3D illumposition;   // illumination center
	Vector3D hull_min;        // ideal movement hull size
	Vector3D hull_max;
	Vector3D view_bbmin;      // clipping bounding box
	Vector3D view_bbmax;

	int flags;

	int numbones;            // bones
	int boneindex;

	int numbonecontrollers;
	int bonecontrollerindex;

	int numhitboxsets;
	int hitboxsetindex;

	int numlocalanim;        // animations/poses
	int localanimindex;      // animation descriptions

	int numlocalseq;         // sequences
	int localseqindex;

	int activitylistversion; // initialization flag - have the sequences been indexed ?

	int materialtypesindex; // index into an array of byte sized material type enums for each material used by the model
	int numtextures;
	int textureindex;

	int numcdtextures;
	int cdtextureindex;

	int numskinref;      // Total number of references (submeshes)
	int numskinfamilies; // Total skins per reference
	int skinindex;       // Offset to data

	int numbodyparts;
	int bodypartindex;

	int numlocalattachments;
	int localattachmentindex;

	int numlocalnodes;
	int localnodeindex;
	int localnodenameindex;

	int numunknodes; // ???
	int nodedataindexindex; // index into an array of int sized offsets that read into the data for each node

	int meshindex; // offset to model meshes

	// all flex related model vars and structs are stripped in respawn source
	int deprecated_numflexcontrollers;
	int deprecated_flexcontrollerindex;

	int deprecated_numflexrules;
	int deprecated_flexruleindex;

	int numikchains;
	int ikchainindex;

	// mesh panels for using rui on models, primarily for weapons
	int numruimeshes;
	int ruimeshindex;

	int numlocalposeparameters;
	int localposeparamindex;

	int surfacepropindex;

	int keyvalueindex;
	int keyvaluesize;

	int numlocalikautoplaylocks;
	int localikautoplaylockindex;

	float mass;
	int contents;

	// unused for packed models
	// technically still functional though I am unsure why you'd want to use it
	int numincludemodels;
	int includemodelindex;

	int virtualModel; // size placeholder

	int bonetablebynameindex;

	// 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;

	float fadeDist; // fadeDistance
	float gatherSize; // gathersize

	int deprecated_numflexcontrollerui;
	int deprecated_flexcontrolleruiindex;

	float flVertAnimFixedPointScale; // to be verified
	int surfacepropLookup; // saved in the file

	int sourceFilenameOffset; // doesn't actually need to be written pretty sure, only four bytes when not present.

	int numsrcbonetransform;
	int srcbonetransformindex;

	int	illumpositionattachmentindex;

	int linearboneindex;

	// unsure what this is for but it exists for jigglbones
	int numprocbones;
	int procbonetableindex;
	int linearprocboneindex;

	// depreciated as they are removed in 12.1
	int m_nBoneFlexDriverCount;
	int m_nBoneFlexDriverIndex;

	int m_nPerTriAABBIndex;
	int m_nPerTriAABBNodeCount;
	int m_nPerTriAABBLeafCount;
	int m_nPerTriAABBVertCount;

	// always "" or "Titan"
	int unkstringindex;

	// this is now used for combined files in rpak, vtx, vvd, and vvc are all combined while vphy is separate.
	// the indexes are added to the offset in the rpak mdl_ header.
	// vphy isn't vphy, looks like a heavily modified vphy.
	int vtxindex; // VTX
	int vvdindex; // VVD / IDSV
	int vvcindex; // VVC / IDCV 
	int vphyindex; // VPHY / IVPS

	int vtxsize;
	int vvdsize;
	int vvcsize;
	int vphysize; // still used in models using vg

	// unused in apex, gets cut in 12.1
	int unkmemberindex1;
	int numunkmember1;

	// only seen on '_animated' suffixed models so far
	int unkcount3;
	int unkindex3;

	// BVH4 size (?)
	Vector3D mins;
	Vector3D maxs; // seem to be the same as hull size

	int unk3_v54[3];

	int bvh4index; // bvh4 tree

	short unk4_v54[2]; // same as unk3_v54_v121, 2nd might be base for other offsets?

	// new in apex vertex weight file for verts that have more than three weights
	int vvwindex; // index will come last after other vertex files
	int vvwsize;


	// Look up hitbox set by index
	mstudiohitboxset_t* GetHitboxSet(int i) const
	{
		Assert(i >= 0 && i < numhitboxsets);
		return (mstudiohitboxset_t*)(((byte*)this) + hitboxsetindex) + i;
	};
};

#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_BY_IKCHAIN 0x20 // bone is influenced by IK chains, added in V52 (Titanfall 1)

#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

// no longer used in apex from what I can tell
//#define BONE_USED_BY_BONE_MERGE		0x00040000	// bone is available for bone merge to occur against it

#define BONE_FLAG_UNK          0x00080000 // respawn flag

#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 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
// v48 -> v49 : BONE_HAS_SAVEFRAME_ROT -> BONE_HAS_SAVEFRAME_ROT64
#define BONE_HAS_SAVEFRAME_ROT64	0x00400000	// Quaternion64
#define BONE_HAS_SAVEFRAME_ROT32	0x00800000	// Quaternion32

struct mstudiobone_t
{
	int sznameindex;

	int parent; // parent bone
	int bonecontroller[6]; // bone controller index, -1 == none

	// default values
	Vector3D pos;
	Quaternion quat;
	RadianEuler rot;
	Vector3D scale; // bone scale(?)

	matrix3x4_t poseToBone;
	Quaternion qAlignment;

	int flags;
	int proctype;
	int procindex; // procedural rule
	int physicsbone; // index into physically simulated bone

	int surfacepropidx; // index into string tablefor property name

	int contents; // See BSPFlags.h for the contents flags

	int surfacepropLookup; // written on compile in v54

	int unk;

	int unkid; // physics index (?)
};

struct mstudiojigglebone_t
{
	byte flags; // looks to be.

	unsigned char bone; // id of bone, might be single byte

	short pad; // possibly unused

	// general params
	float length; // how far from bone base, along bone, is tip
	float tipMass;

	float unkfloat; // v54 adds an extra value here but otherwise the same
					// observed values are between 0-1

	// 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;
};

struct mstudioattachment_t
{
	int sznameindex;
	int flags;

	int localbone; // parent bone

	matrix3x4_t			localmatrix; // attachment point
};

// 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
#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 on server
#define STUDIO_WORLD	0x4000		// sequence blends in worldspace
#define STUDIO_NOFORCELOOP 0x8000	// do not force the animation loop
#define STUDIO_EVENT_CLIENT 0x10000	// Has been updated at runtime to event index on client

// new in respawn models
// PREVIOUSLY: STUDIO_FRAMEANIM
#define STUDIO_ANIM_UNK3    0x0040  // used on something that is clearly not a frame anim in v54
									// animseq/humans/class/heavy/mp_pilot_heavy_core/mp_pt_crypto_base_execution_heavy_victim.rseq
#define STUDIO_ANIM_UNK		0x20000
#define STUDIO_ANIM_UNK1	0x40000
#define STUDIO_ANIM_UNK2	0x80000 // cherry blossom v53, levi in v54

struct mstudioanimdesc_t
{
	int baseptr;

	int sznameindex;

	float fps; // frames per second	
	int flags; // looping/non-looping flags

	int numframes;

	// piecewise movement
	int nummovements;
	int movementindex;

	int framemovementindex; // new in v52

	int animindex; // non-zero when anim data isn't in sections

	int numikrules;
	int ikruleindex; // non-zero when IK data is stored in the mdl

	int sectionindex;
	int sectionframes; // number of frames used in each fast lookup section, zero if not used
};

struct mstudioanimsections_t
{
	int animindex;
};

// flags for the per bone array, in 4 bit sections (two sets of flags per byte)
#define STUDIO_ANIM_POS		0x1 // animation has pos values
#define STUDIO_ANIM_ROT		0x2	// animation has rot values
#define STUDIO_ANIM_SCALE	0x4	// animation has scale values

// the actual animation values
union mstudioanimvalue_t
{
	struct
	{
		byte	valid; // number of valid frames, or how many frames of data this value has
		byte	total; // total number of frames, aka "values"
	} num;
	short		value; // actual value, value*posscale
};

struct mstudioanim_valueptr_t
{
	char packedoffsets[4]; // packing unknown
};

// flags for the actually per bone animation headers
// "mstudioanim_valueptr_t" indicates it has a set of offsets into anim tracks
#define STUDIO_ANIM_DELTA		0x01 // unverified, animation is additive
#define STUDIO_ANIM_ANIMSCALE	0x01 // mstudioanim_valueptr_t
#define STUDIO_ANIM_ANIMROT		0x02 // mstudioanim_valueptr_t
#define STUDIO_ANIM_ANIMPOS		0x04 // mstudioanim_valueptr_t

struct mstudio_rle_anim_t_v54
{

	short size : 13; // total size of all animation data, not nextoffset because even the last one has it

	short flags : 3;

	// if flag 'STUDIO_ANIM_ANIMPOS' then these two
	float posscale;
	mstudioanim_valueptr_t pPosV;
	// else (static value, no actual animation)
	//Vector48 pPos; // type does not exist in R5SDK


	// if flag 'STUDIO_ANIM_ANIMROT' then
	mstudioanim_valueptr_t pRotV;
	// else (static value, no actual animation)
	//Quaternion64 pRot; // type does not exist in R5SDK

	// if flag 'STUDIO_ANIM_ANIMSCALE' then
	mstudioanim_valueptr_t pScaleV;
	// else (static value, no actual animation)
	//Vector48 pScale; // type does not exist in R5SDK
};

// apex ikerrors do the same thing as apex framemovements
struct mstudiocompressedikerror_t
{
	float scale[6]; // these values are the same as what posscale (if it was used) and rotscale are.
	int sectionframes; // frames per section, may not match animdesc
};

struct mstudioikrule_t
{
	int index;
	int type;
	int chain;
	int bone; // gets it from ikchain now pretty sure

	int slot; // iktarget slot. Usually same as chain.
	float height;
	float radius;
	float floor;
	Vector3D pos;
	Quaternion q;

	// apex does this oddly
	mstudiocompressedikerror_t compressedikerror;
	int compressedikerrorindex;

	int iStart;
	int ikerrorindex;

	float start; // beginning of influence
	float peak; // start of full influence
	float tail; // end of full influence
	float end; // end of all influence

	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 szattachmentindex; // name of world attachment

	float endHeight; // new in v52   
};

// basically compressedikerrors and frame movements will have an array of offsets that leads into the traditional 'short offset' array, allowing it to have per section (if the anim uses sections) data
struct mstudioframemovement_t
{
	float scale[4]; // first three values are the same as what posscale (if it was used) is, fourth is similar to unkvector1.
	int sectionframes; // frames per section, may not match animdesc
					   // may have more than one, even when not section anim
};

struct mstudioseqdesc_t
{
	int baseptr;

	int	szlabelindex;

	int szactivitynameindex;

	int flags; // looping/non-looping flags

	int activity; // initialized at loadtime to game DLL values
	int actweight;

	int numevents;
	int eventindex;

	Vector3D bbmin; // per sequence bounding box
	Vector3D bbmax;

	int numblends;

	// Index into array of shorts which is groupsize[0] x groupsize[1] in length
	int animindexindex;

	int movementindex; // [blend] float array for blended movement
	int groupsize[2];
	int paramindex[2]; // X, Y, Z, XR, YR, ZR
	float paramstart[2]; // local (0..1) starting value
	float paramend[2]; // local (0..1) ending value
	int paramparent;

	float fadeintime; // ideal cross fate in time (0.2 default)
	float fadeouttime; // ideal cross fade out time (0.2 default)

	int localentrynode; // transition node at entry
	int localexitnode; // transition node at exit
	int nodeflags; // transition rules

	float entryphase; // used to match entry gait
	float exitphase; // used to match exit gait

	float lastframe; // frame that should generation EndOfSequence

	int nextseq; // auto advancing sequences
	int pose; // index of delta animation between end and nextseq

	int numikrules;

	int numautolayers;
	int autolayerindex;

	int weightlistindex;

	int posekeyindex;

	int numiklocks;
	int iklockindex;

	// Key values
	int keyvalueindex;
	int keyvaluesize;

	int cycleposeindex; // index of pose parameter to use as cycle index

	int activitymodifierindex;
	int numactivitymodifiers;

	int ikResetMask; // new in v52
	int unk1;

	// offset unkindex amount into an array of unkcount 'unkseqdata_t'
	int unkindex;
	int unkcount;
};

struct mstudioautolayer_t
{
	// this needs to have a guid descriptor in rpak
	__int64 guidSequence; // hashed aseq guid asset

	short iSequence; // only used within an rmdl I would imagine
	short iPose;

	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
};

#define NEW_EVENT_STYLE ( 1 << 10 )

struct mstudioevent_t
{
	float cycle;
	int	event;
	int type; // this will be 0 if old style
	char options[256];

	int szeventindex;
};

// will align to four, so takes up eight bytes
struct mstudioactivitymodifier_t_v52
{
	int sznameindex;
	bool negate; // negate all other activity modifiers when this one is active?
};

struct unkseqdata_t
{
	// generally 0-1
	float unkfloat;

	int unk;

	// quaternion mayhaps
	float unkfloat1;
	float unkfloat2;
	float unkfloat3;
	float unkfloat4;
};

struct mstudiobodyparts_t
{
	int sznameindex;
	int nummodels;
	int base;
	int modelindex; // index into models array
};

struct mstudiomodel_t
{
	char name[64];

	int unkindex2; // goes to bones sometimes, string index

	int type;

	float boundingradius;

	int nummeshes;
	int meshindex;

	// cache purposes
	int numvertices; // number of unique vertices/normals/texcoords
	int vertexindex; // vertex Vector
	int tangentsindex; // tangents Vector

	int numattachments;
	int attachmentindex;

	int deprecated_numeyeballs;
	int deprecated_eyeballindex;

	int pad[4];

	int colorindex; // vertex color
					// offset by colorindex number of bytes into vvc vertex colors
	int uv2index; // vertex second uv map
				  // offset by uv2index number of bytes into vvc secondary uv map
};

struct mstudio_meshvertexloddata_t
{
	int modelvertexdataUnusedPad; // likely has none of the funny stuff because unused

	int numLODVertexes[MAX_NUM_LODS]; // depreciated starting with rmdl v14(?)
};

struct mstudiomesh_t
{
	int material;

	int modelindex;

	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)

	int deprecated_numflexes; // vertex animation
	int deprecated_flexindex;

	// special codes for material operations
	int deprecated_materialtype;
	int deprecated_materialparam;

	// a unique ordinal for this mesh
	int meshid;

	Vector3D center;

	mstudio_meshvertexloddata_t vertexloddata;

	char unk[8]; // these are suposed to be filled on load, however this isn't true??
};

struct mstudioposeparamdesc_t
{
	int sznameindex;

	int flags; // ????
	float start; // starting value
	float end; // ending value
	float loop;	// looping range, 0 for no looping, 360 for rotations, etc.
};

struct mstudioikchain_t
{
	int sznameindex;

	int linktype;
	int numlinks;
	int linkindex;

	float unk; // no clue what this does tbh, tweaking it does nothing
			   // default value: 0.707f
};

struct mstudioiklink_t
{
	int bone;
	Vector3D kneeDir; // no kneeDir in apex I think
};

struct mstudiotexturedir_t
{
	int sznameindex; // file name
};

struct mstudiotexture_t
{
	int sznameindex;

	__int64 guid; // guid of the material it references
};

struct mstudiorruiheader_t
{
	int unk; // unsure, it doesn't line up
	int ruimeshindex; // offset to the actual rui mesh
};

// vertex map for a face
struct mstudioruivertmap_t
{
	// order of vertices for triangles:
	// 1st tri: 1-3-2
	// 2nd tri: 4-2-3

	// sometimes these are for a triangle instead of a quad, if that's the case then:
	// tri: 1-4-2
	// in this case the vert map will only cover 3 verts.


	// in v53 the first two are an array like such:
	// 1-3
	// while the third is the fourth vert.
	short vertid[3];
};

struct mstudioruivert_t
{
	int parent; // relative to global mesh parent, assumed

	Vector3D vertexpos; // position of vertex relative to bone
};

struct mstudioruimeshface_t
{
	// this might be the same as the RPak UIMG UV struct.

	// these values are for the two vertices that are not shared
	// for the other do as such:
	// vertex 2: take x from vextex 1 and y from vextex 4
	// vertex 2: take x from vextex 4 and y from vextex 1

	// normal smd uv, seems to calculate for other values
	Vector2D faceuvmin; // vertex 1
	Vector2D faceuvmax; // vertex 4

	// these could likely be calculated by doing math with a height/width scale
	// scale of the ui element
	Vector2D facescalemin; // vertex 1
	Vector2D facescalemax; // vertex 4
};

struct mstudioruimesh_t
{
	short numparents; // apparently you can have meshes parented to more than one bone(?)
	short numvertices; // number of verts
	short numfaces; // number of faces (quads)

	short unk; // num uvs? or this is num faces and current num faces is num uvs

	int parentindex; // this gets padding out front of it to even off the struct

	int vertexindex; // offset into smd style vertex data
	// not actually fourth vert wtf??
	int fourthvertmapindex; // they now fully map the vertices for each quad
	int vertmapindex; // offsets into a vertex map for each quad
	int facedataindex; // offset into uv section
};

struct mstudiosrcbonetransform_t
{
	int sznameindex;

	matrix3x4_t	pretransform;
	matrix3x4_t	posttransform;
};

struct mstudiolinearbone_t
{
	// they cut pos and rot scale, understandable since posscale was never used it tf|2 and they do anims different in apex
	int numbones;

	int flagsindex;

	int	parentindex;

	int	posindex;

	int quatindex;

	int rotindex;

	int posetoboneindex;
};

struct BVH4Axis_t
{
	short minChild0;
	short maxChild0;

	short minChild1;
	short maxChild1;

	short minChild2;
	short maxChild2;

	short minChild3;
	short maxChild3;
};

struct BVH4Node_t
{
	BVH4Axis_t x;
	BVH4Axis_t y;
	BVH4Axis_t z;

	int pad : 8;
	int index0 : 24;

	int collision_mask : 8;
	int index1 : 24;

	int child0_type : 4;
	int child1_type : 4;
	int index2 : 24;

	int child2_type : 4;
	int child3_type : 4;
	int index3 : 24;
};

#pragma pack(pop)

// studio model data
struct studiomeshdata_t
{
	int m_NumGroup;
	void* m_pMeshGroup; // studiomeshgroup_t
};

struct studioloddata_t
{
	studiomeshdata_t* m_pMeshData;
	// !TODO:
};

struct studiohwdata_t
{
	int m_RootLOD; // calced and clamped, nonzero for lod culling
	int m_NumLODs;
	studioloddata_t* m_pLODs;
	int m_NumStudioMeshes;
	// !TODO:
};

class CStudioHdr
{
public: // Detour statics:
	static int LookupSequence(CStudioHdr* pStudio, const char* pszName);

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); };

public:
	inline const studiohdr_t* GetRenderHdr(void) const { return m_pStudioHdr; };
	inline mstudiohitboxset_t* GetHitboxSet(int i) const { return m_pStudioHdr->GetHitboxSet(i); };

private:
	void* vtbl;
	studiohdr_t* m_pStudioHdr;
	void* m_pVModel; // !TODO: 'virtualmodel_t'.
	char gap_18[156];
	mstudiobbox_t m_pHitBox;
	char gap_e0[1888];
	const char* m_pModelName;
	int m_nPerfAnimatedBones;
	int m_nPerfUsedBones;
	int m_nPerfAnimationLayers;
	void* m_pAnimData_Maybe;
	char gap_860[16];
	CMDLCache* m_pMDLCache;
};

class CStudioHWDataRef
{
public:
	bool IsDataRef(void) const { return true; }

	CStudioHWDataRef* m_pVTable;
	uint8_t pad0[0x8];
	studiohwdata_t m_HardwareData;
};

#endif // STUDIO_H
