1 /**
2 * @file
3 */
4
5 /*
6 Copyright (C) 1997-2001 Id Software, Inc.
7
8 This program is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License
10 as published by the Free Software Foundation; either version 2
11 of the License, or (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16
17 See the GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22
23 */
24
25 #include "r_local.h"
26 #include "r_matrix.h"
27 #include "r_entity.h"
28 #include "r_mesh.h"
29 #include "r_mesh_anim.h"
30 #include "r_draw.h"
31 #include "r_matrix.h"
32
33 #define MAX_ENTITIES 2048*2
34
35 static entity_t r_entities[MAX_ENTITIES];
36
37 entity_t* r_opaque_mesh_entities;
38 entity_t* r_blend_mesh_entities;
39 entity_t* r_null_entities;
40 entity_t* r_special_entities;
41
42 /**
43 * @brief setter for entity origin
44 * @param[out] ent The entity to set the origin for
45 * @param[in] origin The new origin for the given entity
46 */
R_EntitySetOrigin(entity_t * ent,const vec3_t origin)47 void R_EntitySetOrigin (entity_t* ent, const vec3_t origin)
48 {
49 VectorCopy(origin, ent->origin);
50 }
51
52 /**
53 * @brief Translates the origin of the given entity by the given offset vector
54 * @param[in,out] ent The entity to translate
55 * @param[in] offset The translation vector
56 */
R_EntityAddToOrigin(entity_t * ent,const vec3_t offset)57 void R_EntityAddToOrigin (entity_t* ent, const vec3_t offset)
58 {
59 VectorAdd(ent->origin, offset, ent->origin);
60 }
61
62 /**
63 * @brief Draws the field marker entity is specified in CL_AddTargeting
64 * @sa CL_AddTargeting
65 * @sa RF_BOX
66 */
R_DrawBox(const entity_t * e)67 static void R_DrawBox (const entity_t* e)
68 {
69 const vec4_t color = {e->color[0], e->color[1], e->color[2], e->alpha};
70
71 if (e->texture) {
72 R_Color(color);
73 R_BindTexture(e->texture->texnum);
74 if (VectorNotEmpty(e->eBox.mins) && VectorNotEmpty(e->eBox.maxs)) {
75 R_DrawTexturedBox(e->eBox.mins, e->eBox.maxs);
76 } else {
77 R_DrawTexturedBox(e->oldorigin, e->origin);
78 }
79 R_Color(nullptr);
80 return;
81 }
82
83 glDisable(GL_TEXTURE_2D);
84
85 R_Color(color);
86
87 if (VectorNotEmpty(e->eBox.mins) && VectorNotEmpty(e->eBox.maxs)) {
88 R_DrawBoundingBox(e->eBox.mins, e->eBox.maxs);
89 } else {
90 vec3_t points[] = { { e->oldorigin[0], e->oldorigin[1], e->oldorigin[2] }, { e->oldorigin[0], e->origin[1],
91 e->oldorigin[2] }, { e->origin[0], e->origin[1], e->oldorigin[2] }, { e->origin[0], e->oldorigin[1],
92 e->oldorigin[2] } };
93
94 glLineWidth(2.0f);
95 R_BindArray(GL_VERTEX_ARRAY, GL_FLOAT, points);
96
97 /** @todo fill one array */
98 glDrawArrays(GL_LINE_LOOP, 0, 4);
99 refdef.batchCount++;
100 points[0][2] = e->origin[2];
101 points[1][2] = e->origin[2];
102 points[2][2] = e->origin[2];
103 points[3][2] = e->origin[2];
104 glDrawArrays(GL_LINE_LOOP, 0, 4);
105 refdef.batchCount++;
106 points[0][2] = e->oldorigin[2];
107 points[1][1] = e->oldorigin[1];
108 points[2][2] = e->oldorigin[2];
109 points[3][1] = e->origin[1];
110 glDrawArrays(GL_LINES, 0, 4);
111 refdef.batchCount++;
112 points[0][0] = e->origin[0];
113 points[1][0] = e->origin[0];
114 points[2][0] = e->oldorigin[0];
115 points[3][0] = e->oldorigin[0];
116 glDrawArrays(GL_LINES, 0, 4);
117 refdef.batchCount++;
118 R_BindDefaultArray(GL_VERTEX_ARRAY);
119 }
120 glEnable(GL_TEXTURE_2D);
121
122 R_Color(nullptr);
123 }
124
125
126 /**
127 * @brief Draws a marker on the ground to indicate pathing CL_AddPathingBox
128 * @sa CL_AddPathing
129 * @sa RF_BOX
130 */
R_DrawFloor(const entity_t * e)131 static void R_DrawFloor (const entity_t* e)
132 {
133 GLint oldDepthFunc;
134 glGetIntegerv(GL_DEPTH_FUNC, &oldDepthFunc);
135
136 image_t* cellIndicator = R_FindImage("pics/sfx/cell", it_pic);
137 const float dx = PLAYER_WIDTH * 2;
138 const vec4_t color = {e->color[0], e->color[1], e->color[2], e->alpha};
139 const float size = 4.0;
140 /** @todo use default_texcoords */
141 const vec2_t texcoords[] = { { 0.0, 1.0 }, { 1.0, 1.0 }, { 1.0, 0.0 }, { 0.0, 0.0 } };
142 const vec3_t points[] = { { e->origin[0] - size, e->origin[1] + dx + size, e->origin[2] }, { e->origin[0] + dx
143 + size, e->origin[1] + dx + size, e->origin[2] }, { e->origin[0] + dx + size, e->origin[1] - size,
144 e->origin[2] }, { e->origin[0] - size, e->origin[1] - size, e->origin[2] } };
145
146 /* Draw it twice, with and without depth check, so it will still be visible if obscured by a wall */
147 R_Color(color);
148 R_BindTexture(cellIndicator->texnum);
149
150 /* circle points */
151 R_BindArray(GL_TEXTURE_COORD_ARRAY, GL_FLOAT, texcoords);
152 R_BindArray(GL_VERTEX_ARRAY, GL_FLOAT, points);
153 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
154
155 glDepthFunc(GL_GREATER);
156 glColor4f(color[0], color[1], color[2], color[3] * 0.25f);
157
158 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
159
160 glDepthFunc(oldDepthFunc);
161
162 R_BindDefaultArray(GL_TEXTURE_COORD_ARRAY);
163 R_BindDefaultArray(GL_VERTEX_ARRAY);
164
165 refdef.batchCount += 2;
166
167 R_Color(nullptr);
168 }
169
170 /**
171 * @brief Draws an arrow between two points
172 * @sa CL_AddArrow
173 * @sa RF_BOX
174 */
R_DrawArrow(const entity_t * e)175 static void R_DrawArrow (const entity_t* e)
176 {
177 const vec3_t upper = { e->origin[0] + 2, e->origin[1], e->origin[2] };
178 const vec3_t mid = { e->origin[0], e->origin[1] + 2, e->origin[2] };
179 const vec3_t lower = { e->origin[0], e->origin[1], e->origin[2] + 2 };
180 const vec4_t color = { e->color[0], e->color[1], e->color[2], e->alpha };
181 const vec3_t points[] = { { e->oldorigin[0], e->oldorigin[1], e->oldorigin[2] }, { upper[0], upper[1], upper[2] },
182 { mid[0], mid[1], mid[2] }, { lower[0], lower[1], lower[2] } };
183
184 R_Color(color);
185
186 glDisable(GL_TEXTURE_2D);
187 glEnable(GL_LINE_SMOOTH);
188
189 R_BindArray(GL_VERTEX_ARRAY, GL_FLOAT, points);
190 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
191 R_BindDefaultArray(GL_VERTEX_ARRAY);
192
193 refdef.batchCount++;
194
195 glDisable(GL_LINE_SMOOTH);
196 glEnable(GL_TEXTURE_2D);
197
198 R_Color(nullptr);
199 }
200
201 static image_t* selectedActorIndicator;
202 static image_t* actorIndicator;
203
204 /**
205 * @brief Draws shadow and highlight effects for the entities (actors)
206 * @note The origins are already transformed
207 */
R_DrawEntityEffects(void)208 void R_DrawEntityEffects (void)
209 {
210 int i;
211 const int mask = r_stencilshadows->integer ? RF_BLOOD : (RF_SHADOW | RF_BLOOD);
212 GLint oldDepthFunc;
213 glGetIntegerv(GL_DEPTH_FUNC, &oldDepthFunc);
214
215 R_EnableBlend(true);
216
217 if (actorIndicator == nullptr) {
218 selectedActorIndicator = R_FindImage("pics/sfx/actor_selected", it_effect);
219 actorIndicator = R_FindImage("pics/sfx/actor", it_effect);
220 }
221
222 for (i = 0; i < refdef.numEntities; i++) {
223 const entity_t* e = &r_entities[i];
224
225 if (e->flags <= RF_BOX)
226 continue;
227
228 glPushMatrix();
229 glMultMatrixf(e->transform.matrix);
230
231 if (e->flags & mask) {
232 const vec3_t points[] = { { -18.0, 14.0, -28.5 }, { 10.0, 14.0, -28.5 }, { 10.0, -14.0, -28.5 }, { -18.0,
233 -14.0, -28.5 } };
234 /** @todo use default_texcoords */
235 const vec2_t texcoords[] = { { 0.0, 1.0 }, { 1.0, 1.0 }, { 1.0, 0.0 }, { 0.0, 0.0 } };
236
237 if (e->flags & RF_SHADOW) {
238 R_BindTexture(shadow->texnum);
239 } else {
240 assert(e->texture);
241 R_BindTexture(e->texture->texnum);
242 }
243
244 R_BindArray(GL_TEXTURE_COORD_ARRAY, GL_FLOAT, texcoords);
245 R_BindArray(GL_VERTEX_ARRAY, GL_FLOAT, points);
246 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
247 R_BindDefaultArray(GL_TEXTURE_COORD_ARRAY);
248 R_BindDefaultArray(GL_VERTEX_ARRAY);
249
250 refdef.batchCount++;
251 }
252
253 if (e->flags & RF_ACTOR) {
254 const float size = 15.0;
255 int texnum;
256 /* draw the circles for team-members and allied troops */
257 vec4_t color = {1, 1, 1, 1};
258 const vec3_t points[] = { { -size, size, -SELECTION_DELTA }, { size, size, -SELECTION_DELTA }, { size, -size,
259 -SELECTION_DELTA }, { -size, -size, -SELECTION_DELTA } };
260 /** @todo use default_texcoords */
261 const vec2_t texcoords[] = { { 0.0, 1.0 }, { 1.0, 1.0 }, { 1.0, 0.0 }, { 0.0, 0.0 } };
262
263 if (e->flags & RF_SELECTED)
264 Vector4Set(color, 0, 1, 0, 0.5);
265 else if (e->flags & RF_MEMBER)
266 Vector4Set(color, 0, 0.8, 0, 0.5);
267 else if (e->flags & RF_ALLIED)
268 Vector4Set(color, 0, 1, 0.5, 0.5);
269 else if (e->flags & RF_NEUTRAL)
270 Vector4Set(color, 1, 1, 0, 0.5);
271 else if (e->flags & RF_OPPONENT)
272 Vector4Set(color, 1, 0, 0, 0.5);
273 else
274 Vector4Set(color, 0.3, 0.3, 0.3, 0.5);
275
276 if (e->flags & RF_SELECTED)
277 texnum = selectedActorIndicator->texnum;
278 else
279 texnum = actorIndicator->texnum;
280
281 R_BindTexture(texnum);
282 R_Color(color);
283 R_EnableDrawAsGlow(true);
284
285 /* circle points */
286 R_BindArray(GL_TEXTURE_COORD_ARRAY, GL_FLOAT, texcoords);
287 R_BindArray(GL_VERTEX_ARRAY, GL_FLOAT, points);
288
289 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
290
291 refdef.batchCount++;
292
293 /* add transparency when something is in front of the circle */
294 color[3] *= 0.25;
295 R_Color(color);
296 glDepthFunc(GL_GREATER);
297 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
298 glDepthFunc(oldDepthFunc);
299
300 refdef.batchCount++;
301
302 R_BindDefaultArray(GL_TEXTURE_COORD_ARRAY);
303 R_BindDefaultArray(GL_VERTEX_ARRAY);
304
305 R_Color(nullptr);
306 R_EnableDrawAsGlow(false);
307 }
308 glPopMatrix();
309 }
310 }
311
312 /**
313 * @brief Draws the list of entities
314 * @param[in,out] ents The list of entities that are going to get rendered
315 * @sa R_GetEntityLists
316 */
R_DrawMeshEntities(entity_t * ents)317 void R_DrawMeshEntities (entity_t* ents)
318 {
319 entity_t* e;
320
321 e = ents;
322
323 while (e) {
324 switch (e->model->type) {
325 case mod_alias_md2:
326 case mod_alias_md3:
327 case mod_obj:
328 R_DrawAliasModel(e);
329 break;
330 default:
331 break;
332 }
333 e = e->next;
334 }
335 }
336
337 /**
338 * @sa R_GetEntityLists
339 */
R_DrawOpaqueMeshEntities(entity_t * ents)340 void R_DrawOpaqueMeshEntities (entity_t* ents)
341 {
342 if (!ents)
343 return;
344
345 if (!(refdef.rendererFlags & RDF_NOWORLDMODEL)) {
346 R_EnableLighting(r_state.model_program, true);
347 }
348 R_DrawMeshEntities(ents);
349 if (!(refdef.rendererFlags & RDF_NOWORLDMODEL)) {
350 R_EnableLighting(nullptr, false);
351 R_EnableGlowMap(nullptr);
352 }
353 }
354
355 /**
356 * Merge sort merge helper function.
357 */
R_MergeSortMerge(entity_t * a,entity_t * b)358 static entity_t* R_MergeSortMerge (entity_t* a, entity_t* b)
359 {
360 entity_t* c;
361
362 if (a == nullptr)
363 return b;
364
365 if (b == nullptr)
366 return a;
367
368 if (a->distanceFromViewOrigin > b->distanceFromViewOrigin) {
369 c = a;
370 c->next = R_MergeSortMerge(a->next, b);
371 } else {
372 c = b;
373 c->next = R_MergeSortMerge(a, b->next);
374 }
375
376 return c;
377 }
378
379 /**
380 * @brief Merge sort for the entity list
381 *
382 * @note We can't use in-place merging of the entities array because there are references
383 * in this array that must stay intact (references for tagged models e.g.).
384 *
385 * @return the first entity to render
386 */
R_MergeSortEntList(entity_t * c)387 static entity_t* R_MergeSortEntList (entity_t* c)
388 {
389 entity_t* a, *b;
390
391 /* list containing one or no entities is already sorted by definition */
392 if (c == nullptr || c->next == nullptr)
393 return c;
394
395 /* two element or longer lists are bisected */
396 a = c;
397 b = c->next;
398 while (b != nullptr && b->next != nullptr) {
399 c = c->next;
400 b = b->next->next;
401 }
402 b = c->next;
403 c->next = nullptr;
404
405 /* these halves are sorted recursively, and merged into one sorted list */
406 return R_MergeSortMerge(R_MergeSortEntList(a), R_MergeSortEntList(b));
407 }
408
409 /**
410 * @sa R_GetEntityLists
411 */
R_DrawBlendMeshEntities(entity_t * ents)412 void R_DrawBlendMeshEntities (entity_t* ents)
413 {
414 if (!ents)
415 return;
416
417 if (!(refdef.rendererFlags & RDF_NOWORLDMODEL)) {
418 R_EnableLighting(r_state.model_program, true);
419 }
420 R_EnableBlend(true);
421
422 R_DrawMeshEntities(R_MergeSortEntList(ents));
423
424 R_EnableBlend(false);
425 if (!(refdef.rendererFlags & RDF_NOWORLDMODEL)) {
426 R_EnableLighting(nullptr, false);
427 R_EnableGlowMap(nullptr);
428 }
429 }
430
431 /**
432 * @brief Draw replacement model (e.g. when model wasn't found)
433 * @sa R_DrawNullEntities
434 */
R_DrawNullModel(const entity_t * e)435 static void R_DrawNullModel (const entity_t* e)
436 {
437 int i;
438 vec3_t points[6];
439
440 R_EnableTexture(&texunit_diffuse, false);
441
442 glPushMatrix();
443 glMultMatrixf(e->transform.matrix);
444
445 R_BindArray(GL_VERTEX_ARRAY, GL_FLOAT, points);
446
447 VectorSet(points[0], 0, 0, -16);
448 for (i = 0; i <= 4; i++) {
449 points[i + 1][0] = 16 * cos(i * (M_PI / 2));
450 points[i + 1][1] = 16 * sin(i * (M_PI / 2));
451 points[i + 1][2] = 0;
452 }
453 glDrawArrays(GL_TRIANGLE_FAN, 0, 6);
454
455 refdef.batchCount++;
456
457 VectorSet(points[0], 0, 0, 16);
458 for (i = 4; i >= 0; i--) {
459 points[i + 1][0] = 16 * cos(i * (M_PI / 2));
460 points[i + 1][1] = 16 * sin(i * (M_PI / 2));
461 points[i + 1][2] = 0;
462 }
463 glDrawArrays(GL_TRIANGLE_FAN, 0, 6);
464
465 refdef.batchCount++;
466
467 R_BindDefaultArray(GL_VERTEX_ARRAY);
468
469 glPopMatrix();
470
471 R_EnableTexture(&texunit_diffuse, true);
472 }
473
R_DrawSpecialEntities(const entity_t * ents)474 void R_DrawSpecialEntities (const entity_t* ents)
475 {
476 const entity_t* e;
477
478 if (!ents)
479 return;
480
481 e = ents;
482
483 R_EnableBlend(true);
484 R_EnableDrawAsGlow(true);
485
486 while (e) {
487 if (e->flags & RF_BOX) {
488 R_DrawBox(e);
489 } else if (e->flags & RF_PATH) {
490 R_DrawFloor(e);
491 } else if (e->flags & RF_ARROW) {
492 R_DrawArrow(e);
493 }
494 e = e->next;
495 }
496
497 R_EnableDrawAsGlow(false);
498 R_EnableBlend(false);
499 }
500
501 /**
502 * @brief Draw entities which models couldn't be loaded
503 */
R_DrawNullEntities(const entity_t * ents)504 void R_DrawNullEntities (const entity_t* ents)
505 {
506 const entity_t* e;
507
508 if (!ents)
509 return;
510
511 e = ents;
512
513 while (e) {
514 R_DrawNullModel(e);
515 e = e->next;
516 }
517 }
518
519 /**
520 * Transforms a point by the inverse of the world-model matrix for the
521 * specified entity.
522 */
R_TransformForEntity(const entity_t * e,const vec3_t in,vec3_t out)523 void R_TransformForEntity (const entity_t* e, const vec3_t in, vec3_t out)
524 {
525 matrix4x4_t tmp, mat;
526
527 Matrix4x4_CreateFromQuakeEntity(&tmp, e->origin[0], e->origin[1], e->origin[2], e->angles[0], e->angles[1],
528 e->angles[2], e->scale[0]);
529
530 Matrix4x4_Invert_Simple(&mat, &tmp);
531 Matrix4x4_Transform(&mat, in, out);
532 }
533
534 /**
535 * @brief Calculates transformation matrix for the model and its tags
536 * @note The transformation matrix is only calculated once
537 */
R_CalcTransform(entity_t * e)538 static float* R_CalcTransform (entity_t* e)
539 {
540 transform_t* t;
541 float* mp;
542 float mt[16], mc[16];
543
544 /* check if this entity is already transformed */
545 t = &e->transform;
546
547 if (t->processing)
548 Com_Error(ERR_DROP, "Ring in entity transformations!");
549
550 if (t->done)
551 return t->matrix;
552
553 /* process this matrix */
554 t->processing = true;
555 mp = nullptr;
556
557 /* do parent object transformations first */
558 if (e->tagent) {
559 /* tag transformation */
560 const model_t* model = e->tagent->model;
561 const mAliasTagOrientation_t* current = nullptr;
562 const mAliasTagOrientation_t* old = nullptr;
563 const animState_t* as = &e->tagent->as;
564
565 R_GetTags(model, e->tagname, as->frame, as->oldframe, ¤t, &old);
566 if (current != nullptr && old != nullptr) {
567 float interpolated[16];
568
569 /* parent transformation */
570 mp = R_CalcTransform(e->tagent);
571
572 /* do interpolation */
573 R_InterpolateTransform(as->backlerp, model->alias.num_frames, current, old, interpolated);
574
575 /* transform */
576 GLMatrixMultiply(mp, interpolated, mt);
577 mp = mt;
578 }
579 }
580
581 GLMatrixAssemble(e->origin, e->angles, mc);
582
583 /* combine transformations */
584 if (mp)
585 GLMatrixMultiply(mp, mc, t->matrix);
586 else
587 memcpy(t->matrix, mc, sizeof(float) * 16);
588
589 /* matrix elements 12..14 contain (forward) translation vector, which is also the origin of model after transform */
590 e->distanceFromViewOrigin = VectorDist(&t->matrix[12], refdef.viewOrigin);
591
592 /* we're done */
593 t->done = true;
594 t->processing = false;
595
596 return t->matrix;
597 }
598
599 /**
600 * @brief Perform a frustum cull check for a given entity
601 * @param[in,out] e The entity to perform the frustum cull check for
602 * @return @c false if visible, @c true is the origin of the entity is outside the
603 * current frustum view
604 */
R_CullEntity(entity_t * e)605 static bool R_CullEntity (entity_t* e)
606 {
607 if (refdef.rendererFlags & RDF_NOWORLDMODEL)
608 return false;
609
610 if (r_nocull->integer)
611 return false;
612
613 if (!e->model) /* don't bother culling null model ents */
614 return false;
615
616 if (e->model->type == mod_bsp_submodel)
617 return R_CullBspModel(e);
618 else
619 return R_CullMeshModel(e);
620 }
621
622 /**
623 * @brief Primary entry point for drawing all entities.
624 * @sa R_RenderFrame
625 */
R_GetEntityLists(void)626 void R_GetEntityLists (void)
627 {
628 int i;
629 entity_t** chain;
630
631 if (!r_drawentities->integer)
632 return;
633
634 r_opaque_mesh_entities = r_special_entities =
635 r_blend_mesh_entities = r_null_entities = nullptr;
636
637 for (i = 0; i < refdef.numEntities; i++) {
638 entity_t* e = &r_entities[i];
639
640 /* frustum cull check */
641 if (R_CullEntity(e))
642 continue;
643
644 R_CalcTransform(e);
645
646 if (!e->model) {
647 if ((e->flags & RF_BOX) || (e->flags & RF_PATH) || (e->flags & RF_ARROW))
648 chain = &r_special_entities;
649 else
650 chain = &r_null_entities;
651 } else {
652 const image_t* skin;
653 switch (e->model->type) {
654 case mod_bsp_submodel:
655 R_AddBspRRef(&(e->model->bsp), e->origin, e->angles, true);
656 continue;
657 case mod_alias_md2:
658 case mod_alias_md3:
659 case mod_obj:
660 skin = R_AliasModelState(e->model, &e->as.mesh, &e->as.frame, &e->as.oldframe, &e->skinnum);
661 if (skin == nullptr || skin->texnum == 0)
662 Com_Error(ERR_DROP, "Model '%s' has no skin assigned", e->model->name);
663 if (skin->has_alpha || (e->flags & RF_TRANSLUCENT))
664 chain = &r_blend_mesh_entities;
665 else
666 chain = &r_opaque_mesh_entities;
667 break;
668 default:
669 Com_Error(ERR_DROP, "Unknown model type in R_GetEntityLists entity chain: %i (%s)",
670 e->model->type, e->model->name);
671 break;
672 }
673 }
674 e->next = *chain;
675 *chain = e;
676 }
677 }
678
679 /**
680 * @brief Get the next free entry in the entity list (the last one)
681 * @note This can't overflow, because R_AddEntity checks the bounds
682 * @sa R_AddEntity
683 */
R_GetFreeEntity(void)684 entity_t* R_GetFreeEntity (void)
685 {
686 if (refdef.numEntities >= MAX_ENTITIES)
687 Com_Error(ERR_DROP, "R_GetFreeEntity: MAX_ENTITIES exceeded");
688 return &r_entities[refdef.numEntities];
689 }
690
691 /**
692 * @brief Returns a specific entity from the list
693 */
R_GetEntity(int id)694 entity_t* R_GetEntity (int id)
695 {
696 if (id < 0 || id >= refdef.numEntities)
697 return nullptr;
698 return &r_entities[id];
699 }
700
701 /**
702 * @brief Adds a copy of the specified entity to the list of all known render entities.
703 * @sa R_GetFreeEntity
704 * @return The position of the entity in the render entity array or @c -1 in case the entity wasn't added.
705 */
R_AddEntity(const entity_t * ent)706 int R_AddEntity (const entity_t* ent)
707 {
708 if (refdef.numEntities >= MAX_ENTITIES)
709 Com_Error(ERR_DROP, "R_AddEntity: MAX_ENTITIES exceeded");
710
711 /* don't add the bsp tiles from random map assemblies */
712 if (ent->model && ent->model->type == mod_bsp)
713 return -1;
714
715 r_entities[refdef.numEntities] = *ent;
716
717 refdef.numEntities++;
718
719 return refdef.numEntities - 1;
720 }
721