cg_ents.c 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038
  1. /*
  2. ===========================================================================
  3. Copyright (C) 1999-2005 Id Software, Inc.
  4. This file is part of Quake III Arena source code.
  5. Quake III Arena source code is free software; you can redistribute it
  6. and/or modify it under the terms of the GNU General Public License as
  7. published by the Free Software Foundation; either version 2 of the License,
  8. or (at your option) any later version.
  9. Quake III Arena source code is distributed in the hope that it will be
  10. useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with Foobar; if not, write to the Free Software
  15. Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
  16. ===========================================================================
  17. */
  18. //
  19. // cg_ents.c -- present snapshot entities, happens every single frame
  20. #include "cg_local.h"
  21. /*
  22. ======================
  23. CG_PositionEntityOnTag
  24. Modifies the entities position and axis by the given
  25. tag location
  26. ======================
  27. */
  28. void CG_PositionEntityOnTag( refEntity_t *entity, const refEntity_t *parent,
  29. qhandle_t parentModel, char *tagName ) {
  30. int i;
  31. orientation_t lerped;
  32. // lerp the tag
  33. trap_R_LerpTag( &lerped, parentModel, parent->oldframe, parent->frame,
  34. 1.0 - parent->backlerp, tagName );
  35. // FIXME: allow origin offsets along tag?
  36. VectorCopy( parent->origin, entity->origin );
  37. for ( i = 0 ; i < 3 ; i++ ) {
  38. VectorMA( entity->origin, lerped.origin[i], parent->axis[i], entity->origin );
  39. }
  40. // had to cast away the const to avoid compiler problems...
  41. MatrixMultiply( lerped.axis, ((refEntity_t *)parent)->axis, entity->axis );
  42. entity->backlerp = parent->backlerp;
  43. }
  44. /*
  45. ======================
  46. CG_PositionRotatedEntityOnTag
  47. Modifies the entities position and axis by the given
  48. tag location
  49. ======================
  50. */
  51. void CG_PositionRotatedEntityOnTag( refEntity_t *entity, const refEntity_t *parent,
  52. qhandle_t parentModel, char *tagName ) {
  53. int i;
  54. orientation_t lerped;
  55. vec3_t tempAxis[3];
  56. //AxisClear( entity->axis );
  57. // lerp the tag
  58. trap_R_LerpTag( &lerped, parentModel, parent->oldframe, parent->frame,
  59. 1.0 - parent->backlerp, tagName );
  60. // FIXME: allow origin offsets along tag?
  61. VectorCopy( parent->origin, entity->origin );
  62. for ( i = 0 ; i < 3 ; i++ ) {
  63. VectorMA( entity->origin, lerped.origin[i], parent->axis[i], entity->origin );
  64. }
  65. // had to cast away the const to avoid compiler problems...
  66. MatrixMultiply( entity->axis, lerped.axis, tempAxis );
  67. MatrixMultiply( tempAxis, ((refEntity_t *)parent)->axis, entity->axis );
  68. }
  69. /*
  70. ==========================================================================
  71. FUNCTIONS CALLED EACH FRAME
  72. ==========================================================================
  73. */
  74. /*
  75. ======================
  76. CG_SetEntitySoundPosition
  77. Also called by event processing code
  78. ======================
  79. */
  80. void CG_SetEntitySoundPosition( centity_t *cent ) {
  81. if ( cent->currentState.solid == SOLID_BMODEL ) {
  82. vec3_t origin;
  83. float *v;
  84. v = cgs.inlineModelMidpoints[ cent->currentState.modelindex ];
  85. VectorAdd( cent->lerpOrigin, v, origin );
  86. trap_S_UpdateEntityPosition( cent->currentState.number, origin );
  87. } else {
  88. trap_S_UpdateEntityPosition( cent->currentState.number, cent->lerpOrigin );
  89. }
  90. }
  91. /*
  92. ==================
  93. CG_EntityEffects
  94. Add continuous entity effects, like local entity emission and lighting
  95. ==================
  96. */
  97. static void CG_EntityEffects( centity_t *cent ) {
  98. // update sound origins
  99. CG_SetEntitySoundPosition( cent );
  100. // add loop sound
  101. if ( cent->currentState.loopSound ) {
  102. if (cent->currentState.eType != ET_SPEAKER) {
  103. trap_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin,
  104. cgs.gameSounds[ cent->currentState.loopSound ] );
  105. } else {
  106. trap_S_AddRealLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin,
  107. cgs.gameSounds[ cent->currentState.loopSound ] );
  108. }
  109. }
  110. // constant light glow
  111. if ( cent->currentState.constantLight ) {
  112. int cl;
  113. int i, r, g, b;
  114. cl = cent->currentState.constantLight;
  115. r = cl & 255;
  116. g = ( cl >> 8 ) & 255;
  117. b = ( cl >> 16 ) & 255;
  118. i = ( ( cl >> 24 ) & 255 ) * 4;
  119. trap_R_AddLightToScene( cent->lerpOrigin, i, r, g, b );
  120. }
  121. }
  122. /*
  123. ==================
  124. CG_General
  125. ==================
  126. */
  127. static void CG_General( centity_t *cent ) {
  128. refEntity_t ent;
  129. entityState_t *s1;
  130. s1 = &cent->currentState;
  131. // if set to invisible, skip
  132. if (!s1->modelindex) {
  133. return;
  134. }
  135. memset (&ent, 0, sizeof(ent));
  136. // set frame
  137. ent.frame = s1->frame;
  138. ent.oldframe = ent.frame;
  139. ent.backlerp = 0;
  140. VectorCopy( cent->lerpOrigin, ent.origin);
  141. VectorCopy( cent->lerpOrigin, ent.oldorigin);
  142. ent.hModel = cgs.gameModels[s1->modelindex];
  143. // player model
  144. if (s1->number == cg.snap->ps.clientNum) {
  145. ent.renderfx |= RF_THIRD_PERSON; // only draw from mirrors
  146. }
  147. // convert angles to axis
  148. AnglesToAxis( cent->lerpAngles, ent.axis );
  149. // add to refresh list
  150. trap_R_AddRefEntityToScene (&ent);
  151. }
  152. /*
  153. ==================
  154. CG_Speaker
  155. Speaker entities can automatically play sounds
  156. ==================
  157. */
  158. static void CG_Speaker( centity_t *cent ) {
  159. if ( ! cent->currentState.clientNum ) { // FIXME: use something other than clientNum...
  160. return; // not auto triggering
  161. }
  162. if ( cg.time < cent->miscTime ) {
  163. return;
  164. }
  165. trap_S_StartSound (NULL, cent->currentState.number, CHAN_ITEM, cgs.gameSounds[cent->currentState.eventParm] );
  166. // ent->s.frame = ent->wait * 10;
  167. // ent->s.clientNum = ent->random * 10;
  168. cent->miscTime = cg.time + cent->currentState.frame * 100 + cent->currentState.clientNum * 100 * crandom();
  169. }
  170. /*
  171. ==================
  172. CG_Item
  173. ==================
  174. */
  175. static void CG_Item( centity_t *cent ) {
  176. refEntity_t ent;
  177. entityState_t *es;
  178. gitem_t *item;
  179. int msec;
  180. float frac;
  181. float scale;
  182. weaponInfo_t *wi;
  183. es = &cent->currentState;
  184. if ( es->modelindex >= bg_numItems ) {
  185. CG_Error( "Bad item index %i on entity", es->modelindex );
  186. }
  187. // if set to invisible, skip
  188. if ( !es->modelindex || ( es->eFlags & EF_NODRAW ) ) {
  189. return;
  190. }
  191. item = &bg_itemlist[ es->modelindex ];
  192. if ( cg_simpleItems.integer && item->giType != IT_TEAM ) {
  193. memset( &ent, 0, sizeof( ent ) );
  194. ent.reType = RT_SPRITE;
  195. VectorCopy( cent->lerpOrigin, ent.origin );
  196. ent.radius = 14;
  197. ent.customShader = cg_items[es->modelindex].icon;
  198. ent.shaderRGBA[0] = 255;
  199. ent.shaderRGBA[1] = 255;
  200. ent.shaderRGBA[2] = 255;
  201. ent.shaderRGBA[3] = 255;
  202. trap_R_AddRefEntityToScene(&ent);
  203. return;
  204. }
  205. // items bob up and down continuously
  206. scale = 0.005 + cent->currentState.number * 0.00001;
  207. cent->lerpOrigin[2] += 4 + cos( ( cg.time + 1000 ) * scale ) * 4;
  208. memset (&ent, 0, sizeof(ent));
  209. // autorotate at one of two speeds
  210. if ( item->giType == IT_HEALTH ) {
  211. VectorCopy( cg.autoAnglesFast, cent->lerpAngles );
  212. AxisCopy( cg.autoAxisFast, ent.axis );
  213. } else {
  214. VectorCopy( cg.autoAngles, cent->lerpAngles );
  215. AxisCopy( cg.autoAxis, ent.axis );
  216. }
  217. wi = NULL;
  218. // the weapons have their origin where they attatch to player
  219. // models, so we need to offset them or they will rotate
  220. // eccentricly
  221. if ( item->giType == IT_WEAPON ) {
  222. wi = &cg_weapons[item->giTag];
  223. cent->lerpOrigin[0] -=
  224. wi->weaponMidpoint[0] * ent.axis[0][0] +
  225. wi->weaponMidpoint[1] * ent.axis[1][0] +
  226. wi->weaponMidpoint[2] * ent.axis[2][0];
  227. cent->lerpOrigin[1] -=
  228. wi->weaponMidpoint[0] * ent.axis[0][1] +
  229. wi->weaponMidpoint[1] * ent.axis[1][1] +
  230. wi->weaponMidpoint[2] * ent.axis[2][1];
  231. cent->lerpOrigin[2] -=
  232. wi->weaponMidpoint[0] * ent.axis[0][2] +
  233. wi->weaponMidpoint[1] * ent.axis[1][2] +
  234. wi->weaponMidpoint[2] * ent.axis[2][2];
  235. cent->lerpOrigin[2] += 8; // an extra height boost
  236. }
  237. ent.hModel = cg_items[es->modelindex].models[0];
  238. VectorCopy( cent->lerpOrigin, ent.origin);
  239. VectorCopy( cent->lerpOrigin, ent.oldorigin);
  240. ent.nonNormalizedAxes = qfalse;
  241. // if just respawned, slowly scale up
  242. msec = cg.time - cent->miscTime;
  243. if ( msec >= 0 && msec < ITEM_SCALEUP_TIME ) {
  244. frac = (float)msec / ITEM_SCALEUP_TIME;
  245. VectorScale( ent.axis[0], frac, ent.axis[0] );
  246. VectorScale( ent.axis[1], frac, ent.axis[1] );
  247. VectorScale( ent.axis[2], frac, ent.axis[2] );
  248. ent.nonNormalizedAxes = qtrue;
  249. } else {
  250. frac = 1.0;
  251. }
  252. // items without glow textures need to keep a minimum light value
  253. // so they are always visible
  254. if ( ( item->giType == IT_WEAPON ) ||
  255. ( item->giType == IT_ARMOR ) ) {
  256. ent.renderfx |= RF_MINLIGHT;
  257. }
  258. // increase the size of the weapons when they are presented as items
  259. if ( item->giType == IT_WEAPON ) {
  260. VectorScale( ent.axis[0], 1.5, ent.axis[0] );
  261. VectorScale( ent.axis[1], 1.5, ent.axis[1] );
  262. VectorScale( ent.axis[2], 1.5, ent.axis[2] );
  263. ent.nonNormalizedAxes = qtrue;
  264. #ifdef MISSIONPACK
  265. trap_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin, cgs.media.weaponHoverSound );
  266. #endif
  267. }
  268. #ifdef MISSIONPACK
  269. if ( item->giType == IT_HOLDABLE && item->giTag == HI_KAMIKAZE ) {
  270. VectorScale( ent.axis[0], 2, ent.axis[0] );
  271. VectorScale( ent.axis[1], 2, ent.axis[1] );
  272. VectorScale( ent.axis[2], 2, ent.axis[2] );
  273. ent.nonNormalizedAxes = qtrue;
  274. }
  275. #endif
  276. // add to refresh list
  277. trap_R_AddRefEntityToScene(&ent);
  278. #ifdef MISSIONPACK
  279. if ( item->giType == IT_WEAPON && wi->barrelModel ) {
  280. refEntity_t barrel;
  281. memset( &barrel, 0, sizeof( barrel ) );
  282. barrel.hModel = wi->barrelModel;
  283. VectorCopy( ent.lightingOrigin, barrel.lightingOrigin );
  284. barrel.shadowPlane = ent.shadowPlane;
  285. barrel.renderfx = ent.renderfx;
  286. CG_PositionRotatedEntityOnTag( &barrel, &ent, wi->weaponModel, "tag_barrel" );
  287. AxisCopy( ent.axis, barrel.axis );
  288. barrel.nonNormalizedAxes = ent.nonNormalizedAxes;
  289. trap_R_AddRefEntityToScene( &barrel );
  290. }
  291. #endif
  292. // accompanying rings / spheres for powerups
  293. if ( !cg_simpleItems.integer )
  294. {
  295. vec3_t spinAngles;
  296. VectorClear( spinAngles );
  297. if ( item->giType == IT_HEALTH || item->giType == IT_POWERUP )
  298. {
  299. if ( ( ent.hModel = cg_items[es->modelindex].models[1] ) != 0 )
  300. {
  301. if ( item->giType == IT_POWERUP )
  302. {
  303. ent.origin[2] += 12;
  304. spinAngles[1] = ( cg.time & 1023 ) * 360 / -1024.0f;
  305. }
  306. AnglesToAxis( spinAngles, ent.axis );
  307. // scale up if respawning
  308. if ( frac != 1.0 ) {
  309. VectorScale( ent.axis[0], frac, ent.axis[0] );
  310. VectorScale( ent.axis[1], frac, ent.axis[1] );
  311. VectorScale( ent.axis[2], frac, ent.axis[2] );
  312. ent.nonNormalizedAxes = qtrue;
  313. }
  314. trap_R_AddRefEntityToScene( &ent );
  315. }
  316. }
  317. }
  318. }
  319. //============================================================================
  320. /*
  321. ===============
  322. CG_Missile
  323. ===============
  324. */
  325. static void CG_Missile( centity_t *cent ) {
  326. refEntity_t ent;
  327. entityState_t *s1;
  328. const weaponInfo_t *weapon;
  329. // int col;
  330. s1 = &cent->currentState;
  331. if ( s1->weapon > WP_NUM_WEAPONS ) {
  332. s1->weapon = 0;
  333. }
  334. weapon = &cg_weapons[s1->weapon];
  335. // calculate the axis
  336. VectorCopy( s1->angles, cent->lerpAngles);
  337. // add trails
  338. if ( weapon->missileTrailFunc )
  339. {
  340. weapon->missileTrailFunc( cent, weapon );
  341. }
  342. /*
  343. if ( cent->currentState.modelindex == TEAM_RED ) {
  344. col = 1;
  345. }
  346. else if ( cent->currentState.modelindex == TEAM_BLUE ) {
  347. col = 2;
  348. }
  349. else {
  350. col = 0;
  351. }
  352. // add dynamic light
  353. if ( weapon->missileDlight ) {
  354. trap_R_AddLightToScene(cent->lerpOrigin, weapon->missileDlight,
  355. weapon->missileDlightColor[col][0], weapon->missileDlightColor[col][1], weapon->missileDlightColor[col][2] );
  356. }
  357. */
  358. // add dynamic light
  359. if ( weapon->missileDlight ) {
  360. trap_R_AddLightToScene(cent->lerpOrigin, weapon->missileDlight,
  361. weapon->missileDlightColor[0], weapon->missileDlightColor[1], weapon->missileDlightColor[2] );
  362. }
  363. // add missile sound
  364. if ( weapon->missileSound ) {
  365. vec3_t velocity;
  366. BG_EvaluateTrajectoryDelta( &cent->currentState.pos, cg.time, velocity );
  367. trap_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, velocity, weapon->missileSound );
  368. }
  369. // create the render entity
  370. memset (&ent, 0, sizeof(ent));
  371. VectorCopy( cent->lerpOrigin, ent.origin);
  372. VectorCopy( cent->lerpOrigin, ent.oldorigin);
  373. if ( cent->currentState.weapon == WP_PLASMAGUN ) {
  374. ent.reType = RT_SPRITE;
  375. ent.radius = 16;
  376. ent.rotation = 0;
  377. ent.customShader = cgs.media.plasmaBallShader;
  378. trap_R_AddRefEntityToScene( &ent );
  379. return;
  380. }
  381. // flicker between two skins
  382. ent.skinNum = cg.clientFrame & 1;
  383. ent.hModel = weapon->missileModel;
  384. ent.renderfx = weapon->missileRenderfx | RF_NOSHADOW;
  385. #ifdef MISSIONPACK
  386. if ( cent->currentState.weapon == WP_PROX_LAUNCHER ) {
  387. if (s1->generic1 == TEAM_BLUE) {
  388. ent.hModel = cgs.media.blueProxMine;
  389. }
  390. }
  391. #endif
  392. // convert direction of travel into axis
  393. if ( VectorNormalize2( s1->pos.trDelta, ent.axis[0] ) == 0 ) {
  394. ent.axis[0][2] = 1;
  395. }
  396. // spin as it moves
  397. if ( s1->pos.trType != TR_STATIONARY ) {
  398. RotateAroundDirection( ent.axis, cg.time / 4 );
  399. } else {
  400. #ifdef MISSIONPACK
  401. if ( s1->weapon == WP_PROX_LAUNCHER ) {
  402. AnglesToAxis( cent->lerpAngles, ent.axis );
  403. }
  404. else
  405. #endif
  406. {
  407. RotateAroundDirection( ent.axis, s1->time );
  408. }
  409. }
  410. // add to refresh list, possibly with quad glow
  411. CG_AddRefEntityWithPowerups( &ent, s1, TEAM_FREE );
  412. }
  413. /*
  414. ===============
  415. CG_Grapple
  416. This is called when the grapple is sitting up against the wall
  417. ===============
  418. */
  419. static void CG_Grapple( centity_t *cent ) {
  420. refEntity_t ent;
  421. entityState_t *s1;
  422. const weaponInfo_t *weapon;
  423. s1 = &cent->currentState;
  424. if ( s1->weapon > WP_NUM_WEAPONS ) {
  425. s1->weapon = 0;
  426. }
  427. weapon = &cg_weapons[s1->weapon];
  428. // calculate the axis
  429. VectorCopy( s1->angles, cent->lerpAngles);
  430. #if 0 // FIXME add grapple pull sound here..?
  431. // add missile sound
  432. if ( weapon->missileSound ) {
  433. trap_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin, weapon->missileSound );
  434. }
  435. #endif
  436. // Will draw cable if needed
  437. CG_GrappleTrail ( cent, weapon );
  438. // create the render entity
  439. memset (&ent, 0, sizeof(ent));
  440. VectorCopy( cent->lerpOrigin, ent.origin);
  441. VectorCopy( cent->lerpOrigin, ent.oldorigin);
  442. // flicker between two skins
  443. ent.skinNum = cg.clientFrame & 1;
  444. ent.hModel = weapon->missileModel;
  445. ent.renderfx = weapon->missileRenderfx | RF_NOSHADOW;
  446. // convert direction of travel into axis
  447. if ( VectorNormalize2( s1->pos.trDelta, ent.axis[0] ) == 0 ) {
  448. ent.axis[0][2] = 1;
  449. }
  450. trap_R_AddRefEntityToScene( &ent );
  451. }
  452. /*
  453. ===============
  454. CG_Mover
  455. ===============
  456. */
  457. static void CG_Mover( centity_t *cent ) {
  458. refEntity_t ent;
  459. entityState_t *s1;
  460. s1 = &cent->currentState;
  461. // create the render entity
  462. memset (&ent, 0, sizeof(ent));
  463. VectorCopy( cent->lerpOrigin, ent.origin);
  464. VectorCopy( cent->lerpOrigin, ent.oldorigin);
  465. AnglesToAxis( cent->lerpAngles, ent.axis );
  466. ent.renderfx = RF_NOSHADOW;
  467. // flicker between two skins (FIXME?)
  468. ent.skinNum = ( cg.time >> 6 ) & 1;
  469. // get the model, either as a bmodel or a modelindex
  470. if ( s1->solid == SOLID_BMODEL ) {
  471. ent.hModel = cgs.inlineDrawModel[s1->modelindex];
  472. } else {
  473. ent.hModel = cgs.gameModels[s1->modelindex];
  474. }
  475. // add to refresh list
  476. trap_R_AddRefEntityToScene(&ent);
  477. // add the secondary model
  478. if ( s1->modelindex2 ) {
  479. ent.skinNum = 0;
  480. ent.hModel = cgs.gameModels[s1->modelindex2];
  481. trap_R_AddRefEntityToScene(&ent);
  482. }
  483. }
  484. /*
  485. ===============
  486. CG_Beam
  487. Also called as an event
  488. ===============
  489. */
  490. void CG_Beam( centity_t *cent ) {
  491. refEntity_t ent;
  492. entityState_t *s1;
  493. s1 = &cent->currentState;
  494. // create the render entity
  495. memset (&ent, 0, sizeof(ent));
  496. VectorCopy( s1->pos.trBase, ent.origin );
  497. VectorCopy( s1->origin2, ent.oldorigin );
  498. AxisClear( ent.axis );
  499. ent.reType = RT_BEAM;
  500. ent.renderfx = RF_NOSHADOW;
  501. // add to refresh list
  502. trap_R_AddRefEntityToScene(&ent);
  503. }
  504. /*
  505. ===============
  506. CG_Portal
  507. ===============
  508. */
  509. static void CG_Portal( centity_t *cent ) {
  510. refEntity_t ent;
  511. entityState_t *s1;
  512. s1 = &cent->currentState;
  513. // create the render entity
  514. memset (&ent, 0, sizeof(ent));
  515. VectorCopy( cent->lerpOrigin, ent.origin );
  516. VectorCopy( s1->origin2, ent.oldorigin );
  517. ByteToDir( s1->eventParm, ent.axis[0] );
  518. PerpendicularVector( ent.axis[1], ent.axis[0] );
  519. // negating this tends to get the directions like they want
  520. // we really should have a camera roll value
  521. VectorSubtract( vec3_origin, ent.axis[1], ent.axis[1] );
  522. CrossProduct( ent.axis[0], ent.axis[1], ent.axis[2] );
  523. ent.reType = RT_PORTALSURFACE;
  524. ent.oldframe = s1->powerups;
  525. ent.frame = s1->frame; // rotation speed
  526. ent.skinNum = s1->clientNum/256.0 * 360; // roll offset
  527. // add to refresh list
  528. trap_R_AddRefEntityToScene(&ent);
  529. }
  530. /*
  531. =========================
  532. CG_AdjustPositionForMover
  533. Also called by client movement prediction code
  534. =========================
  535. */
  536. void CG_AdjustPositionForMover( const vec3_t in, int moverNum, int fromTime, int toTime, vec3_t out ) {
  537. centity_t *cent;
  538. vec3_t oldOrigin, origin, deltaOrigin;
  539. vec3_t oldAngles, angles, deltaAngles;
  540. if ( moverNum <= 0 || moverNum >= ENTITYNUM_MAX_NORMAL ) {
  541. VectorCopy( in, out );
  542. return;
  543. }
  544. cent = &cg_entities[ moverNum ];
  545. if ( cent->currentState.eType != ET_MOVER ) {
  546. VectorCopy( in, out );
  547. return;
  548. }
  549. BG_EvaluateTrajectory( &cent->currentState.pos, fromTime, oldOrigin );
  550. BG_EvaluateTrajectory( &cent->currentState.apos, fromTime, oldAngles );
  551. BG_EvaluateTrajectory( &cent->currentState.pos, toTime, origin );
  552. BG_EvaluateTrajectory( &cent->currentState.apos, toTime, angles );
  553. VectorSubtract( origin, oldOrigin, deltaOrigin );
  554. VectorSubtract( angles, oldAngles, deltaAngles );
  555. VectorAdd( in, deltaOrigin, out );
  556. // FIXME: origin change when on a rotating object
  557. }
  558. /*
  559. =============================
  560. CG_InterpolateEntityPosition
  561. =============================
  562. */
  563. static void CG_InterpolateEntityPosition( centity_t *cent ) {
  564. vec3_t current, next;
  565. float f;
  566. // it would be an internal error to find an entity that interpolates without
  567. // a snapshot ahead of the current one
  568. if ( cg.nextSnap == NULL ) {
  569. CG_Error( "CG_InterpoateEntityPosition: cg.nextSnap == NULL" );
  570. }
  571. f = cg.frameInterpolation;
  572. // this will linearize a sine or parabolic curve, but it is important
  573. // to not extrapolate player positions if more recent data is available
  574. BG_EvaluateTrajectory( &cent->currentState.pos, cg.snap->serverTime, current );
  575. BG_EvaluateTrajectory( &cent->nextState.pos, cg.nextSnap->serverTime, next );
  576. cent->lerpOrigin[0] = current[0] + f * ( next[0] - current[0] );
  577. cent->lerpOrigin[1] = current[1] + f * ( next[1] - current[1] );
  578. cent->lerpOrigin[2] = current[2] + f * ( next[2] - current[2] );
  579. BG_EvaluateTrajectory( &cent->currentState.apos, cg.snap->serverTime, current );
  580. BG_EvaluateTrajectory( &cent->nextState.apos, cg.nextSnap->serverTime, next );
  581. cent->lerpAngles[0] = LerpAngle( current[0], next[0], f );
  582. cent->lerpAngles[1] = LerpAngle( current[1], next[1], f );
  583. cent->lerpAngles[2] = LerpAngle( current[2], next[2], f );
  584. }
  585. /*
  586. ===============
  587. CG_CalcEntityLerpPositions
  588. ===============
  589. */
  590. static void CG_CalcEntityLerpPositions( centity_t *cent ) {
  591. // if this player does not want to see extrapolated players
  592. if ( !cg_smoothClients.integer ) {
  593. // make sure the clients use TR_INTERPOLATE
  594. if ( cent->currentState.number < MAX_CLIENTS ) {
  595. cent->currentState.pos.trType = TR_INTERPOLATE;
  596. cent->nextState.pos.trType = TR_INTERPOLATE;
  597. }
  598. }
  599. if ( cent->interpolate && cent->currentState.pos.trType == TR_INTERPOLATE ) {
  600. CG_InterpolateEntityPosition( cent );
  601. return;
  602. }
  603. // first see if we can interpolate between two snaps for
  604. // linear extrapolated clients
  605. if ( cent->interpolate && cent->currentState.pos.trType == TR_LINEAR_STOP &&
  606. cent->currentState.number < MAX_CLIENTS) {
  607. CG_InterpolateEntityPosition( cent );
  608. return;
  609. }
  610. // just use the current frame and evaluate as best we can
  611. BG_EvaluateTrajectory( &cent->currentState.pos, cg.time, cent->lerpOrigin );
  612. BG_EvaluateTrajectory( &cent->currentState.apos, cg.time, cent->lerpAngles );
  613. // adjust for riding a mover if it wasn't rolled into the predicted
  614. // player state
  615. if ( cent != &cg.predictedPlayerEntity ) {
  616. CG_AdjustPositionForMover( cent->lerpOrigin, cent->currentState.groundEntityNum,
  617. cg.snap->serverTime, cg.time, cent->lerpOrigin );
  618. }
  619. }
  620. /*
  621. ===============
  622. CG_TeamBase
  623. ===============
  624. */
  625. static void CG_TeamBase( centity_t *cent ) {
  626. refEntity_t model;
  627. #ifdef MISSIONPACK
  628. vec3_t angles;
  629. int t, h;
  630. float c;
  631. if ( cgs.gametype == GT_CTF || cgs.gametype == GT_1FCTF ) {
  632. #else
  633. if ( cgs.gametype == GT_CTF) {
  634. #endif
  635. // show the flag base
  636. memset(&model, 0, sizeof(model));
  637. model.reType = RT_MODEL;
  638. VectorCopy( cent->lerpOrigin, model.lightingOrigin );
  639. VectorCopy( cent->lerpOrigin, model.origin );
  640. AnglesToAxis( cent->currentState.angles, model.axis );
  641. if ( cent->currentState.modelindex == TEAM_RED ) {
  642. model.hModel = cgs.media.redFlagBaseModel;
  643. }
  644. else if ( cent->currentState.modelindex == TEAM_BLUE ) {
  645. model.hModel = cgs.media.blueFlagBaseModel;
  646. }
  647. else {
  648. model.hModel = cgs.media.neutralFlagBaseModel;
  649. }
  650. trap_R_AddRefEntityToScene( &model );
  651. }
  652. #ifdef MISSIONPACK
  653. else if ( cgs.gametype == GT_OBELISK ) {
  654. // show the obelisk
  655. memset(&model, 0, sizeof(model));
  656. model.reType = RT_MODEL;
  657. VectorCopy( cent->lerpOrigin, model.lightingOrigin );
  658. VectorCopy( cent->lerpOrigin, model.origin );
  659. AnglesToAxis( cent->currentState.angles, model.axis );
  660. model.hModel = cgs.media.overloadBaseModel;
  661. trap_R_AddRefEntityToScene( &model );
  662. // if hit
  663. if ( cent->currentState.frame == 1) {
  664. // show hit model
  665. // modelindex2 is the health value of the obelisk
  666. c = cent->currentState.modelindex2;
  667. model.shaderRGBA[0] = 0xff;
  668. model.shaderRGBA[1] = c;
  669. model.shaderRGBA[2] = c;
  670. model.shaderRGBA[3] = 0xff;
  671. //
  672. model.hModel = cgs.media.overloadEnergyModel;
  673. trap_R_AddRefEntityToScene( &model );
  674. }
  675. // if respawning
  676. if ( cent->currentState.frame == 2) {
  677. if ( !cent->miscTime ) {
  678. cent->miscTime = cg.time;
  679. }
  680. t = cg.time - cent->miscTime;
  681. h = (cg_obeliskRespawnDelay.integer - 5) * 1000;
  682. //
  683. if (t > h) {
  684. c = (float) (t - h) / h;
  685. if (c > 1)
  686. c = 1;
  687. }
  688. else {
  689. c = 0;
  690. }
  691. // show the lights
  692. AnglesToAxis( cent->currentState.angles, model.axis );
  693. //
  694. model.shaderRGBA[0] = c * 0xff;
  695. model.shaderRGBA[1] = c * 0xff;
  696. model.shaderRGBA[2] = c * 0xff;
  697. model.shaderRGBA[3] = c * 0xff;
  698. model.hModel = cgs.media.overloadLightsModel;
  699. trap_R_AddRefEntityToScene( &model );
  700. // show the target
  701. if (t > h) {
  702. if ( !cent->muzzleFlashTime ) {
  703. trap_S_StartSound (cent->lerpOrigin, ENTITYNUM_NONE, CHAN_BODY, cgs.media.obeliskRespawnSound);
  704. cent->muzzleFlashTime = 1;
  705. }
  706. VectorCopy(cent->currentState.angles, angles);
  707. angles[YAW] += (float) 16 * acos(1-c) * 180 / M_PI;
  708. AnglesToAxis( angles, model.axis );
  709. VectorScale( model.axis[0], c, model.axis[0]);
  710. VectorScale( model.axis[1], c, model.axis[1]);
  711. VectorScale( model.axis[2], c, model.axis[2]);
  712. model.shaderRGBA[0] = 0xff;
  713. model.shaderRGBA[1] = 0xff;
  714. model.shaderRGBA[2] = 0xff;
  715. model.shaderRGBA[3] = 0xff;
  716. //
  717. model.origin[2] += 56;
  718. model.hModel = cgs.media.overloadTargetModel;
  719. trap_R_AddRefEntityToScene( &model );
  720. }
  721. else {
  722. //FIXME: show animated smoke
  723. }
  724. }
  725. else {
  726. cent->miscTime = 0;
  727. cent->muzzleFlashTime = 0;
  728. // modelindex2 is the health value of the obelisk
  729. c = cent->currentState.modelindex2;
  730. model.shaderRGBA[0] = 0xff;
  731. model.shaderRGBA[1] = c;
  732. model.shaderRGBA[2] = c;
  733. model.shaderRGBA[3] = 0xff;
  734. // show the lights
  735. model.hModel = cgs.media.overloadLightsModel;
  736. trap_R_AddRefEntityToScene( &model );
  737. // show the target
  738. model.origin[2] += 56;
  739. model.hModel = cgs.media.overloadTargetModel;
  740. trap_R_AddRefEntityToScene( &model );
  741. }
  742. }
  743. else if ( cgs.gametype == GT_HARVESTER ) {
  744. // show harvester model
  745. memset(&model, 0, sizeof(model));
  746. model.reType = RT_MODEL;
  747. VectorCopy( cent->lerpOrigin, model.lightingOrigin );
  748. VectorCopy( cent->lerpOrigin, model.origin );
  749. AnglesToAxis( cent->currentState.angles, model.axis );
  750. if ( cent->currentState.modelindex == TEAM_RED ) {
  751. model.hModel = cgs.media.harvesterModel;
  752. model.customSkin = cgs.media.harvesterRedSkin;
  753. }
  754. else if ( cent->currentState.modelindex == TEAM_BLUE ) {
  755. model.hModel = cgs.media.harvesterModel;
  756. model.customSkin = cgs.media.harvesterBlueSkin;
  757. }
  758. else {
  759. model.hModel = cgs.media.harvesterNeutralModel;
  760. model.customSkin = 0;
  761. }
  762. trap_R_AddRefEntityToScene( &model );
  763. }
  764. #endif
  765. }
  766. /*
  767. ===============
  768. CG_AddCEntity
  769. ===============
  770. */
  771. static void CG_AddCEntity( centity_t *cent ) {
  772. // event-only entities will have been dealt with already
  773. if ( cent->currentState.eType >= ET_EVENTS ) {
  774. return;
  775. }
  776. // calculate the current origin
  777. CG_CalcEntityLerpPositions( cent );
  778. // add automatic effects
  779. CG_EntityEffects( cent );
  780. switch ( cent->currentState.eType ) {
  781. default:
  782. CG_Error( "Bad entity type: %i\n", cent->currentState.eType );
  783. break;
  784. case ET_INVISIBLE:
  785. case ET_PUSH_TRIGGER:
  786. case ET_TELEPORT_TRIGGER:
  787. break;
  788. case ET_GENERAL:
  789. CG_General( cent );
  790. break;
  791. case ET_PLAYER:
  792. CG_Player( cent );
  793. break;
  794. case ET_ITEM:
  795. CG_Item( cent );
  796. break;
  797. case ET_MISSILE:
  798. CG_Missile( cent );
  799. break;
  800. case ET_MOVER:
  801. CG_Mover( cent );
  802. break;
  803. case ET_BEAM:
  804. CG_Beam( cent );
  805. break;
  806. case ET_PORTAL:
  807. CG_Portal( cent );
  808. break;
  809. case ET_SPEAKER:
  810. CG_Speaker( cent );
  811. break;
  812. case ET_GRAPPLE:
  813. CG_Grapple( cent );
  814. break;
  815. case ET_TEAM:
  816. CG_TeamBase( cent );
  817. break;
  818. }
  819. }
  820. /*
  821. ===============
  822. CG_AddPacketEntities
  823. ===============
  824. */
  825. void CG_AddPacketEntities( void ) {
  826. int num;
  827. centity_t *cent;
  828. playerState_t *ps;
  829. // set cg.frameInterpolation
  830. if ( cg.nextSnap ) {
  831. int delta;
  832. delta = (cg.nextSnap->serverTime - cg.snap->serverTime);
  833. if ( delta == 0 ) {
  834. cg.frameInterpolation = 0;
  835. } else {
  836. cg.frameInterpolation = (float)( cg.time - cg.snap->serverTime ) / delta;
  837. }
  838. } else {
  839. cg.frameInterpolation = 0; // actually, it should never be used, because
  840. // no entities should be marked as interpolating
  841. }
  842. // the auto-rotating items will all have the same axis
  843. cg.autoAngles[0] = 0;
  844. cg.autoAngles[1] = ( cg.time & 2047 ) * 360 / 2048.0;
  845. cg.autoAngles[2] = 0;
  846. cg.autoAnglesFast[0] = 0;
  847. cg.autoAnglesFast[1] = ( cg.time & 1023 ) * 360 / 1024.0f;
  848. cg.autoAnglesFast[2] = 0;
  849. AnglesToAxis( cg.autoAngles, cg.autoAxis );
  850. AnglesToAxis( cg.autoAnglesFast, cg.autoAxisFast );
  851. // generate and add the entity from the playerstate
  852. ps = &cg.predictedPlayerState;
  853. BG_PlayerStateToEntityState( ps, &cg.predictedPlayerEntity.currentState, qfalse );
  854. CG_AddCEntity( &cg.predictedPlayerEntity );
  855. // lerp the non-predicted value for lightning gun origins
  856. CG_CalcEntityLerpPositions( &cg_entities[ cg.snap->ps.clientNum ] );
  857. // add each entity sent over by the server
  858. for ( num = 0 ; num < cg.snap->numEntities ; num++ ) {
  859. cent = &cg_entities[ cg.snap->entities[ num ].number ];
  860. CG_AddCEntity( cent );
  861. }
  862. }