/* Copyright (C) 1997-2001 Id Software, Inc. 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. */ // r_edge.c #include "r_local.h" edge_t *auxedges; edge_t *r_edges, *edge_p, *edge_max; surf_t *surfaces, *surface_p, *surf_max; // surfaces are generated in back to front order by the bsp, so if a surf // pointer is greater than another one, it should be drawn in front // surfaces[1] is the background, and is used as the active surface stack edge_t *newedges[MAXHEIGHT]; edge_t *removeedges[MAXHEIGHT]; espan_t *span_p, *max_span_p; int r_currentkey; int current_iv; int edge_head_u_shift20, edge_tail_u_shift20; static void (*pdrawfunc)( void ); edge_t edge_head; edge_t edge_tail; edge_t edge_aftertail; edge_t edge_sentinel; static float fv; static int miplevel; float scale_for_mip; // FIXME: should go away extern void R_RotateBmodel( void ); extern void R_TransformFrustum( void ); void R_GenerateSpans( void ); void R_GenerateSpansBackward( void ); void R_LeadingEdge( edge_t *edge ); void R_LeadingEdgeBackwards( edge_t *edge ); void R_TrailingEdge( surf_t *surf, edge_t *edge ); /* =============================================================================== EDGE SCANNING =============================================================================== */ /* ============== R_BeginEdgeFrame ============== */ void R_BeginEdgeFrame( void ) { int v; edge_p = r_edges; edge_max = &r_edges[r_numallocatededges]; surface_p = &surfaces[2]; // background is surface 1, // surface 0 is a dummy surfaces[1].spans = NULL; // no background spans yet surfaces[1].flags = 0; // SURF_DRAWBACKGROUND; // put the background behind everything in the world if( sw_draworder.value ) { pdrawfunc = R_GenerateSpansBackward; surfaces[1].key = 0; r_currentkey = 1; } else { pdrawfunc = R_GenerateSpans; surfaces[1].key = 0x7FFfFFFF; r_currentkey = 0; } // FIXME: set with memset for( v = RI.vrect.y; v < RI.vrectbottom; v++ ) { newedges[v] = removeedges[v] = NULL; } } /* ============== R_InsertNewEdges Adds the edges in the linked list edgestoadd, adding them to the edges in the linked list edgelist. edgestoadd is assumed to be sorted on u, and non-empty (this is actually newedges[v]). edgelist is assumed to be sorted on u, with a sentinel at the end (actually, this is the active edge table starting at edge_head.next). ============== */ static void R_InsertNewEdges( edge_t *edgestoadd, edge_t *edgelist ) { edge_t *next_edge; do { next_edge = edgestoadd->next; edgesearch: if( !edgelist ) { // gEngfuncs.Con_Printf("NULL edgelist!\n"); // return; } if( edgelist->u >= edgestoadd->u ) goto addedge; edgelist = edgelist->next; if( edgelist->u >= edgestoadd->u ) goto addedge; edgelist = edgelist->next; if( edgelist->u >= edgestoadd->u ) goto addedge; edgelist = edgelist->next; if( edgelist->u >= edgestoadd->u ) goto addedge; edgelist = edgelist->next; goto edgesearch; // insert edgestoadd before edgelist addedge: edgestoadd->next = edgelist; edgestoadd->prev = edgelist->prev; edgelist->prev->next = edgestoadd; edgelist->prev = edgestoadd; } while(( edgestoadd = next_edge ) != NULL ); } /* ============== R_RemoveEdges ============== */ static void R_RemoveEdges( edge_t *pedge ) { do { pedge->next->prev = pedge->prev; pedge->prev->next = pedge->next; } while(( pedge = pedge->nextremove ) != NULL ); } /* ============== R_StepActiveU ============== */ static void R_StepActiveU( edge_t *pedge ) { edge_t *pnext_edge, *pwedge; while( 1 ) { nextedge: pedge->u += pedge->u_step; if( pedge->u < pedge->prev->u ) goto pushback; pedge = pedge->next; pedge->u += pedge->u_step; if( pedge->u < pedge->prev->u ) goto pushback; pedge = pedge->next; pedge->u += pedge->u_step; if( pedge->u < pedge->prev->u ) goto pushback; pedge = pedge->next; pedge->u += pedge->u_step; if( pedge->u < pedge->prev->u ) goto pushback; pedge = pedge->next; goto nextedge; pushback: if( pedge == &edge_aftertail ) return; // push it back to keep it sorted pnext_edge = pedge->next; // pull the edge out of the edge list pedge->next->prev = pedge->prev; pedge->prev->next = pedge->next; // find out where the edge goes in the edge list pwedge = pedge->prev->prev; // if( !pwedge ) // return; while( pwedge->u > pedge->u ) { pwedge = pwedge->prev; // if( !pwedge ) // return; } // put the edge back into the edge list pedge->next = pwedge->next; pedge->prev = pwedge; pedge->next->prev = pedge; pwedge->next = pedge; pedge = pnext_edge; if( pedge == &edge_tail ) return; } } /* ============== R_CleanupSpan ============== */ static void R_CleanupSpan( void ) { surf_t *surf; int iu; espan_t *span; // now that we've reached the right edge of the screen, we're done with any // unfinished surfaces, so emit a span for whatever's on top surf = surfaces[1].next; iu = edge_tail_u_shift20; if( iu > surf->last_u ) { span = span_p++; span->u = surf->last_u; span->count = iu - span->u; span->v = current_iv; span->pnext = surf->spans; surf->spans = span; } // reset spanstate for all surfaces in the surface stack do { surf->spanstate = 0; surf = surf->next; } while( surf != &surfaces[1] ); } /* ============== R_LeadingEdgeBackwards ============== */ void R_LeadingEdgeBackwards( edge_t *edge ) { espan_t *span; surf_t *surf, *surf2; int iu; // it's adding a new surface in, so find the correct place surf = &surfaces[edge->surfs[1]]; // don't start a span if this is an inverted span, with the end // edge preceding the start edge (that is, we've already seen the // end edge) if( ++surf->spanstate == 1 ) { surf2 = surfaces[1].next; if( surf->key > surf2->key ) goto newtop; // if it's two surfaces on the same plane, the one that's already // active is in front, so keep going unless it's a bmodel if( surf->insubmodel && ( surf->key == surf2->key )) { // must be two bmodels in the same leaf; don't care, because they'll // never be farthest anyway goto newtop; } continue_search: do { surf2 = surf2->next; } while( surf->key < surf2->key ); if( surf->key == surf2->key ) { // if it's two surfaces on the same plane, the one that's already // active is in front, so keep going unless it's a bmodel if( !surf->insubmodel ) goto continue_search; // must be two bmodels in the same leaf; don't care which is really // in front, because they'll never be farthest anyway } goto gotposition; newtop: // emit a span (obscures current top) iu = edge->u >> 20; if( iu > surf2->last_u ) { span = span_p++; span->u = surf2->last_u; span->count = iu - span->u; span->v = current_iv; span->pnext = surf2->spans; surf2->spans = span; } // set last_u on the new span surf->last_u = iu; gotposition: // insert before surf2 surf->next = surf2; surf->prev = surf2->prev; surf2->prev->next = surf; surf2->prev = surf; } } /* ============== R_TrailingEdge ============== */ void R_TrailingEdge( surf_t *surf, edge_t *edge ) { espan_t *span; int iu; // don't generate a span if this is an inverted span, with the end // edge preceding the start edge (that is, we haven't seen the // start edge yet) if( --surf->spanstate == 0 ) { if( surf == surfaces[1].next ) { // emit a span (current top going away) iu = edge->u >> 20; if( iu > surf->last_u ) { span = span_p++; span->u = surf->last_u; span->count = iu - span->u; span->v = current_iv; span->pnext = surf->spans; surf->spans = span; } // set last_u on the surface below surf->next->last_u = iu; } surf->prev->next = surf->next; surf->next->prev = surf->prev; } } /* ============== R_LeadingEdge ============== */ void R_LeadingEdge( edge_t *edge ) { espan_t *span; surf_t *surf, *surf2; int iu; float fu, newzi, testzi, newzitop, newzibottom; if( edge->surfs[1] ) { // it's adding a new surface in, so find the correct place surf = &surfaces[edge->surfs[1]]; // don't start a span if this is an inverted span, with the end // edge preceding the start edge (that is, we've already seen the // end edge) if( ++surf->spanstate == 1 ) { surf2 = surfaces[1].next; if( surf->key < surf2->key ) goto newtop; // if it's two surfaces on the same plane, the one that's already // active is in front, so keep going unless it's a bmodel if( surf->insubmodel && ( surf->key == surf2->key )) { // must be two bmodels in the same leaf; sort on 1/z fu = (float)( edge->u - 0xFFFFF ) * ( 1.0f / 0x100000 ); newzi = surf->d_ziorigin + fv * surf->d_zistepv + fu * surf->d_zistepu; newzibottom = newzi * 0.99f; testzi = surf2->d_ziorigin + fv * surf2->d_zistepv + fu * surf2->d_zistepu; if( newzibottom >= testzi ) { goto newtop; } newzitop = newzi * 1.01f; if( newzitop >= testzi ) { if( surf->d_zistepu >= surf2->d_zistepu ) { goto newtop; } } } continue_search: do { surf2 = surf2->next; } while( surf->key > surf2->key ); if( surf->key == surf2->key ) { // if it's two surfaces on the same plane, the one that's already // active is in front, so keep going unless it's a bmodel if( !surf->insubmodel ) goto continue_search; // must be two bmodels in the same leaf; sort on 1/z fu = (float)( edge->u - 0xFFFFF ) * ( 1.0f / 0x100000 ); newzi = surf->d_ziorigin + fv * surf->d_zistepv + fu * surf->d_zistepu; newzibottom = newzi * 0.99f; testzi = surf2->d_ziorigin + fv * surf2->d_zistepv + fu * surf2->d_zistepu; if( newzibottom >= testzi ) { goto gotposition; } newzitop = newzi * 1.01f; if( newzitop >= testzi ) { if( surf->d_zistepu >= surf2->d_zistepu ) { goto gotposition; } } goto continue_search; } goto gotposition; newtop: // emit a span (obscures current top) iu = edge->u >> 20; if( iu > surf2->last_u ) { span = span_p++; span->u = surf2->last_u; span->count = iu - span->u; span->v = current_iv; span->pnext = surf2->spans; surf2->spans = span; } // set last_u on the new span surf->last_u = iu; gotposition: // insert before surf2 surf->next = surf2; surf->prev = surf2->prev; surf2->prev->next = surf; surf2->prev = surf; } } } /* ============== R_GenerateSpans ============== */ void R_GenerateSpans( void ) { edge_t *edge; surf_t *surf; // clear active surfaces to just the background surface surfaces[1].next = surfaces[1].prev = &surfaces[1]; surfaces[1].last_u = edge_head_u_shift20; // generate spans for( edge = edge_head.next; edge != &edge_tail; edge = edge->next ) { if( edge->surfs[0] ) { // it has a left surface, so a surface is going away for this span surf = &surfaces[edge->surfs[0]]; R_TrailingEdge( surf, edge ); if( !edge->surfs[1] ) continue; } R_LeadingEdge( edge ); } R_CleanupSpan(); } /* ============== R_GenerateSpansBackward ============== */ void R_GenerateSpansBackward( void ) { edge_t *edge; // clear active surfaces to just the background surface surfaces[1].next = surfaces[1].prev = &surfaces[1]; surfaces[1].last_u = edge_head_u_shift20; // generate spans for( edge = edge_head.next; edge != &edge_tail; edge = edge->next ) { if( edge->surfs[0] ) R_TrailingEdge( &surfaces[edge->surfs[0]], edge ); if( edge->surfs[1] ) R_LeadingEdgeBackwards( edge ); } R_CleanupSpan(); } /* ============== R_ScanEdges Input: newedges[] array this has links to edges, which have links to surfaces Output: Each surface has a linked list of its visible spans ============== */ void R_ScanEdges( void ) { int iv, bottom; byte basespans[MAXSPANS * sizeof( espan_t ) + CACHE_SIZE]; espan_t *basespan_p; surf_t *s; basespan_p = (espan_t *) ((uintptr_t)( basespans + CACHE_SIZE - 1 ) & ~( CACHE_SIZE - 1 )); max_span_p = &basespan_p[MAXSPANS - RI.vrect.width]; span_p = basespan_p; // clear active edges to just the background edges around the whole screen // FIXME: most of this only needs to be set up once edge_head.u = RI.vrect.x << 20; edge_head_u_shift20 = edge_head.u >> 20; edge_head.u_step = 0; edge_head.prev = NULL; edge_head.next = &edge_tail; edge_head.surfs[0] = 0; edge_head.surfs[1] = 1; edge_tail.u = ( RI.vrectright << 20 ) + 0xFFFFF; // (r_refdef.vrectright << 20) + 0xFFFFF; edge_tail_u_shift20 = edge_tail.u >> 20; edge_tail.u_step = 0; edge_tail.prev = &edge_head; edge_tail.next = &edge_aftertail; edge_tail.surfs[0] = 1; edge_tail.surfs[1] = 0; edge_aftertail.u = -1; // force a move edge_aftertail.u_step = 0; edge_aftertail.next = &edge_sentinel; edge_aftertail.prev = &edge_tail; // FIXME: do we need this now that we clamp x in r_draw.c? edge_sentinel.u = 2000 << 20; // make sure nothing sorts past this edge_sentinel.prev = &edge_aftertail; // // process all scan lines // bottom = RI.vrectbottom - 1; for( iv = 0; iv < bottom; iv++ ) { current_iv = iv; fv = (float)iv; // mark that the head (background start) span is pre-included surfaces[1].spanstate = 1; if( newedges[iv] ) { R_InsertNewEdges( newedges[iv], edge_head.next ); } ( *pdrawfunc )(); // flush the span list if we can't be sure we have enough spans left for // the next scan if( span_p > max_span_p ) { D_DrawSurfaces(); // clear the surface span pointers for( s = &surfaces[1]; s < surface_p; s++ ) s->spans = NULL; span_p = basespan_p; } if( removeedges[iv] ) R_RemoveEdges( removeedges[iv] ); if( edge_head.next != &edge_tail ) R_StepActiveU( edge_head.next ); } // do the last scan (no need to step or sort or remove on the last scan) current_iv = iv; fv = (float)iv; // mark that the head (background start) span is pre-included surfaces[1].spanstate = 1; if( newedges[iv] ) R_InsertNewEdges( newedges[iv], edge_head.next ); ( *pdrawfunc )(); // draw whatever's left in the span list D_DrawSurfaces(); } /* ========================================================================= SURFACE FILLING ========================================================================= */ msurface_t *pface; surfcache_t *pcurrentcache; vec3_t transformed_modelorg; vec3_t world_transformed_modelorg; vec3_t local_modelorg; /* ============= D_MipLevelForScale ============= */ static int D_MipLevelForScale( float scale ) { int lmiplevel; if( scale >= d_scalemip[0] ) lmiplevel = 0; else if( scale >= d_scalemip[1] ) lmiplevel = 1; else if( scale >= d_scalemip[2] ) lmiplevel = 2; else lmiplevel = 3; if( lmiplevel < d_minmip ) lmiplevel = d_minmip; return lmiplevel; } /* ============== D_FlatFillSurface Simple single color fill with no texture mapping ============== */ static void D_FlatFillSurface( surf_t *surf, int color ) { espan_t *span; pixel_t *pdest; int u, u2; for( span = surf->spans; span; span = span->pnext ) { pdest = d_viewbuffer + r_screenwidth * span->v; u = span->u; u2 = span->u + span->count - 1; for( ; u <= u2; u++ ) pdest[u] = color; } } /* ============== D_CalcGradients ============== */ static void D_CalcGradients( msurface_t *pface ) { mplane_t *pplane; float mipscale; vec3_t p_temp1; vec3_t p_saxis, p_taxis; float t; pplane = pface->plane; mipscale = 1.0f / (float)( 1 << miplevel ); if( pface->texinfo->flags & TEX_WORLD_LUXELS ) { TransformVector( pface->texinfo->vecs[0], p_saxis ); TransformVector( pface->texinfo->vecs[1], p_taxis ); } else { TransformVector( pface->info->lmvecs[0], p_saxis ); TransformVector( pface->info->lmvecs[1], p_taxis ); } t = xscaleinv * mipscale; d_sdivzstepu = p_saxis[0] * t; d_tdivzstepu = p_taxis[0] * t; t = yscaleinv * mipscale; d_sdivzstepv = -p_saxis[1] * t; d_tdivzstepv = -p_taxis[1] * t; d_sdivzorigin = p_saxis[2] * mipscale - xcenter * d_sdivzstepu - ycenter * d_sdivzstepv; d_tdivzorigin = p_taxis[2] * mipscale - xcenter * d_tdivzstepu - ycenter * d_tdivzstepv; VectorScale( transformed_modelorg, mipscale, p_temp1 ); t = 0x10000 * mipscale; if( pface->texinfo->flags & TEX_WORLD_LUXELS ) { sadjust = ((fixed16_t)( DotProduct( p_temp1, p_saxis ) * 0x10000 + 0.5f )) - (( pface->texturemins[0] << 16 ) >> miplevel ) + pface->texinfo->vecs[0][3] * t; tadjust = ((fixed16_t)( DotProduct( p_temp1, p_taxis ) * 0x10000 + 0.5f )) - (( pface->texturemins[1] << 16 ) >> miplevel ) + pface->texinfo->vecs[1][3] * t; } else { sadjust = ((fixed16_t)( DotProduct( p_temp1, p_saxis ) * 0x10000 + 0.5f )) - (( pface->info->lightmapmins[0] << 16 ) >> miplevel ) + pface->info->lmvecs[0][3] * t; tadjust = ((fixed16_t)( DotProduct( p_temp1, p_taxis ) * 0x10000 + 0.5f )) - (( pface->info->lightmapmins[1] << 16 ) >> miplevel ) + pface->info->lmvecs[1][3] * t; } // PGM - changing flow speed for non-warping textures. if( pface->flags & SURF_CONVEYOR ) { if( pface->flags & SURF_DRAWTURB ) sadjust += 0x10000 * ( -128 * (( gp_cl->time * 0.25f ) - (int)( gp_cl->time * 0.25f ))); else sadjust += 0x10000 * ( -128 * (( gp_cl->time * 0.77f ) - (int)( gp_cl->time * 0.77f ))); bbextents = (( pface->extents[0] << 16 ) >> miplevel ) - 1; } else bbextents = (( pface->info->lightextents[0] << 16 ) >> miplevel ) - 1; bbextentt = (( pface->info->lightextents[1] << 16 ) >> miplevel ) - 1; if( pface->texinfo->flags & TEX_WORLD_LUXELS ) { bbextents = (( pface->extents[0] << 16 ) >> miplevel ) - 1; bbextentt = (( pface->extents[1] << 16 ) >> miplevel ) - 1; } } /* ============== D_BackgroundSurf The grey background filler seen when there is a hole in the map ============== */ static void D_BackgroundSurf( surf_t *s ) { // set up a gradient for the background surface that places it // effectively at infinity distance from the viewpoint d_zistepu = 0; d_zistepv = 0; d_ziorigin = -0.9; D_FlatFillSurface( s, (int)sw_clearcolor.value & 0xFFFF ); D_DrawZSpans( s->spans ); } /* ================= D_TurbulentSurf ================= */ static void D_TurbulentSurf( surf_t *s ) { d_zistepu = s->d_zistepu; d_zistepv = s->d_zistepv; d_ziorigin = s->d_ziorigin; pface = s->msurf; miplevel = 0; cacheblock = R_GetTexture( pface->texinfo->texture->gl_texturenum )->pixels[0]; cachewidth = 64; if( s->insubmodel ) { // FIXME: we don't want to do all this for every polygon! // TODO: store once at start of frame RI.currententity = s->entity; // FIXME: make this passed in to // R_RotateBmodel () VectorSubtract( RI.vieworg, RI.currententity->origin, local_modelorg ); TransformVector( local_modelorg, transformed_modelorg ); R_RotateBmodel(); // FIXME: don't mess with the frustum, // make entity passed in } D_CalcGradients( pface ); // ============ // PGM // textures that aren't warping are just flowing. Use NonTurbulent8 instead if( !( pface->flags & SURF_DRAWTURB )) NonTurbulent8( s->spans ); else Turbulent8( s->spans ); // PGM // ============ D_DrawZSpans( s->spans ); if( s->insubmodel ) { // // restore the old drawing state // FIXME: we don't want to do this every time! // TODO: speed up // RI.currententity = NULL; // &r_worldentity; VectorCopy( world_transformed_modelorg, transformed_modelorg ); VectorCopy( RI.base_vpn, RI.vforward ); VectorCopy( RI.base_vup, RI.vup ); VectorCopy( RI.base_vright, RI.vright ); R_TransformFrustum(); } } qboolean alphaspans; /* ============== D_SolidSurf Normal surface cached, texture mapped surface ============== */ static void D_AlphaSurf( surf_t *s ) { int alpha; d_zistepu = s->d_zistepu; d_zistepv = s->d_zistepv; d_ziorigin = s->d_ziorigin; if( s->flags & SURF_DRAWSKY ) return; if( !s->insubmodel )// wtf? how it is possible? return; // FIXME: we don't want to do all this for every polygon! // TODO: store once at start of frame RI.currententity = s->entity; // FIXME: make this passed in to // R_RotateBmodel () VectorSubtract( RI.vieworg, RI.currententity->origin, local_modelorg ); TransformVector( local_modelorg, transformed_modelorg ); R_RotateBmodel(); // FIXME: don't mess with the frustum, // make entity passed in pface = s->msurf; if( !pface ) return; if( pface->flags & SURF_CONVEYOR ) miplevel = 1; else miplevel = 0; alpha = RI.currententity->curstate.renderamt * 7 / 255; if( alpha <= 0 && RI.currententity->curstate.renderamt > 0 ) alpha = 1; if( s->flags & SURF_DRAWTURB ) { cacheblock = R_GetTexture( pface->texinfo->texture->gl_texturenum )->pixels[0]; cachewidth = 64; D_CalcGradients( pface ); TurbulentZ8( s->spans, alpha ); } else { // FIXME: make this passed in to D_CacheSurface pcurrentcache = D_CacheSurface( pface, miplevel ); cacheblock = (pixel_t *)pcurrentcache->data; cachewidth = pcurrentcache->width; D_CalcGradients( pface ); if( RI.currententity->curstate.rendermode == kRenderTransAlpha ) D_AlphaSpans16( s->spans ); else if( RI.currententity->curstate.rendermode == kRenderTransAdd ) D_AddSpans16( s->spans ); else D_BlendSpans16( s->spans, alpha ); } VectorCopy( world_transformed_modelorg, transformed_modelorg ); VectorCopy( RI.base_vpn, RI.vforward ); VectorCopy( RI.base_vup, RI.vup ); VectorCopy( RI.base_vright, RI.vright ); R_TransformFrustum(); } /* ============== D_SolidSurf Normal surface cached, texture mapped surface ============== */ static void D_SolidSurf( surf_t *s ) { d_zistepu = s->d_zistepu; d_zistepv = s->d_zistepv; d_ziorigin = s->d_ziorigin; if( s->flags & SURF_DRAWSKY ) return; if( s->flags & SURF_DRAWTURB ) return; if( s->insubmodel ) { // FIXME: we don't want to do all this for every polygon! // TODO: store once at start of frame RI.currententity = s->entity; // FIXME: make this passed in to // R_RotateBmodel () VectorSubtract( RI.vieworg, RI.currententity->origin, local_modelorg ); TransformVector( local_modelorg, transformed_modelorg ); R_RotateBmodel(); // FIXME: don't mess with the frustum, // make entity passed in // setup dlight transform if( s->msurf && s->msurf->dlightframe == tr.framecount ) { Matrix4x4_CreateFromEntity( RI.objectMatrix, RI.currententity->angles, RI.currententity->origin, 1 ); tr.modelviewIdentity = false; } } else { if( alphaspans ) return; RI.currententity = CL_GetEntityByIndex( 0 ); // r_worldentity; tr.modelviewIdentity = true; } pface = s->msurf; if( !pface ) return; if( pface->flags & SURF_CONVEYOR ) miplevel = 1; else miplevel = D_MipLevelForScale( s->nearzi * scale_for_mip ); while( 1 << miplevel > gEngfuncs.Mod_SampleSizeForFace( pface )) miplevel--; // FIXME: make this passed in to D_CacheSurface pcurrentcache = D_CacheSurface( pface, miplevel ); cacheblock = (pixel_t *)pcurrentcache->data; cachewidth = pcurrentcache->width; D_CalcGradients( pface ); D_DrawSpans16( s->spans ); D_DrawZSpans( s->spans ); if( s->insubmodel ) { // // restore the old drawing state // FIXME: we don't want to do this every time! // TODO: speed up // VectorCopy( world_transformed_modelorg, transformed_modelorg ); VectorCopy( RI.base_vpn, RI.vforward ); VectorCopy( RI.base_vup, RI.vup ); VectorCopy( RI.base_vright, RI.vright ); R_TransformFrustum(); RI.currententity = NULL; // &r_worldentity; } } /* ============= D_DrawflatSurfaces To allow developers to see the polygon carving of the world ============= */ static void D_DrawflatSurfaces( void ) { surf_t *s; for( s = &surfaces[1]; s < surface_p; s++ ) { if( !s->spans ) continue; d_zistepu = s->d_zistepu; d_zistepv = s->d_zistepv; d_ziorigin = s->d_ziorigin; // make a stable color for each surface by taking the low // bits of the msurface pointer D_FlatFillSurface( s, (uintptr_t)s->msurf & 0xFFFF ); D_DrawZSpans( s->spans ); } } /* ============== D_DrawSurfaces Rasterize all the span lists. Guaranteed zero overdraw. May be called more than once a frame if the surf list overflows (higher res) ============== */ void D_DrawSurfaces( void ) { surf_t *s; // currententity = NULL; //&r_worldentity; VectorSubtract( RI.vieworg, vec3_origin, tr.modelorg ); TransformVector( tr.modelorg, transformed_modelorg ); VectorCopy( transformed_modelorg, world_transformed_modelorg ); if( !sw_drawflat.value ) { for( s = &surfaces[1]; s < surface_p; s++ ) { if( !s->spans ) continue; if( alphaspans ) D_AlphaSurf( s ); else if( s->flags & SURF_DRAWSKY ) D_BackgroundSurf( s ); else if( s->flags & SURF_DRAWTURB ) D_TurbulentSurf( s ); else D_SolidSurf( s ); } } else D_DrawflatSurfaces(); // RI.currententity = NULL; //&r_worldentity; VectorSubtract( RI.vieworg, vec3_origin, tr.modelorg ); R_TransformFrustum(); }