diff options
Diffstat (limited to 'src/gl_world.c')
-rw-r--r-- | src/gl_world.c | 476 |
1 files changed, 476 insertions, 0 deletions
diff --git a/src/gl_world.c b/src/gl_world.c new file mode 100644 index 0000000..6edc24b --- /dev/null +++ b/src/gl_world.c @@ -0,0 +1,476 @@ +/* +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" + +vec3_t modelViewOrigin; // viewer origin in model space + +qboolean GL_LightPoint( vec3_t origin, vec3_t color ) { + bsp_t *bsp = gl_static.world.cache; + mface_t *surf; + int s, t; + byte *b1, *b2, *b3, *b4; + int fracu, fracv; + int w1, w2, w3, w4; + byte temp[3]; + int pitch; + 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; + + s >>= 4; + t >>= 4; + + pitch = ( surf->extents[0] >> 4 ) + 1; + b1 = &surf->lightmap[3 * ( ( t + 0 ) * pitch + ( s + 0 ) )]; + b2 = &surf->lightmap[3 * ( ( t + 0 ) * pitch + ( s + 1 ) )]; + b3 = &surf->lightmap[3 * ( ( t + 1 ) * pitch + ( s + 1 ) )]; + b4 = &surf->lightmap[3 * ( ( t + 1 ) * pitch + ( s + 0 ) )]; + + w1 = ( 16 - fracu ) * ( 16 - fracv ); + w2 = fracu * ( 16 - fracv ); + w3 = fracu * fracv; + w4 = ( 16 - fracu ) * fracv; + + 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; + + GL_AdjustColor( temp, temp, 2 ); + + VectorScale( temp, 1.0f / 255, color ); + + return qtrue; +} + +#if USE_DLIGHTS +static void GL_MarkLights_r( mnode_t *node, dlight_t *light ) { + vec_t dot; + int count; + mface_t *face; + int lightbit = 1 << light->index; + + while( node->plane ) { + dot = PlaneDiffFast( light->transformed, node->plane ); + if( dot > light->intensity ) { + node = node->children[0]; + continue; + } + if( dot < -light->intensity ) { + node = node->children[1]; + continue; + } + + face = node->firstFace; + count = node->numFaces; + while( count-- ) { + if( !( face->texinfo->flags & NOLIGHT_MASK ) ) { + if( face->dlightframe != glr.drawframe ) { + face->dlightframe = glr.drawframe; + face->dlightbits = 0; + } + + face->dlightbits |= lightbit; + } + face++; + } + + GL_MarkLights_r( node->children[0], light ); + + node = node->children[1]; + } +} + +void GL_MarkLights( void ) { + int i; + dlight_t *light; + + for( i = 0, light = glr.fd.dlights; i < glr.fd.num_dlights; i++, light++ ) { + light->index = i; + VectorCopy( light->origin, light->transformed ); + GL_MarkLights_r( gl_static.world.cache->nodes, light ); + } +} + +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++ ) { + light->index = i; + 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 ); + } +} + +void GL_AddLights( vec3_t origin, vec3_t color ) { + dlight_t *light; + vec3_t dir; + vec_t dist, f; + int i; + + for( i = 0, light = glr.fd.dlights; i < glr.fd.num_dlights; i++, light++ ) { + VectorSubtract( light->origin, origin, dir ); + dist = VectorLength( dir ); + if( dist > light->intensity ) { + continue; + } + f = 1.0f - dist / light->intensity; + 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 ) { + // add dynamic lights + GL_AddLights( origin, color ); + } +#endif + + // apply modulate twice to mimic original ref_gl behavior + VectorScale( color, gl_modulate->value, 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 ); + } +} + +void GL_MarkLeaves( void ) { + static int lastNodesVisible; + byte vis1[MAX_MAP_VIS]; + byte vis2[MAX_MAP_VIS]; + mleaf_t *leaf; + mnode_t *node; + uint32_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 = ( bsp->visrowsize + 3 ) / 4; + src1 = ( uint32_t * )vis1; + src2 = ( uint32_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; + vec3_t bounds[2]; + vec_t dot; + vec3_t 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 ); + modelViewOrigin[0] = DotProduct( temp, glr.entaxis[0] ); + modelViewOrigin[1] = DotProduct( temp, glr.entaxis[1] ); + modelViewOrigin[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, modelViewOrigin ); + } + + glr.drawframe++; + +#if USE_DLIGHTS + if( gl_dynamic->integer ) { + GL_TransformLights( model ); + } +#endif + + 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( modelViewOrigin, face->plane ); + if( BSP_CullFace( face, dot ) ) { + c.facesCulled++; + } else { + /* FIXME: trans surfaces are not supported on inline models */ + GL_AddSolidFace( face ); + } + face++; + } + + 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; + } +} + +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_AddFace( 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( modelViewOrigin, node->plane ); + if( dot < 0 ) { + side = 1; + } else { + side = 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 + if( gl_dynamic->integer ) { + GL_MarkLights(); + } +#endif + + R_ClearSkyBox(); + + VectorCopy( glr.fd.vieworg, modelViewOrigin ); + + GL_WorldNode_r( gl_static.world.cache->nodes, + gl_cull_nodes->integer ? NODE_CLIPPED : NODE_UNCLIPPED ); + + GL_DrawSolidFaces(); + + R_DrawSkyBox(); +} + |