diff options
author | Andrey Nazarov <skuller@skuller.net> | 2007-08-14 20:18:08 +0000 |
---|---|---|
committer | Andrey Nazarov <skuller@skuller.net> | 2007-08-14 20:18:08 +0000 |
commit | f294db4ccf45f6274e65260dd6f9a2c5faa94313 (patch) | |
tree | e8cf1ba2bfe9c8417eec17faf912442f52fc4ef2 /source/gl_world.c |
Initial import of the new Q2PRO tree.
Diffstat (limited to 'source/gl_world.c')
-rw-r--r-- | source/gl_world.c | 607 |
1 files changed, 607 insertions, 0 deletions
diff --git a/source/gl_world.c b/source/gl_world.c new file mode 100644 index 0000000..f08b4ed --- /dev/null +++ b/source/gl_world.c @@ -0,0 +1,607 @@ +/* +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 */ + +static vec3_t lightcolor; + +static qboolean GL_LightPoint_r( bspNode_t *node, vec3_t start, vec3_t end ) { + cplane_t *plane; + vec_t startFrac, endFrac, midFrac; + vec3_t mid; + int side; + qboolean ret; + bspSurface_t *surf; + bspTexinfo_t *texinfo; + int i, pitch; + int s, t; + byte *b1, *b2, *b3, *b4; + int fracu, fracv; + int w1, w2, w3, w4; + int color[3]; + + if( !( plane = node->plane ) ) { + return qfalse; + } + + /* calculate distancies */ + startFrac = DotProduct( plane->normal, start ) - plane->dist; + endFrac = DotProduct( plane->normal, end ) - plane->dist; + side = ( startFrac < 0 ); + + if( ( endFrac < 0 ) == side ) { + /* both points are one the same side */ + return GL_LightPoint_r( node->children[side], start, end ); + } + + /* find crossing point */ + midFrac = startFrac / ( startFrac - endFrac ); + mid[0] = start[0] + ( end[0] - start[0] ) * midFrac; + mid[1] = start[1] + ( end[1] - start[1] ) * midFrac; + mid[2] = start[2] + ( end[2] - start[2] ) * midFrac; + + /* check near side */ + ret = GL_LightPoint_r( node->children[side], start, mid ); + if( ret ) { + return ret; + } + + surf = node->firstFace; + for( i = 0; i < node->numFaces; i++, surf++ ) { + texinfo = surf->texinfo; + if( texinfo->flags & (SURF_WARP|SURF_SKY) ) { + continue; + } + if( !surf->lightmap ) { + continue; + } + s = DotProduct( texinfo->axis[0], mid ) + texinfo->offset[0]; + t = DotProduct( texinfo->axis[1], mid ) + texinfo->offset[1]; + + s -= surf->texturemins[0]; + t -= surf->texturemins[1]; + if( s < 0 || t < 0 ) { + continue; + } + if( s > surf->extents[0] || t > surf->extents[1] ) { + continue; + } + + 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; + + color[0] = ( w1 * b1[0] + w2 * b2[0] + w3 * b3[0] + w4 * b4[0] ) >> 8; + color[1] = ( w1 * b1[1] + w2 * b2[1] + w3 * b3[1] + w4 * b4[1] ) >> 8; + color[2] = ( w1 * b1[2] + w2 * b2[2] + w3 * b3[2] + w4 * b4[2] ) >> 8; + + VectorMA( lightcolor, 1.0f / 255, color, lightcolor ); + + return qtrue; + } + + /* check far side */ + return GL_LightPoint_r( node->children[side^1], mid, end ); + +} + +void GL_LightPoint( vec3_t origin, vec3_t dest ) { + extern cvar_t *gl_modulate_hack; + vec3_t point; + dlight_t *light; + vec3_t dir; + vec_t dist, f; + int i; + + if( !r_world.name[0] || gl_fullbright->integer ) { + VectorSet( dest, 1, 1, 1 ); + return; + } + + point[0] = origin[0]; + point[1] = origin[1]; + point[2] = origin[2] - 8192; + + VectorClear( lightcolor ); + if( !r_world.lightmap || !GL_LightPoint_r( r_world.nodes, origin, point ) ) { + VectorSet( lightcolor, 1, 1, 1 ); + } + + if( gl_modulate_hack && gl_modulate_hack->integer ) { + VectorScale( lightcolor, gl_modulate->value, lightcolor ); + } + + 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( lightcolor, f, light->color, lightcolor ); + } + + /* apply modulate twice to mimic original ref_gl behavior */ + VectorScale( lightcolor, gl_modulate->value, lightcolor ); + + for( i = 0; i < 3; i++ ) { + if( lightcolor[i] > 1 ) { + lightcolor[i] = 1; + } else if( lightcolor[i] < 0 ) { + lightcolor[i] = 0; + } + } + + VectorCopy( lightcolor, dest ); + +} + +static void GL_MarkLights_r( bspNode_t *node, dlight_t *light ) { + cplane_t *plane; + vec_t dot; + int lightbit, count; + bspSurface_t *face; + + if( !( plane = node->plane ) ) { + return; + } + + switch( plane->type ) { + case PLANE_X: + dot = light->transformed[0] - plane->dist; + break; + case PLANE_Y: + dot = light->transformed[1] - plane->dist; + break; + case PLANE_Z: + dot = light->transformed[2] - plane->dist; + break; + default: + dot = DotProduct( light->transformed, plane->normal ) - plane->dist; + break; + } + + if( dot > light->intensity ) { + GL_MarkLights_r( node->children[0], light ); + return; + } + if( dot < -light->intensity ) { + GL_MarkLights_r( node->children[1], light ); + return; + } + + lightbit = 1 << light->index; + 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 ); + GL_MarkLights_r( node->children[1], light ); +} + +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( r_world.nodes, light ); + } +} + +static void GL_TransformLights( bspSubmodel_t *model ) { + int i; + dlight_t *light; + vec3_t temp; + + if( !model->headnode ) { + // this happens on some maps + // could lead to a crash of the original ref_gl + //Com_WPrintf( "GL_TransformLights: NULL headnode\n" ); + 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_MarkLeaves( void ) { + byte fatvis[MAX_MAP_LEAFS/8]; + bspLeaf_t *leaf, *lastleaf; + bspNode_t *node, *lastnode; + byte *vis1, *vis2; + uint32 *dst, *src1, *src2; + int cluster1, cluster2, longs; + vec3_t tmp; + static int lastNodesVisible; + + leaf = Bsp_FindLeaf( glr.fd.vieworg ); + cluster1 = cluster2 = leaf->cluster; + VectorCopy( glr.fd.vieworg, tmp ); + if( !leaf->contents ) { + tmp[2] -= 16; + } else { + tmp[2] += 16; + } + leaf = Bsp_FindLeaf( tmp ); + if( !( leaf->contents & CONTENTS_SOLID ) ) { + cluster2 = leaf->cluster; + } + + if( cluster1 == glr.viewcluster1 && cluster2 == glr.viewcluster2 ) { + goto finish; + } + + if( gl_lockpvs->integer ) { + goto finish; + } + + vis1 = vis2 = Bsp_ClusterPVS( cluster1 ); + if( cluster1 != cluster2 ) { + vis2 = Bsp_ClusterPVS( cluster2 ); + if( !vis1 ) { + vis1 = vis2; + } else if( !vis2 ) { + vis2 = vis1; + } + } + glr.visframe++; + lastNodesVisible = 0; + glr.viewcluster1 = cluster1; + glr.viewcluster2 = cluster2; + + if( !vis1 || gl_novis->integer ) { + /* mark everything visible */ + lastleaf = r_world.leafs + r_world.numLeafs; + for( leaf = r_world.leafs; leaf != lastleaf; leaf++ ) { + leaf->visframe = glr.visframe; + } + lastnode = r_world.nodes + r_world.numNodes; + for( node = r_world.nodes; node != lastnode; node++ ) { + node->visframe = glr.visframe; + } + lastNodesVisible = r_world.numNodes; + goto finish; + } + + if( vis1 != vis2 ) { + longs = ( r_world.numClusters + 31 ) >> 5; + src1 = ( uint32 * )vis1; + src2 = ( uint32 * )vis2; + dst = ( uint32 * )fatvis; + while( longs-- ) { + *dst++ = *src1++ | *src2++; + } + vis1 = fatvis; + } + + lastleaf = r_world.leafs + r_world.numLeafs; + for( leaf = r_world.leafs; leaf != lastleaf; leaf++ ) { + cluster1 = leaf->cluster; + if( cluster1 == -1 ) { + continue; + } + if( Q_IsBitSet( vis1, cluster1 ) ) { + node = ( bspNode_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 NODE_CLIPPED 0 +#define NODE_UNCLIPPED 15 + +static qboolean GL_ClipNodeToFrustum( bspNode_t *node, int *clipflags ) { + int flags = *clipflags; + int i, bits; + + for( i = 0; i < 4; i++ ) { + if( flags & ( 1 << i ) ) { + continue; + } + bits = BoxOnPlaneSide( node->mins, node->maxs, + &glr.frustumPlanes[i] ); + if( bits == BOX_BEHIND ) { + return qfalse; + } + if( bits == BOX_INFRONT ) { + flags |= 1 << i; + } + } + + *clipflags = flags; + + return qtrue; +} + +typedef void (*drawFaceFunc_t)( bspSurface_t *surf ); + +static drawFaceFunc_t drawFaceFunc; +static drawFaceFunc_t drawFaceFuncTable[] = { + GL_AddBspSurface, + GL_DrawSurfPoly +}; + +static bspSurface_t *alphaFaces; + +#define BACKFACE_EPSILON 0.001f + +#define SIDE_FRONT 0 +#define SIDE_BACK 1 + +void GL_DrawBspModel( bspSubmodel_t *model ) { + bspSurface_t *face; + int count; + vec3_t bounds[2]; + vec_t dot; + cplane_t *plane; + 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( gl_dynamic->integer ) { + GL_TransformLights( model ); + } + + /* draw visible faces */ + /* FIXME: go by headnode instead? */ + face = model->firstFace; + count = model->numFaces; + while( count-- ) { + plane = face->plane; + switch( plane->type ) { + case PLANE_X: + dot = modelViewOrigin[0] - plane->dist; + break; + case PLANE_Y: + dot = modelViewOrigin[1] - plane->dist; + break; + case PLANE_Z: + dot = modelViewOrigin[2] - plane->dist; + break; + default: + dot = DotProduct( modelViewOrigin, plane->normal ) - plane->dist; + break; + } + if( ( dot < -BACKFACE_EPSILON && face->side == SIDE_FRONT ) || + ( dot > BACKFACE_EPSILON && face->side == SIDE_BACK ) ) + { + c.facesCulled++; + } else { + if( face->texinfo->flags & (SURF_TRANS33|SURF_TRANS66) ) { + face->next = alphaFaces; + alphaFaces = face; + } else { + GL_AddBspSurface( face ); + } + c.facesDrawn++; + } + face++; + } + + 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 ); + } + GL_SortAndDrawSurfs( qtrue ); + qglPopMatrix(); + +} + +static void GL_WorldNode_r( bspNode_t *node, int clipflags ) { + bspLeaf_t *leaf; + bspSurface_t **leafFace, *face; + int count, side, area; + cplane_t *plane; + vec_t dot; + uint32 type; + + if( node->visframe != glr.visframe ) { + return; + } + + if( gl_cull_nodes->integer && clipflags != NODE_UNCLIPPED && + GL_ClipNodeToFrustum( node, &clipflags ) == qfalse ) + { + c.nodesCulled++; + return; + } + + if( !node->plane ) { + /* found a leaf */ + leaf = ( bspLeaf_t * )node; + if( leaf->contents == CONTENTS_SOLID ) { + return; + } + area = leaf->area; + if( !glr.fd.areabits || Q_IsBitSet( glr.fd.areabits, area ) ) { + leafFace = leaf->firstLeafFace; + count = leaf->numLeafFaces; + while( count-- ) { + face = *leafFace++; + face->drawframe = glr.drawframe; + } + } + return; + } + + plane = node->plane; + type = plane->type; + if( type < 3 ) { + dot = modelViewOrigin[type] - plane->dist; + } else { + dot = DotProduct( modelViewOrigin, plane->normal ) - plane->dist; + } + + side = dot < 0; + + GL_WorldNode_r( node->children[side], clipflags ); + + face = node->firstFace; + count = node->numFaces; + while( count-- ) { + if( face->drawframe == glr.drawframe ) { + if( face->side == side ) { + if( face->texinfo->flags & (SURF_TRANS33|SURF_TRANS66) ) { + face->next = alphaFaces; + alphaFaces = face; + } else { + drawFaceFunc( face ); + } + c.facesDrawn++; + } else { + c.facesCulled++; + } + } + face++; + } + + GL_WorldNode_r( node->children[ side ^ 1 ], clipflags ); + c.nodesDrawn++; +} + +void GL_DrawWorld( void ) { + GL_MarkLeaves(); + + if( gl_dynamic->integer ) { + GL_MarkLights(); + } + + R_ClearSkyBox(); + + drawFaceFunc = drawFaceFuncTable[gl_primitives->integer & 1]; + + VectorCopy( glr.fd.vieworg, modelViewOrigin ); + + GL_WorldNode_r( r_world.nodes, NODE_CLIPPED ); + GL_SortAndDrawSurfs( qtrue ); + + if( !gl_fastsky->integer ) { + R_DrawSkyBox(); + } + +} + +void GL_DrawAlphaFaces( void ) { + bspSurface_t *face, *next; + + face = alphaFaces; + if( !face ) { + return; + } + + drawFaceFunc = drawFaceFuncTable[gl_primitives->integer & 1]; + + do { + drawFaceFunc( face ); + /* Prevent loop condition in case the same face is included twice. + * This should never happen normally. */ + next = face->next; + face->next = NULL; + face = next; + } while( face ); + + GL_SortAndDrawSurfs( qfalse ); + + alphaFaces = NULL; +} |