/* 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 ; vnext; 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 max_span_p) { D_DrawSurfaces (); // clear the surface span pointers for (s = &surfaces[1] ; sspans = 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] ; sspans) 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] ; sspans) 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 (); }