~ubuntu-branches/ubuntu/precise/openarena/precise

« back to all changes in this revision

Viewing changes to code/cgame/cg_ents.c

  • Committer: Bazaar Package Importer
  • Author(s): Bruno "Fuddl" Kleinert
  • Date: 2007-01-20 12:28:09 UTC
  • Revision ID: james.westby@ubuntu.com-20070120122809-2yza5ojt7nqiyiam
Tags: upstream-0.6.0
ImportĀ upstreamĀ versionĀ 0.6.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
===========================================================================
 
3
Copyright (C) 1999-2005 Id Software, Inc.
 
4
 
 
5
This file is part of Quake III Arena source code.
 
6
 
 
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.
 
11
 
 
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.
 
16
 
 
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
===========================================================================
 
21
*/
 
22
//
 
23
// cg_ents.c -- present snapshot entities, happens every single frame
 
24
 
 
25
#include "cg_local.h"
 
26
 
 
27
 
 
28
/*
 
29
======================
 
30
CG_PositionEntityOnTag
 
31
 
 
32
Modifies the entities position and axis by the given
 
33
tag location
 
34
======================
 
35
*/
 
36
void CG_PositionEntityOnTag( refEntity_t *entity, const refEntity_t *parent, 
 
37
                                                        qhandle_t parentModel, char *tagName ) {
 
38
        int                             i;
 
39
        orientation_t   lerped;
 
40
        
 
41
        // lerp the tag
 
42
        trap_R_LerpTag( &lerped, parentModel, parent->oldframe, parent->frame,
 
43
                1.0 - parent->backlerp, tagName );
 
44
 
 
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 );
 
49
        }
 
50
 
 
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;
 
54
}
 
55
 
 
56
 
 
57
/*
 
58
======================
 
59
CG_PositionRotatedEntityOnTag
 
60
 
 
61
Modifies the entities position and axis by the given
 
62
tag location
 
63
======================
 
64
*/
 
65
void CG_PositionRotatedEntityOnTag( refEntity_t *entity, const refEntity_t *parent, 
 
66
                                                        qhandle_t parentModel, char *tagName ) {
 
67
        int                             i;
 
68
        orientation_t   lerped;
 
69
        vec3_t                  tempAxis[3];
 
70
 
 
71
//AxisClear( entity->axis );
 
72
        // lerp the tag
 
73
        trap_R_LerpTag( &lerped, parentModel, parent->oldframe, parent->frame,
 
74
                1.0 - parent->backlerp, tagName );
 
75
 
 
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 );
 
80
        }
 
81
 
 
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 );
 
85
}
 
86
 
 
87
 
 
88
 
 
89
/*
 
90
==========================================================================
 
91
 
 
92
FUNCTIONS CALLED EACH FRAME
 
93
 
 
94
==========================================================================
 
95
*/
 
96
 
 
97
/*
 
98
======================
 
99
CG_SetEntitySoundPosition
 
100
 
 
101
Also called by event processing code
 
102
======================
 
103
*/
 
104
void CG_SetEntitySoundPosition( centity_t *cent ) {
 
105
        if ( cent->currentState.solid == SOLID_BMODEL ) {
 
106
                vec3_t  origin;
 
107
                float   *v;
 
108
 
 
109
                v = cgs.inlineModelMidpoints[ cent->currentState.modelindex ];
 
110
                VectorAdd( cent->lerpOrigin, v, origin );
 
111
                trap_S_UpdateEntityPosition( cent->currentState.number, origin );
 
112
        } else {
 
113
                trap_S_UpdateEntityPosition( cent->currentState.number, cent->lerpOrigin );
 
114
        }
 
115
}
 
116
 
 
117
/*
 
118
==================
 
119
CG_EntityEffects
 
120
 
 
121
Add continuous entity effects, like local entity emission and lighting
 
122
==================
 
123
*/
 
124
static void CG_EntityEffects( centity_t *cent ) {
 
125
 
 
126
        // update sound origins
 
127
        CG_SetEntitySoundPosition( cent );
 
128
 
 
129
        // add loop sound
 
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 ] );
 
134
                } else {
 
135
                        trap_S_AddRealLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin, 
 
136
                                cgs.gameSounds[ cent->currentState.loopSound ] );
 
137
                }
 
138
        }
 
139
 
 
140
 
 
141
        // constant light glow
 
142
        if ( cent->currentState.constantLight ) {
 
143
                int             cl;
 
144
                int             i, r, g, b;
 
145
 
 
146
                cl = cent->currentState.constantLight;
 
147
                r = cl & 255;
 
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 );
 
152
        }
 
153
 
 
154
}
 
155
 
 
156
 
 
157
/*
 
158
==================
 
159
CG_General
 
160
==================
 
161
*/
 
162
static void CG_General( centity_t *cent ) {
 
163
        refEntity_t                     ent;
 
164
        entityState_t           *s1;
 
165
 
 
166
        s1 = &cent->currentState;
 
167
 
 
168
        // if set to invisible, skip
 
169
        if (!s1->modelindex) {
 
170
                return;
 
171
        }
 
172
 
 
173
        memset (&ent, 0, sizeof(ent));
 
174
 
 
175
        // set frame
 
176
 
 
177
        ent.frame = s1->frame;
 
178
        ent.oldframe = ent.frame;
 
179
        ent.backlerp = 0;
 
180
 
 
181
        VectorCopy( cent->lerpOrigin, ent.origin);
 
182
        VectorCopy( cent->lerpOrigin, ent.oldorigin);
 
183
 
 
184
        ent.hModel = cgs.gameModels[s1->modelindex];
 
185
 
 
186
        // player model
 
187
        if (s1->number == cg.snap->ps.clientNum) {
 
188
                ent.renderfx |= RF_THIRD_PERSON;        // only draw from mirrors
 
189
        }
 
190
 
 
191
        // convert angles to axis
 
192
        AnglesToAxis( cent->lerpAngles, ent.axis );
 
193
 
 
194
        // add to refresh list
 
195
        trap_R_AddRefEntityToScene (&ent);
 
196
}
 
197
 
 
198
/*
 
199
==================
 
200
CG_Speaker
 
201
 
 
202
Speaker entities can automatically play sounds
 
203
==================
 
204
*/
 
205
static void CG_Speaker( centity_t *cent ) {
 
206
        if ( ! cent->currentState.clientNum ) { // FIXME: use something other than clientNum...
 
207
                return;         // not auto triggering
 
208
        }
 
209
 
 
210
        if ( cg.time < cent->miscTime ) {
 
211
                return;
 
212
        }
 
213
 
 
214
        trap_S_StartSound (NULL, cent->currentState.number, CHAN_ITEM, cgs.gameSounds[cent->currentState.eventParm] );
 
215
 
 
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();
 
219
}
 
220
 
 
221
/*
 
222
==================
 
223
CG_Item
 
224
==================
 
225
*/
 
226
static void CG_Item( centity_t *cent ) {
 
227
        refEntity_t             ent;
 
228
        entityState_t   *es;
 
229
        gitem_t                 *item;
 
230
        int                             msec;
 
231
        float                   frac;
 
232
        float                   scale;
 
233
        weaponInfo_t    *wi;
 
234
 
 
235
        es = &cent->currentState;
 
236
        if ( es->modelindex >= bg_numItems ) {
 
237
                CG_Error( "Bad item index %i on entity", es->modelindex );
 
238
        }
 
239
 
 
240
        // if set to invisible, skip
 
241
        if ( !es->modelindex || ( es->eFlags & EF_NODRAW ) ) {
 
242
                return;
 
243
        }
 
244
 
 
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 );
 
250
                ent.radius = 14;
 
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);
 
257
                return;
 
258
        }
 
259
 
 
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;
 
263
 
 
264
        memset (&ent, 0, sizeof(ent));
 
265
 
 
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 );
 
270
        } else {
 
271
                VectorCopy( cg.autoAngles, cent->lerpAngles );
 
272
                AxisCopy( cg.autoAxis, ent.axis );
 
273
        }
 
274
 
 
275
        wi = NULL;
 
276
        // the weapons have their origin where they attatch to player
 
277
        // models, so we need to offset them or they will rotate
 
278
        // eccentricly
 
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];
 
293
 
 
294
                cent->lerpOrigin[2] += 8;       // an extra height boost
 
295
        }
 
296
 
 
297
        ent.hModel = cg_items[es->modelindex].models[0];
 
298
 
 
299
        VectorCopy( cent->lerpOrigin, ent.origin);
 
300
        VectorCopy( cent->lerpOrigin, ent.oldorigin);
 
301
 
 
302
        ent.nonNormalizedAxes = qfalse;
 
303
 
 
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;
 
312
        } else {
 
313
                frac = 1.0;
 
314
        }
 
315
 
 
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;
 
321
        }
 
322
 
 
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;
 
329
#ifdef MISSIONPACK
 
330
                trap_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin, cgs.media.weaponHoverSound );
 
331
#endif
 
332
        }
 
333
 
 
334
#ifdef MISSIONPACK
 
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;
 
340
        }
 
341
#endif
 
342
 
 
343
        // add to refresh list
 
344
        trap_R_AddRefEntityToScene(&ent);
 
345
 
 
346
#ifdef MISSIONPACK
 
347
        if ( item->giType == IT_WEAPON && wi->barrelModel ) {
 
348
                refEntity_t     barrel;
 
349
 
 
350
                memset( &barrel, 0, sizeof( barrel ) );
 
351
 
 
352
                barrel.hModel = wi->barrelModel;
 
353
 
 
354
                VectorCopy( ent.lightingOrigin, barrel.lightingOrigin );
 
355
                barrel.shadowPlane = ent.shadowPlane;
 
356
                barrel.renderfx = ent.renderfx;
 
357
 
 
358
                CG_PositionRotatedEntityOnTag( &barrel, &ent, wi->weaponModel, "tag_barrel" );
 
359
 
 
360
                AxisCopy( ent.axis, barrel.axis );
 
361
                barrel.nonNormalizedAxes = ent.nonNormalizedAxes;
 
362
 
 
363
                trap_R_AddRefEntityToScene( &barrel );
 
364
        }
 
365
#endif
 
366
 
 
367
        // accompanying rings / spheres for powerups
 
368
        if ( !cg_simpleItems.integer ) 
 
369
        {
 
370
                vec3_t spinAngles;
 
371
 
 
372
                VectorClear( spinAngles );
 
373
 
 
374
                if ( item->giType == IT_HEALTH || item->giType == IT_POWERUP )
 
375
                {
 
376
                        if ( ( ent.hModel = cg_items[es->modelindex].models[1] ) != 0 )
 
377
                        {
 
378
                                if ( item->giType == IT_POWERUP )
 
379
                                {
 
380
                                        ent.origin[2] += 12;
 
381
                                        spinAngles[1] = ( cg.time & 1023 ) * 360 / -1024.0f;
 
382
                                }
 
383
                                AnglesToAxis( spinAngles, ent.axis );
 
384
                                
 
385
                                // scale up if respawning
 
386
                                if ( frac != 1.0 ) {
 
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;
 
391
                                }
 
392
                                trap_R_AddRefEntityToScene( &ent );
 
393
                        }
 
394
                }
 
395
        }
 
396
}
 
397
 
 
398
//============================================================================
 
399
 
 
400
/*
 
401
===============
 
402
CG_Missile
 
403
===============
 
404
*/
 
405
static void CG_Missile( centity_t *cent ) {
 
406
        refEntity_t                     ent;
 
407
        entityState_t           *s1;
 
408
        const weaponInfo_t              *weapon;
 
409
//      int     col;
 
410
 
 
411
        s1 = &cent->currentState;
 
412
        if ( s1->weapon > WP_NUM_WEAPONS ) {
 
413
                s1->weapon = 0;
 
414
        }
 
415
        weapon = &cg_weapons[s1->weapon];
 
416
 
 
417
        // calculate the axis
 
418
        VectorCopy( s1->angles, cent->lerpAngles);
 
419
 
 
420
        // add trails
 
421
        if ( weapon->missileTrailFunc ) 
 
422
        {
 
423
                weapon->missileTrailFunc( cent, weapon );
 
424
        }
 
425
/*
 
426
        if ( cent->currentState.modelindex == TEAM_RED ) {
 
427
                col = 1;
 
428
        }
 
429
        else if ( cent->currentState.modelindex == TEAM_BLUE ) {
 
430
                col = 2;
 
431
        }
 
432
        else {
 
433
                col = 0;
 
434
        }
 
435
 
 
436
        // add dynamic light
 
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] );
 
440
        }
 
441
*/
 
442
        // add dynamic light
 
443
        if ( weapon->missileDlight ) {
 
444
                trap_R_AddLightToScene(cent->lerpOrigin, weapon->missileDlight, 
 
445
                        weapon->missileDlightColor[0], weapon->missileDlightColor[1], weapon->missileDlightColor[2] );
 
446
        }
 
447
 
 
448
        // add missile sound
 
449
        if ( weapon->missileSound ) {
 
450
                vec3_t  velocity;
 
451
 
 
452
                BG_EvaluateTrajectoryDelta( &cent->currentState.pos, cg.time, velocity );
 
453
 
 
454
                trap_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, velocity, weapon->missileSound );
 
455
        }
 
456
 
 
457
        // create the render entity
 
458
        memset (&ent, 0, sizeof(ent));
 
459
        VectorCopy( cent->lerpOrigin, ent.origin);
 
460
        VectorCopy( cent->lerpOrigin, ent.oldorigin);
 
461
 
 
462
        if ( cent->currentState.weapon == WP_PLASMAGUN ) {
 
463
                ent.reType = RT_SPRITE;
 
464
                ent.radius = 16;
 
465
                ent.rotation = 0;
 
466
                ent.customShader = cgs.media.plasmaBallShader;
 
467
                trap_R_AddRefEntityToScene( &ent );
 
468
                return;
 
469
        }
 
470
 
 
471
        // flicker between two skins
 
472
        ent.skinNum = cg.clientFrame & 1;
 
473
        ent.hModel = weapon->missileModel;
 
474
        ent.renderfx = weapon->missileRenderfx | RF_NOSHADOW;
 
475
 
 
476
#ifdef MISSIONPACK
 
477
        if ( cent->currentState.weapon == WP_PROX_LAUNCHER ) {
 
478
                if (s1->generic1 == TEAM_BLUE) {
 
479
                        ent.hModel = cgs.media.blueProxMine;
 
480
                }
 
481
        }
 
482
#endif
 
483
 
 
484
        // convert direction of travel into axis
 
485
        if ( VectorNormalize2( s1->pos.trDelta, ent.axis[0] ) == 0 ) {
 
486
                ent.axis[0][2] = 1;
 
487
        }
 
488
 
 
489
        // spin as it moves
 
490
        if ( s1->pos.trType != TR_STATIONARY ) {
 
491
                RotateAroundDirection( ent.axis, cg.time / 4 );
 
492
        } else {
 
493
#ifdef MISSIONPACK
 
494
                if ( s1->weapon == WP_PROX_LAUNCHER ) {
 
495
                        AnglesToAxis( cent->lerpAngles, ent.axis );
 
496
                }
 
497
                else
 
498
#endif
 
499
                {
 
500
                        RotateAroundDirection( ent.axis, s1->time );
 
501
                }
 
502
        }
 
503
 
 
504
        // add to refresh list, possibly with quad glow
 
505
        CG_AddRefEntityWithPowerups( &ent, s1, TEAM_FREE );
 
506
}
 
507
 
 
508
/*
 
509
===============
 
510
CG_Grapple
 
511
 
 
512
This is called when the grapple is sitting up against the wall
 
513
===============
 
514
*/
 
515
static void CG_Grapple( centity_t *cent ) {
 
516
        refEntity_t                     ent;
 
517
        entityState_t           *s1;
 
518
        const weaponInfo_t              *weapon;
 
519
 
 
520
        s1 = &cent->currentState;
 
521
        if ( s1->weapon > WP_NUM_WEAPONS ) {
 
522
                s1->weapon = 0;
 
523
        }
 
524
        weapon = &cg_weapons[s1->weapon];
 
525
 
 
526
        // calculate the axis
 
527
        VectorCopy( s1->angles, cent->lerpAngles);
 
528
 
 
529
#if 0 // FIXME add grapple pull sound here..?
 
530
        // add missile sound
 
531
        if ( weapon->missileSound ) {
 
532
                trap_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin, weapon->missileSound );
 
533
        }
 
534
#endif
 
535
 
 
536
        // Will draw cable if needed
 
537
        CG_GrappleTrail ( cent, weapon );
 
538
 
 
539
        // create the render entity
 
540
        memset (&ent, 0, sizeof(ent));
 
541
        VectorCopy( cent->lerpOrigin, ent.origin);
 
542
        VectorCopy( cent->lerpOrigin, ent.oldorigin);
 
543
 
 
544
        // flicker between two skins
 
545
        ent.skinNum = cg.clientFrame & 1;
 
546
        ent.hModel = weapon->missileModel;
 
547
        ent.renderfx = weapon->missileRenderfx | RF_NOSHADOW;
 
548
 
 
549
        // convert direction of travel into axis
 
550
        if ( VectorNormalize2( s1->pos.trDelta, ent.axis[0] ) == 0 ) {
 
551
                ent.axis[0][2] = 1;
 
552
        }
 
553
 
 
554
        trap_R_AddRefEntityToScene( &ent );
 
555
}
 
556
 
 
557
/*
 
558
===============
 
559
CG_Mover
 
560
===============
 
561
*/
 
562
static void CG_Mover( centity_t *cent ) {
 
563
        refEntity_t                     ent;
 
564
        entityState_t           *s1;
 
565
 
 
566
        s1 = &cent->currentState;
 
567
 
 
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 );
 
573
 
 
574
        ent.renderfx = RF_NOSHADOW;
 
575
 
 
576
        // flicker between two skins (FIXME?)
 
577
        ent.skinNum = ( cg.time >> 6 ) & 1;
 
578
 
 
579
        // get the model, either as a bmodel or a modelindex
 
580
        if ( s1->solid == SOLID_BMODEL ) {
 
581
                ent.hModel = cgs.inlineDrawModel[s1->modelindex];
 
582
        } else {
 
583
                ent.hModel = cgs.gameModels[s1->modelindex];
 
584
        }
 
585
 
 
586
        // add to refresh list
 
587
        trap_R_AddRefEntityToScene(&ent);
 
588
 
 
589
        // add the secondary model
 
590
        if ( s1->modelindex2 ) {
 
591
                ent.skinNum = 0;
 
592
                ent.hModel = cgs.gameModels[s1->modelindex2];
 
593
                trap_R_AddRefEntityToScene(&ent);
 
594
        }
 
595
 
 
596
}
 
597
 
 
598
/*
 
599
===============
 
600
CG_Beam
 
601
 
 
602
Also called as an event
 
603
===============
 
604
*/
 
605
void CG_Beam( centity_t *cent ) {
 
606
        refEntity_t                     ent;
 
607
        entityState_t           *s1;
 
608
 
 
609
        s1 = &cent->currentState;
 
610
 
 
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;
 
617
 
 
618
        ent.renderfx = RF_NOSHADOW;
 
619
 
 
620
        // add to refresh list
 
621
        trap_R_AddRefEntityToScene(&ent);
 
622
}
 
623
 
 
624
 
 
625
/*
 
626
===============
 
627
CG_Portal
 
628
===============
 
629
*/
 
630
static void CG_Portal( centity_t *cent ) {
 
631
        refEntity_t                     ent;
 
632
        entityState_t           *s1;
 
633
 
 
634
        s1 = &cent->currentState;
 
635
 
 
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] );
 
642
 
 
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] );
 
646
 
 
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
 
652
 
 
653
        // add to refresh list
 
654
        trap_R_AddRefEntityToScene(&ent);
 
655
}
 
656
 
 
657
 
 
658
/*
 
659
=========================
 
660
CG_AdjustPositionForMover
 
661
 
 
662
Also called by client movement prediction code
 
663
=========================
 
664
*/
 
665
void CG_AdjustPositionForMover( const vec3_t in, int moverNum, int fromTime, int toTime, vec3_t out ) {
 
666
        centity_t       *cent;
 
667
        vec3_t  oldOrigin, origin, deltaOrigin;
 
668
        vec3_t  oldAngles, angles, deltaAngles;
 
669
 
 
670
        if ( moverNum <= 0 || moverNum >= ENTITYNUM_MAX_NORMAL ) {
 
671
                VectorCopy( in, out );
 
672
                return;
 
673
        }
 
674
 
 
675
        cent = &cg_entities[ moverNum ];
 
676
        if ( cent->currentState.eType != ET_MOVER ) {
 
677
                VectorCopy( in, out );
 
678
                return;
 
679
        }
 
680
 
 
681
        BG_EvaluateTrajectory( &cent->currentState.pos, fromTime, oldOrigin );
 
682
        BG_EvaluateTrajectory( &cent->currentState.apos, fromTime, oldAngles );
 
683
 
 
684
        BG_EvaluateTrajectory( &cent->currentState.pos, toTime, origin );
 
685
        BG_EvaluateTrajectory( &cent->currentState.apos, toTime, angles );
 
686
 
 
687
        VectorSubtract( origin, oldOrigin, deltaOrigin );
 
688
        VectorSubtract( angles, oldAngles, deltaAngles );
 
689
 
 
690
        VectorAdd( in, deltaOrigin, out );
 
691
 
 
692
        // FIXME: origin change when on a rotating object
 
693
}
 
694
 
 
695
 
 
696
/*
 
697
=============================
 
698
CG_InterpolateEntityPosition
 
699
=============================
 
700
*/
 
701
static void CG_InterpolateEntityPosition( centity_t *cent ) {
 
702
        vec3_t          current, next;
 
703
        float           f;
 
704
 
 
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" );
 
709
        }
 
710
 
 
711
        f = cg.frameInterpolation;
 
712
 
 
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( &cent->currentState.pos, cg.snap->serverTime, current );
 
716
        BG_EvaluateTrajectory( &cent->nextState.pos, cg.nextSnap->serverTime, next );
 
717
 
 
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] );
 
721
 
 
722
        BG_EvaluateTrajectory( &cent->currentState.apos, cg.snap->serverTime, current );
 
723
        BG_EvaluateTrajectory( &cent->nextState.apos, cg.nextSnap->serverTime, next );
 
724
 
 
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 );
 
728
 
 
729
}
 
730
 
 
731
/*
 
732
===============
 
733
CG_CalcEntityLerpPositions
 
734
 
 
735
===============
 
736
*/
 
737
static void CG_CalcEntityLerpPositions( centity_t *cent ) {
 
738
 
 
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;
 
745
                }
 
746
        }
 
747
 
 
748
        if ( cent->interpolate && cent->currentState.pos.trType == TR_INTERPOLATE ) {
 
749
                CG_InterpolateEntityPosition( cent );
 
750
                return;
 
751
        }
 
752
 
 
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 );
 
758
                return;
 
759
        }
 
760
 
 
761
        // just use the current frame and evaluate as best we can
 
762
        BG_EvaluateTrajectory( &cent->currentState.pos, cg.time, cent->lerpOrigin );
 
763
        BG_EvaluateTrajectory( &cent->currentState.apos, cg.time, cent->lerpAngles );
 
764
 
 
765
        // adjust for riding a mover if it wasn't rolled into the predicted
 
766
        // player state
 
767
        if ( cent != &cg.predictedPlayerEntity ) {
 
768
                CG_AdjustPositionForMover( cent->lerpOrigin, cent->currentState.groundEntityNum, 
 
769
                cg.snap->serverTime, cg.time, cent->lerpOrigin );
 
770
        }
 
771
}
 
772
 
 
773
/*
 
774
===============
 
775
CG_TeamBase
 
776
===============
 
777
*/
 
778
static void CG_TeamBase( centity_t *cent ) {
 
779
        refEntity_t model;
 
780
#ifdef MISSIONPACK
 
781
        vec3_t angles;
 
782
        int t, h;
 
783
        float c;
 
784
 
 
785
        if ( cgs.gametype == GT_CTF || cgs.gametype == GT_1FCTF ) {
 
786
#else
 
787
        if ( cgs.gametype == GT_CTF) {
 
788
#endif
 
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;
 
797
                }
 
798
                else if ( cent->currentState.modelindex == TEAM_BLUE ) {
 
799
                        model.hModel = cgs.media.blueFlagBaseModel;
 
800
                }
 
801
                else {
 
802
                        model.hModel = cgs.media.neutralFlagBaseModel;
 
803
                }
 
804
                trap_R_AddRefEntityToScene( &model );
 
805
        }
 
806
#ifdef MISSIONPACK
 
807
        else if ( cgs.gametype == GT_OBELISK ) {
 
808
                // show the 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 );
 
814
 
 
815
                model.hModel = cgs.media.overloadBaseModel;
 
816
                trap_R_AddRefEntityToScene( &model );
 
817
                // if hit
 
818
                if ( cent->currentState.frame == 1) {
 
819
                        // show hit model
 
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;
 
826
                        //
 
827
                        model.hModel = cgs.media.overloadEnergyModel;
 
828
                        trap_R_AddRefEntityToScene( &model );
 
829
                }
 
830
                // if respawning
 
831
                if ( cent->currentState.frame == 2) {
 
832
                        if ( !cent->miscTime ) {
 
833
                                cent->miscTime = cg.time;
 
834
                        }
 
835
                        t = cg.time - cent->miscTime;
 
836
                        h = (cg_obeliskRespawnDelay.integer - 5) * 1000;
 
837
                        //
 
838
                        if (t > h) {
 
839
                                c = (float) (t - h) / h;
 
840
                                if (c > 1)
 
841
                                        c = 1;
 
842
                        }
 
843
                        else {
 
844
                                c = 0;
 
845
                        }
 
846
                        // show the lights
 
847
                        AnglesToAxis( cent->currentState.angles, model.axis );
 
848
                        //
 
849
                        model.shaderRGBA[0] = c * 0xff;
 
850
                        model.shaderRGBA[1] = c * 0xff;
 
851
                        model.shaderRGBA[2] = c * 0xff;
 
852
                        model.shaderRGBA[3] = c * 0xff;
 
853
 
 
854
                        model.hModel = cgs.media.overloadLightsModel;
 
855
                        trap_R_AddRefEntityToScene( &model );
 
856
                        // show the target
 
857
                        if (t > h) {
 
858
                                if ( !cent->muzzleFlashTime ) {
 
859
                                        trap_S_StartSound (cent->lerpOrigin, ENTITYNUM_NONE, CHAN_BODY,  cgs.media.obeliskRespawnSound);
 
860
                                        cent->muzzleFlashTime = 1;
 
861
                                }
 
862
                                VectorCopy(cent->currentState.angles, angles);
 
863
                                angles[YAW] += (float) 16 * acos(1-c) * 180 / M_PI;
 
864
                                AnglesToAxis( angles, model.axis );
 
865
 
 
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]);
 
869
 
 
870
                                model.shaderRGBA[0] = 0xff;
 
871
                                model.shaderRGBA[1] = 0xff;
 
872
                                model.shaderRGBA[2] = 0xff;
 
873
                                model.shaderRGBA[3] = 0xff;
 
874
                                //
 
875
                                model.origin[2] += 56;
 
876
                                model.hModel = cgs.media.overloadTargetModel;
 
877
                                trap_R_AddRefEntityToScene( &model );
 
878
                        }
 
879
                        else {
 
880
                                //FIXME: show animated smoke
 
881
                        }
 
882
                }
 
883
                else {
 
884
                        cent->miscTime = 0;
 
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;
 
892
                        // show the lights
 
893
                        model.hModel = cgs.media.overloadLightsModel;
 
894
                        trap_R_AddRefEntityToScene( &model );
 
895
                        // show the target
 
896
                        model.origin[2] += 56;
 
897
                        model.hModel = cgs.media.overloadTargetModel;
 
898
                        trap_R_AddRefEntityToScene( &model );
 
899
                }
 
900
        }
 
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 );
 
908
 
 
909
                if ( cent->currentState.modelindex == TEAM_RED ) {
 
910
                        model.hModel = cgs.media.harvesterModel;
 
911
                        model.customSkin = cgs.media.harvesterRedSkin;
 
912
                }
 
913
                else if ( cent->currentState.modelindex == TEAM_BLUE ) {
 
914
                        model.hModel = cgs.media.harvesterModel;
 
915
                        model.customSkin = cgs.media.harvesterBlueSkin;
 
916
                }
 
917
                else {
 
918
                        model.hModel = cgs.media.harvesterNeutralModel;
 
919
                        model.customSkin = 0;
 
920
                }
 
921
                trap_R_AddRefEntityToScene( &model );
 
922
        }
 
923
#endif
 
924
}
 
925
 
 
926
/*
 
927
===============
 
928
CG_AddCEntity
 
929
 
 
930
===============
 
931
*/
 
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 ) {
 
935
                return;
 
936
        }
 
937
 
 
938
        // calculate the current origin
 
939
        CG_CalcEntityLerpPositions( cent );
 
940
 
 
941
        // add automatic effects
 
942
        CG_EntityEffects( cent );
 
943
 
 
944
        switch ( cent->currentState.eType ) {
 
945
        default:
 
946
                CG_Error( "Bad entity type: %i\n", cent->currentState.eType );
 
947
                break;
 
948
        case ET_INVISIBLE:
 
949
        case ET_PUSH_TRIGGER:
 
950
        case ET_TELEPORT_TRIGGER:
 
951
                break;
 
952
        case ET_GENERAL:
 
953
                CG_General( cent );
 
954
                break;
 
955
        case ET_PLAYER:
 
956
                CG_Player( cent );
 
957
                break;
 
958
        case ET_ITEM:
 
959
                CG_Item( cent );
 
960
                break;
 
961
        case ET_MISSILE:
 
962
                CG_Missile( cent );
 
963
                break;
 
964
        case ET_MOVER:
 
965
                CG_Mover( cent );
 
966
                break;
 
967
        case ET_BEAM:
 
968
                CG_Beam( cent );
 
969
                break;
 
970
        case ET_PORTAL:
 
971
                CG_Portal( cent );
 
972
                break;
 
973
        case ET_SPEAKER:
 
974
                CG_Speaker( cent );
 
975
                break;
 
976
        case ET_GRAPPLE:
 
977
                CG_Grapple( cent );
 
978
                break;
 
979
        case ET_TEAM:
 
980
                CG_TeamBase( cent );
 
981
                break;
 
982
        }
 
983
}
 
984
 
 
985
/*
 
986
===============
 
987
CG_AddPacketEntities
 
988
 
 
989
===============
 
990
*/
 
991
void CG_AddPacketEntities( void ) {
 
992
        int                                     num;
 
993
        centity_t                       *cent;
 
994
        playerState_t           *ps;
 
995
 
 
996
        // set cg.frameInterpolation
 
997
        if ( cg.nextSnap ) {
 
998
                int             delta;
 
999
 
 
1000
                delta = (cg.nextSnap->serverTime - cg.snap->serverTime);
 
1001
                if ( delta == 0 ) {
 
1002
                        cg.frameInterpolation = 0;
 
1003
                } else {
 
1004
                        cg.frameInterpolation = (float)( cg.time - cg.snap->serverTime ) / delta;
 
1005
                }
 
1006
        } else {
 
1007
                cg.frameInterpolation = 0;      // actually, it should never be used, because 
 
1008
                                                                        // no entities should be marked as interpolating
 
1009
        }
 
1010
 
 
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;
 
1015
 
 
1016
        cg.autoAnglesFast[0] = 0;
 
1017
        cg.autoAnglesFast[1] = ( cg.time & 1023 ) * 360 / 1024.0f;
 
1018
        cg.autoAnglesFast[2] = 0;
 
1019
 
 
1020
        AnglesToAxis( cg.autoAngles, cg.autoAxis );
 
1021
        AnglesToAxis( cg.autoAnglesFast, cg.autoAxisFast );
 
1022
 
 
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 );
 
1027
 
 
1028
        // lerp the non-predicted value for lightning gun origins
 
1029
        CG_CalcEntityLerpPositions( &cg_entities[ cg.snap->ps.clientNum ] );
 
1030
 
 
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 );
 
1035
        }
 
1036
}
 
1037