mirror of
https://github.com/openmoh/openmohaa.git
synced 2025-05-08 11:38:01 +03:00
697 lines
19 KiB
C
697 lines
19 KiB
C
/*
|
|
===========================================================================
|
|
Copyright (C) 2023 the OpenMoHAA team
|
|
|
|
This file is part of OpenMoHAA source code.
|
|
|
|
OpenMoHAA source code is free software; you can redistribute it
|
|
and/or modify it under the terms of the GNU General Public License as
|
|
published by the Free Software Foundation; either version 2 of the License,
|
|
or (at your option) any later version.
|
|
|
|
OpenMoHAA source code is distributed in the hope that it will be
|
|
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with OpenMoHAA source code; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
===========================================================================
|
|
*/
|
|
|
|
// cg_ents.c -- present snapshot entities, happens every single frame
|
|
|
|
#include "cg_local.h"
|
|
|
|
/*
|
|
==========================================================================
|
|
|
|
FUNCTIONS CALLED EACH FRAME
|
|
|
|
==========================================================================
|
|
*/
|
|
|
|
/*
|
|
======================
|
|
CG_SetEntitySoundPosition
|
|
======================
|
|
*/
|
|
void CG_SetEntitySoundPosition( centity_t *cent )
|
|
{
|
|
vec3_t origin;
|
|
|
|
if ( cent->currentState.solid == SOLID_BMODEL )
|
|
{
|
|
float *v;
|
|
vec3_t vel;
|
|
|
|
v = cgs.inlineModelMidpoints[ cent->currentState.modelindex ];
|
|
VectorAdd( cent->lerpOrigin, v, origin );
|
|
|
|
vel[0] = 0.0;
|
|
vel[1] = 0.0;
|
|
vel[2] = 0.0;
|
|
|
|
cgi.S_UpdateEntity( cent->currentState.number, origin, vel, qfalse );
|
|
}
|
|
else
|
|
{
|
|
if ( cent && cg.snap && cent->currentState.parent == cg.snap->ps.clientNum )
|
|
{
|
|
vec3_t origin;
|
|
vec3_t velocity;
|
|
|
|
origin[0] = 0;
|
|
origin[1] = 0;
|
|
origin[2] = 0;
|
|
|
|
velocity[0] = 0;
|
|
velocity[1] = 0;
|
|
velocity[2] = 0;
|
|
|
|
cgi.S_UpdateEntity( cent->currentState.number, origin, velocity, qtrue );
|
|
}
|
|
else
|
|
{
|
|
CG_GetOrigin( cent, origin );
|
|
cgi.S_UpdateEntity( cent->currentState.number, origin, cent->currentState.pos.trDelta, qfalse );
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
==================
|
|
CG_EntityEffects
|
|
|
|
Add continuous entity effects, like local entity emission and lighting
|
|
==================
|
|
*/
|
|
void CG_EntityEffects( centity_t *cent )
|
|
{
|
|
// initialize with the client colors
|
|
cent->color[ 0 ] = cent->client_color[ 0 ];
|
|
cent->color[ 1 ] = cent->client_color[ 1 ];
|
|
cent->color[ 2 ] = cent->client_color[ 2 ];
|
|
cent->color[ 3 ] = cent->client_color[ 3 ];
|
|
|
|
if ( cent->currentState.constantLight != 0xffffff )
|
|
{
|
|
int style;
|
|
unsigned cl;
|
|
float i, r, g, b;
|
|
|
|
cl = cent->currentState.constantLight;
|
|
style = ( cl & 255 );
|
|
r = ( float )style / 255.0f;
|
|
g = ( float )( ( cl >> 8 ) & 255 ) / 255.0f;
|
|
b = ( float )( ( cl >> 16 ) & 255 ) / 255.0f;
|
|
i = ( ( cl >> 24 ) & 255 ) * CONSTANTLIGHT_RADIUS_SCALE;
|
|
if ( cent->currentState.renderfx & RF_LIGHTSTYLE_DLIGHT )
|
|
{
|
|
float color[ 4 ];
|
|
|
|
CG_LightStyleColor( style, cg.time, color, qfalse );
|
|
r = color[ 0 ];
|
|
g = color[ 1 ];
|
|
b = color[ 2 ];
|
|
i *= color[ 3 ];
|
|
}
|
|
if ( i )
|
|
{
|
|
int flags;
|
|
|
|
flags = 0;
|
|
if ( cent->currentState.renderfx & RF_LENSFLARE )
|
|
{
|
|
flags |= lensflare;
|
|
}
|
|
else if ( cent->currentState.renderfx & RF_VIEWLENSFLARE )
|
|
{
|
|
flags |= viewlensflare;
|
|
}
|
|
if ( cent->currentState.renderfx & RF_ADDITIVE_DLIGHT )
|
|
{
|
|
flags |= additive;
|
|
}
|
|
cgi.R_AddLightToScene( cent->lerpOrigin, i, r, g, b, flags );
|
|
}
|
|
if ( r < cent->color[ 0 ] )
|
|
cent->color[ 0 ] = r;
|
|
if ( g < cent->color[ 1 ] )
|
|
cent->color[ 1 ] = g;
|
|
if ( b < cent->color[ 2 ] )
|
|
cent->color[ 2 ] = b;
|
|
}
|
|
}
|
|
|
|
/*
|
|
==================
|
|
CG_General
|
|
==================
|
|
*/
|
|
void CG_General( centity_t *cent ) {
|
|
refEntity_t ent;
|
|
entityState_t *s1;
|
|
int i;
|
|
vec3_t vMins, vMaxs, vTmp;
|
|
|
|
s1 = ¢->currentState;
|
|
|
|
// add loop sound
|
|
if ( s1->loopSound )
|
|
{
|
|
cgi.S_AddLoopingSound( cent->lerpOrigin, vec3_origin, cgs.sound_precache[s1->loopSound], s1->loopSoundVolume, s1->loopSoundMinDist, s1->loopSoundMaxDist, s1->loopSoundPitch, s1->loopSoundFlags );
|
|
}
|
|
if ( cent->tikiLoopSound )
|
|
cgi.S_AddLoopingSound( cent->lerpOrigin, vec3_origin, cent->tikiLoopSound, cent->tikiLoopSoundVolume, cent->tikiLoopSoundMinDist, cent->tikiLoopSoundMaxDist, cent->tikiLoopSoundPitch, cent->tikiLoopSoundFlags );
|
|
|
|
// if set to invisible, skip
|
|
if (!s1->modelindex) {
|
|
return;
|
|
}
|
|
|
|
memset (&ent, 0, sizeof(ent));
|
|
|
|
// set frame
|
|
|
|
ent.wasframe = s1->wasframe;
|
|
|
|
VectorCopy( cent->lerpOrigin, ent.origin);
|
|
VectorCopy( cent->lerpOrigin, ent.oldorigin);
|
|
|
|
// set skin
|
|
IntegerToBoundingBox(s1->solid, vMins, vMaxs);
|
|
VectorSubtract(vMins, vMaxs, vTmp);
|
|
ent.lightingOrigin[0] = ent.origin[0] + (vMins[0] + vMaxs[0]) * 0.5;
|
|
ent.lightingOrigin[1] = ent.origin[1] + (vMins[1] + vMaxs[1]) * 0.5;
|
|
ent.lightingOrigin[2] = ent.origin[2] + (vMins[2] + vMaxs[2]) * 0.5;
|
|
ent.radius = VectorLength(vTmp) * 0.5;
|
|
|
|
ent.skinNum = s1->skinNum;
|
|
|
|
ent.hModel = cgs.model_draw[s1->modelindex];
|
|
|
|
// set surfaces
|
|
memcpy( ent.surfaces, s1->surfaces, MAX_MODEL_SURFACES );
|
|
|
|
// Modulation based off the color
|
|
for( i=0; i<3; i++ )
|
|
ent.shaderRGBA[ i ] = cent->color[ i ] * 255;
|
|
|
|
// take the alpha from the entity if less than 1, else grab it from the client commands version
|
|
if ( s1->alpha < 1 )
|
|
{
|
|
ent.shaderRGBA[ 3 ] = s1->alpha * 255;
|
|
}
|
|
else
|
|
{
|
|
ent.shaderRGBA[ 3 ] = cent->color[ 3 ] * 255;
|
|
}
|
|
|
|
// convert angles to axis
|
|
AnglesToAxis( cent->lerpAngles, ent.axis );
|
|
|
|
// Interpolated state variables
|
|
if ( cent->interpolate )
|
|
{
|
|
ent.scale = s1->scale + cg.frameInterpolation * ( cent->nextState.scale - cent->currentState.scale );
|
|
}
|
|
else
|
|
{
|
|
ent.scale = s1->scale;
|
|
}
|
|
// set the entity number
|
|
ent.entityNumber = s1->number;
|
|
|
|
// copy shader specific data
|
|
ent.shader_data[ 0 ] = s1->tag_num;
|
|
ent.shader_data[ 1 ] = s1->skinNum;
|
|
ent.renderfx |= s1->renderfx;
|
|
|
|
if ( ent.renderfx & RF_SKYORIGIN )
|
|
{
|
|
memcpy( cg.sky_axis, ent.axis, sizeof( cg.sky_axis ) );
|
|
VectorCopy( ent.origin, cg.sky_origin );
|
|
}
|
|
|
|
ent.tiki = cgi.R_Model_GetHandle(cgs.model_draw[s1->modelindex]);
|
|
ent.frameInfo[0].index = s1->frameInfo[0].index;
|
|
ent.frameInfo[0].time = s1->frameInfo[0].time;
|
|
ent.frameInfo[0].weight = s1->frameInfo[0].weight;
|
|
ent.actionWeight = 1.0;
|
|
|
|
// add to refresh list
|
|
cgi.R_AddRefEntityToScene (&ent, ENTITYNUM_NONE);
|
|
|
|
if (ent.tiki)
|
|
{
|
|
// update any emitter's...
|
|
CG_UpdateEntityEmitters(s1->number, &ent, cent);
|
|
}
|
|
}
|
|
|
|
/*
|
|
==================
|
|
CG_Speaker
|
|
|
|
Speaker entities can automatically play sounds
|
|
==================
|
|
*/
|
|
void CG_Speaker( centity_t *cent )
|
|
{
|
|
if ( ! cent->currentState.clientNum ) // FIXME: use something other than clientNum...
|
|
{
|
|
return; // not auto triggering
|
|
}
|
|
|
|
if ( cg.time < cent->miscTime )
|
|
{
|
|
return;
|
|
}
|
|
|
|
// FIXME
|
|
//cgi.S_StartSound (NULL, cent->currentState.number, CHAN_ITEM, cgs.sound_precache[cent->currentState.eventParm] );
|
|
|
|
// ent->s.frame = ent->wait * 10;
|
|
// ent->s.clientNum = ent->random * 10;
|
|
cent->miscTime = cg.time + cent->currentState.wasframe * 100 + cent->currentState.clientNum * 100 * crandom();
|
|
}
|
|
|
|
|
|
/*
|
|
===============
|
|
CG_Mover
|
|
===============
|
|
*/
|
|
void CG_Mover( centity_t *cent ) {
|
|
refEntity_t ent;
|
|
entityState_t *s1;
|
|
|
|
s1 = ¢->currentState;
|
|
|
|
// create the render entity
|
|
memset (&ent, 0, sizeof(ent));
|
|
VectorCopy( cent->lerpOrigin, ent.origin);
|
|
VectorCopy( cent->lerpOrigin, ent.oldorigin);
|
|
AnglesToAxis( cent->lerpAngles, ent.axis );
|
|
|
|
ent.renderfx &= ~RF_SHADOW;
|
|
|
|
// flicker between two skins (FIXME?)
|
|
ent.skinNum = ( cg.time >> 6 ) & 1;
|
|
|
|
// get the model, either as a bmodel or a modelindex
|
|
if ( s1->solid == SOLID_BMODEL ) {
|
|
ent.hModel = cgs.inlineDrawModel[s1->modelindex];
|
|
} else {
|
|
ent.hModel = cgs.model_draw[s1->modelindex];
|
|
}
|
|
|
|
// add to refresh list
|
|
cgi.R_AddRefEntityToScene(&ent, ENTITYNUM_NONE);
|
|
}
|
|
|
|
/*
|
|
===============
|
|
CG_Beam
|
|
===============
|
|
*/
|
|
void CG_Beam( centity_t *cent ) {
|
|
entityState_t *s1;
|
|
vec3_t vz={0,0,0},origin={0,0,0};
|
|
float modulate[4];
|
|
int i;
|
|
|
|
s1 = ¢->currentState;
|
|
|
|
for ( i=0;i<4;i++ )
|
|
modulate[i] = cent->color[i];
|
|
|
|
if ( s1->beam_entnum != ENTITYNUM_NONE )
|
|
{
|
|
refEntity_t *parent;
|
|
parent = cgi.R_GetRenderEntity( s1->beam_entnum);
|
|
|
|
if ( !parent )
|
|
{
|
|
cgi.DPrintf( "CG_Beam: Could not find parent entity\n" );
|
|
return;
|
|
}
|
|
|
|
VectorAdd( s1->origin, parent->origin, origin );
|
|
}
|
|
else
|
|
{
|
|
VectorCopy( s1->origin, origin );
|
|
}
|
|
|
|
CG_CreateBeam( origin, // start
|
|
vz, // dir ( auto calculated by using origin2-origin )
|
|
s1->number, // owner number
|
|
cgs.model_draw[s1->modelindex], //hModel
|
|
s1->alpha, // alpha
|
|
s1->scale, // scale
|
|
s1->skinNum, // flags
|
|
0, // length ( auto calculated )
|
|
PKT_TO_BEAM_PARM( s1->surfaces[0] ) * 1000, // life
|
|
qfalse, // don't always create the beam, just update it
|
|
s1->origin2, // endpoint
|
|
s1->bone_angles[0][0], // min offset
|
|
s1->bone_angles[0][1], // max offset
|
|
PKT_TO_BEAM_PARM( s1->surfaces[3] ), // overlap
|
|
s1->surfaces[4], // subdivisions
|
|
PKT_TO_BEAM_PARM( s1->surfaces[5] ) * 1000, // delay
|
|
CG_ConfigString( CS_IMAGES + s1->tag_num ), // index for shader configstring
|
|
modulate, // modulate color
|
|
s1->surfaces[6], // num sphere beams
|
|
PKT_TO_BEAM_PARM( s1->surfaces[7] ), // sphere radius
|
|
PKT_TO_BEAM_PARM( s1->surfaces[8] ), // toggle delay
|
|
PKT_TO_BEAM_PARM( s1->surfaces[9] ), // end alpha
|
|
s1->renderfx,
|
|
""
|
|
);
|
|
}
|
|
|
|
|
|
void CG_Decal
|
|
(
|
|
centity_t *cent
|
|
)
|
|
|
|
{
|
|
qhandle_t shader;
|
|
vec3_t dir;
|
|
entityState_t *s1;
|
|
|
|
s1 = ¢->currentState;
|
|
|
|
shader = cgi.R_RegisterShader( CG_ConfigString( CS_IMAGES + s1->tag_num ) );
|
|
ByteToDir( s1->surfaces[0], dir );
|
|
CG_ImpactMark( shader,
|
|
s1->origin,
|
|
dir,
|
|
s1->angles[2],
|
|
cent->color[0],
|
|
cent->color[1],
|
|
cent->color[2],
|
|
cent->color[3],
|
|
qtrue,
|
|
s1->scale,
|
|
qfalse,
|
|
-1,
|
|
qfalse,
|
|
0.f,
|
|
0.f);
|
|
}
|
|
|
|
/*
|
|
===============
|
|
CG_Portal
|
|
===============
|
|
*/
|
|
void CG_Portal(centity_t* cent)
|
|
{
|
|
}
|
|
|
|
/*
|
|
================
|
|
BG_EvaluateTrajectory
|
|
================
|
|
*/
|
|
void BG_EvaluateTrajectory(const trajectory_t* tr, int atTime, const vec3_t base, vec3_t result) {
|
|
float deltaTime;
|
|
|
|
if (atTime > cg_smoothClientsTime->integer + tr->trTime) {
|
|
atTime = cg_smoothClientsTime->integer + tr->trTime;
|
|
}
|
|
|
|
deltaTime = (float)(atTime - tr->trTime) / 1000.0;
|
|
|
|
result[0] = tr->trDelta[0] * deltaTime + base[0];
|
|
result[1] = tr->trDelta[1] * deltaTime + base[1];
|
|
result[2] = tr->trDelta[2] * deltaTime + base[2];
|
|
}
|
|
|
|
/*
|
|
===============
|
|
CG_CalcEntityLerpPositions
|
|
|
|
===============
|
|
*/
|
|
void CG_CalcEntityLerpPositions( centity_t *cent )
|
|
{
|
|
int i;
|
|
float f;
|
|
|
|
f = cg.frameInterpolation;
|
|
|
|
if (cent->currentState.eType == ET_PLAYER)
|
|
{
|
|
if (cent->currentState.number == cg.snap->ps.clientNum)
|
|
{
|
|
// if the player, take position from prediction
|
|
VectorCopy(cg.predicted_player_state.origin, cent->lerpOrigin);
|
|
for (i = 0; i < 3; i++) {
|
|
cent->lerpAngles[i] = LerpAngle(cent->currentState.angles[i], cent->nextState.angles[i], f);
|
|
}
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (cent->currentState.eType != ET_PLAYER || !cg_smoothClients->integer) {
|
|
float quat[4];
|
|
float mat[3][3];
|
|
|
|
if (!cent->interpolate) {
|
|
VectorCopy(cent->currentState.angles, cent->lerpAngles);
|
|
VectorCopy(cent->currentState.origin, cent->lerpOrigin);
|
|
return;
|
|
}
|
|
|
|
for (i = 0; i < 3; i++) {
|
|
cent->lerpOrigin[i] = cent->currentState.origin[i] +
|
|
f * (cent->nextState.origin[i] - cent->currentState.origin[i]);
|
|
}
|
|
|
|
if (!memcmp(cent->currentState.angles, cent->nextState.angles, sizeof(vec3_t))) {
|
|
VectorCopy(cent->currentState.angles, cent->lerpAngles);
|
|
}
|
|
else {
|
|
// use spherical interpolation using quaternions so that bound objects
|
|
// rotate properly without gimble lock.
|
|
SlerpQuaternion(cent->currentState.quat, cent->nextState.quat, f, quat);
|
|
QuatToMat(quat, mat);
|
|
MatrixToEulerAngles(mat, cent->lerpAngles);
|
|
}
|
|
}
|
|
else if (cent->interpolate) {
|
|
float quat[4];
|
|
float mat[3][3];
|
|
|
|
// if the entity has a valid next state, interpolate a value between the frames
|
|
// unless it is a mover with a known start and stop
|
|
vec3_t current, next;
|
|
|
|
// this will linearize a sine or parabolic curve, but it is important
|
|
// to not extrapolate player positions if more recent data is available
|
|
BG_EvaluateTrajectory(¢->currentState.pos, cg.snap->serverTime, cent->currentState.origin, current);
|
|
BG_EvaluateTrajectory(¢->nextState.pos, cg.nextSnap->serverTime, cent->nextState.origin, next);
|
|
|
|
cent->lerpOrigin[0] = current[0] + f * (next[0] - current[0]);
|
|
cent->lerpOrigin[1] = current[1] + f * (next[1] - current[1]);
|
|
cent->lerpOrigin[2] = current[2] + f * (next[2] - current[2]);
|
|
|
|
if (!memcmp(cent->currentState.angles, cent->nextState.angles, sizeof(vec3_t))) {
|
|
VectorCopy(cent->currentState.angles, cent->lerpAngles);
|
|
}
|
|
else {
|
|
// use spherical interpolation using quaternions so that bound objects
|
|
// rotate properly without gimble lock.
|
|
SlerpQuaternion(cent->currentState.quat, cent->nextState.quat, f, quat);
|
|
QuatToMat(quat, mat);
|
|
MatrixToEulerAngles(mat, cent->lerpAngles);
|
|
}
|
|
}
|
|
else {
|
|
// just use the current frame and evaluate as best we can
|
|
BG_EvaluateTrajectory(¢->currentState.pos, cg.time, cent->currentState.origin, cent->lerpOrigin);
|
|
VectorCopy(cent->currentState.angles, cent->lerpAngles);
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
CG_AddCEntity
|
|
|
|
===============
|
|
*/
|
|
void CG_AddCEntity(centity_t* cent)
|
|
{
|
|
// event-only entities will have been dealt with already
|
|
if (cent->currentState.eType >= ET_EVENTS) {
|
|
return;
|
|
}
|
|
|
|
// calculate the current origin
|
|
CG_CalcEntityLerpPositions(cent);
|
|
|
|
// add automatic effects
|
|
CG_EntityEffects(cent);
|
|
|
|
CG_SetEntitySoundPosition(cent);
|
|
|
|
switch (cent->currentState.eType) {
|
|
default:
|
|
cgi.Error(ERR_DROP, "Bad entity type: %i\n", cent->currentState.eType);
|
|
break;
|
|
// intentional fallthrough
|
|
case ET_MODELANIM_SKEL:
|
|
case ET_MODELANIM:
|
|
CG_Splash(cent);
|
|
CG_ModelAnim(cent, qfalse);
|
|
break;
|
|
case ET_VEHICLE:
|
|
CG_Vehicle(cent);
|
|
CG_Splash(cent);
|
|
CG_ModelAnim(cent, qtrue);
|
|
break;
|
|
case ET_PLAYER:
|
|
CG_Player(cent);
|
|
CG_Splash(cent);
|
|
case ET_ITEM:
|
|
CG_ModelAnim(cent, qfalse);
|
|
break;
|
|
case ET_GENERAL:
|
|
CG_General(cent);
|
|
break;
|
|
case ET_MOVER:
|
|
CG_Mover(cent);
|
|
break;
|
|
case ET_BEAM:
|
|
CG_Beam(cent);
|
|
break;
|
|
case ET_MULTIBEAM: // skip
|
|
break;
|
|
case ET_PORTAL:
|
|
CG_Portal(cent);
|
|
break;
|
|
case ET_RAIN:
|
|
CG_Rain(cent);
|
|
break;
|
|
case ET_DECAL:
|
|
CG_Decal(cent);
|
|
break;
|
|
case ET_EMITTER:
|
|
CG_Emitter(cent);
|
|
break;
|
|
case ET_ROPE: // skip
|
|
CG_Rope(cent);
|
|
break;
|
|
case ET_EXEC_COMMANDS:
|
|
CG_ModelAnim(cent, qfalse);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
CG_AddPacketEntities
|
|
|
|
===============
|
|
*/
|
|
void CG_AddPacketEntities( void ) {
|
|
int num;
|
|
centity_t *cent;
|
|
int child, parent;
|
|
qboolean processed[MAX_ENTITIES];
|
|
int i;
|
|
|
|
// the auto-rotating items will all have the same axis
|
|
cg.autoAngles[0] = 0;
|
|
cg.autoAngles[1] = ( cg.time & 2047 ) * 360 / 2048.0;
|
|
cg.autoAngles[2] = 0;
|
|
|
|
cg.autoAnglesSlow[0] = 0;
|
|
cg.autoAnglesSlow[1] = ( cg.time & 4095 ) * 360 / 4096.0f;
|
|
cg.autoAnglesSlow[2] = 0;
|
|
|
|
cg.autoAnglesFast[0] = 0;
|
|
cg.autoAnglesFast[1] = ( cg.time & 1023 ) * 360 / 1024.0f;
|
|
cg.autoAnglesFast[2] = 0;
|
|
|
|
AnglesToAxis( cg.autoAngles, cg.autoAxis );
|
|
AnglesToAxis( cg.autoAnglesSlow, cg.autoAxisSlow );
|
|
AnglesToAxis( cg.autoAnglesFast, cg.autoAxisFast );
|
|
|
|
for (i = 0; i < MAX_ENTITIES; i++) {
|
|
processed[i] = qtrue;
|
|
}
|
|
|
|
for (num = 0; num < cg.snap->numEntities; ++num) {
|
|
processed[cg.snap->entities[num].number] = qfalse;
|
|
}
|
|
|
|
// add each entity sent over by the server
|
|
for (num = 0; num < cg.snap->numEntities; num++) {
|
|
child = cg.snap->entities[num].number;
|
|
cent = &cg_entities[child];
|
|
// add the parent first
|
|
// so attachments are consistent
|
|
for (parent = cent->currentState.parent;
|
|
parent != ENTITYNUM_NONE && !processed[parent];
|
|
parent = cg_entities[parent].currentState.parent)
|
|
{
|
|
processed[parent] = qtrue;
|
|
CG_AddCEntity(&cg_entities[parent]);
|
|
}
|
|
|
|
if (!processed[child])
|
|
{
|
|
// now add the children if not processed
|
|
processed[child] = qtrue;
|
|
CG_AddCEntity(cent);
|
|
}
|
|
}
|
|
|
|
// Add in the multibeams at the end
|
|
for ( num = 0 ; num < cg.snap->numEntities ; num++ ) {
|
|
cent = &cg_entities[ cg.snap->entities[ num ].number ];
|
|
if ( cent->currentState.eType == ET_MULTIBEAM )
|
|
CG_MultiBeam( cent );
|
|
}
|
|
|
|
}
|
|
|
|
void CG_GetOrigin(centity_t* cent, vec3_t origin)
|
|
{
|
|
if (cent->currentState.parent == ENTITYNUM_NONE)
|
|
{
|
|
VectorCopy(cent->lerpOrigin, origin);
|
|
}
|
|
else
|
|
{
|
|
int i;
|
|
orientation_t or;
|
|
refEntity_t* parent;
|
|
|
|
parent = cgi.R_GetRenderEntity(cent->currentState.parent);
|
|
|
|
if (!parent)
|
|
{
|
|
cgi.DPrintf("CG_GetOrigin: Could not find parent entity\n");
|
|
return;
|
|
}
|
|
|
|
cgi.R_Model_GetHandle(parent->hModel);
|
|
or = cgi.TIKI_Orientation(parent, cent->currentState.tag_num);
|
|
|
|
VectorCopy(parent->origin, origin);
|
|
|
|
for (i = 0; i < 3; i++)
|
|
{
|
|
VectorMA(origin, or .origin[i], parent->axis[i], origin);
|
|
}
|
|
}
|
|
}
|