2
===========================================================================
3
Copyright (C) 1999-2005 Id Software, Inc.
5
This file is part of Quake III Arena source code.
7
Quake III Arena source code is free software; you can redistribute it
8
and/or modify it under the terms of the GNU General Public License as
9
published by the Free Software Foundation; either version 2 of the License,
10
or (at your option) any later version.
12
Quake III Arena source code is distributed in the hope that it will be
13
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
14
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
GNU General Public License for more details.
17
You should have received a copy of the GNU General Public License
18
along with Quake III Arena source code; if not, write to the Free Software
19
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20
===========================================================================
23
// cg_ents.c -- present snapshot entities, happens every single frame
29
======================
30
CG_PositionEntityOnTag
32
Modifies the entities position and axis by the given
34
======================
36
void CG_PositionEntityOnTag( refEntity_t *entity, const refEntity_t *parent,
37
qhandle_t parentModel, char *tagName ) {
42
trap_R_LerpTag( &lerped, parentModel, parent->oldframe, parent->frame,
43
1.0 - parent->backlerp, tagName );
45
// FIXME: allow origin offsets along tag?
46
VectorCopy( parent->origin, entity->origin );
47
for ( i = 0 ; i < 3 ; i++ ) {
48
VectorMA( entity->origin, lerped.origin[i], parent->axis[i], entity->origin );
51
// had to cast away the const to avoid compiler problems...
52
MatrixMultiply( lerped.axis, ((refEntity_t *)parent)->axis, entity->axis );
53
entity->backlerp = parent->backlerp;
58
======================
59
CG_PositionRotatedEntityOnTag
61
Modifies the entities position and axis by the given
63
======================
65
void CG_PositionRotatedEntityOnTag( refEntity_t *entity, const refEntity_t *parent,
66
qhandle_t parentModel, char *tagName ) {
71
//AxisClear( entity->axis );
73
trap_R_LerpTag( &lerped, parentModel, parent->oldframe, parent->frame,
74
1.0 - parent->backlerp, tagName );
76
// FIXME: allow origin offsets along tag?
77
VectorCopy( parent->origin, entity->origin );
78
for ( i = 0 ; i < 3 ; i++ ) {
79
VectorMA( entity->origin, lerped.origin[i], parent->axis[i], entity->origin );
82
// had to cast away the const to avoid compiler problems...
83
MatrixMultiply( entity->axis, lerped.axis, tempAxis );
84
MatrixMultiply( tempAxis, ((refEntity_t *)parent)->axis, entity->axis );
90
==========================================================================
92
FUNCTIONS CALLED EACH FRAME
94
==========================================================================
98
======================
99
CG_SetEntitySoundPosition
101
Also called by event processing code
102
======================
104
void CG_SetEntitySoundPosition( centity_t *cent ) {
105
if ( cent->currentState.solid == SOLID_BMODEL ) {
109
v = cgs.inlineModelMidpoints[ cent->currentState.modelindex ];
110
VectorAdd( cent->lerpOrigin, v, origin );
111
trap_S_UpdateEntityPosition( cent->currentState.number, origin );
113
trap_S_UpdateEntityPosition( cent->currentState.number, cent->lerpOrigin );
121
Add continuous entity effects, like local entity emission and lighting
124
static void CG_EntityEffects( centity_t *cent ) {
126
// update sound origins
127
CG_SetEntitySoundPosition( cent );
130
if ( cent->currentState.loopSound ) {
131
if (cent->currentState.eType != ET_SPEAKER) {
132
trap_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin,
133
cgs.gameSounds[ cent->currentState.loopSound ] );
135
trap_S_AddRealLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin,
136
cgs.gameSounds[ cent->currentState.loopSound ] );
141
// constant light glow
142
if ( cent->currentState.constantLight ) {
146
cl = cent->currentState.constantLight;
148
g = ( cl >> 8 ) & 255;
149
b = ( cl >> 16 ) & 255;
150
i = ( ( cl >> 24 ) & 255 ) * 4;
151
trap_R_AddLightToScene( cent->lerpOrigin, i, r, g, b );
162
static void CG_General( centity_t *cent ) {
166
s1 = ¢->currentState;
168
// if set to invisible, skip
169
if (!s1->modelindex) {
173
memset (&ent, 0, sizeof(ent));
177
ent.frame = s1->frame;
178
ent.oldframe = ent.frame;
181
VectorCopy( cent->lerpOrigin, ent.origin);
182
VectorCopy( cent->lerpOrigin, ent.oldorigin);
184
ent.hModel = cgs.gameModels[s1->modelindex];
187
if (s1->number == cg.snap->ps.clientNum) {
188
ent.renderfx |= RF_THIRD_PERSON; // only draw from mirrors
191
// convert angles to axis
192
AnglesToAxis( cent->lerpAngles, ent.axis );
194
// add to refresh list
195
trap_R_AddRefEntityToScene (&ent);
202
Speaker entities can automatically play sounds
205
static void CG_Speaker( centity_t *cent ) {
206
if ( ! cent->currentState.clientNum ) { // FIXME: use something other than clientNum...
207
return; // not auto triggering
210
if ( cg.time < cent->miscTime ) {
214
trap_S_StartSound (NULL, cent->currentState.number, CHAN_ITEM, cgs.gameSounds[cent->currentState.eventParm] );
216
// ent->s.frame = ent->wait * 10;
217
// ent->s.clientNum = ent->random * 10;
218
cent->miscTime = cg.time + cent->currentState.frame * 100 + cent->currentState.clientNum * 100 * crandom();
226
static void CG_Item( centity_t *cent ) {
235
es = ¢->currentState;
236
if ( es->modelindex >= bg_numItems ) {
237
CG_Error( "Bad item index %i on entity", es->modelindex );
240
// if set to invisible, skip
241
if ( !es->modelindex || ( es->eFlags & EF_NODRAW ) ) {
245
item = &bg_itemlist[ es->modelindex ];
246
if ( cg_simpleItems.integer && item->giType != IT_TEAM ) {
247
memset( &ent, 0, sizeof( ent ) );
248
ent.reType = RT_SPRITE;
249
VectorCopy( cent->lerpOrigin, ent.origin );
251
ent.customShader = cg_items[es->modelindex].icon;
252
ent.shaderRGBA[0] = 255;
253
ent.shaderRGBA[1] = 255;
254
ent.shaderRGBA[2] = 255;
255
ent.shaderRGBA[3] = 255;
256
trap_R_AddRefEntityToScene(&ent);
260
// items bob up and down continuously
261
scale = 0.005 + cent->currentState.number * 0.00001;
262
cent->lerpOrigin[2] += 4 + cos( ( cg.time + 1000 ) * scale ) * 4;
264
memset (&ent, 0, sizeof(ent));
266
// autorotate at one of two speeds
267
if ( item->giType == IT_HEALTH ) {
268
VectorCopy( cg.autoAnglesFast, cent->lerpAngles );
269
AxisCopy( cg.autoAxisFast, ent.axis );
271
VectorCopy( cg.autoAngles, cent->lerpAngles );
272
AxisCopy( cg.autoAxis, ent.axis );
276
// the weapons have their origin where they attatch to player
277
// models, so we need to offset them or they will rotate
279
if ( item->giType == IT_WEAPON ) {
280
wi = &cg_weapons[item->giTag];
281
cent->lerpOrigin[0] -=
282
wi->weaponMidpoint[0] * ent.axis[0][0] +
283
wi->weaponMidpoint[1] * ent.axis[1][0] +
284
wi->weaponMidpoint[2] * ent.axis[2][0];
285
cent->lerpOrigin[1] -=
286
wi->weaponMidpoint[0] * ent.axis[0][1] +
287
wi->weaponMidpoint[1] * ent.axis[1][1] +
288
wi->weaponMidpoint[2] * ent.axis[2][1];
289
cent->lerpOrigin[2] -=
290
wi->weaponMidpoint[0] * ent.axis[0][2] +
291
wi->weaponMidpoint[1] * ent.axis[1][2] +
292
wi->weaponMidpoint[2] * ent.axis[2][2];
294
cent->lerpOrigin[2] += 8; // an extra height boost
297
ent.hModel = cg_items[es->modelindex].models[0];
299
VectorCopy( cent->lerpOrigin, ent.origin);
300
VectorCopy( cent->lerpOrigin, ent.oldorigin);
302
ent.nonNormalizedAxes = qfalse;
304
// if just respawned, slowly scale up
305
msec = cg.time - cent->miscTime;
306
if ( msec >= 0 && msec < ITEM_SCALEUP_TIME ) {
307
frac = (float)msec / ITEM_SCALEUP_TIME;
308
VectorScale( ent.axis[0], frac, ent.axis[0] );
309
VectorScale( ent.axis[1], frac, ent.axis[1] );
310
VectorScale( ent.axis[2], frac, ent.axis[2] );
311
ent.nonNormalizedAxes = qtrue;
316
// items without glow textures need to keep a minimum light value
317
// so they are always visible
318
if ( ( item->giType == IT_WEAPON ) ||
319
( item->giType == IT_ARMOR ) ) {
320
ent.renderfx |= RF_MINLIGHT;
323
// increase the size of the weapons when they are presented as items
324
if ( item->giType == IT_WEAPON ) {
325
VectorScale( ent.axis[0], 1.5, ent.axis[0] );
326
VectorScale( ent.axis[1], 1.5, ent.axis[1] );
327
VectorScale( ent.axis[2], 1.5, ent.axis[2] );
328
ent.nonNormalizedAxes = qtrue;
330
trap_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin, cgs.media.weaponHoverSound );
335
if ( item->giType == IT_HOLDABLE && item->giTag == HI_KAMIKAZE ) {
336
VectorScale( ent.axis[0], 2, ent.axis[0] );
337
VectorScale( ent.axis[1], 2, ent.axis[1] );
338
VectorScale( ent.axis[2], 2, ent.axis[2] );
339
ent.nonNormalizedAxes = qtrue;
343
// add to refresh list
344
trap_R_AddRefEntityToScene(&ent);
347
if ( item->giType == IT_WEAPON && wi->barrelModel ) {
350
memset( &barrel, 0, sizeof( barrel ) );
352
barrel.hModel = wi->barrelModel;
354
VectorCopy( ent.lightingOrigin, barrel.lightingOrigin );
355
barrel.shadowPlane = ent.shadowPlane;
356
barrel.renderfx = ent.renderfx;
358
CG_PositionRotatedEntityOnTag( &barrel, &ent, wi->weaponModel, "tag_barrel" );
360
AxisCopy( ent.axis, barrel.axis );
361
barrel.nonNormalizedAxes = ent.nonNormalizedAxes;
363
trap_R_AddRefEntityToScene( &barrel );
367
// accompanying rings / spheres for powerups
368
if ( !cg_simpleItems.integer )
372
VectorClear( spinAngles );
374
if ( item->giType == IT_HEALTH || item->giType == IT_POWERUP )
376
if ( ( ent.hModel = cg_items[es->modelindex].models[1] ) != 0 )
378
if ( item->giType == IT_POWERUP )
381
spinAngles[1] = ( cg.time & 1023 ) * 360 / -1024.0f;
383
AnglesToAxis( spinAngles, ent.axis );
385
// scale up if respawning
387
VectorScale( ent.axis[0], frac, ent.axis[0] );
388
VectorScale( ent.axis[1], frac, ent.axis[1] );
389
VectorScale( ent.axis[2], frac, ent.axis[2] );
390
ent.nonNormalizedAxes = qtrue;
392
trap_R_AddRefEntityToScene( &ent );
398
//============================================================================
405
static void CG_Missile( centity_t *cent ) {
408
const weaponInfo_t *weapon;
411
s1 = ¢->currentState;
412
if ( s1->weapon > WP_NUM_WEAPONS ) {
415
weapon = &cg_weapons[s1->weapon];
417
// calculate the axis
418
VectorCopy( s1->angles, cent->lerpAngles);
421
if ( weapon->missileTrailFunc )
423
weapon->missileTrailFunc( cent, weapon );
426
if ( cent->currentState.modelindex == TEAM_RED ) {
429
else if ( cent->currentState.modelindex == TEAM_BLUE ) {
437
if ( weapon->missileDlight ) {
438
trap_R_AddLightToScene(cent->lerpOrigin, weapon->missileDlight,
439
weapon->missileDlightColor[col][0], weapon->missileDlightColor[col][1], weapon->missileDlightColor[col][2] );
443
if ( weapon->missileDlight ) {
444
trap_R_AddLightToScene(cent->lerpOrigin, weapon->missileDlight,
445
weapon->missileDlightColor[0], weapon->missileDlightColor[1], weapon->missileDlightColor[2] );
449
if ( weapon->missileSound ) {
452
BG_EvaluateTrajectoryDelta( ¢->currentState.pos, cg.time, velocity );
454
trap_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, velocity, weapon->missileSound );
457
// create the render entity
458
memset (&ent, 0, sizeof(ent));
459
VectorCopy( cent->lerpOrigin, ent.origin);
460
VectorCopy( cent->lerpOrigin, ent.oldorigin);
462
if ( cent->currentState.weapon == WP_PLASMAGUN ) {
463
ent.reType = RT_SPRITE;
466
ent.customShader = cgs.media.plasmaBallShader;
467
trap_R_AddRefEntityToScene( &ent );
471
// flicker between two skins
472
ent.skinNum = cg.clientFrame & 1;
473
ent.hModel = weapon->missileModel;
474
ent.renderfx = weapon->missileRenderfx | RF_NOSHADOW;
477
if ( cent->currentState.weapon == WP_PROX_LAUNCHER ) {
478
if (s1->generic1 == TEAM_BLUE) {
479
ent.hModel = cgs.media.blueProxMine;
484
// convert direction of travel into axis
485
if ( VectorNormalize2( s1->pos.trDelta, ent.axis[0] ) == 0 ) {
490
if ( s1->pos.trType != TR_STATIONARY ) {
491
RotateAroundDirection( ent.axis, cg.time / 4 );
494
if ( s1->weapon == WP_PROX_LAUNCHER ) {
495
AnglesToAxis( cent->lerpAngles, ent.axis );
500
RotateAroundDirection( ent.axis, s1->time );
504
// add to refresh list, possibly with quad glow
505
CG_AddRefEntityWithPowerups( &ent, s1, TEAM_FREE );
512
This is called when the grapple is sitting up against the wall
515
static void CG_Grapple( centity_t *cent ) {
518
const weaponInfo_t *weapon;
520
s1 = ¢->currentState;
521
if ( s1->weapon > WP_NUM_WEAPONS ) {
524
weapon = &cg_weapons[s1->weapon];
526
// calculate the axis
527
VectorCopy( s1->angles, cent->lerpAngles);
529
#if 0 // FIXME add grapple pull sound here..?
531
if ( weapon->missileSound ) {
532
trap_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin, weapon->missileSound );
536
// Will draw cable if needed
537
CG_GrappleTrail ( cent, weapon );
539
// create the render entity
540
memset (&ent, 0, sizeof(ent));
541
VectorCopy( cent->lerpOrigin, ent.origin);
542
VectorCopy( cent->lerpOrigin, ent.oldorigin);
544
// flicker between two skins
545
ent.skinNum = cg.clientFrame & 1;
546
ent.hModel = weapon->missileModel;
547
ent.renderfx = weapon->missileRenderfx | RF_NOSHADOW;
549
// convert direction of travel into axis
550
if ( VectorNormalize2( s1->pos.trDelta, ent.axis[0] ) == 0 ) {
554
trap_R_AddRefEntityToScene( &ent );
562
static void CG_Mover( centity_t *cent ) {
566
s1 = ¢->currentState;
568
// create the render entity
569
memset (&ent, 0, sizeof(ent));
570
VectorCopy( cent->lerpOrigin, ent.origin);
571
VectorCopy( cent->lerpOrigin, ent.oldorigin);
572
AnglesToAxis( cent->lerpAngles, ent.axis );
574
ent.renderfx = RF_NOSHADOW;
576
// flicker between two skins (FIXME?)
577
ent.skinNum = ( cg.time >> 6 ) & 1;
579
// get the model, either as a bmodel or a modelindex
580
if ( s1->solid == SOLID_BMODEL ) {
581
ent.hModel = cgs.inlineDrawModel[s1->modelindex];
583
ent.hModel = cgs.gameModels[s1->modelindex];
586
// add to refresh list
587
trap_R_AddRefEntityToScene(&ent);
589
// add the secondary model
590
if ( s1->modelindex2 ) {
592
ent.hModel = cgs.gameModels[s1->modelindex2];
593
trap_R_AddRefEntityToScene(&ent);
602
Also called as an event
605
void CG_Beam( centity_t *cent ) {
609
s1 = ¢->currentState;
611
// create the render entity
612
memset (&ent, 0, sizeof(ent));
613
VectorCopy( s1->pos.trBase, ent.origin );
614
VectorCopy( s1->origin2, ent.oldorigin );
615
AxisClear( ent.axis );
616
ent.reType = RT_BEAM;
618
ent.renderfx = RF_NOSHADOW;
620
// add to refresh list
621
trap_R_AddRefEntityToScene(&ent);
630
static void CG_Portal( centity_t *cent ) {
634
s1 = ¢->currentState;
636
// create the render entity
637
memset (&ent, 0, sizeof(ent));
638
VectorCopy( cent->lerpOrigin, ent.origin );
639
VectorCopy( s1->origin2, ent.oldorigin );
640
ByteToDir( s1->eventParm, ent.axis[0] );
641
PerpendicularVector( ent.axis[1], ent.axis[0] );
643
// negating this tends to get the directions like they want
644
// we really should have a camera roll value
645
VectorSubtract( vec3_origin, ent.axis[1], ent.axis[1] );
647
CrossProduct( ent.axis[0], ent.axis[1], ent.axis[2] );
648
ent.reType = RT_PORTALSURFACE;
649
ent.oldframe = s1->powerups;
650
ent.frame = s1->frame; // rotation speed
651
ent.skinNum = s1->clientNum/256.0 * 360; // roll offset
653
// add to refresh list
654
trap_R_AddRefEntityToScene(&ent);
659
=========================
660
CG_AdjustPositionForMover
662
Also called by client movement prediction code
663
=========================
665
void CG_AdjustPositionForMover( const vec3_t in, int moverNum, int fromTime, int toTime, vec3_t out ) {
667
vec3_t oldOrigin, origin, deltaOrigin;
668
vec3_t oldAngles, angles, deltaAngles;
670
if ( moverNum <= 0 || moverNum >= ENTITYNUM_MAX_NORMAL ) {
671
VectorCopy( in, out );
675
cent = &cg_entities[ moverNum ];
676
if ( cent->currentState.eType != ET_MOVER ) {
677
VectorCopy( in, out );
681
BG_EvaluateTrajectory( ¢->currentState.pos, fromTime, oldOrigin );
682
BG_EvaluateTrajectory( ¢->currentState.apos, fromTime, oldAngles );
684
BG_EvaluateTrajectory( ¢->currentState.pos, toTime, origin );
685
BG_EvaluateTrajectory( ¢->currentState.apos, toTime, angles );
687
VectorSubtract( origin, oldOrigin, deltaOrigin );
688
VectorSubtract( angles, oldAngles, deltaAngles );
690
VectorAdd( in, deltaOrigin, out );
692
// FIXME: origin change when on a rotating object
697
=============================
698
CG_InterpolateEntityPosition
699
=============================
701
static void CG_InterpolateEntityPosition( centity_t *cent ) {
702
vec3_t current, next;
705
// it would be an internal error to find an entity that interpolates without
706
// a snapshot ahead of the current one
707
if ( cg.nextSnap == NULL ) {
708
CG_Error( "CG_InterpoateEntityPosition: cg.nextSnap == NULL" );
711
f = cg.frameInterpolation;
713
// this will linearize a sine or parabolic curve, but it is important
714
// to not extrapolate player positions if more recent data is available
715
BG_EvaluateTrajectory( ¢->currentState.pos, cg.snap->serverTime, current );
716
BG_EvaluateTrajectory( ¢->nextState.pos, cg.nextSnap->serverTime, next );
718
cent->lerpOrigin[0] = current[0] + f * ( next[0] - current[0] );
719
cent->lerpOrigin[1] = current[1] + f * ( next[1] - current[1] );
720
cent->lerpOrigin[2] = current[2] + f * ( next[2] - current[2] );
722
BG_EvaluateTrajectory( ¢->currentState.apos, cg.snap->serverTime, current );
723
BG_EvaluateTrajectory( ¢->nextState.apos, cg.nextSnap->serverTime, next );
725
cent->lerpAngles[0] = LerpAngle( current[0], next[0], f );
726
cent->lerpAngles[1] = LerpAngle( current[1], next[1], f );
727
cent->lerpAngles[2] = LerpAngle( current[2], next[2], f );
733
CG_CalcEntityLerpPositions
737
static void CG_CalcEntityLerpPositions( centity_t *cent ) {
739
// if this player does not want to see extrapolated players
740
if ( !cg_smoothClients.integer ) {
741
// make sure the clients use TR_INTERPOLATE
742
if ( cent->currentState.number < MAX_CLIENTS ) {
743
cent->currentState.pos.trType = TR_INTERPOLATE;
744
cent->nextState.pos.trType = TR_INTERPOLATE;
748
if ( cent->interpolate && cent->currentState.pos.trType == TR_INTERPOLATE ) {
749
CG_InterpolateEntityPosition( cent );
753
// first see if we can interpolate between two snaps for
754
// linear extrapolated clients
755
if ( cent->interpolate && cent->currentState.pos.trType == TR_LINEAR_STOP &&
756
cent->currentState.number < MAX_CLIENTS) {
757
CG_InterpolateEntityPosition( cent );
761
// just use the current frame and evaluate as best we can
762
BG_EvaluateTrajectory( ¢->currentState.pos, cg.time, cent->lerpOrigin );
763
BG_EvaluateTrajectory( ¢->currentState.apos, cg.time, cent->lerpAngles );
765
// adjust for riding a mover if it wasn't rolled into the predicted
767
if ( cent != &cg.predictedPlayerEntity ) {
768
CG_AdjustPositionForMover( cent->lerpOrigin, cent->currentState.groundEntityNum,
769
cg.snap->serverTime, cg.time, cent->lerpOrigin );
778
static void CG_TeamBase( centity_t *cent ) {
785
if ( cgs.gametype == GT_CTF || cgs.gametype == GT_1FCTF ) {
787
if ( cgs.gametype == GT_CTF) {
789
// show the flag base
790
memset(&model, 0, sizeof(model));
791
model.reType = RT_MODEL;
792
VectorCopy( cent->lerpOrigin, model.lightingOrigin );
793
VectorCopy( cent->lerpOrigin, model.origin );
794
AnglesToAxis( cent->currentState.angles, model.axis );
795
if ( cent->currentState.modelindex == TEAM_RED ) {
796
model.hModel = cgs.media.redFlagBaseModel;
798
else if ( cent->currentState.modelindex == TEAM_BLUE ) {
799
model.hModel = cgs.media.blueFlagBaseModel;
802
model.hModel = cgs.media.neutralFlagBaseModel;
804
trap_R_AddRefEntityToScene( &model );
807
else if ( cgs.gametype == GT_OBELISK ) {
809
memset(&model, 0, sizeof(model));
810
model.reType = RT_MODEL;
811
VectorCopy( cent->lerpOrigin, model.lightingOrigin );
812
VectorCopy( cent->lerpOrigin, model.origin );
813
AnglesToAxis( cent->currentState.angles, model.axis );
815
model.hModel = cgs.media.overloadBaseModel;
816
trap_R_AddRefEntityToScene( &model );
818
if ( cent->currentState.frame == 1) {
820
// modelindex2 is the health value of the obelisk
821
c = cent->currentState.modelindex2;
822
model.shaderRGBA[0] = 0xff;
823
model.shaderRGBA[1] = c;
824
model.shaderRGBA[2] = c;
825
model.shaderRGBA[3] = 0xff;
827
model.hModel = cgs.media.overloadEnergyModel;
828
trap_R_AddRefEntityToScene( &model );
831
if ( cent->currentState.frame == 2) {
832
if ( !cent->miscTime ) {
833
cent->miscTime = cg.time;
835
t = cg.time - cent->miscTime;
836
h = (cg_obeliskRespawnDelay.integer - 5) * 1000;
839
c = (float) (t - h) / h;
847
AnglesToAxis( cent->currentState.angles, model.axis );
849
model.shaderRGBA[0] = c * 0xff;
850
model.shaderRGBA[1] = c * 0xff;
851
model.shaderRGBA[2] = c * 0xff;
852
model.shaderRGBA[3] = c * 0xff;
854
model.hModel = cgs.media.overloadLightsModel;
855
trap_R_AddRefEntityToScene( &model );
858
if ( !cent->muzzleFlashTime ) {
859
trap_S_StartSound (cent->lerpOrigin, ENTITYNUM_NONE, CHAN_BODY, cgs.media.obeliskRespawnSound);
860
cent->muzzleFlashTime = 1;
862
VectorCopy(cent->currentState.angles, angles);
863
angles[YAW] += (float) 16 * acos(1-c) * 180 / M_PI;
864
AnglesToAxis( angles, model.axis );
866
VectorScale( model.axis[0], c, model.axis[0]);
867
VectorScale( model.axis[1], c, model.axis[1]);
868
VectorScale( model.axis[2], c, model.axis[2]);
870
model.shaderRGBA[0] = 0xff;
871
model.shaderRGBA[1] = 0xff;
872
model.shaderRGBA[2] = 0xff;
873
model.shaderRGBA[3] = 0xff;
875
model.origin[2] += 56;
876
model.hModel = cgs.media.overloadTargetModel;
877
trap_R_AddRefEntityToScene( &model );
880
//FIXME: show animated smoke
885
cent->muzzleFlashTime = 0;
886
// modelindex2 is the health value of the obelisk
887
c = cent->currentState.modelindex2;
888
model.shaderRGBA[0] = 0xff;
889
model.shaderRGBA[1] = c;
890
model.shaderRGBA[2] = c;
891
model.shaderRGBA[3] = 0xff;
893
model.hModel = cgs.media.overloadLightsModel;
894
trap_R_AddRefEntityToScene( &model );
896
model.origin[2] += 56;
897
model.hModel = cgs.media.overloadTargetModel;
898
trap_R_AddRefEntityToScene( &model );
901
else if ( cgs.gametype == GT_HARVESTER ) {
902
// show harvester model
903
memset(&model, 0, sizeof(model));
904
model.reType = RT_MODEL;
905
VectorCopy( cent->lerpOrigin, model.lightingOrigin );
906
VectorCopy( cent->lerpOrigin, model.origin );
907
AnglesToAxis( cent->currentState.angles, model.axis );
909
if ( cent->currentState.modelindex == TEAM_RED ) {
910
model.hModel = cgs.media.harvesterModel;
911
model.customSkin = cgs.media.harvesterRedSkin;
913
else if ( cent->currentState.modelindex == TEAM_BLUE ) {
914
model.hModel = cgs.media.harvesterModel;
915
model.customSkin = cgs.media.harvesterBlueSkin;
918
model.hModel = cgs.media.harvesterNeutralModel;
919
model.customSkin = 0;
921
trap_R_AddRefEntityToScene( &model );
932
static void CG_AddCEntity( centity_t *cent ) {
933
// event-only entities will have been dealt with already
934
if ( cent->currentState.eType >= ET_EVENTS ) {
938
// calculate the current origin
939
CG_CalcEntityLerpPositions( cent );
941
// add automatic effects
942
CG_EntityEffects( cent );
944
switch ( cent->currentState.eType ) {
946
CG_Error( "Bad entity type: %i\n", cent->currentState.eType );
949
case ET_PUSH_TRIGGER:
950
case ET_TELEPORT_TRIGGER:
991
void CG_AddPacketEntities( void ) {
996
// set cg.frameInterpolation
1000
delta = (cg.nextSnap->serverTime - cg.snap->serverTime);
1002
cg.frameInterpolation = 0;
1004
cg.frameInterpolation = (float)( cg.time - cg.snap->serverTime ) / delta;
1007
cg.frameInterpolation = 0; // actually, it should never be used, because
1008
// no entities should be marked as interpolating
1011
// the auto-rotating items will all have the same axis
1012
cg.autoAngles[0] = 0;
1013
cg.autoAngles[1] = ( cg.time & 2047 ) * 360 / 2048.0;
1014
cg.autoAngles[2] = 0;
1016
cg.autoAnglesFast[0] = 0;
1017
cg.autoAnglesFast[1] = ( cg.time & 1023 ) * 360 / 1024.0f;
1018
cg.autoAnglesFast[2] = 0;
1020
AnglesToAxis( cg.autoAngles, cg.autoAxis );
1021
AnglesToAxis( cg.autoAnglesFast, cg.autoAxisFast );
1023
// generate and add the entity from the playerstate
1024
ps = &cg.predictedPlayerState;
1025
BG_PlayerStateToEntityState( ps, &cg.predictedPlayerEntity.currentState, qfalse );
1026
CG_AddCEntity( &cg.predictedPlayerEntity );
1028
// lerp the non-predicted value for lightning gun origins
1029
CG_CalcEntityLerpPositions( &cg_entities[ cg.snap->ps.clientNum ] );
1031
// add each entity sent over by the server
1032
for ( num = 0 ; num < cg.snap->numEntities ; num++ ) {
1033
cent = &cg_entities[ cg.snap->entities[ num ].number ];
1034
CG_AddCEntity( cent );