// leave this as first line for PCH reasons...
//

 
#include "../client/client.h"	//FIXME!! EVIL - just include the definitions needed 


						  
#if !defined(TR_LOCAL_H)
	#include "tr_local.h"
#endif

#include "matcomp.h"
#if !defined(_QCOMMON_H_)
	#include "../qcommon/qcommon.h"
#endif
#if !defined(G2_H_INC)
	#include "../ghoul2/G2.h"
#endif
#include "../ghoul2/G2_local.h"
#include "matcomp.h"

#pragma warning (disable: 4512)	//default assignment operator could not be gened
#include "../qcommon/disablewarnings.h"

#define	LL(x) x=LittleLong(x)

extern cvar_t	*r_Ghoul2AnimSmooth;
extern cvar_t	*r_Ghoul2UnSqashAfterSmooth;

// I hate doing this, but this is the simplest way to get this into the routines it needs to be
mdxaBone_t		worldMatrix;
mdxaBone_t		worldMatrixInv;

class CConstructBoneList
{
public:
	int				surfaceNum;
	int				*boneUsedList;
	surfaceInfo_v	&rootSList;
	model_t			*currentModel;
	boneInfo_v		&boneList; 

	CConstructBoneList(
	int				initsurfaceNum,
	int				*initboneUsedList,
	surfaceInfo_v	&initrootSList,
	model_t			*initcurrentModel,
	boneInfo_v		&initboneList):

	surfaceNum(initsurfaceNum),
	boneUsedList(initboneUsedList),
	rootSList(initrootSList),
	currentModel(initcurrentModel),
	boneList(initboneList) { }

};

// optimised structures, so we aren't passing all these variables down the transform recursion pipe with each call.
class CTransformBone
{
public:
	int				newFrame;
	int				currentFrame;
	int				parent;
	int				child;
	int				frameSize;
	mdxaHeader_t	*header;
	float			backlerp;
	int				*usedBoneList;
	boneInfo_v		&rootBoneList;
	mdxaBone_v		&bonePtr;
	boltInfo_v		&boltList;
	mdxaBone_t		&rootMatrix;
	bool			rootBone;
	int				incomingTime;
	float			blendFrame;
	int				blendOldFrame;
	bool			blendMode;
	float			blendLerp;
	int				boltNum;
	vec3_t			newModelOrigin;

	CTransformBone(
	int				initnewFrame,
	int				initcurrentFrame,
	int				initparent,
	int				initchild,
	int				initframeSize,
	mdxaHeader_t	*initheader,
	float			initbacklerp,
	int				*initusedBoneList,
	boneInfo_v		&initrootBoneList,
	mdxaBone_v		&initbonePtr,
	boltInfo_v		&initboltList,
	mdxaBone_t		&initrootMatrix,
	bool			initrootBone,
	int				initincomingTime,
	float			initblendFrame,
	int				initblendOldFrame,
	bool			initblendMode,
	float			initblendLerp,
	int				initboltNum):

	newFrame(initnewFrame),
	currentFrame(initcurrentFrame),
	parent(initparent),
	child(initchild),
	frameSize(initframeSize),
	header(initheader),
	backlerp(initbacklerp),
	usedBoneList(initusedBoneList),
	rootBoneList(initrootBoneList),
	bonePtr(initbonePtr),
	boltList(initboltList),
	rootMatrix(initrootMatrix),
	rootBone(initrootBone),
	incomingTime(initincomingTime),
	blendFrame(initblendFrame),
	blendOldFrame(initblendOldFrame),
	blendMode(initblendMode),
	blendLerp(initblendLerp),
	boltNum(initboltNum)
	{
		VectorClear(newModelOrigin);
	}
};

class CRenderSurface
{
public:
	int				surfaceNum;
	surfaceInfo_v	&rootSList;
	shader_t		*cust_shader;
	int				fogNum;
	qboolean		personalModel;
	mdxaBone_v		&bonePtr;
	int				renderfx;
	skin_t			*skin;
	model_t			*currentModel;
	int				lod;
	boltInfo_v		&boltList;

	CRenderSurface(
	int				initsurfaceNum,
	surfaceInfo_v	&initrootSList,
	shader_t		*initcust_shader,
	int				initfogNum,
	qboolean		initpersonalModel,
	mdxaBone_v		&initbonePtr,
	int				initrenderfx,
	skin_t			*initskin,
	model_t			*initcurrentModel,
	int				initlod,
	boltInfo_v		&initboltList):

	surfaceNum(initsurfaceNum),
	rootSList(initrootSList),
	cust_shader(initcust_shader),
	fogNum(initfogNum),
	personalModel(initpersonalModel),
	bonePtr(initbonePtr),
	renderfx(initrenderfx),
	skin(initskin),
	currentModel(initcurrentModel),
	lod(initlod),
	boltList(initboltList)
	{}
};

/*

All bones should be an identity orientation to display the mesh exactly
as it is specified.

For all other frames, the bones represent the transformation from the 
orientation of the bone in the base frame to the orientation in this
frame.

*/


/*
=============
R_ACullModel
=============
*/
static int R_GCullModel( trRefEntity_t *ent ) {

	// scale the radius if need be
	float largestScale = ent->e.modelScale[0];

	if (ent->e.modelScale[1] > largestScale)
	{
		largestScale = ent->e.modelScale[1];
	}
	if (ent->e.modelScale[2] > largestScale)
	{
		largestScale = ent->e.modelScale[2];
	}
	if (!largestScale)
	{
		largestScale = 1;
	}

	// cull bounding sphere 
  	switch ( R_CullLocalPointAndRadius( NULL,  ent->e.radius * largestScale) )
  	{
  	case CULL_OUT:
  		tr.pc.c_sphere_cull_md3_out++;
  		return CULL_OUT;

	case CULL_IN:
		tr.pc.c_sphere_cull_md3_in++;
		return CULL_IN;

	case CULL_CLIP:
		tr.pc.c_sphere_cull_md3_clip++;
		return CULL_IN;
 	}
	return CULL_IN;
}


/*
=================
R_AComputeFogNum

=================
*/
static int R_GComputeFogNum( trRefEntity_t *ent ) {

	int				i, j;
	fog_t			*fog;

	if ( tr.refdef.rdflags & RDF_NOWORLDMODEL ) {
		return 0;
	}

	for ( i = 1 ; i < tr.world->numfogs ; i++ ) {
		fog = &tr.world->fogs[i];
		for ( j = 0 ; j < 3 ; j++ ) {
			if ( ent->e.origin[j] - ent->e.radius >= fog->bounds[1][j] ) {
				break;
			}
			if ( ent->e.origin[j] + ent->e.radius <= fog->bounds[0][j] ) {
				break;
			}
		}
		if ( j == 3 ) {
			return i;
		}
	}

	return 0;
}

// work out lod for this entity.
static int G2_ComputeLOD( trRefEntity_t *ent, const model_t *currentModel, int lodBias ) 
{
	float flod, lodscale;
	float projectedRadius;
	int lod;

	if ( currentModel->numLods < 2 )
	{	// model has only 1 LOD level, skip computations and bias
		return(0);
	}

	if ( r_lodbias->integer > lodBias ) 
	{
		lodBias = r_lodbias->integer;
	}

	// scale the radius if need be
	float largestScale = ent->e.modelScale[0];

	if (ent->e.modelScale[1] > largestScale)
	{
		largestScale = ent->e.modelScale[1];
	}
	if (ent->e.modelScale[2] > largestScale)
	{
		largestScale = ent->e.modelScale[2];
	}
	if (!largestScale)
	{
		largestScale = 1;
	}

	if ( ( projectedRadius = ProjectRadius( 0.75*largestScale*ent->e.radius, ent->e.origin ) ) != 0 )	//we reduce the radius to make the LOD match other model types which use the actual bound box size
 	{
 		lodscale = (r_lodscale->value+r_autolodscalevalue->value);
 		if ( lodscale > 20 ) 
		{
			lodscale = 20;	 
		}
		else if ( lodscale < 0 )
		{
			lodscale = 0;
		}
 		flod = 1.0f - projectedRadius * lodscale;
 	}
 	else
 	{
 		// object intersects near view plane, e.g. view weapon
 		flod = 0;
 	}
#ifdef DEDICATED
#define myftol(x) ((int)(x))
#endif
 	flod *= currentModel->numLods;
 	lod = myftol( flod );

 	if ( lod < 0 )
 	{
 		lod = 0;
 	}
 	else if ( lod >= currentModel->numLods )
 	{
 		lod = currentModel->numLods - 1;
 	}


	lod += lodBias;
	
	if ( lod >= currentModel->numLods )
		lod = currentModel->numLods - 1;
	if ( lod < 0 )
		lod = 0;

	return lod;
}

//======================================================================
//
// Bone Manipulation code


void G2_CreateQuaterion(mdxaBone_t *mat, vec4_t quat)
{
	// this is revised for the 3x4 matrix we use in G2.
    float t = 1 + mat->matrix[0][0] + mat->matrix[1][1] + mat->matrix[2][2];
	float s;

    //If the trace of the matrix is greater than zero, then
    //perform an "instant" calculation.
    //Important note wrt. rouning errors:
    //Test if ( T > 0.00000001 ) to avoid large distortions!
	if (t > 0.00000001)
	{
      s = sqrt(t) * 2;
      quat[0] = ( mat->matrix[1][2] - mat->matrix[2][1] ) / s;
      quat[1] = ( mat->matrix[2][0] - mat->matrix[0][2] ) / s;
      quat[2] = ( mat->matrix[0][1] - mat->matrix[1][0] ) / s;
      quat[3] = 0.25 * s;
	}
	else
	{
		//If the trace of the matrix is equal to zero then identify
		//which major diagonal element has the greatest value.

		//Depending on this, calculate the following:

		if ( mat->matrix[0][0] > mat->matrix[1][1] && mat->matrix[0][0] > mat->matrix[2][2] )  {	// Column 0: 
			s  = sqrt( 1.0 + mat->matrix[0][0] - mat->matrix[1][1] - mat->matrix[2][2])* 2;
			quat[0] = 0.25 * s;
			quat[1] = (mat->matrix[0][1] + mat->matrix[1][0] ) / s;
			quat[2] = (mat->matrix[2][0] + mat->matrix[0][2] ) / s;
			quat[3] = (mat->matrix[1][2] - mat->matrix[2][1] ) / s;
		
		} else if ( mat->matrix[1][1] > mat->matrix[2][2] ) {			// Column 1: 
			s  = sqrt( 1.0 + mat->matrix[1][1] - mat->matrix[0][0] - mat->matrix[2][2] ) * 2;
			quat[0] = (mat->matrix[0][1] + mat->matrix[1][0] ) / s;
			quat[1] = 0.25 * s;
			quat[2] = (mat->matrix[1][2] + mat->matrix[2][1] ) / s;
			quat[3] = (mat->matrix[2][0] - mat->matrix[0][2] ) / s;

		} else {						// Column 2:
			s  = sqrt( 1.0 + mat->matrix[2][2] - mat->matrix[0][0] - mat->matrix[1][1] ) * 2;
			quat[0] = (mat->matrix[2][0]+ mat->matrix[0][2] ) / s;
			quat[1] = (mat->matrix[1][2] + mat->matrix[2][1] ) / s;
			quat[2] = 0.25 * s;
			quat[3] = (mat->matrix[0][1] - mat->matrix[1][0] ) / s;
		}
	}
}

void G2_CreateMatrixFromQuaterion(mdxaBone_t *mat, vec4_t quat)
{
	
    float xx      = quat[0] * quat[0];
    float xy      = quat[0] * quat[1];
    float xz      = quat[0] * quat[2];
    float xw      = quat[0] * quat[3];

    float yy      = quat[1] * quat[1];
    float yz      = quat[1] * quat[2];
    float yw      = quat[1] * quat[3];

    float zz      = quat[2] * quat[2];
    float zw      = quat[2] * quat[3];

    mat->matrix[0][0]  = 1 - 2 * ( yy + zz );
    mat->matrix[1][0]  =     2 * ( xy - zw );
    mat->matrix[2][0]  =     2 * ( xz + yw );

    mat->matrix[0][1]  =     2 * ( xy + zw );
    mat->matrix[1][1]  = 1 - 2 * ( xx + zz );
    mat->matrix[2][1]  =     2 * ( yz - xw );

    mat->matrix[0][2]  =     2 * ( xz - yw );
    mat->matrix[1][2]  =     2 * ( yz + xw );
    mat->matrix[2][2]  = 1 - 2 * ( xx + yy );

    mat->matrix[0][3]  = mat->matrix[1][3] = mat->matrix[2][3] = 0;
}

// nasty little matrix multiply going on here..
void Multiply_3x4Matrix(mdxaBone_t *out, mdxaBone_t *in2, mdxaBone_t *in) 
{
	// first row of out                                                                                      
	out->matrix[0][0] = (in2->matrix[0][0] * in->matrix[0][0]) + (in2->matrix[0][1] * in->matrix[1][0]) + (in2->matrix[0][2] * in->matrix[2][0]);
	out->matrix[0][1] = (in2->matrix[0][0] * in->matrix[0][1]) + (in2->matrix[0][1] * in->matrix[1][1]) + (in2->matrix[0][2] * in->matrix[2][1]);
	out->matrix[0][2] = (in2->matrix[0][0] * in->matrix[0][2]) + (in2->matrix[0][1] * in->matrix[1][2]) + (in2->matrix[0][2] * in->matrix[2][2]);
	out->matrix[0][3] = (in2->matrix[0][0] * in->matrix[0][3]) + (in2->matrix[0][1] * in->matrix[1][3]) + (in2->matrix[0][2] * in->matrix[2][3]) + in2->matrix[0][3];
	// second row of outf out                                                                                     
	out->matrix[1][0] = (in2->matrix[1][0] * in->matrix[0][0]) + (in2->matrix[1][1] * in->matrix[1][0]) + (in2->matrix[1][2] * in->matrix[2][0]);
	out->matrix[1][1] = (in2->matrix[1][0] * in->matrix[0][1]) + (in2->matrix[1][1] * in->matrix[1][1]) + (in2->matrix[1][2] * in->matrix[2][1]);
	out->matrix[1][2] = (in2->matrix[1][0] * in->matrix[0][2]) + (in2->matrix[1][1] * in->matrix[1][2]) + (in2->matrix[1][2] * in->matrix[2][2]);
	out->matrix[1][3] = (in2->matrix[1][0] * in->matrix[0][3]) + (in2->matrix[1][1] * in->matrix[1][3]) + (in2->matrix[1][2] * in->matrix[2][3]) + in2->matrix[1][3];
	// third row of out  out                                                                                      
	out->matrix[2][0] = (in2->matrix[2][0] * in->matrix[0][0]) + (in2->matrix[2][1] * in->matrix[1][0]) + (in2->matrix[2][2] * in->matrix[2][0]);
	out->matrix[2][1] = (in2->matrix[2][0] * in->matrix[0][1]) + (in2->matrix[2][1] * in->matrix[1][1]) + (in2->matrix[2][2] * in->matrix[2][1]);
	out->matrix[2][2] = (in2->matrix[2][0] * in->matrix[0][2]) + (in2->matrix[2][1] * in->matrix[1][2]) + (in2->matrix[2][2] * in->matrix[2][2]);
	out->matrix[2][3] = (in2->matrix[2][0] * in->matrix[0][3]) + (in2->matrix[2][1] * in->matrix[1][3]) + (in2->matrix[2][2] * in->matrix[2][3]) + in2->matrix[2][3]; 
}


static int G2_GetBonePoolIndex(	const mdxaHeader_t *pMDXAHeader, int iFrame, int iBone)
{
	const int iOffsetToIndex = (iFrame * pMDXAHeader->numBones * 3) + (iBone * 3);

	mdxaIndex_t *pIndex = (mdxaIndex_t *) ((byte*) pMDXAHeader + pMDXAHeader->ofsFrames + iOffsetToIndex);

	return pIndex->iIndex & 0x00FFFFFF;	// this will cause problems for big-endian machines... ;-)
}


/*static inline*/ void UnCompressBone(float mat[3][4], int iBoneIndex, const mdxaHeader_t *pMDXAHeader, int iFrame)
{
	mdxaCompQuatBone_t *pCompBonePool = (mdxaCompQuatBone_t *) ((byte *)pMDXAHeader + pMDXAHeader->ofsCompBonePool);
	MC_UnCompressQuat(mat, pCompBonePool[ G2_GetBonePoolIndex( pMDXAHeader, iFrame, iBoneIndex ) ].Comp);
}

#define DEBUG_G2_TIMING (0)

// transform each individual bone's information - making sure to use any override information provided, both for angles and for animations, as
// well as multiplying each bone's matrix by it's parents matrix 
void G2_TransformBone (CTransformBone &TB)
{
	mdxaBone_t		tbone[6];
	mdxaSkel_t		*skel;
	mdxaSkelOffsets_t *offsets;
	boneInfo_v		&boneList = TB.rootBoneList;
	int				i, j, boneListIndex;
	int				angleOverride = 0;

#if DEBUG_G2_TIMING
	bool printTiming=false;
#endif

	// decide here if we should go down this path? - is this bone used? -If not, return from this function. Due the hierarchial nature of the bones
	// any bone below this one in the tree shouldn't be used either.
	if (!TB.usedBoneList[TB.child])
	{
		return;
	}

	// should this bone be overridden by a bone in the bone list?
	boneListIndex = G2_Find_Bone_In_List(boneList, TB.child);
	if (boneListIndex != -1)
	{
		// we found a bone in the list - we need to override something here.
		
		// do we override the rotational angles?
		if ((boneList[boneListIndex].flags) & (BONE_ANGLES_TOTAL))
		{
			angleOverride = (boneList[boneListIndex].flags) & (BONE_ANGLES_TOTAL);
		}

		if (boneList[boneListIndex].flags & BONE_ANIM_BLEND)
		{
			float blendTime = TB.incomingTime - boneList[boneListIndex].blendStart;
			// only set up the blend anim if we actually have some blend time left on this bone anim - otherwise we might corrupt some blend higher up the hiearchy
			if (blendTime>=0.0f&&blendTime < boneList[boneListIndex].blendTime)
			{
				TB.blendFrame = boneList[boneListIndex].blendFrame;
				TB.blendOldFrame = boneList[boneListIndex].blendLerpFrame;
				TB.blendLerp = (blendTime / boneList[boneListIndex].blendTime);
				TB.blendMode = true;
			}
			else
			{
				TB.blendMode = false;
			}
		}
		else if ((boneList[boneListIndex].flags) & (BONE_ANIM_OVERRIDE_LOOP | BONE_ANIM_OVERRIDE))
		{
			TB.blendMode = false;
		}

		if ((boneList[boneListIndex].flags) & (BONE_ANIM_OVERRIDE_LOOP | BONE_ANIM_OVERRIDE))
		{
			float	animSpeed = boneList[boneListIndex].animSpeed;
			float	time;
			if (boneList[boneListIndex].pauseTime)
			{
				time = (boneList[boneListIndex].pauseTime - boneList[boneListIndex].startTime) / 50.0f;
			}
			else
			{			
				time = (TB.incomingTime - boneList[boneListIndex].startTime) / 50.0f;
			}
			if (time<0)
			{
				time=0;
			}
			float	newFrame_g = boneList[boneListIndex].startFrame + (time * animSpeed);

			float	endFrame = (float)boneList[boneListIndex].endFrame ;

			int		animSize = endFrame - boneList[boneListIndex].startFrame;
			// we are supposed to be animating right?
			if (animSize)
			{
				// did we run off the end?
				if (((animSpeed > 0.0f) && (newFrame_g > endFrame - 1)) || 
					((animSpeed < 0.0f) && (newFrame_g < endFrame + 1)))
				{
					// yep - decide what to do
					if (boneList[boneListIndex].flags & BONE_ANIM_OVERRIDE_LOOP)
					{
						// get our new animation frame back within the bounds of the animation set
						if (animSpeed < 0.0f)
						{
							if ((newFrame_g < endFrame+1) && (newFrame_g >= endFrame))
							{
								// now figure out what we are lerping between
								// delta is the fraction between this frame and the next, since the new anim is always at a .0f;
								TB.backlerp = (float(endFrame+1)-newFrame_g);
								// frames are easy to calculate
								TB.currentFrame = endFrame;
								TB.newFrame = boneList[boneListIndex].startFrame;
							}
							else
							{
								if (newFrame_g <= endFrame+1)
								{
									newFrame_g=endFrame+fmod(newFrame_g-endFrame,animSize)-animSize;
								}
								// now figure out what we are lerping between
								// delta is the fraction between this frame and the next, since the new anim is always at a .0f;
								TB.backlerp = (ceil(newFrame_g)-newFrame_g);
								// frames are easy to calculate
								TB.currentFrame = ceil(newFrame_g);
								// should we be creating a virtual frame?
								if (newFrame_g <= endFrame + 1)
								{

									TB.newFrame = boneList[boneListIndex].startFrame;
								}
								else
								{
									TB.newFrame = TB.currentFrame - 1;
								}
							}
						}
						else
						{
							// should we be creating a virtual frame?
							if ((newFrame_g > endFrame - 1) && (newFrame_g < endFrame))
							{
								// now figure out what we are lerping between
								// delta is the fraction between this frame and the next, since the new anim is always at a .0f;
								TB.backlerp = (newFrame_g - (int)newFrame_g);
								// frames are easy to calculate
								TB.currentFrame = (int)newFrame_g;
								TB.newFrame = boneList[boneListIndex].startFrame;
							}
							else
							{
								if (newFrame_g >= endFrame)
								{
									newFrame_g=endFrame+fmod(newFrame_g-endFrame,animSize)-animSize;
								}
								// now figure out what we are lerping between
								// delta is the fraction between this frame and the next, since the new anim is always at a .0f;
								TB.backlerp = (newFrame_g - (int)newFrame_g);
								// frames are easy to calculate
								TB.currentFrame = (int)newFrame_g;
								// should we be creating a virtual frame?
								if (newFrame_g >= endFrame - 1)
								{
									TB.newFrame = boneList[boneListIndex].startFrame;
								}
								else
								{
									TB.newFrame = TB.currentFrame + 1;
								}
							}
						}
						// sanity check
						assert ((TB.newFrame < endFrame) && (TB.newFrame >= boneList[boneListIndex].startFrame) || (animSize < 10)); 
					}
					else
					{
						if ((boneList[boneListIndex].flags & BONE_ANIM_OVERRIDE_FREEZE) == BONE_ANIM_OVERRIDE_FREEZE)
						{
							// if we are supposed to reset the default anim, then do so
							if (animSpeed > 0.0f)
							{
								TB.currentFrame = boneList[boneListIndex].endFrame - 1;
							}
							else
							{
								TB.currentFrame = boneList[boneListIndex].endFrame + 1;
							}

							TB.newFrame = TB.currentFrame;
							TB.backlerp = 0;
						}

						// nope, just stop processing this bone. And do nothing	- let the bone take the parents anim info
			   		}
				}
				else //
				{
					if (animSpeed> 0.0)
					{

						TB.currentFrame = (int)newFrame_g;

						// figure out the difference between the two frames	- we have to decide what frame and what percentage of that
						// frame we want to display
						TB.backlerp = (newFrame_g - TB.currentFrame);

						TB.newFrame = TB.currentFrame + 1;
						if (TB.newFrame >= (int)endFrame)
						{
							// we only want to lerp with the first frame of the anim if we are looping 
							if (boneList[boneListIndex].flags & BONE_ANIM_OVERRIDE_LOOP)
							{
					  			TB.newFrame = boneList[boneListIndex].startFrame;
							}
							// if we intend to end this anim or freeze after this, then just keep on the last frame
							else
							{
								TB.newFrame = boneList[boneListIndex].endFrame;
							}
						}
					}
					else
					{
						TB.backlerp = (ceil(newFrame_g)-newFrame_g);
						TB.currentFrame = ceil(newFrame_g);
						if (TB.currentFrame>boneList[boneListIndex].startFrame)
						{
							TB.currentFrame=boneList[boneListIndex].startFrame;
							TB.newFrame = TB.currentFrame;
							TB.backlerp=0.0f;
						}
						else
						{
							TB.newFrame=TB.currentFrame-1;
							// are we now on the end frame?
							if (TB.newFrame < endFrame+1)
							{
								if (boneList[boneListIndex].flags & BONE_ANIM_OVERRIDE_LOOP)
								{
						  			TB.newFrame = boneList[boneListIndex].startFrame;
								}
								else
								{
									TB.newFrame = boneList[boneListIndex].endFrame+1;
								}
							}
						}
					}
				}
			}
			else
			{
				if (animSpeed<0.0)
				{
					TB.currentFrame = boneList[boneListIndex].endFrame+1;
				}
				else
				{
					TB.currentFrame = boneList[boneListIndex].endFrame-1;
				}
				if (TB.currentFrame<0)
				{
					TB.currentFrame=0;
				}
				TB.newFrame = TB.currentFrame;
				TB.backlerp = 0;

			}
#if DEBUG_G2_TIMING
			printTiming=true;
#endif
		}	
	}

	//If either of these things happen the code below will choke to death miserably. Or so I gather.
	//Seems to only happen when the server lags badly and cuts off updates to the client state, perhaps
	//it doesn't like extremely long intervals between updating the animation state?
	if (TB.currentFrame < 0 || TB.newFrame < 0)
	{
		return;
	}	
	else if (TB.blendMode && (TB.blendFrame < 0 || TB.blendOldFrame < 0))
	{
		return;
	}
#if DEBUG_G2_TIMING
	if (printTiming&&boneListIndex==2)
	{
		char mess[1000];
		if (TB.blendMode)
		{
			sprintf(mess,"frb[%2d] %5d   %5d %5d %5d %5d  %f %f\n",boneListIndex,TB.incomingTime,(int)TB.newFrame,(int)TB.currentFrame,(int)TB.blendFrame,(int)TB.blendOldFrame,TB.backlerp,TB.blendLerp);
		}
		else
		{
			sprintf(mess,"fra[%2d] %5d   %5d %5d              %f\n",boneListIndex,TB.incomingTime,TB.newFrame,TB.currentFrame,TB.backlerp);
		}
		OutputDebugString(mess);
		const boneInfo_t &bone=boneList[boneListIndex];
		if (bone.flags&BONE_ANIM_BLEND)
		{
			sprintf(mess,"                                                                    bfb[%2d] %5d  %5d  (%5d-%5d) %4.2f %4x   bt(%5d-%5d) %7.2f %5d\n",
				boneListIndex,
				TB.incomingTime,
				bone.startTime,
				bone.startFrame,
				bone.endFrame,
				bone.animSpeed,
				bone.flags,
				bone.blendStart,
				bone.blendStart+bone.blendTime,
				bone.blendFrame,
				bone.blendLerpFrame
				);
		}
		else
		{
			sprintf(mess,"                                                                    bfa[%2d] %5d  %5d  (%5d-%5d) %4.2f %4x\n",
				boneListIndex,
				TB.incomingTime,
				bone.startTime,
				bone.startFrame,
				bone.endFrame,
				bone.animSpeed,
				bone.flags
				);
		}
		OutputDebugString(mess);
	}
#endif

//	const mdxaCompBone_t	*compBonePointer = (mdxaCompBone_t *)((byte *)TB.header + TB.header->ofsCompBonePool);

	// are we blending with another frame of anim?
	if (TB.blendMode)
	{
		const float backlerp = TB.blendFrame - (int)TB.blendFrame;
		const float frontlerp = 1.0 - backlerp;

		// figure out where the location of the blended animation data is
//	 	const mdxaFrame_t	*bFrame =	(mdxaFrame_t *)((byte *)TB.header + TB.header->ofsFrames + (int)TB.blendFrame * TB.frameSize );
//		const mdxaFrame_t	*boldFrame =(mdxaFrame_t *)((byte *)TB.header + TB.header->ofsFrames + TB.blendOldFrame * TB.frameSize );

//  		MC_UnCompress(tbone[3].matrix,compBonePointer[bFrame->boneIndexes[TB.child]].Comp);
//  		MC_UnCompress(tbone[4].matrix,compBonePointer[boldFrame->boneIndexes[TB.child]].Comp);
			UnCompressBone(tbone[3].matrix,TB.child, TB.header, TB.blendFrame);
			UnCompressBone(tbone[4].matrix,TB.child, TB.header, TB.blendOldFrame);
				
		for ( j = 0 ; j < 12 ; j++ ) 
		{
  			((float *)&tbone[5])[j] = (backlerp * ((float *)&tbone[3])[j])
				+ (frontlerp * ((float *)&tbone[4])[j]);
		}
	}

	// figure out where the location of the bone animation data is
	assert (TB.header->numFrames > TB.currentFrame);//validate the frame we're about to grab
//	const mdxaFrame_t	*aoldFrame = (mdxaFrame_t *)((byte *)TB.header + TB.header->ofsFrames + TB.currentFrame * TB.frameSize );

	// figure out where the bone hirearchy info is
	offsets = (mdxaSkelOffsets_t *)((byte *)TB.header + sizeof(mdxaHeader_t));
	skel = (mdxaSkel_t *)((byte *)TB.header + sizeof(mdxaHeader_t) + offsets->offsets[TB.child]);
  	//
  	// lerp this bone - use the temp space on the ref entity to put the bone transforms into
  	//
  	if (!TB.backlerp)
  	{
// 		MC_UnCompress(tbone[2].matrix,compBonePointer[aoldFrame->boneIndexes[TB.child]].Comp);
		UnCompressBone(tbone[2].matrix,TB.child, TB.header, TB.currentFrame);

		// blend in the other frame if we need to
		if (TB.blendMode)
		{
		float blendFrontlerp = 1.0 - TB.blendLerp;
	  		for ( j = 0 ; j < 12 ; j++ ) 
			{
  				((float *)&tbone[2])[j] = (TB.blendLerp * ((float *)&tbone[2])[j])
					+ (blendFrontlerp * ((float *)&tbone[5])[j]);
			}
		}

  		if (TB.rootBone)
		{
			// now multiply by the root matrix, so we can offset this model should we need to
			Multiply_3x4Matrix(&TB.bonePtr[TB.child].second, &TB.rootMatrix, &tbone[2]);
 		}
  	}
	else
  	{
		const float frontlerp = 1.0 - TB.backlerp;
		// figure out where the location of the bone animation data is
		assert (TB.header->numFrames > TB.newFrame);//validate the frame we're about to grab
//	 	const mdxaFrame_t	*aFrame = (mdxaFrame_t *)((byte *)TB.header + TB.header->ofsFrames + TB.newFrame * TB.frameSize );

// 		MC_UnCompress(tbone[0].matrix,compBonePointer[aFrame->boneIndexes[TB.child]].Comp);
//		MC_UnCompress(tbone[1].matrix,compBonePointer[aoldFrame->boneIndexes[TB.child]].Comp);
		UnCompressBone(tbone[0].matrix,TB.child, TB.header, TB.newFrame);
		UnCompressBone(tbone[1].matrix,TB.child, TB.header, TB.currentFrame);		

		for ( j = 0 ; j < 12 ; j++ ) 
		{
  			((float *)&tbone[2])[j] = (TB.backlerp * ((float *)&tbone[0])[j])
				+ (frontlerp * ((float *)&tbone[1])[j]);
		}

		// blend in the other frame if we need to
		if (TB.blendMode)
		{
			const float blendFrontlerp = 1.0 - TB.blendLerp;
	  		for ( j = 0 ; j < 12 ; j++ ) 
			{
				((float *)&tbone[2])[j] = (TB.blendLerp * ((float *)&tbone[2])[j])
					+ (blendFrontlerp * ((float *)&tbone[5])[j]);
			}
		}

  		if (TB.rootBone)
  		{
			// now multiply by the root matrix, so we can offset this model should we need to
			Multiply_3x4Matrix(&TB.bonePtr[TB.child].second, &TB.rootMatrix, &tbone[2]);
  		}
	}


	if (angleOverride & BONE_ANGLES_REPLACE)
	{
		mdxaBone_t temp, firstPass;
		mdxaBone_t &bone = TB.bonePtr[TB.child].second;
		boneInfo_t &boneOverride = boneList[boneListIndex];
		// give us the matrix the animation thinks we should have, so we can get the correct X&Y coors
		Multiply_3x4Matrix(&firstPass, &TB.bonePtr[TB.parent].second, &tbone[2]);

		// are we attempting to blend with the base animation? and still within blend time?
		if (boneOverride.boneBlendTime && (((boneOverride.boneBlendTime + boneOverride.boneBlendStart) < TB.incomingTime)))
		{
			// ok, we are supposed to be blending. Work out lerp
			const float blendTime = TB.incomingTime - boneList[boneListIndex].boneBlendStart;
			float blendLerp = (blendTime / boneList[boneListIndex].boneBlendTime);
			if (blendLerp <= 1)
			{
				if (blendLerp < 0)
				{
					assert(0);
				}

				// now work out the matrix we want to get *to* - firstPass is where we are coming *from*
				Multiply_3x4Matrix(&temp, &firstPass, &skel->BasePoseMat);

				const float	matrixScale = VectorLength((float*)&temp);
				
				for (int i=0; i<3;i++)
				{
					for(int x=0;x<3; x++)
					{
						boneOverride.newMatrix.matrix[i][x] *= matrixScale;
					}
				}

				boneOverride.newMatrix.matrix[0][3] = temp.matrix[0][3];
				boneOverride.newMatrix.matrix[1][3] = temp.matrix[1][3]; 
				boneOverride.newMatrix.matrix[2][3] = temp.matrix[2][3];
			
 				Multiply_3x4Matrix(&temp, &boneOverride.newMatrix,&skel->BasePoseMatInv);

				// now do the blend into the destination
				const float blendFrontlerp = 1.0 - blendLerp;
	  			for ( j = 0 ; j < 12 ; j++ ) 
				{
  					((float *)&bone)[j] = (blendLerp * ((float *)&temp)[j])
						+ (blendFrontlerp * ((float *)&firstPass)[j]);
				}
			}
			else
			{
				bone = firstPass;
			}
		}
		// no, so just override it directly
		else
		{

			Multiply_3x4Matrix(&temp, &firstPass, &skel->BasePoseMat);
			const float	matrixScale = VectorLength((float*)&temp);
			
			for (int i=0; i<3;i++)
			{
				for(int x=0;x<3; x++)
				{
					boneOverride.newMatrix.matrix[i][x] *= matrixScale;
				}
			}

			boneOverride.newMatrix.matrix[0][3] = temp.matrix[0][3];
			boneOverride.newMatrix.matrix[1][3] = temp.matrix[1][3]; 
			boneOverride.newMatrix.matrix[2][3] = temp.matrix[2][3];
		
 			Multiply_3x4Matrix(&bone, &boneOverride.newMatrix,&skel->BasePoseMatInv);
		}
	}
	else
	if (angleOverride & BONE_ANGLES_PREMULT)
	{
		if (TB.rootBone)
		{
			// use the in coming root matrix as our basis
			Multiply_3x4Matrix(&TB.bonePtr[TB.child].second, &TB.rootMatrix, &boneList[boneListIndex].newMatrix);
 		}
		else
		{
			// convert from 3x4 matrix to a 4x4 matrix
			Multiply_3x4Matrix(&TB.bonePtr[TB.child].second, &TB.bonePtr[TB.parent].second, &boneList[boneListIndex].newMatrix);
		}
	}
	else
	// now transform the matrix by it's TB.parent, asumming we have a TB.parent, and we aren't overriding the angles absolutely
	if (!TB.rootBone)
	{
		Multiply_3x4Matrix(&TB.bonePtr[TB.child].second, &TB.bonePtr[TB.parent].second, &tbone[2]);
	}

	// now multiply our resulting bone by an override matrix should we need to
	if (angleOverride & BONE_ANGLES_POSTMULT)
	{
		mdxaBone_t	tempMatrix;
		memcpy (&tempMatrix, &TB.bonePtr[TB.child].second, sizeof(mdxaBone_t));
	  	Multiply_3x4Matrix(&TB.bonePtr[TB.child].second, &tempMatrix, &boneList[boneListIndex].newMatrix);
	}

	// are the bone that we are resetting to the origin?
	if (TB.child == TB.boltNum)
	{
		//create a world matrix for the new origin
		mdxaBone_t tempMatrix;
		Multiply_3x4Matrix(&tempMatrix, &TB.bonePtr[TB.child].second, &skel->BasePoseMat);
		TB.newModelOrigin[0] = tempMatrix.matrix[0][3];
		TB.newModelOrigin[1] = tempMatrix.matrix[1][3];
		TB.newModelOrigin[2] = tempMatrix.matrix[2][3];
	}

	TB.rootBone = false;
	const int		parent = TB.child;
	const int		newFrame = TB.newFrame;
	const int		currentFrame = TB.currentFrame;
	const float	backLerp = TB.backlerp;
	const bool	blendMode = TB.blendMode;
	const float	blendFrame = TB.blendFrame;
	const int		blendOldFrame = TB.blendOldFrame;
	const float 	blendLerp = TB.blendLerp;
	// now work out what children we have to call this recursively for
	for (i=0; i< skel->numChildren; i++)
	{
		TB.newFrame = newFrame;
		TB.currentFrame = currentFrame;
		TB.backlerp = backLerp;
		TB.parent = parent;
		TB.child = skel->children[i];
		TB.blendFrame = blendFrame;
		TB.blendOldFrame = blendOldFrame;
		TB.blendMode = blendMode;
		TB.blendLerp = blendLerp;
		G2_TransformBone(TB);
	}
}

void G2_SetUpBolts( mdxaHeader_t *header, CGhoul2Info &ghoul2, mdxaBone_v &bonePtr, boltInfo_v &boltList)
{
	mdxaSkel_t		*skel;
	mdxaSkelOffsets_t *offsets;
	offsets = (mdxaSkelOffsets_t *)((byte *)header + sizeof(mdxaHeader_t));

	for (int i=0; i<boltList.size(); i++)
	{
		if (boltList[i].boneNumber != -1)
		{
			// figure out where the bone hirearchy info is
			skel = (mdxaSkel_t *)((byte *)header + sizeof(mdxaHeader_t) + offsets->offsets[boltList[i].boneNumber]);
			Multiply_3x4Matrix(&boltList[i].position, &bonePtr[boltList[i].boneNumber].second, &skel->BasePoseMat);
		}
	}
}


// start the recursive hirearchial bone transform and lerp process for this model
void G2_TransformGhoulBones( mdxaHeader_t *header, int *usedBoneList, boneInfo_v &rootBoneList, mdxaBone_v &bonePtr, boltInfo_v &boltList,
							mdxaBone_t &rootMatrix, CGhoul2Info &ghoul2, int time, int boneCount)
{
	int				frameSize, i;
	int				rootBoneIndex = 0;

    frameSize = 0;//	unused	(int)( &((mdxaFrame_t *)0)->boneIndexes[ header->numBones ] );   
	
	// figure out where our rootbone is
	for (i=0; i<boneCount; i++)
	{
		if (usedBoneList[i])
		{
			rootBoneIndex = i;
			break;
		}
	}
	 
	// now recursively call the bone transform routines using the bone hirearchy
	CTransformBone TB(ghoul2.mAnimFrameDefault, ghoul2.mAnimFrameDefault, 0, rootBoneIndex, frameSize, header, 0.0f, usedBoneList,
					  rootBoneList, bonePtr, boltList, rootMatrix, true, time, 0, 0, false, 0,  ghoul2.mNewOrigin);

	G2_TransformBone (TB);
	// now set up the bolt positions for those bolts required.
	G2_SetUpBolts(header, ghoul2, bonePtr, boltList);

}


#define MDX_TAG_ORIGIN 2

//======================================================================
//
// Surface Manipulation code 


// We've come across a surface that's designated as a bolt surface, process it and put it in the appropriate bolt place
void G2_ProcessSurfaceBolt(mdxaBone_v &bonePtr, mdxmSurface_t *surface, int boltNum, boltInfo_v &boltList, surfaceInfo_t *surfInfo, model_t *mod)
{
 	mdxmVertex_t 	*v, *vert0, *vert1, *vert2;
 	vec3_t			axes[3], sides[3];
 	float			pTri[3][3], d;
 	int				j, k;

	// now there are two types of tag surface - model ones and procedural generated types - lets decide which one we have here.
	if (surfInfo && surfInfo->offFlags == G2SURFACEFLAG_GENERATED)
	{
		int surfNumber = surfInfo->genPolySurfaceIndex & 0x0ffff;
		int	polyNumber = (surfInfo->genPolySurfaceIndex >> 16) & 0x0ffff;

		// find original surface our original poly was in.
		mdxmSurface_t	*originalSurf = (mdxmSurface_t *)G2_FindSurface((void*)mod, surfNumber, surfInfo->genLod);
		mdxmTriangle_t	*originalTriangleIndexes = (mdxmTriangle_t *)((byte*)originalSurf + originalSurf->ofsTriangles);

		// get the original polys indexes 
		int index0 = originalTriangleIndexes[polyNumber].indexes[0];
		int index1 = originalTriangleIndexes[polyNumber].indexes[1];
		int index2 = originalTriangleIndexes[polyNumber].indexes[2];

		// decide where the original verts are

 		vert0 = (mdxmVertex_t *) ((byte *)originalSurf + originalSurf->ofsVerts);
		vert0+= index0;

 		vert1 = (mdxmVertex_t *) ((byte *)originalSurf + originalSurf->ofsVerts);
		vert1+= index1;
		
 		vert2 = (mdxmVertex_t *) ((byte *)originalSurf + originalSurf->ofsVerts);
		vert2+= index2;

		// clear out the triangle verts to be
 	   	VectorClear( pTri[0] );
 	   	VectorClear( pTri[1] );
 	   	VectorClear( pTri[2] );

//		mdxmWeight_t	*w;

		int *piBoneRefs = (int*) ((byte*)originalSurf + originalSurf->ofsBoneReferences);		

		// now go and transform just the points we need from the surface that was hit originally
//		w = vert0->weights;
		float fTotalWeight = 0.0f;
		int iNumWeights = G2_GetVertWeights( vert0 );
 		for ( k = 0 ; k < iNumWeights ; k++ ) 
 		{
			int		iBoneIndex	= G2_GetVertBoneIndex( vert0, k );
			float	fBoneWeight	= G2_GetVertBoneWeight( vert0, k, fTotalWeight, iNumWeights );

 			pTri[0][0] += fBoneWeight * ( DotProduct( bonePtr[piBoneRefs[iBoneIndex]].second.matrix[0], vert0->vertCoords ) + bonePtr[piBoneRefs[iBoneIndex]].second.matrix[0][3] );
 			pTri[0][1] += fBoneWeight * ( DotProduct( bonePtr[piBoneRefs[iBoneIndex]].second.matrix[1], vert0->vertCoords ) + bonePtr[piBoneRefs[iBoneIndex]].second.matrix[1][3] );
 			pTri[0][2] += fBoneWeight * ( DotProduct( bonePtr[piBoneRefs[iBoneIndex]].second.matrix[2], vert0->vertCoords ) + bonePtr[piBoneRefs[iBoneIndex]].second.matrix[2][3] );
		}
//		w = vert1->weights;
		fTotalWeight = 0.0f;
		iNumWeights = G2_GetVertWeights( vert1 );
 		for ( k = 0 ; k < iNumWeights ; k++ ) 
 		{
			int		iBoneIndex	= G2_GetVertBoneIndex( vert1, k );
			float	fBoneWeight	= G2_GetVertBoneWeight( vert1, k, fTotalWeight, iNumWeights );

 			pTri[1][0] += fBoneWeight * ( DotProduct( bonePtr[piBoneRefs[iBoneIndex]].second.matrix[0], vert1->vertCoords ) + bonePtr[piBoneRefs[iBoneIndex]].second.matrix[0][3] );
 			pTri[1][1] += fBoneWeight * ( DotProduct( bonePtr[piBoneRefs[iBoneIndex]].second.matrix[1], vert1->vertCoords ) + bonePtr[piBoneRefs[iBoneIndex]].second.matrix[1][3] );
 			pTri[1][2] += fBoneWeight * ( DotProduct( bonePtr[piBoneRefs[iBoneIndex]].second.matrix[2], vert1->vertCoords ) + bonePtr[piBoneRefs[iBoneIndex]].second.matrix[2][3] );
		}
//		w = vert2->weights;
		fTotalWeight = 0.0f;
		iNumWeights = G2_GetVertWeights( vert2 );
 		for ( k = 0 ; k < iNumWeights ; k++ ) 
 		{
			int		iBoneIndex	= G2_GetVertBoneIndex( vert2, k );
			float	fBoneWeight	= G2_GetVertBoneWeight( vert2, k, fTotalWeight, iNumWeights );

 			pTri[2][0] += fBoneWeight * ( DotProduct( bonePtr[piBoneRefs[iBoneIndex]].second.matrix[0], vert2->vertCoords ) + bonePtr[piBoneRefs[iBoneIndex]].second.matrix[0][3] );
 			pTri[2][1] += fBoneWeight * ( DotProduct( bonePtr[piBoneRefs[iBoneIndex]].second.matrix[1], vert2->vertCoords ) + bonePtr[piBoneRefs[iBoneIndex]].second.matrix[1][3] );
 			pTri[2][2] += fBoneWeight * ( DotProduct( bonePtr[piBoneRefs[iBoneIndex]].second.matrix[2], vert2->vertCoords ) + bonePtr[piBoneRefs[iBoneIndex]].second.matrix[2][3] );
		}
 			
   		vec3_t normal;
		vec3_t up;
		vec3_t right;
		vec3_t vec0, vec1;
		// work out baryCentricK
		float baryCentricK = 1.0 - (surfInfo->genBarycentricI + surfInfo->genBarycentricJ);

		// now we have the model transformed into model space, now generate an origin.
		boltList[boltNum].position.matrix[0][3] = (pTri[0][0] * surfInfo->genBarycentricI) + (pTri[1][0] * surfInfo->genBarycentricJ) + (pTri[2][0] * baryCentricK);
		boltList[boltNum].position.matrix[1][3] = (pTri[0][1] * surfInfo->genBarycentricI) + (pTri[1][1] * surfInfo->genBarycentricJ) + (pTri[2][1] * baryCentricK);
		boltList[boltNum].position.matrix[2][3] = (pTri[0][2] * surfInfo->genBarycentricI) + (pTri[1][2] * surfInfo->genBarycentricJ) + (pTri[2][2] * baryCentricK);

		// generate a normal to this new triangle
		VectorSubtract(pTri[0], pTri[1], vec0);
		VectorSubtract(pTri[2], pTri[1], vec1);

		CrossProduct(vec0, vec1, normal);
		VectorNormalize(normal);

		// forward vector
		boltList[boltNum].position.matrix[0][0] = normal[0];
		boltList[boltNum].position.matrix[1][0] = normal[1];
		boltList[boltNum].position.matrix[2][0] = normal[2];

		// up will be towards point 0 of the original triangle.
		// so lets work it out. Vector is hit point - point 0
		up[0] = boltList[boltNum].position.matrix[0][3] - pTri[0][0];
		up[1] = boltList[boltNum].position.matrix[1][3] - pTri[0][1];
		up[2] = boltList[boltNum].position.matrix[2][3] - pTri[0][2];

		// normalise it
		VectorNormalize(up);

		// that's the up vector
		boltList[boltNum].position.matrix[0][1] = up[0];
		boltList[boltNum].position.matrix[1][1] = up[1];
		boltList[boltNum].position.matrix[2][1] = up[2];

		// right is always straight

		CrossProduct( normal, up, right );
		// that's the up vector
		boltList[boltNum].position.matrix[0][2] = right[0];
		boltList[boltNum].position.matrix[1][2] = right[1];
		boltList[boltNum].position.matrix[2][2] = right[2];


	}
	// no, we are looking at a normal model tag
	else
	{
		int *piBoneRefs = (int*) ((byte*)surface + surface->ofsBoneReferences);		

	 	// whip through and actually transform each vertex
 		v = (mdxmVertex_t *) ((byte *)surface + surface->ofsVerts);
 		for ( j = 0; j < 3; j++ ) 
 		{
// 			mdxmWeight_t	*w;

 			VectorClear( pTri[j] );
 //			w = v->weights;

			const int iNumWeights = G2_GetVertWeights( v );
			float fTotalWeight = 0.0f;
 			for ( k = 0 ; k < iNumWeights ; k++ ) 
 			{
				int		iBoneIndex	= G2_GetVertBoneIndex( v, k );
				float	fBoneWeight	= G2_GetVertBoneWeight( v, k, fTotalWeight, iNumWeights );

 				//bone = bonePtr + piBoneRefs[w->boneIndex];

 				pTri[j][0] += fBoneWeight * ( DotProduct( bonePtr[piBoneRefs[iBoneIndex]].second.matrix[0], v->vertCoords ) + bonePtr[piBoneRefs[iBoneIndex]].second.matrix[0][3] );
 				pTri[j][1] += fBoneWeight * ( DotProduct( bonePtr[piBoneRefs[iBoneIndex]].second.matrix[1], v->vertCoords ) + bonePtr[piBoneRefs[iBoneIndex]].second.matrix[1][3] );
 				pTri[j][2] += fBoneWeight * ( DotProduct( bonePtr[piBoneRefs[iBoneIndex]].second.matrix[2], v->vertCoords ) + bonePtr[piBoneRefs[iBoneIndex]].second.matrix[2][3] );
 			}
 			
 			v++;// = (mdxmVertex_t *)&v->weights[/*v->numWeights*/surface->maxVertBoneWeights];
 		}

 		// clear out used arrays
 		memset( axes, 0, sizeof( axes ) );
 		memset( sides, 0, sizeof( sides ) );

 		// work out actual sides of the tag triangle
 		for ( j = 0; j < 3; j++ )
 		{
 			sides[j][0] = pTri[(j+1)%3][0] - pTri[j][0];
 			sides[j][1] = pTri[(j+1)%3][1] - pTri[j][1];
 			sides[j][2] = pTri[(j+1)%3][2] - pTri[j][2];
 		}

 		// do math trig to work out what the matrix will be from this triangle's translated position
 		VectorNormalize2( sides[iG2_TRISIDE_LONGEST], axes[0] );
 		VectorNormalize2( sides[iG2_TRISIDE_SHORTEST], axes[1] );

 		// project shortest side so that it is exactly 90 degrees to the longer side
 		d = DotProduct( axes[0], axes[1] );
 		VectorMA( axes[0], -d, axes[1], axes[0] );
 		VectorNormalize2( axes[0], axes[0] );

 		CrossProduct( sides[iG2_TRISIDE_LONGEST], sides[iG2_TRISIDE_SHORTEST], axes[2] );
 		VectorNormalize2( axes[2], axes[2] );

 		// set up location in world space of the origin point in out going matrix
 		boltList[boltNum].position.matrix[0][3] = pTri[MDX_TAG_ORIGIN][0];
 		boltList[boltNum].position.matrix[1][3] = pTri[MDX_TAG_ORIGIN][1];
 		boltList[boltNum].position.matrix[2][3] = pTri[MDX_TAG_ORIGIN][2];

 		// copy axis to matrix - do some magic to orient minus Y to positive X and so on so bolt on stuff is oriented correctly
		boltList[boltNum].position.matrix[0][0] = axes[1][0];
		boltList[boltNum].position.matrix[0][1] = axes[0][0];
		boltList[boltNum].position.matrix[0][2] = -axes[2][0];

		boltList[boltNum].position.matrix[1][0] = axes[1][1];
		boltList[boltNum].position.matrix[1][1] = axes[0][1];
		boltList[boltNum].position.matrix[1][2] = -axes[2][1];

		boltList[boltNum].position.matrix[2][0] = axes[1][2];
		boltList[boltNum].position.matrix[2][1] = axes[0][2];
		boltList[boltNum].position.matrix[2][2] = -axes[2][2];
	}

}

 
// now go through all the generated surfaces that aren't included in the model surface hierarchy and create the correct bolt info for them				  
void G2_ProcessGeneratedSurfaceBolts(CGhoul2Info &ghoul2, mdxaBone_v &bonePtr, model_t *mod_t)
{
	// look through the surfaces off the end of the pre-defined model surfaces
	for (int i=0; i< ghoul2.mSlist.size(); i++)
	{
		// only look for bolts if we are actually a generated surface, and not just an overriden one
		if (ghoul2.mSlist[i].offFlags & G2SURFACEFLAG_GENERATED)
		{
	   		// well alrighty then. Lets see if there is a bolt that is attempting to use it
			int boltNum = G2_Find_Bolt_Surface_Num(ghoul2.mBltlist, i, G2SURFACEFLAG_GENERATED);
			// yes - ok, processing time.
			if (boltNum != -1)
			{
				G2_ProcessSurfaceBolt(bonePtr, NULL, boltNum, ghoul2.mBltlist, &ghoul2.mSlist[i], mod_t);
			}
		}
	}
}

#ifndef DEDICATED
// set up each surface ready for rendering in the back end
void RenderSurfaces(CRenderSurface &RS)
{
	int			i;
	shader_t	*shader = 0;
	int			offFlags = 0;
	
	// back track and get the surfinfo struct for this surface
	mdxmSurface_t			*surface = (mdxmSurface_t *)G2_FindSurface((void *)RS.currentModel, RS.surfaceNum, RS.lod);
	mdxmHierarchyOffsets_t	*surfIndexes = (mdxmHierarchyOffsets_t *)((byte *)RS.currentModel->mdxm + sizeof(mdxmHeader_t));
	mdxmSurfHierarchy_t		*surfInfo = (mdxmSurfHierarchy_t *)((byte *)surfIndexes + surfIndexes->offsets[surface->thisSurfaceIndex]);
	
	// see if we have an override surface in the surface list
	surfaceInfo_t	*surfOverride = G2_FindOverrideSurface(RS.surfaceNum, RS.rootSList);

	// really, we should use the default flags for this surface unless it's been overriden
	offFlags = surfInfo->flags;

	// set the off flags if we have some
	if (surfOverride)
	{
		offFlags = surfOverride->offFlags;
	}

	// if this surface is not off, add it to the shader render list
	if (!offFlags)
	{	
 		if ( RS.cust_shader ) 
		{
			shader = RS.cust_shader;
		} 
		else if ( RS.skin ) 
		{
			int		j;
			
			// match the surface name to something in the skin file
			shader = tr.defaultShader;
			for ( j = 0 ; j < RS.skin->numSurfaces ; j++ )
			{
				// the names have both been lowercased
				if ( !strcmp( RS.skin->surfaces[j]->name, surfInfo->name ) ) 
				{
					shader = RS.skin->surfaces[j]->shader;
					break;
				}
			}
		} 
		else 
		{
			shader = R_GetShaderByHandle( surfInfo->shaderIndex );
		}
		// we will add shadows even if the main object isn't visible in the view

		// stencil shadows can't do personal models unless I polyhedron clip
		if ( !RS.personalModel
			&& r_shadows->integer == 2 
			&& RS.fogNum == 0
			&& !(RS.renderfx & ( RF_NOSHADOW | RF_DEPTHHACK ) ) 
			&& shader->sort == SS_OPAQUE ) 
		{		// set the surface info to point at the where the transformed bone list is going to be for when the surface gets rendered out
			CRenderableSurface *newSurf = new CRenderableSurface;
			newSurf->surfaceData = surface;
			newSurf->boneList = &RS.bonePtr;
			R_AddDrawSurf( (surfaceType_t *)newSurf, tr.shadowShader, 0, qfalse );
		}

		// projection shadows work fine with personal models
		if ( r_shadows->integer == 3
			&& RS.fogNum == 0
			&& (RS.renderfx & RF_SHADOW_PLANE )
			&& shader->sort == SS_OPAQUE ) 
		{		// set the surface info to point at the where the transformed bone list is going to be for when the surface gets rendered out
			CRenderableSurface *newSurf = new CRenderableSurface;
			newSurf->surfaceData = surface;
			newSurf->boneList = &RS.bonePtr;
			R_AddDrawSurf( (surfaceType_t *)newSurf, tr.projectionShadowShader, 0, qfalse );
		}

		// don't add third_person objects if not viewing through a portal
		if ( !RS.personalModel ) 
		{		// set the surface info to point at the where the transformed bone list is going to be for when the surface gets rendered out
			CRenderableSurface *newSurf = new CRenderableSurface;
			newSurf->surfaceData = surface;
			newSurf->boneList = &RS.bonePtr;
			R_AddDrawSurf( (surfaceType_t *)newSurf, shader, RS.fogNum, qfalse );
		}
	}
	
	// is this surface considered a bolt surface?
	if (offFlags & G2SURFACEFLAG_ISBOLT)
	{
		// well alrighty then. Lets see if there is a bolt that is attempting to use it
		int boltNum = G2_Find_Bolt_Surface_Num(RS.boltList, RS.surfaceNum, 0);
		// yes - ok, processing time.
		if (boltNum != -1)
		{
			G2_ProcessSurfaceBolt(RS.bonePtr, surface, boltNum, RS.boltList, surfOverride, RS.currentModel);
		}
	}
	
	// if we are turning off all descendants, then stop this recursion now
	if (offFlags & G2SURFACEFLAG_NODESCENDANTS)
	{
		return;
	}

	// now recursively call for the children
	for (i=0; i< surfInfo->numChildren; i++)
	{
		RS.surfaceNum = surfInfo->childIndexes[i];
		RenderSurfaces(RS);
	}
}
#endif //!DEDICATED

// Go through the model and deal with just the surfaces that are tagged as bolt on points - this is for the server side skeleton construction
void ProcessModelBoltSurfaces(int surfaceNum, surfaceInfo_v &rootSList,
					mdxaBone_v &bonePtr, model_t *currentModel, int lod, boltInfo_v &boltList)
{
	int			i;
	int			offFlags = 0;
	
	// back track and get the surfinfo struct for this surface
	mdxmSurface_t			*surface = (mdxmSurface_t *)G2_FindSurface((void *)currentModel, surfaceNum, 0);
	mdxmHierarchyOffsets_t	*surfIndexes = (mdxmHierarchyOffsets_t *)((byte *)currentModel->mdxm + sizeof(mdxmHeader_t));
	mdxmSurfHierarchy_t		*surfInfo = (mdxmSurfHierarchy_t *)((byte *)surfIndexes + surfIndexes->offsets[surface->thisSurfaceIndex]);
	
	// see if we have an override surface in the surface list
	surfaceInfo_t	*surfOverride = G2_FindOverrideSurface(surfaceNum, rootSList);

	// really, we should use the default flags for this surface unless it's been overriden
	offFlags = surfInfo->flags;

	// set the off flags if we have some
	if (surfOverride)
	{
		offFlags = surfOverride->offFlags;
	}

	// is this surface considered a bolt surface?
	if (surfInfo->flags & G2SURFACEFLAG_ISBOLT)
	{
		// well alrighty then. Lets see if there is a bolt that is attempting to use it
		int boltNum = G2_Find_Bolt_Surface_Num(boltList, surfaceNum, 0);
		// yes - ok, processing time.
		if (boltNum != -1)
		{
			G2_ProcessSurfaceBolt(bonePtr, surface, boltNum, boltList, surfOverride, currentModel);
		}
	}
	
	// if we are turning off all descendants, then stop this recursion now
	if (offFlags & G2SURFACEFLAG_NODESCENDANTS)
	{
		return;
	}

	// now recursively call for the children
	for (i=0; i< surfInfo->numChildren; i++)
	{
		ProcessModelBoltSurfaces(surfInfo->childIndexes[i], rootSList, bonePtr, currentModel, lod, boltList);
	}
}


// build the used bone list so when doing bone transforms we can determine if we need to do it or not
void G2_ConstructUsedBoneList(CConstructBoneList &CBL)
{
	int	 		i, j;
	int			offFlags = 0;

	// back track and get the surfinfo struct for this surface
	const mdxmSurface_t			*surface = (mdxmSurface_t *)G2_FindSurface((void *)CBL.currentModel, CBL.surfaceNum, 0);
	const mdxmHierarchyOffsets_t	*surfIndexes = (mdxmHierarchyOffsets_t *)((byte *)CBL.currentModel->mdxm + sizeof(mdxmHeader_t));
	const mdxmSurfHierarchy_t	*surfInfo = (mdxmSurfHierarchy_t *)((byte *)surfIndexes + surfIndexes->offsets[surface->thisSurfaceIndex]);
	const model_t				*mod_a = R_GetModelByHandle(CBL.currentModel->mdxm->animIndex);
	const mdxaSkelOffsets_t		*offsets = (mdxaSkelOffsets_t *)((byte *)mod_a->mdxa + sizeof(mdxaHeader_t));
	const mdxaSkel_t			*skel, *childSkel;

	// see if we have an override surface in the surface list
	const surfaceInfo_t	*surfOverride = G2_FindOverrideSurface(CBL.surfaceNum, CBL.rootSList);

	// really, we should use the default flags for this surface unless it's been overriden
	offFlags = surfInfo->flags;

	// set the off flags if we have some
	if (surfOverride)
	{
		offFlags = surfOverride->offFlags;
	}

	// if this surface is not off, add it to the shader render list
	if (!(offFlags & G2SURFACEFLAG_OFF))
	{
		int	*bonesReferenced = (int *)((byte*)surface + surface->ofsBoneReferences);
		// now whip through the bones this surface uses
		for (i=0; i<surface->numBoneReferences;i++)
		{
			int iBoneIndex = bonesReferenced[i];
			CBL.boneUsedList[iBoneIndex] = 1;

			// now go and check all the descendant bones attached to this bone and see if any have the always flag on them. If so, activate them
 			skel = (mdxaSkel_t *)((byte *)mod_a->mdxa + sizeof(mdxaHeader_t) + offsets->offsets[iBoneIndex]);

			// for every child bone...
			for (j=0; j< skel->numChildren; j++)
			{
				// get the skel data struct for each child bone of the referenced bone
 				childSkel = (mdxaSkel_t *)((byte *)mod_a->mdxa + sizeof(mdxaHeader_t) + offsets->offsets[skel->children[j]]);

				// does it have the always on flag on?
				if (childSkel->flags & G2BONEFLAG_ALWAYSXFORM)
				{
					// yes, make sure it's in the list of bones to be transformed.
					CBL.boneUsedList[skel->children[j]] = 1;
				}
			}

			// now we need to ensure that the parents of this bone are actually active...
			//			
			int iParentBone = skel->parent;
			while (iParentBone != -1)
			{	
				if (CBL.boneUsedList[iParentBone])	// no need to go higher
					break;
				CBL.boneUsedList[iParentBone] = 1;
				skel = (mdxaSkel_t *)((byte *)mod_a->mdxa + sizeof(mdxaHeader_t) + offsets->offsets[iParentBone]);				
				iParentBone = skel->parent;
			}
		}
	}
 	else
	// if we are turning off all descendants, then stop this recursion now
	if (offFlags & G2SURFACEFLAG_NODESCENDANTS)
	{
		return;
	}

	// now recursively call for the children
	for (i=0; i< surfInfo->numChildren; i++)
	{
		CBL.surfaceNum = surfInfo->childIndexes[i];
		G2_ConstructUsedBoneList(CBL);
	}
}


// sort all the ghoul models in this list so if they go in reference order. This will ensure the bolt on's are attached to the right place
// on the previous model, since it ensures the model being attached to is built and rendered first.

// NOTE!! This assumes at least one model will NOT have a parent. If it does - we are screwed
static void G2_Sort_Models(CGhoul2Info_v &ghoul2, int * const modelList, int * const modelCount)
{
	int		startPoint, endPoint;
	int		i, boltTo, j;

	*modelCount = 0;

	// first walk all the possible ghoul2 models, and stuff the out array with those with no parents
	for (i=0; i<ghoul2.size();i++)
	{
		// have a ghoul model here?
		if (ghoul2[i].mModelindex == -1)
		{
			continue;
		}
		// are we attached to anything?
		if (ghoul2[i].mModelBoltLink == -1)
		{
			// no, insert us first
			modelList[(*modelCount)++] = i;
	 	}
	}

	startPoint = 0;
	endPoint = *modelCount;

	// now, using that list of parentless models, walk the descendant tree for each of them, inserting the descendents in the list
	while (startPoint != endPoint)
	{
		for (i=0; i<ghoul2.size(); i++)
		{
			// have a ghoul model here?
			if (ghoul2[i].mModelindex == -1)
			{
				continue;
			}

			// what does this model think it's attached to?
			if (ghoul2[i].mModelBoltLink != -1)
			{
				boltTo = (ghoul2[i].mModelBoltLink >> MODEL_SHIFT) & MODEL_AND;
				// is it any of the models we just added to the list?
				for (j=startPoint; j<endPoint; j++)
				{
					// is this my parent model?
					if (boltTo == modelList[j])
					{
						// yes, insert into list and exit now
						modelList[(*modelCount)++] = i;
						break;
					}
				}
			}
		}
		// update start and end points
		startPoint = endPoint;
		endPoint = *modelCount;
	}
}

const static mdxaBone_t		identityMatrix = 
{ 
	0.0f, -1.0f, 0.0f, 0.0f,
	1.0f, 0.0f, 0.0f, 0.0f,
	0.0f, 0.0f, 1.0f, 0.0f
};

/*
==============
R_AddGHOULSurfaces
==============
*/

void R_AddGhoulSurfaces( trRefEntity_t *ent ) {
#ifndef DEDICATED
	mdxaHeader_t	*aHeader;
	shader_t		*cust_shader = 0;
	int				fogNum = 0;
	qboolean		personalModel;
	int				cull;
	int				i, whichLod, j;
	skin_t			*skin;
	model_t			*currentModel;
	model_t			*animModel;
	int				modelCount;
	int				*modelList;
	mdxaBone_t		rootMatrix;
	bool			setNewOrigin = false;
	CGhoul2Info_v	&ghoul2 = *((CGhoul2Info_v *)ent->e.ghoul2);

	// if we don't want server ghoul2 models and this is one, then return
	if ((r_noServerGhoul2->integer && !(ghoul2[0].mCreationID & WF_CLIENTONLY)) )
	{
		return;
	}

	// cull the entire model if merged bounding box of both frames
	// is outside the view frustum.
	cull = R_GCullModel (ent );
	if ( cull == CULL_OUT ) 
	{
		return;
	}

	// are any of these models setting a new origin?
	for (i=0; i<ghoul2.size(); i++)
	{
		if (ghoul2[i].mModelindex != -1)
		{
			if (ghoul2[i].mFlags & GHOUL2_NEWORIGIN)
			{
				// yes. ooooookkkkkk - now we have to do a GetBoltMatrix for that bone on that model, so we can get it's data into the root matrix for this pass
				// this will call this same function recursively, so we set checkForNewOrigin to false on the GetBoltMatrixCall so we don't do this loop
				G2_ConstructGhoulSkeleton(ghoul2, tr.refdef.time -100, 0,false, ent->e.angles, ent->e.origin, ent->e.modelScale, true); 

				// scale the bolt position by the scale factor for this model since at this point its still in model space
				if (ent->e.modelScale[0])
				{
					ghoul2[i].mBltlist[ghoul2[i].mNewOrigin].position.matrix[0][3] *= ent->e.modelScale[0];
				}
				if (ent->e.modelScale[1])
				{
					ghoul2[i].mBltlist[ghoul2[i].mNewOrigin].position.matrix[1][3] *= ent->e.modelScale[1];
				}
				if (ent->e.modelScale[2])
				{ 
					ghoul2[i].mBltlist[ghoul2[i].mNewOrigin].position.matrix[2][3] *= ent->e.modelScale[2];
				}

				VectorNormalize((float*)&ghoul2[i].mBltlist[ghoul2[i].mNewOrigin].position.matrix[0]);
				VectorNormalize((float*)&ghoul2[i].mBltlist[ghoul2[i].mNewOrigin].position.matrix[1]);
				VectorNormalize((float*)&ghoul2[i].mBltlist[ghoul2[i].mNewOrigin].position.matrix[2]);
				mdxaBone_t		tempMatrix;
				tempMatrix.matrix[0][0]=1.0f;
				tempMatrix.matrix[0][1]=0.0f;
				tempMatrix.matrix[0][2]=0.0f;
				tempMatrix.matrix[0][3]=-ghoul2[i].mBltlist[ghoul2[i].mNewOrigin].position.matrix[0][3];
				tempMatrix.matrix[1][0]=0.0f;
				tempMatrix.matrix[1][1]=1.0f;
				tempMatrix.matrix[1][2]=0.0f;
				tempMatrix.matrix[1][3]=-ghoul2[i].mBltlist[ghoul2[i].mNewOrigin].position.matrix[1][3];
				tempMatrix.matrix[2][0]=0.0f;
				tempMatrix.matrix[2][1]=0.0f;
				tempMatrix.matrix[2][2]=1.0f;
				tempMatrix.matrix[2][3]=-ghoul2[i].mBltlist[ghoul2[i].mNewOrigin].position.matrix[2][3];
				//Inverse_Matrix(&ghoul2[i].mBltlist[ghoul2[i].mNewOrigin].position, &tempMatrix);
				Multiply_3x4Matrix(&rootMatrix, &tempMatrix, (mdxaBone_t*)&identityMatrix);

				setNewOrigin = true;
			}
		}
	}

   	// don't add third_person objects if not in a portal
	personalModel = (qboolean)((ent->e.renderfx & RF_THIRD_PERSON) && !tr.viewParms.isPortal);

	modelList = (int*)Z_Malloc(ghoul2.size() * 4, TAG_GHOUL2, qtrue);
#ifndef DEDICATED
	// set up lighting now that we know we aren't culled
	if ( !personalModel || r_shadows->integer > 1 ) 
	{
		// FIXME!! Is there something here we should be looking at?
		R_SetupEntityLighting( &tr.refdef, ent );
	}

	// see if we are in a fog volume
	fogNum = R_GComputeFogNum( ent );
#endif // !DEDICATED

	// order sort the ghoul 2 models so bolt ons get bolted to the right model
	G2_Sort_Models(ghoul2, modelList, &modelCount);

	// construct a world matrix for this entity
	G2_GenerateWorldMatrix(ent->e.angles, ent->e.origin);

	// walk each possible model for this entity and try rendering it out
	for (j=0; j<modelCount; j++)
	{
		// get the sorted model to play with
		i = modelList[j];

		// do we really really want to deal with this model?
		if (!(ghoul2[i].mFlags & GHOUL2_NOMODEL))
		{
			currentModel = R_GetModelByHandle(ghoul2[i].mModel);
			animModel =  R_GetModelByHandle(currentModel->mdxm->animIndex);
			aHeader = animModel->mdxa;
 #ifndef DEDICATED 		
			//
			// figure out whether we should be using a custom shader for this model
			//
			skin = NULL;
			if (ent->e.customShader)
			{
				cust_shader = R_GetShaderByHandle(ent->e.customShader );
			}
			else
			{
				cust_shader = NULL;
				// figure out the custom skin thing
				if (ghoul2[i].mCustomSkin)
				{
					skin = R_GetSkinByHandle(ghoul2[i].mCustomSkin );
				}
				else if (ent->e.customSkin)
				{
					skin = R_GetSkinByHandle(ent->e.customSkin );
				}
				else if ( ghoul2[i].mSkin > 0 && ghoul2[i].mSkin < tr.numSkins ) 
				{
					skin = R_GetSkinByHandle( ghoul2[i].mSkin );
				}
			}
#endif //!DEDICATED

			if (ghoul2[i].mSkelFrameNum != tr.refdef.time)
			{
				int	*boneUsedList;

				ghoul2[i].mSkelFrameNum = tr.refdef.time;

				// construct a list of all bones used by this model - this makes the bone transform go a bit faster since it will dump out bones
				// that aren't being used. - NOTE this will screw up any models that have surfaces turned off where the lower surfaces aren't.
				boneUsedList = (int *)Z_Malloc(animModel->mdxa->numBones * 4, TAG_GHOUL2, qtrue);			
				memset(boneUsedList, 0, (animModel->mdxa->numBones * 4));

				CConstructBoneList	CBL(ghoul2[i].mSurfaceRoot, 
					boneUsedList,
					ghoul2[i].mSlist,
					currentModel,
					ghoul2[i].mBlist
					);

				G2_ConstructUsedBoneList(CBL);

				if (!ghoul2[i].mSurfaceRoot)
				{
					// make sure the root bone is marked as being referenced
					boneUsedList[0] =1;
				}

				if (ghoul2[i].mTempBoneList.size()!=animModel->mdxa->numBones+1)
				{
					ghoul2[i].mTempBoneList.resize(animModel->mdxa->numBones+1);
					int k;
					for (k=0;k<animModel->mdxa->numBones;k++)
					{
						ghoul2[i].mTempBoneList[k].first=-10000; //reset it to an invalid time
					}	
				}
				
				// if this is the root model, and we have a new root matrix because the model has a new origin, use that
				if (!setNewOrigin || j)
				{
					// decide what to do about the root matrix
					if (ghoul2[i].mModelBoltLink == -1)
					{
						// we aren't bolted to anything
						rootMatrix = identityMatrix;
					}
					// yes we are bolted to another model, better use the bolt as the root matrix then, so our model is offset correctly for the bone bolt
					else
					{
						int	boltMod = (ghoul2[i].mModelBoltLink >> MODEL_SHIFT) & MODEL_AND;
						int	boltNum = (ghoul2[i].mModelBoltLink >> BOLT_SHIFT) & BOLT_AND;
						rootMatrix = ghoul2[boltMod].mBltlist[boltNum].position;
					}
				}

				mdxaBone_v oldBones;
				if (r_Ghoul2AnimSmooth&&r_Ghoul2AnimSmooth->value>0.005f&&r_Ghoul2AnimSmooth->value<0.995f)
				{
					oldBones=ghoul2[i].mTempBoneList;
				}
				// pre-transform all the bones of this model
				G2_TransformGhoulBones( aHeader, boneUsedList, ghoul2[i].mBlist, ghoul2[i].mTempBoneList, ghoul2[i].mBltlist, rootMatrix, ghoul2[i], tr.refdef.time, animModel->mdxa->numBones);
				if (oldBones.size())
				{
					int b;
					for (b=0;b<animModel->mdxa->numBones;b++)
					{
						if (r_Ghoul2AnimSmooth&&r_Ghoul2AnimSmooth->value>0.005f&&r_Ghoul2AnimSmooth->value<0.995f)
						{
							if (tr.refdef.time-ghoul2[i].mTempBoneList[b].first<200&&tr.refdef.time-ghoul2[i].mTempBoneList[b].first>-200)
							{
								int k;
								float *oldM=&oldBones[b].second.matrix[0][0];
								float *newM=&ghoul2[i].mTempBoneList[b].second.matrix[0][0];
								for (k=0;k<12;k++,oldM++,newM++)
								{
									*newM=r_Ghoul2AnimSmooth->value*(*oldM-*newM)+*newM;
								}
							}
							ghoul2[i].mTempBoneList[b].first=tr.refdef.time;
						}
					}
				}

				if (r_Ghoul2UnSqashAfterSmooth&&r_Ghoul2UnSqashAfterSmooth->value>0.5f)
				{
					mdxaSkelOffsets_t *offsets = (mdxaSkelOffsets_t *)((byte *)aHeader + sizeof(mdxaHeader_t));
					int b;
					for (b=0;b<animModel->mdxa->numBones;b++)
					{
						if (!boneUsedList)
						{
							continue;
						}
						mdxaSkel_t		*skel= (mdxaSkel_t *)((byte *)aHeader + sizeof(mdxaHeader_t) + offsets->offsets[b]);
						mdxaBone_t tempMatrix;
						Multiply_3x4Matrix(&tempMatrix,&ghoul2[i].mTempBoneList[b].second, &skel->BasePoseMat);
						float maxl;
						maxl=VectorLength(&skel->BasePoseMat.matrix[0][0]);
						VectorNormalize(&tempMatrix.matrix[0][0]);
						VectorNormalize(&tempMatrix.matrix[1][0]);
						VectorNormalize(&tempMatrix.matrix[2][0]);

						VectorScale(&tempMatrix.matrix[0][0],maxl,&tempMatrix.matrix[0][0]);
						VectorScale(&tempMatrix.matrix[1][0],maxl,&tempMatrix.matrix[1][0]);
						VectorScale(&tempMatrix.matrix[2][0],maxl,&tempMatrix.matrix[2][0]);
						Multiply_3x4Matrix(&ghoul2[i].mTempBoneList[b].second,&tempMatrix,&skel->BasePoseMatInv);
					}
					
				}

				Z_Free(boneUsedList);
			}
			//
			// compute LOD
   			//
			whichLod = G2_ComputeLOD( ent, currentModel, ghoul2[i].mLodBias );

			// do we really want to render this?
			//-----------------------------------------------------------------------------
			// TEMP TEMP! Adding this in for the cheesy hacked first person mode, basically isn't supposed to render the player when in "first person"...but still renders the saber model
			//-----------------------------------------------------------------------------
			// TEMP TEMP! Adding this in for the cheesy hacked first person mode, basically isn't supposed to render the player when in "first person"...but still renders the saber model
			//-----------------------------------------------------------------------------
			// TEMP TEMP! Adding this in for the cheesy hacked first person mode, basically isn't supposed to render the player when in "first person"...but still renders the saber model
			//-----------------------------------------------------------------------------
			// TEMP TEMP! Adding this in for the cheesy hacked first person mode, basically isn't supposed to render the player when in "first person"...but still renders the saber model
			//-----------------------------------------------------------------------------
			if ( ghoul2[i].mFlags & GHOUL2_NORENDER)// /*temp ->*/|| ( ghoul2[i].mModelBoltLink == -1 && ent->e.renderfx & RF_ALPHA_FADE ))
			{
				// if we didn't render it, then we should still go sort out all the bolt surfaces in the model
				ProcessModelBoltSurfaces(ghoul2[i].mSurfaceRoot, ghoul2[i].mSlist, ghoul2[i].mTempBoneList, currentModel, whichLod, ghoul2[i].mBltlist);
			}
			else
			{ // start the walk of the surface hierarchy	
				CRenderSurface RS(ghoul2[i].mSurfaceRoot, ghoul2[i].mSlist, cust_shader, fogNum, personalModel, ghoul2[i].mTempBoneList, ent->e.renderfx, skin, currentModel, whichLod, ghoul2[i].mBltlist);
				RenderSurfaces(RS);
			}

			// go through all the generated surfaces and create their bolt info if we need it.
			G2_ProcessGeneratedSurfaceBolts(ghoul2[i], ghoul2[i].mTempBoneList, currentModel);

		}
	}
	Z_Free(modelList);
#endif
}	

/*
==============
G2_ConstructGhoulSkeleton - builds a complete skeleton for all ghoul models in a CGhoul2Info_v class	- using LOD 0
==============
*/
void G2_ConstructGhoulSkeleton( CGhoul2Info_v &ghoul2, const int frameNum, qhandle_t *modelPointerList, bool checkForNewOrigin, const vec3_t angles, const vec3_t position, const vec3_t scale, bool modelSet) {
	mdxaHeader_t	*aHeader;
	int				i, j;
	int				*boneUsedList;
	model_t			*currentModel;
	model_t			*animModel;
	int				modelCount;
	int				*modelList;
	bool			setNewOrigin = false;
	mdxaBone_t		rootMatrix;

	// if we don't want server ghoul2 models and this is one, then return
	if ((r_noServerGhoul2->integer && !(ghoul2[0].mCreationID & WF_CLIENTONLY)) )
	{
		return;
	}

	// have we already transformed this group of ghoul2 skeletons this frame?
	if (ghoul2[0].mSkelFrameNum == frameNum)
	{
		return;
	}

	// should we be looking to see if any of these models are setting a new origin?
	if (checkForNewOrigin)
	{
		// are any of these models setting a new origin?
		for (int i=0; i<ghoul2.size(); i++)
		{
			if (ghoul2[i].mModelindex != -1)
			{
				if (ghoul2[i].mFlags & GHOUL2_NEWORIGIN)
				{
					// yes. ooooookkkkkk - now we have to do a GetBoltMatrix for that bone on that model, so we can get it's data into the root matrix for this pass
					// this will call this same function recursively, so we set checkForNewOrigin to false on the GetBoltMatrixCall so we don't do this loop
					G2_ConstructGhoulSkeleton(ghoul2, frameNum-100, modelPointerList,false, angles, position, scale, false); 
					// scale the bolt position by the scale factor for this model since at this point its still in model space
					if (scale[0])
					{
						ghoul2[i].mBltlist[ghoul2[i].mNewOrigin].position.matrix[0][3] *= scale[0];
					}
					if (scale[1])
					{
						ghoul2[i].mBltlist[ghoul2[i].mNewOrigin].position.matrix[1][3] *= scale[1];
					}
					if (scale[2])
					{
						ghoul2[i].mBltlist[ghoul2[i].mNewOrigin].position.matrix[2][3] *= scale[2];
					}

					VectorNormalize((float*)&ghoul2[i].mBltlist[ghoul2[i].mNewOrigin].position.matrix[0]);
					VectorNormalize((float*)&ghoul2[i].mBltlist[ghoul2[i].mNewOrigin].position.matrix[1]);
					VectorNormalize((float*)&ghoul2[i].mBltlist[ghoul2[i].mNewOrigin].position.matrix[2]);
					mdxaBone_t		tempMatrix;
					Inverse_Matrix(&ghoul2[i].mBltlist[ghoul2[i].mNewOrigin].position, &tempMatrix);
					Multiply_3x4Matrix(&rootMatrix, &tempMatrix, (mdxaBone_t*)&identityMatrix);
					setNewOrigin = true;
				}
			}
		}
	}

	modelList = (int*)Z_Malloc(ghoul2.size() * 4, TAG_GHOUL2, qtrue);
	// order sort the ghoul 2 models so bolt ons get bolted to the right model
	G2_Sort_Models(ghoul2, modelList, &modelCount);

	// walk each possible model for this entity and try rendering it out
	for (j=0; j<modelCount; j++)
	{
		// get the sorted model to play with
		i = modelList[j];

		// do we really really want to deal with this model?
		if (!(ghoul2[i].mFlags & GHOUL2_NOMODEL))
		{
			if (modelSet)
			{
				currentModel = R_GetModelByHandle(ghoul2[i].mModel);
			}
			else
			if (!ghoul2[i].mFileName[0])
			{
				int iModelIndex = ghoul2[i].mModelindex;
				currentModel = R_GetModelByHandle(modelPointerList[iModelIndex]);
			}
			else
			{
				char *psFilename = ghoul2[i].mFileName;
				currentModel = R_GetModelByHandle(RE_RegisterModel(psFilename));
			}
			animModel =  R_GetModelByHandle(currentModel->mdxm->animIndex);
			aHeader = animModel->mdxa;

			ghoul2[i].mSkelFrameNum = frameNum;
  		
			// construct a list of all bones used by this model - this makes the bone transform go a bit faster since it will dump out bones
			// that aren't being used. - NOTE this will screw up any models that have surfaces turned off where the lower surfaces aren't.
			boneUsedList = (int *)Z_Malloc(animModel->mdxa->numBones * 4, TAG_GHOUL2, qtrue);
			memset(boneUsedList, 0, (animModel->mdxa->numBones * 4));

			CConstructBoneList	CBL(
			ghoul2[i].mSurfaceRoot, 
			boneUsedList,
			ghoul2[i].mSlist,
			currentModel,
			ghoul2[i].mBlist
			);

			G2_ConstructUsedBoneList(CBL);

			if (!ghoul2[i].mSurfaceRoot)
			{
				// make sure the root bone is marked as being referenced
				boneUsedList[0] =1;
			}

			if (ghoul2[i].mTempBoneList.size()!=animModel->mdxa->numBones+1)
			{
				ghoul2[i].mTempBoneList.resize(animModel->mdxa->numBones+1);
				int k;
				for (k=0;k<animModel->mdxa->numBones;k++)
				{
					ghoul2[i].mTempBoneList[k].first=-10000; //reset it to an invalid time
				}	
			}

			// decide what to do about the root matrix

			// if this is the root model, and we have a new root matrix because the model has a new origin, use that
			if (!setNewOrigin || j)
			{
				if (ghoul2[i].mModelBoltLink == -1)
				{
					// we aren't bolted to anything
					rootMatrix = identityMatrix;
				}
				// yes we are bolted to another model, better use the bolt as the root matrix then, so our model is offset correctly for the bone bolt
				else
				{
					int	boltMod = (ghoul2[i].mModelBoltLink >> MODEL_SHIFT) & MODEL_AND;
					int	boltNum = (ghoul2[i].mModelBoltLink >> BOLT_SHIFT) & BOLT_AND;
					rootMatrix = ghoul2[boltMod].mBltlist[boltNum].position;
				}
			}

			mdxaBone_v oldBones;
			if (r_Ghoul2AnimSmooth&&r_Ghoul2AnimSmooth->value>0.005f&&r_Ghoul2AnimSmooth->value<0.995f)
			{
				oldBones=ghoul2[i].mTempBoneList;
			}
			// pre-transform all the bones of this model
			G2_TransformGhoulBones( aHeader, boneUsedList, ghoul2[i].mBlist, ghoul2[i].mTempBoneList, ghoul2[i].mBltlist, rootMatrix, ghoul2[i], frameNum, animModel->mdxa->numBones);

			if (oldBones.size())
			{
				int b;
				for (b=0;b<animModel->mdxa->numBones;b++)
				{
					if (r_Ghoul2AnimSmooth&&r_Ghoul2AnimSmooth->value>0.005f&&r_Ghoul2AnimSmooth->value<0.995f)
					{
						if (tr.refdef.time-ghoul2[i].mTempBoneList[b].first<200&&tr.refdef.time-ghoul2[i].mTempBoneList[b].first>-200)
						{
							int k;
							float *oldM=&oldBones[b].second.matrix[0][0];
							float *newM=&ghoul2[i].mTempBoneList[b].second.matrix[0][0];
							for (k=0;k<12;k++,oldM++,newM++)
							{
								*newM=r_Ghoul2AnimSmooth->value*(*oldM-*newM)+*newM;
							}
						}
						ghoul2[i].mTempBoneList[b].first=tr.refdef.time;
					}
				}
			}

			if (r_Ghoul2UnSqashAfterSmooth&&r_Ghoul2UnSqashAfterSmooth->value>0.5f)
			{
				mdxaSkelOffsets_t *offsets = (mdxaSkelOffsets_t *)((byte *)aHeader + sizeof(mdxaHeader_t));
				int b;
				for (b=0;b<animModel->mdxa->numBones;b++)
				{
					if (!boneUsedList)
					{
						continue;
					}
					mdxaSkel_t		*skel= (mdxaSkel_t *)((byte *)aHeader + sizeof(mdxaHeader_t) + offsets->offsets[b]);
					mdxaBone_t tempMatrix;
					Multiply_3x4Matrix(&tempMatrix,&ghoul2[i].mTempBoneList[b].second, &skel->BasePoseMat);
					float maxl;
					maxl=VectorLength(&skel->BasePoseMat.matrix[0][0]);
					VectorNormalize(&tempMatrix.matrix[0][0]);
					VectorNormalize(&tempMatrix.matrix[1][0]);
					VectorNormalize(&tempMatrix.matrix[2][0]);

					VectorScale(&tempMatrix.matrix[0][0],maxl,&tempMatrix.matrix[0][0]);
					VectorScale(&tempMatrix.matrix[1][0],maxl,&tempMatrix.matrix[1][0]);
					VectorScale(&tempMatrix.matrix[2][0],maxl,&tempMatrix.matrix[2][0]);
					Multiply_3x4Matrix(&ghoul2[i].mTempBoneList[b].second,&tempMatrix,&skel->BasePoseMatInv);
				}
				
			}

			Z_Free(boneUsedList);

			// call function that will go through the main model and generate all the bolts required
			ProcessModelBoltSurfaces(ghoul2[i].mSurfaceRoot, ghoul2[i].mSlist, ghoul2[i].mTempBoneList, currentModel, 0, ghoul2[i].mBltlist);

			// go through all the generated surfaces and create their bolt info if we need it.
			G2_ProcessGeneratedSurfaceBolts(ghoul2[i], ghoul2[i].mTempBoneList, currentModel);

		}
	}
	Z_Free(modelList);
	return;
}	

#ifndef DEDICATED
/*
==============
RB_SurfaceGhoul
==============
*/
void RB_SurfaceGhoul( CRenderableSurface *surf ) {
	int				 j, k;

	// grab the pointer to the surface info within the loaded mesh file
	mdxmSurface_t	*surface = (mdxmSurface_t *)surf->surfaceData;

	// point us at the bone structure that should have been pre-computed
	mdxaBone_v &bonePtr = *((mdxaBone_v *)surf->boneList);

	delete surf;
	//
	// deform the vertexes by the lerped bones
	//
   
	// first up, sanity check our numbers
	RB_CheckOverflow( surface->numVerts, surface->numTriangles );

	// now copy the right number of verts to the temporary area for verts for this shader
	const int baseVertex = tess.numVertexes;
	const int *triangles = (int *) ((byte *)surface + surface->ofsTriangles);
	const int baseIndex = tess.numIndexes;
#if 0
	const int indexes = surface->numTriangles * 3;
	for (j = 0 ; j < indexes ; j++) {
		tess.indexes[baseIndex + j] = baseVertex + triangles[j];
	}
	tess.numIndexes += indexes;
#else
	const int indexes = surface->numTriangles; //*3;	//unrolled 3 times, don't multiply
	unsigned int * tessIndexes = &tess.indexes[baseIndex];
	for (j = 0 ; j < indexes ; j++) {
		*tessIndexes++ = baseVertex + *triangles++;
		*tessIndexes++ = baseVertex + *triangles++;
		*tessIndexes++ = baseVertex + *triangles++;
	}
	tess.numIndexes += indexes*3;
#endif

	
	// whip through and actually transform each vertex

	const int *piBoneRefs = (int*) ((byte*)surface + surface->ofsBoneReferences);		
	const int numVerts = surface->numVerts;
	const mdxmVertex_t 	*v = (mdxmVertex_t *) ((byte *)surface + surface->ofsVerts);
	mdxmVertexTexCoord_t *pTexCoords = (mdxmVertexTexCoord_t *) &v[numVerts];

	int baseVert = tess.numVertexes;
	for ( j = 0; j < numVerts; j++, baseVert++ ) 
	{
		const int iNumWeights = G2_GetVertWeights( v );
//		const mdxmWeight_t	*w = v->weights;
		VectorClear( tess.xyz[baseVert]);
		VectorClear( tess.normal[baseVert]);

		float fTotalWeight = 0.0f;
		for ( k = 0 ; k < iNumWeights ; k++ ) 
		{
			int		iBoneIndex	= G2_GetVertBoneIndex( v, k );
			float	fBoneWeight	= G2_GetVertBoneWeight( v, k, fTotalWeight, iNumWeights );

			mdxaBone_t &bone = bonePtr[piBoneRefs[iBoneIndex]].second;

			tess.xyz[baseVert][0] += fBoneWeight * ( DotProduct( bone.matrix[0], v->vertCoords ) + bone.matrix[0][3] );
			tess.xyz[baseVert][1] += fBoneWeight * ( DotProduct( bone.matrix[1], v->vertCoords ) + bone.matrix[1][3] );
			tess.xyz[baseVert][2] += fBoneWeight * ( DotProduct( bone.matrix[2], v->vertCoords ) + bone.matrix[2][3] );

			tess.normal[baseVert][0] += fBoneWeight * DotProduct( bone.matrix[0], v->normal );
			tess.normal[baseVert][1] += fBoneWeight * DotProduct( bone.matrix[1], v->normal );
			tess.normal[baseVert][2] += fBoneWeight * DotProduct( bone.matrix[2], v->normal );
		}

		tess.texCoords[baseVert][0][0] = pTexCoords[j].texCoords[0];
		tess.texCoords[baseVert][0][1] = pTexCoords[j].texCoords[1];

		v++;// = (mdxmVertex_t *)&v->weights[/*v->numWeights*/surface->maxVertBoneWeights];
	}

	tess.numVertexes += surface->numVerts;
}
#endif // !DEDICATED
 
/*
=================
R_LoadMDXM - load a Ghoul 2 Mesh file
=================
*/
qboolean R_LoadMDXM( model_t *mod, void *buffer, const char *mod_name, qboolean bAlreadyCached ) {
	int					i,l, j;
	mdxmHeader_t		*pinmodel, *mdxm;
	mdxmLOD_t			*lod;
	mdxmSurface_t		*surf;
	int					version;
	int					size;
	mdxmSurfHierarchy_t	*surfInfo;

#ifndef _M_IX86
	int					k;
	int					frameSize;
	mdxmTag_t			*tag;
	mdxmTriangle_t		*tri;
	mdxmVertex_t		*v;
 	mdxmFrame_t			*cframe;
	int					*boneRef;
#endif
    
	pinmodel= (mdxmHeader_t *)buffer;
	//
	// read some fields from the binary, but only LittleLong() them when we know this wasn't an already-cached model...
	//	
	version = (pinmodel->version);
	size	= (pinmodel->ofsEnd);

	if (!bAlreadyCached)
	{
		version = LittleLong(version);
		size	= LittleLong(size);
	}

	if (version != MDXM_VERSION) {
		ri.Printf( PRINT_WARNING, "R_LoadMDXM: %s has wrong version (%i should be %i)\n",
				 mod_name, version, MDXM_VERSION);
		return qfalse;
	}

	mod->type	   = MOD_MDXM;
	mod->dataSize += size;	
	
	qboolean bAlreadyFound = qfalse;
	mdxm = mod->mdxm = (mdxmHeader_t*) //ri.Hunk_Alloc( size );
										RE_RegisterModels_Malloc(size, mod_name, &bAlreadyFound, TAG_MODEL_GLM);

	assert(bAlreadyCached == bAlreadyFound);	// I should probably eliminate 'bAlreadyFound', but wtf?

	if (!bAlreadyFound)
	{
		memcpy( mdxm, buffer, size );	

		LL(mdxm->ident);
		LL(mdxm->version);
		LL(mdxm->numLODs);
		LL(mdxm->ofsLODs);
		LL(mdxm->numSurfaces);
		LL(mdxm->ofsSurfHierarchy);
		LL(mdxm->ofsEnd);
	}
		
	// first up, go load in the animation file we need that has the skeletal animation info for this model
	mdxm->animIndex = RE_RegisterModel(va ("%s.gla",mdxm->animName));
	if (!mdxm->animIndex) 
	{
		ri.Printf( PRINT_WARNING, "R_LoadMDXM: missing animation file %s for mesh %s\n", mdxm->animName, mdxm->name);
		return qfalse;
	}

	mod->numLods = mdxm->numLODs -1 ;	//copy this up to the model for ease of use - it wil get inced after this.

	if (bAlreadyFound)
	{
		return qtrue;	// All done. Stop, go no further, do not LittleLong(), do not pass Go...
	}

	surfInfo = (mdxmSurfHierarchy_t *)( (byte *)mdxm + mdxm->ofsSurfHierarchy);
 	for ( i = 0 ; i < mdxm->numSurfaces ; i++) 
	{
		LL(surfInfo->numChildren);
		LL(surfInfo->parentIndex);

		// do all the children indexs
		for (j=0; j<surfInfo->numChildren; j++)
		{
			LL(surfInfo->childIndexes[j]);
		}
#ifdef DEDICATED
		surfInfo->shaderIndex = 0;
#else
		shader_t	*sh;
		// get the shader name
		sh = R_FindShader( surfInfo->shader, lightmapsNone, stylesDefault, qtrue );
		// insert it in the surface list
		if ( sh->defaultShader ) 
		{
			surfInfo->shaderIndex = 0;
		}
		else
		{
			surfInfo->shaderIndex = sh->index;
		}
#endif		
		RE_RegisterModels_StoreShaderRequest(mod_name, &surfInfo->shader[0], &surfInfo->shaderIndex);		

		// find the next surface
		surfInfo = (mdxmSurfHierarchy_t *)( (byte *)surfInfo + (int)( &((mdxmSurfHierarchy_t *)0)->childIndexes[ surfInfo->numChildren ] ));
  	}
	
#if _DEBUG
	ri.Printf(0, "For Ghoul2 mesh file %s\n", mod_name);
#endif
	// swap all the LOD's	(we need to do the middle part of this even for intel, because of shader reg and err-check)
	lod = (mdxmLOD_t *) ( (byte *)mdxm + mdxm->ofsLODs );
	for ( l = 0 ; l < mdxm->numLODs ; l++)
	{
		int	triCount = 0;

		LL(lod->ofsEnd);
		// swap all the surfaces
		surf = (mdxmSurface_t *) ( (byte *)lod + sizeof (mdxmLOD_t) + (mdxm->numSurfaces * sizeof(mdxmLODSurfOffset_t)) );
		for ( i = 0 ; i < mdxm->numSurfaces ; i++) 
		{
			LL(surf->numTriangles);
			LL(surf->ofsTriangles);
			LL(surf->numVerts);
			LL(surf->ofsVerts);
			LL(surf->ofsEnd);
			LL(surf->ofsHeader);
			LL(surf->numBoneReferences);
			LL(surf->ofsBoneReferences);
//			LL(surf->maxVertBoneWeights);

			triCount += surf->numTriangles;
										
			if ( surf->numVerts > SHADER_MAX_VERTEXES ) {
				ri.Error (ERR_DROP, "R_LoadMDXM: %s has more than %i verts on a surface (%i)",
					mod_name, SHADER_MAX_VERTEXES, surf->numVerts );
			}
			if ( surf->numTriangles*3 > SHADER_MAX_INDEXES ) {
				ri.Error (ERR_DROP, "R_LoadMDXM: %s has more than %i triangles on a surface (%i)",
					mod_name, SHADER_MAX_INDEXES / 3, surf->numTriangles );
			}
		
			// change to surface identifier
			surf->ident = SF_MDX;

			// register the shaders
#ifndef _M_IX86
//
// optimisation, we don't bother doing this for standard intel case since our data's already in that format...
//
			// FIXME - is this correct? 
			// do all the bone reference data
			boneRef = (int *) ( (byte *)surf + surf->ofsBoneReferences );
			for ( j = 0 ; j < surf->numBoneReferences ; j++ ) 
			{
					LL(boneRef[j]);
			}

			
			// swap all the triangles
			tri = (mdxmTriangle_t *) ( (byte *)surf + surf->ofsTriangles );
			for ( j = 0 ; j < surf->numTriangles ; j++, tri++ ) 
			{
				LL(tri->indexes[0]);
				LL(tri->indexes[1]);
				LL(tri->indexes[2]);
			}

			// swap all the vertexes
			v = (mdxmVertex_t *) ( (byte *)surf + surf->ofsVerts );
			for ( j = 0 ; j < surf->numVerts ; j++ ) 
			{
				v->normal[0] = LittleFloat( v->normal[0] );
				v->normal[1] = LittleFloat( v->normal[1] );
				v->normal[2] = LittleFloat( v->normal[2] );

				v->texCoords[0] = LittleFloat( v->texCoords[0] );
				v->texCoords[1] = LittleFloat( v->texCoords[1] );

				v->numWeights = LittleLong( v->numWeights );
  			    v->offset[0] = LittleFloat( v->offset[0] );
				v->offset[1] = LittleFloat( v->offset[1] );
				v->offset[2] = LittleFloat( v->offset[2] );

				for ( k = 0 ; k < /*v->numWeights*/surf->maxVertBoneWeights ; k++ ) 
				{
					v->weights[k].boneIndex = LittleLong( v->weights[k].boneIndex );
					v->weights[k].boneWeight = LittleFloat( v->weights[k].boneWeight );
				}
				v = (mdxmVertex_t *)&v->weights[/*v->numWeights*/surf->maxVertBoneWeights];
			}
#endif

			// find the next surface
			surf = (mdxmSurface_t *)( (byte *)surf + surf->ofsEnd );
		}
#if _DEBUG
		ri.Printf(0, "Lod %d has %d tris in %d surfaces with %d bones\n", l, triCount, mdxm->numSurfaces, mdxm->numBones);
#endif
		// find the next LOD
		lod = (mdxmLOD_t *)( (byte *)lod + lod->ofsEnd );
	}


// This is junk, there's no such structure...
//
//#ifndef _M_IX86
////
//// optimisation, we don't bother doing this for standard intel case since our data's already in that format...
////
//	tag = (mdxmTag_t *) ( (byte *)mdxm + mdxm->ofsTags );
//	for ( i = 0 ; i < md4->numTags ; i++) {
//		LL(tag->boneIndex);
//		tag++;
//	}
//#endif

	return qtrue;
}

/*
=================
R_LoadMDXA - load a Ghoul 2 animation file
=================
*/
qboolean R_LoadMDXA( model_t *mod, void *buffer, const char *mod_name, qboolean bAlreadyCached ) {

	mdxaHeader_t		*pinmodel, *mdxa;
	int					version;
	int					size;

#ifndef _M_IX86
	int					j, k, i;
	int					frameSize;
	mdxaFrame_t			*cframe;
	mdxaSkel_t			*boneInfo;
#endif

 	pinmodel = (mdxaHeader_t *)buffer;
	//
	// read some fields from the binary, but only LittleLong() them when we know this wasn't an already-cached model...
	//	
	version = (pinmodel->version);
	size	= (pinmodel->ofsEnd);

	if (!bAlreadyCached)
	{
		version = LittleLong(version);
		size	= LittleLong(size);
	}
	
	if (version != MDXA_VERSION) {
		ri.Printf( PRINT_WARNING, "R_LoadMDXA: %s has wrong version (%i should be %i)\n",
				 mod_name, version, MDXA_VERSION);
		return qfalse;
	}

	mod->type		= MOD_MDXA;
	mod->dataSize  += size;

	qboolean bAlreadyFound = qfalse;
	mdxa = mod->mdxa = (mdxaHeader_t*) //ri.Hunk_Alloc( size );
										RE_RegisterModels_Malloc(size, mod_name, &bAlreadyFound, TAG_MODEL_GLA);

	assert(bAlreadyCached == bAlreadyFound);	// I should probably eliminate 'bAlreadyFound', but wtf?

	if (!bAlreadyFound)
	{
		memcpy( mdxa, buffer, size );

		LL(mdxa->ident);
		LL(mdxa->version);
		LL(mdxa->numFrames);
		LL(mdxa->numBones);
		LL(mdxa->ofsFrames);
		LL(mdxa->ofsEnd);
	}

 	if ( mdxa->numFrames < 1 ) {
		ri.Printf( PRINT_WARNING, "R_LoadMDXA: %s has no frames\n", mod_name );
		return qfalse;
	}

	if (bAlreadyFound)
	{
		return qtrue;	// All done, stop here, do not LittleLong() etc. Do not pass go...
	}

#ifndef _M_IX86

	//
	// optimisation, we don't bother doing this for standard intel case since our data's already in that format...
	//

	// swap all the skeletal info
	boneInfo = (mdxaSkel_t *)( (byte *)mdxa + mdxa->ofsSkel);
	for ( i = 0 ; i < mdxa->numBones ; i++) 
	{
		LL(boneInfo->numChildren);
		LL(boneInfo->parent);
		for (k=0; k<boneInfo->numChildren; k++)
		{
			LL(boneInfo->children[k]);
		}

		// get next bone
		boneInfo += (int)( &((mdxaSkel_t *)0)->children[ boneInfo->numChildren ] );
	}


	// swap all the frames
	frameSize = (int)( &((mdxaFrame_t *)0)->bones[ mdxa->numBones ] );
	for ( i = 0 ; i < mdxa->numFrames ; i++) 
	{
		cframe = (mdxaFrame_t *) ( (byte *)mdxa + mdxa->ofsFrames + i * frameSize );
   		cframe->radius = LittleFloat( cframe->radius );
		for ( j = 0 ; j < 3 ; j++ ) 
		{
			cframe->bounds[0][j] = LittleFloat( cframe->bounds[0][j] );
			cframe->bounds[1][j] = LittleFloat( cframe->bounds[1][j] );
    		cframe->localOrigin[j] = LittleFloat( cframe->localOrigin[j] );
		}
		for ( j = 0 ; j < mdxa->numBones * sizeof( mdxaBone_t ) / 2 ; j++ ) 
		{
			((short *)cframe->bones)[j] = LittleShort( ((short *)cframe->bones)[j] );
		}
	}
#endif
	return qtrue;
}







