home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Learn 3D Graphics Programming on the PC
/
Learn_3D_Graphics_Programming_on_the_PC_Ferraro.iso
/
rwdos
/
gunbit.c
< prev
next >
Wrap
C/C++ Source or Header
|
1995-02-15
|
29KB
|
916 lines
/**********************************************************************
*
* File : gunbit.c
*
* Abstract : The gun and explosion bit handler. This module handles
* the movement and display of the gun and the bits that
* appear when an object is struck or explodes.
*
**********************************************************************
*
* This file is a product of Criterion Software Ltd.
*
* This file is provided as is with no warranties of any kind and is
* provided without any obligation on Criterion Software Ltd. or
* Canon Inc. to assist in its use or modification.
*
* Criterion Software Ltd. will not, under any
* circumstances, be liable for any lost revenue or other damages arising
* from the use of this file.
*
* Copyright (c) 1995 Criterion Software Ltd.
* All Rights Reserved.
*
* RenderWare is a trademark of Canon Inc.
*
************************************************************************/
/*--- Include Files ---*/
#include "global.h" /* Application general includes */
/*--- Macros and Magic Number definitions */
#define GUN_UP -0.26 /* The gun position when it is fully
up (ie the normal firing position */
#define GUN_REMOVE -0.5 /* The gun is removed once it drops
below this value */
#define GUN_FORWARD 1.0 /* distance gun is in front of the
camera */
#define GUN_SPEED 0.05 /* When the gun is rising and falling
it moves at this rate */
#define GUNSIGHT_POS 6.0 /* The distance that the gun is drawn
in front of the camera */
#define EXPLOSION_HEIGHT 0.1 /* The start height for the bits of
explosion */
#define BIT_BOUNCE (-1.0/2.0) /* The change in velocity of a bit
when it hits the ground */
/* Bit Definitions */
/* define number of random matrices to create for random bit orientation
* Note: MAX_BIT_ROTATIONS must be n^2 to allow BIT_ROT_MASK to be used as a
* selector
*/
#define MAX_BIT_ROTATIONS 8 /* Number of different bit rotations */
#define BIT_ROT_MASK (MAX_BIT_ROTATIONS - 1)
#define EXP_VEL 0.05 /* The maximum velocity of an
exploding bit */
#define BIT_SIZE 0.025 /* The size of a bit */
#define SPARK_BITS 1 /* no of bits created for a spark */
#define TROPHY_BITS 2 /* no of bits created when the trophy is hit */
#define HIT_BITS 5 /* no of bits created when a rat is hit */
#define EXPLOSION_BITS 20 /* no of bits created when a rat explodes */
/*--- Structure Definitions ---*/
/* Bit: A bit is one of the small particles that can be observed when the gun
* fires at a wall or the trophy (a spark) or when a Rat explodes. Bits are
* created in blocks. The size of the block depends on the type of bit being
* created.
*/
typedef struct
{
Object oObj; /* Bit's are objects just like everything else */
int *npCount; /* Reference counter. When this is zero all Bits
in the current group have been destroyed and
the global structure can also be destroyed */
int nPos; /* identifies which of the Random bit rotation
matrices this Bit is using */
RwV3d vVel; /* the velocity of the Bit */
} Bit;
/* All Bits: A single global variable of this type is created and used as
* a global pool for the Bit Data
*/
typedef struct
{
RwClump *cpExplode; /* The clump for the Explosion Bit */
RwClump *cpSpark; /* The clump for the Spark bit */
#ifdef WITH_SHADOWS
RwClump *cpShadow; /* The clump for the Bit's shadow. Both
Bit types share the same shadow clump */
#endif
RwMatrix4d *mpaPos[MAX_BIT_ROTATIONS]; /* Random Bit orientation matrices */
RwMatrix4d *mpaRot[MAX_BIT_ROTATIONS]; /* Small rotations incrementally applied
to mpaPos */
} AllBits;
/* Gun definition */
typedef struct
{
RwClump *cpGun; /* The clump for the Gun */
RwClump *cpSight; /* The clump for the Gun Sight */
RwClump *cpHit;
RwReal nPos;
RwTexture *tpTex;
int nFiring; /* TRUE if gun is firing, FALSE otherwise */
int nFireFlag; /* TRUE if gun has been fired */
RwInt32 nFrameCount;
int nEnabled; /* 1 if enabled, 0 if disabled */
} Gun;
/*--- Global Variable ---*/
static Gun gGGun;
static RwPickRecord prGCityPick;
static RwPickRecord prGPick;
static AllBits abGBits;
/*--- Function Definitions ---*/
/* Bit Functions */
/************************************************************************
*
* Function: AllBitsSetup()
*
* Description: Initialise the global AllBits data structure
*
* Return Value: TRUE on success, FALSE on failure
*
************************************************************************/
int AllBitsSetup(void)
{
int nCount; RwV3d vTmp;
/* Read the RenderWare script files for the 3 bit clump types */
if (!(abGBits.cpExplode = DoRwReadShape("bit.rwx")) ||
#ifdef WITH_SHADOWS
!(abGBits.cpShadow = DoRwReadShape("bitshad.rwx")) ||
#endif
!(abGBits.cpSpark = DoRwReadShape("spark.rwx")))
{
return FALSE; /* Unable to read script files - Error */
}
/* Create the Random Rotation matrices */
for (nCount=0; nCount < MAX_BIT_ROTATIONS; nCount++)
{
if (!(abGBits.mpaRot[nCount] = RwCreateMatrix()) ||
!(abGBits.mpaPos[nCount] = RwCreateMatrix()))
{
return FALSE; /* Unable to create matrices - Error */
}
/* The mpaRot matrices are rotation matrices about an arbitrary axis
with an angular component between +10.0 and -10.0 degrees */
RandomVec(&vTmp);
RwRotateMatrix(abGBits.mpaRot[nCount],
vTmp.x, vTmp.y, vTmp.z,
RANDOM_REAL(CREAL(-10.0), CREAL(10.0)),
rwREPLACE);
/* The mpaPos matrices are rotation matrices about an arbitrary axis
with a random orientation */
RandomVec(&vTmp);
RwRotateMatrix(abGBits.mpaPos[nCount],
vTmp.x,vTmp.y,vTmp.z,
RANDOM_REAL(CREAL(0.0), CREAL(360.0)),
rwREPLACE);
}
return TRUE; /* success */
}
/************************************************************************
*
* Function: AllBitsDestroy()
*
* Description: Destroy the global AllBits data structure
*
*
************************************************************************/
void AllBitsDestroy(void)
{
int nCount;
RwDestroyClump(abGBits.cpExplode); /* destroy the Explosion clump */
#ifdef WITH_SHADOWS
RwDestroyClump(abGBits.cpShadow); /* destroy the shadow clump */
#endif
RwDestroyClump(abGBits.cpSpark); /* destroy the spark clump */
/* destroy the random rotation matrices */
for (nCount=0; nCount < MAX_BIT_ROTATIONS; nCount++)
{
RwDestroyMatrix(abGBits.mpaRot[nCount]);
RwDestroyMatrix(abGBits.mpaPos[nCount]);
}
}
/************************************************************************
*
* Function: AllBitsUpdate()
*
* Description: Increment all of the orientation matrices in the
* global AllBits data structure.
*
************************************************************************/
void AllBitsUpdate(void)
{
int nCount;
static int nOrtho; /* orthonormalise a different matrix each time this function
is called */
for (nCount=0; nCount < MAX_BIT_ROTATIONS; nCount++)
{
/* Postconcat the mpaRot incremental matrix
onto the mpaPos position matrix */
RwTransformMatrix(abGBits.mpaPos[nCount],
abGBits.mpaRot[nCount],
rwPOSTCONCAT);
}
nOrtho++;
RwOrthoNormalizeMatrix(abGBits.mpaPos[nOrtho & BIT_ROT_MASK],
abGBits.mpaPos[nOrtho & BIT_ROT_MASK]);
}
/************************************************************************
*
* Function: BitObjectDestroy()
*
* Description: Destroy a single Bit Object
*
************************************************************************/
static void BitObjectDestroy(Object *opObj)
{
Bit *bpBit;
bpBit = opObj->pExtra;
/* since Bit's are allocated in blocks, decrement the reference
count and only free the allocated space once all Bit's in the
block have been freed */
if (--(*(bpBit->npCount)) <= 0)
{
free(bpBit->npCount);
}
}
/************************************************************************
*
* Function: BitRender()
*
* Description: Render a single Bit Object
*
************************************************************************/
static void BitRender(Object *opObj)
{
Bit *bpBit;
bpBit = opObj->pExtra;
#ifdef WITH_SHADOWS
/* Render the shadow */
/* Check if the bit is in the scene */
if ((opObj->vPos.z > CREAL(FAR_STREET)) &&
(opObj->vPos.z < CREAL(NEAR_STREET)))
{
/* Bit in the scene. Check whether the shadow is on the road
or on the kerb */
if ((opObj->vPos.x < CREAL(LEFT_KERB)) ||
(opObj->vPos.x > CREAL(RIGHT_KERB)) )
{
/* Its on the kerb */
RwTranslateMatrix(RwScratchMatrix(),
opObj->vPos.x,
CREAL(KERB_HEIGHT),
opObj->vPos.z,
rwREPLACE);
}
else
{
/* Its on the street */
RwTranslateMatrix(RwScratchMatrix(),
opObj->vPos.x,
CREAL(STREET_HEIGHT),
opObj->vPos.z,
rwREPLACE);
}
/* Position and Render the shadow */
RwTransformClump(abGBits.cpShadow, RwScratchMatrix(), rwREPLACE);
RwRenderClump(abGBits.cpShadow);
}
#endif /* WITH_SHADOWS */
/* Render the Bit */
/* Get the rotation matrix for this Bit */
RwCopyMatrix(abGBits.mpaPos[bpBit->nPos], RwScratchMatrix());
/* Position the bit */
RwTranslateMatrix(RwScratchMatrix(),
opObj->vPos.x,
opObj->vPos.y,
opObj->vPos.z,
rwPOSTCONCAT);
RwTransformClump(opObj->cpClump, RwScratchMatrix(), rwREPLACE);
/* And render it */
RwRenderClump(opObj->cpClump);
}
/************************************************************************
*
* Function: BitUpdate()
*
* Description: Update the position of a single Bit
*
************************************************************************/
static void BitUpdate(Object *opObj)
{
Bit *bpBit;
bpBit = (Bit *)opObj->pExtra;
/* Move bit to new position */
RwAddVector(&bpBit->oObj.vPos, &bpBit->vVel, &bpBit->oObj.vPos);
/* Apply gravity */
bpBit->vVel.y -= CREAL(GRAVITY);
/* Test for collisions with the side walls */
if(bpBit->vVel.x < CREAL(0.0))
{
/* moving towards left wall */
if (bpBit->oObj.vPos.x < CREAL(LEFT_STREET + BIT_SIZE))
{
/* collided with left wall - bounce */
bpBit->vVel.x = RMul(bpBit->vVel.x, CREAL(RICOCHET));
}
}
else
{
/* moving towards the right wall */
if (bpBit->oObj.vPos.x > CREAL(RIGHT_STREET - BIT_SIZE))
{
bpBit->vVel.x = RMul(bpBit->vVel.x, CREAL(RICOCHET));
}
}
/* Test for collisions with the end walls */
if (bpBit->vVel.z < CREAL(0.0))
{
/* travelling towards the far wall */
if (bpBit->oObj.vPos.z < CREAL(FAR_STREET + BIT_SIZE))
{
/* collided with far wall - bounce */
bpBit->vVel.z = RMul(bpBit->vVel.z, CREAL(RICOCHET));
}
}
else
{
/* travelling towards the near wall */
if (bpBit->oObj.vPos.z > CREAL(NEAR_STREET - BIT_SIZE))
{
/* collided with near wall - bounce */
bpBit->vVel.z = -RMul(bpBit->vVel.z, CREAL(RICOCHET));
}
}
/* Test for collision with side of kerb */
if (bpBit->oObj.vPos.y < CREAL(KERB_HEIGHT + BIT_SIZE))
{
/* below kerb height - possible collision */
if (bpBit->vVel.x < CREAL(0.0))
{
/* moving to the left - test against left kerb */
if (bpBit->oObj.vPos.x < CREAL(LEFT_KERB + BIT_SIZE))
{
/* collided with left kerb - bounce */
bpBit->vVel.x = RMul(bpBit->vVel.x, CREAL(RICOCHET));
}
}
else
{
/* moving to the right - test against right kerb */
if (bpBit->oObj.vPos.x > CREAL(RIGHT_KERB - BIT_SIZE))
{
/* collided with right kerb */
bpBit->vVel.x = RMul(bpBit->vVel.x, CREAL(RICOCHET));
}
}
}
/* Check for collisions with ground */
if (bpBit->vVel.y < CREAL(0.0))
{
/* falling bit */
if ((bpBit->oObj.vPos.x > CREAL(LEFT_KERB + BIT_SIZE)) &&
(bpBit->oObj.vPos.x < CREAL(RIGHT_KERB - BIT_SIZE)))
{
/* Over the road - test against road height */
if (bpBit->oObj.vPos.y < CREAL(STREET_HEIGHT + BIT_SIZE))
{
/* collided with road - bounce */
bpBit->vVel.y = RMul(bpBit->vVel.y, CREAL(BIT_BOUNCE));
}
}
else
{
/* Over the kerb - test against kerb height */
if (bpBit->oObj.vPos.y < CREAL(KERB_HEIGHT + BIT_SIZE))
{
/* collided with kerb - bounce */
bpBit->vVel.y = RMul(bpBit->vVel.y, CREAL(BIT_BOUNCE));
}
}
}
/* If the bit isn't moving in then destroy it */
if ((bpBit->vVel.y < CREAL(0.0)) &&
(bpBit->vVel.y > CREAL(-0.001)) )
{
ObjectDelete(&bpBit->oObj);
}
}
/************************************************************************
*
* Function: BitSetup()
*
* Description: Initialise a single Bit.
*
************************************************************************/
static void BitSetup(
Bit *bpBit, /* the bit to setup */
RwReal nX, /* initial bit position */
RwReal nY,
RwReal nZ,
int *npCount, /* the global reference counter location */
RwClump *cpClump /* the clump type to use for the bit */
)
{
static RwInt32 nPos;
ObjectSetup(&bpBit->oObj,nX,nY,nZ,cpClump); /* initialise the object data */
bpBit->oObj.fpUpdate = BitUpdate; /* function to update object */
bpBit->oObj.fpRender = BitRender; /* function to render object */
bpBit->oObj.fpDestroy = BitObjectDestroy; /* function to destroy object */
bpBit->oObj.pExtra = (void *)bpBit; /* how to get Bit from object */
bpBit->nPos = nPos & BIT_ROT_MASK; /* pick a Bit rotation matrix */
nPos++; /* next call picks next Bit rotation matrix */
/* Set the initial Bit velocity */
bpBit->vVel.x = RANDOM_REAL(CREAL(-EXP_VEL), CREAL(EXP_VEL));
bpBit->vVel.y = RANDOM_REAL(CREAL(-EXP_VEL), CREAL(EXP_VEL));
bpBit->vVel.z = RANDOM_REAL(CREAL(-EXP_VEL), CREAL(EXP_VEL));
bpBit->npCount = npCount; /* Set the Bit reference count */
AllObjectsAddObject(&bpBit->oObj); /* Add this new object to the scene */
}
/************************************************************************
*
* Function: BitExplosionSetup()
*
* Description: Create and Initialise all the Bits for an
* explosion.
*
************************************************************************/
static void BitExplosionSetup(
RwReal nX, /* Start position for the explosion */
RwReal nY,
RwReal nZ,
int nAmo, /* The number of Bits in the explosion */
RwClump *cpClump /* The Bit clump to use for this
explosion */
)
{
Bit *bpBit;
int *npCount;
/* Allocate enough space for all the Bits and a reference counter */
npCount = (int *)malloc(sizeof(int) + (sizeof(Bit) * nAmo));
if (npCount)
{
/* Initialise reference count with the number of bits */
(*npCount) = nAmo;
bpBit = (Bit *) (&npCount[1]);
/* Set up each of the Bits in turn */
for (; nAmo > 0; nAmo--)
{
BitSetup(bpBit, nX, nY, nZ, npCount, cpClump);
bpBit++;
}
}
}
/*--- Gun Functions ---*/
/************************************************************************
*
* Function: GunVisible()
*
* Description: return TRUE if gun visible, FALSE otherwise
*
************************************************************************/
int GunVisible(void)
{
return(!(gGGun.nPos <= CREAL(GUN_REMOVE)));
}
/************************************************************************
*
* Function: GunEnable()
*
* Description: Enable display of the gun
*
************************************************************************/
void GunEnable(void)
{
gGGun.nEnabled = 1;
}
/************************************************************************
*
* Function: GunDisable()
*
* Description: Disable display of the gun
*
************************************************************************/
void GunDisable(void)
{
gGGun.nEnabled = 0;
}
/************************************************************************
*
* Function: GunToggle()
*
* Description: Toggle display of the gun
*
************************************************************************/
void GunToggle(void)
{
gGGun.nEnabled^=1;
}
/************************************************************************
*
* Function: GunFiring()
*
* Description: Set firing state for the gun
*
************************************************************************/
void GunFiring(int state)
{
if (!gGGun.nFiring && state)
{
if (gGGun.nFrameCount==RwGetTextureNumFrames(gGGun.tpTex)-1)
{
gGGun.nFrameCount=0;
gGGun.nFireFlag = 1;
}
}
else
{
gGGun.nFireFlag = 0;
}
gGGun.nFiring=state;
}
/************************************************************************
*
* Function: GunSetup()
*
* Description: Setup the gun
*
************************************************************************/
int GunSetup(void)
{
/* Read the scripts for the gun */
if (!(gGGun.cpGun = DoRwReadShape("gun.rwx")) ||
!(gGGun.cpSight = DoRwReadShape("sight.rwx")) ||
!(gGGun.cpHit = DoRwReadShape("hit.rwx")))
{
/* couldn't read the scripts - fail */
return FALSE;
}
/* Find the gun texture and set the current frame to be the last one */
gGGun.tpTex = RwFindNamedTexture("gun");
gGGun.nFrameCount = RwGetTextureNumFrames(gGGun.tpTex) - 1;
gGGun.nPos = CREAL(GUN_UP); /* gun starts in the visible position */
gGGun.nFiring = FALSE; /* gun isn't firing */
gGGun.nEnabled = 1; /* gun is enabled */
return TRUE;
}
/************************************************************************
*
* Function: GunDestroy()
*
* Description: Destroy the gun
*
************************************************************************/
void GunDestroy(void)
{
RwDestroyClump(gGGun.cpGun);
RwDestroyClump(gGGun.cpSight);
RwDestroyClump(gGGun.cpHit);
}
/************************************************************************
*
* Function: GunUpdate()
*
* Description: Update the gun
*
************************************************************************/
void GunUpdate(void)
{
#ifdef WITH_SOUND
RwInt32 nRand;
#endif
Object *opObj;
RwInt32 nW, nH;
if (!GunVisible())
return;
RwGetCameraViewport(cpGCamera, NULL, NULL, &nW, &nH);
/* First see if we are targetting a rat. The sight is in the
centre of the viewport */
RwPickScene(spGTargetScene, nW/2, nH/2, cpGCamera, &prGPick);
if (gGGun.nFireFlag)
{
#ifdef WITH_SOUND
/* Make the gun firing sound */
AllSoundsPlaySound("gun");
#endif
gGGun.nFireFlag = 0; /* only fire once each round */
if (prGPick.type == rwPICKCLUMP)
{
/* Hit a target so destroy it. We identify whether the target is
a rat or the trophy by the number of polygons on the picked clump
Each rat only has one polygon in the target scene */
if (RwGetClumpNumPolygons(prGPick.object.clump.clump) == 1)
{
/* We've hit a rat. Identify the rat that has been hit */
opObj = (Object *) RwGetClumpData(prGPick.object.clump.clump);
/* Rat hit returns true if the rat has been destroyed,
false otherwise */
if (RatHit(opObj))
{
/* Explode the rat */
#ifdef WITH_SOUND
/* Make the squish sound */
AllSoundsPlaySound("squish");
#endif
BitExplosionSetup(
opObj->vPos.x,
opObj->vPos.y + CREAL(EXPLOSION_HEIGHT),
opObj->vPos.z,
EXPLOSION_BITS,
abGBits.cpExplode);
/* Destroy the Rat */
ObjectDelete(opObj);
if (NumRats() == 1)
{
TrophyEnable();
}
}
else
{
/* Have a little bit of an explosion */
BitExplosionSetup(
prGPick.object.clump.wcpoint.x,
prGPick.object.clump.wcpoint.y,
prGPick.object.clump.wcpoint.z,
HIT_BITS,
abGBits.cpExplode);
#ifdef WITH_SOUND
/* Make the ricochet sound */
nRand = RwRandom() & 255;
if (nRand < 80)
{
AllSoundsPlaySound("squeak");
}
else
{
if (nRand < 160)
{
AllSoundsPlaySound("ratty");
}
else
{
AllSoundsPlaySound("ratty2");
}
}
#endif
}
}
else
{
TrophyHit();
/* must be a trophy */
#ifdef WITH_SOUND
/* Make the clang sound */
AllSoundsPlaySound("clang");
#endif
BitExplosionSetup(
prGPick.object.clump.wcpoint.x,
prGPick.object.clump.wcpoint.y,
prGPick.object.clump.wcpoint.z,
TROPHY_BITS,
abGBits.cpSpark);
}
}
else
{
/* Nothing. See if we will hit a wall */
RwPickScene(spGScene, nW/2, nH/2, cpGCamera, &prGCityPick);
if (prGCityPick.type == rwPICKCLUMP)
{
/* A wall has been hit !!! */
#ifdef WITH_SOUND
/* Make the ricochet sound */
AllSoundsPlaySound("ricochet");
#endif
/* Wall has been hit */
BitExplosionSetup(
prGCityPick.object.clump.wcpoint.x,
prGCityPick.object.clump.wcpoint.y,
prGCityPick.object.clump.wcpoint.z,
SPARK_BITS,
abGBits.cpSpark);
}
}
}
}
/************************************************************************
*
* Function: GunRender()
*
* Description: Render the gun
*
************************************************************************/
void GunRender(void)
{
RwV3d vPos;
RwV3d vAt;
RwV3d vUp;
if (gGGun.nPos <= CREAL(GUN_REMOVE))
{
/* The gun has been removed */
if (gGGun.nEnabled)
{
/* Bring it back */
gGGun.nPos += CREAL(GUN_SPEED);
}
}
else
{
/* The gun is on screen */
RwSetTextureFrame(gGGun.tpTex, gGGun.nFrameCount);
if (gGGun.nFrameCount < RwGetTextureNumFrames(gGGun.tpTex) - 1)
{
gGGun.nFrameCount++;
}
if (!gGGun.nEnabled)
{
gGGun.nPos -= CREAL(GUN_SPEED);
}
else
{
if (gGGun.nPos >= CREAL(GUN_UP))
{
gGGun.nPos = CREAL(GUN_UP);
if ((gGGun.nFrameCount==(RwGetTextureNumFrames(gGGun.tpTex)-1)) &&
(gGGun.nFiring))
{
/* The gun has been fired !!!!!!!!!!!!!!! */
gGGun.nFrameCount=0;
gGGun.nFireFlag = 1;
}
}
else
{
gGGun.nPos+= CREAL(GUN_SPEED);
}
}
/* Render the Sight */
RwGetCameraPosition(cpGCamera, &vPos);
RwGetCameraLookAt(cpGCamera, &vAt);
RwScaleVector(&vAt, CREAL(GUNSIGHT_POS), &vAt);
RwAddVector(&vPos, &vAt, &vPos);
RwTranslateMatrix(RwScratchMatrix(),
vPos.x, vPos.y, vPos.z, rwREPLACE);
if (prGPick.type == rwPICKCLUMP)
{
/* something has been targetted */
RwTransformClump(gGGun.cpHit, RwScratchMatrix(), rwREPLACE);
RwRenderClump(gGGun.cpHit);
}
else
{
RwTransformClump(gGGun.cpSight, RwScratchMatrix(), rwREPLACE);
RwRenderClump(gGGun.cpSight);
}
/* Render the gun */
RwGetCameraPosition(cpGCamera, &vPos);
RwGetCameraLookAt(cpGCamera, &vAt);
RwScaleVector(&vAt, CREAL(GUN_FORWARD), &vAt);
RwAddVector(&vPos, &vAt, &vPos);
RwGetCameraLookUp(cpGCamera,&vUp);
RwScaleVector(&vUp, gGGun.nPos, &vUp);
RwAddVector(&vPos, &vUp, &vPos);
RwTranslateMatrix(RwScratchMatrix(), vPos.x, vPos.y, vPos.z, rwREPLACE);
RwTransformClump(gGGun.cpGun, RwScratchMatrix(), rwREPLACE);
RwRenderClump(gGGun.cpGun);
}
}