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, &current, &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