1 /**
2 * @file
3 * @brief BSP model code
4 */
5
6 /*
7 Copyright (C) 1997-2001 Id Software, Inc.
8
9 This program is free software; you can redistribute it and/or
10 modify it under the terms of the GNU General Public License
11 as published by the Free Software Foundation; either version 2
12 of the License, or (at your option) any later version.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17
18 See the GNU General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23
24 */
25
26 #include "r_local.h"
27 #include "r_lightmap.h"
28 #include "r_material.h"
29 #include "r_light.h"
30 #include "r_draw.h"
31
32 /*
33 =============================================================
34 BRUSH MODELS
35 =============================================================
36 */
37
38 #define BACKFACE_EPSILON 0.01
39
40 #define MAX_BSPS_TO_RENDER 1024
41
42 typedef struct bspRenderRef_s {
43 const mBspModel_t* bsp;
44 vec3_t origin;
45 vec3_t angles;
46 } bspRenderRef_t;
47
48 static bspRenderRef_t bspRRefs[MAX_BSPS_TO_RENDER];
49 static int numBspRRefs;
50
51 /**
52 * @brief Returns whether if the specified bounding box is completely culled by the
53 * view frustum (PSIDE_BACK), completely not culled (PSIDE_FRONT) or split by it (PSIDE_BOTH)
54 * @param[in] mins The mins of the bounding box
55 * @param[in] maxs The maxs of the bounding box
56 */
R_CullBox(const vec3_t mins,const vec3_t maxs)57 static int R_CullBox (const vec3_t mins, const vec3_t maxs)
58 {
59 int i;
60 int cullState = 0;
61
62 if (r_nocull->integer)
63 return PSIDE_FRONT;
64
65 for (i = lengthof(r_locals.frustum) - 1; i >= 0; i--) {
66 int planeSide = TR_BoxOnPlaneSide(mins, maxs, &r_locals.frustum[i]);
67 if (planeSide == PSIDE_BACK)
68 return PSIDE_BACK; /* completely culled away */
69 cullState |= planeSide;
70 }
71
72 return cullState;
73 }
74
75
76 /**
77 * @brief Performs a spherical frustum check
78 * @param[in] centre The world coordinate that is the center of the sphere
79 * @param[in] radius The radius of the sphere to check the frustum for
80 * @param[in] clipflags Can be used to skip sides of the frustum planes
81 * @return @c true if the sphere is completely outside the frustum, @c false otherwise
82 */
R_CullSphere(const vec3_t centre,const float radius,const unsigned int clipflags)83 bool R_CullSphere (const vec3_t centre, const float radius, const unsigned int clipflags)
84 {
85 unsigned int i;
86 unsigned int bit;
87 const cBspPlane_t* p;
88
89 if (r_nocull->integer)
90 return false;
91
92 for (i = lengthof(r_locals.frustum), bit = 1, p = r_locals.frustum; i > 0; i--, bit <<= 1, p++) {
93 if (!(clipflags & bit))
94 continue;
95 if (DotProduct(centre, p->normal) - p->dist <= -radius)
96 return true;
97 }
98
99 return false;
100 }
101
102 /**
103 * @brief Returns true if the specified entity is completely culled by the view
104 * frustum, false otherwise.
105 * @param[in] e The entity to check
106 * @sa R_CullBox
107 */
R_CullBspModel(const entity_t * e)108 bool R_CullBspModel (const entity_t* e)
109 {
110 vec3_t mins, maxs;
111
112 /* no surfaces */
113 if (!e->model->bsp.nummodelsurfaces)
114 return true;
115
116 if (e->isOriginBrushModel) {
117 int i;
118 for (i = 0; i < 3; i++) {
119 mins[i] = e->origin[i] - e->model->radius;
120 maxs[i] = e->origin[i] + e->model->radius;
121 }
122 } else {
123 VectorAdd(e->origin, e->model->mins, mins);
124 VectorAdd(e->origin, e->model->maxs, maxs);
125 }
126
127 return R_CullBox(mins, maxs) == PSIDE_BACK;
128 }
129
130 /*
131 =============================================================
132 WORLD MODEL
133 =============================================================
134 */
135
136 /**
137 * @brief Developer tool for viewing BSP vertex normals. Only Phong interpolated
138 * surfaces show their normals when r_shownormals > 1.
139 */
R_DrawBspNormals(int tile)140 void R_DrawBspNormals (int tile)
141 {
142 int i, j, k;
143 const mBspSurface_t* surf;
144 const mBspModel_t* bsp;
145 const vec4_t color = {1.0, 0.0, 0.0, 1.0};
146
147 if (!r_shownormals->integer)
148 return;
149
150 R_EnableTexture(&texunit_diffuse, false);
151
152 R_ResetArrayState(); /* default arrays */
153
154 R_Color(color);
155
156 k = 0;
157 bsp = &r_mapTiles[tile]->bsp;
158 surf = bsp->surfaces;
159 for (i = 0; i < bsp->numsurfaces; i++, surf++) {
160 if (surf->frame != r_locals.frame)
161 continue; /* not visible */
162
163 if (surf->texinfo->flags & SURF_WARP)
164 continue; /* don't care */
165
166 if (r_shownormals->integer > 1 && !(surf->texinfo->flags & SURF_PHONG))
167 continue; /* don't care */
168
169 /* avoid overflows, draw in batches */
170 if (k > r_state.array_size - 512) {
171 glDrawArrays(GL_LINES, 0, k / 3);
172 k = 0;
173
174 refdef.batchCount++;
175 }
176
177 for (j = 0; j < surf->numedges; j++) {
178 vec3_t end;
179 const GLfloat* vertex = &bsp->verts[(surf->index + j) * 3];
180 const GLfloat* normal = &bsp->normals[(surf->index + j) * 3];
181
182 VectorMA(vertex, 12.0, normal, end);
183
184 memcpy(&r_state.vertex_array_3d[k], vertex, sizeof(vec3_t));
185 memcpy(&r_state.vertex_array_3d[k + 3], end, sizeof(vec3_t));
186 k += sizeof(vec3_t) / sizeof(vec_t) * 2;
187 R_ReallocateStateArrays(k);
188 }
189 }
190
191 glDrawArrays(GL_LINES, 0, k / 3);
192
193 refdef.batchCount++;
194
195 R_EnableTexture(&texunit_diffuse, true);
196
197 R_Color(nullptr);
198 }
199
200 /**
201 * @brief Recurse down the bsp tree and mark all surfaces as visible
202 * for being rendered
203 * @sa R_DrawWorld
204 * @sa R_RecurseWorld
205 * @sa R_RecursiveWorldNode
206 * @param[in] node The bsp node to mark
207 * @param[in] tile The maptile (map assembly)
208 */
R_RecursiveVisibleWorldNode(const mBspNode_t * node,int tile)209 static void R_RecursiveVisibleWorldNode (const mBspNode_t* node, int tile)
210 {
211 /* if a leaf node, nothing to mark */
212 if (node->contents > CONTENTS_NODE)
213 return;
214
215 /* pathfinding nodes are invalid here */
216 assert(node->plane);
217
218 mBspSurface_t* surf = r_mapTiles[tile]->bsp.surfaces + node->firstsurface;
219 for (int i = 0; i < node->numsurfaces; i++, surf++)
220 surf->frame = r_locals.frame;
221
222 /* recurse down the children */
223 R_RecursiveVisibleWorldNode(node->children[0], tile);
224 R_RecursiveVisibleWorldNode(node->children[1], tile);
225 }
226
227 /**
228 * @brief Recurse down the bsp tree and mark surfaces that are visible (not culled)
229 * for being rendered
230 * @sa R_DrawWorld
231 * @sa R_RecurseWorld
232 * @sa R_RecursiveVisibleWorldNode
233 * @param[in] node The bsp node to check
234 * @param[in] tile The maptile (map assembly)
235 */
R_RecursiveWorldNode(const mBspNode_t * node,int tile)236 static void R_RecursiveWorldNode (const mBspNode_t* node, int tile)
237 {
238 int i;
239 int cullState;
240 mBspSurface_t* surf;
241
242 /* if a leaf node, nothing to mark */
243 if (node->contents > CONTENTS_NODE)
244 return;
245
246 cullState = R_CullBox(node->minmaxs, node->minmaxs + 3);
247
248 if (cullState == PSIDE_BACK)
249 return; /* culled out */
250
251 /* pathfinding nodes are invalid here */
252 assert(node->plane);
253
254 surf = r_mapTiles[tile]->bsp.surfaces + node->firstsurface;
255 for (i = 0; i < node->numsurfaces; i++, surf++)
256 surf->frame = r_locals.frame;
257
258 /* recurse down the children */
259 /** @todo avoid being too precise, it's a waste of CPU time; possibly, granularity of 256x256x256 should be enough */
260 if (cullState == PSIDE_FRONT) {
261 /* completely inside the frustum - no need to do any further checks */
262 R_RecursiveVisibleWorldNode(node->children[0], tile);
263 R_RecursiveVisibleWorldNode(node->children[1], tile);
264 } else {
265 /* partially clipped by frustum - recurse to do finer checks */
266 R_RecursiveWorldNode(node->children[0], tile);
267 R_RecursiveWorldNode(node->children[1], tile);
268 }
269 }
270
271 /**
272 * @brief Wrapper that recurses the bsp nodes but skip the pathfinding nodes
273 * @sa R_GetLevelSurfaceLists
274 * @param[in] node The bsp node to check
275 * @param[in] tile The maptile (map assembly)
276 * @sa R_ModLoadNodes about pathfinding nodes
277 */
R_RecurseWorld(const mBspNode_t * node,int tile)278 static void R_RecurseWorld (const mBspNode_t* node, int tile)
279 {
280 /* skip special pathfinding nodes */
281 if (node->contents == CONTENTS_PATHFINDING_NODE) {
282 R_RecurseWorld(node->children[0], tile);
283 R_RecurseWorld(node->children[1], tile);
284 } else {
285 R_RecursiveWorldNode(node, tile);
286 }
287 }
288
289
290 /**
291 * @brief Fills the surface chains for the current worldlevel and hide other levels
292 * @sa cvar cl_worldlevel
293 */
R_GetLevelSurfaceLists(void)294 void R_GetLevelSurfaceLists (void)
295 {
296 if (!r_drawworld->integer)
297 return;
298
299 const int mask = 1 << refdef.worldlevel;
300
301 for (int tile = 0; tile < r_numMapTiles; tile++) {
302 /* don't draw weaponclip, actorclip and stepon */
303 for (int i = 0; i <= LEVEL_LASTVISIBLE; i++) {
304 /* check the worldlevel flags */
305 if (i && !(i & mask))
306 continue;
307
308 mBspModel_t* bspModel = &r_mapTiles[tile]->bsp;
309 mBspHeader_t* header = &bspModel->submodels[i];
310 if (!header->numfaces)
311 continue;
312
313 R_RecurseWorld(bspModel->nodes + header->headnode, tile);
314 }
315 }
316 }
317
318 /*
319 =============================================================
320 Deferred rendering
321 =============================================================
322 */
323
R_ClearBspRRefs(void)324 void R_ClearBspRRefs (void)
325 {
326 numBspRRefs = 0;
327 }
328
329 /**
330 * @brief Adds bsp render references
331 * @note If forceVisibility is set, will mark the surfaces of the given bsp model as visible for this frame.
332 * @param[in] model The bsp model to add to the render chain
333 * @param[in] origin
334 * @param[in] angles
335 * @param[in] forceVisibility force model to be fully visible
336 */
R_AddBspRRef(const mBspModel_t * model,const vec3_t origin,const vec3_t angles,const bool forceVisibility)337 void R_AddBspRRef (const mBspModel_t* model, const vec3_t origin, const vec3_t angles, const bool forceVisibility)
338 {
339 if (numBspRRefs >= MAX_BSPS_TO_RENDER) {
340 Com_Printf("Cannot add BSP model rendering reference: MAX_BSPS_TO_RENDER exceeded\n");
341 return;
342 }
343
344 if (!model) {
345 Com_Printf("R_AddBspRRef: null model!\n");
346 return;
347 }
348
349 bspRenderRef_t* bspRR = &bspRRefs[numBspRRefs++];
350 bspRR->bsp = model;
351 VectorCopy(origin, bspRR->origin);
352 VectorCopy(angles, bspRR->angles);
353
354 if (!forceVisibility)
355 return;
356
357 mBspSurface_t* surf = &model->surfaces[model->firstmodelsurface];
358 for (int i = 0; i < model->nummodelsurfaces; i++, surf++) {
359 /* visible flag for rendering */
360 surf->frame = r_locals.frame;
361 }
362 }
363
364 typedef void (*drawSurfaceFunc)(const mBspSurfaces_t* surfs, glElementIndex_t* indexPtr);
365
366 /**
367 * @param[in] drawFunc The function pointer to the surface draw function
368 */
R_RenderBspRRefs(drawSurfaceFunc drawFunc,surfaceArrayType_t surfType)369 static void R_RenderBspRRefs (drawSurfaceFunc drawFunc, surfaceArrayType_t surfType)
370 {
371 glEnable(GL_CULL_FACE);
372 glCullFace(GL_FRONT); /* our triangles are backwards to what OpenGL expects, so tell it to render only back faces */
373
374 for (int i = 0; i < numBspRRefs; i++) {
375 const bspRenderRef_t* const bspRR = &bspRRefs[i];
376 const mBspModel_t* const bsp = bspRR->bsp;
377 const mBspModel_t* const tile = &r_mapTiles[bsp->maptile]->bsp; /* This is required to find the tile (world) bsp model to which arrays belong (submodels do not own arrays, but use world model ones) */
378 glElementIndex_t* indexPtr;
379
380 if (!bsp->sorted_surfaces[surfType]->count)
381 continue;
382
383 R_SetArrayState(tile);
384
385 /* Vertex buffers are nullptr-based, arrays are not */
386 if (qglBindBuffer && r_vertexbuffers->integer)
387 indexPtr = nullptr;
388 else
389 indexPtr = tile->indexes;
390
391 glPushMatrix();
392
393 glTranslatef(bspRR->origin[0], bspRR->origin[1], bspRR->origin[2]);
394 glRotatef(bspRR->angles[YAW], 0, 0, 1);
395 glRotatef(bspRR->angles[PITCH], 0, 1, 0);
396 glRotatef(bspRR->angles[ROLL], 1, 0, 0);
397
398 drawFunc(bsp->sorted_surfaces[surfType], indexPtr);
399
400 /** @todo make it work again; also reimplement r_showbox 2 */
401 #if 0
402 /* show model bounding box */
403 if (r_showbox->integer) {
404 const model_t* model = bspRR->bsp;
405 R_DrawBoundingBox(model->mins, model->maxs);
406 }
407 #endif
408
409 glPopMatrix();
410 }
411
412 /* and restore array pointers */
413 R_ResetArrayState();
414
415 glCullFace(GL_BACK);
416 glDisable(GL_CULL_FACE);
417 }
418
419 /**
420 * @brief Draw all simple opaque bsp surfaces with multitexture enabled and light enabled
421 */
R_RenderOpaqueBspRRefs(void)422 void R_RenderOpaqueBspRRefs (void)
423 {
424 R_EnableTexture(&texunit_lightmap, true);
425 R_EnableLighting(r_state.world_program, true);
426 R_EnableWorldLights();
427
428 R_RenderBspRRefs(R_DrawSurfaces, S_OPAQUE);
429
430 R_EnableLighting(nullptr, false);
431 R_EnableGlowMap(nullptr);
432 R_EnableTexture(&texunit_lightmap, false);
433 }
434
435 /**
436 * @brief Draw all warped opaque bsp surfaces via warp shader
437 */
R_RenderOpaqueWarpBspRRefs(void)438 void R_RenderOpaqueWarpBspRRefs (void)
439 {
440 R_EnableWarp(r_state.warp_program, true);
441
442 R_RenderBspRRefs(R_DrawSurfaces, S_OPAQUE_WARP);
443
444 R_EnableWarp(nullptr, false);
445 R_EnableGlowMap(nullptr);
446 }
447
R_RenderAlphaTestBspRRefs(void)448 void R_RenderAlphaTestBspRRefs (void)
449 {
450 R_EnableAlphaTest(true);
451 R_EnableLighting(r_state.world_program, true);
452 R_EnableWorldLights();
453
454 R_RenderBspRRefs(R_DrawSurfaces, S_ALPHA_TEST);
455
456 R_EnableLighting(nullptr, false);
457 R_EnableGlowMap(nullptr);
458 R_EnableAlphaTest(false);
459 }
460
R_RenderMaterialBspRRefs(void)461 void R_RenderMaterialBspRRefs (void)
462 {
463 R_RenderBspRRefs(R_DrawMaterialSurfaces, S_MATERIAL);
464 }
465
R_RenderFlareBspRRefs(void)466 void R_RenderFlareBspRRefs (void)
467 {
468 R_RenderBspRRefs(R_DrawFlareSurfaces, S_FLARE);
469 }
470
471 /**
472 * @brief Draw all translucent bsp surfaces with multitexture enabled and blend enabled
473 */
R_RenderBlendBspRRefs(void)474 void R_RenderBlendBspRRefs (void)
475 {
476 assert(r_state.blend_enabled);
477 R_EnableTexture(&texunit_lightmap, true);
478
479 R_RenderBspRRefs(R_DrawSurfaces, S_BLEND);
480
481 R_EnableTexture(&texunit_lightmap, false);
482 }
483
484 /**
485 * @brief Draw all warped translucent bsp surfaces via warp shader and with blend enabled
486 */
R_RenderBlendWarpBspRRefs(void)487 void R_RenderBlendWarpBspRRefs (void)
488 {
489 assert(r_state.blend_enabled);
490 R_EnableWarp(r_state.warp_program, true);
491
492 R_RenderBspRRefs(R_DrawSurfaces, S_BLEND_WARP);
493
494 R_EnableWarp(nullptr, false);
495 R_EnableGlowMap(nullptr);
496 }
497