code:
#pragma once

typedef rw::int8 RwInt8;
typedef rw::int16 RwInt16;
typedef rw::int32 RwInt32;
typedef rw::uint8 RwUInt8;
typedef rw::uint16 RwUInt16;
typedef rw::uint32 RwUInt32;
typedef rw::float32 RwReal;

typedef char RwChar;
typedef RwInt32 RwBool;

#define __RWUNUSED__

#ifndef FALSE
#define FALSE 0
#endif

#ifndef TRUE
#define TRUE !FALSE
#endif

// used for unicode
#define RWSTRING(x) x

typedef rw::V2d RwV2d;

typedef rw::V3d RwV3d;

typedef rw::Rect RwRect;

typedef rw::Sphere RwSphere;

enum RwTextureCoordinateIndex
{
	rwNARWTEXTURECOORDINATEINDEX = 0,
	rwTEXTURECOORDINATEINDEX0,
	rwTEXTURECOORDINATEINDEX1,
	rwTEXTURECOORDINATEINDEX2,
	rwTEXTURECOORDINATEINDEX3,
	rwTEXTURECOORDINATEINDEX4,
	rwTEXTURECOORDINATEINDEX5,
	rwTEXTURECOORDINATEINDEX6,
	rwTEXTURECOORDINATEINDEX7,
};

typedef rw::TexCoords RwTexCoords;

typedef rw::SurfaceProperties RwSurfaceProperties;

#define RWRGBALONG(r,g,b,a)                                             \
    ((RwUInt32) (((a) << 24) | ((r) << 16) | ((g) << 8) | (b)))

#define MAKECHUNKID(vendorID, chunkID) (((vendorID & 0xFFFFFF) << 8) | (chunkID & 0xFF))

enum RwCorePluginID
{
	rwID_NAOBJECT               = 0x00,
	rwID_STRUCT                 = 0x01,
	rwID_STRING                 = 0x02,
	rwID_EXTENSION              = 0x03,
	rwID_CAMERA                 = 0x05,
	rwID_TEXTURE                = 0x06,
	rwID_MATERIAL               = 0x07,
	rwID_MATLIST                = 0x08,
	rwID_ATOMICSECT             = 0x09,
	rwID_PLANESECT              = 0x0A,
	rwID_WORLD                  = 0x0B,
	rwID_SPLINE                 = 0x0C,
	rwID_MATRIX                 = 0x0D,
	rwID_FRAMELIST              = 0x0E,
	rwID_GEOMETRY               = 0x0F,
	rwID_CLUMP                  = 0x10,
	rwID_LIGHT                  = 0x12,
	rwID_UNICODESTRING          = 0x13,
	rwID_ATOMIC                 = 0x14,
	rwID_TEXTURENATIVE          = 0x15,
	rwID_TEXDICTIONARY          = 0x16,
	rwID_ANIMDATABASE           = 0x17,
	rwID_IMAGE                  = 0x18,
	rwID_SKINANIMATION          = 0x19,
	rwID_GEOMETRYLIST           = 0x1A,
	rwID_HANIMANIMATION         = 0x1B,
	rwID_TEAM                   = 0x1C,
	rwID_CROWD                  = 0x1D,
	rwID_DMORPHANIMATION        = 0x1E,
	rwID_RIGHTTORENDER          = 0x1f,
	rwID_MTEFFECTNATIVE         = 0x20,
	rwID_MTEFFECTDICT           = 0x21,
	rwID_TEAMDICTIONARY         = 0x22,
	rwID_PITEXDICTIONARY        = 0x23,
	rwID_TOC                    = 0x24,
	rwID_PRTSTDGLOBALDATA       = 0x25,
	/* Insert before MAX and increment MAX */
	rwID_COREPLUGINIDMAX        = 0x26,
};

/*
 ***********************************************
 *
 * RwObject
 *
 ***********************************************
 */

//struct RwObject;
typedef rw::Object RwObject;
typedef rw::Frame RwFrame;

typedef RwObject *(*RwObjectCallBack)(RwObject *object, void *data);

RwUInt8 RwObjectGetType(const RwObject *obj);
RwFrame* rwObjectGetParent(const RwObject *obj);

#define rwsprintf   sprintf
#define rwvsprintf  vsprintf
#define rwstrcpy    strcpy
#define rwstrncpy   strncpy
#define rwstrcat    strcat
#define rwstrncat   strncat
#define rwstrrchr   strrchr
#define rwstrchr    strchr
#define rwstrstr    strstr
#define rwstrcmp    strcmp
#define rwstricmp   stricmp
#define rwstrlen    strlen
#define rwstrupr    strupr
#define rwstrlwr    strlwr
#define rwstrtok    strtok
#define rwsscanf    sscanf

/*
 ***********************************************
 *
 * Memory
 *
 ***********************************************
 */

struct RwMemoryFunctions
{
	// NB: from RW 3.6 on the allocating functions take
	// a hint parameter!
	void *(*rwmalloc)(size_t size);
	void  (*rwfree)(void *mem);
	void *(*rwrealloc)(void *mem, size_t newSize);
	void *(*rwcalloc)(size_t numObj, size_t sizeObj);
};

void *RwMalloc(size_t size);
void  RwFree(void *mem);
void *RwRealloc(void *mem, size_t newSize);
void *RwCalloc(size_t numObj, size_t sizeObj);

/*
 ***********************************************
 *
 * RwStream
 *
 ***********************************************
 */

//struct RwStream;
typedef rw::Stream RwStream;

struct RwMemory
{
	RwUInt8     *start;
	RwUInt32    length;
};

enum RwStreamType
{
	rwNASTREAM = 0,
	rwSTREAMFILE,
	rwSTREAMFILENAME,
	rwSTREAMMEMORY,
	rwSTREAMCUSTOM
};

enum RwStreamAccessType
{
	rwNASTREAMACCESS = 0,
	rwSTREAMREAD,
	rwSTREAMWRITE,
	rwSTREAMAPPEND
};

RwStream *RwStreamOpen(RwStreamType type, RwStreamAccessType accessType, const void *pData);
RwBool RwStreamClose(RwStream * stream, void *pData);
RwUInt32 RwStreamRead(RwStream * stream, void *buffer, RwUInt32 length);
RwStream *RwStreamWrite(RwStream * stream, const void *buffer, RwUInt32 length);
RwStream *RwStreamSkip(RwStream * stream, RwUInt32 offset);

/*
 ***********************************************
 *
 * Plugin Registry
 *
 ***********************************************
 */

#define RWPLUGINOFFSET(_type, _base, _offset)                   \
   ((_type *)((RwUInt8 *)(_base) + (_offset)))

typedef RwStream *(*RwPluginDataChunkWriteCallBack)(RwStream *stream, RwInt32 binaryLength, const void *object, RwInt32 offsetInObject, RwInt32 sizeInObject);
typedef RwStream *(*RwPluginDataChunkReadCallBack)(RwStream *stream, RwInt32 binaryLength, void *object, RwInt32 offsetInObject, RwInt32 sizeInObject);
typedef RwInt32(*RwPluginDataChunkGetSizeCallBack)(const void *object, RwInt32 offsetInObject, RwInt32 sizeInObject);
typedef RwBool(*RwPluginDataChunkAlwaysCallBack)(void *object, RwInt32 offsetInObject, RwInt32 sizeInObject);
typedef RwBool(*RwPluginDataChunkRightsCallBack)(void *object, RwInt32 offsetInObject, RwInt32 sizeInObject, RwUInt32 extraData);
typedef void *(*RwPluginObjectConstructor)(void *object, RwInt32 offsetInObject, RwInt32 sizeInObject);
typedef void *(*RwPluginObjectCopy)(void *dstObject, const void *srcObject, RwInt32 offsetInObject, RwInt32 sizeInObject);
typedef void *(*RwPluginObjectDestructor)(void *object, RwInt32 offsetInObject, RwInt32 sizeInObject);

/*
 ***********************************************
 *
 * RwMatrix
 *
 ***********************************************
 */

typedef rw::Matrix RwMatrix;

enum RwOpCombineType
{
	rwCOMBINEREPLACE = rw::COMBINEREPLACE,
	rwCOMBINEPRECONCAT = rw::COMBINEPRECONCAT,
	rwCOMBINEPOSTCONCAT = rw::COMBINEPOSTCONCAT
};

enum RwMatrixType
{
	rwMATRIXTYPENORMAL = rw::Matrix::TYPENORMAL,
	rwMATRIXTYPEORTHOGANAL = rw::Matrix::TYPEORTHOGONAL,
	rwMATRIXTYPEORTHONORMAL = rw::Matrix::TYPEORTHONORMAL,
	rwMATRIXTYPEMASK = 0x00000003,
};

typedef rw::Matrix::Tolerance RwMatrixTolerance;

RwBool RwMatrixDestroy(RwMatrix *mpMat);
RwMatrix *RwMatrixCreate(void);
void RwMatrixCopy(RwMatrix * dstMatrix, const RwMatrix * srcMatrix);
void RwMatrixSetIdentity(RwMatrix * matrix);
RwMatrix *RwMatrixMultiply(RwMatrix * matrixOut, const RwMatrix * MatrixIn1, const RwMatrix * matrixIn2);
RwMatrix *RwMatrixTransform(RwMatrix * matrix, const RwMatrix * transform, RwOpCombineType combineOp);
RwMatrix *RwMatrixOrthoNormalize(RwMatrix * matrixOut, const RwMatrix * matrixIn);
RwMatrix *RwMatrixInvert(RwMatrix * matrixOut, const RwMatrix * matrixIn);
RwMatrix *RwMatrixScale(RwMatrix * matrix, const RwV3d * scale, RwOpCombineType combineOp);
RwMatrix *RwMatrixTranslate(RwMatrix * matrix, const RwV3d * translation, RwOpCombineType combineOp);
RwMatrix *RwMatrixRotate(RwMatrix * matrix, const RwV3d * axis, RwReal angle, RwOpCombineType combineOp);
RwMatrix *RwMatrixRotateOneMinusCosineSine(RwMatrix * matrix, const RwV3d * unitAxis, RwReal oneMinusCosine, RwReal sine, RwOpCombineType combineOp);
const RwMatrix *RwMatrixQueryRotate(const RwMatrix * matrix, RwV3d * unitAxis, RwReal * angle, RwV3d * center);
RwV3d *RwMatrixGetRight(RwMatrix * matrix);
RwV3d *RwMatrixGetUp(RwMatrix * matrix);
RwV3d *RwMatrixGetAt(RwMatrix * matrix);
RwV3d *RwMatrixGetPos(RwMatrix * matrix);
RwMatrix *RwMatrixUpdate(RwMatrix * matrix);
RwMatrix *RwMatrixOptimize(RwMatrix * matrix, const RwMatrixTolerance *tolerance);

/*
 ***********************************************
 *
 * RwRGBA
 *
 ***********************************************
 */

typedef rw::RGBA RwRGBA;
typedef rw::RGBAf RwRGBAReal;

inline void RwRGBAAssign(RwRGBA *target, const RwRGBA *source) { *target = *source; }

RwReal RwV3dNormalize(RwV3d * out, const RwV3d * in);
RwReal RwV3dLength(const RwV3d * in);
RwReal RwV2dLength(const RwV2d * in);
RwReal RwV2dNormalize(RwV2d * out, const RwV2d * in);
void RwV2dAssign(RwV2d * out, const RwV2d * ina);
void RwV2dAdd(RwV2d * out, const RwV2d * ina, const RwV2d * inb);
void RwV2dLineNormal(RwV2d * out, const RwV2d * ina, const RwV2d * inb);
void RwV2dSub(RwV2d * out, const RwV2d * ina, const RwV2d * inb);
void RwV2dPerp(RwV2d * out, const RwV2d * in);
void RwV2dScale(RwV2d * out, const RwV2d * in, RwReal scalar);
RwReal RwV2dDotProduct(const RwV2d * ina, const RwV2d * inb);
void RwV3dAssign(RwV3d * out, const RwV3d * ina);
void RwV3dAdd(RwV3d * out, const RwV3d * ina, const RwV3d * inb);
void RwV3dSub(RwV3d * out, const RwV3d * ina, const RwV3d * inb);
void RwV3dScale(RwV3d * out, const RwV3d * in, RwReal scalar);
void RwV3dIncrementScaled(RwV3d * out,  const RwV3d * in, RwReal scalar);
void RwV3dNegate(RwV3d * out, const RwV3d * in);
RwReal RwV3dDotProduct(const RwV3d * ina, const RwV3d * inb);
void RwV3dCrossProduct(RwV3d * out, const RwV3d * ina, const RwV3d * inb);
RwV3d *RwV3dTransformPoints(RwV3d * pointsOut, const RwV3d * pointsIn, RwInt32 numPoints, const RwMatrix * matrix);
RwV3d *RwV3dTransformVectors(RwV3d * vectorsOut, const RwV3d * vectorsIn, RwInt32 numPoints, const RwMatrix * matrix);

/*
 ***********************************************
 *
 * Render States
 *
 ***********************************************
 */

// not librw because we don't support all of them (yet?) - mapping in wrapper functions
enum RwRenderState
{
	rwRENDERSTATENARENDERSTATE = 0,
	rwRENDERSTATETEXTURERASTER,
	rwRENDERSTATETEXTUREADDRESS,
	rwRENDERSTATETEXTUREADDRESSU,
	rwRENDERSTATETEXTUREADDRESSV,
	rwRENDERSTATETEXTUREPERSPECTIVE,
	rwRENDERSTATEZTESTENABLE,
	rwRENDERSTATESHADEMODE,
	rwRENDERSTATEZWRITEENABLE,
	rwRENDERSTATETEXTUREFILTER,
	rwRENDERSTATESRCBLEND,
	rwRENDERSTATEDESTBLEND,
	rwRENDERSTATEVERTEXALPHAENABLE,
	rwRENDERSTATEBORDERCOLOR,
	rwRENDERSTATEFOGENABLE,
	rwRENDERSTATEFOGCOLOR,
	rwRENDERSTATEFOGTYPE,
	rwRENDERSTATEFOGDENSITY,
	rwRENDERSTATEFOGTABLE,
	rwRENDERSTATEALPHAPRIMITIVEBUFFER,
	rwRENDERSTATECULLMODE,
	rwRENDERSTATESTENCILENABLE,
	rwRENDERSTATESTENCILFAIL,
	rwRENDERSTATESTENCILZFAIL,
	rwRENDERSTATESTENCILPASS,
	rwRENDERSTATESTENCILFUNCTION,
	rwRENDERSTATESTENCILFUNCTIONREF,
	rwRENDERSTATESTENCILFUNCTIONMASK,
	rwRENDERSTATESTENCILFUNCTIONWRITEMASK
};

// not supported - we only do gouraud
enum RwShadeMode
{
	rwSHADEMODENASHADEMODE = 0,
	rwSHADEMODEFLAT,
	rwSHADEMODEGOURAUD
};

enum RwBlendFunction
{
	rwBLENDNABLEND = 0,
	rwBLENDZERO = rw::BLENDZERO,
	rwBLENDONE = rw::BLENDONE,
	rwBLENDSRCCOLOR = rw::BLENDSRCCOLOR,
	rwBLENDINVSRCCOLOR = rw::BLENDINVSRCCOLOR,
	rwBLENDSRCALPHA = rw::BLENDSRCALPHA,
	rwBLENDINVSRCALPHA = rw::BLENDINVSRCALPHA,
	rwBLENDDESTALPHA = rw::BLENDDESTALPHA,
	rwBLENDINVDESTALPHA = rw::BLENDINVDESTALPHA,
	rwBLENDDESTCOLOR = rw::BLENDDESTCOLOR,
	rwBLENDINVDESTCOLOR = rw::BLENDINVDESTCOLOR,
	rwBLENDSRCALPHASAT = rw::BLENDSRCALPHASAT
};

// unsupported - we only need linear
enum RwFogType
{
	rwFOGTYPENAFOGTYPE = 0,
	rwFOGTYPELINEAR,
	rwFOGTYPEEXPONENTIAL,
	rwFOGTYPEEXPONENTIAL2
};

enum RwTextureFilterMode
{
	rwFILTERNAFILTERMODE = 0,
	rwFILTERNEAREST = rw::Texture::NEAREST,
	rwFILTERLINEAR = rw::Texture::LINEAR,
	rwFILTERMIPNEAREST = rw::Texture::MIPNEAREST,
	rwFILTERMIPLINEAR = rw::Texture::MIPLINEAR,
	rwFILTERLINEARMIPNEAREST = rw::Texture::LINEARMIPNEAREST,
	rwFILTERLINEARMIPLINEAR = rw::Texture::LINEARMIPLINEAR
};

enum RwTextureAddressMode
{
	rwTEXTUREADDRESSNATEXTUREADDRESS = 0,
	rwTEXTUREADDRESSWRAP = rw::Texture::WRAP,
	rwTEXTUREADDRESSMIRROR = rw::Texture::MIRROR,
	rwTEXTUREADDRESSCLAMP = rw::Texture::CLAMP,
	rwTEXTUREADDRESSBORDER = rw::Texture::BORDER
};

enum RwCullMode
{
	rwCULLMODENACULLMODE = 0,
	rwCULLMODECULLNONE = rw::CULLNONE,
	rwCULLMODECULLBACK = rw::CULLBACK,
	rwCULLMODECULLFRONT = rw::CULLFRONT
};

enum RwPrimitiveType
{
	rwPRIMTYPENAPRIMTYPE = rw::PRIMTYPENONE,
	rwPRIMTYPELINELIST = rw::PRIMTYPELINELIST,
	rwPRIMTYPEPOLYLINE = rw::PRIMTYPEPOLYLINE,
	rwPRIMTYPETRILIST = rw::PRIMTYPETRILIST,
	rwPRIMTYPETRISTRIP = rw::PRIMTYPETRISTRIP,
	rwPRIMTYPETRIFAN = rw::PRIMTYPETRIFAN,
	rwPRIMTYPEPOINTLIST = rw::PRIMTYPEPOINTLIST
};

RwBool RwRenderStateGet(RwRenderState state, void *value);
RwBool RwRenderStateSet(RwRenderState state, void *value);

/*
 ***********************************************
 *
 * Engine
 *
 ***********************************************
 */

struct RwEngineOpenParams
{
	void    *displayID;
};

typedef rw::SubSystemInfo RwSubSystemInfo;

enum RwVideoModeFlag
{
	rwVIDEOMODEEXCLUSIVE  = rw::VIDEOMODEEXCLUSIVE,
/*
	rwVIDEOMODEINTERLACE  = 0x2,
	rwVIDEOMODEFFINTERLACE  = 0x4,
	rwVIDEOMODEFSAA0 = 0x8,
	rwVIDEOMODEFSAA1 = 0x10
*/
};

typedef rw::VideoMode RwVideoMode;

#if 0
struct RwFileFunctions
{
	rwFnFexist  rwfexist; /**< Pointer to fexist function */
	rwFnFopen   rwfopen;  /**< Pointer to fopen function */
	rwFnFclose  rwfclose; /**< Pointer to fclose function */
	rwFnFread   rwfread;  /**< Pointer to fread function */
	rwFnFwrite  rwfwrite; /**< Pointer to fwrite function */
	rwFnFgets   rwfgets;  /**< Pointer to fgets function */
	rwFnFputs   rwfputs;  /**< Pointer to puts function */
	rwFnFeof    rwfeof;   /**< Pointer to feof function */
	rwFnFseek   rwfseek;  /**< Pointer to fseek function */
	rwFnFflush  rwfflush; /**< Pointer to fflush function */
	rwFnFtell   rwftell;  /**< Pointer to ftell function */
};
RwFileFunctions *RwOsGetFileInterface(void);
#endif

RwBool RwEngineInit(RwMemoryFunctions *memFuncs, RwUInt32 initFlags, RwUInt32 resArenaSize);
RwInt32 RwEngineRegisterPlugin(RwInt32 size, RwUInt32 pluginID, RwPluginObjectConstructor initCB, RwPluginObjectDestructor termCB);
RwInt32 RwEngineGetPluginOffset(RwUInt32 pluginID);
RwBool RwEngineOpen(RwEngineOpenParams *initParams);
RwBool RwEngineStart(void);
RwBool RwEngineStop(void);
RwBool RwEngineClose(void);
RwBool RwEngineTerm(void);
RwInt32 RwEngineGetNumSubSystems(void);
RwSubSystemInfo *RwEngineGetSubSystemInfo(RwSubSystemInfo *subSystemInfo, RwInt32 subSystemIndex);
RwInt32 RwEngineGetCurrentSubSystem(void);
RwBool RwEngineSetSubSystem(RwInt32 subSystemIndex);
RwInt32 RwEngineGetNumVideoModes(void);
RwVideoMode *RwEngineGetVideoModeInfo(RwVideoMode *modeinfo, RwInt32 modeIndex);
RwInt32 RwEngineGetCurrentVideoMode(void);
RwBool RwEngineSetVideoMode(RwInt32 modeIndex);
RwInt32 RwEngineGetTextureMemorySize(void);
RwInt32 RwEngineGetMaxTextureSize(void);

/*
 ***********************************************
 *
 * Binary stream
 *
 ***********************************************
 */

RwBool RwStreamFindChunk(RwStream *stream, RwUInt32 type, RwUInt32 *lengthOut, RwUInt32 *versionOut);


code:
#include "common.h"

CMatrix::CMatrix(void)
{
	m_attachment = nil;
	m_hasRwMatrix = false;
}

CMatrix::CMatrix(CMatrix const &m)
{
	m_attachment = nil;
	m_hasRwMatrix = false;
	*this = m;
}

CMatrix::CMatrix(RwMatrix *matrix, bool owner)
{
	m_attachment = nil;
	Attach(matrix, owner);
}

CMatrix::~CMatrix(void)
{
	if (m_hasRwMatrix && m_attachment)
		RwMatrixDestroy(m_attachment);
}

void
CMatrix::Attach(RwMatrix *matrix, bool owner)
{
#ifdef FIX_BUGS
	if (m_attachment && m_hasRwMatrix)
#else
	if (m_hasRwMatrix && m_attachment)
#endif
		RwMatrixDestroy(m_attachment);
	m_attachment = matrix;
	m_hasRwMatrix = owner;
	Update();
}

void
CMatrix::AttachRW(RwMatrix *matrix, bool owner)
{
	if (m_hasRwMatrix && m_attachment)
		RwMatrixDestroy(m_attachment);
	m_attachment = matrix;
	m_hasRwMatrix = owner;
	UpdateRW();
}

void
CMatrix::Detach(void)
{
	if (m_hasRwMatrix && m_attachment)
		RwMatrixDestroy(m_attachment);
	m_attachment = nil;
}

void
CMatrix::Update(void)
{
	GetRight() = m_attachment->right;
	GetForward() = m_attachment->up;
	GetUp() = m_attachment->at;
	GetPosition() = m_attachment->pos;
}

void
CMatrix::UpdateRW(void)
{
	if (m_attachment) {
		m_attachment->right = GetRight();
		m_attachment->up = GetForward();
		m_attachment->at = GetUp();
		m_attachment->pos = GetPosition();
		RwMatrixUpdate(m_attachment);
	}
}

void
CMatrix::operator=(CMatrix const &rhs)
{
	memcpy(this, &rhs, sizeof(f));
	if (m_attachment)
		UpdateRW();
}

void
CMatrix::CopyOnlyMatrix(const CMatrix &other)
{
	memcpy(this, &other, sizeof(f));
}

CMatrix &
CMatrix::operator+=(CMatrix const &rhs)
{
	GetRight() += rhs.GetRight();
	GetForward() += rhs.GetForward();
	GetUp() += rhs.GetUp();
	GetPosition() += rhs.GetPosition();
	return *this;
}

void
CMatrix::SetUnity(void)
{
	rx = 1.0f;
	ry = 0.0f;
	rz = 0.0f;
	fx = 0.0f;
	fy = 1.0f;
	fz = 0.0f;
	ux = 0.0f;
	uy = 0.0f;
	uz = 1.0f;
	px = 0.0f;
	py = 0.0f;
	pz = 0.0f;
}

void
CMatrix::ResetOrientation(void)
{
	rx = 1.0f;
	ry = 0.0f;
	rz = 0.0f;
	fx = 0.0f;
	fy = 1.0f;
	fz = 0.0f;
	ux = 0.0f;
	uy = 0.0f;
	uz = 1.0f;
}

void
CMatrix::SetScale(float s)
{
	rx = s;
	ry = 0.0f;
	rz = 0.0f;

	fx = 0.0f;
	fy = s;
	fz = 0.0f;

	ux = 0.0f;
	uy = 0.0f;
	uz = s;

	px = 0.0f;
	py = 0.0f;
	pz = 0.0f;
}

void
CMatrix::SetTranslate(float x, float y, float z)
{
	rx = 1.0f;
	ry = 0.0f;
	rz = 0.0f;

	fx = 0.0f;
	fy = 1.0f;
	fz = 0.0f;

	ux = 0.0f;
	uy = 0.0f;
	uz = 1.0f;

	px = x;
	py = y;
	pz = z;
}

void
CMatrix::SetRotateXOnly(float angle)
{
	float c = Cos(angle);
	float s = Sin(angle);

	rx = 1.0f;
	ry = 0.0f;
	rz = 0.0f;

	fx = 0.0f;
	fy = c;
	fz = s;

	ux = 0.0f;
	uy = -s;
	uz = c;
}

void
CMatrix::SetRotateYOnly(float angle)
{
	float c = Cos(angle);
	float s = Sin(angle);

	rx = c;
	ry = 0.0f;
	rz = -s;

	fx = 0.0f;
	fy = 1.0f;
	fz = 0.0f;

	ux = s;
	uy = 0.0f;
	uz = c;
}

void
CMatrix::SetRotateZOnly(float angle)
{
	float c = Cos(angle);
	float s = Sin(angle);

	rx = c;
	ry = s;
	rz = 0.0f;

	fx = -s;
	fy = c;
	fz = 0.0f;

	ux = 0.0f;
	uy = 0.0f;
	uz = 1.0f;
}

void
CMatrix::SetRotateX(float angle)
{
	SetRotateXOnly(angle);
	px = 0.0f;
	py = 0.0f;
	pz = 0.0f;
}

void
CMatrix::SetRotateY(float angle)
{
	SetRotateYOnly(angle);
	px = 0.0f;
	py = 0.0f;
	pz = 0.0f;
}

void
CMatrix::SetRotateZ(float angle)
{
	SetRotateZOnly(angle);
	px = 0.0f;
	py = 0.0f;
	pz = 0.0f;
}

void
CMatrix::SetRotate(float xAngle, float yAngle, float zAngle)
{
	float cX = Cos(xAngle);
	float sX = Sin(xAngle);
	float cY = Cos(yAngle);
	float sY = Sin(yAngle);
	float cZ = Cos(zAngle);
	float sZ = Sin(zAngle);

	rx = cZ * cY - (sZ * sX) * sY;
	ry = (cZ * sX) * sY + sZ * cY;
	rz = -cX * sY;

	fx = -sZ * cX;
	fy = cZ * cX;
	fz = sX;

	ux = (sZ * sX) * cY + cZ * sY;
	uy = sZ * sY - (cZ * sX) * cY;
	uz = cX * cY;

	px = 0.0f;
	py = 0.0f;
	pz = 0.0f;
}

void
CMatrix::RotateX(float x)
{
	float c = Cos(x);
	float s = Sin(x);

	float ry = this->ry;
	float rz = this->rz;
	float uy = this->fy;
	float uz = this->fz;
	float ay = this->uy;
	float az = this->uz;
	float py = this->py;
	float pz = this->pz;

	this->ry = c * ry - s * rz;
	this->rz = c * rz + s * ry;
	this->fy = c * uy - s * uz;
	this->fz = c * uz + s * uy;
	this->uy = c * ay - s * az;
	this->uz = c * az + s * ay;
	this->py = c * py - s * pz;
	this->pz = c * pz + s * py;
}

void
CMatrix::RotateY(float y)
{
	float c = Cos(y);
	float s = Sin(y);

	float rx = this->rx;
	float rz = this->rz;
	float ux = this->fx;
	float uz = this->fz;
	float ax = this->ux;
	float az = this->uz;
	float px = this->px;
	float pz = this->pz;

	this->rx = c * rx + s * rz;
	this->rz = c * rz - s * rx;
	this->fx = c * ux + s * uz;
	this->fz = c * uz - s * ux;
	this->ux = c * ax + s * az;
	this->uz = c * az - s * ax;
	this->px = c * px + s * pz;
	this->pz = c * pz - s * px;
}

void
CMatrix::RotateZ(float z)
{
	float c = Cos(z);
	float s = Sin(z);

	float ry = this->ry;
	float rx = this->rx;
	float uy = this->fy;
	float ux = this->fx;
	float ay = this->uy;
	float ax = this->ux;
	float py = this->py;
	float px = this->px;

	this->rx = c * rx - s * ry;
	this->ry = c * ry + s * rx;
	this->fx = c * ux - s * uy;
	this->fy = c * uy + s * ux;
	this->ux = c * ax - s * ay;
	this->uy = c * ay + s * ax;
	this->px = c * px - s * py;
	this->py = c * py + s * px;

}

void
CMatrix::Rotate(float x, float y, float z)
{
	float cX = Cos(x);
	float sX = Sin(x);
	float cY = Cos(y);
	float sY = Sin(y);
	float cZ = Cos(z);
	float sZ = Sin(z);

	float rx = this->rx;
	float ry = this->ry;
	float rz = this->rz;
	float ux = this->fx;
	float uy = this->fy;
	float uz = this->fz;
	float ax = this->ux;
	float ay = this->uy;
	float az = this->uz;
	float px = this->px;
	float py = this->py;
	float pz = this->pz;

	float x1 = cZ * cY - (sZ * sX) * sY;
	float x2 = (cZ * sX) * sY + sZ * cY;
	float x3 = -cX * sY;
	float y1 = -sZ * cX;
	float y2 = cZ * cX;
	float y3 = sX;
	float z1 = (sZ * sX) * cY + cZ * sY;
	float z2 = sZ * sY - (cZ * sX) * cY;
	float z3 = cX * cY;

	this->rx = x1 * rx + y1 * ry + z1 * rz;
	this->ry = x2 * rx + y2 * ry + z2 * rz;
	this->rz = x3 * rx + y3 * ry + z3 * rz;
	this->fx = x1 * ux + y1 * uy + z1 * uz;
	this->fy = x2 * ux + y2 * uy + z2 * uz;
	this->fz = x3 * ux + y3 * uy + z3 * uz;
	this->ux = x1 * ax + y1 * ay + z1 * az;
	this->uy = x2 * ax + y2 * ay + z2 * az;
	this->uz = x3 * ax + y3 * ay + z3 * az;
	this->px = x1 * px + y1 * py + z1 * pz;
	this->py = x2 * px + y2 * py + z2 * pz;
	this->pz = x3 * px + y3 * py + z3 * pz;
}

CMatrix &
CMatrix::operator*=(CMatrix const &rhs)
{
	// TODO: VU0 code
	*this = *this * rhs;
	return *this;
}

void
CMatrix::Reorthogonalise(void)
{
	CVector &r = GetRight();
	CVector &f = GetForward();
	CVector &u = GetUp();
	u = CrossProduct(r, f);
	u.Normalise();
	r = CrossProduct(f, u);
	r.Normalise();
	f = CrossProduct(u, r);
}

CMatrix
operator*(const CMatrix &m1, const CMatrix &m2)
{
	// TODO: VU0 code
	CMatrix out;
	out.rx = m1.rx * m2.rx + m1.fx * m2.ry + m1.ux * m2.rz;
	out.ry = m1.ry * m2.rx + m1.fy * m2.ry + m1.uy * m2.rz;
	out.rz = m1.rz * m2.rx + m1.fz * m2.ry + m1.uz * m2.rz;
	out.fx = m1.rx * m2.fx + m1.fx * m2.fy + m1.ux * m2.fz;
	out.fy = m1.ry * m2.fx + m1.fy * m2.fy + m1.uy * m2.fz;
	out.fz = m1.rz * m2.fx + m1.fz * m2.fy + m1.uz * m2.fz;
	out.ux = m1.rx * m2.ux + m1.fx * m2.uy + m1.ux * m2.uz;
	out.uy = m1.ry * m2.ux + m1.fy * m2.uy + m1.uy * m2.uz;
	out.uz = m1.rz * m2.ux + m1.fz * m2.uy + m1.uz * m2.uz;
	out.px = m1.rx * m2.px + m1.fx * m2.py + m1.ux * m2.pz + m1.px;
	out.py = m1.ry * m2.px + m1.fy * m2.py + m1.uy * m2.pz + m1.py;
	out.pz = m1.rz * m2.px + m1.fz * m2.py + m1.uz * m2.pz + m1.pz;
	return out;
}

CMatrix &
Invert(const CMatrix &src, CMatrix &dst)
{
	// TODO: VU0 code
	// GTA handles this as a raw 4x4 orthonormal matrix
	// and trashes the RW flags, let's not do that
	dst.f[3][0] = dst.f[3][1] = dst.f[3][2] = 0.0f;
#ifndef FIX_BUGS
	dst.f[3][3] = src.f[3][3];
#endif

	dst.f[0][0] = src.f[0][0];
	dst.f[0][1] = src.f[1][0];
	dst.f[0][2] = src.f[2][0];
#ifndef FIX_BUGS
	dst.f[0][3] = src.f[3][0];
#endif
	dst.f[1][0] = src.f[0][1];
	dst.f[1][1] = src.f[1][1];
	dst.f[1][2] = src.f[2][1];
#ifndef FIX_BUGS
	dst.f[1][3] = src.f[3][1];
#endif
	dst.f[2][0] = src.f[0][2];
	dst.f[2][1] = src.f[1][2];
	dst.f[2][2] = src.f[2][2];
#ifndef FIX_BUGS
	dst.f[2][3] = src.f[3][2];
#endif

	dst.f[3][0] += dst.f[0][0] * src.f[3][0];
	dst.f[3][1] += dst.f[0][1] * src.f[3][0];
	dst.f[3][2] += dst.f[0][2] * src.f[3][0];
#ifndef FIX_BUGS
	dst.f[3][3] += dst.f[0][3] * src.f[3][0];
#endif

	dst.f[3][0] += dst.f[1][0] * src.f[3][1];
	dst.f[3][1] += dst.f[1][1] * src.f[3][1];
	dst.f[3][2] += dst.f[1][2] * src.f[3][1];
#ifndef FIX_BUGS
	dst.f[3][3] += dst.f[1][3] * src.f[3][1];
#endif

	dst.f[3][0] += dst.f[2][0] * src.f[3][2];
	dst.f[3][1] += dst.f[2][1] * src.f[3][2];
	dst.f[3][2] += dst.f[2][2] * src.f[3][2];
#ifndef FIX_BUGS
	dst.f[3][3] += dst.f[2][3] * src.f[3][2];
#endif

	dst.f[3][0] = -dst.f[3][0];
	dst.f[3][1] = -dst.f[3][1];
	dst.f[3][2] = -dst.f[3][2];
#ifndef FIX_BUGS
	dst.f[3][3] = src.f[3][3] - dst.f[3][3];
#endif

	return dst;
}

CMatrix
Invert(const CMatrix &matrix)
{
	CMatrix inv;
	return Invert(matrix, inv);
}

void
CCompressedMatrixNotAligned::CompressFromFullMatrix(CMatrix &other)
{
	m_rightX = 127.0f * other.GetRight().x;
	m_rightY = 127.0f * other.GetRight().y;
	m_rightZ = 127.0f * other.GetRight().z;
	m_upX = 127.0f * other.GetForward().x;
	m_upY = 127.0f * other.GetForward().y;
	m_upZ = 127.0f * other.GetForward().z;
	m_vecPos = other.GetPosition();
}

void
CCompressedMatrixNotAligned::DecompressIntoFullMatrix(CMatrix &other)
{
	other.GetRight().x = m_rightX / 127.0f;
	other.GetRight().y = m_rightY / 127.0f;
	other.GetRight().z = m_rightZ / 127.0f;
	other.GetForward().x = m_upX / 127.0f;
	other.GetForward().y = m_upY / 127.0f;
	other.GetForward().z = m_upZ / 127.0f;
	other.GetUp() = CrossProduct(other.GetRight(), other.GetForward());
	other.GetPosition() = m_vecPos;
	other.Reorthogonalise();
}

license: mit


code:
#pragma once

class CMatrix
{
public:
	union
	{
		float f[4][4];
		struct
		{
			float rx, ry, rz, rw;
			float fx, fy, fz, fw;
			float ux, uy, uz, uw;
			float px, py, pz, pw;
		};
	};

	RwMatrix *m_attachment;
	bool m_hasRwMatrix;	// are we the owner?

	CMatrix(void);
	CMatrix(CMatrix const &m);
	CMatrix(RwMatrix *matrix, bool owner = false);
	CMatrix(float scale){
		m_attachment = nil;
		m_hasRwMatrix = false;
		SetScale(scale);
	}
	~CMatrix(void);
	void Attach(RwMatrix *matrix, bool owner = false);
	void AttachRW(RwMatrix *matrix, bool owner = false);
	void Detach(void);
	void Update(void);
	void UpdateRW(void);
	void operator=(CMatrix const &rhs);
	CMatrix &operator+=(CMatrix const &rhs);
	CMatrix &operator*=(CMatrix const &rhs);

	CVector &GetPosition(void) { return *(CVector*)&px; }
	CVector &GetRight(void) { return *(CVector*)℞ }
	CVector &GetForward(void) { return *(CVector*)&fx; }
	CVector &GetUp(void) { return *(CVector*)&ux; }

	const CVector &GetPosition(void) const { return *(CVector*)&px; }
	const CVector &GetRight(void) const { return *(CVector*)℞ }
	const CVector &GetForward(void) const { return *(CVector*)&fx; }
	const CVector &GetUp(void) const { return *(CVector*)&ux; }

	void SetTranslate(float x, float y, float z);
	void SetTranslate(const CVector &trans){ SetTranslate(trans.x, trans.y, trans.z); }
	void Translate(float x, float y, float z){
		px += x;
		py += y;
		pz += z;
	}
	void Translate(const CVector &trans){ Translate(trans.x, trans.y, trans.z); }

	void SetScale(float s);
	void Scale(float scale)
	{
		for (int i = 0; i < 3; i++)
#ifdef FIX_BUGS // BUGFIX from VC
			for (int j = 0; j < 3; j++)
#else
			for (int j = 0; j < 4; j++)
#endif
				f[i][j] *= scale;
	}

	void SetRotateXOnly(float angle);
	void SetRotateYOnly(float angle);
	void SetRotateZOnly(float angle);
	void SetRotateX(float angle);
	void SetRotateY(float angle);
	void SetRotateZ(float angle);
	void SetRotate(float xAngle, float yAngle, float zAngle);
	void Rotate(float x, float y, float z);
	void RotateX(float x);
	void RotateY(float y);
	void RotateZ(float z);

	void Reorthogonalise(void);
	void CopyOnlyMatrix(const CMatrix &other);
	void SetUnity(void);
	void ResetOrientation(void);
	void SetTranslateOnly(float x, float y, float z) {
		px = x;
		py = y;
		pz = z;
	}
	void SetTranslateOnly(const CVector& pos) {
		SetTranslateOnly(pos.x, pos.y, pos.z);
	}
	void CheckIntegrity(){}
};

CMatrix &Invert(const CMatrix &src, CMatrix &dst);
CMatrix Invert(const CMatrix &matrix);
CMatrix operator*(const CMatrix &m1, const CMatrix &m2);
inline CVector MultiplyInverse(const CMatrix &mat, const CVector &vec)
{
	CVector v(vec.x - mat.px, vec.y - mat.py, vec.z - mat.pz);
	return CVector(
		mat.rx * v.x + mat.ry * v.y + mat.rz * v.z,
		mat.fx * v.x + mat.fy * v.y + mat.fz * v.z,
		mat.ux * v.x + mat.uy * v.y + mat.uz * v.z);
}

class CCompressedMatrixNotAligned
{
	CVector m_vecPos;
	int8 m_rightX;
	int8 m_rightY;
	int8 m_rightZ;
	int8 m_upX;
	int8 m_upY;
	int8 m_upZ;
public:
	void CompressFromFullMatrix(CMatrix &other);
	void DecompressIntoFullMatrix(CMatrix &other);
};

class CCompressedMatrix : public CCompressedMatrixNotAligned
{
	int _alignment; // no clue what would this align to
};


code:
#include "common.h"
#include "Quaternion.h"

void
CQuaternion::Normalise(void)
{
	float sq = MagnitudeSqr();
	if (sq == 0.0f)
		w = 1.0f;
	else {
		float invsqrt = RecipSqrt(sq);
		x *= invsqrt;
		y *= invsqrt;
		z *= invsqrt;
		w *= invsqrt;
	}
}

void
CQuaternion::Slerp(const CQuaternion &q1, const CQuaternion &q2, float theta, float invSin, float t)
{
	if (theta == 0.0f)
		*this = q2;
	else {
		float w1, w2;
		if (theta > PI / 2) {
			theta = PI - theta;
			w1 = Sin((1.0f - t) * theta) * invSin;
			w2 = -Sin(t * theta) * invSin;
		} else {
			w1 = Sin((1.0f - t) * theta) * invSin;
			w2 = Sin(t * theta) * invSin;
		}
		// TODO: VU0 code
		*this = w1 * q1 + w2 * q2;
	}
}

void
CQuaternion::Multiply(const CQuaternion &q1, const CQuaternion &q2)
{
	x = (q2.z * q1.y) - (q1.z * q2.y) + (q1.x * q2.w) + (q2.x * q1.w);
	y = (q2.x * q1.z) - (q1.x * q2.z) + (q1.y * q2.w) + (q2.y * q1.w);
	z = (q2.y * q1.x) - (q1.y * q2.x) + (q1.z * q2.w) + (q2.z * q1.w);
	w = (q2.w * q1.w) - (q2.x * q1.x) - (q2.y * q1.y) - (q2.z * q1.z);
}

void
CQuaternion::Get(RwV3d *axis, float *angle)
{
	*angle = Acos(w);
	float s = Sin(*angle);

	axis->x = x * (1.0f / s);
	axis->y = y * (1.0f / s);
	axis->z = z * (1.0f / s);
}

void
CQuaternion::Set(RwV3d *axis, float angle)
{
	float halfCos = Cos(angle * 0.5f);
	float halfSin = Sin(angle * 0.5f);
	x = axis->x * halfSin;
	y = axis->y * halfSin;
	z = axis->z * halfSin;
	w = halfCos;
}

void
CQuaternion::Get(RwMatrix *matrix)
{
	float x2 = x + x;
	float y2 = y + y;
	float z2 = z + z;

	float x_2x = x * x2;
	float x_2y = x * y2;
	float x_2z = x * z2;
	float y_2y = y * y2;
	float y_2z = y * z2;
	float z_2z = z * z2;
	float w_2x = w * x2;
	float w_2y = w * y2;
	float w_2z = w * z2;

	matrix->right.x = 1.0f - (y_2y + z_2z);
	matrix->up.x = x_2y - w_2z;
	matrix->at.x = x_2z + w_2y;
	matrix->right.y = x_2y + w_2z;
	matrix->up.y = 1.0f - (x_2x + z_2z);
	matrix->at.y = y_2z - w_2x;
	matrix->right.z = x_2z - w_2y;
	matrix->up.z = y_2z + w_2x;
	matrix->at.z = 1.0f - (x_2x + y_2y);
}

void
CQuaternion::Set(const RwMatrix &matrix)
{
	float f, s, m;

	f = matrix.up.y + matrix.right.x + matrix.at.z;
	if (f >= 0.0f) {
		s = Sqrt(f + 1.0f);
		w = 0.5f * s;
		m = 0.5f / s;
		x = (matrix.up.z - matrix.at.y) * m;
		y = (matrix.at.x - matrix.right.z) * m;
		z = (matrix.right.y - matrix.up.x) * m;
		return;
	}

	f = matrix.right.x - matrix.up.y - matrix.at.z;
	if (f >= 0.0f) {
		s = Sqrt(f + 1.0f);
		x = 0.5f * s;
		m = 0.5f / s;
		y = (matrix.up.x + matrix.right.y) * m;
		z = (matrix.at.x + matrix.right.z) * m;
		w = (matrix.up.z - matrix.at.y) * m;
		return;
	}

	f = matrix.up.y - matrix.right.x - matrix.at.z;
	if (f >= 0.0f) {
		s = Sqrt(f + 1.0f);
		y = 0.5f * s;
		m = 0.5f / s;
		w = (matrix.at.x - matrix.right.z) * m;
		x = (matrix.up.x - matrix.right.y) * m;
		z = (matrix.at.y + matrix.up.z) * m;
		return;
	}

	f = matrix.at.z - (matrix.up.y + matrix.right.x);
	s = Sqrt(f + 1.0f);
	z = 0.5f * s;
	m = 0.5f / s;
	w = (matrix.right.y - matrix.up.x) * m;
	x = (matrix.at.x + matrix.right.z) * m;
	y = (matrix.at.y + matrix.up.z) * m;
}

void
CQuaternion::Get(float *f1, float *f2, float *f3)
{
	RwMatrix matrix;

	Get(&matrix);
	*f3 = Atan2(matrix.right.y, matrix.up.y);
	if (*f3 < 0.0f)
		*f3 += TWOPI;
	float s = Sin(*f3);
	float c = Cos(*f3);
	*f1 = Atan2(-matrix.at.y, s * matrix.right.y + c * matrix.up.y);
	if (*f1 < 0.0f)
		*f1 += TWOPI;
	*f2 = Atan2(-(matrix.right.z * c - matrix.up.z * s), matrix.right.x * c - matrix.up.x * s);
	if (*f2 < 0.0f)
		*f2 += TWOPI;
}

void
CQuaternion::Set(float f1, float f2, float f3)
{
	float c1 = Cos(f1 * 0.5f);
	float c2 = Cos(f2 * 0.5f);
	float c3 = Cos(f3 * 0.5f);
	float s1 = Sin(f1 * 0.5f);
	float s2 = Sin(f2 * 0.5f);
	float s3 = Sin(f3 * 0.5f);
	x = ((c2 * c1) * s3) - ((s2 * s1) * c3);
	y = ((s1 * c2) * c3) + ((s2 * c1) * s3);
	z = ((s2 * c1) * c3) - ((s1 * c2) * s3);
	w = ((c2 * c1) * c3) + ((s2 * s1) * s3);
}


code:
#pragma once

// TODO: actually implement this
class CQuaternion
{
public:
	float x, y, z, w;
	CQuaternion(void) {}
	CQuaternion(float x, float y, float z, float w) : x(x), y(y), z(z), w(w) {}

	float Magnitude(void) const { return Sqrt(x*x + y*y + z*z + w*w); }
	float MagnitudeSqr(void) const { return x*x + y*y + z*z + w*w; }
	void Normalise(void);
	void Multiply(const CQuaternion &q1, const CQuaternion &q2);
	void Invert(void){	// Conjugate would have been a better name
		x = -x;
		y = -y;
		z = -z;
	}

	const CQuaternion &operator+=(CQuaternion const &right) {
		x += right.x;
		y += right.y;
		z += right.z;
		w += right.w;
		return *this;
	}

	const CQuaternion &operator-=(CQuaternion const &right) {
		x -= right.x;
		y -= right.y;
		z -= right.z;
		w -= right.w;
		return *this;
	}

	const CQuaternion &operator*=(float right) {
		x *= right;
		y *= right;
		z *= right;
		w *= right;
		return *this;
	}

	const CQuaternion &operator/=(float right) {
		x /= right;
		y /= right;
		z /= right;
		w /= right;
		return *this;
	}

	CQuaternion operator-() const {
		return CQuaternion(-x, -y, -z, -w);
	}

	void Slerp(const CQuaternion &q1, const CQuaternion &q2, float theta, float invSin, float t);
	void Get(RwV3d *axis, float *angle);
	void Set(RwV3d *axis, float angle);
	void Get(RwMatrix *matrix);
	void Set(const RwMatrix &matrix);
	void Set(float f1, float f2, float f3);
	void Get(float *f1, float *f2, float *f3);
};

inline float
DotProduct(const CQuaternion &q1, const CQuaternion &q2)
{
	return q1.x*q2.x + q1.y*q2.y + q1.z*q2.z + q1.w*q2.w;
}

inline CQuaternion operator+(const CQuaternion &left, const CQuaternion &right)
{
	return CQuaternion(left.x + right.x, left.y + right.y, left.z + right.z, left.w + right.w);
}

inline CQuaternion operator-(const CQuaternion &left, const CQuaternion &right)
{
	return CQuaternion(left.x - right.x, left.y - right.y, left.z - right.z, left.w - right.w);
}

inline CQuaternion operator*(const CQuaternion &left, float right)
{
	return CQuaternion(left.x * right, left.y * right, left.z * right, left.w * right);
}

inline CQuaternion operator*(float left, const CQuaternion &right)
{
	return CQuaternion(left * right.x, left * right.y, left * right.z, left * right.w);
}

inline CQuaternion operator/(const CQuaternion &left, float right)
{
	return CQuaternion(left.x / right, left.y / right, left.z / right, left.w / right);
}


code:
#include "common.h"

CRect::CRect(void)
{
	left = 1000000.0f;
	top = 1000000.0f;
	right = -1000000.0f;
	bottom = -1000000.0f;
}

CRect::CRect(float l, float t, float r, float b)
{
	left = l;
	top = t;
	right = r;
	bottom = b;
}


code:
#pragma once

class CRect
{
public:
	float left;     // x min
	float bottom;   // y max
	float right;    // x max
	float top;      // y min

	CRect(void);
	CRect(float l, float t, float r, float b);
	void ContainPoint(CVector const &v){
		if(v.x < left) left = v.x;
		if(v.x > right) right = v.x;
		if(v.y < top) top = v.y;
		if(v.y > bottom) bottom = v.y;
	}
	void ContainRect(const CRect &r){
		if(r.left < left) left = r.left;
		if(r.right > right) right = r.right;
		if(r.top < top) top = r.top;
		if(r.bottom > bottom) bottom = r.bottom;
	}

	bool IsPointInside(const CVector2D &p){
		return p.x >= left &&
			p.x <= right &&
			p.y >= top &&
			p.y <= bottom;
	}
	bool IsPointInside(const CVector2D &p, float extraRadius){
		return p.x >= left-extraRadius &&
			p.x <= right+extraRadius &&
			p.y >= top-extraRadius &&
			p.y <= bottom+extraRadius;
	}

	void Translate(float x, float y){
		left += x;
		right += x;
		bottom += y;
		top += y;
	}

	void Grow(float r) {
		left -= r;
		right += r;
		top -= r;
		bottom += r;
	}

	void Grow(float l, float r)
	{
		left -= l;
		top -= l;
		right += r;
		bottom += r;
	}

	void Grow(float l, float r, float t, float b)
	{
		left -= l;
		top -= t;
		right += r;
		bottom += b;
	}

	float GetWidth(void) { return right - left; }
	float GetHeight(void) { return bottom - top; }
};


code:
#include "common.h"

void
CVector::Normalise(void)
{
	float sq = MagnitudeSqr();
	if (sq > 0.0f) {
		float invsqrt = RecipSqrt(sq);
		x *= invsqrt;
		y *= invsqrt;
		z *= invsqrt;
	} else
		x = 1.0f;
}

CVector
CrossProduct(const CVector &v1, const CVector &v2)
{
	return CVector(v1.y * v2.z - v1.z * v2.y, v1.z * v2.x - v1.x * v2.z, v1.x * v2.y - v1.y * v2.x);
}

CVector
Multiply3x3(const CMatrix &mat, const CVector &vec)
{
	// TODO: VU0 code
	return CVector(mat.rx * vec.x + mat.fx * vec.y + mat.ux * vec.z,
	               mat.ry * vec.x + mat.fy * vec.y + mat.uy * vec.z,
	               mat.rz * vec.x + mat.fz * vec.y + mat.uz * vec.z);
}

CVector
Multiply3x3(const CVector &vec, const CMatrix &mat)
{
	return CVector(mat.rx * vec.x + mat.ry * vec.y + mat.rz * vec.z,
	               mat.fx * vec.x + mat.fy * vec.y + mat.fz * vec.z,
	               mat.ux * vec.x + mat.uy * vec.y + mat.uz * vec.z);
}

CVector
operator*(const CMatrix &mat, const CVector &vec)
{
	// TODO: VU0 code
	return CVector(mat.rx * vec.x + mat.fx * vec.y + mat.ux * vec.z + mat.px,
	               mat.ry * vec.x + mat.fy * vec.y + mat.uy * vec.z + mat.py,
	               mat.rz * vec.x + mat.fz * vec.y + mat.uz * vec.z + mat.pz);
}


code:
#pragma once

class CVector : public RwV3d
{
public:
	CVector(void) {}
	CVector(float x, float y, float z)
	{
		this->x = x;
		this->y = y;
		this->z = z;
	}

	CVector(const RwV3d &v)
	{
		x = v.x;
		y = v.y;
		z = v.z;
	}
	// (0,1,0) means no rotation. So get right vector and its atan
	float Heading(void) const { return Atan2(-x, y); }
	float Magnitude(void) const { return Sqrt(x*x + y*y + z*z); }
	float MagnitudeSqr(void) const { return x*x + y*y + z*z; }
	float Magnitude2D(void) const { return Sqrt(x*x + y*y); }
	float MagnitudeSqr2D(void) const { return x*x + y*y; }
	void Normalise(void);

	void Normalise2D(void) {
		float sq = MagnitudeSqr2D();
		float invsqrt = RecipSqrt(sq);
		x *= invsqrt;
		y *= invsqrt;
	}

	const CVector &operator+=(CVector const &right) {
		x += right.x;
		y += right.y;
		z += right.z;
		return *this;
	}

	const CVector &operator-=(CVector const &right) {
		x -= right.x;
		y -= right.y;
		z -= right.z;
		return *this;
	}

	const CVector &operator*=(float right) {
		x *= right;
		y *= right;
		z *= right;
		return *this;
	}

	const CVector &operator/=(float right) {
		x /= right;
		y /= right;
		z /= right;
		return *this;
	}

	CVector operator-() const {
		return CVector(-x, -y, -z);
	}

	const bool operator==(CVector const &right) {
		return x == right.x && y == right.y && z == right.z;
	}

	const bool operator!=(CVector const &right) {
		return x != right.x || y != right.y || z != right.z;
	}

	bool IsZero(void) const { return x == 0.0f && y == 0.0f && z == 0.0f; }
};

inline CVector operator+(const CVector &left, const CVector &right)
{
	return CVector(left.x + right.x, left.y + right.y, left.z + right.z);
}

inline CVector operator-(const CVector &left, const CVector &right)
{
	return CVector(left.x - right.x, left.y - right.y, left.z - right.z);
}

inline CVector operator*(const CVector &left, float right)
{
	return CVector(left.x * right, left.y * right, left.z * right);
}

inline CVector operator*(float left, const CVector &right)
{
	return CVector(left * right.x, left * right.y, left * right.z);
}

inline CVector operator/(const CVector &left, float right)
{
	return CVector(left.x / right, left.y / right, left.z / right);
}

inline float
DotProduct(const CVector &v1, const CVector &v2)
{
	return v1.x*v2.x + v1.y*v2.y + v1.z*v2.z;
}

CVector CrossProduct(const CVector &v1, const CVector &v2);

inline float
Distance(const CVector &v1, const CVector &v2)
{
	return (v2 - v1).Magnitude();
}

inline float
Distance2D(const CVector &v1, const CVector &v2)
{
	float x = v2.x - v1.x;
	float y = v2.y - v1.y;
	return Sqrt(x*x + y*y);
}

class CMatrix;

CVector Multiply3x3(const CMatrix &mat, const CVector &vec);
CVector Multiply3x3(const CVector &vec, const CMatrix &mat);
CVector operator*(const CMatrix &mat, const CVector &vec);


code:
#pragma once

class CVector2D
{
public:
	float x, y;
	CVector2D(void) {}
	CVector2D(float x, float y) : x(x), y(y) {}
	CVector2D(const CVector &v) : x(v.x), y(v.y) {}
	float Heading(void) const { return Atan2(-x, y); }
	float Magnitude(void) const { return Sqrt(x*x + y*y); }
	float MagnitudeSqr(void) const { return x*x + y*y; }

	void Normalise(void) {
		float sq = MagnitudeSqr();
		// assert(sq != 0.0f);	// just be safe here
		float invsqrt = RecipSqrt(sq);
		x *= invsqrt;
		y *= invsqrt;
	}

	void NormaliseSafe(void) {
		float sq = MagnitudeSqr();
		if(sq > 0.0f){
			float invsqrt = RecipSqrt(sq);
			x *= invsqrt;
			y *= invsqrt;
		}else
			x = 1.0f;
	}

	const CVector2D &operator+=(CVector2D const &right) {
		x += right.x;
		y += right.y;
		return *this;
	}

	const CVector2D &operator-=(CVector2D const &right) {
		x -= right.x;
		y -= right.y;
		return *this;
	}

	const CVector2D &operator*=(float right) {
		x *= right;
		y *= right;
		return *this;
	}

	const CVector2D &operator/=(float right) {
		x /= right;
		y /= right;
		return *this;
	}
	CVector2D operator-(const CVector2D &rhs) const {
		return CVector2D(x-rhs.x, y-rhs.y);
	}
	CVector2D operator+(const CVector2D &rhs) const {
		return CVector2D(x+rhs.x, y+rhs.y);
	}
	CVector2D operator/(float t) const {
		return CVector2D(x/t, y/t);
	}
};

inline float
DotProduct2D(const CVector2D &v1, const CVector2D &v2)
{
	return v1.x*v2.x + v1.y*v2.y;
}

inline float
CrossProduct2D(const CVector2D &v1, const CVector2D &v2)
{
	return v1.x*v2.y - v1.y*v2.x;
}

inline float
Distance2D(const CVector2D &v, float x, float y)
{
	return Sqrt((v.x-x)*(v.x-x) + (v.y-y)*(v.y-y));
}

inline float
DistanceSqr2D(const CVector2D &v, float x, float y)
{
	return (v.x-x)*(v.x-x) + (v.y-y)*(v.y-y);
}

inline void
NormalizeXY(float &x, float &y)
{
	float l  = Sqrt(x*x + y*y);
	if(l != 0.0f){
		x /= l;
		y /= l;
	}else
		x = 1.0f;
}

inline CVector2D operator*(const CVector2D &left, float right)
{
	return CVector2D(left.x * right, left.y * right);
}

inline CVector2D operator*(float left, const CVector2D &right)
{
	return CVector2D(left * right.x, left * right.y);
}


code:
#pragma once

class TYPEALIGN(16) CVuVector : public CVector
{
public:
	float w;
	CVuVector(void) {}
	CVuVector(float x, float y, float z) : CVector(x, y, z) {}
	CVuVector(float x, float y, float z, float w) : CVector(x, y, z), w(w) {}
	CVuVector(const CVector &v) : CVector(v.x, v.y, v.z) {}
	CVuVector(const RwV3d &v) : CVector(v) {}
/*
	void Normalise(void) {
		float sq = MagnitudeSqr();
		// TODO: VU0 code
		if(sq > 0.0f){
			float invsqrt = RecipSqrt(sq);
			x *= invsqrt;
			y *= invsqrt;
			z *= invsqrt;
		}else
			x = 1.0f;
	}
*/

	// TODO: operator-
};

void TransformPoint(CVuVector &out, const CMatrix &mat, const CVuVector &in);
void TransformPoint(CVuVector &out, const CMatrix &mat, const RwV3d &in);
void TransformPoints(CVuVector *out, int n, const CMatrix &mat, const RwV3d *in, int stride);
void TransformPoints(CVuVector *out, int n, const CMatrix &mat, const CVuVector *in);


code:
#include "common.h"

#include "VuVector.h"

// TODO: move more stuff into here

void TransformPoint(CVuVector &out, const CMatrix &mat, const CVuVector &in)
{
#ifdef GTA_PS2
	__asm__ __volatile__("\n\
		lqc2    vf01,0x0(%2)\n\
		lqc2    vf02,0x0(%1)\n\
		lqc2    vf03,0x10(%1)\n\
		lqc2    vf04,0x20(%1)\n\
		lqc2    vf05,0x30(%1)\n\
		vmulax.xyz	ACC,   vf02,vf01\n\
		vmadday.xyz	ACC,   vf03,vf01\n\
		vmaddaz.xyz	ACC,   vf04,vf01\n\
		vmaddw.xyz	vf06,vf05,vf00\n\
		sqc2    vf06,0x0(%0)\n\
		": : "r" (&out) , "r" (&mat) ,"r" (&in): "memory");
#else
	out = mat * in;
#endif
}

void TransformPoint(CVuVector &out, const CMatrix &mat, const RwV3d &in)
{
#ifdef GTA_PS2
	__asm__ __volatile__("\n\
		ldr	$8,0x0(%2)\n\
		ldl	$8,0x7(%2)\n\
		lw	$9,0x8(%2)\n\
		pcpyld	$10,$9,$8\n\
		qmtc2	$10,vf01\n\
		lqc2    vf02,0x0(%1)\n\
		lqc2    vf03,0x10(%1)\n\
		lqc2    vf04,0x20(%1)\n\
		lqc2    vf05,0x30(%1)\n\
		vmulax.xyz	ACC,   vf02,vf01\n\
		vmadday.xyz	ACC,   vf03,vf01\n\
		vmaddaz.xyz	ACC,   vf04,vf01\n\
		vmaddw.xyz	vf06,vf05,vf00\n\
		sqc2    vf06,0x0(%0)\n\
		": : "r" (&out) , "r" (&mat) ,"r" (&in): "memory");
#else
	out = mat * in;
#endif
}

void TransformPoints(CVuVector *out, int n, const CMatrix &mat, const RwV3d *in, int stride)
{
#ifdef GTA_PS3
	__asm__ __volatile__("\n\
		paddub	$3,%4,$0\n\
		lqc2    vf02,0x0(%2)\n\
		lqc2    vf03,0x10(%2)\n\
		lqc2    vf04,0x20(%2)\n\
		lqc2    vf05,0x30(%2)\n\
		ldr	$8,0x0(%3)\n\
		ldl	$8,0x7(%3)\n\
		lw	$9,0x8(%3)\n\
		pcpyld	$10,$9,$8\n\
		qmtc2	$10,vf01\n\
	1:	vmulax.xyz	ACC,   vf02,vf01\n\
		vmadday.xyz	ACC,   vf03,vf01\n\
		vmaddaz.xyz	ACC,   vf04,vf01\n\
		vmaddw.xyz	vf06,vf05,vf00\n\
		add	%3,%3,$3\n\
		ldr	$8,0x0(%3)\n\
		ldl	$8,0x7(%3)\n\
		lw	$9,0x8(%3)\n\
		pcpyld	$10,$9,$8\n\
		qmtc2	$10,vf01\n\
		addi	%1,%1,-1\n\
		addiu	%0,%0,0x10\n\
		sqc2    vf06,-0x10(%0)\n\
		bnez	%1,1b\n\
		": : "r" (out) , "r" (n), "r" (&mat), "r" (in), "r" (stride): "memory");
#else
	while(n--){
		*out = mat * *in;
		in = (RwV3d*)((uint8*)in + stride);
		out++;
	}
#endif
}

void TransformPoints(CVuVector *out, int n, const CMatrix &mat, const CVuVector *in)
{
#ifdef GTA_PS2
	__asm__ __volatile__("\n\
		lqc2    vf02,0x0(%2)\n\
		lqc2    vf03,0x10(%2)\n\
		lqc2    vf04,0x20(%2)\n\
		lqc2    vf05,0x30(%2)\n\
		lqc2    vf01,0x0(%3)\n\
		nop\n\
	1:	vmulax.xyz	ACC,   vf02,vf01\n\
		vmadday.xyz	ACC,   vf03,vf01\n\
		vmaddaz.xyz	ACC,   vf04,vf01\n\
		vmaddw.xyz	vf06,vf05,vf00\n\
		lqc2	vf01,0x10(%3)\n\
		addiu	%3,%3,0x10\n\
		addi	%1,%1,-1\n\
		addiu	%0,%0,0x10\n\
		sqc2    vf06,-0x10(%0)\n\
		bnez	%1,1b\n\
		": : "r" (out) , "r" (n), "r" (&mat) ,"r" (in): "memory");
#else
	while(n--){
		*out = mat * *in;
		in++;
		out++;
	}
#endif
}


code:
#pragma once

// wrapper around float versions of functions
// in gta they are in CMaths but that makes the code rather noisy

inline float Sin(float x) { return sinf(x); }
inline float Asin(float x) { return asinf(x); }
inline float Cos(float x) { return cosf(x); }
inline float Acos(float x) { return acosf(x); }
inline float Tan(float x) { return tanf(x); }
inline float Atan(float x) { return atanf(x); }
inline float Atan2(float y, float x) { return atan2f(y, x); }
inline float Abs(float x) { return fabsf(x); }
inline float Sqrt(float x) { return sqrtf(x); }
inline float RecipSqrt(float x, float y) { return x/Sqrt(y); }
inline float RecipSqrt(float x) { return RecipSqrt(1.0f, x); }
inline float Pow(float x, float y) { return powf(x, y); }
inline float Floor(float x) { return floorf(x); }
inline float Ceil(float x) { return ceilf(x); }


code:
#include "common.h"

#include "templates.h"
#include "TxdStore.h"
#include "2dEffect.h"
#include "BaseModelInfo.h"
#include "ColModel.h"

CBaseModelInfo::CBaseModelInfo(ModelInfoType type)
{
	m_colModel = nil;
	m_twodEffects = nil;
	m_objectId = -1;
	m_refCount = 0;
	m_txdSlot = -1;
	m_type = type;
	m_num2dEffects = 0;
	m_bOwnsColModel = false;
}

void
CBaseModelInfo::Shutdown(void)
{
	DeleteCollisionModel();
	DeleteRwObject();
	m_twodEffects = nil;
	m_num2dEffects = 0;
	m_txdSlot = -1;
}

void
CBaseModelInfo::DeleteCollisionModel(void)
{
	if(m_colModel && m_bOwnsColModel){
		if(m_colModel)
			delete m_colModel;
		m_colModel = nil;
	}
}

void
CBaseModelInfo::AddRef(void)
{
	m_refCount++;
	AddTexDictionaryRef();
}

void
CBaseModelInfo::RemoveRef(void)
{
	m_refCount--;
	RemoveTexDictionaryRef();
}

void
CBaseModelInfo::SetTexDictionary(const char *name)
{
	int slot = CTxdStore::FindTxdSlot(name);
	if(slot == -1)
		slot = CTxdStore::AddTxdSlot(name);
	m_txdSlot = slot;
}

void
CBaseModelInfo::AddTexDictionaryRef(void)
{
	CTxdStore::AddRef(m_txdSlot);
}

void
CBaseModelInfo::RemoveTexDictionaryRef(void)
{
	CTxdStore::RemoveRef(m_txdSlot);
}

void
CBaseModelInfo::Init2dEffects(void)
{
	m_twodEffects = nil;
	m_num2dEffects = 0;
}

void
CBaseModelInfo::Add2dEffect(C2dEffect *fx)
{
	if(m_twodEffects)
		m_num2dEffects++;
	else{
		m_twodEffects = fx;
		m_num2dEffects = 1;
	}
}

C2dEffect*
CBaseModelInfo::Get2dEffect(int n)
{
	if(m_twodEffects)
		return &m_twodEffects[n];
	else
		return nil;
}


code:
#pragma once

struct CColModel;

#define MAX_MODEL_NAME (24)

enum ModelInfoType
{
	MITYPE_NA        = 0,
	MITYPE_SIMPLE    = 1,
	MITYPE_MLO       = 2,
	MITYPE_TIME      = 3,
	MITYPE_CLUMP     = 4,
	MITYPE_VEHICLE   = 5,
	MITYPE_PED       = 6,
	MITYPE_XTRACOMPS = 7,
};

class C2dEffect;

class CBaseModelInfo
{
protected:
	char         m_name[MAX_MODEL_NAME];
	CColModel   *m_colModel;
	C2dEffect   *m_twodEffects;
	int16        m_objectId;
	uint16       m_refCount;
	int16        m_txdSlot;
	uint8        m_type;
	uint8        m_num2dEffects;
	bool         m_bOwnsColModel;
#ifdef EXTRA_MODEL_FLAGS
public:
	// from mobile
	bool         m_bIsDoubleSided;
	bool         m_bIsTree;
	bool         m_bCanBeIgnored;	// for low-end devices
	bool RenderDoubleSided(void) { return m_bIsDoubleSided || m_bIsTree; }
#endif

public:
	CBaseModelInfo(ModelInfoType type);
	virtual ~CBaseModelInfo() {}
	virtual void Shutdown(void);
	virtual void DeleteRwObject(void) = 0;
	virtual RwObject *CreateInstance(void) = 0;
	virtual RwObject *CreateInstance(RwMatrix *) = 0;
	virtual RwObject *GetRwObject(void) = 0;

	// one day it becomes virtual
	uint8 GetModelType() const { return m_type; }
	bool IsSimple(void) { return m_type == MITYPE_SIMPLE || m_type == MITYPE_TIME; }
	bool IsClump(void) { return m_type == MITYPE_CLUMP || m_type == MITYPE_PED || m_type == MITYPE_VEHICLE ||
		m_type == MITYPE_MLO || m_type == MITYPE_XTRACOMPS;	// unused but what the heck
	}
	char *GetModelName(void) { return m_name; }
	void SetModelName(const char *name) { strncpy(m_name, name, MAX_MODEL_NAME); }
	void SetColModel(CColModel *col, bool owns = false){
		m_colModel = col; m_bOwnsColModel = owns; }
	CColModel *GetColModel(void) { return m_colModel; }
	bool DoesOwnColModel(void) { return m_bOwnsColModel; }
	void DeleteCollisionModel(void);
	void ClearTexDictionary(void) { m_txdSlot = -1; }
	int16 GetObjectID(void) { return m_objectId; }
	void SetObjectID(int16 id) { m_objectId = id; }
	int16 GetTxdSlot(void) { return m_txdSlot; }
	void AddRef(void);
	void RemoveRef(void);
	void SetTexDictionary(const char *name);
	void AddTexDictionaryRef(void);
	void RemoveTexDictionaryRef(void);
	void Init2dEffects(void);
	void Add2dEffect(C2dEffect *fx);
	C2dEffect *Get2dEffect(int n);
	uint8 GetNum2dEffects() const { return m_num2dEffects; }
	uint16 GetNumRefs() const { return m_refCount; }
};

VALIDATE_SIZE(CBaseModelInfo, 0x30);


code:
#include "common.h"

#include "RwHelper.h"
#include "General.h"
#include "NodeName.h"
#include "VisibilityPlugins.h"
#include "ModelInfo.h"
#include "ModelIndices.h"

void
CClumpModelInfo::DeleteRwObject(void)
{
	if(m_clump){
		RpClumpDestroy(m_clump);
		m_clump = nil;
		RemoveTexDictionaryRef();
	}
}

#ifdef PED_SKIN
static RpAtomic*
SetHierarchyForSkinAtomic(RpAtomic *atomic, void *data)
{
	RpSkinAtomicSetHAnimHierarchy(atomic, (RpHAnimHierarchy*)data);
	return nil;
}
#endif

RwObject*
CClumpModelInfo::CreateInstance(void)
{
	if(m_clump == nil)
		return nil;
	RpClump *clone = RpClumpClone(m_clump);
#ifdef PED_SKIN
	if(IsClumpSkinned(clone)){
		RpHAnimHierarchy *hier;
		RpHAnimAnimation *anim;

		hier = GetAnimHierarchyFromClump(clone);
		assert(hier);
		// This seems dangerous as only the first atomic will get a hierarchy
		// can we guarantee this if hands and head are also in the clump?
		RpClumpForAllAtomics(clone, SetHierarchyForSkinAtomic, hier);
		anim = HAnimAnimationCreateForHierarchy(hier);
		RpHAnimHierarchySetCurrentAnim(hier, anim);
		RpHAnimHierarchySetFlags(hier, (RpHAnimHierarchyFlag)(rpHANIMHIERARCHYUPDATEMODELLINGMATRICES|rpHANIMHIERARCHYUPDATELTMS));
		// the rest is xbox only:
		// RpSkinGetNumBones(RpSkinGeometryGetSkin(RpAtomicGetGeometry(IsClumpSkinned(clone))));
		RpHAnimHierarchyUpdateMatrices(hier);
	}
#endif
	return (RwObject*)clone;
}

RwObject*
CClumpModelInfo::CreateInstance(RwMatrix *m)
{
	if(m_clump){
		RpClump *clump = (RpClump*)CreateInstance();
		*RwFrameGetMatrix(RpClumpGetFrame(clump)) = *m;
		return (RwObject*)clump;
	}
	return nil;
}

RpAtomic*
CClumpModelInfo::SetAtomicRendererCB(RpAtomic *atomic, void *data)
{
	CVisibilityPlugins::SetAtomicRenderCallback(atomic, (RpAtomicCallBackRender)data);
	return atomic;
}

void
CClumpModelInfo::SetClump(RpClump *clump)
{
	m_clump = clump;
	CVisibilityPlugins::SetClumpModelInfo(m_clump, this);
	AddTexDictionaryRef();
	RpClumpForAllAtomics(clump, SetAtomicRendererCB, nil);

#ifdef PED_SKIN
	if(IsClumpSkinned(clump)){
		int i;
		RpHAnimHierarchy *hier;
		RpAtomic *skinAtomic;
		RpSkin *skin;

		// mobile:
//		hier = nil;
//		RwFrameForAllChildren(RpClumpGetFrame(clump), GetHierarchyFromChildNodesCB, &hier);
//		assert(hier);
//		RpClumpForAllAtomics(clump, SetHierarchyForSkinAtomic, hier);
//		skinAtomic = GetFirstAtomic(clump);

		// xbox:
		hier = GetAnimHierarchyFromClump(clump);
		assert(hier);
		RpSkinAtomicSetHAnimHierarchy(IsClumpSkinned(clump), hier);
		skinAtomic = IsClumpSkinned(clump);

		assert(skinAtomic);
		skin = RpSkinGeometryGetSkin(RpAtomicGetGeometry(skinAtomic));
		// ignore const
		for(i = 0; i < RpGeometryGetNumVertices(RpAtomicGetGeometry(skinAtomic)); i++){
			RwMatrixWeights *weights = (RwMatrixWeights*)&RpSkinGetVertexBoneWeights(skin)[i];
			float sum = weights->w0 + weights->w1 + weights->w2 + weights->w3;
			weights->w0 /= sum;
			weights->w1 /= sum;
			weights->w2 /= sum;
			weights->w3 /= sum;
		}
		RpHAnimHierarchySetFlags(hier, (RpHAnimHierarchyFlag)(rpHANIMHIERARCHYUPDATEMODELLINGMATRICES|rpHANIMHIERARCHYUPDATELTMS));
	}
	if(strcmp(GetModelName(), "playerh") == 0){
		// playerh is incompatible with the xbox player skin
		// so check if player model is skinned and only apply skin to head if it isn't
		CPedModelInfo *body = (CPedModelInfo*)CModelInfo::GetModelInfo(MI_PLAYER);
		if(!(body->m_clump && IsClumpSkinned(body->m_clump)))
			RpClumpForAllAtomics(clump, SetAtomicRendererCB, (void*)CVisibilityPlugins::RenderPlayerCB);
	}
#else
	if(strcmp(GetModelName(), "playerh") == 0)
		RpClumpForAllAtomics(clump, SetAtomicRendererCB, (void*)CVisibilityPlugins::RenderPlayerCB);
#endif
}

void
CClumpModelInfo::SetFrameIds(RwObjectNameIdAssocation *assocs)
{
	int32 i;
	RwObjectNameAssociation objname;

	for(i = 0; assocs[i].name; i++)
		if((assocs[i].flags & CLUMP_FLAG_NO_HIERID) == 0){
			objname.frame = nil;
			objname.name = assocs[i].name;
			RwFrameForAllChildren(RpClumpGetFrame(m_clump), FindFrameFromNameWithoutIdCB, &objname);
			if(objname.frame)
				CVisibilityPlugins::SetFrameHierarchyId(objname.frame, assocs[i].hierId);
		}
}

RwFrame*
CClumpModelInfo::FindFrameFromIdCB(RwFrame *frame, void *data)
{
	RwObjectIdAssociation *assoc = (RwObjectIdAssociation*)data;

	if(CVisibilityPlugins::GetFrameHierarchyId(frame) == assoc->id){
		assoc->frame = frame;
		return nil;
	}
	RwFrameForAllChildren(frame, FindFrameFromIdCB, assoc);
	return assoc->frame ? nil : frame;
}

RwFrame*
CClumpModelInfo::FindFrameFromNameCB(RwFrame *frame, void *data)
{
	RwObjectNameAssociation *assoc = (RwObjectNameAssociation*)data;

	if(!CGeneral::faststricmp(GetFrameNodeName(frame), assoc->name)){
		assoc->frame = frame;
		return nil;
	}
	RwFrameForAllChildren(frame, FindFrameFromNameCB, assoc);
	return assoc->frame ? nil : frame;
}

RwFrame*
CClumpModelInfo::FindFrameFromNameWithoutIdCB(RwFrame *frame, void *data)
{
	RwObjectNameAssociation *assoc = (RwObjectNameAssociation*)data;

	if(CVisibilityPlugins::GetFrameHierarchyId(frame) == 0 &&
	   !CGeneral::faststricmp(GetFrameNodeName(frame), assoc->name)){
		assoc->frame = frame;
		return nil;
	}
	RwFrameForAllChildren(frame, FindFrameFromNameWithoutIdCB, assoc);
	return assoc->frame ? nil : frame;
}

RwFrame*
CClumpModelInfo::FillFrameArrayCB(RwFrame *frame, void *data)
{
	int32 id;
	RwFrame **frames = (RwFrame**)data;
	id = CVisibilityPlugins::GetFrameHierarchyId(frame);
	if(id > 0)
		frames[id] = frame;
	RwFrameForAllChildren(frame, FillFrameArrayCB, data);
	return frame;
}

void
CClumpModelInfo::FillFrameArray(RpClump *clump, RwFrame **frames)
{
	RwFrameForAllChildren(RpClumpGetFrame(clump), FillFrameArrayCB, frames);
}

RwFrame*
CClumpModelInfo::GetFrameFromId(RpClump *clump, int32 id)
{
	RwObjectIdAssociation assoc;
	assoc.id = id;
	assoc.frame = nil;
	RwFrameForAllChildren(RpClumpGetFrame(clump), FindFrameFromIdCB, &assoc);
	return assoc.frame;
}


code:
#pragma once

#include "BaseModelInfo.h"

struct RwObjectNameIdAssocation
{
	const char *name;
	int32 hierId;
	uint32 flags;
};

struct RwObjectNameAssociation
{
	const char *name;
	RwFrame *frame;
};

struct RwObjectIdAssociation
{
	int32 id;
	RwFrame *frame;
};

enum {
	CLUMP_FLAG_NO_HIERID = 0x1,
};

class CClumpModelInfo : public CBaseModelInfo
{
public:
	RpClump *m_clump;

	CClumpModelInfo(void) : CBaseModelInfo(MITYPE_CLUMP) {}
	CClumpModelInfo(ModelInfoType id) : CBaseModelInfo(id) {}
	~CClumpModelInfo() {}
	void DeleteRwObject(void);
	RwObject *CreateInstance(void);
	RwObject *CreateInstance(RwMatrix *);
	RwObject *GetRwObject(void) { return (RwObject*)m_clump; }

	virtual void SetClump(RpClump *);

	static RpAtomic *SetAtomicRendererCB(RpAtomic *atomic, void *data);
	void SetFrameIds(RwObjectNameIdAssocation *assocs);
	static RwFrame *FindFrameFromNameCB(RwFrame *frame, void *data);
	static RwFrame *FindFrameFromNameWithoutIdCB(RwFrame *frame, void *data);
	static RwFrame *FindFrameFromIdCB(RwFrame *frame, void *data);
	static void FillFrameArray(RpClump *clump, RwFrame **frames);
	static RwFrame *FillFrameArrayCB(RwFrame *frame, void *data);
	static RwFrame *GetFrameFromId(RpClump *clump, int32 id);
};

VALIDATE_SIZE(CClumpModelInfo, 0x34);


code:
#include "common.h"

#include "VisibilityPlugins.h"
#include "ModelInfo.h"

void
CMloModelInfo::ConstructClump()
{
	m_clump = RpClumpCreate();
	RwFrame *mainFrame = RwFrameCreate();
	RwFrameSetIdentity(mainFrame);
	RpClumpSetFrame(m_clump, mainFrame);

	for (int i = firstInstance; i < lastInstance; i++) {
		int modelId = CModelInfo::GetMloInstanceStore().store[i].m_modelIndex;
		RwMatrix *attMat = CModelInfo::GetMloInstanceStore().store[i].GetMatrix().m_attachment;
		CSimpleModelInfo *minfo = (CSimpleModelInfo*)CModelInfo::GetModelInfo(modelId);

		if (minfo->m_atomics[0] != nil) {
			RpAtomic *newAtomic = RpAtomicClone(minfo->m_atomics[0]);
			RwFrame *newFrame = RwFrameCreate();
			if (newAtomic != nil && newFrame != nil) {
				*RwFrameGetMatrix(newFrame) = *attMat;
				RpAtomicSetFrame(newAtomic, newFrame);
				RwFrameAddChild(mainFrame, newFrame);
				RpClumpAddAtomic(m_clump, newAtomic);
			} else {
				debug("Failed to allocate memory while creating template MLO.\n");
			}
		}
	}

	if (RpClumpGetNumAtomics(m_clump) != 0) {
		CVisibilityPlugins::SetClumpModelInfo(m_clump, this);
	} else {
		RpClumpDestroy(m_clump);
		m_clump = nil;
	}
}


code:
#pragma once

#include "ClumpModelInfo.h"

class CMloModelInfo : public CClumpModelInfo
{
public:
	float drawDist;
	int firstInstance;
	int lastInstance;
public:
	CMloModelInfo(void) : CClumpModelInfo(MITYPE_MLO) {}
	void ConstructClump();
};


code:
#include "common.h"

#include "General.h"
#include "ModelIndices.h"

#define X(name, var) int16 var = -1;
	MODELINDICES
#undef X

void
InitModelIndices(void)
{
#define X(name, var) var = -1;
	MODELINDICES
#undef X
}

void
MatchModelString(const char *modelname, int16 id)
{
#define X(name, var) \
	if(!CGeneral::faststrcmp(name, modelname)){ \
		var = id; \
		return; \
	}
	MODELINDICES
#undef X
}

void
TestModelIndices(void)
{
	;
}


code:
#pragma once

#define MODELINDICES \
	X("fire_hydrant",	MI_FIRE_HYDRANT) \
	X("bagelstnd02",	MI_BAGELSTAND2) \
	X("fish01",		MI_FISHSTALL01) \
	X("fishstall02",	MI_FISHSTALL02) \
	X("fishstall03",	MI_FISHSTALL03) \
	X("fishstall04",	MI_FISHSTALL04) \
	X("taxisign",		MI_TAXISIGN) \
	X("phonesign",		MI_PHONESIGN) \
	X("noparkingsign1",	MI_NOPARKINGSIGN1) \
	X("bussign1",		MI_BUSSIGN1) \
	X("roadworkbarrier1",	MI_ROADWORKBARRIER1) \
	X("dump1",		MI_DUMP1) \
	X("trafficcone",	MI_TRAFFICCONE) \
	X("newsstand1",		MI_NEWSSTAND) \
	X("postbox1",		MI_POSTBOX1) \
	X("bin1",		MI_BIN) \
	X("wastebin",		MI_WASTEBIN) \
	X("phonebooth1",	MI_PHONEBOOTH1) \
	X("parkingmeter",	MI_PARKINGMETER) \
	X("trafficlight1",	MI_TRAFFICLIGHTS) \
	X("lamppost1",		MI_SINGLESTREETLIGHTS1) \
	X("lamppost2",		MI_SINGLESTREETLIGHTS2) \
	X("lamppost3",		MI_SINGLESTREETLIGHTS3) \
	X("doublestreetlght1",	MI_DOUBLESTREETLIGHTS) \
	X("rd_Road2A10",	MI_ROADSFORROADBLOCKSSTART) \
	X("rd_Road1A30",	MI_ROADSFORROADBLOCKSEND) \
	X("veg_tree1",		MI_TREE1) \
	X("veg_tree3",		MI_TREE2) \
	X("veg_treea1",		MI_TREE3) \
	X("veg_treenew01",	MI_TREE4) \
	X("veg_treenew05",	MI_TREE5) \
	X("veg_treeb1",		MI_TREE6) \
	X("veg_treenew10",	MI_TREE7) \
	X("veg_treea3",		MI_TREE8) \
	X("veg_treenew09",	MI_TREE9) \
	X("veg_treenew08",	MI_TREE10) \
	X("veg_treenew03",	MI_TREE11) \
	X("veg_treenew16",	MI_TREE12) \
	X("veg_treenew17",	MI_TREE13) \
	X("veg_treenew06",	MI_TREE14) \
	X("doc_crane_cab",	MODELID_CRANE_1) \
	X("cranetopb",		MODELID_CRANE_2) \
	X("cranetopa",		MODELID_CRANE_3) \
	X("package1",		MI_COLLECTABLE1) \
	X("Money",		MI_MONEY) \
	X("barrel1",		MI_CARMINE) \
	X("oddjgaragdoor",	MI_GARAGEDOOR1) \
	X("bombdoor",		MI_GARAGEDOOR2) \
	X("door_bombshop",	MI_GARAGEDOOR3) \
	X("vheistlocdoor",	MI_GARAGEDOOR4) \
	X("door2_garage",	MI_GARAGEDOOR5) \
	X("ind_slidedoor",	MI_GARAGEDOOR6) \
	X("bankjobdoor",	MI_GARAGEDOOR7) \
	X("door_jmsgrage",	MI_GARAGEDOOR9) \
	X("jamesgrge_kb",	MI_GARAGEDOOR10) \
	X("door_sfehousegrge",	MI_GARAGEDOOR11) \
	X("shedgaragedoor",	MI_GARAGEDOOR12) \
	X("door4_garage",	MI_GARAGEDOOR13) \
	X("door_col_compnd_01",	MI_GARAGEDOOR14) \
	X("door_col_compnd_02",	MI_GARAGEDOOR15) \
	X("door_col_compnd_03",	MI_GARAGEDOOR16) \
	X("door_col_compnd_04",	MI_GARAGEDOOR17) \
	X("door_col_compnd_05",	MI_GARAGEDOOR18) \
	X("impex_door",		MI_GARAGEDOOR19) \
	X("SalvGarage",		MI_GARAGEDOOR20) \
	X("door3_garage",	MI_GARAGEDOOR21) \
	X("leveldoor2",		MI_GARAGEDOOR22) \
	X("double_garage_dr",	MI_GARAGEDOOR23) \
	X("amcogaragedoor",	MI_GARAGEDOOR24) \
	X("towergaragedoor1",	MI_GARAGEDOOR25) \
	X("towergaragedoor2",	MI_GARAGEDOOR26) \
	X("towergaragedoor3",	MI_GARAGEDOOR27) \
	X("plysve_gragedoor",	MI_GARAGEDOOR28) \
	X("impexpsubgrgdoor",	MI_GARAGEDOOR29) \
	X("Sub_sprayshopdoor",	MI_GARAGEDOOR30) \
	X("ind_plyrwoor",	MI_GARAGEDOOR31) \
	X("8ballsuburbandoor",	MI_GARAGEDOOR32) \
	X("barrel2",		MI_NAUTICALMINE) \
	X("crushercrush",	MI_CRUSHERBODY) \
	X("crushertop",		MI_CRUSHERLID) \
	X("donkeymag",		MI_DONKEYMAG) \
	X("bullion",		MI_BULLION) \
	X("floatpackge1",	MI_FLOATPACKAGE1) \
	X("briefcase",		MI_BRIEFCASE) \
	X("chinabanner1",	MI_CHINABANNER1) \
	X("chinabanner2",	MI_CHINABANNER2) \
	X("chinabanner3",	MI_CHINABANNER3) \
	X("chinabanner4",	MI_CHINABANNER4) \
	X("iten_chinatown5",	MI_CHINABANNER5) \
	X("iten_chinatown7",	MI_CHINABANNER6) \
	X("iten_chinatown3",	MI_CHINABANNER7) \
	X("iten_chinatown2",	MI_CHINABANNER8) \
	X("iten_chinatown4",	MI_CHINABANNER9) \
	X("iten_washline01",	MI_CHINABANNER10) \
	X("iten_washline02",	MI_CHINABANNER11) \
	X("iten_washline03",	MI_CHINABANNER12) \
	X("chinalanterns",	MI_CHINALANTERN) \
	X("glassfx1",		MI_GLASS1) \
	X("glassfx2",		MI_GLASS2) \
	X("glassfx3",		MI_GLASS3) \
	X("glassfx4",		MI_GLASS4) \
	X("glassfx55",		MI_GLASS5) \
	X("glassfxsub1",	MI_GLASS6) \
	X("glassfxsub2",	MI_GLASS7) \
	X("glassfx_composh",	MI_GLASS8) \
	X("bridge_liftsec",	MI_BRIDGELIFT) \
	X("bridge_liftweight",	MI_BRIDGEWEIGHT) \
	X("subbridge_lift",	MI_BRIDGEROADSEGMENT) \
	X("barrel4",		MI_EXPLODINGBARREL) \
	X("flagsitaly",		MI_ITALYBANNER1) \
	X("adrenaline",		MI_PICKUP_ADRENALINE) \
	X("bodyarmour",		MI_PICKUP_BODYARMOUR) \
	X("info",		MI_PICKUP_INFO) \
	X("health",		MI_PICKUP_HEALTH) \
	X("bonus",		MI_PICKUP_BONUS) \
	X("bribe",		MI_PICKUP_BRIBE) \
	X("killfrenzy",		MI_PICKUP_KILLFRENZY) \
	X("camerapickup",	MI_PICKUP_CAMERA) \
	X("bollardlight",	MI_BOLLARDLIGHT) \
	X("magnet",		MI_MAGNET) \
	X("streetlamp1",	MI_STREETLAMP1) \
	X("streetlamp2",	MI_STREETLAMP2) \
	X("railtrax_lo4b",	MI_RAILTRACKS) \
	X("bar_barrier10",	MI_FENCE) \
	X("bar_barrier12",	MI_FENCE2) \
	X("petrolpump",		MI_PETROLPUMP) \
	X("bodycast",		MI_BODYCAST) \
	X("backdoor",		MI_BACKDOOR) \
	X("coffee",		MI_COFFEE) \
	X("bouy",		MI_BUOY) \
	X("parktable1",		MI_PARKTABLE) \
	X("sbwy_tunl_start",	MI_SUBWAY1) \
	X("sbwy_tunl_bit",	MI_SUBWAY2) \
	X("sbwy_tunl_bend",	MI_SUBWAY3) \
	X("sbwy_tunl_cstm6",	MI_SUBWAY4) \
	X("sbwy_tunl_cstm7",	MI_SUBWAY5) \
	X("sbwy_tunl_cstm8",	MI_SUBWAY6) \
	X("sbwy_tunl_cstm10",	MI_SUBWAY7) \
	X("sbwy_tunl_cstm9",	MI_SUBWAY8) \
	X("sbwy_tunl_cstm11",	MI_SUBWAY9) \
	X("sbwy_tunl_cstm1",	MI_SUBWAY10) \
	X("sbwy_tunl_cstm2",	MI_SUBWAY11) \
	X("sbwy_tunl_cstm4",	MI_SUBWAY12) \
	X("sbwy_tunl_cstm3",	MI_SUBWAY13) \
	X("sbwy_tunl_cstm5",	MI_SUBWAY14) \
	X("subplatform_n2",	MI_SUBWAY15) \
	X("suby_tunl_start",	MI_SUBWAY16) \
	X("sbwy_tunl_start2",	MI_SUBWAY17) \
	X("indy_tunl_start",	MI_SUBWAY18) \
	X("indsubway03",	MI_SUBPLATFORM_IND) \
	X("comerside_subway",	MI_SUBPLATFORM_COMS) \
	X("subplatform",	MI_SUBPLATFORM_COMS2) \
	X("subplatform_n",	MI_SUBPLATFORM_COMN) \
	X("Otherside_subway",	MI_SUBPLATFORM_SUB) \
	X("subplatform_sub",	MI_SUBPLATFORM_SUB2) \
	X("files",		MI_FILES)

#define X(name, var) extern int16 var;
	MODELINDICES
#undef X

// and some hardcoded ones
// expand as needed
enum
{
	MI_PLAYER = 0,
	MI_COP,
	MI_SWAT,
	MI_FBI,
	MI_ARMY,
	MI_MEDIC,
	MI_FIREMAN,
	MI_MALE01,
	MI_TAXI_D,
	MI_PIMP,
	MI_GANG01,
	MI_GANG02,
	MI_GANG03,
	MI_GANG04,
	MI_GANG05,
	MI_GANG06,
	MI_GANG07,
	MI_GANG08,
	MI_GANG09,
	MI_GANG10,
	MI_GANG11,
	MI_GANG12,
	MI_GANG13,
	MI_GANG14,
	MI_CRIMINAL01,
	MI_CRIMINAL02,
	MI_SPECIAL01,
	MI_SPECIAL02,
	MI_SPECIAL03,
	MI_SPECIAL04,
	MI_MALE02,
	MI_MALE03,
	MI_FATMALE01,
	MI_FATMALE02,
	MI_FEMALE01,
	MI_FEMALE02,
	MI_FEMALE03,
	MI_FATFEMALE01,
	MI_FATFEMALE02,
	MI_PROSTITUTE,
	MI_PROSTITUTE2,
	MI_P_MAN1,
	MI_P_MAN2,
	MI_P_WOM1,
	MI_P_WOM2,
	MI_CT_MAN1,
	MI_CT_MAN2,
	MI_CT_WOM1,
	MI_CT_WOM2,
	MI_LI_MAN1,
	MI_LI_MAN2,
	MI_LI_WOM1,
	MI_LI_WOM2,
	MI_DOCKER1,
	MI_DOCKER2,
	MI_SCUM_MAN,
	MI_SCUM_WOM,
	MI_WORKER1,
	MI_WORKER2,
	MI_B_MAN1,
	MI_B_MAN2,
	MI_B_MAN3,
	MI_B_WOM1,
	MI_B_WOM2,
	MI_B_WOM3,
	MI_MOD_MAN,
	MI_MOD_WOM,
	MI_ST_MAN,
	MI_ST_WOM,
	MI_FAN_MAN1,
	MI_FAN_MAN2,
	MI_FAN_WOM,
	MI_HOS_MAN,
	MI_HOS_WOM,
	MI_CONST1,
	MI_CONST2,
	MI_SHOPPER1,
	MI_SHOPPER2,
	MI_SHOPPER3,
	MI_STUD_MAN,
	MI_STUD_WOM,
	MI_CAS_MAN,
	MI_CAS_WOM,
	MI_BUSKER1,
	MI_BUSKER2,
	MI_BUSKER3,
	MI_BUSKER4,
	// three more peds possible

	MI_LAST_PED = 89,
	MI_FIRST_VEHICLE,

	MI_LANDSTAL = MI_FIRST_VEHICLE,
	MI_IDAHO,
	MI_STINGER,
	MI_LINERUN,
	MI_PEREN,
	MI_SENTINEL,
	MI_PATRIOT,
	MI_FIRETRUCK,
	MI_TRASH,
	MI_STRETCH,
	MI_MANANA,
	MI_INFERNUS,
	MI_BLISTA,
	MI_PONY,
	MI_MULE,
	MI_CHEETAH,
	MI_AMBULAN,
	MI_FBICAR,
	MI_MOONBEAM,
	MI_ESPERANT,
	MI_TAXI,
	MI_KURUMA,
	MI_BOBCAT,
	MI_MRWHOOP,
	MI_BFINJECT,
	MI_CORPSE,
	MI_POLICE,
	MI_ENFORCER,
	MI_SECURICA,
	MI_BANSHEE,
	MI_PREDATOR,
	MI_BUS,
	MI_RHINO,
	MI_BARRACKS,
	MI_TRAIN,
	MI_CHOPPER,
	MI_DODO,
	MI_COACH,
	MI_CABBIE,
	MI_STALLION,
	MI_RUMPO,
	MI_RCBANDIT,
	MI_BELLYUP,
	MI_MRWONGS,
	MI_MAFIA,
	MI_YARDIE,
	MI_YAKUZA,
	MI_DIABLOS,
	MI_COLUMB ,
	MI_HOODS,
	MI_AIRTRAIN,
	MI_DEADDODO,
	MI_SPEEDER,
	MI_REEFER,
	MI_PANLANT,
	MI_FLATBED,
	MI_YANKEE,
	MI_ESCAPE,
	MI_BORGNINE,
	MI_TOYZ,
	MI_GHOST,

	// leftovers on PC
	MI_MIAMI_RCBARON = 154,
	MI_MIAMI_RCRAIDER = 155,
	MI_MIAMI_SPARROW = 159,

	MI_GRENADE = 170,
	MI_AK47,
	MI_BASEBALL_BAT,
	MI_COLT,
	MI_MOLOTOV,
	MI_ROCKETLAUNCHER,
	MI_SHOTGUN,
	MI_SNIPER,
	MI_UZI,
	MI_MISSILE,
	MI_M16,
	MI_FLAMETHROWER,
	MI_BOMB,
	MI_FINGERS,

	MI_CUTOBJ01 = 185,
	MI_CUTOBJ02,
	MI_CUTOBJ03,
	MI_CUTOBJ04,
	MI_CUTOBJ05,

	MI_CAR_DOOR = 190,
	MI_CAR_BUMPER,
	MI_CAR_PANEL,
	MI_CAR_BONNET,
	MI_CAR_BOOT,
	MI_CAR_WHEEL,
	MI_BODYPARTA,
	MI_BODYPARTB,

	MI_AIRTRAIN_VLO = 198,
	MI_LOPOLYGUY,

	NUM_DEFAULT_MODELS
};

enum{
	NUM_OF_SPECIAL_CHARS = 4,
	NUM_OF_CUTSCENE_OBJECTS = 5
};

void InitModelIndices(void);
void MatchModelString(const char *name, int16 id);
void TestModelIndices(void);

inline bool
IsGlass(int16 id)
{
	return id == MI_GLASS1 ||
		id == MI_GLASS2 ||
		id == MI_GLASS3 ||
		id == MI_GLASS4 ||
		id == MI_GLASS5 ||
		id == MI_GLASS6 ||
		id == MI_GLASS7 ||
		id == MI_GLASS8;
}

inline bool
IsStreetLight(int16 id)
{
	return id == MI_TRAFFICLIGHTS ||
		id == MI_SINGLESTREETLIGHTS1 ||
		id == MI_SINGLESTREETLIGHTS2 ||
		id == MI_SINGLESTREETLIGHTS3 ||
		id == MI_DOUBLESTREETLIGHTS;
}

inline bool
IsBodyPart(int16 id)
{
	return id == MI_BODYPARTA || id == MI_BODYPARTB;
}

// This is bad and should perhaps not be used
inline bool
IsBoatModel(int16 id)
{
	return id == MI_PREDATOR ||
		id == MI_REEFER ||
		id == MI_SPEEDER ||
		id == MI_GHOST;
}

inline bool
IsPedModel(int16 id)
{
	return id >= MI_PLAYER && id <= MI_LAST_PED;
}

inline bool
IsTreeModel(int16 id)
{
	return id == MI_TREE1 ||
		id == MI_TREE2 ||
		id == MI_TREE3 ||
		id == MI_TREE4 ||
		id == MI_TREE5 ||
		id == MI_TREE6 ||
		id == MI_TREE7 ||
		id == MI_TREE8 ||
		id == MI_TREE9 ||
		id == MI_TREE10 ||
		id == MI_TREE11 ||
		id == MI_TREE12 ||
		id == MI_TREE13 ||
		id == MI_TREE14;
}

inline bool
IsBannerModel(int16 id)
{
	return id == MI_CHINABANNER1 ||
		id == MI_CHINABANNER2 ||
		id == MI_CHINABANNER3 ||
		id == MI_CHINABANNER4 ||
		id == MI_CHINABANNER5 ||
		id == MI_CHINABANNER6 ||
		id == MI_CHINABANNER7 ||
		id == MI_CHINABANNER8 ||
		id == MI_CHINABANNER9 ||
		id == MI_CHINABANNER10 ||
		id == MI_CHINABANNER11 ||
		id == MI_CHINABANNER12 ||
		id == MI_ITALYBANNER1 ||
		id == MI_CHINALANTERN;
}
inline bool
IsPickupModel(int16 id)
{
	return id == MI_GRENADE ||
		id == MI_AK47 ||
		id == MI_BASEBALL_BAT ||
		id == MI_COLT ||
		id == MI_MOLOTOV ||
		id == MI_ROCKETLAUNCHER ||
		id == MI_SHOTGUN ||
		id == MI_SNIPER ||
		id == MI_UZI ||
		id == MI_M16 ||
		id == MI_FLAMETHROWER ||
		id == MI_PICKUP_ADRENALINE ||
		id == MI_PICKUP_BODYARMOUR ||
		id == MI_PICKUP_INFO ||
		id == MI_PICKUP_HEALTH ||
		id == MI_PICKUP_BONUS ||
		id == MI_PICKUP_BRIBE ||
		id == MI_PICKUP_KILLFRENZY ||
		id == MI_PICKUP_CAMERA;
}

inline bool
IsPolicePedModel(int16 id)
{
	return id == MI_COP ||
		id == MI_SWAT ||
		id == MI_FBI ||
		id == MI_ARMY;
}

inline bool
IsPoliceVehicleModel(int16 id)
{
	return id == MI_CHOPPER ||
		id == MI_PREDATOR ||
		id == MI_POLICE ||
		id == MI_ENFORCER;
}

inline bool
IsExplosiveThingModel(int16 id)
{
	return id == MI_EXPLODINGBARREL ||
		id == MI_PETROLPUMP;
}

inline bool
IsFence(int16 id)
{
	return id == MI_FENCE ||
		id == MI_FENCE2;
}


code:
#include "common.h"

#include "General.h"
#include "TempColModels.h"
#include "ModelIndices.h"
#include "ModelInfo.h"
#include "Frontend.h"

CBaseModelInfo *CModelInfo::ms_modelInfoPtrs[MODELINFOSIZE];

CStore<CSimpleModelInfo, SIMPLEMODELSIZE> CModelInfo::ms_simpleModelStore;
CStore<CMloModelInfo, MLOMODELSIZE> CModelInfo::ms_mloModelStore;
CStore<CInstance, MLOINSTANCESIZE> CModelInfo::ms_mloInstanceStore;
CStore<CTimeModelInfo, TIMEMODELSIZE> CModelInfo::ms_timeModelStore;
CStore<CClumpModelInfo, CLUMPMODELSIZE> CModelInfo::ms_clumpModelStore;
CStore<CPedModelInfo, PEDMODELSIZE> CModelInfo::ms_pedModelStore;
CStore<CVehicleModelInfo, VEHICLEMODELSIZE> CModelInfo::ms_vehicleModelStore;
CStore<CXtraCompsModelInfo, XTRACOMPSMODELSIZE> CModelInfo::ms_xtraCompsModelStore;
CStore<C2dEffect, TWODFXSIZE> CModelInfo::ms_2dEffectStore;

void
CModelInfo::Initialise(void)
{
	int i;
	CSimpleModelInfo *m;

	for(i = 0; i < MODELINFOSIZE; i++)
		ms_modelInfoPtrs[i] = nil;
	ms_2dEffectStore.Clear();
	ms_mloInstanceStore.Clear();
	ms_xtraCompsModelStore.Clear();
	ms_simpleModelStore.Clear();
	ms_timeModelStore.Clear();
	ms_mloModelStore.Clear();
	ms_clumpModelStore.Clear();
	ms_pedModelStore.Clear();
	ms_vehicleModelStore.Clear();

	m = AddSimpleModel(MI_CAR_DOOR);
	m->SetColModel(&CTempColModels::ms_colModelDoor1);
	m->SetTexDictionary("generic");
	m->SetNumAtomics(1);
	m->m_lodDistances[0] = 80.0f;

	m = AddSimpleModel(MI_CAR_BUMPER);
	m->SetColModel(&CTempColModels::ms_colModelBumper1);
	m->SetTexDictionary("generic");
	m->SetNumAtomics(1);
	m->m_lodDistances[0] = 80.0f;

	m = AddSimpleModel(MI_CAR_PANEL);
	m->SetColModel(&CTempColModels::ms_colModelPanel1);
	m->SetTexDictionary("generic");
	m->SetNumAtomics(1);
	m->m_lodDistances[0] = 80.0f;

	m = AddSimpleModel(MI_CAR_BONNET);
	m->SetColModel(&CTempColModels::ms_colModelBonnet1);
	m->SetTexDictionary("generic");
	m->SetNumAtomics(1);
	m->m_lodDistances[0] = 80.0f;

	m = AddSimpleModel(MI_CAR_BOOT);
	m->SetColModel(&CTempColModels::ms_colModelBoot1);
	m->SetTexDictionary("generic");
	m->SetNumAtomics(1);
	m->m_lodDistances[0] = 80.0f;

	m = AddSimpleModel(MI_CAR_WHEEL);
	m->SetColModel(&CTempColModels::ms_colModelWheel1);
	m->SetTexDictionary("generic");
	m->SetNumAtomics(1);
	m->m_lodDistances[0] = 80.0f;

	m = AddSimpleModel(MI_BODYPARTA);
	m->SetColModel(&CTempColModels::ms_colModelBodyPart1);
	m->SetTexDictionary("generic");
	m->SetNumAtomics(1);
	m->m_lodDistances[0] = 80.0f;

	m = AddSimpleModel(MI_BODYPARTB);
	m->SetColModel(&CTempColModels::ms_colModelBodyPart2);
	m->SetTexDictionary("generic");
	m->SetNumAtomics(1);
	m->m_lodDistances[0] = 80.0f;
}

void
CModelInfo::ShutDown(void)
{
	int i;
	for(i = 0; i < ms_simpleModelStore.allocPtr; i++)
		ms_simpleModelStore.store[i].Shutdown();
	for(i = 0; i < ms_mloInstanceStore.allocPtr; i++)
		ms_mloInstanceStore.store[i].Shutdown();
	for(i = 0; i < ms_timeModelStore.allocPtr; i++)
		ms_timeModelStore.store[i].Shutdown();
	for(i = 0; i < ms_clumpModelStore.allocPtr; i++)
		ms_clumpModelStore.store[i].Shutdown();
	for(i = 0; i < ms_vehicleModelStore.allocPtr; i++)
		ms_vehicleModelStore.store[i].Shutdown();
	for(i = 0; i < ms_pedModelStore.allocPtr; i++)
		ms_pedModelStore.store[i].Shutdown();
	for(i = 0; i < ms_xtraCompsModelStore.allocPtr; i++)
		ms_xtraCompsModelStore.store[i].Shutdown();
	for(i = 0; i < ms_mloInstanceStore.allocPtr; i++)
		ms_mloInstanceStore.store[i].Shutdown();
	for(i = 0; i < ms_2dEffectStore.allocPtr; i++)
		ms_2dEffectStore.store[i].Shutdown();

	ms_2dEffectStore.Clear();
	ms_simpleModelStore.Clear();
	ms_mloInstanceStore.Clear();
	ms_mloModelStore.Clear();
	ms_xtraCompsModelStore.Clear();
	ms_timeModelStore.Clear();
	ms_pedModelStore.Clear();
	ms_clumpModelStore.Clear();
	ms_vehicleModelStore.Clear();
}

CSimpleModelInfo*
CModelInfo::AddSimpleModel(int id)
{
	CSimpleModelInfo *modelinfo;
	modelinfo = CModelInfo::ms_simpleModelStore.Alloc();
	CModelInfo::ms_modelInfoPtrs[id] = modelinfo;
	modelinfo->Init();
	return modelinfo;
}

CMloModelInfo *
CModelInfo::AddMloModel(int id)
{
	CMloModelInfo *modelinfo;
	modelinfo = CModelInfo::ms_mloModelStore.Alloc();
	CModelInfo::ms_modelInfoPtrs[id] = modelinfo;
	modelinfo->m_clump = nil;
	modelinfo->firstInstance = 0;
	modelinfo->lastInstance = 0;
	return modelinfo;
}

CTimeModelInfo*
CModelInfo::AddTimeModel(int id)
{
	CTimeModelInfo *modelinfo;
	modelinfo = CModelInfo::ms_timeModelStore.Alloc();
	CModelInfo::ms_modelInfoPtrs[id] = modelinfo;
	modelinfo->Init();
	return modelinfo;
}

CClumpModelInfo*
CModelInfo::AddClumpModel(int id)
{
	CClumpModelInfo *modelinfo;
	modelinfo = CModelInfo::ms_clumpModelStore.Alloc();
	CModelInfo::ms_modelInfoPtrs[id] = modelinfo;
	modelinfo->m_clump = nil;
	return modelinfo;
}

CPedModelInfo*
CModelInfo::AddPedModel(int id)
{
	CPedModelInfo *modelinfo;
	modelinfo = CModelInfo::ms_pedModelStore.Alloc();
	CModelInfo::ms_modelInfoPtrs[id] = modelinfo;
	modelinfo->m_clump = nil;
	return modelinfo;
}

CVehicleModelInfo*
CModelInfo::AddVehicleModel(int id)
{
	CVehicleModelInfo *modelinfo;
	modelinfo = CModelInfo::ms_vehicleModelStore.Alloc();
	CModelInfo::ms_modelInfoPtrs[id] = modelinfo;
	modelinfo->m_clump = nil;
	modelinfo->m_vehicleType = -1;
	modelinfo->m_wheelId = -1;
	modelinfo->m_materials1[0] = nil;
	modelinfo->m_materials2[0] = nil;
	modelinfo->m_bikeSteerAngle = 999.99f;
	return modelinfo;
}

CBaseModelInfo*
CModelInfo::GetModelInfo(const char *name, int *id)
{
	CBaseModelInfo *modelinfo;
	for(int i = 0; i < MODELINFOSIZE; i++){
		modelinfo = CModelInfo::ms_modelInfoPtrs[i];
	 	if(modelinfo && !CGeneral::faststricmp(modelinfo->GetModelName(), name)){
			if(id)
				*id = i;
			return modelinfo;
		}
	}
	return nil;
}

bool
CModelInfo::IsBoatModel(int32 id)
{
	return GetModelInfo(id)->GetModelType() == MITYPE_VEHICLE &&
		((CVehicleModelInfo*)GetModelInfo(id))->m_vehicleType == VEHICLE_TYPE_BOAT;
}

bool
CModelInfo::IsBikeModel(int32 id)
{
	return GetModelInfo(id)->GetModelType() == MITYPE_VEHICLE &&
		((CVehicleModelInfo*)GetModelInfo(id))->m_vehicleType == VEHICLE_TYPE_BIKE;
}

void
CModelInfo::RemoveColModelsFromOtherLevels(eLevelName level)
{
	ISLAND_LOADING_IS(LOW)
	{
		int i;
		CBaseModelInfo *mi;
		CColModel *colmodel;

		for (i = 0; i < MODELINFOSIZE; i++) {
			mi = GetModelInfo(i);
			if (mi) {
				colmodel = mi->GetColModel();
				if (colmodel && colmodel->level != LEVEL_GENERIC && colmodel->level != level)
					colmodel->RemoveCollisionVolumes();
			}
		}
	}
}

void
CModelInfo::ConstructMloClumps()
{
	for (int i = 0; i < ms_mloModelStore.allocPtr; i++)
		ms_mloModelStore.store[i].ConstructClump();
}

void
CModelInfo::ReInit2dEffects()
{
	ms_2dEffectStore.Clear();

	for (int i = 0; i < MODELINFOSIZE; i++) {
		if (ms_modelInfoPtrs[i])
			ms_modelInfoPtrs[i]->Init2dEffects();
	}
}


code:
#pragma once

#include "2dEffect.h"
#include "BaseModelInfo.h"
#include "SimpleModelInfo.h"
#include "MloModelInfo.h"
#include "TimeModelInfo.h"
#include "ClumpModelInfo.h"
#include "PedModelInfo.h"
#include "VehicleModelInfo.h"
#include "XtraCompsModelInfo.h"
#include "Instance.h"
#include "Game.h"

class CModelInfo
{
	static CBaseModelInfo *ms_modelInfoPtrs[MODELINFOSIZE];
	static CStore<CSimpleModelInfo, SIMPLEMODELSIZE> ms_simpleModelStore;
	static CStore<CMloModelInfo, MLOMODELSIZE> ms_mloModelStore;
	static CStore<CInstance, MLOINSTANCESIZE> ms_mloInstanceStore;
	static CStore<CTimeModelInfo, TIMEMODELSIZE> ms_timeModelStore;
	static CStore<CClumpModelInfo, CLUMPMODELSIZE> ms_clumpModelStore;
	static CStore<CPedModelInfo, PEDMODELSIZE> ms_pedModelStore;
	static CStore<CVehicleModelInfo, VEHICLEMODELSIZE> ms_vehicleModelStore;
	static CStore<C2dEffect, TWODFXSIZE> ms_2dEffectStore;
	static CStore<CXtraCompsModelInfo, XTRACOMPSMODELSIZE> ms_xtraCompsModelStore;

public:
	static void Initialise(void);
	static void ShutDown(void);

	static CSimpleModelInfo *AddSimpleModel(int id);
	static CMloModelInfo *AddMloModel(int id);
	static CTimeModelInfo *AddTimeModel(int id);
	static CClumpModelInfo *AddClumpModel(int id);
	static CPedModelInfo *AddPedModel(int id);
	static CVehicleModelInfo *AddVehicleModel(int id);

	static CStore<C2dEffect, TWODFXSIZE> &Get2dEffectStore(void) { return ms_2dEffectStore; }
	static CStore<CInstance, MLOINSTANCESIZE> &GetMloInstanceStore(void) { return ms_mloInstanceStore; }

	static CBaseModelInfo *GetModelInfo(const char *name, int *id);
	static CBaseModelInfo *GetModelInfo(int id){
		return ms_modelInfoPtrs[id];
	}
	static CColModel *GetColModel(int id){
		return ms_modelInfoPtrs[id]->GetColModel();
	}

	static bool IsBoatModel(int32 id);
	static bool IsBikeModel(int32 id);
	static void RemoveColModelsFromOtherLevels(eLevelName level);
	static void ConstructMloClumps();
	static void ReInit2dEffects();
};


code:
#include "common.h"

#include "RwHelper.h"
#include "General.h"
#include "Bones.h"
#include "SurfaceTable.h"
#include "Ped.h"
#include "NodeName.h"
#include "VisibilityPlugins.h"
#include "ModelInfo.h"
#include "custompipes.h"

void
CPedModelInfo::DeleteRwObject(void)
{
	if(m_hitColModel)
		delete m_hitColModel;
	m_hitColModel = nil;
#ifdef PED_SKIN
	RwFrame *frame;
	if(m_head){
		frame = RpAtomicGetFrame(m_head);
		RpAtomicDestroy(m_head);
		RwFrameDestroy(frame);
		m_head = nil;
	}
	if(m_lhand){
		frame = RpAtomicGetFrame(m_lhand);
		RpAtomicDestroy(m_lhand);
		RwFrameDestroy(frame);
		m_lhand = nil;
	}
	if(m_rhand){
		frame = RpAtomicGetFrame(m_rhand);
		RpAtomicDestroy(m_rhand);
		RwFrameDestroy(frame);
		m_rhand = nil;
	}
#endif
	CClumpModelInfo::DeleteRwObject();	// PC calls this first
}

RwObjectNameIdAssocation CPedModelInfo::m_pPedIds[PED_NODE_MAX] = {
	{ "Smid",	PED_MID, 0, },	// that is strange...
	{ "Shead",	PED_HEAD, 0, },
	{ "Supperarml",	PED_UPPERARML, 0, },
	{ "Supperarmr",	PED_UPPERARMR, 0, },
	{ "SLhand",	PED_HANDL, 0, },
	{ "SRhand",	PED_HANDR, 0, },
	{ "Supperlegl",	PED_UPPERLEGL, 0, },
	{ "Supperlegr",	PED_UPPERLEGR, 0, },
	{ "Sfootl",	PED_FOOTL, 0, },
	{ "Sfootr",	PED_FOOTR, 0, },
	{ "Slowerlegr",	PED_LOWERLEGR, 0, },
	{ nil,	0, 0, },
};

#ifdef PED_SKIN
struct LimbCBarg
{
	CPedModelInfo *mi;
	RpClump *clump;
	int32 frameIDs[3];
};

RpAtomic*
CPedModelInfo::findLimbsCb(RpAtomic *atomic, void *data)
{
	LimbCBarg *limbs = (LimbCBarg*)data;
	RwFrame *frame = RpAtomicGetFrame(atomic);
	const char *name = GetFrameNodeName(frame);
	if(CGeneral::faststricmp(name, "Shead01") == 0){
		limbs->frameIDs[0] = RpHAnimFrameGetID(frame);
		limbs->mi->m_head = atomic;
		RpClumpRemoveAtomic(limbs->clump, atomic);
		RwFrameRemoveChild(frame);
	}else if(CGeneral::faststricmp(name, "SLhand01") == 0){
		limbs->frameIDs[1] = RpHAnimFrameGetID(frame);
		limbs->mi->m_lhand = atomic;
		RpClumpRemoveAtomic(limbs->clump, atomic);
		RwFrameRemoveChild(frame);
	}else if(CGeneral::faststricmp(name, "SRhand01") == 0){
		limbs->frameIDs[2] = RpHAnimFrameGetID(frame);
		limbs->mi->m_rhand = atomic;
		RpClumpRemoveAtomic(limbs->clump, atomic);
		RwFrameRemoveChild(frame);
	}
	return atomic;
}
#endif

void
CPedModelInfo::SetClump(RpClump *clump)
{
#ifdef EXTENDED_PIPELINES
	CustomPipes::AttachRimPipe(clump);
#endif
#ifdef PED_SKIN
	// CB has to be set here before atomics are detached from clump
	if(strcmp(GetModelName(), "player") == 0)
		RpClumpForAllAtomics(clump, SetAtomicRendererCB, (void*)CVisibilityPlugins::RenderPlayerCB);
	if(IsClumpSkinned(clump)){
		LimbCBarg limbs = { this, clump, { 0, 0, 0 } };
		RpClumpForAllAtomics(clump, findLimbsCb, &limbs);
	}
	CClumpModelInfo::SetClump(clump);
	SetFrameIds(m_pPedIds);
	if(m_hitColModel == nil && !IsClumpSkinned(clump))
		CreateHitColModel();
	// And again because CClumpModelInfo resets it
	if(strcmp(GetModelName(), "player") == 0)
		RpClumpForAllAtomics(m_clump, SetAtomicRendererCB, (void*)CVisibilityPlugins::RenderPlayerCB);
	else if(IsClumpSkinned(clump))
		// skinned peds have no low detail version, so they don't have the right render Cb
		RpClumpForAllAtomics(m_clump, SetAtomicRendererCB, (void*)CVisibilityPlugins::RenderPedCB);
#else
	CClumpModelInfo::SetClump(clump);
	SetFrameIds(m_pPedIds);
	if(m_hitColModel == nil)
		CreateHitColModel();
	if(strcmp(GetModelName(), "player") == 0)
		RpClumpForAllAtomics(m_clump, SetAtomicRendererCB, (void*)CVisibilityPlugins::RenderPlayerCB);
#endif
}

RpAtomic*
CountAtomicsCB(RpAtomic *atomic, void *data)
{
	(*(int32*)data)++;
	return atomic;
}

RpAtomic*
GetAtomicListCB(RpAtomic *atomic, void *data)
{
	**(RpAtomic***)data = atomic;
	(*(RpAtomic***)data)++;
	return atomic;
}

RwFrame*
FindPedFrameFromNameCB(RwFrame *frame, void *data)
{
	RwObjectNameAssociation *assoc = (RwObjectNameAssociation*)data;

	if(CGeneral::faststricmp(GetFrameNodeName(frame)+1, assoc->name+1)){
		RwFrameForAllChildren(frame, FindPedFrameFromNameCB, assoc);
		return assoc->frame ? nil : frame;
	}else{
		assoc->frame = frame;
		return nil;
	}
}

void
CPedModelInfo::SetLowDetailClump(RpClump *lodclump)
{
	RpAtomic *atomics[16];
	RpAtomic **pAtm;
	int32 numAtm, numLodAtm;
	int i;
	RwObjectNameAssociation assoc;

	numAtm = 0;
	numLodAtm = 0;
	RpClumpForAllAtomics(m_clump, CountAtomicsCB, &numAtm);		// actually unused
	RpClumpForAllAtomics(lodclump, CountAtomicsCB, &numLodAtm);

	RpClumpForAllAtomics(m_clump, SetAtomicRendererCB, (void*)CVisibilityPlugins::RenderPedHiDetailCB);
	RpClumpForAllAtomics(lodclump, SetAtomicRendererCB, (void*)CVisibilityPlugins::RenderPedLowDetailCB);

	pAtm = atomics;
	RpClumpForAllAtomics(lodclump, GetAtomicListCB, &pAtm);

	for(i = 0; i < numLodAtm; i++){
		assoc.name = GetFrameNodeName(RpAtomicGetFrame(atomics[i]));
		assoc.frame = nil;
		RwFrameForAllChildren(RpClumpGetFrame(m_clump), FindPedFrameFromNameCB, &assoc);
		if(assoc.frame){
			RpAtomicSetFrame(atomics[i], assoc.frame);
			RpClumpRemoveAtomic(lodclump, atomics[i]);
			RpClumpAddAtomic(m_clump, atomics[i]);
		}
	}
}

struct ColNodeInfo
{
	Const char *name;
	int pedNode;
	int pieceType;
	float x, z;
	float radius;
};

#define NUMPEDINFONODES 8
ColNodeInfo m_pColNodeInfos[NUMPEDINFONODES] = {
	{ nil,          PED_HEAD,		PEDPIECE_HEAD,  0.0f,   0.05f, 0.2f },
	{ "Storso",     0,				PEDPIECE_TORSO,  0.0f,   0.15f, 0.2f },
	{ "Storso",     0,				PEDPIECE_TORSO,  0.0f,  -0.05f, 0.3f },
	{ nil,          PED_MID,		PEDPIECE_MID,  0.0f,  -0.07f, 0.3f },
	{ nil,          PED_UPPERARML,	PEDPIECE_LEFTARM,  0.07f, -0.1f,  0.2f },
	{ nil,          PED_UPPERARMR,	PEDPIECE_RIGHTARM, -0.07f, -0.1f,  0.2f },
	{ "Slowerlegl", 0,				PEDPIECE_LEFTLEG,  0.0f,   0.07f, 0.25f },
	{ nil,          PED_LOWERLEGR,	PEDPIECE_RIGHTLEG,  0.0f,   0.07f, 0.25f },
};

RwObject*
FindHeadRadiusCB(RwObject *object, void *data)
{
	RpAtomic *atomic = (RpAtomic*)object;
	*(float*)data = RpAtomicGetBoundingSphere(atomic)->radius;
	return nil;
}

void
CPedModelInfo::CreateHitColModel(void)
{
	RwObjectNameAssociation nameAssoc;
	RwObjectIdAssociation idAssoc;
	RwFrame *nodeFrame;
	CColModel *colmodel = new CColModel;
	CColSphere *spheres = (CColSphere*)RwMalloc(NUMPEDINFONODES*sizeof(CColSphere));
	RwFrame *root = RpClumpGetFrame(m_clump);
	RwMatrix *mat = RwMatrixCreate();
	for(int i = 0; i < NUMPEDINFONODES; i++){
		nodeFrame = nil;
		if(m_pColNodeInfos[i].name){
			nameAssoc.name = m_pColNodeInfos[i].name;
			nameAssoc.frame = nil;
			RwFrameForAllChildren(root, FindFrameFromNameCB, &nameAssoc);
			nodeFrame = nameAssoc.frame;
		}else{
			idAssoc.id = m_pColNodeInfos[i].pedNode;
			idAssoc.frame = nil;
			RwFrameForAllChildren(root, FindFrameFromIdCB, &idAssoc);
			nodeFrame = idAssoc.frame;
		}
		if(nodeFrame){
			float radius = m_pColNodeInfos[i].radius;
			if(m_pColNodeInfos[i].pieceType == PEDPIECE_HEAD)
				RwFrameForAllObjects(nodeFrame, FindHeadRadiusCB, &radius);
			RwMatrixTransform(mat, RwFrameGetMatrix(nodeFrame), rwCOMBINEREPLACE);
			const char *name = GetFrameNodeName(nodeFrame);
			for(nodeFrame = RwFrameGetParent(nodeFrame);
			    nodeFrame;
			    nodeFrame = RwFrameGetParent(nodeFrame)){
				name = GetFrameNodeName(nodeFrame);
				RwMatrixTransform(mat, RwFrameGetMatrix(nodeFrame), rwCOMBINEPOSTCONCAT);
				if(RwFrameGetParent(nodeFrame) == root)
					break;
			}
			spheres[i].center = mat->pos + CVector(m_pColNodeInfos[i].x, 0.0f, m_pColNodeInfos[i].z);
			spheres[i].radius = radius;
			spheres[i].surface = SURFACE_PED;
			spheres[i].piece = m_pColNodeInfos[i].pieceType;
		}
	}
	RwMatrixDestroy(mat);
	colmodel->spheres = spheres;
	colmodel->numSpheres = NUMPEDINFONODES;
	colmodel->boundingSphere.Set(2.0f, CVector(0.0f, 0.0f, 0.0f), SURFACE_DEFAULT, 0);
	colmodel->boundingBox.Set(CVector(-0.5f, -0.5f, -1.2f), CVector(0.5f, 0.5f, 1.2f), SURFACE_DEFAULT, 0);
	colmodel->level = LEVEL_GENERIC;
	m_hitColModel = colmodel;
}

CColModel*
CPedModelInfo::AnimatePedColModel(CColModel* colmodel, RwFrame* frame)
{
	RwObjectNameAssociation nameAssoc;
	RwObjectIdAssociation idAssoc;
	RwMatrix* mat = RwMatrixCreate();
	CColSphere* spheres = colmodel->spheres;

	for (int i = 0; i < NUMPEDINFONODES; i++) {
		RwFrame* f = nil;
		if (m_pColNodeInfos[i].name) {
			nameAssoc.name = m_pColNodeInfos[i].name;
			nameAssoc.frame = nil;
			RwFrameForAllChildren(frame, FindFrameFromNameCB, &nameAssoc);
			f = nameAssoc.frame;
		}
		else {
			idAssoc.id = m_pColNodeInfos[i].pedNode;
			idAssoc.frame = nil;
			RwFrameForAllChildren(frame, FindFrameFromIdCB, &idAssoc);
			f = idAssoc.frame;
		}
		if (f) {
			RwMatrixCopy(mat, RwFrameGetMatrix(f));

			for (f = RwFrameGetParent(f); f; f = RwFrameGetParent(f)) {
				RwMatrixTransform(mat, RwFrameGetMatrix(f), rwCOMBINEPOSTCONCAT);
				if (RwFrameGetParent(f) == frame)
					break;
			}

			spheres[i].center = mat->pos + CVector(m_pColNodeInfos[i].x, 0.0f, m_pColNodeInfos[i].z);
		}
	}

	return colmodel;
}

#ifdef PED_SKIN
void
CPedModelInfo::CreateHitColModelSkinned(RpClump *clump)
{
	RpHAnimHierarchy *hier = GetAnimHierarchyFromSkinClump(clump);
	CColModel *colmodel = new CColModel;
	CColSphere *spheres = (CColSphere*)RwMalloc(NUMPEDINFONODES*sizeof(CColSphere));
	RwFrame *root = RpClumpGetFrame(m_clump);
	RwMatrix *invmat = RwMatrixCreate();
	RwMatrix *mat = RwMatrixCreate();
	RwMatrixInvert(invmat, RwFrameGetMatrix(RpClumpGetFrame(clump)));

	for(int i = 0; i < NUMPEDINFONODES; i++){
		*mat = *invmat;
		// From LCS. Otherwise gives FPE
#ifdef FIX_BUGS
		spheres[i].center = CVector(0.0f, 0.0f, 0.0f);
#else
		int id = ConvertPedNode2BoneTag(m_pColNodeInfos[i].pedNode);	// this is wrong, wtf R* ???
		int idx = RpHAnimIDGetIndex(hier, id);

		// This doesn't really work as the positions are not initialized yet
		RwMatrixTransform(mat, &RpHAnimHierarchyGetMatrixArray(hier)[idx], rwCOMBINEPRECONCAT);
		RwV3d pos = { 0.0f, 0.0f, 0.0f };
		RwV3dTransformPoints(&pos, &pos, 1, mat);

		spheres[i].center = pos + CVector(m_pColNodeInfos[i].x, 0.0f, m_pColNodeInfos[i].z);
#endif
		spheres[i].radius = m_pColNodeInfos[i].radius;
		spheres[i].surface = SURFACE_PED;
		spheres[i].piece = m_pColNodeInfos[i].pieceType;
	}
	RwMatrixDestroy(invmat);
	RwMatrixDestroy(mat);
	colmodel->spheres = spheres;
	colmodel->numSpheres = NUMPEDINFONODES;
	colmodel->boundingSphere.Set(2.0f, CVector(0.0f, 0.0f, 0.0f), SURFACE_DEFAULT, 0);
	colmodel->boundingBox.Set(CVector(-0.5f, -0.5f, -1.2f), CVector(0.5f, 0.5f, 1.2f), SURFACE_DEFAULT, 0);
	colmodel->level = LEVEL_GENERIC;
	m_hitColModel = colmodel;
}

CColModel*
CPedModelInfo::AnimatePedColModelSkinned(RpClump *clump)
{
	if(m_hitColModel == nil){
		CreateHitColModelSkinned(clump);
		return m_hitColModel;
	}
	RwMatrix *invmat, *mat;
	CColSphere *spheres = m_hitColModel->spheres;
	RpHAnimHierarchy *hier = GetAnimHierarchyFromSkinClump(clump);
	invmat = RwMatrixCreate();
	mat = RwMatrixCreate();
	RwMatrixInvert(invmat, RwFrameGetMatrix(RpClumpGetFrame(clump)));

	for(int i = 0; i < NUMPEDINFONODES; i++){
		*mat = *invmat;
		int id = ConvertPedNode2BoneTag(m_pColNodeInfos[i].pedNode);
		int idx = RpHAnimIDGetIndex(hier, id);

		RwMatrixTransform(mat, &RpHAnimHierarchyGetMatrixArray(hier)[idx], rwCOMBINEPRECONCAT);
		RwV3d pos = { 0.0f, 0.0f, 0.0f };
		RwV3dTransformPoints(&pos, &pos, 1, mat);

		spheres[i].center = pos + CVector(m_pColNodeInfos[i].x, 0.0f, m_pColNodeInfos[i].z);
	}
	RwMatrixDestroy(invmat);
	RwMatrixDestroy(mat);
	return m_hitColModel;
}

#endif


code:
#pragma once

#include "ClumpModelInfo.h"
#include "ColModel.h"
#include "PedType.h"

enum PedNode {
	PED_TORSO,
	PED_MID,	// Smid on PS2/PC, Storso on mobile/xbox
	PED_HEAD,
	PED_UPPERARML,
	PED_UPPERARMR,
	PED_HANDL,
	PED_HANDR,
	PED_UPPERLEGL,
	PED_UPPERLEGR,
	PED_FOOTL,
	PED_FOOTR,
	PED_LOWERLEGR,
	PED_NODE_MAX// Not valid: PED_LOWERLEGL
};

class CPedModelInfo : public CClumpModelInfo
{
public:
	uint32 m_animGroup;
	ePedType m_pedType;
	ePedStats m_pedStatType;
	uint32 m_carsCanDrive;
	CColModel *m_hitColModel;
#ifdef PED_SKIN
	RpAtomic *m_head;
	RpAtomic *m_lhand;
	RpAtomic *m_rhand;
#endif

	static RwObjectNameIdAssocation m_pPedIds[PED_NODE_MAX];

	CPedModelInfo(void) : CClumpModelInfo(MITYPE_PED) {
		m_hitColModel = nil;
#ifdef PED_SKIN
		m_head = nil;
		m_lhand = nil;
		m_rhand = nil;
#endif
	}
	~CPedModelInfo(void) { delete m_hitColModel; }
	void DeleteRwObject(void);
	void SetClump(RpClump *);

	void SetLowDetailClump(RpClump*);
	void CreateHitColModel(void);
	void CreateHitColModelSkinned(RpClump *clump);
	CColModel *GetHitColModel(void) { return m_hitColModel; }
	static CColModel *AnimatePedColModel(CColModel* colmodel, RwFrame* frame);
	CColModel *AnimatePedColModelSkinned(RpClump *clump);

#ifdef PED_SKIN
	static RpAtomic *findLimbsCb(RpAtomic *atomic, void *data);
	RpAtomic *getHead(void) { return m_head; }
	RpAtomic *getLeftHand(void) { return m_lhand; }
	RpAtomic *getRightHand(void) { return m_rhand; }
#endif
};
#ifndef PED_SKIN
VALIDATE_SIZE(CPedModelInfo, 0x48);
#endif


code:
#include "common.h"

#include "General.h"
#include "Camera.h"
#include "Renderer.h"
#include "ModelInfo.h"
#include "custompipes.h"

void
CSimpleModelInfo::DeleteRwObject(void)
{
	int i;
	RwFrame *f;
	for(i = 0; i < m_numAtomics; i++)
		if(m_atomics[i]){
			f = RpAtomicGetFrame(m_atomics[i]);
			RpAtomicDestroy(m_atomics[i]);
			RwFrameDestroy(f);
			m_atomics[i] = nil;
			RemoveTexDictionaryRef();
		}
}

RwObject*
CSimpleModelInfo::CreateInstance(void)
{
	RpAtomic *atomic;
	if(m_atomics[0] == nil)
		return nil;
	atomic = RpAtomicClone(m_atomics[0]);
	RpAtomicSetFrame(atomic, RwFrameCreate());
	return (RwObject*)atomic;
}

RwObject*
CSimpleModelInfo::CreateInstance(RwMatrix *matrix)
{
	RpAtomic *atomic;
	RwFrame *frame;

	if(m_atomics[0] == nil)
		return nil;
	atomic = RpAtomicClone(m_atomics[0]);
	frame = RwFrameCreate();
	*RwFrameGetMatrix(frame) = *matrix;
	RpAtomicSetFrame(atomic, frame);
	return (RwObject*)atomic;
}

void
CSimpleModelInfo::Init(void)
{
	m_atomics[0] = nil;
	m_atomics[1] = nil;
	m_atomics[2] = nil;
	m_numAtomics = 0;
	m_firstDamaged  = 0;
	m_normalCull    = 0;
	m_isDamaged     = 0;
	m_isBigBuilding = 0;
	m_noFade        = 0;
	m_drawLast      = 0;
	m_additive      = 0;
	m_isSubway      = 0;
	m_ignoreLight   = 0;
	m_noZwrite      = 0;
}

void
CSimpleModelInfo::SetAtomic(int n, RpAtomic *atomic)
{
	AddTexDictionaryRef();
	m_atomics[n] = atomic;
	if(m_ignoreLight){
		RpGeometry *geo = RpAtomicGetGeometry(atomic);
		RpGeometrySetFlags(geo, RpGeometryGetFlags(geo) & ~rpGEOMETRYLIGHT);
	}

#ifdef EXTENDED_PIPELINES
	CustomPipes::AttachWorldPipe(atomic);
#endif
}

void
CSimpleModelInfo::SetLodDistances(float *dist)
{
	m_lodDistances[0] = dist[0];
	m_lodDistances[1] = dist[1];
	m_lodDistances[2] = dist[2];
}

void
CSimpleModelInfo::IncreaseAlpha(void)
{
	if(m_alpha >= 0xEF)
		m_alpha = 0xFF;
	else
		m_alpha += 0x10;
}

float
CSimpleModelInfo::GetLodDistance(int i)
{
	return m_lodDistances[i] * TheCamera.LODDistMultiplier;
}

float
CSimpleModelInfo::GetNearDistance(void)
{
	return m_lodDistances[2] * TheCamera.LODDistMultiplier;
}

float
CSimpleModelInfo::GetLargestLodDistance(void)
{
	float d;
	if(m_firstDamaged == 0 || m_isDamaged)
		d = m_lodDistances[m_numAtomics-1];
	else
		d = m_lodDistances[m_firstDamaged-1];
	return d * TheCamera.LODDistMultiplier;
}

RpAtomic*
CSimpleModelInfo::GetAtomicFromDistance(float dist)
{
	int i;
	i = 0;
	if(m_isDamaged)
		i = m_firstDamaged;
	for(; i < m_numAtomics; i++)
		if(dist < m_lodDistances[i] * TheCamera.LODDistMultiplier)
			return m_atomics[i];
	return nil;
}

void
CSimpleModelInfo::FindRelatedModel(void)
{
	int i;
	CBaseModelInfo *mi;
	for(i = 0; i < MODELINFOSIZE; i++){
		mi = CModelInfo::GetModelInfo(i);
		if(mi && mi != this &&
		   !CGeneral::faststrcmp(GetModelName()+3, mi->GetModelName()+3)){
			assert(mi->IsSimple());
			this->SetRelatedModel((CSimpleModelInfo*)mi);
			return;
		}
	}
}

void
CSimpleModelInfo::SetupBigBuilding(void)
{
	CSimpleModelInfo *related;
	if(m_lodDistances[0] > LOD_DISTANCE && GetRelatedModel() == nil){
		m_isBigBuilding = 1;
		FindRelatedModel();
		related = GetRelatedModel();
		if(related)
			m_lodDistances[2] = related->GetLargestLodDistance()/TheCamera.LODDistMultiplier;
		else
#ifdef FIX_BUGS
		if(toupper(m_name[0]) == 'L' && toupper(m_name[1]) == 'O' && toupper(m_name[2]) == 'D')
			m_lodDistances[2] = 100.0f;
		else
			m_lodDistances[2] = 0.0f;
#else
			m_lodDistances[2] = 100.0f;
#endif
	}
}


code:
#pragma once

#include "BaseModelInfo.h"

class CSimpleModelInfo : public CBaseModelInfo
{
public:
	// atomics[2] is often a pointer to the non-LOD modelinfo
	RpAtomic *m_atomics[3];
	// m_lodDistances[2] holds the near distance for LODs
	float  m_lodDistances[3];
	uint8  m_numAtomics;
	uint8  m_alpha;
	/* // For reference, PS2 has:
	uint8  m_firstDamaged;
	uint8  m_normalCull    : 1;
	uint8  m_isDamaged     : 1;
	uint8  m_isBigBuilding : 1;
	uint8  m_noFade        : 1;
	uint8  m_drawLast      : 1;
	uint8  m_additive      : 1;
	uint8  m_isSubway      : 1;
	uint8  m_ignoreLight   : 1;
	// m_noZwrite is missing because not needed
	*/
	uint16 m_firstDamaged   : 2; // 0: no damage model
	                         // 1: 1 and 2 are damage models
	                         // 2: 2 is damage model
	uint16  m_normalCull    : 1;
	uint16  m_isDamaged     : 1;
	uint16  m_isBigBuilding : 1;
	uint16  m_noFade        : 1;
	uint16  m_drawLast      : 1;
	uint16  m_additive      : 1;
	uint16  m_isSubway      : 1;
	uint16  m_ignoreLight   : 1;
	uint16  m_noZwrite      : 1;

	CSimpleModelInfo(void) : CBaseModelInfo(MITYPE_SIMPLE) {}
	CSimpleModelInfo(ModelInfoType id) : CBaseModelInfo(id) {}
	~CSimpleModelInfo() {}
	void DeleteRwObject(void);
	RwObject *CreateInstance(void);
	RwObject *CreateInstance(RwMatrix *);
	RwObject *GetRwObject(void) { return (RwObject*)m_atomics[0]; }

	void Init(void);
	void IncreaseAlpha(void);
	void SetAtomic(int n, RpAtomic *atomic);
	void SetLodDistances(float *dist);
	float GetLodDistance(int i);
	float GetNearDistance(void);
	float GetLargestLodDistance(void);
	RpAtomic *GetAtomicFromDistance(float dist);
	void FindRelatedModel(void);
	void SetupBigBuilding(void);

	void SetNumAtomics(int n) { m_numAtomics = n; }
	CSimpleModelInfo *GetRelatedModel(void){
		return (CSimpleModelInfo*)m_atomics[2]; }
	void SetRelatedModel(CSimpleModelInfo *m){
		m_atomics[2] = (RpAtomic*)m; }
};

VALIDATE_SIZE(CSimpleModelInfo, 0x4C);


code:
#include "common.h"

#include "Camera.h"
#include "ModelInfo.h"
#include "General.h"

CTimeModelInfo*
CTimeModelInfo::FindOtherTimeModel(void)
{
	char name[40];
	char *p;
	int i;

	strcpy(name, GetModelName());
	// change _nt to _dy
	if(p = strstr(name, "_nt"))
		strncpy(p, "_dy", 4);
	// change _dy to _nt
	else if(p = strstr(name, "_dy"))
		strncpy(p, "_nt", 4);
	else
		return nil;

	for(i = 0; i < MODELINFOSIZE; i++){
		CBaseModelInfo *mi = CModelInfo::GetModelInfo(i);
		if (mi && mi->GetModelType() == MITYPE_TIME &&
		   !CGeneral::faststrncmp(name, mi->GetModelName(), MAX_MODEL_NAME)){
			m_otherTimeModelID = i;
			return (CTimeModelInfo*)mi;
		}
	}
	return nil;
}


code:
#pragma once

#include "SimpleModelInfo.h"

class CTimeModelInfo : public CSimpleModelInfo
{
	int32 m_timeOn;
	int32 m_timeOff;
	int32 m_otherTimeModelID;
public:
	CTimeModelInfo(void) : CSimpleModelInfo(MITYPE_TIME) { m_otherTimeModelID = -1; }

	int32 GetTimeOn(void) { return m_timeOn; }
	int32 GetTimeOff(void) { return m_timeOff; }
	void SetTimes(int32 on, int32 off) { m_timeOn = on; m_timeOff = off; }
	int32 GetOtherTimeModel(void) { return m_otherTimeModelID; }
	void SetOtherTimeModel(int32 other) { m_otherTimeModelID = other; }
	CTimeModelInfo *FindOtherTimeModel(void);
};

VALIDATE_SIZE(CTimeModelInfo, 0x58);