diff --git a/r_bsp.c b/r_bsp.c new file mode 100644 index 00000000..4f1c49ef --- /dev/null +++ b/r_bsp.c @@ -0,0 +1,626 @@ +/* +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_bsp.c + +#include "r_local.h" + +// +// current entity info +// +qboolean insubmodel; +vec3_t modelorg; // modelorg is the viewpoint reletive to + // the currently rendering entity +vec3_t r_entorigin; // the currently rendering entity in world + // coordinates + +float entity_rotation[3][3]; + +int r_currentbkey; + +typedef enum {touchessolid, drawnode, nodrawnode} solidstate_t; + +#define MAX_BMODEL_VERTS 500 // 6K +#define MAX_BMODEL_EDGES 1000 // 12K + +static mvertex_t *pbverts; +static bedge_t *pbedges; +static int numbverts, numbedges; + +static mvertex_t *pfrontenter, *pfrontexit; + +static qboolean makeclippededge; + + +//=========================================================================== + +/* +================ +R_EntityRotate +================ +*/ +void R_EntityRotate (vec3_t vec) +{ + vec3_t tvec; + + VectorCopy (vec, tvec); + vec[0] = DotProduct (entity_rotation[0], tvec); + vec[1] = DotProduct (entity_rotation[1], tvec); + vec[2] = DotProduct (entity_rotation[2], tvec); +} + + +/* +================ +R_RotateBmodel +================ +*/ +void R_RotateBmodel (void) +{ + float angle, s, c, temp1[3][3], temp2[3][3], temp3[3][3]; + +// TODO: should use a look-up table +// TODO: should really be stored with the entity instead of being reconstructed +// TODO: could cache lazily, stored in the entity +// TODO: share work with R_SetUpAliasTransform + +// yaw + angle = RI.currententity->angles[YAW]; + angle = angle * M_PI*2 / 360; + s = sin(angle); + c = cos(angle); + + temp1[0][0] = c; + temp1[0][1] = s; + temp1[0][2] = 0; + temp1[1][0] = -s; + temp1[1][1] = c; + temp1[1][2] = 0; + temp1[2][0] = 0; + temp1[2][1] = 0; + temp1[2][2] = 1; + + +// pitch + angle = RI.currententity->angles[PITCH]; + angle = angle * M_PI*2 / 360; + s = sin(angle); + c = cos(angle); + + temp2[0][0] = c; + temp2[0][1] = 0; + temp2[0][2] = -s; + temp2[1][0] = 0; + temp2[1][1] = 1; + temp2[1][2] = 0; + temp2[2][0] = s; + temp2[2][1] = 0; + temp2[2][2] = c; + + R_ConcatRotations (temp2, temp1, temp3); + +// roll + angle = RI.currententity->angles[ROLL]; + angle = angle * M_PI*2 / 360; + s = sin(angle); + c = cos(angle); + + temp1[0][0] = 1; + temp1[0][1] = 0; + temp1[0][2] = 0; + temp1[1][0] = 0; + temp1[1][1] = c; + temp1[1][2] = s; + temp1[2][0] = 0; + temp1[2][1] = -s; + temp1[2][2] = c; + + R_ConcatRotations (temp1, temp3, entity_rotation); + +// +// rotate modelorg and the transformation matrix +// + R_EntityRotate (modelorg); + R_EntityRotate (vpn); + R_EntityRotate (vright); + R_EntityRotate (vup); + + R_TransformFrustum (); +} + + +/* +================ +R_RecursiveClipBPoly + +Clip a bmodel poly down the world bsp tree +================ +*/ +void R_RecursiveClipBPoly (bedge_t *pedges, mnode_t *pnode, msurface_t *psurf) +{ + bedge_t *psideedges[2], *pnextedge, *ptedge; + int i, side, lastside; + float dist, frac, lastdist; + mplane_t *splitplane, tplane; + mvertex_t *pvert, *plastvert, *ptvert; + mnode_t *pn; + int area; + + psideedges[0] = psideedges[1] = NULL; + + makeclippededge = false; + +// transform the BSP plane into model space +// FIXME: cache these? + splitplane = pnode->plane; + tplane.dist = splitplane->dist - + DotProduct(r_entorigin, splitplane->normal); + tplane.normal[0] = DotProduct (entity_rotation[0], splitplane->normal); + tplane.normal[1] = DotProduct (entity_rotation[1], splitplane->normal); + tplane.normal[2] = DotProduct (entity_rotation[2], splitplane->normal); + +// clip edges to BSP plane + for ( ; pedges ; pedges = pnextedge) + { + pnextedge = pedges->pnext; + + // set the status for the last point as the previous point + // FIXME: cache this stuff somehow? + plastvert = pedges->v[0]; + lastdist = DotProduct (plastvert->position, tplane.normal) - + tplane.dist; + + if (lastdist > 0) + lastside = 0; + else + lastside = 1; + + pvert = pedges->v[1]; + + dist = DotProduct (pvert->position, tplane.normal) - tplane.dist; + + if (dist > 0) + side = 0; + else + side = 1; + + if (side != lastside) + { + // clipped + if (numbverts >= MAX_BMODEL_VERTS) + return; + + // generate the clipped vertex + frac = lastdist / (lastdist - dist); + ptvert = &pbverts[numbverts++]; + ptvert->position[0] = plastvert->position[0] + + frac * (pvert->position[0] - + plastvert->position[0]); + ptvert->position[1] = plastvert->position[1] + + frac * (pvert->position[1] - + plastvert->position[1]); + ptvert->position[2] = plastvert->position[2] + + frac * (pvert->position[2] - + plastvert->position[2]); + + // split into two edges, one on each side, and remember entering + // and exiting points + // FIXME: share the clip edge by having a winding direction flag? + if (numbedges >= (MAX_BMODEL_EDGES - 1)) + { + gEngfuncs.Con_Printf ("Out of edges for bmodel\n"); + return; + } + + ptedge = &pbedges[numbedges]; + ptedge->pnext = psideedges[lastside]; + psideedges[lastside] = ptedge; + ptedge->v[0] = plastvert; + ptedge->v[1] = ptvert; + + ptedge = &pbedges[numbedges + 1]; + ptedge->pnext = psideedges[side]; + psideedges[side] = ptedge; + ptedge->v[0] = ptvert; + ptedge->v[1] = pvert; + + numbedges += 2; + + if (side == 0) + { + // entering for front, exiting for back + pfrontenter = ptvert; + makeclippededge = true; + } + else + { + pfrontexit = ptvert; + makeclippededge = true; + } + } + else + { + // add the edge to the appropriate side + pedges->pnext = psideedges[side]; + psideedges[side] = pedges; + } + } + +// if anything was clipped, reconstitute and add the edges along the clip +// plane to both sides (but in opposite directions) + if (makeclippededge) + { + if (numbedges >= (MAX_BMODEL_EDGES - 2)) + { + gEngfuncs.Con_Printf ("Out of edges for bmodel\n"); + return; + } + + ptedge = &pbedges[numbedges]; + ptedge->pnext = psideedges[0]; + psideedges[0] = ptedge; + ptedge->v[0] = pfrontexit; + ptedge->v[1] = pfrontenter; + + ptedge = &pbedges[numbedges + 1]; + ptedge->pnext = psideedges[1]; + psideedges[1] = ptedge; + ptedge->v[0] = pfrontenter; + ptedge->v[1] = pfrontexit; + + numbedges += 2; + } + // draw or recurse further + for (i=0 ; i<2 ; i++) + { + if (psideedges[i]) + { + // draw if we've reached a non-solid leaf, done if all that's left is a + // solid leaf, and continue down the tree if it's not a leaf + pn = pnode->children[i]; + + // we're done with this branch if the node or leaf isn't in the PVS + if (pn->visframe == r_visframecount) + { + if (pn->contents < 0) + { + if (pn->contents != CONTENTS_SOLID) + { + r_currentbkey = ((mleaf_t *)pn)->cluster; + R_RenderBmodelFace (psideedges[i], psurf); + } + } + else + { + R_RecursiveClipBPoly (psideedges[i], pnode->children[i], + psurf); + } + } + } + } + +} + + +/* +================ +R_DrawSolidClippedSubmodelPolygons + +Bmodel crosses multiple leafs +================ +*/ +void R_DrawSolidClippedSubmodelPolygons (model_t *pmodel, mnode_t *topnode) +{ + int i, j, lindex; + vec_t dot; + msurface_t *psurf; + int numsurfaces; + mplane_t *pplane; + mvertex_t bverts[MAX_BMODEL_VERTS]; + bedge_t bedges[MAX_BMODEL_EDGES], *pbedge; + medge_t *pedge, *pedges; + +// FIXME: use bounding-box-based frustum clipping info? + + psurf = &pmodel->surfaces[pmodel->firstmodelsurface]; + numsurfaces = pmodel->nummodelsurfaces; + pedges = pmodel->edges; + + for (i=0 ; iplane; + + dot = DotProduct (modelorg, pplane->normal) - pplane->dist; + + // draw the polygon + if (( !(psurf->flags & SURF_PLANEBACK) && (dot < -BACKFACE_EPSILON)) || + ((psurf->flags & SURF_PLANEBACK) && (dot > BACKFACE_EPSILON))) + continue; + + // FIXME: use bounding-box-based frustum clipping info? + + // copy the edges to bedges, flipping if necessary so always + // clockwise winding + // FIXME: if edges and vertices get caches, these assignments must move + // outside the loop, and overflow checking must be done here + pbverts = bverts; + pbedges = bedges; + numbverts = numbedges = 0; + pbedge = &bedges[numbedges]; + numbedges += psurf->numedges; + + for (j=0 ; jnumedges ; j++) + { + lindex = pmodel->surfedges[psurf->firstedge+j]; + + if (lindex > 0) + { + pedge = &pedges[lindex]; + pbedge[j].v[0] = &r_pcurrentvertbase[pedge->v[0]]; + pbedge[j].v[1] = &r_pcurrentvertbase[pedge->v[1]]; + } + else + { + lindex = -lindex; + pedge = &pedges[lindex]; + pbedge[j].v[0] = &r_pcurrentvertbase[pedge->v[1]]; + pbedge[j].v[1] = &r_pcurrentvertbase[pedge->v[0]]; + } + + pbedge[j].pnext = &pbedge[j+1]; + } + + pbedge[j-1].pnext = NULL; // mark end of edges + + //if ( !( psurf->texinfo->flags & ( SURF_TRANS66 | SURF_TRANS33 ) ) ) + R_RecursiveClipBPoly (pbedge, topnode, psurf); + //else + // R_RenderBmodelFace( pbedge, psurf ); + } +} + + +/* +================ +R_DrawSubmodelPolygons + +All in one leaf +================ +*/ +void R_DrawSubmodelPolygons (model_t *pmodel, int clipflags, mnode_t *topnode) +{ + int i; + vec_t dot; + msurface_t *psurf; + int numsurfaces; + mplane_t *pplane; + +// FIXME: use bounding-box-based frustum clipping info? + + psurf = &pmodel->surfaces[pmodel->firstmodelsurface]; + numsurfaces = pmodel->nummodelsurfaces; + + for (i=0 ; iplane; + + dot = DotProduct (modelorg, pplane->normal) - pplane->dist; + + // draw the polygon + if (((psurf->flags & SURF_PLANEBACK) && (dot < -BACKFACE_EPSILON)) || + (!(psurf->flags & SURF_PLANEBACK) && (dot > BACKFACE_EPSILON))) + { + r_currentkey = ((mleaf_t *)topnode)->cluster; + + // FIXME: use bounding-box-based frustum clipping info? + R_RenderFace (psurf, clipflags); + } + } +} + + +int c_drawnode; + +/* +================ +R_RecursiveWorldNode +================ +*/ +void R_RecursiveWorldNode (mnode_t *node, int clipflags) +{ + int i, c, side, *pindex; + vec3_t acceptpt, rejectpt; + mplane_t *plane; + msurface_t *surf, **mark; + mleaf_t *pleaf; + double d, dot; + + if (node->contents == CONTENTS_SOLID) + return; // solid + + if (node->visframe != r_visframecount) + return; + +// cull the clipping planes if not trivial accept +// FIXME: the compiler is doing a lousy job of optimizing here; it could be +// twice as fast in ASM + if (clipflags) + { + for (i=0 ; i<4 ; i++) + { + if (! (clipflags & (1<minmaxs[pindex[0]]; + rejectpt[1] = (float)node->minmaxs[pindex[1]]; + rejectpt[2] = (float)node->minmaxs[pindex[2]]; + + d = DotProduct (rejectpt, view_clipplanes[i].normal); + d -= view_clipplanes[i].dist; + + if (d <= 0) + return; + + acceptpt[0] = (float)node->minmaxs[pindex[3+0]]; + acceptpt[1] = (float)node->minmaxs[pindex[3+1]]; + acceptpt[2] = (float)node->minmaxs[pindex[3+2]]; + + d = DotProduct (acceptpt, view_clipplanes[i].normal); + d -= view_clipplanes[i].dist; + + if (d >= 0) + clipflags &= ~(1<contents < 0) + { + pleaf = (mleaf_t *)node; + + mark = pleaf->firstmarksurface; + c = pleaf->nummarksurfaces; + + if (c) + { + do + { + (*mark)->visframe = r_framecount; + mark++; + } while (--c); + } + + // deal with model fragments in this leaf + if (pleaf->efrags) + { + gEngfuncs.R_StoreEfrags(&pleaf->efrags,tr.realframecount); + } + + pleaf->cluster = r_currentkey; + r_currentkey++; // all bmodels in a leaf share the same key + } + else + { + // node is just a decision point, so go down the apropriate sides + + // find which side of the node we are on + plane = node->plane; + + switch (plane->type) + { + case PLANE_X: + dot = modelorg[0] - plane->dist; + break; + case PLANE_Y: + dot = modelorg[1] - plane->dist; + break; + case PLANE_Z: + dot = modelorg[2] - plane->dist; + break; + default: + dot = DotProduct (modelorg, plane->normal) - plane->dist; + break; + } + + if (dot >= 0) + side = 0; + else + side = 1; + + // recurse down the children, front side first + R_RecursiveWorldNode (node->children[side], clipflags); + + // draw stuff + c = node->numsurfaces; + + if (c) + { + surf = WORLDMODEL->surfaces + node->firstsurface; + + if (dot < -BACKFACE_EPSILON) + { + do + { + if ((surf->flags & SURF_PLANEBACK) && + (surf->visframe == r_framecount)) + { + R_RenderFace (surf, clipflags); + } + + surf++; + } while (--c); + } + else if (dot > BACKFACE_EPSILON) + { + do + { + if (!(surf->flags & SURF_PLANEBACK) && + (surf->visframe == r_framecount)) + { + R_RenderFace (surf, clipflags); + } + + surf++; + } while (--c); + } + + // all surfaces on the same node share the same sequence number + r_currentkey++; + } + + // recurse down the back side + R_RecursiveWorldNode (node->children[!side], clipflags); + } +} + +/* +================ +R_RenderWorld +================ +*/ +void R_RenderWorld (void) +{ + + //if (!r_drawworld->value) + // return; + if ( !RI.drawWorld ) + return; + + c_drawnode=0; + + // auto cycle the world frame for texture animation + RI.currententity = gEngfuncs.GetEntityByIndex(0); + //RI.currententity->frame = (int)(gpGlobals->time*2); + + VectorCopy (r_origin, modelorg); + RI.currentmodel = WORLDMODEL; + r_pcurrentvertbase = RI.currentmodel->vertexes; + + R_RecursiveWorldNode (RI.currentmodel->nodes, 15); +} + + diff --git a/r_context.c b/r_context.c index 7c22764e..48e7b997 100644 --- a/r_context.c +++ b/r_context.c @@ -22,6 +22,10 @@ gl_globals_t tr; ref_speeds_t r_stats; byte *r_temppool; cvar_t *gl_emboss_scale; +cvar_t *r_drawentities; +cvar_t *r_norefresh; +cvar_t *vid_gamma; +cvar_t *vid_brightness; viddef_t vid; static void R_ClearScreen( void ) { @@ -319,7 +323,7 @@ void GL_SetTexCoordArrayMode() { } -void R_InitBlit(); + void GL_OnContextCreated() { R_InitBlit(); @@ -333,30 +337,6 @@ void GL_InitExtensions() void GL_ClearExtensions() { -} -void R_BeginFrame(qboolean clearScene) -{ -} - -void R_RenderScene() -{ - -} - -void R_EndFrame() -{ - // blit pixels with GL until engine supports REF_SOFT context - R_BlitScreen(); -} - -void R_PushScene() -{ - -} - -void R_PopScene() -{ - } void GL_BackendStartFrame() @@ -369,10 +349,6 @@ void GL_BackendEndFrame() } -void R_AllowFog(qboolean allowed) -{ - -} void GL_SetRenderMode(int mode) { @@ -381,12 +357,6 @@ void GL_SetRenderMode(int mode) /// maybe, setup block drawing function pointers here } -qboolean R_AddEntity(struct cl_entity_s *pRefEntity, int entityType) -{ - // no entities support until we draw world... - return false; -} - void CL_AddCustomBeam(cl_entity_t *pEnvBeam) { // same for beams @@ -417,12 +387,6 @@ qboolean VID_CubemapShot(const char *base, uint size, const float *vieworg, qboo // cubemaps? in my softrender??? } -colorVec R_LightPoint(const vec3_t p0) -{ - colorVec c = {0}; - return c; -} - void R_DecalShoot(int textureIndex, int entityIndex, int modelIndex, vec3_t pos, int flags, float scale) { @@ -468,11 +432,6 @@ void GL_SubdivideSurface(msurface_t *fa) } -void CL_RunLightStyles() -{ - -} - void Mod_LoadMapSprite(model_t *mod, const void *buffer, size_t size, qboolean *loaded) { @@ -558,17 +517,6 @@ struct mstudiotex_s *R_StudioGetTexture(cl_entity_t *e) return NULL; } -colorVec R_LightVec(const vec3_t start, const vec3_t end, vec3_t lightspot, vec3_t lightvec) -{ - colorVec v = {0}; - return v; -} - -int R_RenderFrame(const struct ref_viewpass_s *vp) -{ - -} - void GL_BuildLightmaps() { @@ -589,16 +537,6 @@ byte *Mod_GetCurrentVis() return NULL; } -void R_ClearScene() -{ - -} - -void R_NewMap() -{ - -} - void R_ScreenToWorld(const vec3_t screen, vec3_t point) { @@ -617,39 +555,6 @@ void GL_SetupAttributes( int safegl ) gEngfuncs.GL_SetAttribute( REF_GL_BLUE_SIZE, 5 ); } - - - -qboolean R_Init() -{ - gl_emboss_scale = gEngfuncs.Cvar_Get( "gl_emboss_scale", "0", FCVAR_ARCHIVE|FCVAR_LATCH, "fake bumpmapping scale" ); - // create the window and set up the context - r_temppool = Mem_AllocPool( "ref_sw zone" ); - - vid.width = 1920; - vid.height = 1080; - vid.rowbytes = 1920; // rowpixels - - vid.buffer = Mem_Malloc( r_temppool, 1920*1080*sizeof( pixel_t ) ); - if( !gEngfuncs.R_Init_Video( REF_GL )) // request GL context - { - gEngfuncs.R_Free_Video(); - - gEngfuncs.Host_Error( "Can't initialize video subsystem\nProbably driver was not installed" ); - return false; - } - - - R_InitImages(); - return true; -} - -void R_Shutdown() -{ - R_ShutdownImages(); - gEngfuncs.R_Free_Video(); -} - ref_interface_t gReffuncs = { R_Init, diff --git a/r_draw.c b/r_draw.c index 53dd1776..05f4d10b 100644 --- a/r_draw.c +++ b/r_draw.c @@ -151,7 +151,7 @@ void R_DrawStretchPicImplementation (int x, int y, int w, int h, int s1, int t1, } else if( alpha < 7) // && (vid.rendermode == kRenderTransAlpha || vid.rendermode == kRenderTransTexture ) ) { - pixel_t screen = dest[u]; + pixel_t screen = dest[u]; // | 0xff & screen & src ; dest[u] = vid.alphamap[( alpha << 16)|(src & 0xff00)|(screen>>8)] << 8 | (screen & 0xff) >> 3 | ((src & 0xff) >> 3); } @@ -181,6 +181,12 @@ void R_DrawStretchPic( float x, float y, float w, float h, float s1, float t1, f R_DrawStretchPicImplementation(x,y,w,h, pic->width * s1, pic->height * t1, pic->width * s2, pic->height * t2, pic); } +void Draw_Fill (int x, int y, int w, int h, int c) +{ + // todo: color + R_DrawStretchPicImplementation(x,y,w,h, 0, 0, 12, 1, tr.whiteTexture ); +} + /* ============= Draw_TileClear diff --git a/r_edge.c b/r_edge.c new file mode 100644 index 00000000..ff3984cb --- /dev/null +++ b/r_edge.c @@ -0,0 +1,1132 @@ +/* +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" + +#ifndef id386 +void R_SurfacePatch (void) +{ +} + +void R_EdgeCodeStart (void) +{ +} + +void R_EdgeCodeEnd (void) +{ +} +#endif + + +#if 0 +the complex cases add new polys on most lines, so dont optimize for keeping them the same +have multiple free span lists to try to get better coherence? +low depth complexity -- 1 to 3 or so + +have a sentinal at both ends? +#endif + + +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; + +float fv; + +static int miplevel; + +float scale_for_mip; +int ubasestep, errorterm, erroradjustup, erroradjustdown; + +// 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=0 ; vheight ; v++) + { + newedges[v] = removeedges[v] = NULL; + } +} + + +#if !id386 + +/* +============== +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). +============== +*/ +void R_InsertNewEdges (edge_t *edgestoadd, edge_t *edgelist) +{ + edge_t *next_edge; + + do + { + next_edge = edgestoadd->next; +edgesearch: + 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); +} + +#endif // !id386 + + +#if !id386 + +/* +============== +R_RemoveEdges +============== +*/ +void R_RemoveEdges (edge_t *pedge) +{ + + do + { + pedge->next->prev = pedge->prev; + pedge->prev->next = pedge->next; + } while ((pedge = pedge->nextremove) != NULL); +} + +#endif // !id386 + + +#if !id386 + +/* +============== +R_StepActiveU +============== +*/ +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; + + while (pwedge->u > pedge->u) + { + pwedge = pwedge->prev; + } + + // 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; + } +} + +#endif // !id386 + + +/* +============== +R_CleanupSpan +============== +*/ +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; + } +} + + +#if !id386 + +/* +============== +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.0 / 0x100000); + newzi = surf->d_ziorigin + fv*surf->d_zistepv + + fu*surf->d_zistepu; + newzibottom = newzi * 0.99; + + testzi = surf2->d_ziorigin + fv*surf2->d_zistepv + + fu*surf2->d_zistepu; + + if (newzibottom >= testzi) + { + goto newtop; + } + + newzitop = newzi * 1.01; + 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.0 / 0x100000); + newzi = surf->d_ziorigin + fv*surf->d_zistepv + + fu*surf->d_zistepu; + newzibottom = newzi * 0.99; + + testzi = surf2->d_ziorigin + fv*surf2->d_zistepv + + fu*surf2->d_zistepu; + + if (newzibottom >= testzi) + { + goto gotposition; + } + + newzitop = newzi * 1.01; + 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 (); +} + +#endif // !id386 + + +/* +============== +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 *) + ((long)(basespans + CACHE_SIZE - 1) & ~(CACHE_SIZE - 1)); + max_span_p = &basespan_p[MAXSPANS - gpGlobals->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 = 0; //r_refdef.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 = 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 << 24; // make sure nothing sorts past this + edge_sentinel.prev = &edge_aftertail; + +// +// process all scan lines +// + bottom = gpGlobals->height; //r_refdef.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 +============= +*/ +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 +============== +*/ +void D_FlatFillSurface (surf_t *surf, int color) +{ + espan_t *span; + byte *pdest; + int u, u2; + + for (span=surf->spans ; span ; span=span->pnext) + { + pdest = (byte *)d_viewbuffer + r_screenwidth*span->v; + u = span->u; + u2 = span->u + span->count - 1; + for ( ; u <= u2 ; u++) + pdest[u] = color; + } +} + + +/* +============== +D_CalcGradients +============== +*/ +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.0 / (float)(1 << miplevel); + + TransformVector (pface->texinfo->vecs[0], p_saxis); + TransformVector (pface->texinfo->vecs[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; + sadjust = ((fixed16_t)(DotProduct (p_temp1, p_saxis) * 0x10000 + 0.5)) - + ((pface->texturemins[0] << 16) >> miplevel) + + pface->texinfo->vecs[0][3]*t; + tadjust = ((fixed16_t)(DotProduct (p_temp1, p_taxis) * 0x10000 + 0.5)) - + ((pface->texturemins[1] << 16) >> miplevel) + + pface->texinfo->vecs[1][3]*t; +#if 0 + // PGM - changing flow speed for non-warping textures. + if (pface->texinfo->flags & SURF_FLOWING) + { + if(pface->texinfo->flags & SURF_WARP) + sadjust += 0x10000 * (-128 * ( (r_newrefdef.time * 0.25) - (int)(r_newrefdef.time * 0.25) )); + else + sadjust += 0x10000 * (-128 * ( (r_newrefdef.time * 0.77) - (int)(r_newrefdef.time * 0.77) )); + } + // PGM +#endif +// +// -1 (-epsilon) so we never wander off the edge of the texture +// + 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 +============== +*/ +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 & 0xFF); + D_DrawZSpans (s->spans); +} + +/* +================= +D_TurbulentSurf +================= +*/ +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 1 + NonTurbulent8 (s->spans); +#else + if(!(pface->texinfo->flags & SURF_DRAWTURB)) + NonTurbulent8 (s->spans); + else + Turbulent8 (s->spans); +#endif +//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 (base_vpn, vpn); + VectorCopy (base_vup, vup); + VectorCopy (base_vright, vright); + R_TransformFrustum (); + } +} + +/* +============== +D_SkySurf +============== +*/ +void D_SkySurf (surf_t *s) +{ + pface = s->msurf; + miplevel = 0; + if (!pface->texinfo->texture) + return; + cacheblock = R_GetTexture(pface->texinfo->texture->gl_texturenum)->pixels[0]; + cachewidth = 256; + + d_zistepu = s->d_zistepu; + d_zistepv = s->d_zistepv; + d_ziorigin = s->d_ziorigin; + + D_CalcGradients (pface); + + D_DrawSpans16 (s->spans); + +// 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_DrawZSpans (s->spans); +} + +/* +============== +D_SolidSurf + +Normal surface cached, texture mapped surface +============== +*/ +void D_SolidSurf (surf_t *s) +{ + d_zistepu = s->d_zistepu; + d_zistepv = s->d_zistepv; + d_ziorigin = s->d_ziorigin; + + 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 + } + else + RI.currententity = gEngfuncs.GetEntityByIndex(0); //r_worldentity; + + pface = s->msurf; +#if 1 + miplevel = 0; //D_MipLevelForScale(s->nearzi * scale_for_mip * pface->texinfo->mipadjust); +#else + { + float dot; + float normal[3]; + + if ( s->insubmodel ) + { + VectorCopy( pface->plane->normal, normal ); +// TransformVector( pface->plane->normal, normal); + dot = DotProduct( normal, vpn ); + } + else + { + VectorCopy( pface->plane->normal, normal ); + dot = DotProduct( normal, vpn ); + } + + if ( pface->flags & SURF_PLANEBACK ) + dot = -dot; + + if ( dot > 0 ) + printf( "blah" ); + + miplevel = D_MipLevelForScale(s->nearzi * scale_for_mip * pface->texinfo->mipadjust); + } +#endif + +// 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 (base_vpn, vpn); + VectorCopy (base_vup, vup); + VectorCopy (base_vright, vright); + R_TransformFrustum (); + RI.currententity = NULL; //&r_worldentity; + } +} + +/* +============= +D_DrawflatSurfaces + +To allow developers to see the polygon carving of the world +============= +*/ +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, (int)s->msurf & 0xFF); + 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, modelorg); + TransformVector (modelorg, transformed_modelorg); + VectorCopy (transformed_modelorg, world_transformed_modelorg); + + if (!sw_drawflat->value) + { + for (s = &surfaces[1] ; sspans) + continue; + + r_drawnpolycount++; +#if 1 + D_SolidSurf (s); +#else + if (! (s->flags & (SURF_DRAWSKYBOX|SURF_DRAWBACKGROUND|SURF_DRAWTURB) ) ) + D_SolidSurf (s); + else if (s->flags & SURF_DRAWSKYBOX) + D_SkySurf (s); + else if (s->flags & SURF_DRAWBACKGROUND) + D_BackgroundSurf (s); + else if (s->flags & SURF_DRAWTURB) + D_TurbulentSurf (s); +#endif + } + } + else + D_DrawflatSurfaces (); + + RI.currententity = NULL; //&r_worldentity; + VectorSubtract (RI.vieworg, vec3_origin, modelorg); + R_TransformFrustum (); +} + diff --git a/r_glblit.c b/r_glblit.c index 434be575..fac3ba30 100644 --- a/r_glblit.c +++ b/r_glblit.c @@ -218,5 +218,5 @@ void R_BlitScreen() pglEnd(); pglDisable( GL_TEXTURE_2D ); gEngfuncs.GL_SwapBuffers(); - memset( vid.buffer, 0, vid.width * vid.height * 2 ); +// memset( vid.buffer, 0, vid.width * vid.height * 2 ); } diff --git a/r_light.c b/r_light.c new file mode 100644 index 00000000..7b3e00cf --- /dev/null +++ b/r_light.c @@ -0,0 +1,499 @@ +/* +gl_rlight.c - dynamic and static lights +Copyright (C) 2010 Uncle Mike + +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 3 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. +*/ + +#include "r_local.h" +#include "pm_local.h" +#include "studio.h" +#include "mathlib.h" +#include "ref_params.h" + +//unused, need refactor +unsigned blocklights[1024]; + +/* +============================================================================= + +DYNAMIC LIGHTS + +============================================================================= +*/ +/* +================== +CL_RunLightStyles + +================== +*/ +void CL_RunLightStyles( void ) +{ + int i, k, flight, clight; + float l, lerpfrac, backlerp; + float frametime = (gpGlobals->time - gpGlobals->oldtime); + float scale; + lightstyle_t *ls; + + if( !WORLDMODEL ) return; + + scale = 1; //r_lighting_modulate->value; + + // light animations + // 'm' is normal light, 'a' is no light, 'z' is double bright + for( i = 0; i < MAX_LIGHTSTYLES; i++ ) + { + ls = gEngfuncs.GetLightStyle( i ); + if( !WORLDMODEL->lightdata ) + { + tr.lightstylevalue[i] = 256 * 256; + continue; + } + + if( !gEngfuncs.CL_GetRenderParm( PARAM_GAMEPAUSED, 0 ) && frametime <= 0.1f ) + ls->time += frametime; // evaluate local time + + flight = (int)Q_floor( ls->time * 10 ); + clight = (int)Q_ceil( ls->time * 10 ); + lerpfrac = ( ls->time * 10 ) - flight; + backlerp = 1.0f - lerpfrac; + + if( !ls->length ) + { + tr.lightstylevalue[i] = 256 * scale; + continue; + } + else if( ls->length == 1 ) + { + // single length style so don't bother interpolating + tr.lightstylevalue[i] = ls->map[0] * 22 * scale; + continue; + } + else if( !ls->interp ) // || !CVAR_TO_BOOL( cl_lightstyle_lerping )) + { + tr.lightstylevalue[i] = ls->map[flight%ls->length] * 22 * scale; + continue; + } + + // interpolate animating light + // frame just gone + k = ls->map[flight % ls->length]; + l = (float)( k * 22.0f ) * backlerp; + + // upcoming frame + k = ls->map[clight % ls->length]; + l += (float)( k * 22.0f ) * lerpfrac; + + tr.lightstylevalue[i] = (int)l * scale; + } +} + +/* +============= +R_MarkLights +============= +*/ +void R_MarkLights( dlight_t *light, int bit, mnode_t *node ) +{ + float dist; + msurface_t *surf; + int i; + + if( !node || node->contents < 0 ) + return; + + dist = PlaneDiff( light->origin, node->plane ); + + if( dist > light->radius ) + { + R_MarkLights( light, bit, node->children[0] ); + return; + } + if( dist < -light->radius ) + { + R_MarkLights( light, bit, node->children[1] ); + return; + } + + // mark the polygons + surf = RI.currentmodel->surfaces + node->firstsurface; + + for( i = 0; i < node->numsurfaces; i++, surf++ ) + { + if( !BoundsAndSphereIntersect( surf->info->mins, surf->info->maxs, light->origin, light->radius )) + continue; // no intersection + + if( surf->dlightframe != tr.dlightframecount ) + { + surf->dlightbits = 0; + surf->dlightframe = tr.dlightframecount; + } + surf->dlightbits |= bit; + } + + R_MarkLights( light, bit, node->children[0] ); + R_MarkLights( light, bit, node->children[1] ); +} + +/* +============= +R_PushDlights +============= +*/ +void R_PushDlights( void ) +{ + dlight_t *l; + int i; + + tr.dlightframecount = tr.framecount; + + RI.currententity = gEngfuncs.GetEntityByIndex( 0 ); + RI.currentmodel = RI.currententity->model; + + for( i = 0; i < MAX_DLIGHTS; i++, l++ ) + { + l = gEngfuncs.GetDynamicLight( i ); + + if( l->die < gpGlobals->time || !l->radius ) + continue; + + //if( GL_FrustumCullSphere( &RI.frustum, l->origin, l->radius, 15 )) + //continue; + + R_MarkLights( l, 1<nodes ); + } +} + +/* +============= +R_CountDlights +============= +*/ +int R_CountDlights( void ) +{ + dlight_t *l; + int i, numDlights = 0; + + for( i = 0; i < MAX_DLIGHTS; i++ ) + { + l = gEngfuncs.GetDynamicLight( i ); + + if( l->die < gpGlobals->time || !l->radius ) + continue; + + numDlights++; + } + + return numDlights; +} + +/* +============= +R_CountSurfaceDlights +============= +*/ +int R_CountSurfaceDlights( msurface_t *surf ) +{ + int i, numDlights = 0; + + for( i = 0; i < MAX_DLIGHTS; i++ ) + { + if(!( surf->dlightbits & BIT( i ))) + continue; // not lit by this light + + numDlights++; + } + + return numDlights; +} + +/* +======================================================================= + + AMBIENT LIGHTING + +======================================================================= +*/ +static vec3_t g_trace_lightspot; +static vec3_t g_trace_lightvec; +static float g_trace_fraction; + +/* +================= +R_RecursiveLightPoint +================= +*/ +static qboolean R_RecursiveLightPoint( model_t *model, mnode_t *node, float p1f, float p2f, colorVec *cv, const vec3_t start, const vec3_t end ) +{ + float front, back, frac, midf; + int i, map, side, size; + float ds, dt, s, t; + int sample_size; + color24 *lm, *dm; + mextrasurf_t *info; + msurface_t *surf; + mtexinfo_t *tex; + matrix3x4 tbn; + vec3_t mid; + + // didn't hit anything + if( !node || node->contents < 0 ) + { + cv->r = cv->g = cv->b = cv->a = 0; + return false; + } + + // calculate mid point + front = PlaneDiff( start, node->plane ); + back = PlaneDiff( end, node->plane ); + + side = front < 0; + if(( back < 0 ) == side ) + return R_RecursiveLightPoint( model, node->children[side], p1f, p2f, cv, start, end ); + + frac = front / ( front - back ); + + VectorLerp( start, frac, end, mid ); + midf = p1f + ( p2f - p1f ) * frac; + + // co down front side + if( R_RecursiveLightPoint( model, node->children[side], p1f, midf, cv, start, mid )) + return true; // hit something + + if(( back < 0 ) == side ) + { + cv->r = cv->g = cv->b = cv->a = 0; + return false; // didn't hit anything + } + + // check for impact on this node + surf = model->surfaces + node->firstsurface; + VectorCopy( mid, g_trace_lightspot ); + + for( i = 0; i < node->numsurfaces; i++, surf++ ) + { + int smax, tmax; + + tex = surf->texinfo; + info = surf->info; + + if( FBitSet( surf->flags, SURF_DRAWTILED )) + continue; // no lightmaps + + s = DotProduct( mid, info->lmvecs[0] ) + info->lmvecs[0][3]; + t = DotProduct( mid, info->lmvecs[1] ) + info->lmvecs[1][3]; + + if( s < info->lightmapmins[0] || t < info->lightmapmins[1] ) + continue; + + ds = s - info->lightmapmins[0]; + dt = t - info->lightmapmins[1]; + + if ( ds > info->lightextents[0] || dt > info->lightextents[1] ) + continue; + + cv->r = cv->g = cv->b = cv->a = 0; + + if( !surf->samples ) + return true; + + sample_size = gEngfuncs.Mod_SampleSizeForFace( surf ); + smax = (info->lightextents[0] / sample_size) + 1; + tmax = (info->lightextents[1] / sample_size) + 1; + ds /= sample_size; + dt /= sample_size; + + lm = surf->samples + Q_rint( dt ) * smax + Q_rint( ds ); + g_trace_fraction = midf; + size = smax * tmax; + dm = NULL; + + if( surf->info->deluxemap ) + { + vec3_t faceNormal; + + if( FBitSet( surf->flags, SURF_PLANEBACK )) + VectorNegate( surf->plane->normal, faceNormal ); + else VectorCopy( surf->plane->normal, faceNormal ); + + // compute face TBN +#if 1 + Vector4Set( tbn[0], surf->info->lmvecs[0][0], surf->info->lmvecs[0][1], surf->info->lmvecs[0][2], 0.0f ); + Vector4Set( tbn[1], -surf->info->lmvecs[1][0], -surf->info->lmvecs[1][1], -surf->info->lmvecs[1][2], 0.0f ); + Vector4Set( tbn[2], faceNormal[0], faceNormal[1], faceNormal[2], 0.0f ); +#else + Vector4Set( tbn[0], surf->info->lmvecs[0][0], -surf->info->lmvecs[1][0], faceNormal[0], 0.0f ); + Vector4Set( tbn[1], surf->info->lmvecs[0][1], -surf->info->lmvecs[1][1], faceNormal[1], 0.0f ); + Vector4Set( tbn[2], surf->info->lmvecs[0][2], -surf->info->lmvecs[1][2], faceNormal[2], 0.0f ); +#endif + VectorNormalize( tbn[0] ); + VectorNormalize( tbn[1] ); + VectorNormalize( tbn[2] ); + dm = surf->info->deluxemap + Q_rint( dt ) * smax + Q_rint( ds ); + } + + for( map = 0; map < MAXLIGHTMAPS && surf->styles[map] != 255; map++ ) + { + uint scale = tr.lightstylevalue[surf->styles[map]]; + + if( tr.ignore_lightgamma ) + { + cv->r += lm->r * scale; + cv->g += lm->g * scale; + cv->b += lm->b * scale; + } + else + { + cv->r += gEngfuncs.LightToTexGamma( lm->r ) * scale; + cv->g += gEngfuncs.LightToTexGamma( lm->g ) * scale; + cv->b += gEngfuncs.LightToTexGamma( lm->b ) * scale; + } + lm += size; // skip to next lightmap + + if( dm != NULL ) + { + vec3_t srcNormal, lightNormal; + float f = (1.0f / 128.0f); + + VectorSet( srcNormal, ((float)dm->r - 128.0f) * f, ((float)dm->g - 128.0f) * f, ((float)dm->b - 128.0f) * f ); + Matrix3x4_VectorIRotate( tbn, srcNormal, lightNormal ); // turn to world space + VectorScale( lightNormal, (float)scale * -1.0f, lightNormal ); // turn direction from light + VectorAdd( g_trace_lightvec, lightNormal, g_trace_lightvec ); + dm += size; // skip to next deluxmap + } + } + + return true; + } + + // go down back side + return R_RecursiveLightPoint( model, node->children[!side], midf, p2f, cv, mid, end ); +} + +/* +================= +R_LightVec + +check bspmodels to get light from +================= +*/ +colorVec R_LightVecInternal( const vec3_t start, const vec3_t end, vec3_t lspot, vec3_t lvec ) +{ + float last_fraction; + int i, maxEnts = 1; + colorVec light, cv; + + if( lspot ) VectorClear( lspot ); + if( lvec ) VectorClear( lvec ); + + if( WORLDMODEL && WORLDMODEL->lightdata ) + { + light.r = light.g = light.b = light.a = 0; + last_fraction = 1.0f; + + // get light from bmodels too + //if( CVAR_TO_BOOL( r_lighting_extended )) + maxEnts = MAX_PHYSENTS; + + // check all the bsp-models + for( i = 0; i < maxEnts; i++ ) + { + physent_t *pe = gEngfuncs.EV_GetPhysent( i ); + vec3_t offset, start_l, end_l; + mnode_t *pnodes; + matrix4x4 matrix; + + if( !pe ) + break; + + if( !pe->model || pe->model->type != mod_brush ) + continue; // skip non-bsp models + + pnodes = &pe->model->nodes[pe->model->hulls[0].firstclipnode]; + VectorSubtract( pe->model->hulls[0].clip_mins, vec3_origin, offset ); + VectorAdd( offset, pe->origin, offset ); + VectorSubtract( start, offset, start_l ); + VectorSubtract( end, offset, end_l ); + + // rotate start and end into the models frame of reference + if( !VectorIsNull( pe->angles )) + { + Matrix4x4_CreateFromEntity( matrix, pe->angles, offset, 1.0f ); + Matrix4x4_VectorITransform( matrix, start, start_l ); + Matrix4x4_VectorITransform( matrix, end, end_l ); + } + + VectorClear( g_trace_lightspot ); + VectorClear( g_trace_lightvec ); + g_trace_fraction = 1.0f; + + if( !R_RecursiveLightPoint( pe->model, pnodes, 0.0f, 1.0f, &cv, start_l, end_l )) + continue; // didn't hit anything + + if( g_trace_fraction < last_fraction ) + { + if( lspot ) VectorCopy( g_trace_lightspot, lspot ); + if( lvec ) VectorNormalize2( g_trace_lightvec, lvec ); + light.r = Q_min(( cv.r >> 7 ), 255 ); + light.g = Q_min(( cv.g >> 7 ), 255 ); + light.b = Q_min(( cv.b >> 7 ), 255 ); + last_fraction = g_trace_fraction; + + if(( light.r + light.g + light.b ) != 0 ) + break; // we get light now + } + } + } + else + { + light.r = light.g = light.b = 255; + light.a = 0; + } + + return light; +} + +/* +================= +R_LightVec + +check bspmodels to get light from +================= +*/ +colorVec R_LightVec( const vec3_t start, const vec3_t end, vec3_t lspot, vec3_t lvec ) +{ + colorVec light = R_LightVecInternal( start, end, lspot, lvec ); + + if( lspot != NULL && lvec != NULL ) // CVAR_TO_BOOL( r_lighting_extended ) && + { + // trying to get light from ceiling (but ignore gradient analyze) + if(( light.r + light.g + light.b ) == 0 ) + return R_LightVecInternal( end, start, lspot, lvec ); + } + + return light; +} + +/* +================= +R_LightPoint + +light from floor +================= +*/ +colorVec R_LightPoint( const vec3_t p0 ) +{ + vec3_t p1; + + VectorSet( p1, p0[0], p0[1], p0[2] - 2048.0f ); + + return R_LightVec( p0, p1, NULL, NULL ); +} diff --git a/r_local.h b/r_local.h index 1cc2262e..8d0eaad0 100644 --- a/r_local.h +++ b/r_local.h @@ -32,6 +32,10 @@ GNU General Public License for more details. #include "pm_movevars.h" //#include "cvar.h" typedef struct mip_s mip_t; + +typedef int fixed8_t; +typedef int fixed16_t; + #define offsetof(s,m) (size_t)&(((s *)0)->m) #define ASSERT(x) if(!( x )) gEngfuncs.Host_Error( "assert failed at %s:%i\n", __FILE__, __LINE__ ) @@ -426,7 +430,7 @@ void R_InitDlightTexture( void ); void R_TextureList_f( void ); void R_InitImages( void ); void R_ShutdownImages( void ); -#if 0 +#if 1 // // gl_rlight.c // @@ -439,7 +443,7 @@ colorVec R_LightVec( const vec3_t start, const vec3_t end, vec3_t lightspot, vec int R_CountSurfaceDlights( msurface_t *surf ); colorVec R_LightPoint( const vec3_t p0 ); int R_CountDlights( void ); - +#endif // // gl_rmain.c // @@ -457,7 +461,7 @@ void R_FindViewLeaf( void ); void R_PushScene( void ); void R_PopScene( void ); void R_DrawFog( void ); - +#if 0 // // gl_rmath.c // @@ -610,8 +614,8 @@ void Mod_SpriteUnloadTextures( void *data ); void Mod_UnloadAliasModel( struct model_s *mod ); void Mod_AliasUnloadTextures( void *data ); void GL_SetRenderMode( int mode ); -void R_RunViewmodelEvents( void ); -void R_DrawViewModel( void ); +//void R_RunViewmodelEvents( void ); +//void R_DrawViewModel( void ); int R_GetSpriteTexture( const struct model_s *m_pSpriteModel, int frame ); void R_DecalShoot( int textureIndex, int entityIndex, int modelIndex, vec3_t pos, int flags, float scale ); void R_RemoveEfrags( struct cl_entity_s *ent ); @@ -654,9 +658,20 @@ void TriFogParams( float flDensity, int iFogSkybox ); void TriCullFace( TRICULLSTYLE mode ); +// +// r_blitscreen.c +// +void R_BlitScreen(); +void R_InitBlit(); + + extern ref_api_t gEngfuncs; extern ref_globals_t *gpGlobals; extern cvar_t *gl_emboss_scale; +extern cvar_t *r_drawentities; +extern cvar_t *vid_brightness; +extern cvar_t *vid_gamma; +extern cvar_t *r_norefresh; #if 0 // // renderer cvars @@ -682,7 +697,6 @@ extern cvar_t *gl_stencilbits; extern cvar_t *r_speeds; extern cvar_t *r_fullbright; -extern cvar_t *r_norefresh; extern cvar_t *r_showtree; // build graph of visible hull extern cvar_t *r_lighting_extended; extern cvar_t *r_lighting_modulate; @@ -715,6 +729,496 @@ extern cvar_t *traceralpha; extern cvar_t *cl_lightstyle_lerping; extern cvar_t *r_showhull; #endif +// softrender defs + +#define CACHE_SIZE 32 + +/* +==================================================== + CONSTANTS +==================================================== +*/ + +#define VID_CBITS 6 +#define VID_GRADES (1 << VID_CBITS) + + +// r_shared.h: general refresh-related stuff shared between the refresh and the +// driver + + +#define MAXVERTS 64 // max points in a surface polygon +#define MAXWORKINGVERTS (MAXVERTS+4) // max points in an intermediate + // polygon (while processing) +// !!! if this is changed, it must be changed in d_ifacea.h too !!! +#define MAXHEIGHT 1200 +#define MAXWIDTH 1600 + +#define INFINITE_DISTANCE 0x10000 // distance that's always guaranteed to + // be farther away than anything in + // the scene + + +// d_iface.h: interface header file for rasterization driver modules + +#define WARP_WIDTH 320 +#define WARP_HEIGHT 240 + +#define MAX_LBM_HEIGHT 480 + + +#define PARTICLE_Z_CLIP 8.0 + +// !!! must be kept the same as in quakeasm.h !!! +#define TRANSPARENT_COLOR 0xFF + + +// !!! if this is changed, it must be changed in d_ifacea.h too !!! +#define TURB_TEX_SIZE 64 // base turbulent texture size + +// !!! if this is changed, it must be changed in d_ifacea.h too !!! +#define CYCLE 128 // turbulent cycle size + +#define SCANBUFFERPAD 0x1000 + +#define DS_SPAN_LIST_END -128 + +#define NUMSTACKEDGES 2000 +#define MINEDGES NUMSTACKEDGES +#define NUMSTACKSURFACES 1000 +#define MINSURFACES NUMSTACKSURFACES +#define MAXSPANS 3000 + +// flags in finalvert_t.flags +#define ALIAS_LEFT_CLIP 0x0001 +#define ALIAS_TOP_CLIP 0x0002 +#define ALIAS_RIGHT_CLIP 0x0004 +#define ALIAS_BOTTOM_CLIP 0x0008 +#define ALIAS_Z_CLIP 0x0010 +#define ALIAS_XY_CLIP_MASK 0x000F + +#define SURFCACHE_SIZE_AT_320X240 1024*768 + +#define BMODEL_FULLY_CLIPPED 0x10 // value returned by R_BmodelCheckBBox () + // if bbox is trivially rejected + +#define XCENTERING (1.0 / 2.0) +#define YCENTERING (1.0 / 2.0) + +#define CLIP_EPSILON 0.001 + +#define BACKFACE_EPSILON 0.01 + +// !!! if this is changed, it must be changed in asm_draw.h too !!! +#define NEAR_CLIP 0.01 + + +#define MAXALIASVERTS 2000 // TODO: tune this +#define ALIAS_Z_CLIP_PLANE 4 + +// turbulence stuff + +#define AMP 8*0x10000 +#define AMP2 3 +#define SPEED 20 + + +/* +==================================================== +TYPES +==================================================== +*/ + +typedef struct +{ + float u, v; + float s, t; + float zi; +} emitpoint_t; + +/* +** if you change this structure be sure to change the #defines +** listed after it! +*/ +#define SMALL_FINALVERT 0 + +#if SMALL_FINALVERT + +typedef struct finalvert_s { + short u, v, s, t; + int l; + int zi; + int flags; + float xyz[3]; // eye space +} finalvert_t; + +#define FINALVERT_V0 0 +#define FINALVERT_V1 2 +#define FINALVERT_V2 4 +#define FINALVERT_V3 6 +#define FINALVERT_V4 8 +#define FINALVERT_V5 12 +#define FINALVERT_FLAGS 16 +#define FINALVERT_X 20 +#define FINALVERT_Y 24 +#define FINALVERT_Z 28 +#define FINALVERT_SIZE 32 + +#else + +typedef struct finalvert_s { + int u, v, s, t; + int l; + int zi; + int flags; + float xyz[3]; // eye space +} finalvert_t; + +#define FINALVERT_V0 0 +#define FINALVERT_V1 4 +#define FINALVERT_V2 8 +#define FINALVERT_V3 12 +#define FINALVERT_V4 16 +#define FINALVERT_V5 20 +#define FINALVERT_FLAGS 24 +#define FINALVERT_X 28 +#define FINALVERT_Y 32 +#define FINALVERT_Z 36 +#define FINALVERT_SIZE 40 + +#endif + + +typedef struct +{ + short s; + short t; +} dstvert_t; + +typedef struct +{ + short index_xyz[3]; + short index_st[3]; +} dtriangle_t; + +typedef struct +{ + byte v[3]; // scaled byte to fit in frame mins/maxs + byte lightnormalindex; +} dtrivertx_t; + +#define DTRIVERTX_V0 0 +#define DTRIVERTX_V1 1 +#define DTRIVERTX_V2 2 +#define DTRIVERTX_LNI 3 +#define DTRIVERTX_SIZE 4 + +typedef struct +{ + void *pskin; + int pskindesc; + int skinwidth; + int skinheight; + dtriangle_t *ptriangles; + finalvert_t *pfinalverts; + int numtriangles; + int drawtype; + int seamfixupX16; + qboolean do_vis_thresh; + int vis_thresh; +} affinetridesc_t; + + + +typedef struct +{ + byte *surfdat; // destination for generated surface + int rowbytes; // destination logical width in bytes + msurface_t *surf; // description for surface to generate + fixed8_t lightadj[MAXLIGHTMAPS]; + // adjust for lightmap levels for dynamic lighting + image_t *image; + int surfmip; // mipmapped ratio of surface texels / world pixels + int surfwidth; // in mipmapped texels + int surfheight; // in mipmapped texels +} drawsurf_t; + + +#if 0 +typedef struct { + int ambientlight; + int shadelight; + float *plightvec; +} alight_t; + +#endif + +// clipped bmodel edges + +typedef struct bedge_s +{ + mvertex_t *v[2]; + struct bedge_s *pnext; +} bedge_t; + + +// !!! if this is changed, it must be changed in asm_draw.h too !!! +typedef struct clipplane_s +{ + vec3_t normal; + float dist; + struct clipplane_s *next; + byte leftedge; + byte rightedge; + byte reserved[2]; +} clipplane_t; + + +typedef struct surfcache_s +{ + struct surfcache_s *next; + struct surfcache_s **owner; // NULL is an empty chunk of memory + int lightadj[MAXLIGHTMAPS]; // checked for strobe flush + int dlight; + int size; // including header + unsigned width; + unsigned height; // DEBUG only needed for debug + float mipscale; + image_t *image; + byte data[4]; // width*height elements +} surfcache_t; + +// !!! if this is changed, it must be changed in asm_draw.h too !!! +typedef struct espan_s +{ + int u, v, count; + struct espan_s *pnext; +} espan_t; + +// used by the polygon drawer (R_POLY.C) and sprite setup code (R_SPRITE.C) +typedef struct +{ + int nump; + emitpoint_t *pverts; + byte *pixels; // image + int pixel_width; // image width + int pixel_height; // image height + vec3_t vup, vright, vpn; // in worldspace, for plane eq + float dist; + float s_offset, t_offset; + float viewer_position[3]; + void (*drawspanlet)( void ); + int stipple_parity; +} polydesc_t; + +// FIXME: compress, make a union if that will help +// insubmodel is only 1, flags is fewer than 32, spanstate could be a byte +typedef struct surf_s +{ + struct surf_s *next; // active surface stack in r_edge.c + struct surf_s *prev; // used in r_edge.c for active surf stack + struct espan_s *spans; // pointer to linked list of spans to draw + int key; // sorting key (BSP order) + int last_u; // set during tracing + int spanstate; // 0 = not in span + // 1 = in span + // -1 = in inverted span (end before + // start) + int flags; // currentface flags + msurface_t *msurf; + cl_entity_t *entity; + float nearzi; // nearest 1/z on surface, for mipmapping + qboolean insubmodel; + float d_ziorigin, d_zistepu, d_zistepv; + + int pad[2]; // to 64 bytes +} surf_t; + +// !!! if this is changed, it must be changed in asm_draw.h too !!! +typedef struct edge_s +{ + fixed16_t u; + fixed16_t u_step; + struct edge_s *prev, *next; + unsigned short surfs[2]; + struct edge_s *nextremove; + float nearzi; + medge_t *owner; +} edge_t; + + +/* +==================================================== +VARS +==================================================== +*/ + +extern int d_spanpixcount; +extern int r_framecount; // sequence # of current frame since Quake + // started +extern float r_aliasuvscale; // scale-up factor for screen u and v + // on Alias vertices passed to driver +extern qboolean r_dowarp; + +extern affinetridesc_t r_affinetridesc; + +extern vec3_t r_pright, r_pup, r_ppn; + +void D_DrawSurfaces (void); +void R_DrawParticle( void ); +void D_ViewChanged (void); +void D_WarpScreen (void); +void R_PolysetUpdateTables (void); + +extern void *acolormap; // FIXME: should go away + +//=======================================================================// + +// callbacks to Quake + +extern drawsurf_t r_drawsurf; + +void R_DrawSurface (void); + +extern int c_surf; + +extern byte r_warpbuffer[WARP_WIDTH * WARP_HEIGHT]; + + + + +extern float scale_for_mip; + +extern qboolean d_roverwrapped; +extern surfcache_t *sc_rover; +extern surfcache_t *d_initial_rover; + +extern float d_sdivzstepu, d_tdivzstepu, d_zistepu; +extern float d_sdivzstepv, d_tdivzstepv, d_zistepv; +extern float d_sdivzorigin, d_tdivzorigin, d_ziorigin; + +extern fixed16_t sadjust, tadjust; +extern fixed16_t bbextents, bbextentt; + + +void D_DrawSpans16 (espan_t *pspans); +void D_DrawZSpans (espan_t *pspans); +void Turbulent8 (espan_t *pspan); +void NonTurbulent8 (espan_t *pspan); //PGM + +surfcache_t *D_CacheSurface (msurface_t *surface, int miplevel); + +extern int d_vrectx, d_vrecty, d_vrectright_particle, d_vrectbottom_particle; + +extern int d_pix_min, d_pix_max, d_pix_shift; + +extern pixel_t *d_viewbuffer; +extern short *d_pzbuffer; +extern unsigned int d_zrowbytes, d_zwidth; +extern short *zspantable[MAXHEIGHT]; +extern int d_scantable[MAXHEIGHT]; + +extern int d_minmip; +extern float d_scalemip[3]; + +//=================================================================== + +extern int cachewidth; +extern pixel_t *cacheblock; +extern int r_screenwidth; + +extern int r_drawnpolycount; + +extern int sintable[1280]; +extern int intsintable[1280]; +extern int blanktable[1280]; // PGM + +extern vec3_t vup, base_vup; +extern vec3_t vpn, base_vpn; +extern vec3_t vright, base_vright; + +extern 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. +// surfaces[0] is a dummy, because index 0 is used to indicate no surface +// attached to an edge_t + +//=================================================================== + +extern vec3_t sxformaxis[4]; // s axis transformed into viewspace +extern vec3_t txformaxis[4]; // t axis transformed into viewspac + +extern float xcenter, ycenter; +extern float xscale, yscale; +extern float xscaleinv, yscaleinv; +extern float xscaleshrink, yscaleshrink; + + +extern edge_t *auxedges; +extern int r_numallocatededges; +extern edge_t *r_edges, *edge_p, *edge_max; + +extern edge_t *newedges[MAXHEIGHT]; +extern edge_t *removeedges[MAXHEIGHT]; + +// FIXME: make stack vars when debugging done +extern edge_t edge_head; +extern edge_t edge_tail; +extern edge_t edge_aftertail; + + +extern int r_frustum_indexes[4*6]; +extern int r_maxsurfsseen, r_maxedgesseen, r_cnumsurfs; +extern qboolean r_surfsonstack; + +extern mleaf_t *r_viewleaf; +extern int r_viewcluster, r_oldviewcluster; + +extern int r_clipflags; +extern int r_dlightframecount; +extern qboolean r_fov_greater_than_90; + + +extern cvar_t *sw_aliasstats; +extern cvar_t *sw_clearcolor; +extern cvar_t *sw_drawflat; +extern cvar_t *sw_draworder; +extern cvar_t *sw_maxedges; +extern cvar_t *sw_maxsurfs; +extern cvar_t *sw_mipcap; +extern cvar_t *sw_mipscale; +extern cvar_t *sw_mode; +extern cvar_t *sw_reportsurfout; +extern cvar_t *sw_reportedgeout; +extern cvar_t *sw_stipplealpha; +extern cvar_t *sw_surfcacheoverride; +extern cvar_t *sw_waterwarp; + +extern vec3_t modelorg; +extern vec3_t r_origin; +extern mplane_t screenedge[4]; + + +extern clipplane_t view_clipplanes[4]; +extern int *pfrustum_indexes[4]; + +extern vec3_t vup, base_vup; +extern vec3_t vpn, base_vpn; +extern vec3_t vright, base_vright; + +extern cvar_t *r_fullbright; + +#define CACHESPOT(surf) ((surfcache_t**)surf->info->reserved) +extern int r_visframecount; +extern mvertex_t *r_pcurrentvertbase; +extern int r_maxvalidedgeoffset; +extern int r_currentkey; +extern int r_currentbkey; + + + // // engine callbacks // diff --git a/r_main.c b/r_main.c new file mode 100644 index 00000000..089ba850 --- /dev/null +++ b/r_main.c @@ -0,0 +1,1576 @@ +/* +gl_rmain.c - renderer main loop +Copyright (C) 2010 Uncle Mike + +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 3 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. +*/ + +#include "r_local.h" +#include "mathlib.h" +#include "library.h" +//#include "beamdef.h" +//#include "particledef.h" +#include "entity_types.h" + +#define IsLiquidContents( cnt ) ( cnt == CONTENTS_WATER || cnt == CONTENTS_SLIME || cnt == CONTENTS_LAVA ) + +float gldepthmin, gldepthmax; +ref_instance_t RI; + + +// quake defines. will be refactored + +// view origin +// +vec3_t vup, base_vup; +vec3_t vpn, base_vpn; +vec3_t vright, base_vright; +vec3_t r_origin; + +// +// screen size info +// +float xcenter, ycenter; +float xscale, yscale; +float xscaleinv, yscaleinv; +float xscaleshrink, yscaleshrink; +float aliasxscale, aliasyscale, aliasxcenter, aliasycenter; + +int r_screenwidth; + +float verticalFieldOfView; +float xOrigin, yOrigin; + +mplane_t screenedge[4]; + + +// +// refresh flags +// +int r_framecount = 1; // so frame counts initialized to 0 don't match +int r_visframecount; +int d_spanpixcount; +int r_polycount; +int r_drawnpolycount; +int r_wholepolycount; + +int *pfrustum_indexes[4]; +int r_frustum_indexes[4*6]; + +mleaf_t *r_viewleaf; +int r_viewcluster, r_oldviewcluster; + +cvar_t *r_lefthand; +cvar_t *sw_aliasstats; +cvar_t *sw_allow_modex; +cvar_t *sw_clearcolor; +cvar_t *sw_drawflat; +cvar_t *sw_draworder; +cvar_t *sw_maxedges; +cvar_t *sw_maxsurfs; +cvar_t *sw_mode; +cvar_t *sw_reportedgeout; +cvar_t *sw_reportsurfout; +cvar_t *sw_stipplealpha; +cvar_t *sw_surfcacheoverride; +cvar_t *sw_waterwarp; + +cvar_t *r_drawworld; +cvar_t *r_drawentities; +cvar_t *r_dspeeds; +cvar_t *r_fullbright; +cvar_t *r_lerpmodels; +cvar_t *r_novis; + +cvar_t *r_speeds; +cvar_t *r_lightlevel; //FIXME HACK + +cvar_t *vid_fullscreen; +cvar_t *vid_gamma; + +//PGM +cvar_t *sw_lockpvs; +//PGM + + +mleaf_t *r_viewleaf; +int r_viewcluster, r_oldviewcluster; + +float d_sdivzstepu, d_tdivzstepu, d_zistepu; +float d_sdivzstepv, d_tdivzstepv, d_zistepv; +float d_sdivzorigin, d_tdivzorigin, d_ziorigin; + +fixed16_t sadjust, tadjust, bbextents, bbextentt; + +pixel_t *cacheblock; +int cachewidth; +pixel_t *d_viewbuffer; +short *d_pzbuffer; +unsigned int d_zrowbytes; +unsigned int d_zwidth; + +qboolean r_dowarp; + +mvertex_t *r_pcurrentvertbase; + +int c_surf; +int r_maxsurfsseen, r_maxedgesseen, r_cnumsurfs; +qboolean r_surfsonstack; +int r_clipflags; +byte r_warpbuffer[WARP_WIDTH * WARP_HEIGHT]; +int r_numallocatededges; + + +/* +================ +R_ConcatRotations +================ +*/ +void R_ConcatRotations (float in1[3][3], float in2[3][3], float out[3][3]) +{ + out[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] + + in1[0][2] * in2[2][0]; + out[0][1] = in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] + + in1[0][2] * in2[2][1]; + out[0][2] = in1[0][0] * in2[0][2] + in1[0][1] * in2[1][2] + + in1[0][2] * in2[2][2]; + out[1][0] = in1[1][0] * in2[0][0] + in1[1][1] * in2[1][0] + + in1[1][2] * in2[2][0]; + out[1][1] = in1[1][0] * in2[0][1] + in1[1][1] * in2[1][1] + + in1[1][2] * in2[2][1]; + out[1][2] = in1[1][0] * in2[0][2] + in1[1][1] * in2[1][2] + + in1[1][2] * in2[2][2]; + out[2][0] = in1[2][0] * in2[0][0] + in1[2][1] * in2[1][0] + + in1[2][2] * in2[2][0]; + out[2][1] = in1[2][0] * in2[0][1] + in1[2][1] * in2[1][1] + + in1[2][2] * in2[2][1]; + out[2][2] = in1[2][0] * in2[0][2] + in1[2][1] * in2[1][2] + + in1[2][2] * in2[2][2]; +} + + + +static int R_RankForRenderMode( int rendermode ) +{ + switch( rendermode ) + { + case kRenderTransTexture: + return 1; // draw second + case kRenderTransAdd: + return 2; // draw third + case kRenderGlow: + return 3; // must be last! + } + return 0; +} + +/* +================ +R_GetEntityRenderMode + +check for texture flags +================ +*/ +int R_GetEntityRenderMode( cl_entity_t *ent ) +{ + int i, opaque, trans; + mstudiotexture_t *ptexture; + cl_entity_t *oldent; + model_t *model; + studiohdr_t *phdr; + + oldent = RI.currententity; + RI.currententity = ent; + return ent->curstate.rendermode; +} + + +void R_AllowFog( qboolean allowed ) +{ +} + +/* +=============== +R_OpaqueEntity + +Opaque entity can be brush or studio model but sprite +=============== +*/ +static qboolean R_OpaqueEntity( cl_entity_t *ent ) +{ + if( R_GetEntityRenderMode( ent ) == kRenderNormal ) + return true; + return false; +} + +/* +=============== +R_TransEntityCompare + +Sorting translucent entities by rendermode then by distance +=============== +*/ +static int R_TransEntityCompare( const cl_entity_t **a, const cl_entity_t **b ) +{ + cl_entity_t *ent1, *ent2; + vec3_t vecLen, org; + float dist1, dist2; + int rendermode1; + int rendermode2; + + ent1 = (cl_entity_t *)*a; + ent2 = (cl_entity_t *)*b; + rendermode1 = R_GetEntityRenderMode( ent1 ); + rendermode2 = R_GetEntityRenderMode( ent2 ); + + // sort by distance + if( ent1->model->type != mod_brush || rendermode1 != kRenderTransAlpha ) + { + VectorAverage( ent1->model->mins, ent1->model->maxs, org ); + VectorAdd( ent1->origin, org, org ); + VectorSubtract( RI.vieworg, org, vecLen ); + dist1 = DotProduct( vecLen, vecLen ); + } + else dist1 = 1000000000; + + if( ent2->model->type != mod_brush || rendermode2 != kRenderTransAlpha ) + { + VectorAverage( ent2->model->mins, ent2->model->maxs, org ); + VectorAdd( ent2->origin, org, org ); + VectorSubtract( RI.vieworg, org, vecLen ); + dist2 = DotProduct( vecLen, vecLen ); + } + else dist2 = 1000000000; + + if( dist1 > dist2 ) + return -1; + if( dist1 < dist2 ) + return 1; + + // then sort by rendermode + if( R_RankForRenderMode( rendermode1 ) > R_RankForRenderMode( rendermode2 )) + return 1; + if( R_RankForRenderMode( rendermode1 ) < R_RankForRenderMode( rendermode2 )) + return -1; + + return 0; +} + +#if 0 + +/* +=============== +R_WorldToScreen + +Convert a given point from world into screen space +Returns true if we behind to screen +=============== +*/ +int R_WorldToScreen( const vec3_t point, vec3_t screen ) +{ + matrix4x4 worldToScreen; + qboolean behind; + float w; + + if( !point || !screen ) + return true; + + Matrix4x4_Copy( worldToScreen, RI.worldviewProjectionMatrix ); + screen[0] = worldToScreen[0][0] * point[0] + worldToScreen[0][1] * point[1] + worldToScreen[0][2] * point[2] + worldToScreen[0][3]; + screen[1] = worldToScreen[1][0] * point[0] + worldToScreen[1][1] * point[1] + worldToScreen[1][2] * point[2] + worldToScreen[1][3]; + w = worldToScreen[3][0] * point[0] + worldToScreen[3][1] * point[1] + worldToScreen[3][2] * point[2] + worldToScreen[3][3]; + screen[2] = 0.0f; // just so we have something valid here + + if( w < 0.001f ) + { + screen[0] *= 100000; + screen[1] *= 100000; + behind = true; + } + else + { + float invw = 1.0f / w; + screen[0] *= invw; + screen[1] *= invw; + behind = false; + } + + return behind; +} + +/* +=============== +R_ScreenToWorld + +Convert a given point from screen into world space +=============== +*/ +void R_ScreenToWorld( const vec3_t screen, vec3_t point ) +{ + matrix4x4 screenToWorld; + float w; + + if( !point || !screen ) + return; + + Matrix4x4_Invert_Full( screenToWorld, RI.worldviewProjectionMatrix ); + + point[0] = screen[0] * screenToWorld[0][0] + screen[1] * screenToWorld[0][1] + screen[2] * screenToWorld[0][2] + screenToWorld[0][3]; + point[1] = screen[0] * screenToWorld[1][0] + screen[1] * screenToWorld[1][1] + screen[2] * screenToWorld[1][2] + screenToWorld[1][3]; + point[2] = screen[0] * screenToWorld[2][0] + screen[1] * screenToWorld[2][1] + screen[2] * screenToWorld[2][2] + screenToWorld[2][3]; + w = screen[0] * screenToWorld[3][0] + screen[1] * screenToWorld[3][1] + screen[2] * screenToWorld[3][2] + screenToWorld[3][3]; + if( w != 0.0f ) VectorScale( point, ( 1.0f / w ), point ); +} + +#endif + +/* +=============== +R_PushScene +=============== +*/ +void R_PushScene( void ) +{ + if( ++tr.draw_stack_pos >= MAX_DRAW_STACK ) + gEngfuncs.Host_Error( "draw stack overflow\n" ); + + tr.draw_list = &tr.draw_stack[tr.draw_stack_pos]; +} + +/* +=============== +R_PopScene +=============== +*/ +void R_PopScene( void ) +{ + if( --tr.draw_stack_pos < 0 ) + gEngfuncs.Host_Error( "draw stack underflow\n" ); + tr.draw_list = &tr.draw_stack[tr.draw_stack_pos]; +} + +/* +=============== +R_ClearScene +=============== +*/ +void R_ClearScene( void ) +{ + tr.draw_list->num_solid_entities = 0; + tr.draw_list->num_trans_entities = 0; + tr.draw_list->num_beam_entities = 0; + + // clear the scene befor start new frame + if( gEngfuncs.drawFuncs->R_ClearScene != NULL ) + gEngfuncs.drawFuncs->R_ClearScene(); + +} + +/* +=============== +R_AddEntity +=============== +*/ +qboolean R_AddEntity( struct cl_entity_s *clent, int type ) +{ + if( !r_drawentities->value ) + return false; // not allow to drawing + + if( !clent || !clent->model ) + return false; // if set to invisible, skip + + if( FBitSet( clent->curstate.effects, EF_NODRAW )) + return false; // done + + if( !R_ModelOpaque( clent->curstate.rendermode ) && gEngfuncs.CL_FxBlend( clent ) <= 0 ) + return true; // invisible + + if( type == ET_FRAGMENTED ) + r_stats.c_client_ents++; + + if( R_OpaqueEntity( clent )) + { + // opaque + if( tr.draw_list->num_solid_entities >= MAX_VISIBLE_PACKET ) + return false; + + tr.draw_list->solid_entities[tr.draw_list->num_solid_entities] = clent; + tr.draw_list->num_solid_entities++; + } + else + { + // translucent + if( tr.draw_list->num_trans_entities >= MAX_VISIBLE_PACKET ) + return false; + + tr.draw_list->trans_entities[tr.draw_list->num_trans_entities] = clent; + tr.draw_list->num_trans_entities++; + } + + return true; +} + +/* +============= +R_Clear +============= +*/ +static void R_Clear( int bitMask ) +{ + int bits; +#if 0 + if( gEngfuncs.CL_IsDevOverviewMode( )) + pglClearColor( 0.0f, 1.0f, 0.0f, 1.0f ); // green background (Valve rules) + else pglClearColor( 0.5f, 0.5f, 0.5f, 1.0f ); + + bits = GL_DEPTH_BUFFER_BIT; + + if( glState.stencilEnabled ) + bits |= GL_STENCIL_BUFFER_BIT; + + bits &= bitMask; + + pglClear( bits ); + + // change ordering for overview + if( RI.drawOrtho ) + { + gldepthmin = 1.0f; + gldepthmax = 0.0f; + } + else + { + gldepthmin = 0.0f; + gldepthmax = 1.0f; + } + + pglDepthFunc( GL_LEQUAL ); + pglDepthRange( gldepthmin, gldepthmax ); +#endif + memset( vid.buffer, 0, 1920*1080*2); +} + +//============================================================================= +/* +=============== +R_GetFarClip +=============== +*/ +static float R_GetFarClip( void ) +{ + if( WORLDMODEL && RI.drawWorld ) + return MOVEVARS->zmax * 1.73f; + return 2048.0f; +} + +/* +=============== +R_SetupFrustum +=============== +*/ +void R_SetupFrustum( void ) +{ +#if 0 + ref_overview_t *ov = gEngfuncs.GetOverviewParms(); + + if( RP_NORMALPASS() && ( gEngfuncs.GetWaterLevel() >= 3 )) + { + RI.fov_x = atan( tan( DEG2RAD( RI.fov_x ) / 2 ) * ( 0.97 + sin( gpGlobals->time * 1.5 ) * 0.03 )) * 2 / (M_PI / 180.0); + RI.fov_y = atan( tan( DEG2RAD( RI.fov_y ) / 2 ) * ( 1.03 - sin( gpGlobals->time * 1.5 ) * 0.03 )) * 2 / (M_PI / 180.0); + } + + // build the transformation matrix for the given view angles + AngleVectors( RI.viewangles, RI.vforward, RI.vright, RI.vup ); + + if( !r_lockfrustum->value ) + { + VectorCopy( RI.vieworg, RI.cullorigin ); + VectorCopy( RI.vforward, RI.cull_vforward ); + VectorCopy( RI.vright, RI.cull_vright ); + VectorCopy( RI.vup, RI.cull_vup ); + } + + if( RI.drawOrtho ) + GL_FrustumInitOrtho( &RI.frustum, ov->xLeft, ov->xRight, ov->yTop, ov->yBottom, ov->zNear, ov->zFar ); + else GL_FrustumInitProj( &RI.frustum, 0.0f, R_GetFarClip(), RI.fov_x, RI.fov_y ); // NOTE: we ignore nearplane here (mirrors only) +#endif +} + +/* +============= +R_SetupProjectionMatrix +============= +*/ +static void R_SetupProjectionMatrix( matrix4x4 m ) +{ +#if 0 + GLdouble xMin, xMax, yMin, yMax, zNear, zFar; + + if( RI.drawOrtho ) + { + ref_overview_t *ov = gEngfuncs.GetOverviewParms(); + Matrix4x4_CreateOrtho( m, ov->xLeft, ov->xRight, ov->yTop, ov->yBottom, ov->zNear, ov->zFar ); + return; + } + + RI.farClip = R_GetFarClip(); + + zNear = 4.0f; + zFar = max( 256.0f, RI.farClip ); + + yMax = zNear * tan( RI.fov_y * M_PI / 360.0 ); + yMin = -yMax; + + xMax = zNear * tan( RI.fov_x * M_PI / 360.0 ); + xMin = -xMax; + + Matrix4x4_CreateProjection( m, xMax, xMin, yMax, yMin, zNear, zFar ); +#endif +} + +/* +============= +R_SetupModelviewMatrix +============= +*/ +static void R_SetupModelviewMatrix( matrix4x4 m ) +{ +#if 0 + Matrix4x4_CreateModelview( m ); + Matrix4x4_ConcatRotate( m, -RI.viewangles[2], 1, 0, 0 ); + Matrix4x4_ConcatRotate( m, -RI.viewangles[0], 0, 1, 0 ); + Matrix4x4_ConcatRotate( m, -RI.viewangles[1], 0, 0, 1 ); + Matrix4x4_ConcatTranslate( m, -RI.vieworg[0], -RI.vieworg[1], -RI.vieworg[2] ); +#endif +} + +/* +============= +R_LoadIdentity +============= +*/ +void R_LoadIdentity( void ) +{ +#if 0 + if( tr.modelviewIdentity ) return; + + Matrix4x4_LoadIdentity( RI.objectMatrix ); + Matrix4x4_Copy( RI.modelviewMatrix, RI.worldviewMatrix ); + + pglMatrixMode( GL_MODELVIEW ); + GL_LoadMatrix( RI.modelviewMatrix ); + tr.modelviewIdentity = true; +#endif +} + +/* +============= +R_RotateForEntity +============= +*/ +void R_RotateForEntity( cl_entity_t *e ) +{ +#if 0 + float scale = 1.0f; + + if( e == gEngfuncs.GetEntityByIndex( 0 ) ) + { + R_LoadIdentity(); + return; + } + + if( e->model->type != mod_brush && e->curstate.scale > 0.0f ) + scale = e->curstate.scale; + + Matrix4x4_CreateFromEntity( RI.objectMatrix, e->angles, e->origin, scale ); + Matrix4x4_ConcatTransforms( RI.modelviewMatrix, RI.worldviewMatrix, RI.objectMatrix ); + + pglMatrixMode( GL_MODELVIEW ); + GL_LoadMatrix( RI.modelviewMatrix ); + tr.modelviewIdentity = false; +#endif +} + +/* +============= +R_TranslateForEntity +============= +*/ +void R_TranslateForEntity( cl_entity_t *e ) +{ +#if 0 + float scale = 1.0f; + + if( e == gEngfuncs.GetEntityByIndex( 0 ) ) + { + R_LoadIdentity(); + return; + } + + if( e->model->type != mod_brush && e->curstate.scale > 0.0f ) + scale = e->curstate.scale; + + Matrix4x4_CreateFromEntity( RI.objectMatrix, vec3_origin, e->origin, scale ); + Matrix4x4_ConcatTransforms( RI.modelviewMatrix, RI.worldviewMatrix, RI.objectMatrix ); + + pglMatrixMode( GL_MODELVIEW ); + GL_LoadMatrix( RI.modelviewMatrix ); + tr.modelviewIdentity = false; +#endif +} + +/* +=============== +R_FindViewLeaf +=============== +*/ +void R_FindViewLeaf( void ) +{ + RI.oldviewleaf = RI.viewleaf; + RI.viewleaf = gEngfuncs.Mod_PointInLeaf( RI.pvsorigin, WORLDMODEL->nodes ); +} + +/* +=============== +R_SetupFrame +=============== +*/ +static void R_SetupFrame( void ) +{ + // setup viewplane dist + RI.viewplanedist = DotProduct( RI.vieworg, RI.vforward ); + +// if( !gl_nosort->value ) + { + // sort translucents entities by rendermode and distance + qsort( tr.draw_list->trans_entities, tr.draw_list->num_trans_entities, sizeof( cl_entity_t* ), R_TransEntityCompare ); + } + + // current viewleaf + if( RI.drawWorld ) + { + RI.isSkyVisible = false; // unknown at this moment + R_FindViewLeaf(); + } + + // setup twice until globals fully refactored + R_SetupFrameQ(); +} +#if 0 + +/* +============= +R_SetupGL +============= +*/ +void R_SetupGL( qboolean set_gl_state ) +{ + R_SetupModelviewMatrix( RI.worldviewMatrix ); + R_SetupProjectionMatrix( RI.projectionMatrix ); + + Matrix4x4_Concat( RI.worldviewProjectionMatrix, RI.projectionMatrix, RI.worldviewMatrix ); + + if( !set_gl_state ) return; + + if( RP_NORMALPASS( )) + { + int x, x2, y, y2; + + // set up viewport (main, playersetup) + x = floor( RI.viewport[0] * gpGlobals->width / gpGlobals->width ); + x2 = ceil(( RI.viewport[0] + RI.viewport[2] ) * gpGlobals->width / gpGlobals->width ); + y = floor( gpGlobals->height - RI.viewport[1] * gpGlobals->height / gpGlobals->height ); + y2 = ceil( gpGlobals->height - ( RI.viewport[1] + RI.viewport[3] ) * gpGlobals->height / gpGlobals->height ); + + pglViewport( x, y2, x2 - x, y - y2 ); + } + else + { + // envpass, mirrorpass + pglViewport( RI.viewport[0], RI.viewport[1], RI.viewport[2], RI.viewport[3] ); + } + + pglMatrixMode( GL_PROJECTION ); + GL_LoadMatrix( RI.projectionMatrix ); + + pglMatrixMode( GL_MODELVIEW ); + GL_LoadMatrix( RI.worldviewMatrix ); + + if( FBitSet( RI.params, RP_CLIPPLANE )) + { + GLdouble clip[4]; + mplane_t *p = &RI.clipPlane; + + clip[0] = p->normal[0]; + clip[1] = p->normal[1]; + clip[2] = p->normal[2]; + clip[3] = -p->dist; + + pglClipPlane( GL_CLIP_PLANE0, clip ); + pglEnable( GL_CLIP_PLANE0 ); + } + + GL_Cull( GL_FRONT ); + + pglDisable( GL_BLEND ); + pglDisable( GL_ALPHA_TEST ); + pglColor4f( 1.0f, 1.0f, 1.0f, 1.0f ); +} + +/* +============= +R_EndGL +============= +*/ +static void R_EndGL( void ) +{ + if( RI.params & RP_CLIPPLANE ) + pglDisable( GL_CLIP_PLANE0 ); +} + +#endif + +/* +============= +R_RecursiveFindWaterTexture + +using to find source waterleaf with +watertexture to grab fog values from it +============= +*/ +static image_t *R_RecursiveFindWaterTexture( const mnode_t *node, const mnode_t *ignore, qboolean down ) +{ + image_t *tex = NULL; + + // assure the initial node is not null + // we could check it here, but we would rather check it + // outside the call to get rid of one additional recursion level + Assert( node != NULL ); + + // ignore solid nodes + if( node->contents == CONTENTS_SOLID ) + return NULL; + + if( node->contents < 0 ) + { + mleaf_t *pleaf; + msurface_t **mark; + int i, c; + + // ignore non-liquid leaves + if( node->contents != CONTENTS_WATER && node->contents != CONTENTS_LAVA && node->contents != CONTENTS_SLIME ) + return NULL; + + // find texture + pleaf = (mleaf_t *)node; + mark = pleaf->firstmarksurface; + c = pleaf->nummarksurfaces; + + for( i = 0; i < c; i++, mark++ ) + { + if( (*mark)->flags & SURF_DRAWTURB && (*mark)->texinfo && (*mark)->texinfo->texture ) + return R_GetTexture( (*mark)->texinfo->texture->gl_texturenum ); + } + + // texture not found + return NULL; + } + + // this is a regular node + // traverse children + if( node->children[0] && ( node->children[0] != ignore )) + { + tex = R_RecursiveFindWaterTexture( node->children[0], node, true ); + if( tex ) return tex; + } + + if( node->children[1] && ( node->children[1] != ignore )) + { + tex = R_RecursiveFindWaterTexture( node->children[1], node, true ); + if( tex ) return tex; + } + + // for down recursion, return immediately + if( down ) return NULL; + + // texture not found, step up if any + if( node->parent ) + return R_RecursiveFindWaterTexture( node->parent, node, false ); + + // top-level node, bail out + return NULL; +} + +/* +============= +R_CheckFog + +check for underwater fog +Using backward recursion to find waterline leaf +from underwater leaf (idea: XaeroX) +============= +*/ +static void R_CheckFog( void ) +{ + cl_entity_t *ent; + image_t *tex; + int i, cnt, count; + + // quake global fog + if( gEngfuncs.Host_IsQuakeCompatible( )) + { + if( !MOVEVARS->fog_settings ) + { + // if( pglIsEnabled( GL_FOG )) + // pglDisable( GL_FOG ); + RI.fogEnabled = false; + return; + } + + // quake-style global fog + RI.fogColor[0] = ((MOVEVARS->fog_settings & 0xFF000000) >> 24) / 255.0f; + RI.fogColor[1] = ((MOVEVARS->fog_settings & 0xFF0000) >> 16) / 255.0f; + RI.fogColor[2] = ((MOVEVARS->fog_settings & 0xFF00) >> 8) / 255.0f; + RI.fogDensity = ((MOVEVARS->fog_settings & 0xFF) / 255.0f) * 0.01f; + RI.fogStart = RI.fogEnd = 0.0f; + RI.fogColor[3] = 1.0f; + RI.fogCustom = false; + RI.fogEnabled = true; + RI.fogSkybox = true; + return; + } + + RI.fogEnabled = false; + + if( RI.onlyClientDraw || gEngfuncs.GetWaterLevel() < 3 || !RI.drawWorld || !RI.viewleaf ) + { + if( RI.cached_waterlevel == 3 ) + { + // in some cases waterlevel jumps from 3 to 1. Catch it + RI.cached_waterlevel = gEngfuncs.GetWaterLevel(); + RI.cached_contents = CONTENTS_EMPTY; + //if( !RI.fogCustom ) pglDisable( GL_FOG ); + } + return; + } + + ent = gEngfuncs.CL_GetWaterEntity( RI.vieworg ); + if( ent && ent->model && ent->model->type == mod_brush && ent->curstate.skin < 0 ) + cnt = ent->curstate.skin; + else cnt = RI.viewleaf->contents; + + RI.cached_waterlevel = gEngfuncs.GetWaterLevel(); + + if( !IsLiquidContents( RI.cached_contents ) && IsLiquidContents( cnt )) + { + tex = NULL; + + // check for water texture + if( ent && ent->model && ent->model->type == mod_brush ) + { + msurface_t *surf; + + count = ent->model->nummodelsurfaces; + + for( i = 0, surf = &ent->model->surfaces[ent->model->firstmodelsurface]; i < count; i++, surf++ ) + { + if( surf->flags & SURF_DRAWTURB && surf->texinfo && surf->texinfo->texture ) + { + tex = R_GetTexture( surf->texinfo->texture->gl_texturenum ); + RI.cached_contents = ent->curstate.skin; + break; + } + } + } + else + { + tex = R_RecursiveFindWaterTexture( RI.viewleaf->parent, NULL, false ); + if( tex ) RI.cached_contents = RI.viewleaf->contents; + } + + if( !tex ) return; // no valid fogs + + // copy fog params + RI.fogColor[0] = tex->fogParams[0] / 255.0f; + RI.fogColor[1] = tex->fogParams[1] / 255.0f; + RI.fogColor[2] = tex->fogParams[2] / 255.0f; + RI.fogDensity = tex->fogParams[3] * 0.000025f; + RI.fogStart = RI.fogEnd = 0.0f; + RI.fogColor[3] = 1.0f; + RI.fogCustom = false; + RI.fogEnabled = true; + RI.fogSkybox = true; + } + else + { + RI.fogCustom = false; + RI.fogEnabled = true; + RI.fogSkybox = true; + } +} + +/* +============= +R_CheckGLFog + +special condition for Spirit 1.9 +that used direct calls of glFog-functions +============= +*/ +static void R_CheckGLFog( void ) +{ +#ifdef HACKS_RELATED_HLMODS + if(( !RI.fogEnabled && !RI.fogCustom ) && pglIsEnabled( GL_FOG ) && VectorIsNull( RI.fogColor )) + { + // fill the fog color from GL-state machine + pglGetFloatv( GL_FOG_COLOR, RI.fogColor ); + RI.fogSkybox = true; + } +#endif +} + +/* +============= +R_DrawFog + +============= +*/ +void R_DrawFog( void ) +{ + if( !RI.fogEnabled ) return; +#if 0 + pglEnable( GL_FOG ); + if( gEngfuncs.Host_IsQuakeCompatible( )) + pglFogi( GL_FOG_MODE, GL_EXP2 ); + else pglFogi( GL_FOG_MODE, GL_EXP ); + pglFogf( GL_FOG_DENSITY, RI.fogDensity ); + pglFogfv( GL_FOG_COLOR, RI.fogColor ); + pglHint( GL_FOG_HINT, GL_NICEST ); +#endif +} + +/* +============= +R_DrawEntitiesOnList +============= +*/ +void R_DrawEntitiesOnList( void ) +{ + int i; + + tr.blend = 1.0f; +// GL_CheckForErrors(); + + // first draw solid entities + for( i = 0; i < tr.draw_list->num_solid_entities && !RI.onlyClientDraw; i++ ) + { + RI.currententity = tr.draw_list->solid_entities[i]; + RI.currentmodel = RI.currententity->model; + + Assert( RI.currententity != NULL ); + Assert( RI.currentmodel != NULL ); + + switch( RI.currentmodel->type ) + { + case mod_brush: + //R_DrawBrushModel( RI.currententity ); + break; + case mod_alias: + //R_DrawAliasModel( RI.currententity ); + break; + case mod_studio: + //R_DrawStudioModel( RI.currententity ); + break; + default: + break; + } + } + +// GL_CheckForErrors(); + + // quake-specific feature +// R_DrawAlphaTextureChains(); + +// GL_CheckForErrors(); + + // draw sprites seperately, because of alpha blending + for( i = 0; i < tr.draw_list->num_solid_entities && !RI.onlyClientDraw; i++ ) + { + RI.currententity = tr.draw_list->solid_entities[i]; + RI.currentmodel = RI.currententity->model; + + Assert( RI.currententity != NULL ); + Assert( RI.currentmodel != NULL ); + + switch( RI.currentmodel->type ) + { + case mod_sprite: + // R_DrawSpriteModel( RI.currententity ); + break; + } + } + +// GL_CheckForErrors(); + + if( !RI.onlyClientDraw ) + { + gEngfuncs.CL_DrawEFX( tr.frametime, false ); + } + +// GL_CheckForErrors(); + + if( RI.drawWorld ) + gEngfuncs.pfnDrawNormalTriangles(); + +// GL_CheckForErrors(); + + // then draw translucent entities + for( i = 0; i < tr.draw_list->num_trans_entities && !RI.onlyClientDraw; i++ ) + { + RI.currententity = tr.draw_list->trans_entities[i]; + RI.currentmodel = RI.currententity->model; + + // handle studiomodels with custom rendermodes on texture + if( RI.currententity->curstate.rendermode != kRenderNormal ) + tr.blend = gEngfuncs.CL_FxBlend( RI.currententity ) / 255.0f; + else tr.blend = 1.0f; // draw as solid but sorted by distance + + if( tr.blend <= 0.0f ) continue; + + Assert( RI.currententity != NULL ); + Assert( RI.currentmodel != NULL ); + + switch( RI.currentmodel->type ) + { + case mod_brush: + // R_DrawBrushModel( RI.currententity ); + break; + case mod_alias: + //R_DrawAliasModel( RI.currententity ); + break; + case mod_studio: + // R_DrawStudioModel( RI.currententity ); + break; + case mod_sprite: + // R_DrawSpriteModel( RI.currententity ); + break; + default: + break; + } + } + +// GL_CheckForErrors(); + + if( RI.drawWorld ) + { + // pglTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ); + gEngfuncs.pfnDrawTransparentTriangles (); + } + +// GL_CheckForErrors(); + + if( !RI.onlyClientDraw ) + { + R_AllowFog( false ); + gEngfuncs.CL_DrawEFX( tr.frametime, true ); + R_AllowFog( true ); + } + + //GL_CheckForErrors(); + +// pglDisable( GL_BLEND ); // Trinity Render issues + + //if( !RI.onlyClientDraw ) + //R_DrawViewModel(); + gEngfuncs.CL_ExtraUpdate(); + + //GL_CheckForErrors(); +} + +#if 0 + +/* +============= +R_DrawBEntitiesOnList +============= +*/ +void R_DrawBEntitiesOnList (void) +{ + int i, clipflags; + vec3_t oldorigin; + vec3_t mins, maxs; + float minmaxs[6]; + mnode_t *topnode; + + if (!r_drawentities->value) + return; + + VectorCopy (modelorg, oldorigin); + insubmodel = true; + r_dlightframecount = r_framecount; + + for (i=0 ; imodel; + if (!currentmodel) + continue; + if (currentmodel->nummodelsurfaces == 0) + continue; // clip brush only + if ( currententity->flags & RF_BEAM ) + continue; + if (currentmodel->type != mod_brush) + continue; + // see if the bounding box lets us trivially reject, also sets + // trivial accept status + RotatedBBox (currentmodel->mins, currentmodel->maxs, + currententity->angles, mins, maxs); + VectorAdd (mins, currententity->origin, minmaxs); + VectorAdd (maxs, currententity->origin, (minmaxs+3)); + + clipflags = R_BmodelCheckBBox (minmaxs); + if (clipflags == BMODEL_FULLY_CLIPPED) + continue; // off the edge of the screen + + topnode = R_FindTopnode (minmaxs, minmaxs+3); + if (!topnode) + continue; // no part in a visible leaf + + VectorCopy (currententity->origin, r_entorigin); + VectorSubtract (r_origin, r_entorigin, modelorg); + + r_pcurrentvertbase = currentmodel->vertexes; + + // FIXME: stop transforming twice + R_RotateBmodel (); + + // calculate dynamic lighting for bmodel + R_PushDlights (currentmodel); + + if (topnode->contents == CONTENTS_NODE) + { + // not a leaf; has to be clipped to the world BSP + r_clipflags = clipflags; + R_DrawSolidClippedSubmodelPolygons (currentmodel, topnode); + } + else + { + // falls entirely in one leaf, so we just put all the + // edges in the edge list and let 1/z sorting handle + // drawing order + R_DrawSubmodelPolygons (currentmodel, clipflags, topnode); + } + + // put back world rotation and frustum clipping + // FIXME: R_RotateBmodel should just work off base_vxx + VectorCopy (base_vpn, vpn); + VectorCopy (base_vup, vup); + VectorCopy (base_vright, vright); + VectorCopy (oldorigin, modelorg); + R_TransformFrustum (); + } + + insubmodel = false; +} +#endif + +/* +================ +R_EdgeDrawing +================ +*/ +void R_EdgeDrawing (void) +{ + edge_t ledges[NUMSTACKEDGES + + ((CACHE_SIZE - 1) / sizeof(edge_t)) + 1]; + surf_t lsurfs[NUMSTACKSURFACES + + ((CACHE_SIZE - 1) / sizeof(surf_t)) + 1]; + + if ( !RI.drawWorld ) + return; + + if (auxedges) + { + r_edges = auxedges; + } + else + { + r_edges = (edge_t *) + (((long)&ledges[0] + CACHE_SIZE - 1) & ~(CACHE_SIZE - 1)); + } + + if (r_surfsonstack) + { + surfaces = (surf_t *) + (((long)&lsurfs[0] + CACHE_SIZE - 1) & ~(CACHE_SIZE - 1)); + surf_max = &surfaces[r_cnumsurfs]; + // surface 0 doesn't really exist; it's just a dummy because index 0 + // is used to indicate no edge attached to surface + surfaces--; + R_SurfacePatch (); + } + + R_BeginEdgeFrame (); + + // this will prepare edges + R_RenderWorld (); + + // move brushes to separate list to merge with edges? + //R_DrawBEntitiesOnList (); + + // display all edges + R_ScanEdges (); +} + +/* +================ +R_RenderScene + +R_SetupRefParams must be called right before +================ +*/ +void R_RenderScene( void ) +{ + if( !WORLDMODEL && RI.drawWorld ) + gEngfuncs.Host_Error( "R_RenderView: NULL worldmodel\n" ); + + // frametime is valid only for normal pass + if( RP_NORMALPASS( )) + tr.frametime = gpGlobals->time - gpGlobals->oldtime; + else tr.frametime = 0.0; + + // begin a new frame + tr.framecount++; + +// R_PushDlights(); + + R_SetupFrustum(); + R_SetupFrame(); +// R_SetupGL( true ); + R_Clear( ~0 ); + + //R_MarkLeaves(); + R_DrawFog (); + // R_PushDlights (r_worldmodel); ?? + R_CheckGLFog(); + //R_DrawWorld(); + R_EdgeDrawing (); + R_CheckFog(); + + gEngfuncs.CL_ExtraUpdate (); // don't let sound get messed up if going slow + + R_DrawEntitiesOnList(); + +// R_DrawWaterSurfaces(); + +// R_EndGL(); +} + +/* +=============== +R_DoResetGamma + +gamma will be reset for +some type of screenshots +=============== +*/ +qboolean R_DoResetGamma( void ) +{ + // FIXME: this looks ugly. apply the backward gamma changes to the output image + return false; +#if 0 + switch( cls.scrshot_action ) + { + case scrshot_normal: + if( CL_IsDevOverviewMode( )) + return true; + return false; + case scrshot_snapshot: + if( CL_IsDevOverviewMode( )) + return true; + return false; + case scrshot_plaque: + case scrshot_savegame: + case scrshot_envshot: + case scrshot_skyshot: + case scrshot_mapshot: + return true; + default: + return false; + } +#endif +} + +/* +=============== +R_BeginFrame +=============== +*/ +void R_BeginFrame( qboolean clearScene ) +{ + + if( R_DoResetGamma( )) + { + gEngfuncs.BuildGammaTable( 1.8f, 0.0f ); + // glConfig.softwareGammaUpdate = true; + // GL_RebuildLightmaps(); + // glConfig.softwareGammaUpdate = false; + + // next frame will be restored gamma + // SetBits( vid_brightness->flags, FCVAR_CHANGED ); + // SetBits( vid_gamma->flags, FCVAR_CHANGED ); + } + else if( FBitSet( vid_gamma->flags, FCVAR_CHANGED ) || FBitSet( vid_brightness->flags, FCVAR_CHANGED )) + { + // gEngfuncs.BuildGammaTable( vid_gamma->value, vid_brightness->value ); + //glConfig.softwareGammaUpdate = true; + // GL_RebuildLightmaps(); + //glConfig.softwareGammaUpdate = false; + } + + R_Set2DMode( true ); + + // draw buffer stuff + //pglDrawBuffer( GL_BACK ); + + // update texture parameters + //if( FBitSet( gl_texture_nearest->flags|gl_lightmap_nearest->flags|gl_texture_anisotropy->flags|gl_texture_lodbias->flags, FCVAR_CHANGED )) + //R_SetTextureParameters(); + + gEngfuncs.CL_ExtraUpdate (); +} + +/* +=============== +R_SetupRefParams + +set initial params for renderer +=============== +*/ +void R_SetupRefParams( const ref_viewpass_t *rvp ) +{ + RI.params = RP_NONE; + RI.drawWorld = FBitSet( rvp->flags, RF_DRAW_WORLD ); + RI.onlyClientDraw = FBitSet( rvp->flags, RF_ONLY_CLIENTDRAW ); + RI.farClip = 0; + + if( !FBitSet( rvp->flags, RF_DRAW_CUBEMAP )) + RI.drawOrtho = FBitSet( rvp->flags, RF_DRAW_OVERVIEW ); + else RI.drawOrtho = false; + + // setup viewport + RI.viewport[0] = rvp->viewport[0]; + RI.viewport[1] = rvp->viewport[1]; + RI.viewport[2] = rvp->viewport[2]; + RI.viewport[3] = rvp->viewport[3]; + + // calc FOV + RI.fov_x = rvp->fov_x; + RI.fov_y = rvp->fov_y; + + VectorCopy( rvp->vieworigin, RI.vieworg ); + VectorCopy( rvp->viewangles, RI.viewangles ); + VectorCopy( rvp->vieworigin, RI.pvsorigin ); +} + +/* +=============== +R_RenderFrame +=============== +*/ +int R_RenderFrame( const ref_viewpass_t *rvp ) +{ + if( r_norefresh->value ) + return 1; + + // setup the initial render params + R_SetupRefParams( rvp ); + + // completely override rendering + if( gEngfuncs.drawFuncs->GL_RenderFrame != NULL ) + { + tr.fCustomRendering = true; + + if( gEngfuncs.drawFuncs->GL_RenderFrame( rvp )) + { + //R_GatherPlayerLight(); + tr.realframecount++; + tr.fResetVis = true; + return 1; + } + } + + tr.fCustomRendering = false; +// if( !RI.onlyClientDraw ) + // R_RunViewmodelEvents(); + + tr.realframecount++; // right called after viewmodel events + R_RenderScene(); + + return 1; +} + +/* +=============== +R_EndFrame +=============== +*/ +void R_EndFrame( void ) +{ + // flush any remaining 2D bits + R_Set2DMode( false ); + + // blit pixels with GL until engine supports REF_SOFT context + R_BlitScreen(); +} + +/* +=============== +R_DrawCubemapView +=============== +*/ +void R_DrawCubemapView( const vec3_t origin, const vec3_t angles, int size ) +{ + ref_viewpass_t rvp; + + // basic params + rvp.flags = rvp.viewentity = 0; + SetBits( rvp.flags, RF_DRAW_WORLD ); + SetBits( rvp.flags, RF_DRAW_CUBEMAP ); + + rvp.viewport[0] = rvp.viewport[1] = 0; + rvp.viewport[2] = rvp.viewport[3] = size; + rvp.fov_x = rvp.fov_y = 90.0f; // this is a final fov value + + // setup origin & angles + VectorCopy( origin, rvp.vieworigin ); + VectorCopy( angles, rvp.viewangles ); + + R_RenderFrame( &rvp ); + + RI.viewleaf = NULL; // force markleafs next frame +} + +/* +=============== +R_NewMap +=============== +*/ +void R_NewMap (void) +{ + r_viewcluster = -1; + + r_cnumsurfs = sw_maxsurfs->value; + + if (r_cnumsurfs <= MINSURFACES) + r_cnumsurfs = MINSURFACES; + + if (r_cnumsurfs > NUMSTACKSURFACES) + { + surfaces = malloc (r_cnumsurfs * sizeof(surf_t)); + surface_p = surfaces; + surf_max = &surfaces[r_cnumsurfs]; + r_surfsonstack = false; + // surface 0 doesn't really exist; it's just a dummy because index 0 + // is used to indicate no edge attached to surface + surfaces--; + R_SurfacePatch (); + } + else + { + r_surfsonstack = true; + } + + r_maxedgesseen = 0; + r_maxsurfsseen = 0; + + r_numallocatededges = sw_maxedges->value; + + if (r_numallocatededges < MINEDGES) + r_numallocatededges = MINEDGES; + + if (r_numallocatededges <= NUMSTACKEDGES) + { + auxedges = NULL; + } + else + { + auxedges = malloc (r_numallocatededges * sizeof(edge_t)); + } +} + + + + +qboolean R_Init() +{ + gl_emboss_scale = gEngfuncs.Cvar_Get( "gl_emboss_scale", "0", FCVAR_ARCHIVE|FCVAR_LATCH, "fake bumpmapping scale" ); + vid_gamma = gEngfuncs.pfnGetCvarPointer( "gamma", 0 ); + r_norefresh = gEngfuncs.Cvar_Get( "r_norefresh", "0", 0, "disable 3D rendering (use with caution)" ); + r_drawentities = gEngfuncs.Cvar_Get( "r_drawentities", "1", FCVAR_CHEAT, "render entities" ); + vid_brightness = gEngfuncs.pfnGetCvarPointer( "brightness", 0 ); + r_fullbright = gEngfuncs.Cvar_Get( "r_fullbright", "0", FCVAR_CHEAT, "disable lightmaps, get fullbright for entities" ); + sw_mipcap = gEngfuncs.Cvar_Get( "r_fullbright", "0", FCVAR_CHEAT, "disable lightmaps, get fullbright for entities" ); + +// sw_aliasstats = ri.Cvar_Get ("sw_polymodelstats", "0", 0); +// sw_allow_modex = ri.Cvar_Get( "sw_allow_modex", "1", CVAR_ARCHIVE ); + sw_clearcolor = gEngfuncs.Cvar_Get ("sw_clearcolor", "2", 0, "screen clear color"); + sw_drawflat = gEngfuncs.Cvar_Get ("sw_drawflat", "0", 0, ""); + sw_draworder = gEngfuncs.Cvar_Get ("sw_draworder", "0", 0, ""); + sw_maxedges = gEngfuncs.Cvar_Get ("sw_maxedges", "32", 0, ""); + sw_maxsurfs = gEngfuncs.Cvar_Get ("sw_maxsurfs", "0", 0, ""); + sw_mipscale = gEngfuncs.Cvar_Get ("sw_mipscale", "1", 0, ""); + sw_reportedgeout = gEngfuncs.Cvar_Get ("sw_reportedgeout", "0", 0, ""); + sw_reportsurfout = gEngfuncs.Cvar_Get ("sw_reportsurfout", "0", 0, ""); + sw_stipplealpha = gEngfuncs.Cvar_Get( "sw_stipplealpha", "0", FCVAR_ARCHIVE, "" ); + sw_surfcacheoverride = gEngfuncs.Cvar_Get ("sw_surfcacheoverride", "0", 0, ""); + sw_waterwarp = gEngfuncs.Cvar_Get ("sw_waterwarp", "1", 0, ""); + sw_mode = gEngfuncs.Cvar_Get( "sw_mode", "0", FCVAR_ARCHIVE, ""); + + //r_lefthand = ri.Cvar_Get( "hand", "0", FCVAR_USERINFO | FCVAR_ARCHIVE ); +// r_speeds = ri.Cvar_Get ("r_speeds", "0", 0); + + //r_drawworld = ri.Cvar_Get ("r_drawworld", "1", 0); + //r_dspeeds = ri.Cvar_Get ("r_dspeeds", "0", 0); +// r_lightlevel = ri.Cvar_Get ("r_lightlevel", "0", 0); + //r_lerpmodels = ri.Cvar_Get( "r_lerpmodels", "1", 0 ); + //r_novis = ri.Cvar_Get( "r_novis", "0", 0 ); + + // create the window and set up the context + r_temppool = Mem_AllocPool( "ref_sw zone" ); + + vid.width = 1920; + vid.height = 1080; + vid.rowbytes = 1920; // rowpixels + + vid.buffer = Mem_Malloc( r_temppool, 1920*1080*sizeof( pixel_t ) ); + if( !gEngfuncs.R_Init_Video( REF_GL )) // request GL context + { + gEngfuncs.R_Free_Video(); + + gEngfuncs.Host_Error( "Can't initialize video subsystem\nProbably driver was not installed" ); + return false; + } + + + R_InitImages(); + // init draw stack + tr.draw_list = &tr.draw_stack[0]; + tr.draw_stack_pos = 0; + return true; +} + +void R_Shutdown() +{ + R_ShutdownImages(); + gEngfuncs.R_Free_Video(); +} + diff --git a/r_misc.c b/r_misc.c new file mode 100644 index 00000000..6ba955ac --- /dev/null +++ b/r_misc.c @@ -0,0 +1,421 @@ +/* +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_misc.c + +#include "r_local.h" + +#define NUM_MIPS 4 + +cvar_t *sw_mipcap; +cvar_t *sw_mipscale; + +surfcache_t *d_initial_rover; +qboolean d_roverwrapped; +int d_minmip; +float d_scalemip[NUM_MIPS-1]; + +static float basemip[NUM_MIPS-1] = {1.0, 0.5*0.8, 0.25*0.8}; + +extern int d_aflatcolor; + +int d_vrectx, d_vrecty, d_vrectright_particle, d_vrectbottom_particle; + +int d_pix_min, d_pix_max, d_pix_shift; + +int d_scantable[MAXHEIGHT]; +short *zspantable[MAXHEIGHT]; + +/* +================ +D_Patch +================ +*/ +void D_Patch (void) +{ +#if id386 + extern void D_Aff8Patch( void ); + static qboolean protectset8 = false; + extern void D_PolysetAff8Start( void ); + + if (!protectset8) + { + Sys_MakeCodeWriteable ((int)D_PolysetAff8Start, + (int)D_Aff8Patch - (int)D_PolysetAff8Start); + Sys_MakeCodeWriteable ((long)R_Surf8Start, + (long)R_Surf8End - (long)R_Surf8Start); + protectset8 = true; + } + colormap = vid.colormap; + + R_Surf8Patch (); + D_Aff8Patch(); +#endif +} +/* +================ +D_ViewChanged +================ +*/ +unsigned char *alias_colormap; + +void D_ViewChanged (void) +{ + int i; + + scale_for_mip = xscale; + if (yscale > xscale) + scale_for_mip = yscale; + + d_zrowbytes = vid.width * 2; + d_zwidth = vid.width; + + d_pix_min = gpGlobals->width / 320; + if (d_pix_min < 1) + d_pix_min = 1; + + d_pix_max = (int)((float)gpGlobals->height / (320.0 / 4.0) + 0.5); + d_pix_shift = 8 - (int)((float)gpGlobals->height / 320.0 + 0.5); + if (d_pix_max < 1) + d_pix_max = 1; + + d_vrectx = 0;//r_refdef.vrect.x; + d_vrecty = 0;//r_refdef.vrect.y; + d_vrectright_particle = gpGlobals->width - d_pix_max; + d_vrectbottom_particle = + gpGlobals->height - d_pix_max; + + for (i=0 ; iwidth, gpGlobals->height,( int ) sw_clearcolor->value & 0xff ); + } + + alias_colormap = vid.colormap; + + D_Patch (); +} + + + +/* +=================== +R_TransformFrustum +=================== +*/ +void R_TransformFrustum (void) +{ + int i; + vec3_t v, v2; + + for (i=0 ; i<4 ; i++) + { + v[0] = screenedge[i].normal[2]; + v[1] = -screenedge[i].normal[0]; + v[2] = screenedge[i].normal[1]; + + v2[0] = v[1]*vright[0] + v[2]*vup[0] + v[0]*vpn[0]; + v2[1] = v[1]*vright[1] + v[2]*vup[1] + v[0]*vpn[1]; + v2[2] = v[1]*vright[2] + v[2]*vup[2] + v[0]*vpn[2]; + + VectorCopy (v2, view_clipplanes[i].normal); + + view_clipplanes[i].dist = DotProduct (modelorg, v2); + } +} + + +/* +================ +TransformVector +================ +*/ +void TransformVector (vec3_t in, vec3_t out) +{ + out[0] = DotProduct(in,vright); + out[1] = DotProduct(in,vup); + out[2] = DotProduct(in,vpn); +} + +/* +================ +R_TransformPlane +================ +*/ +void R_TransformPlane (mplane_t *p, float *normal, float *dist) +{ + float d; + + d = DotProduct (RI.vieworg, p->normal); + *dist = p->dist - d; +// TODO: when we have rotating entities, this will need to use the view matrix + TransformVector (p->normal, normal); +} + + +/* +=============== +R_SetUpFrustumIndexes +=============== +*/ +void R_SetUpFrustumIndexes (void) +{ + int i, j, *pindex; + + pindex = r_frustum_indexes; + + for (i=0 ; i<4 ; i++) + { + for (j=0 ; j<3 ; j++) + { + if (view_clipplanes[i].normal[j] < 0) + { + pindex[j] = j; + pindex[j+3] = j+3; + } + else + { + pindex[j] = j+3; + pindex[j+3] = j; + } + } + + // FIXME: do just once at start + pfrustum_indexes[i] = pindex; + pindex += 6; + } +} + +/* +=============== +R_ViewChanged + +Called every time the vid structure or r_refdef changes. +Guaranteed to be called before the first refresh +=============== +*/ +void R_ViewChanged (vrect_t *vr) +{ + int i; +#if 0 + r_refdef.vrect = *vr; + + r_refdef.horizontalFieldOfView = 2*tan((float)r_newrefdef.fov_x/360*M_PI);; + verticalFieldOfView = 2*tan((float)r_newrefdef.fov_y/360*M_PI); + + r_refdef.fvrectx = (float)r_refdef.vrect.x; + r_refdef.fvrectx_adj = (float)r_refdef.vrect.x - 0.5; + r_refdef.vrect_x_adj_shift20 = (r_refdef.vrect.x<<20) + (1<<19) - 1; + r_refdef.fvrecty = (float)r_refdef.vrect.y; + r_refdef.fvrecty_adj = (float)r_refdef.vrect.y - 0.5; + r_refdef.vrectright = r_refdef.vrect.x + r_refdef.vrect.width; + r_refdef.vrectright_adj_shift20 = (r_refdef.vrectright<<20) + (1<<19) - 1; + r_refdef.fvrectright = (float)r_refdef.vrectright; + r_refdef.fvrectright_adj = (float)r_refdef.vrectright - 0.5; + r_refdef.vrectrightedge = (float)r_refdef.vrectright - 0.99; + r_refdef.vrectbottom = r_refdef.vrect.y + r_refdef.vrect.height; + r_refdef.fvrectbottom = (float)r_refdef.vrectbottom; + r_refdef.fvrectbottom_adj = (float)r_refdef.vrectbottom - 0.5; + + r_refdef.aliasvrect.x = (int)(r_refdef.vrect.x * r_aliasuvscale); + r_refdef.aliasvrect.y = (int)(r_refdef.vrect.y * r_aliasuvscale); + r_refdef.aliasvrect.width = (int)(r_refdef.vrect.width * r_aliasuvscale); + r_refdef.aliasvrect.height = (int)(r_refdef.vrect.height * r_aliasuvscale); + r_refdef.aliasvrectright = r_refdef.aliasvrect.x + + r_refdef.aliasvrect.width; + r_refdef.aliasvrectbottom = r_refdef.aliasvrect.y + + r_refdef.aliasvrect.height; + + xOrigin = r_refdef.xOrigin; + yOrigin = r_refdef.yOrigin; + +// values for perspective projection +// if math were exact, the values would range from 0.5 to to range+0.5 +// hopefully they wll be in the 0.000001 to range+.999999 and truncate +// the polygon rasterization will never render in the first row or column +// but will definately render in the [range] row and column, so adjust the +// buffer origin to get an exact edge to edge fill + xcenter = ((float)r_refdef.vrect.width * XCENTERING) + + r_refdef.vrect.x - 0.5; + aliasxcenter = xcenter * r_aliasuvscale; + ycenter = ((float)r_refdef.vrect.height * YCENTERING) + + r_refdef.vrect.y - 0.5; + aliasycenter = ycenter * r_aliasuvscale; + + xscale = r_refdef.vrect.width / r_refdef.horizontalFieldOfView; + aliasxscale = xscale * r_aliasuvscale; + xscaleinv = 1.0 / xscale; + + yscale = xscale; + aliasyscale = yscale * r_aliasuvscale; + yscaleinv = 1.0 / yscale; + xscaleshrink = (r_refdef.vrect.width-6)/r_refdef.horizontalFieldOfView; + yscaleshrink = xscaleshrink; + +// left side clip + screenedge[0].normal[0] = -1.0 / (xOrigin*r_refdef.horizontalFieldOfView); + screenedge[0].normal[1] = 0; + screenedge[0].normal[2] = 1; + screenedge[0].type = PLANE_ANYZ; + +// right side clip + screenedge[1].normal[0] = + 1.0 / ((1.0-xOrigin)*r_refdef.horizontalFieldOfView); + screenedge[1].normal[1] = 0; + screenedge[1].normal[2] = 1; + screenedge[1].type = PLANE_ANYZ; + +// top side clip + screenedge[2].normal[0] = 0; + screenedge[2].normal[1] = -1.0 / (yOrigin*verticalFieldOfView); + screenedge[2].normal[2] = 1; + screenedge[2].type = PLANE_ANYZ; + +// bottom side clip + screenedge[3].normal[0] = 0; + screenedge[3].normal[1] = 1.0 / ((1.0-yOrigin)*verticalFieldOfView); + screenedge[3].normal[2] = 1; + screenedge[3].type = PLANE_ANYZ; +#endif + for (i=0 ; i<4 ; i++) + VectorNormalize (screenedge[i].normal); + + D_ViewChanged (); +} + + +/* +=============== +R_SetupFrame +=============== +*/ +void R_SetupFrameQ (void) +{ + int i; + vrect_t vrect; + + if (r_fullbright->flags & FCVAR_CHANGED) + { + r_fullbright->flags &= ~FCVAR_CHANGED; + D_FlushCaches (); // so all lighting changes + } + + r_framecount++; + + +// build the transformation matrix for the given view angles + VectorCopy (RI.vieworg, modelorg); + VectorCopy (RI.vieworg, r_origin); + + AngleVectors (RI.viewangles, vpn, vright, vup); + +// current viewleaf + if ( RI.drawWorld ) + { + r_viewleaf = gEngfuncs.Mod_PointInLeaf (r_origin, WORLDMODEL->nodes); + r_viewcluster = r_viewleaf->cluster; + } + +// if (sw_waterwarp->value && (r_newrefdef.rdflags & RDF_UNDERWATER) ) +// r_dowarp = true; +// else + r_dowarp = false; + + if (r_dowarp) + { // warp into off screen buffer + vrect.x = 0; + vrect.y = 0; + //vrect.width = r_newrefdef.width < WARP_WIDTH ? r_newrefdef.width : WARP_WIDTH; + //vrect.height = r_newrefdef.height < WARP_HEIGHT ? r_newrefdef.height : WARP_HEIGHT; + + d_viewbuffer = r_warpbuffer; + r_screenwidth = WARP_WIDTH; + } + else + { + vrect.x = 0;//r_newrefdef.x; + vrect.y = 0;//r_newrefdef.y; + vrect.width = gpGlobals->width; + vrect.height = gpGlobals->height; + + d_viewbuffer = (void *)vid.buffer; + r_screenwidth = vid.rowbytes; + } + + R_ViewChanged (&vrect); + +// start off with just the four screen edge clip planes + R_TransformFrustum (); + R_SetUpFrustumIndexes (); + +// save base values + VectorCopy (vpn, base_vpn); + VectorCopy (vright, base_vright); + VectorCopy (vup, base_vup); + +// clear frame counts +/* c_faceclip = 0; + d_spanpixcount = 0; + r_polycount = 0; + r_drawnpolycount = 0; + r_wholepolycount = 0; + r_amodels_drawn = 0; + r_outofsurfaces = 0; + r_outofedges = 0;*/ + +// d_setup + d_roverwrapped = false; + d_initial_rover = sc_rover; + + d_minmip = sw_mipcap->value; + if (d_minmip > 3) + d_minmip = 3; + else if (d_minmip < 0) + d_minmip = 0; + + for (i=0 ; i<(NUM_MIPS-1) ; i++) + d_scalemip[i] = basemip[i] * sw_mipscale->value; + + //d_aflatcolor = 0; +} + + +#if !id386 + +/* +================ +R_SurfacePatch +================ +*/ +/*void R_SurfacePatch (void) +{ + // we only patch code on Intel +} +*/ +#endif // !id386 diff --git a/r_rast.c b/r_rast.c new file mode 100644 index 00000000..19666843 --- /dev/null +++ b/r_rast.c @@ -0,0 +1,852 @@ +/* +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_rast.c + +#include + +#include "r_local.h" + +#define MAXLEFTCLIPEDGES 100 + +// !!! if these are changed, they must be changed in asm_draw.h too !!! +#define FULLY_CLIPPED_CACHED 0x80000000 +#define FRAMECOUNT_MASK 0x7FFFFFFF + +unsigned int cacheoffset; + +int c_faceclip; // number of faces clipped + + +clipplane_t *entity_clipplanes; +clipplane_t view_clipplanes[4]; +clipplane_t world_clipplanes[16]; + +medge_t *r_pedge; + +qboolean r_leftclipped, r_rightclipped; +static qboolean makeleftedge, makerightedge; +qboolean r_nearzionly; + +int sintable[1280]; +int intsintable[1280]; +int blanktable[1280]; // PGM + +mvertex_t r_leftenter, r_leftexit; +mvertex_t r_rightenter, r_rightexit; + +typedef struct +{ + float u,v; + int ceilv; +} evert_t; + +int r_emitted; +float r_nearzi; +float r_u1, r_v1, r_lzi1; +int r_ceilv1; + +qboolean r_lastvertvalid; +int r_skyframe; + +msurface_t *r_skyfaces; +mplane_t r_skyplanes[6]; +mtexinfo_t r_skytexinfo[6]; +mvertex_t *r_skyverts; +medge_t *r_skyedges; +int *r_skysurfedges; + +// I just copied this data from a box map... +int skybox_planes[12] = {2,-128, 0,-128, 2,128, 1,128, 0,128, 1,-128}; + +int box_surfedges[24] = { 1,2,3,4, -1,5,6,7, 8,9,-6,10, -2,-7,-9,11, + 12,-3,-11,-8, -12,-10,-5,-4}; +int box_edges[24] = { 1,2, 2,3, 3,4, 4,1, 1,5, 5,6, 6,2, 7,8, 8,6, 5,7, 8,3, 7,4}; + +int box_faces[6] = {0,0,2,2,2,0}; + +vec3_t box_vecs[6][2] = { + { {0,-1,0}, {-1,0,0} }, + { {0,1,0}, {0,0,-1} }, + { {0,-1,0}, {1,0,0} }, + { {1,0,0}, {0,0,-1} }, + { {0,-1,0}, {0,0,-1} }, + { {-1,0,0}, {0,0,-1} } +}; + +float box_verts[8][3] = { + {-1,-1,-1}, + {-1,1,-1}, + {1,1,-1}, + {1,-1,-1}, + {-1,-1,1}, + {-1,1,1}, + {1,-1,1}, + {1,1,1} +}; + +// down, west, up, north, east, south +// {"rt", "bk", "lf", "ft", "up", "dn"}; +#if 0 +/* +================ +R_InitSkyBox + +================ +*/ +void R_InitSkyBox (void) +{ + int i; + extern model_t *loadmodel; + + r_skyfaces = loadmodel->surfaces + loadmodel->numsurfaces; + loadmodel->numsurfaces += 6; + r_skyverts = loadmodel->vertexes + loadmodel->numvertexes; + loadmodel->numvertexes += 8; + r_skyedges = loadmodel->edges + loadmodel->numedges; + loadmodel->numedges += 12; + r_skysurfedges = loadmodel->surfedges + loadmodel->numsurfedges; + loadmodel->numsurfedges += 24; + if (loadmodel->numsurfaces > MAX_MAP_FACES + || loadmodel->numvertexes > MAX_MAP_VERTS + || loadmodel->numedges > MAX_MAP_EDGES) + ri.Sys_Error (ERR_DROP, "InitSkyBox: map overflow"); + + memset (r_skyfaces, 0, 6*sizeof(*r_skyfaces)); + for (i=0 ; i<6 ; i++) + { + r_skyplanes[i].normal[skybox_planes[i*2]] = 1; + r_skyplanes[i].dist = skybox_planes[i*2+1]; + + VectorCopy (box_vecs[i][0], r_skytexinfo[i].vecs[0]); + VectorCopy (box_vecs[i][1], r_skytexinfo[i].vecs[1]); + + r_skyfaces[i].plane = &r_skyplanes[i]; + r_skyfaces[i].numedges = 4; + r_skyfaces[i].flags = box_faces[i] | SURF_DRAWSKYBOX; + r_skyfaces[i].firstedge = loadmodel->numsurfedges-24+i*4; + r_skyfaces[i].texinfo = &r_skytexinfo[i]; + r_skyfaces[i].texturemins[0] = -128; + r_skyfaces[i].texturemins[1] = -128; + r_skyfaces[i].extents[0] = 256; + r_skyfaces[i].extents[1] = 256; + } + + for (i=0 ; i<24 ; i++) + if (box_surfedges[i] > 0) + r_skysurfedges[i] = loadmodel->numedges-13 + box_surfedges[i]; + else + r_skysurfedges[i] = - (loadmodel->numedges-13 + -box_surfedges[i]); + + for(i=0 ; i<12 ; i++) + { + r_skyedges[i].v[0] = loadmodel->numvertexes-9+box_edges[i*2+0]; + r_skyedges[i].v[1] = loadmodel->numvertexes-9+box_edges[i*2+1]; + r_skyedges[i].cachededgeoffset = 0; + } +} + +/* +================ +R_EmitSkyBox +================ +*/ +void R_EmitSkyBox (void) +{ + int i, j; + int oldkey; + + if (insubmodel) + return; // submodels should never have skies + if (r_skyframe == r_framecount) + return; // already set this frame + + r_skyframe = r_framecount; + + // set the eight fake vertexes + for (i=0 ; i<8 ; i++) + for (j=0 ; j<3 ; j++) + r_skyverts[i].position[j] = r_origin[j] + box_verts[i][j]*128; + + // set the six fake planes + for (i=0 ; i<6 ; i++) + if (skybox_planes[i*2+1] > 0) + r_skyplanes[i].dist = r_origin[skybox_planes[i*2]]+128; + else + r_skyplanes[i].dist = r_origin[skybox_planes[i*2]]-128; + + // fix texture offseets + for (i=0 ; i<6 ; i++) + { + r_skytexinfo[i].vecs[0][3] = -DotProduct (r_origin, r_skytexinfo[i].vecs[0]); + r_skytexinfo[i].vecs[1][3] = -DotProduct (r_origin, r_skytexinfo[i].vecs[1]); + } + + // emit the six faces + oldkey = r_currentkey; + r_currentkey = 0x7ffffff0; + for (i=0 ; i<6 ; i++) + { + R_RenderFace (r_skyfaces + i, 15); + } + r_currentkey = oldkey; // bsp sorting order +} + +#endif +#if !id386 + +/* +================ +R_EmitEdge +================ +*/ +void R_EmitEdge (mvertex_t *pv0, mvertex_t *pv1) +{ + edge_t *edge, *pcheck; + int u_check; + float u, u_step; + vec3_t local, transformed; + float *world; + int v, v2, ceilv0; + float scale, lzi0, u0, v0; + int side; + + if (r_lastvertvalid) + { + u0 = r_u1; + v0 = r_v1; + lzi0 = r_lzi1; + ceilv0 = r_ceilv1; + } + else + { + world = &pv0->position[0]; + + // transform and project + VectorSubtract (world, modelorg, local); + TransformVector (local, transformed); + + if (transformed[2] < NEAR_CLIP) + transformed[2] = NEAR_CLIP; + + lzi0 = 1.0 / transformed[2]; + + // FIXME: build x/yscale into transform? + scale = xscale * lzi0; + u0 = (xcenter + scale*transformed[0]); + if (u0 < 0) + u0 = 0; + if (u0 > gpGlobals->width) + u0 = gpGlobals->width; + + scale = yscale * lzi0; + v0 = (ycenter - scale*transformed[1]); + if (v0 < 0) + v0 = 0; + if (v0 > gpGlobals->height) + v0 = gpGlobals->height; + + ceilv0 = (int) ceil(v0); + } + + world = &pv1->position[0]; + +// transform and project + VectorSubtract (world, modelorg, local); + TransformVector (local, transformed); + + if (transformed[2] < NEAR_CLIP) + transformed[2] = NEAR_CLIP; + + r_lzi1 = 1.0 / transformed[2]; + + scale = xscale * r_lzi1; + r_u1 = (xcenter + scale*transformed[0]); + if (r_u1 < 0) + r_u1 = 0; + if (r_u1 > gpGlobals->width) + r_u1 = gpGlobals->width; + + scale = yscale * r_lzi1; + r_v1 = (ycenter - scale*transformed[1]); + if (r_v1 < 0) + r_v1 = 0; + if (r_v1 > gpGlobals->height) + r_v1 = gpGlobals->height; + + if (r_lzi1 > lzi0) + lzi0 = r_lzi1; + + if (lzi0 > r_nearzi) // for mipmap finding + r_nearzi = lzi0; + +// for right edges, all we want is the effect on 1/z + if (r_nearzionly) + return; + + r_emitted = 1; + + r_ceilv1 = (int) ceil(r_v1); + + +// create the edge + if (ceilv0 == r_ceilv1) + { + // we cache unclipped horizontal edges as fully clipped + if (cacheoffset != 0x7FFFFFFF) + { + cacheoffset = FULLY_CLIPPED_CACHED | + (r_framecount & FRAMECOUNT_MASK); + } + + return; // horizontal edge + } + + side = ceilv0 > r_ceilv1; + + edge = edge_p++; + + edge->owner = r_pedge; + + edge->nearzi = lzi0; + + if (side == 0) + { + // trailing edge (go from p1 to p2) + v = ceilv0; + v2 = r_ceilv1 - 1; + + edge->surfs[0] = surface_p - surfaces; + edge->surfs[1] = 0; + + u_step = ((r_u1 - u0) / (r_v1 - v0)); + u = u0 + ((float)v - v0) * u_step; + } + else + { + // leading edge (go from p2 to p1) + v2 = ceilv0 - 1; + v = r_ceilv1; + + edge->surfs[0] = 0; + edge->surfs[1] = surface_p - surfaces; + + u_step = ((u0 - r_u1) / (v0 - r_v1)); + u = r_u1 + ((float)v - r_v1) * u_step; + } + + edge->u_step = u_step*0x100000; + edge->u = u*0x100000 + 0xFFFFF; + +// we need to do this to avoid stepping off the edges if a very nearly +// horizontal edge is less than epsilon above a scan, and numeric error causes +// it to incorrectly extend to the scan, and the extension of the line goes off +// the edge of the screen +// FIXME: is this actually needed? +// if (edge->u < r_refdef.vrect_x_adj_shift20) +// edge->u = r_refdef.vrect_x_adj_shift20; +// if (edge->u > r_refdef.vrectright_adj_shift20) +// edge->u = r_refdef.vrectright_adj_shift20; + +// +// sort the edge in normally +// + u_check = edge->u; + if (edge->surfs[0]) + u_check++; // sort trailers after leaders + + if (!newedges[v] || newedges[v]->u >= u_check) + { + edge->next = newedges[v]; + newedges[v] = edge; + } + else + { + pcheck = newedges[v]; + while (pcheck->next && pcheck->next->u < u_check) + pcheck = pcheck->next; + edge->next = pcheck->next; + pcheck->next = edge; + } + + edge->nextremove = removeedges[v2]; + removeedges[v2] = edge; +} + + +/* +================ +R_ClipEdge +================ +*/ +void R_ClipEdge (mvertex_t *pv0, mvertex_t *pv1, clipplane_t *clip) +{ + float d0, d1, f; + mvertex_t clipvert; + + if (clip) + { + do + { + d0 = DotProduct (pv0->position, clip->normal) - clip->dist; + d1 = DotProduct (pv1->position, clip->normal) - clip->dist; + + if (d0 >= 0) + { + // point 0 is unclipped + if (d1 >= 0) + { + // both points are unclipped + continue; + } + + // only point 1 is clipped + + // we don't cache clipped edges + cacheoffset = 0x7FFFFFFF; + + f = d0 / (d0 - d1); + clipvert.position[0] = pv0->position[0] + + f * (pv1->position[0] - pv0->position[0]); + clipvert.position[1] = pv0->position[1] + + f * (pv1->position[1] - pv0->position[1]); + clipvert.position[2] = pv0->position[2] + + f * (pv1->position[2] - pv0->position[2]); + + if (clip->leftedge) + { + r_leftclipped = true; + r_leftexit = clipvert; + } + else if (clip->rightedge) + { + r_rightclipped = true; + r_rightexit = clipvert; + } + + R_ClipEdge (pv0, &clipvert, clip->next); + return; + } + else + { + // point 0 is clipped + if (d1 < 0) + { + // both points are clipped + // we do cache fully clipped edges + if (!r_leftclipped) + cacheoffset = FULLY_CLIPPED_CACHED | + (r_framecount & FRAMECOUNT_MASK); + return; + } + + // only point 0 is clipped + r_lastvertvalid = false; + + // we don't cache partially clipped edges + cacheoffset = 0x7FFFFFFF; + + f = d0 / (d0 - d1); + clipvert.position[0] = pv0->position[0] + + f * (pv1->position[0] - pv0->position[0]); + clipvert.position[1] = pv0->position[1] + + f * (pv1->position[1] - pv0->position[1]); + clipvert.position[2] = pv0->position[2] + + f * (pv1->position[2] - pv0->position[2]); + + if (clip->leftedge) + { + r_leftclipped = true; + r_leftenter = clipvert; + } + else if (clip->rightedge) + { + r_rightclipped = true; + r_rightenter = clipvert; + } + + R_ClipEdge (&clipvert, pv1, clip->next); + return; + } + } while ((clip = clip->next) != NULL); + } + +// add the edge + R_EmitEdge (pv0, pv1); +} + +#endif // !id386 + + +/* +================ +R_EmitCachedEdge +================ +*/ +void R_EmitCachedEdge (void) +{ + edge_t *pedge_t; + + pedge_t = (edge_t *)((unsigned long)r_edges + r_pedge->cachededgeoffset); + + if (!pedge_t->surfs[0]) + pedge_t->surfs[0] = surface_p - surfaces; + else + pedge_t->surfs[1] = surface_p - surfaces; + + if (pedge_t->nearzi > r_nearzi) // for mipmap finding + r_nearzi = pedge_t->nearzi; + + r_emitted = 1; +} + + +/* +================ +R_RenderFace +================ +*/ +void R_RenderFace (msurface_t *fa, int clipflags) +{ + int i, lindex; + unsigned mask; + mplane_t *pplane; + float distinv; + vec3_t p_normal; + medge_t *pedges, tedge; + clipplane_t *pclip; + + // translucent surfaces are not drawn by the edge renderer +/* if (fa->texinfo->flags & (SURF_TRANS33|SURF_TRANS66)) + { + fa->nextalphasurface = r_alpha_surfaces; + r_alpha_surfaces = fa; + return; + }*/ + + // sky surfaces encountered in the world will cause the + // environment box surfaces to be emited +/* if ( fa->texinfo->flags & SURF_SKY ) + { + R_EmitSkyBox (); + return; + }*/ + +// skip out if no more surfs + if ((surface_p) >= surf_max) + { + // r_outofsurfaces++; + return; + } + +// ditto if not enough edges left, or switch to auxedges if possible + if ((edge_p + fa->numedges + 4) >= edge_max) + { + //r_outofedges += fa->numedges; + return; + } + + c_faceclip++; + +// set up clip planes + pclip = NULL; + + for (i=3, mask = 0x08 ; i>=0 ; i--, mask >>= 1) + { + if (clipflags & mask) + { + view_clipplanes[i].next = pclip; + pclip = &view_clipplanes[i]; + } + } + +// push the edges through + r_emitted = 0; + r_nearzi = 0; + r_nearzionly = false; + makeleftedge = makerightedge = false; + pedges = RI.currentmodel->edges; + r_lastvertvalid = false; + + for (i=0 ; inumedges ; i++) + { + lindex = RI.currentmodel->surfedges[fa->firstedge + i]; + + if (lindex > 0) + { + r_pedge = &pedges[lindex]; + + // if the edge is cached, we can just reuse the edge + //if (!insubmodel) + { + if (r_pedge->cachededgeoffset & FULLY_CLIPPED_CACHED) + { + if ((r_pedge->cachededgeoffset & FRAMECOUNT_MASK) == + r_framecount) + { + r_lastvertvalid = false; + continue; + } + } + else + { + if ((((unsigned long)edge_p - (unsigned long)r_edges) > + r_pedge->cachededgeoffset) && + (((edge_t *)((unsigned long)r_edges + + r_pedge->cachededgeoffset))->owner == r_pedge)) + { + R_EmitCachedEdge (); + r_lastvertvalid = false; + continue; + } + } + } + + // assume it's cacheable + cacheoffset = (byte *)edge_p - (byte *)r_edges; + r_leftclipped = r_rightclipped = false; + R_ClipEdge (&r_pcurrentvertbase[r_pedge->v[0]], + &r_pcurrentvertbase[r_pedge->v[1]], + pclip); + r_pedge->cachededgeoffset = cacheoffset; + + if (r_leftclipped) + makeleftedge = true; + if (r_rightclipped) + makerightedge = true; + r_lastvertvalid = true; + } + else + { + lindex = -lindex; + r_pedge = &pedges[lindex]; + // if the edge is cached, we can just reuse the edge + //if (!insubmodel) + { + if (r_pedge->cachededgeoffset & FULLY_CLIPPED_CACHED) + { + if ((r_pedge->cachededgeoffset & FRAMECOUNT_MASK) == + r_framecount) + { + r_lastvertvalid = false; + continue; + } + } + else + { + // it's cached if the cached edge is valid and is owned + // by this medge_t + if ((((unsigned long)edge_p - (unsigned long)r_edges) > + r_pedge->cachededgeoffset) && + (((edge_t *)((unsigned long)r_edges + + r_pedge->cachededgeoffset))->owner == r_pedge)) + { + R_EmitCachedEdge (); + r_lastvertvalid = false; + continue; + } + } + } + + // assume it's cacheable + cacheoffset = (byte *)edge_p - (byte *)r_edges; + r_leftclipped = r_rightclipped = false; + R_ClipEdge (&r_pcurrentvertbase[r_pedge->v[1]], + &r_pcurrentvertbase[r_pedge->v[0]], + pclip); + r_pedge->cachededgeoffset = cacheoffset; + + if (r_leftclipped) + makeleftedge = true; + if (r_rightclipped) + makerightedge = true; + r_lastvertvalid = true; + } + } + +// if there was a clip off the left edge, add that edge too +// FIXME: faster to do in screen space? +// FIXME: share clipped edges? + if (makeleftedge) + { + r_pedge = &tedge; + r_lastvertvalid = false; + R_ClipEdge (&r_leftexit, &r_leftenter, pclip->next); + } + +// if there was a clip off the right edge, get the right r_nearzi + if (makerightedge) + { + r_pedge = &tedge; + r_lastvertvalid = false; + r_nearzionly = true; + R_ClipEdge (&r_rightexit, &r_rightenter, view_clipplanes[1].next); + } + +// if no edges made it out, return without posting the surface + if (!r_emitted) + return; + +// r_polycount++; + + surface_p->msurf = fa; + surface_p->nearzi = r_nearzi; + surface_p->flags = fa->flags; + //surface_p->insubmodel = insubmodel; + surface_p->spanstate = 0; + surface_p->entity = RI.currententity; + surface_p->key = r_currentkey++; + surface_p->spans = NULL; + + pplane = fa->plane; +// FIXME: cache this? + TransformVector (pplane->normal, p_normal); +// FIXME: cache this? + distinv = 1.0 / (pplane->dist - DotProduct (modelorg, pplane->normal)); + + surface_p->d_zistepu = p_normal[0] * xscaleinv * distinv; + surface_p->d_zistepv = -p_normal[1] * yscaleinv * distinv; + surface_p->d_ziorigin = p_normal[2] * distinv - + xcenter * surface_p->d_zistepu - + ycenter * surface_p->d_zistepv; + + surface_p++; +} + + +/* +================ +R_RenderBmodelFace +================ +*/ +void R_RenderBmodelFace (bedge_t *pedges, msurface_t *psurf) +{ + int i; + unsigned mask; + mplane_t *pplane; + float distinv; + vec3_t p_normal; + medge_t tedge; + clipplane_t *pclip; + + /*if (psurf->texinfo->flags & (SURF_TRANS33|SURF_TRANS66)) + { + psurf->nextalphasurface = r_alpha_surfaces; + r_alpha_surfaces = psurf; + return; + }*/ + +// skip out if no more surfs + if (surface_p >= surf_max) + { + //r_outofsurfaces++; + return; + } + +// ditto if not enough edges left, or switch to auxedges if possible + if ((edge_p + psurf->numedges + 4) >= edge_max) + { + //r_outofedges += psurf->numedges; + return; + } + + c_faceclip++; + +// this is a dummy to give the caching mechanism someplace to write to + r_pedge = &tedge; + +// set up clip planes + pclip = NULL; + + for (i=3, mask = 0x08 ; i>=0 ; i--, mask >>= 1) + { + if (r_clipflags & mask) + { + view_clipplanes[i].next = pclip; + pclip = &view_clipplanes[i]; + } + } + +// push the edges through + r_emitted = 0; + r_nearzi = 0; + r_nearzionly = false; + makeleftedge = makerightedge = false; +// FIXME: keep clipped bmodel edges in clockwise order so last vertex caching +// can be used? + r_lastvertvalid = false; + + for ( ; pedges ; pedges = pedges->pnext) + { + r_leftclipped = r_rightclipped = false; + R_ClipEdge (pedges->v[0], pedges->v[1], pclip); + + if (r_leftclipped) + makeleftedge = true; + if (r_rightclipped) + makerightedge = true; + } + +// if there was a clip off the left edge, add that edge too +// FIXME: faster to do in screen space? +// FIXME: share clipped edges? + if (makeleftedge) + { + r_pedge = &tedge; + R_ClipEdge (&r_leftexit, &r_leftenter, pclip->next); + } + +// if there was a clip off the right edge, get the right r_nearzi + if (makerightedge) + { + r_pedge = &tedge; + r_nearzionly = true; + R_ClipEdge (&r_rightexit, &r_rightenter, view_clipplanes[1].next); + } + +// if no edges made it out, return without posting the surface + if (!r_emitted) + return; + + //r_polycount++; + + surface_p->msurf = psurf; + surface_p->nearzi = r_nearzi; + surface_p->flags = psurf->flags; + surface_p->insubmodel = true; + surface_p->spanstate = 0; + surface_p->entity = RI.currententity; + surface_p->key = r_currentbkey; + surface_p->spans = NULL; + + pplane = psurf->plane; +// FIXME: cache this? + TransformVector (pplane->normal, p_normal); +// FIXME: cache this? + distinv = 1.0 / (pplane->dist - DotProduct (modelorg, pplane->normal)); + + surface_p->d_zistepu = p_normal[0] * xscaleinv * distinv; + surface_p->d_zistepv = -p_normal[1] * yscaleinv * distinv; + surface_p->d_ziorigin = p_normal[2] * distinv - + xcenter * surface_p->d_zistepu - + ycenter * surface_p->d_zistepv; + + surface_p++; +} + diff --git a/r_scan.c b/r_scan.c new file mode 100644 index 00000000..5aaece7f --- /dev/null +++ b/r_scan.c @@ -0,0 +1,593 @@ +/* +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. + +*/ +// d_scan.c +// +// Portable C scan-level rasterization code, all pixel depths. + +#include "r_local.h" + +unsigned char *r_turb_pbase, *r_turb_pdest; +fixed16_t r_turb_s, r_turb_t, r_turb_sstep, r_turb_tstep; +int *r_turb_turb; +int r_turb_spancount; + +void D_DrawTurbulent8Span (void); + + +/* +============= +D_WarpScreen + +this performs a slight compression of the screen at the same time as +the sine warp, to keep the edges from wrapping +============= +*/ +void D_WarpScreen (void) +{ +#if 0 + int w, h; + int u,v, u2, v2; + byte *dest; + int *turb; + int *col; + byte **row; + + static int cached_width, cached_height; + static byte *rowptr[1200+AMP2*2]; + static int column[1600+AMP2*2]; + + // + // these are constant over resolutions, and can be saved + // + w = r_newrefdef.width; + h = r_newrefdef.height; + if (w != cached_width || h != cached_height) + { + cached_width = w; + cached_height = h; + for (v=0 ; v>16)&(CYCLE-1)])>>16)&63; + tturb = ((r_turb_t + r_turb_turb[(r_turb_s>>16)&(CYCLE-1)])>>16)&63; + *r_turb_pdest++ = *(r_turb_pbase + (tturb<<6) + sturb); + r_turb_s += r_turb_sstep; + r_turb_t += r_turb_tstep; + } while (--r_turb_spancount > 0); +} + +#endif // !id386 + + +/* +============= +Turbulent8 +============= +*/ +void Turbulent8 (espan_t *pspan) +{ + int count; + fixed16_t snext, tnext; + float sdivz, tdivz, zi, z, du, dv, spancountminus1; + float sdivz16stepu, tdivz16stepu, zi16stepu; + + r_turb_turb = sintable + ((int)(gpGlobals->time*SPEED)&(CYCLE-1)); + + r_turb_sstep = 0; // keep compiler happy + r_turb_tstep = 0; // ditto + + r_turb_pbase = (unsigned char *)cacheblock; + + sdivz16stepu = d_sdivzstepu * 16; + tdivz16stepu = d_tdivzstepu * 16; + zi16stepu = d_zistepu * 16; + + do + { + r_turb_pdest = (unsigned char *)((byte *)d_viewbuffer + + (r_screenwidth * pspan->v) + pspan->u); + + count = pspan->count; + + // calculate the initial s/z, t/z, 1/z, s, and t and clamp + du = (float)pspan->u; + dv = (float)pspan->v; + + sdivz = d_sdivzorigin + dv*d_sdivzstepv + du*d_sdivzstepu; + tdivz = d_tdivzorigin + dv*d_tdivzstepv + du*d_tdivzstepu; + zi = d_ziorigin + dv*d_zistepv + du*d_zistepu; + z = (float)0x10000 / zi; // prescale to 16.16 fixed-point + + r_turb_s = (int)(sdivz * z) + sadjust; + if (r_turb_s > bbextents) + r_turb_s = bbextents; + else if (r_turb_s < 0) + r_turb_s = 0; + + r_turb_t = (int)(tdivz * z) + tadjust; + if (r_turb_t > bbextentt) + r_turb_t = bbextentt; + else if (r_turb_t < 0) + r_turb_t = 0; + + do + { + // calculate s and t at the far end of the span + if (count >= 16) + r_turb_spancount = 16; + else + r_turb_spancount = count; + + count -= r_turb_spancount; + + if (count) + { + // calculate s/z, t/z, zi->fixed s and t at far end of span, + // calculate s and t steps across span by shifting + sdivz += sdivz16stepu; + tdivz += tdivz16stepu; + zi += zi16stepu; + z = (float)0x10000 / zi; // prescale to 16.16 fixed-point + + snext = (int)(sdivz * z) + sadjust; + if (snext > bbextents) + snext = bbextents; + else if (snext < 16) + snext = 16; // prevent round-off error on <0 steps from + // from causing overstepping & running off the + // edge of the texture + + tnext = (int)(tdivz * z) + tadjust; + if (tnext > bbextentt) + tnext = bbextentt; + else if (tnext < 16) + tnext = 16; // guard against round-off error on <0 steps + + r_turb_sstep = (snext - r_turb_s) >> 4; + r_turb_tstep = (tnext - r_turb_t) >> 4; + } + else + { + // calculate s/z, t/z, zi->fixed s and t at last pixel in span (so + // can't step off polygon), clamp, calculate s and t steps across + // span by division, biasing steps low so we don't run off the + // texture + spancountminus1 = (float)(r_turb_spancount - 1); + sdivz += d_sdivzstepu * spancountminus1; + tdivz += d_tdivzstepu * spancountminus1; + zi += d_zistepu * spancountminus1; + z = (float)0x10000 / zi; // prescale to 16.16 fixed-point + snext = (int)(sdivz * z) + sadjust; + if (snext > bbextents) + snext = bbextents; + else if (snext < 16) + snext = 16; // prevent round-off error on <0 steps from + // from causing overstepping & running off the + // edge of the texture + + tnext = (int)(tdivz * z) + tadjust; + if (tnext > bbextentt) + tnext = bbextentt; + else if (tnext < 16) + tnext = 16; // guard against round-off error on <0 steps + + if (r_turb_spancount > 1) + { + r_turb_sstep = (snext - r_turb_s) / (r_turb_spancount - 1); + r_turb_tstep = (tnext - r_turb_t) / (r_turb_spancount - 1); + } + } + + r_turb_s = r_turb_s & ((CYCLE<<16)-1); + r_turb_t = r_turb_t & ((CYCLE<<16)-1); + + D_DrawTurbulent8Span (); + + r_turb_s = snext; + r_turb_t = tnext; + + } while (count > 0); + + } while ((pspan = pspan->pnext) != NULL); +} + +//==================== +//PGM +/* +============= +NonTurbulent8 - this is for drawing scrolling textures. they're warping water textures + but the turbulence is automatically 0. +============= +*/ +void NonTurbulent8 (espan_t *pspan) +{ + int count; + fixed16_t snext, tnext; + float sdivz, tdivz, zi, z, du, dv, spancountminus1; + float sdivz16stepu, tdivz16stepu, zi16stepu; + +// r_turb_turb = sintable + ((int)(r_newrefdef.time*SPEED)&(CYCLE-1)); + r_turb_turb = blanktable; + + r_turb_sstep = 0; // keep compiler happy + r_turb_tstep = 0; // ditto + + r_turb_pbase = (unsigned char *)cacheblock; + + sdivz16stepu = d_sdivzstepu * 16; + tdivz16stepu = d_tdivzstepu * 16; + zi16stepu = d_zistepu * 16; + + do + { + r_turb_pdest = (unsigned char *)((byte *)d_viewbuffer + + (r_screenwidth * pspan->v) + pspan->u); + + count = pspan->count; + + // calculate the initial s/z, t/z, 1/z, s, and t and clamp + du = (float)pspan->u; + dv = (float)pspan->v; + + sdivz = d_sdivzorigin + dv*d_sdivzstepv + du*d_sdivzstepu; + tdivz = d_tdivzorigin + dv*d_tdivzstepv + du*d_tdivzstepu; + zi = d_ziorigin + dv*d_zistepv + du*d_zistepu; + z = (float)0x10000 / zi; // prescale to 16.16 fixed-point + + r_turb_s = (int)(sdivz * z) + sadjust; + if (r_turb_s > bbextents) + r_turb_s = bbextents; + else if (r_turb_s < 0) + r_turb_s = 0; + + r_turb_t = (int)(tdivz * z) + tadjust; + if (r_turb_t > bbextentt) + r_turb_t = bbextentt; + else if (r_turb_t < 0) + r_turb_t = 0; + + do + { + // calculate s and t at the far end of the span + if (count >= 16) + r_turb_spancount = 16; + else + r_turb_spancount = count; + + count -= r_turb_spancount; + + if (count) + { + // calculate s/z, t/z, zi->fixed s and t at far end of span, + // calculate s and t steps across span by shifting + sdivz += sdivz16stepu; + tdivz += tdivz16stepu; + zi += zi16stepu; + z = (float)0x10000 / zi; // prescale to 16.16 fixed-point + + snext = (int)(sdivz * z) + sadjust; + if (snext > bbextents) + snext = bbextents; + else if (snext < 16) + snext = 16; // prevent round-off error on <0 steps from + // from causing overstepping & running off the + // edge of the texture + + tnext = (int)(tdivz * z) + tadjust; + if (tnext > bbextentt) + tnext = bbextentt; + else if (tnext < 16) + tnext = 16; // guard against round-off error on <0 steps + + r_turb_sstep = (snext - r_turb_s) >> 4; + r_turb_tstep = (tnext - r_turb_t) >> 4; + } + else + { + // calculate s/z, t/z, zi->fixed s and t at last pixel in span (so + // can't step off polygon), clamp, calculate s and t steps across + // span by division, biasing steps low so we don't run off the + // texture + spancountminus1 = (float)(r_turb_spancount - 1); + sdivz += d_sdivzstepu * spancountminus1; + tdivz += d_tdivzstepu * spancountminus1; + zi += d_zistepu * spancountminus1; + z = (float)0x10000 / zi; // prescale to 16.16 fixed-point + snext = (int)(sdivz * z) + sadjust; + if (snext > bbextents) + snext = bbextents; + else if (snext < 16) + snext = 16; // prevent round-off error on <0 steps from + // from causing overstepping & running off the + // edge of the texture + + tnext = (int)(tdivz * z) + tadjust; + if (tnext > bbextentt) + tnext = bbextentt; + else if (tnext < 16) + tnext = 16; // guard against round-off error on <0 steps + + if (r_turb_spancount > 1) + { + r_turb_sstep = (snext - r_turb_s) / (r_turb_spancount - 1); + r_turb_tstep = (tnext - r_turb_t) / (r_turb_spancount - 1); + } + } + + r_turb_s = r_turb_s & ((CYCLE<<16)-1); + r_turb_t = r_turb_t & ((CYCLE<<16)-1); + + D_DrawTurbulent8Span (); + + r_turb_s = snext; + r_turb_t = tnext; + + } while (count > 0); + + } while ((pspan = pspan->pnext) != NULL); +} +//PGM +//==================== + + +#if !id386 + +/* +============= +D_DrawSpans16 + + FIXME: actually make this subdivide by 16 instead of 8!!! +============= +*/ +void D_DrawSpans16 (espan_t *pspan) +{ + int count, spancount; + unsigned char *pbase, *pdest; + fixed16_t s, t, snext, tnext, sstep, tstep; + float sdivz, tdivz, zi, z, du, dv, spancountminus1; + float sdivz8stepu, tdivz8stepu, zi8stepu; + + sstep = 0; // keep compiler happy + tstep = 0; // ditto + + pbase = (unsigned char *)cacheblock; + + sdivz8stepu = d_sdivzstepu * 8; + tdivz8stepu = d_tdivzstepu * 8; + zi8stepu = d_zistepu * 8; + + do + { + pdest = (unsigned char *)((byte *)d_viewbuffer + + (r_screenwidth * pspan->v) + pspan->u); + + count = pspan->count; + + // calculate the initial s/z, t/z, 1/z, s, and t and clamp + du = (float)pspan->u; + dv = (float)pspan->v; + + sdivz = d_sdivzorigin + dv*d_sdivzstepv + du*d_sdivzstepu; + tdivz = d_tdivzorigin + dv*d_tdivzstepv + du*d_tdivzstepu; + zi = d_ziorigin + dv*d_zistepv + du*d_zistepu; + z = (float)0x10000 / zi; // prescale to 16.16 fixed-point + + s = (int)(sdivz * z) + sadjust; + if (s > bbextents) + s = bbextents; + else if (s < 0) + s = 0; + + t = (int)(tdivz * z) + tadjust; + if (t > bbextentt) + t = bbextentt; + else if (t < 0) + t = 0; + + do + { + // calculate s and t at the far end of the span + if (count >= 8) + spancount = 8; + else + spancount = count; + + count -= spancount; + + if (count) + { + // calculate s/z, t/z, zi->fixed s and t at far end of span, + // calculate s and t steps across span by shifting + sdivz += sdivz8stepu; + tdivz += tdivz8stepu; + zi += zi8stepu; + z = (float)0x10000 / zi; // prescale to 16.16 fixed-point + + snext = (int)(sdivz * z) + sadjust; + if (snext > bbextents) + snext = bbextents; + else if (snext < 8) + snext = 8; // prevent round-off error on <0 steps from + // from causing overstepping & running off the + // edge of the texture + + tnext = (int)(tdivz * z) + tadjust; + if (tnext > bbextentt) + tnext = bbextentt; + else if (tnext < 8) + tnext = 8; // guard against round-off error on <0 steps + + sstep = (snext - s) >> 3; + tstep = (tnext - t) >> 3; + } + else + { + // calculate s/z, t/z, zi->fixed s and t at last pixel in span (so + // can't step off polygon), clamp, calculate s and t steps across + // span by division, biasing steps low so we don't run off the + // texture + spancountminus1 = (float)(spancount - 1); + sdivz += d_sdivzstepu * spancountminus1; + tdivz += d_tdivzstepu * spancountminus1; + zi += d_zistepu * spancountminus1; + z = (float)0x10000 / zi; // prescale to 16.16 fixed-point + snext = (int)(sdivz * z) + sadjust; + if (snext > bbextents) + snext = bbextents; + else if (snext < 8) + snext = 8; // prevent round-off error on <0 steps from + // from causing overstepping & running off the + // edge of the texture + + tnext = (int)(tdivz * z) + tadjust; + if (tnext > bbextentt) + tnext = bbextentt; + else if (tnext < 8) + tnext = 8; // guard against round-off error on <0 steps + + if (spancount > 1) + { + sstep = (snext - s) / (spancount - 1); + tstep = (tnext - t) / (spancount - 1); + } + } + + do + { + *pdest++ = *(pbase + (s >> 16) + (t >> 16) * cachewidth); + s += sstep; + t += tstep; + } while (--spancount > 0); + + s = snext; + t = tnext; + + } while (count > 0); + + } while ((pspan = pspan->pnext) != NULL); +} + +#endif + + +#if !id386 + +/* +============= +D_DrawZSpans +============= +*/ +void D_DrawZSpans (espan_t *pspan) +{ + int count, doublecount, izistep; + int izi; + short *pdest; + unsigned ltemp; + float zi; + float du, dv; + +// FIXME: check for clamping/range problems +// we count on FP exceptions being turned off to avoid range problems + izistep = (int)(d_zistepu * 0x8000 * 0x10000); + + do + { + pdest = d_pzbuffer + (d_zwidth * pspan->v) + pspan->u; + + count = pspan->count; + + // calculate the initial 1/z + du = (float)pspan->u; + dv = (float)pspan->v; + + zi = d_ziorigin + dv*d_zistepv + du*d_zistepu; + // we count on FP exceptions being turned off to avoid range problems + izi = (int)(zi * 0x8000 * 0x10000); + + if ((long)pdest & 0x02) + { + *pdest++ = (short)(izi >> 16); + izi += izistep; + count--; + } + + if ((doublecount = count >> 1) > 0) + { + do + { + ltemp = izi >> 16; + izi += izistep; + ltemp |= izi & 0xFFFF0000; + izi += izistep; + *(int *)pdest = ltemp; + pdest += 2; + } while (--doublecount > 0); + } + + if (count & 1) + *pdest = (short)(izi >> 16); + + } while ((pspan = pspan->pnext) != NULL); +} + +#endif + diff --git a/r_surf.c b/r_surf.c new file mode 100644 index 00000000..cb17b9d1 --- /dev/null +++ b/r_surf.c @@ -0,0 +1,753 @@ +/* +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_surf.c: surface-related refresh code + +#include "r_local.h" + +drawsurf_t r_drawsurf; + +int lightleft, sourcesstep, blocksize, sourcetstep; +int lightdelta, lightdeltastep; +int lightright, lightleftstep, lightrightstep, blockdivshift; +unsigned blockdivmask; +void *prowdestbase; +unsigned char *pbasesource; +int surfrowbytes; // used by ASM files +unsigned *r_lightptr; +int r_stepback; +int r_lightwidth; +int r_numhblocks, r_numvblocks; +unsigned char *r_source, *r_sourcemax; + +void R_DrawSurfaceBlock8_mip0 (void); +void R_DrawSurfaceBlock8_mip1 (void); +void R_DrawSurfaceBlock8_mip2 (void); +void R_DrawSurfaceBlock8_mip3 (void); + +static void (*surfmiptable[4])(void) = { + R_DrawSurfaceBlock8_mip0, + R_DrawSurfaceBlock8_mip1, + R_DrawSurfaceBlock8_mip2, + R_DrawSurfaceBlock8_mip3 +}; + +void R_BuildLightMap (void); +extern unsigned blocklights[1024]; // allow some very large lightmaps + +float surfscale; +qboolean r_cache_thrash; // set if surface cache is thrashing + +int sc_size; +surfcache_t *sc_rover, *sc_base; + +static int rtable[MOD_FRAMES][MOD_FRAMES]; + +void R_InitRandomTable( void ) +{ + int tu, tv; + + // make random predictable + gEngfuncs.COM_SetRandomSeed( 255 ); + + for( tu = 0; tu < MOD_FRAMES; tu++ ) + { + for( tv = 0; tv < MOD_FRAMES; tv++ ) + { + rtable[tu][tv] = gEngfuncs.COM_RandomLong( 0, 0x7FFF ); + } + } + + gEngfuncs.COM_SetRandomSeed( 0 ); +} + +/* +=============== +R_TextureAnim + +Returns the proper texture for a given time and base texture, do not process random tiling +=============== +*/ +texture_t *R_TextureAnim( texture_t *b ) +{ + texture_t *base = b; + int count, reletive; + + if( RI.currententity->curstate.frame ) + { + if( base->alternate_anims ) + base = base->alternate_anims; + } + + if( !base->anim_total ) + return base; + if( base->name[0] == '-' ) + { + return b; // already tiled + } + else + { + int speed; + + // Quake1 textures uses 10 frames per second + if( FBitSet( R_GetTexture( base->gl_texturenum )->flags, TF_QUAKEPAL )) + speed = 10; + else speed = 20; + + reletive = (int)(gpGlobals->time * speed) % base->anim_total; + } + + + count = 0; + + while( base->anim_min > reletive || base->anim_max <= reletive ) + { + base = base->anim_next; + + if( !base || ++count > MOD_FRAMES ) + return b; + } + + return base; +} + +/* +=============== +R_TextureAnimation + +Returns the proper texture for a given time and surface +=============== +*/ +texture_t *R_TextureAnimation( msurface_t *s ) +{ + texture_t *base = s->texinfo->texture; + int count, reletive; + + if( RI.currententity && RI.currententity->curstate.frame ) + { + if( base->alternate_anims ) + base = base->alternate_anims; + } + + if( !base->anim_total ) + return base; + + if( base->name[0] == '-' ) + { + int tx = (int)((s->texturemins[0] + (base->width << 16)) / base->width) % MOD_FRAMES; + int ty = (int)((s->texturemins[1] + (base->height << 16)) / base->height) % MOD_FRAMES; + + reletive = rtable[tx][ty] % base->anim_total; + } + else + { + int speed; + + // Quake1 textures uses 10 frames per second + if( FBitSet( R_GetTexture( base->gl_texturenum )->flags, TF_QUAKEPAL )) + speed = 10; + else speed = 20; + + reletive = (int)(gpGlobals->time * speed) % base->anim_total; + } + + count = 0; + + while( base->anim_min > reletive || base->anim_max <= reletive ) + { + base = base->anim_next; + + if( !base || ++count > MOD_FRAMES ) + return s->texinfo->texture; + } + + return base; +} + + +/* +=============== +R_DrawSurface +=============== +*/ +void R_DrawSurface (void) +{ + unsigned char *basetptr; + int smax, tmax, twidth; + int u; + int soffset, basetoffset, texwidth; + int horzblockstep; + unsigned char *pcolumndest; + void (*pblockdrawer)(void); + image_t *mt; + + surfrowbytes = r_drawsurf.rowbytes; + + mt = r_drawsurf.image; + + r_source = mt->pixels[r_drawsurf.surfmip]; + +// the fractional light values should range from 0 to (VID_GRADES - 1) << 16 +// from a source range of 0 - 255 + + texwidth = mt->width >> r_drawsurf.surfmip; + + blocksize = 16 >> r_drawsurf.surfmip; + blockdivshift = 4 - r_drawsurf.surfmip; + blockdivmask = (1 << blockdivshift) - 1; + + r_lightwidth = (r_drawsurf.surf->extents[0]>>4)+1; + + r_numhblocks = r_drawsurf.surfwidth >> blockdivshift; + r_numvblocks = r_drawsurf.surfheight >> blockdivshift; + +//============================== + + pblockdrawer = surfmiptable[r_drawsurf.surfmip]; +// TODO: only needs to be set when there is a display settings change + horzblockstep = blocksize; + + smax = mt->width >> r_drawsurf.surfmip; + twidth = texwidth; + tmax = mt->height >> r_drawsurf.surfmip; + sourcetstep = texwidth; + r_stepback = tmax * twidth; + + r_sourcemax = r_source + (tmax * smax); + + soffset = r_drawsurf.surf->texturemins[0]; + basetoffset = r_drawsurf.surf->texturemins[1]; + +// << 16 components are to guarantee positive values for % + soffset = ((soffset >> r_drawsurf.surfmip) + (smax << 16)) % smax; + basetptr = &r_source[((((basetoffset >> r_drawsurf.surfmip) + + (tmax << 16)) % tmax) * twidth)]; + + pcolumndest = r_drawsurf.surfdat; + + for (u=0 ; u= smax) + soffset = 0; + + pcolumndest += horzblockstep; + } +} + + +//============================================================================= + +#if !id386 + +/* +================ +R_DrawSurfaceBlock8_mip0 +================ +*/ +void R_DrawSurfaceBlock8_mip0 (void) +{ + int v, i, b, lightstep, lighttemp, light; + unsigned char pix, *psource, *prowdest; + + psource = pbasesource; + prowdest = prowdestbase; + + for (v=0 ; v> 4; + lightrightstep = (r_lightptr[1] - lightright) >> 4; + + for (i=0 ; i<16 ; i++) + { + lighttemp = lightleft - lightright; + lightstep = lighttemp >> 4; + + light = lightright; + + for (b=15; b>=0; b--) + { + pix = psource[b]; + prowdest[b] = ((unsigned char *)vid.colormap) + [(light & 0xFF00) + pix]; + light += lightstep; + } + + psource += sourcetstep; + lightright += lightrightstep; + lightleft += lightleftstep; + prowdest += surfrowbytes; + } + + if (psource >= r_sourcemax) + psource -= r_stepback; + } +} + + +/* +================ +R_DrawSurfaceBlock8_mip1 +================ +*/ +void R_DrawSurfaceBlock8_mip1 (void) +{ + int v, i, b, lightstep, lighttemp, light; + unsigned char pix, *psource, *prowdest; + + psource = pbasesource; + prowdest = prowdestbase; + + for (v=0 ; v> 3; + lightrightstep = (r_lightptr[1] - lightright) >> 3; + + for (i=0 ; i<8 ; i++) + { + lighttemp = lightleft - lightright; + lightstep = lighttemp >> 3; + + light = lightright; + + for (b=7; b>=0; b--) + { + pix = psource[b]; + prowdest[b] = ((unsigned char *)vid.colormap) + [(light & 0xFF00) + pix]; + light += lightstep; + } + + psource += sourcetstep; + lightright += lightrightstep; + lightleft += lightleftstep; + prowdest += surfrowbytes; + } + + if (psource >= r_sourcemax) + psource -= r_stepback; + } +} + + +/* +================ +R_DrawSurfaceBlock8_mip2 +================ +*/ +void R_DrawSurfaceBlock8_mip2 (void) +{ + int v, i, b, lightstep, lighttemp, light; + unsigned char pix, *psource, *prowdest; + + psource = pbasesource; + prowdest = prowdestbase; + + for (v=0 ; v> 2; + lightrightstep = (r_lightptr[1] - lightright) >> 2; + + for (i=0 ; i<4 ; i++) + { + lighttemp = lightleft - lightright; + lightstep = lighttemp >> 2; + + light = lightright; + + for (b=3; b>=0; b--) + { + pix = psource[b]; + prowdest[b] = ((unsigned char *)vid.colormap) + [(light & 0xFF00) + pix]; + light += lightstep; + } + + psource += sourcetstep; + lightright += lightrightstep; + lightleft += lightleftstep; + prowdest += surfrowbytes; + } + + if (psource >= r_sourcemax) + psource -= r_stepback; + } +} + + +/* +================ +R_DrawSurfaceBlock8_mip3 +================ +*/ +void R_DrawSurfaceBlock8_mip3 (void) +{ + int v, i, b, lightstep, lighttemp, light; + unsigned char pix, *psource, *prowdest; + + psource = pbasesource; + prowdest = prowdestbase; + + for (v=0 ; v> 1; + lightrightstep = (r_lightptr[1] - lightright) >> 1; + + for (i=0 ; i<2 ; i++) + { + lighttemp = lightleft - lightright; + lightstep = lighttemp >> 1; + + light = lightright; + + for (b=1; b>=0; b--) + { + pix = psource[b]; + prowdest[b] = ((unsigned char *)vid.colormap) + [(light & 0xFF00) + pix]; + light += lightstep; + } + + psource += sourcetstep; + lightright += lightrightstep; + lightleft += lightleftstep; + prowdest += surfrowbytes; + } + + if (psource >= r_sourcemax) + psource -= r_stepback; + } +} + +#endif + + +//============================================================================ + + +/* +================ +R_InitCaches + +================ +*/ +void R_InitCaches (void) +{ + int size; + int pix; + + // calculate size to allocate + if (sw_surfcacheoverride->value) + { + size = sw_surfcacheoverride->value; + } + else + { + size = SURFCACHE_SIZE_AT_320X240; + + pix = vid.width*vid.height; + if (pix > 64000) + size += (pix-64000)*3; + } + + // round up to page size + size = (size + 8191) & ~8191; + + gEngfuncs.Con_Printf ("%ik surface cache\n", size/1024); + + sc_size = size; + sc_base = (surfcache_t *)malloc(size); + sc_rover = sc_base; + + sc_base->next = NULL; + sc_base->owner = NULL; + sc_base->size = sc_size; +} + + +/* +================== +D_FlushCaches +================== +*/ +void D_FlushCaches (void) +{ + surfcache_t *c; + + if (!sc_base) + return; + + for (c = sc_base ; c ; c = c->next) + { + if (c->owner) + *c->owner = NULL; + } + + sc_rover = sc_base; + sc_base->next = NULL; + sc_base->owner = NULL; + sc_base->size = sc_size; +} + +/* +================= +D_SCAlloc +================= +*/ +surfcache_t *D_SCAlloc (int width, int size) +{ + surfcache_t *new; + qboolean wrapped_this_time; + + if ((width < 0) || (width > 256)) + gEngfuncs.Host_Error ("D_SCAlloc: bad cache width %d\n", width); + + if ((size <= 0) || (size > 0x10000)) + gEngfuncs.Host_Error ("D_SCAlloc: bad cache size %d\n", size); + + size = (int)&((surfcache_t *)0)->data[size]; + size = (size + 3) & ~3; + if (size > sc_size) + gEngfuncs.Host_Error ("D_SCAlloc: %i > cache size of %i",size, sc_size); + +// if there is not size bytes after the rover, reset to the start + wrapped_this_time = false; + + if ( !sc_rover || (byte *)sc_rover - (byte *)sc_base > sc_size - size) + { + if (sc_rover) + { + wrapped_this_time = true; + } + sc_rover = sc_base; + } + +// colect and free surfcache_t blocks until the rover block is large enough + new = sc_rover; + if (sc_rover->owner) + *sc_rover->owner = NULL; + + while (new->size < size) + { + // free another + sc_rover = sc_rover->next; + if (!sc_rover) + gEngfuncs.Host_Error ("D_SCAlloc: hit the end of memory"); + if (sc_rover->owner) + *sc_rover->owner = NULL; + + new->size += sc_rover->size; + new->next = sc_rover->next; + } + +// create a fragment out of any leftovers + if (new->size - size > 256) + { + sc_rover = (surfcache_t *)( (byte *)new + size); + sc_rover->size = new->size - size; + sc_rover->next = new->next; + sc_rover->width = 0; + sc_rover->owner = NULL; + new->next = sc_rover; + new->size = size; + } + else + sc_rover = new->next; + + new->width = width; +// DEBUG + if (width > 0) + new->height = (size - sizeof(*new) + sizeof(new->data)) / width; + + new->owner = NULL; // should be set properly after return + + if (d_roverwrapped) + { + if (wrapped_this_time || (sc_rover >= d_initial_rover)) + r_cache_thrash = true; + } + else if (wrapped_this_time) + { + d_roverwrapped = true; + } + + return new; +} + + +/* +================= +D_SCDump +================= +*/ +void D_SCDump (void) +{ + surfcache_t *test; + + for (test = sc_base ; test ; test = test->next) + { + if (test == sc_rover) + gEngfuncs.Con_Printf ("ROVER:\n"); + gEngfuncs.Con_Printf ("%p : %i bytes %i width\n",test, test->size, test->width); + } +} + +//============================================================================= + +// if the num is not a power of 2, assume it will not repeat + +int MaskForNum (int num) +{ + if (num==128) + return 127; + if (num==64) + return 63; + if (num==32) + return 31; + if (num==16) + return 15; + return 255; +} + +int D_log2 (int num) +{ + int c; + + c = 0; + + while (num>>=1) + c++; + return c; +} + +//============================================================================= + +/* +================ +D_CacheSurface +================ +*/ +surfcache_t *D_CacheSurface (msurface_t *surface, int miplevel) +{ + surfcache_t *cache; + +// +// if the surface is animating or flashing, flush the cache +// + r_drawsurf.image = R_GetTexture(R_TextureAnimation (surface->texinfo->texture->gl_texturenum)); + + /// todo: port this + //r_drawsurf.lightadj[0] = r_newrefdef.lightstyles[surface->styles[0]].white*128; + //r_drawsurf.lightadj[1] = r_newrefdef.lightstyles[surface->styles[1]].white*128; + //r_drawsurf.lightadj[2] = r_newrefdef.lightstyles[surface->styles[2]].white*128; + //r_drawsurf.lightadj[3] = r_newrefdef.lightstyles[surface->styles[3]].white*128; + +// +// see if the cache holds apropriate data +// + cache = CACHESPOT(surface)[miplevel]; + + + if (cache && !cache->dlight && surface->dlightframe != r_framecount + && cache->image == r_drawsurf.image + && cache->lightadj[0] == r_drawsurf.lightadj[0] + && cache->lightadj[1] == r_drawsurf.lightadj[1] + && cache->lightadj[2] == r_drawsurf.lightadj[2] + && cache->lightadj[3] == r_drawsurf.lightadj[3] ) + return cache; + +// +// determine shape of surface +// + surfscale = 1.0 / (1<extents[0] >> miplevel; + r_drawsurf.rowbytes = r_drawsurf.surfwidth; + r_drawsurf.surfheight = surface->extents[1] >> miplevel; + +// +// allocate memory if needed +// + if (!cache) // if a texture just animated, don't reallocate it + { + cache = D_SCAlloc (r_drawsurf.surfwidth, + r_drawsurf.surfwidth * r_drawsurf.surfheight); + CACHESPOT(surface)[miplevel] = cache; + cache->owner = &CACHESPOT(surface)[miplevel]; + cache->mipscale = surfscale; + } + + if (surface->dlightframe == r_framecount) + cache->dlight = 1; + else + cache->dlight = 0; + + r_drawsurf.surfdat = (pixel_t *)cache->data; + + cache->image = r_drawsurf.image; + cache->lightadj[0] = r_drawsurf.lightadj[0]; + cache->lightadj[1] = r_drawsurf.lightadj[1]; + cache->lightadj[2] = r_drawsurf.lightadj[2]; + cache->lightadj[3] = r_drawsurf.lightadj[3]; + +// +// draw and light the surface texture +// + r_drawsurf.surf = surface; + + c_surf++; + + // calculate the lightings + //R_BuildLightMap (); + + // rasterize the surface into the cache + R_DrawSurface (); + + return cache; +} + +