/* Copyright (C) 2003-2006 Andrey Nazarov This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "gl_local.h" static qboolean GL_LightPoint(vec3_t origin, vec3_t color) { bsp_t *bsp = gl_static.world.cache; mface_t *surf; int s, t, i; byte *lightmap; byte *b1, *b2, *b3, *b4; int fracu, fracv; int w1, w2, w3, w4; byte temp[3]; int smax, tmax, size; lightstyle_t *style; vec3_t point; if (!bsp || !bsp->lightmap) { return qfalse; } point[0] = origin[0]; point[1] = origin[1]; point[2] = origin[2] - 8192; surf = BSP_LightPoint(bsp->nodes, origin, point, &s, &t); if (!surf) { return qfalse; } fracu = s & 15; fracv = t & 15; w1 = (16 - fracu) * (16 - fracv); w2 = fracu * (16 - fracv); w3 = fracu * fracv; w4 = (16 - fracu) * fracv; s >>= 4; t >>= 4; smax = S_MAX(surf); tmax = T_MAX(surf); size = smax * tmax * 3; VectorClear(color); lightmap = surf->lightmap; for (i = 0; i < surf->numstyles; i++) { b1 = &lightmap[3 * ((t + 0) * smax + (s + 0))]; b2 = &lightmap[3 * ((t + 0) * smax + (s + 1))]; b3 = &lightmap[3 * ((t + 1) * smax + (s + 1))]; b4 = &lightmap[3 * ((t + 1) * smax + (s + 0))]; temp[0] = (w1 * b1[0] + w2 * b2[0] + w3 * b3[0] + w4 * b4[0]) >> 8; temp[1] = (w1 * b1[1] + w2 * b2[1] + w3 * b3[1] + w4 * b4[1]) >> 8; temp[2] = (w1 * b1[2] + w2 * b2[2] + w3 * b3[2] + w4 * b4[2]) >> 8; style = LIGHT_STYLE(surf, i); color[0] += temp[0] * style->rgb[0]; color[1] += temp[1] * style->rgb[1]; color[2] += temp[2] * style->rgb[2]; lightmap += size; } GL_AdjustColor(color); return qtrue; } #if USE_DLIGHTS static void GL_MarkLights_r(mnode_t *node, dlight_t *light, int lightbit) { vec_t dot; int count; mface_t *face; while (node->plane) { dot = PlaneDiffFast(light->transformed, node->plane); if (dot > light->intensity - DLIGHT_CUTOFF) { node = node->children[0]; continue; } if (dot < -light->intensity + DLIGHT_CUTOFF) { node = node->children[1]; continue; } face = node->firstface; count = node->numfaces; while (count--) { if (!(face->drawflags & SURF_NOLM_MASK)) { if (face->dlightframe != glr.dlightframe) { face->dlightframe = glr.dlightframe; face->dlightbits = 0; } face->dlightbits |= lightbit; } face++; } GL_MarkLights_r(node->children[0], light, lightbit); node = node->children[1]; } } static void GL_MarkLights(void) { int i; dlight_t *light; for (i = 0, light = glr.fd.dlights; i < glr.fd.num_dlights; i++, light++) { VectorCopy(light->origin, light->transformed); GL_MarkLights_r(gl_static.world.cache->nodes, light, 1 << i); } } static void GL_TransformLights(mmodel_t *model) { int i; dlight_t *light; vec3_t temp; if (!model->headnode) { return; } for (i = 0, light = glr.fd.dlights; i < glr.fd.num_dlights; i++, light++) { VectorSubtract(light->origin, glr.ent->origin, temp); light->transformed[0] = DotProduct(temp, glr.entaxis[0]); light->transformed[1] = DotProduct(temp, glr.entaxis[1]); light->transformed[2] = DotProduct(temp, glr.entaxis[2]); GL_MarkLights_r(model->headnode, light, 1 << i); } } static void GL_AddLights(vec3_t origin, vec3_t color) { dlight_t *light; vec3_t dir; vec_t f; int i; for (i = 0, light = glr.fd.dlights; i < glr.fd.num_dlights; i++, light++) { VectorSubtract(light->origin, origin, dir); f = light->intensity - VectorLength(dir); if (f > 0) { f *= (1.0f / 255); VectorMA(color, f, light->color, color); } } } #endif void _R_LightPoint(vec3_t origin, vec3_t color) { if (gl_fullbright->integer) { VectorSet(color, 1, 1, 1); return; } // get lighting from world if (!GL_LightPoint(origin, color)) { VectorSet(color, 1, 1, 1); } #if USE_DLIGHTS if (gl_dynamic->integer == 1) { // add dynamic lights GL_AddLights(origin, color); } #endif if (gl_doublelight_entities->integer) { // apply modulate twice to mimic original ref_gl behavior VectorScale(color, gl_static.entity_modulate, color); } } void R_LightPoint(vec3_t origin, vec3_t color) { int i; _R_LightPoint(origin, color); for (i = 0; i < 3; i++) { clamp(color[i], 0, 1); } } static void GL_MarkLeaves(void) { static int lastNodesVisible; byte vis1[VIS_MAX_BYTES]; byte vis2[VIS_MAX_BYTES]; mleaf_t *leaf; mnode_t *node; uint_fast32_t *src1, *src2; int cluster1, cluster2, longs; vec3_t tmp; int i; bsp_t *bsp = gl_static.world.cache; leaf = BSP_PointLeaf(bsp->nodes, glr.fd.vieworg); cluster1 = cluster2 = leaf->cluster; VectorCopy(glr.fd.vieworg, tmp); if (!leaf->contents) { tmp[2] -= 16; } else { tmp[2] += 16; } leaf = BSP_PointLeaf(bsp->nodes, tmp); if (!(leaf->contents & CONTENTS_SOLID)) { cluster2 = leaf->cluster; } if (cluster1 == glr.viewcluster1 && cluster2 == glr.viewcluster2) { goto finish; } if (gl_lockpvs->integer) { goto finish; } glr.visframe++; glr.viewcluster1 = cluster1; glr.viewcluster2 = cluster2; if (!bsp->vis || gl_novis->integer || cluster1 == -1) { // mark everything visible for (i = 0; i < bsp->numnodes; i++) { bsp->nodes[i].visframe = glr.visframe; } for (i = 0; i < bsp->numleafs; i++) { bsp->leafs[i].visframe = glr.visframe; } lastNodesVisible = bsp->numnodes; goto finish; } BSP_ClusterVis(bsp, vis1, cluster1, DVIS_PVS); if (cluster1 != cluster2) { BSP_ClusterVis(bsp, vis2, cluster2, DVIS_PVS); longs = VIS_FAST_LONGS(bsp); src1 = (uint_fast32_t *)vis1; src2 = (uint_fast32_t *)vis2; while (longs--) { *src1++ |= *src2++; } } lastNodesVisible = 0; for (i = 0, leaf = bsp->leafs; i < bsp->numleafs; i++, leaf++) { cluster1 = leaf->cluster; if (cluster1 == -1) { continue; } if (Q_IsBitSet(vis1, cluster1)) { node = (mnode_t *)leaf; // mark parent nodes visible do { if (node->visframe == glr.visframe) { break; } node->visframe = glr.visframe; node = node->parent; lastNodesVisible++; } while (node); } } finish: c.nodesVisible = lastNodesVisible; } #define BACKFACE_EPSILON 0.001f #define BSP_CullFace(face, dot) \ (((dot) < -BACKFACE_EPSILON && !((face)->drawflags & DSURF_PLANEBACK)) || \ ((dot) > BACKFACE_EPSILON && ((face)->drawflags & DSURF_PLANEBACK))) void GL_DrawBspModel(mmodel_t *model) { mface_t *face; int count, mask = 0; vec3_t bounds[2]; vec_t dot; vec3_t transformed, temp; entity_t *ent = glr.ent; glCullResult_t cull; if (glr.entrotated) { cull = GL_CullSphere(ent->origin, model->radius); if (cull == CULL_OUT) { c.spheresCulled++; return; } if (cull == CULL_CLIP) { VectorCopy(model->mins, bounds[0]); VectorCopy(model->maxs, bounds[1]); cull = GL_CullLocalBox(ent->origin, bounds); if (cull == CULL_OUT) { c.rotatedBoxesCulled++; return; } } VectorSubtract(glr.fd.vieworg, ent->origin, temp); transformed[0] = DotProduct(temp, glr.entaxis[0]); transformed[1] = DotProduct(temp, glr.entaxis[1]); transformed[2] = DotProduct(temp, glr.entaxis[2]); } else { VectorAdd(model->mins, ent->origin, bounds[0]); VectorAdd(model->maxs, ent->origin, bounds[1]); cull = GL_CullBox(bounds); if (cull == CULL_OUT) { c.boxesCulled++; return; } VectorSubtract(glr.fd.vieworg, ent->origin, transformed); if (VectorEmpty(ent->origin) && model->drawframe != glr.drawframe) { mask = SURF_TRANS33 | SURF_TRANS66; } } // protect against infinite loop if the same inline model // with alpha faces is referenced by multiple entities model->drawframe = glr.drawframe; #if USE_DLIGHTS glr.dlightframe++; if (gl_dynamic->integer == 1) { GL_TransformLights(model); } #endif if (gl_dynamic->integer) { GL_BeginLights(); } qglPushMatrix(); qglTranslatef(ent->origin[0], ent->origin[1], ent->origin[2]); if (glr.entrotated) { qglRotatef(ent->angles[YAW], 0, 0, 1); qglRotatef(ent->angles[PITCH], 0, 1, 0); qglRotatef(ent->angles[ROLL], 1, 0, 0); } // draw visible faces // FIXME: go by headnode instead? face = model->firstface; count = model->numfaces; while (count--) { dot = PlaneDiffFast(transformed, face->plane); if (BSP_CullFace(face, dot)) { c.facesCulled++; } else if (face->drawflags & mask) { // FIXME: alpha faces are not supported // on rotated or translated inline models GL_AddAlphaFace(face); } else { GL_AddSolidFace(face); } face++; } if (gl_dynamic->integer) { GL_EndLights(); } GL_DrawSolidFaces(); qglPopMatrix(); } #define NODE_CLIPPED 0 #define NODE_UNCLIPPED 15 static inline qboolean GL_ClipNode(mnode_t *node, int *clipflags) { int flags = *clipflags; int i, bits, mask; if (flags == NODE_UNCLIPPED) { return qtrue; } for (i = 0, mask = 1; i < 4; i++, mask <<= 1) { if (flags & mask) { continue; } bits = BoxOnPlaneSide(node->mins, node->maxs, &glr.frustumPlanes[i]); if (bits == BOX_BEHIND) { return qfalse; } if (bits == BOX_INFRONT) { flags |= mask; } } *clipflags = flags; return qtrue; } static inline void GL_DrawLeaf(mleaf_t *leaf) { mface_t **face, **last; if (leaf->contents == CONTENTS_SOLID) { return; // solid leaf } if (glr.fd.areabits && !Q_IsBitSet(glr.fd.areabits, leaf->area)) { return; // door blocks sight } last = leaf->firstleafface + leaf->numleaffaces; for (face = leaf->firstleafface; face < last; face++) { (*face)->drawframe = glr.drawframe; } c.leavesDrawn++; } static inline void GL_AddGenericFace(mface_t *face) { if (face->drawflags & SURF_SKY) { R_AddSkySurface(face); return; } if (face->drawflags & (SURF_TRANS33 | SURF_TRANS66)) { GL_AddAlphaFace(face); return; } GL_AddSolidFace(face); } static inline void GL_DrawNode(mnode_t *node, vec_t dot) { mface_t *face, *last = node->firstface + node->numfaces; for (face = node->firstface; face < last; face++) { if (face->drawframe != glr.drawframe) { continue; } if (BSP_CullFace(face, dot)) { c.facesCulled++; } else { GL_AddGenericFace(face); } } c.nodesDrawn++; } static void GL_WorldNode_r(mnode_t *node, int clipflags) { int side; vec_t dot; while (node->visframe == glr.visframe) { if (!GL_ClipNode(node, &clipflags)) { c.nodesCulled++; break; } if (!node->plane) { GL_DrawLeaf((mleaf_t *)node); break; } dot = PlaneDiffFast(glr.fd.vieworg, node->plane); side = dot < 0; GL_WorldNode_r(node->children[side], clipflags); GL_DrawNode(node, dot); node = node->children[side ^ 1]; } } void GL_DrawWorld(void) { GL_MarkLeaves(); #if USE_DLIGHTS glr.dlightframe++; if (gl_dynamic->integer == 1) { GL_MarkLights(); } #endif R_ClearSkyBox(); if (gl_dynamic->integer) { GL_BeginLights(); } GL_WorldNode_r(gl_static.world.cache->nodes, gl_cull_nodes->integer ? NODE_CLIPPED : NODE_UNCLIPPED); if (gl_dynamic->integer) { GL_EndLights(); } GL_DrawSolidFaces(); R_DrawSkyBox(); }