diff --git a/src/hardware/hw_batching.c b/src/hardware/hw_batching.c index 85997f0c3..41a7f5695 100644 --- a/src/hardware/hw_batching.c +++ b/src/hardware/hw_batching.c @@ -33,28 +33,50 @@ typedef struct GLMipmap_t *current_texture = NULL; GLMipmap_t *current_brightmap = NULL; -boolean currently_batching = false; +//boolean currently_batching = false; // Dynamic arrays for: // - unsorted draw calls -// - sorted order of draw calls // - unsorted vertices +// These can be postponed by pushing them to the batching state stack + +typedef struct +{ + boolean currently_batching; + DrawCallInfo *drawCalls; + int drawCallsSize; + int drawCallsCapacity; + FOutVector *unsortedVertices; + int unsortedVerticesSize; + int unsortedVerticesCapacity; +} BatchingState; + +// used for postponing the rendering until after the skybox is done +#define STATE_STACK_SIZE 2 +BatchingState stateStack[STATE_STACK_SIZE] = {0}; +int stateStackLevel = 0; // currently used level in state stack +BatchingState *curState = &stateStack[0]; + +// Dynamic arrays for: +// - sorted order of draw calls // - final (sorted) vertices // - vertex indices for sorted vertices +// These are only used during HWR_RenderBatches so they don't need +// to be in the state stac // contains the draw calls from DrawPolygon, waiting to be processed -DrawCallInfo* drawCalls = NULL; +/*DrawCallInfo* drawCalls = NULL; int drawCallsSize = 0; -int drawCallsCapacity = 65536; +int drawCallsCapacity = 65536;*/ // contains sorted order (array indices) for drawCalls // (therefore size and capacity shared with it) UINT32* drawCallOrder = NULL; // contains unsorted vertices and texture coordinates from DrawPolygon -FOutVector* unsortedVertices = NULL; +/*FOutVector* unsortedVertices = NULL; int unsortedVerticesSize = 0; -int unsortedVerticesCapacity = 65536; +int unsortedVerticesCapacity = 65536;*/ // contains subset of sorted vertices and texture coordinates to be sent to gpu FOutVector* finalVertices = NULL; @@ -70,20 +92,36 @@ int finalIndicesSize = 0; // Call HWR_RenderBatches to render all the collected geometry. void HWR_StartBatching(void) { - if (currently_batching) + if (curState->currently_batching) I_Error("Repeat call to HWR_StartBatching without HWR_RenderBatches"); // init arrays if that has not been done yet + if (!curState->drawCalls) + { + curState->drawCallsCapacity = curState->unsortedVerticesCapacity = 65536; + curState->drawCalls = malloc( + curState->drawCallsCapacity * sizeof(DrawCallInfo)); + curState->unsortedVertices = malloc( + curState->unsortedVerticesCapacity * sizeof(FOutVector)); + } if (!finalVertices) { finalVertices = malloc(finalVerticesCapacity * sizeof(FOutVector)); finalIndices = malloc(finalVerticesCapacity * 3 * sizeof(UINT32)); - drawCalls = malloc(drawCallsCapacity * sizeof(DrawCallInfo)); - drawCallOrder = malloc(drawCallsCapacity * sizeof(UINT32)); - unsortedVertices = malloc(unsortedVerticesCapacity * sizeof(FOutVector)); + drawCallOrder = malloc(curState->drawCallsCapacity * sizeof(UINT32)); } - currently_batching = true; + curState->currently_batching = true; +} + +// Disable batching while keeping collected draw calls. +// Call HWR_StartBatching again to resume. +void HWR_PauseBatching(void) +{ + if (!curState->currently_batching) + I_Error("HWR_PauseBatching: batching not started"); + + curState->currently_batching = false; } // This replaces the direct calls to pfnSetTexture in cases where batching is available. @@ -91,7 +129,7 @@ void HWR_StartBatching(void) // Doing this was easier than getting a texture pointer to HWR_ProcessPolygon. void HWR_SetCurrentTexture(GLMipmap_t *texture) { - if (currently_batching) + if (curState->currently_batching) { if (texture != NULL) { @@ -113,47 +151,48 @@ void HWR_SetCurrentTexture(GLMipmap_t *texture) static void HWR_CollectDrawCallInfo(FSurfaceInfo *pSurf, FUINT iNumPts, FBITFIELD PolyFlags, int shader_target, boolean horizonSpecial) { // make sure dynamic array has capacity - if (drawCallsSize == drawCallsCapacity) + if (curState->drawCallsSize == curState->drawCallsCapacity) { DrawCallInfo* new_array; // ran out of space, make new array double the size - drawCallsCapacity *= 2; - new_array = malloc(drawCallsCapacity * sizeof(DrawCallInfo)); - memcpy(new_array, drawCalls, drawCallsSize * sizeof(DrawCallInfo)); - free(drawCalls); - drawCalls = new_array; + curState->drawCallsCapacity *= 2; + new_array = malloc(curState->drawCallsCapacity * sizeof(DrawCallInfo)); + memcpy(new_array, curState->drawCalls, curState->drawCallsSize * sizeof(DrawCallInfo)); + free(curState->drawCalls); + curState->drawCalls = new_array; // also need to redo the index array, dont need to copy it though free(drawCallOrder); - drawCallOrder = malloc(drawCallsCapacity * sizeof(UINT32)); + drawCallOrder = malloc(curState->drawCallsCapacity * sizeof(UINT32)); } // add entry to array - drawCalls[drawCallsSize].surf = *pSurf; - drawCalls[drawCallsSize].vertsIndex = unsortedVerticesSize; - drawCalls[drawCallsSize].numVerts = iNumPts; - drawCalls[drawCallsSize].polyFlags = PolyFlags; - drawCalls[drawCallsSize].texture = current_texture; - drawCalls[drawCallsSize].brightmap = current_brightmap; - drawCalls[drawCallsSize].shader = (shader_target != -1) ? HWR_GetShaderFromTarget(shader_target) : shader_target; - drawCalls[drawCallsSize].horizonSpecial = horizonSpecial; - drawCallsSize++; + curState->drawCalls[curState->drawCallsSize].surf = *pSurf; + curState->drawCalls[curState->drawCallsSize].vertsIndex = curState->unsortedVerticesSize; + curState->drawCalls[curState->drawCallsSize].numVerts = iNumPts; + curState->drawCalls[curState->drawCallsSize].polyFlags = PolyFlags; + curState->drawCalls[curState->drawCallsSize].texture = current_texture; + curState->drawCalls[curState->drawCallsSize].brightmap = current_brightmap; + curState->drawCalls[curState->drawCallsSize].shader = (shader_target != -1) ? HWR_GetShaderFromTarget(shader_target) : shader_target; + curState->drawCalls[curState->drawCallsSize].horizonSpecial = horizonSpecial; + curState->drawCallsSize++; } static void HWR_CollectDrawCallVertices(FOutVector *pOutVerts, FUINT iNumPts) { // make sure dynamic array has capacity - while (unsortedVerticesSize + (int)iNumPts > unsortedVerticesCapacity) + while (curState->unsortedVerticesSize + (int)iNumPts > + curState->unsortedVerticesCapacity) { FOutVector* new_array; // need more space for vertices in unsortedVertices - unsortedVerticesCapacity *= 2; - new_array = malloc(unsortedVerticesCapacity * sizeof(FOutVector)); - memcpy(new_array, unsortedVertices, unsortedVerticesSize * sizeof(FOutVector)); - free(unsortedVertices); - unsortedVertices = new_array; + curState->unsortedVerticesCapacity *= 2; + new_array = malloc(curState->unsortedVerticesCapacity * sizeof(FOutVector)); + memcpy(new_array, curState->unsortedVertices, curState->unsortedVerticesSize * sizeof(FOutVector)); + free(curState->unsortedVertices); + curState->unsortedVertices = new_array; } // add vertices to array - memcpy(&unsortedVertices[unsortedVerticesSize], pOutVerts, iNumPts * sizeof(FOutVector)); - unsortedVerticesSize += iNumPts; + memcpy(&curState->unsortedVertices[curState->unsortedVerticesSize], pOutVerts, iNumPts * sizeof(FOutVector)); + curState->unsortedVerticesSize += iNumPts; } // If batching is enabled, this function collects the polygon data and the chosen texture @@ -161,7 +200,7 @@ static void HWR_CollectDrawCallVertices(FOutVector *pOutVerts, FUINT iNumPts) // render the polygon immediately. void HWR_ProcessPolygon(FSurfaceInfo *pSurf, FOutVector *pOutVerts, FUINT iNumPts, FBITFIELD PolyFlags, int shader_target, boolean horizonSpecial) { - if (currently_batching) + if (curState->currently_batching) { if (!pSurf) { @@ -179,12 +218,33 @@ void HWR_ProcessPolygon(FSurfaceInfo *pSurf, FOutVector *pOutVerts, FUINT iNumPt } } +// TODO could alternatively save drawCalls and unsortedVertices variables here +// instead of having "curState->" everywhere +void HWR_PushBatchingState(void) +{ + if (stateStackLevel == STATE_STACK_SIZE - 1) + I_Error("HWR_PushBatchingState: State stack overflow"); + + stateStackLevel++; + curState++; +} + +// TODO as above +void HWR_PopBatchingState(void) +{ + if (stateStackLevel == 0) + I_Error("HWR_PopBatchingState: State stack underflow"); + + stateStackLevel--; + curState--; +} + static int compareDrawCalls(const void *p1, const void *p2) { UINT32 index1 = *(const UINT32*)p1; UINT32 index2 = *(const UINT32*)p2; - DrawCallInfo* poly1 = &drawCalls[index1]; - DrawCallInfo* poly2 = &drawCalls[index2]; + DrawCallInfo* poly1 = &curState->drawCalls[index1]; + DrawCallInfo* poly2 = &curState->drawCalls[index2]; int diff; INT64 diff64; UINT32 downloaded1 = 0; @@ -246,8 +306,8 @@ static int compareDrawCallsNoShaders(const void *p1, const void *p2) { unsigned int index1 = *(const unsigned int*)p1; unsigned int index2 = *(const unsigned int*)p2; - DrawCallInfo* poly1 = &drawCalls[index1]; - DrawCallInfo* poly2 = &drawCalls[index2]; + DrawCallInfo* poly1 = &curState->drawCalls[index1]; + DrawCallInfo* poly2 = &curState->drawCalls[index2]; int diff; INT64 diff64; @@ -303,7 +363,7 @@ static void HWR_CollectVerticesIntoBatch(DrawCallInfo *drawCall) finalIndices = new_index_array; } // write the vertices of the polygon - memcpy(&finalVertices[finalVerticesSize], &unsortedVertices[drawCall->vertsIndex], + memcpy(&finalVertices[finalVerticesSize], &curState->unsortedVertices[drawCall->vertsIndex], numVerts * sizeof(FOutVector)); // write the indexes, pointing to the fan vertexes but in triangles format firstVIndex = finalVerticesSize; @@ -401,7 +461,7 @@ static void HWR_ExecuteStateChanges(unsigned int stateChanges, DrawCallInfo *dc) static void HWR_InitBatchingStats(void) { - ps_hw_numpolys = drawCallsSize; + ps_hw_numpolys = curState->drawCallsSize; ps_hw_numcalls = ps_hw_numverts = ps_hw_numshaders = ps_hw_numtextures = ps_hw_numpolyflags = ps_hw_numcolors = 0; @@ -414,26 +474,26 @@ void HWR_RenderBatches(void) int drawCallReadPos = 0; // position in drawCallOrder int i; - if (!currently_batching) + if (!curState->currently_batching) I_Error("HWR_RenderBatches called without starting batching"); - currently_batching = false; // no longer collecting batches + curState->currently_batching = false; // no longer collecting batches HWR_InitBatchingStats(); - if (!drawCallsSize) + if (!curState->drawCallsSize) return; // nothing to draw // init drawCallOrder - for (i = 0; i < drawCallsSize; i++) + for (i = 0; i < curState->drawCallsSize; i++) drawCallOrder[i] = i; // sort the draw calls ps_hw_batchsorttime = I_GetPreciseTime(); if (cv_glshaders.value && gl_shadersavailable) - qs22j(drawCallOrder, drawCallsSize, sizeof(UINT32), compareDrawCalls); + qs22j(drawCallOrder, curState->drawCallsSize, sizeof(UINT32), compareDrawCalls); else - qs22j(drawCallOrder, drawCallsSize, sizeof(UINT32), compareDrawCallsNoShaders); + qs22j(drawCallOrder, curState->drawCallsSize, sizeof(UINT32), compareDrawCallsNoShaders); ps_hw_batchsorttime = I_GetPreciseTime() - ps_hw_batchsorttime; // sort grouping order // 1. shader @@ -446,7 +506,7 @@ void HWR_RenderBatches(void) ps_hw_batchdrawtime = I_GetPreciseTime(); // set state for first batch - HWR_ExecuteStateChanges(0xFFFF, &drawCalls[drawCallOrder[0]]); + HWR_ExecuteStateChanges(0xFFFF, &curState->drawCalls[drawCallOrder[0]]); // - iterate through draw calls // - accumulate converted vertices and indices into finalVertices and finalIndices @@ -467,18 +527,18 @@ void HWR_RenderBatches(void) // repeat loop unsigned int stateChanges = 0; // flags defined above HWR_MarkStateChanges - DrawCallInfo *currentDrawCall = &drawCalls[drawCallOrder[drawCallReadPos++]]; + DrawCallInfo *currentDrawCall = &curState->drawCalls[drawCallOrder[drawCallReadPos++]]; DrawCallInfo *nextDrawCall = NULL; HWR_CollectVerticesIntoBatch(currentDrawCall); - if (drawCallReadPos >= drawCallsSize) // was that the last draw call? + if (drawCallReadPos >= curState->drawCallsSize) // was that the last draw call? { stopFlag = true; } else { - nextDrawCall = &drawCalls[drawCallOrder[drawCallReadPos]]; + nextDrawCall = &curState->drawCalls[drawCallOrder[drawCallReadPos]]; stateChanges = HWR_MarkStateChanges(currentDrawCall, nextDrawCall); } @@ -504,8 +564,8 @@ void HWR_RenderBatches(void) HWR_ExecuteStateChanges(stateChanges, nextDrawCall); } // reset the arrays (set sizes to 0) - drawCallsSize = 0; - unsortedVerticesSize = 0; + curState->drawCallsSize = 0; + curState->unsortedVerticesSize = 0; ps_hw_batchdrawtime = I_GetPreciseTime() - ps_hw_batchdrawtime; } diff --git a/src/hardware/hw_batching.h b/src/hardware/hw_batching.h index 4906fb35f..60fe9e3bb 100644 --- a/src/hardware/hw_batching.h +++ b/src/hardware/hw_batching.h @@ -21,8 +21,11 @@ extern "C" { #endif void HWR_StartBatching(void); +void HWR_PauseBatching(void); void HWR_SetCurrentTexture(GLMipmap_t *texture); void HWR_ProcessPolygon(FSurfaceInfo *pSurf, FOutVector *pOutVerts, FUINT iNumPts, FBITFIELD PolyFlags, int shader, boolean horizonSpecial); +void HWR_PushBatchingState(void); +void HWR_PopBatchingState(void); void HWR_RenderBatches(void); #ifdef __cplusplus diff --git a/src/hardware/hw_bsp.c b/src/hardware/hw_bsp.c index 393689425..20f57ed73 100644 --- a/src/hardware/hw_bsp.c +++ b/src/hardware/hw_bsp.c @@ -13,10 +13,12 @@ #ifdef HWRENDER #include "hw_clip.h" #include "hw_glob.h" +#include "../p_local.h" #include "../p_slopes.h" #include "../r_local.h" #include "../z_zone.h" +boolean gl_sky_found = true; // From PrBoom: // @@ -210,14 +212,30 @@ static boolean CheckClip(seg_t * seg, sector_t * afrontsector, sector_t * abacks return false; } -// -----------------+ -// HWR_AddLine : Clips the given segment and adds any visible pieces to the line list. -// Notes : gl_cursectorlight is set to the current subsector -> sector -> light value -// : (it may be mixed with the wall's own flat colour in the future ...) -// -----------------+ +// returns true if the point is on the correct (viewable) side of the +// portal destination line +static boolean HWR_PortalCheckPointSide(fixed_t x, fixed_t y) +{ + // we are checking if the point is on the viewable side of the portal exit. + // being exactly on the portal exit line is not enough to pass the test. + // P_PointOnLineSide could behave differently from this expectation on this case, + // so first check if the point is precisely on the line, and then if not, check the side. + + vertex_t closest_point; + P_ClosestPointOnLine(x, y, gl_portalclipline, &closest_point); + if (closest_point.x != x || closest_point.y != y) + { + if (P_PointOnLineSide(x, y, gl_portalclipline) != gl_portalviewside) + return true; + } + return false; +} + +// Handles seg clipping and renders the seg if it could be visible. static void HWR_AddLine(seg_t * line) { angle_t angle1, angle2; + boolean skipseg = false; // SoM: Backsector needs to be run through R_FakeFlat static sector_t tempsec; @@ -267,6 +285,42 @@ static void HWR_AddLine(seg_t * line) checkforemptylines = true; gl_backsector = line->backsector; + // do extra checks on the seg when rendering portals: + // don't render segs that are behind the portal destination line + if (gl_portalclipline && + !HWR_PortalCheckPointSide(line->v1->x, line->v1->y) && + !HWR_PortalCheckPointSide(line->v2->x, line->v2->y)) + { + return; + } + + // Portal line + if (line->linedef->special == 40 && line->side == 0) + { + size_t p; + mtag_t tag = Tag_FGet(&line->linedef->tags); + INT32 li1 = line->linedef-lines; + INT32 li2; + + for (p = 0; (li2 = Tag_Iterate_Lines(tag, p)) >= 0; p++) + { + // Skip invalid lines. + if ((tag != Tag_FGet(&lines[li2].tags)) || (lines[li1].special != lines[li2].special) || (li1 == li2)) + continue; + + // call will bail and return false if recursion limit is reached + if (HWR_AddPortal(&lines[li1], &lines[li2], line)) + { + skipseg = true; // TODO could this cause unintentional disappearing of some walls? + if (gl_printportals && !gl_portal_level && !gl_rendering_skybox) + { + // print some info about first level portals + CONS_Printf("line %d -> line %d\n", li1, li2); + } + } + } + } + if (!line->backsector) { gld_clipper_SafeAddClipRange(angle2, angle1); @@ -310,8 +364,8 @@ static void HWR_AddLine(seg_t * line) return; } - HWR_ProcessSeg(); // Doesn't need arguments because they're defined globally :D - return; + if (!skipseg) + HWR_ProcessSeg(); // Doesn't need arguments because they're defined globally :D } // HWR_CheckBBox @@ -329,16 +383,16 @@ static boolean HWR_CheckBBox(const fixed_t *bspcoord) // Find the corners of the box // that define the edges from current viewpoint. - if (dup_viewx <= bspcoord[BOXLEFT]) + if (viewx <= bspcoord[BOXLEFT]) boxpos = 0; - else if (dup_viewx < bspcoord[BOXRIGHT]) + else if (viewx < bspcoord[BOXRIGHT]) boxpos = 1; else boxpos = 2; - if (dup_viewy >= bspcoord[BOXTOP]) + if (viewy >= bspcoord[BOXTOP]) boxpos |= 0; - else if (dup_viewy > bspcoord[BOXBOTTOM]) + else if (viewy > bspcoord[BOXBOTTOM]) boxpos |= 1<<2; else boxpos |= 2<<2; @@ -357,6 +411,25 @@ static boolean HWR_CheckBBox(const fixed_t *bspcoord) return gld_clipper_SafeCheckRange(angle2, angle1); } +// Check if bounding box is (partially or fully) in the correct side +// of the portal destination. +static boolean HWR_PortalCheckBBox(const fixed_t *bspcoord) +{ + if (!gl_portalclipline) + return true; + + if (HWR_PortalCheckPointSide(bspcoord[BOXLEFT], bspcoord[BOXTOP]) || + HWR_PortalCheckPointSide(bspcoord[BOXLEFT], bspcoord[BOXBOTTOM]) || + HWR_PortalCheckPointSide(bspcoord[BOXRIGHT], bspcoord[BOXTOP]) || + HWR_PortalCheckPointSide(bspcoord[BOXRIGHT], bspcoord[BOXBOTTOM])) + { + return true; + } + + // we did not find any reason to pass the check, so return failure + return false; +} + // // HWR_AddPolyObjectSegs // @@ -485,6 +558,9 @@ static void HWR_Subsector(size_t num) extracolormap_t *ceilingcolormap; ffloor_t *rover; + //if (gl_printportals && gl_portalclipline) + // CONS_Printf("subsector %d in portal\n", (INT32)num); + #ifdef PARANOIA //no risk while developing, enough debugging nights! if (num >= addsubsector) I_Error("HWR_Subsector: ss %s with numss = %s, addss = %s\n", @@ -580,7 +656,7 @@ static void HWR_Subsector(size_t num) // render floor ? // yeah, easy backface cull! :) - if (cullFloorHeight < dup_viewz) + if (cullFloorHeight < viewz) { if (gl_frontsector->floorpic != skyflatnum) { @@ -597,7 +673,7 @@ static void HWR_Subsector(size_t num) } } - if (cullCeilingHeight > dup_viewz) + if (cullCeilingHeight > viewz) { if (gl_frontsector->ceilingpic != skyflatnum) { @@ -614,6 +690,11 @@ static void HWR_Subsector(size_t num) } } + // Moved here because before, when above the ceiling and the floor does not have the sky flat, it doesn't draw the sky + if (gl_frontsector->ceilingpic == skyflatnum || gl_frontsector->floorpic == skyflatnum) + gl_sky_found = true; + + if (gl_frontsector->ffloors) { /// \todo fix light, xoffs, yoffs, extracolormap ? @@ -633,14 +714,14 @@ static void HWR_Subsector(size_t num) if (centerHeight <= locCeilingHeight && centerHeight >= locFloorHeight && - ((dup_viewz < cullHeight && (rover->fofflags & FOF_BOTHPLANES || !(rover->fofflags & FOF_INVERTPLANES))) || - (dup_viewz > cullHeight && (rover->fofflags & FOF_BOTHPLANES || rover->fofflags & FOF_INVERTPLANES)))) + ((viewz < cullHeight && (rover->fofflags & FOF_BOTHPLANES || !(rover->fofflags & FOF_INVERTPLANES))) || + (viewz > cullHeight && (rover->fofflags & FOF_BOTHPLANES || rover->fofflags & FOF_INVERTPLANES)))) { if (rover->fofflags & FOF_FOG) { UINT8 alpha; - light = R_GetPlaneLight(gl_frontsector, centerHeight, dup_viewz < cullHeight ? true : false); + light = R_GetPlaneLight(gl_frontsector, centerHeight, viewz < cullHeight ? true : false); alpha = HWR_FogBlockAlpha(*gl_frontsector->lightlist[light].lightlevel, rover->master->frontsector->extra_colormap); HWR_AddTransparentFloor(0, @@ -656,7 +737,7 @@ static void HWR_Subsector(size_t num) { FBITFIELD blendmode = HWR_GetBlendModeFlag(rover->blend) | HWR_RippleBlend(gl_frontsector, rover, false); - light = R_GetPlaneLight(gl_frontsector, centerHeight, dup_viewz < cullHeight ? true : false); + light = R_GetPlaneLight(gl_frontsector, centerHeight, viewz < cullHeight ? true : false); HWR_AddTransparentFloor(&levelflats[*rover->bottompic], &extrasubsectors[num], @@ -669,7 +750,7 @@ static void HWR_Subsector(size_t num) else { HWR_GetLevelFlat(&levelflats[*rover->bottompic], R_NoEncore(gl_frontsector, &levelflats[*rover->bottompic], false)); - light = R_GetPlaneLight(gl_frontsector, centerHeight, dup_viewz < cullHeight ? true : false); + light = R_GetPlaneLight(gl_frontsector, centerHeight, viewz < cullHeight ? true : false); HWR_RenderPlane(sub, &extrasubsectors[num], false, *rover->bottomheight, HWR_RippleBlend(gl_frontsector, rover, false) | PF_Occlude, *gl_frontsector->lightlist[light].lightlevel, &levelflats[*rover->bottompic], rover->master->frontsector, 255, *gl_frontsector->lightlist[light].extra_colormap); } @@ -681,14 +762,14 @@ static void HWR_Subsector(size_t num) if (centerHeight >= locFloorHeight && centerHeight <= locCeilingHeight && - ((dup_viewz > cullHeight && (rover->fofflags & FOF_BOTHPLANES || !(rover->fofflags & FOF_INVERTPLANES))) || - (dup_viewz < cullHeight && (rover->fofflags & FOF_BOTHPLANES || rover->fofflags & FOF_INVERTPLANES)))) + ((viewz > cullHeight && (rover->fofflags & FOF_BOTHPLANES || !(rover->fofflags & FOF_INVERTPLANES))) || + (viewz < cullHeight && (rover->fofflags & FOF_BOTHPLANES || rover->fofflags & FOF_INVERTPLANES)))) { if (rover->fofflags & FOF_FOG) { UINT8 alpha; - light = R_GetPlaneLight(gl_frontsector, centerHeight, dup_viewz < cullHeight ? true : false); + light = R_GetPlaneLight(gl_frontsector, centerHeight, viewz < cullHeight ? true : false); alpha = HWR_FogBlockAlpha(*gl_frontsector->lightlist[light].lightlevel, rover->master->frontsector->extra_colormap); HWR_AddTransparentFloor(0, @@ -704,7 +785,7 @@ static void HWR_Subsector(size_t num) { FBITFIELD blendmode = HWR_GetBlendModeFlag(rover->blend) | HWR_RippleBlend(gl_frontsector, rover, false); - light = R_GetPlaneLight(gl_frontsector, centerHeight, dup_viewz < cullHeight ? true : false); + light = R_GetPlaneLight(gl_frontsector, centerHeight, viewz < cullHeight ? true : false); HWR_AddTransparentFloor(&levelflats[*rover->toppic], &extrasubsectors[num], @@ -717,7 +798,7 @@ static void HWR_Subsector(size_t num) else { HWR_GetLevelFlat(&levelflats[*rover->toppic], R_NoEncore(gl_frontsector, &levelflats[*rover->toppic], true)); - light = R_GetPlaneLight(gl_frontsector, centerHeight, dup_viewz < cullHeight ? true : false); + light = R_GetPlaneLight(gl_frontsector, centerHeight, viewz < cullHeight ? true : false); HWR_RenderPlane(sub, &extrasubsectors[num], true, *rover->topheight, HWR_RippleBlend(gl_frontsector, rover, true) | PF_Occlude, *gl_frontsector->lightlist[light].lightlevel, &levelflats[*rover->toppic], rover->master->frontsector, 255, *gl_frontsector->lightlist[light].extra_colormap); } @@ -805,17 +886,39 @@ void HWR_RenderBSPNode(INT32 bspnum) side = R_PointOnSideFast(viewx, viewy, bsp); // Recursively divide front space. - HWR_RenderBSPNode(bsp->children[side]); + if (HWR_PortalCheckBBox(bsp->bbox[side])) + HWR_RenderBSPNode(bsp->children[side]); // Possibly divide back space - if (!(HWR_CheckBBox(bsp->bbox[side^1]))) + if (!(HWR_CheckBBox(bsp->bbox[side^1]) && HWR_PortalCheckBBox(bsp->bbox[side^1]))) return; bspnum = bsp->children[side^1]; } // e6y: support for extended nodes - HWR_Subsector(bspnum == -1 ? 0 : bspnum & ~NF_SUBSECTOR); + if (bspnum & NF_SUBSECTOR) + { + if (bspnum == -1) + { + //*(gl_drawsubsector_p++) = 0; + HWR_Subsector(0); + } + else + { + if (gl_portalcullsector) + { + // skip all subsectors encountered before the portal + // destination's front sector + if (gl_portalcullsector != subsectors[bspnum & ~NF_SUBSECTOR].sector) + return; + else + gl_portalcullsector = NULL; + } + //*(gl_drawsubsector_p++) = bspnum&(~NF_SUBSECTOR); + HWR_Subsector(bspnum&(~NF_SUBSECTOR)); + } + } } #endif diff --git a/src/hardware/hw_defs.h b/src/hardware/hw_defs.h index f79268d7a..b27b01301 100644 --- a/src/hardware/hw_defs.h +++ b/src/hardware/hw_defs.h @@ -19,6 +19,7 @@ extern "C" { #endif +#define FAR_CLIPPING_PLANE 32768.0f // Draw further! Tails 01-21-2001 #define ZCLIP_PLANE 4.0f // Used for the actual game drawing #define NZCLIP_PLANE 0.9f // Seems to be only used for the HUD and screen textures @@ -308,8 +309,9 @@ enum hwdsetspecialstate HWD_SET_SHADERS, HWD_SET_TEXTUREFILTERMODE, HWD_SET_TEXTUREANISOTROPICMODE, - - HWD_NUMSTATE + HWD_SET_STENCIL_MODE, + HWD_SET_STENCIL_LEVEL, // must set mode afterwards for level to come into effect + HWD_NUMSTATE // (TODO could create separate stencil function in r_opengl.c to avoid hacky behaviour like this) }; typedef enum hwdsetspecialstate hwdspecialstate_t; @@ -321,6 +323,15 @@ enum hwdshaderstage typedef enum hwdshaderstage hwdshaderstage_t; +// stencil modes +enum +{ + HWD_STENCIL_INACTIVE, + HWD_STENCIL_PORTAL_BEGIN, + HWD_STENCIL_PORTAL_INSIDE, + HWD_STENCIL_PORTAL_FINISH +}; + // Lactozilla: Shader info // Generally set at the start of the frame. enum hwdshaderinfo diff --git a/src/hardware/hw_draw.c b/src/hardware/hw_draw.c index 27d48ac6d..bedc108da 100644 --- a/src/hardware/hw_draw.c +++ b/src/hardware/hw_draw.c @@ -1067,7 +1067,7 @@ void HWR_DrawFill(INT32 x, INT32 y, INT32 w, INT32 h, INT32 color) clearColour.green = (float)rgbaColour.s.green / 255; clearColour.blue = (float)rgbaColour.s.blue / 255; clearColour.alpha = 1; - GL_ClearBuffer(true, false, &clearColour); + GL_ClearBuffer(true, false, false, &clearColour); return; } } diff --git a/src/hardware/hw_drawnodes.c b/src/hardware/hw_drawnodes.c index 65fac1cf8..e255e47fb 100644 --- a/src/hardware/hw_drawnodes.c +++ b/src/hardware/hw_drawnodes.c @@ -82,26 +82,40 @@ typedef struct // initial size of drawnode array #define DRAWNODES_INIT_SIZE 64 -gl_drawnode_t *drawnodes = NULL; +/*gl_drawnode_t *drawnodes = NULL; INT32 numdrawnodes = 0; -INT32 alloceddrawnodes = 0; +INT32 alloceddrawnodes = 0;*/ + +typedef struct +{ + gl_drawnode_t *drawnodes; + INT32 numdrawnodes; + INT32 alloceddrawnodes; +} gl_drawnode_state_t; + +// todo magic number 16 +// portal rendering will push and pop this stack +// to keep multiple drawnode lists around until they're needed +static gl_drawnode_state_t state_stack[16] = {0}; +static int stack_level = 0; +static gl_drawnode_state_t *cst = &state_stack[0]; // current state static void *HWR_CreateDrawNode(gl_drawnode_type_t type) { gl_drawnode_t *drawnode; - if (!drawnodes) + if (!cst->drawnodes) { - alloceddrawnodes = DRAWNODES_INIT_SIZE; - drawnodes = Z_Malloc(alloceddrawnodes * sizeof(gl_drawnode_t), PU_LEVEL, &drawnodes); + cst->alloceddrawnodes = DRAWNODES_INIT_SIZE; + cst->drawnodes = Z_Malloc(cst->alloceddrawnodes * sizeof(gl_drawnode_t), PU_LEVEL, &cst->drawnodes); } - else if (numdrawnodes >= alloceddrawnodes) + else if (cst->numdrawnodes >= cst->alloceddrawnodes) { - alloceddrawnodes *= 2; - Z_Realloc(drawnodes, alloceddrawnodes * sizeof(gl_drawnode_t), PU_LEVEL, &drawnodes); + cst->alloceddrawnodes *= 2; + Z_Realloc(cst->drawnodes, cst->alloceddrawnodes * sizeof(gl_drawnode_t), PU_LEVEL, &cst->drawnodes); } - drawnode = &drawnodes[numdrawnodes++]; + drawnode = &cst->drawnodes[cst->numdrawnodes++]; drawnode->type = type; // not sure if returning different pointers to a union is necessary @@ -166,12 +180,32 @@ void HWR_AddTransparentPolyobjectFloor(levelflat_t *levelflat, polyobj_t *polyse polyplaneinfo->planecolormap = planecolormap; } +// pushes all drawnode rendering state to stack +void HWR_PushDrawNodeState(void) +{ + // todo magic number 16 + if (stack_level == 15) + I_Error("HWR_PushDrawNodeState: State stack overflow"); + + stack_level++; + cst++; +} + +void HWR_PopDrawNodeState(void) +{ + if (stack_level == 0) + I_Error("HWR_PopDrawNodeState: State stack underflow"); + + stack_level--; + cst--; +} + static int CompareDrawNodePlanes(const void *p1, const void *p2) { INT32 n1 = *(const INT32*)p1; INT32 n2 = *(const INT32*)p2; - return ABS(drawnodes[n2].u.plane.fixedheight - viewz) - ABS(drawnodes[n1].u.plane.fixedheight - viewz); + return ABS(cst->drawnodes[n2].u.plane.fixedheight - viewz) - ABS(cst->drawnodes[n1].u.plane.fixedheight - viewz); } // HWR_RenderDrawNodes @@ -184,34 +218,34 @@ void HWR_RenderDrawNodes(void) // A list of indices into the drawnodes array. INT32 *sortindex; - if (!numdrawnodes) + if (!cst->numdrawnodes) return; - ps_numdrawnodes = numdrawnodes; + ps_numdrawnodes = cst->numdrawnodes; ps_hw_nodesorttime = I_GetPreciseTime(); - sortindex = Z_Malloc(sizeof(INT32) * numdrawnodes, PU_STATIC, NULL); + sortindex = Z_Malloc(sizeof(INT32) * cst->numdrawnodes, PU_STATIC, NULL); // Reversed order - for (i = 0; i < numdrawnodes; i++) - sortindex[i] = numdrawnodes - i - 1; + for (i = 0; i < cst->numdrawnodes; i++) + sortindex[i] = cst->numdrawnodes - i - 1; // The order is correct apart from planes in the same subsector. // So scan the list and sort out these cases. // For each consecutive run of planes in the list, sort that run based on // plane height and view height. - while (run_start < numdrawnodes-1) // numdrawnodes-1 because a 1 plane run at the end of the list does not count + while (run_start < cst->numdrawnodes-1) // numdrawnodes-1 because a 1 plane run at the end of the list does not count { // locate run start - if (drawnodes[sortindex[run_start]].type == DRAWNODE_PLANE) + if (cst->drawnodes[sortindex[run_start]].type == DRAWNODE_PLANE) { // found it, now look for run end INT32 run_end;// (inclusive) - for (i = run_start+1; i < numdrawnodes; i++) + for (i = run_start+1; i < cst->numdrawnodes; i++) { - if (drawnodes[sortindex[i]].type != DRAWNODE_PLANE) break; + if (cst->drawnodes[sortindex[i]].type != DRAWNODE_PLANE) break; } run_end = i-1; if (run_end > run_start) // if there are multiple consecutive planes, not just one @@ -235,9 +269,9 @@ void HWR_RenderDrawNodes(void) // Okay! Let's draw it all! Woo! GL_SetTransform(&atransform); - for (i = 0; i < numdrawnodes; i++) + for (i = 0; i < cst->numdrawnodes; i++) { - gl_drawnode_t *drawnode = &drawnodes[sortindex[i]]; + gl_drawnode_t *drawnode = &cst->drawnodes[sortindex[i]]; if (drawnode->type == DRAWNODE_PLANE) { @@ -279,7 +313,7 @@ void HWR_RenderDrawNodes(void) ps_hw_nodedrawtime = I_GetPreciseTime() - ps_hw_nodedrawtime; - numdrawnodes = 0; + cst->numdrawnodes = 0; // No mem leaks, please. Z_Free(sortindex); diff --git a/src/hardware/hw_glob.h b/src/hardware/hw_glob.h index 013b80c5e..099eb7e9e 100644 --- a/src/hardware/hw_glob.h +++ b/src/hardware/hw_glob.h @@ -166,6 +166,7 @@ void HWR_ClearLightTables(void); // hw_bsp.c // -------- void HWR_RenderBSPNode(INT32 bspnum); +extern boolean gl_sky_found; // -------- // hw_draw.c @@ -188,6 +189,8 @@ void HWR_AddTransparentWall(FOutVector *wallVerts, FSurfaceInfo *pSurf, INT32 te void HWR_AddTransparentFloor(levelflat_t *levelflat, extrasubsector_t *xsub, boolean isceiling, fixed_t fixedheight, INT32 lightlevel, INT32 alpha, sector_t *FOFSector, FBITFIELD blend, boolean fogplane, extracolormap_t *planecolormap); void HWR_AddTransparentPolyobjectFloor(levelflat_t *levelflat, polyobj_t *polysector, boolean isceiling, fixed_t fixedheight, INT32 lightlevel, INT32 alpha, sector_t *FOFSector, FBITFIELD blend, extracolormap_t *planecolormap); +void HWR_PushDrawNodeState(void); +void HWR_PopDrawNodeState(void); void HWR_RenderDrawNodes(void); // -------- @@ -220,12 +223,13 @@ void HWR_DrawSkyBackground(player_t *player); // -------- // hw_things.c // -------- -extern UINT32 gl_visspritecount; void HWR_ClearSprites(void); +void HWR_PushSpriteState(void); +void HWR_PopSpriteState(void); void HWR_AddSprites(sector_t *sec); void HWR_AddPrecipitationSprites(void); -void HWR_SortVisSprites(void); +UINT32 HWR_SortVisSprites(void); void HWR_DrawSprites(void); void HWR_ProjectBoundingBox(mobj_t *thing); diff --git a/src/hardware/hw_gpu.h b/src/hardware/hw_gpu.h index 68dc5da2c..ad3941caf 100644 --- a/src/hardware/hw_gpu.h +++ b/src/hardware/hw_gpu.h @@ -31,7 +31,7 @@ void GL_DrawPolygon(FSurfaceInfo *pSurf, FOutVector *pOutVerts, FUINT iNumPts, F void GL_DrawIndexedTriangles(FSurfaceInfo *pSurf, FOutVector *pOutVerts, FUINT iNumPts, FBITFIELD PolyFlags, UINT32 *IndexArray); void GL_RenderSkyDome(gl_sky_t *sky); void GL_SetBlend(FBITFIELD PolyFlags); -void GL_ClearBuffer(FBOOLEAN ColorMask, FBOOLEAN DepthMask, FRGBAFloat *ClearColor); +void GL_ClearBuffer(FBOOLEAN ColorMask, FBOOLEAN DepthMask, FBOOLEAN StencilMask, FRGBAFloat *ClearColor); void GL_SetTexture(GLMipmap_t *pTexInfo); void GL_UpdateTexture(GLMipmap_t *pTexInfo); void GL_DeleteTexture(GLMipmap_t *pTexInfo); diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c index 9770f5ee2..a1bba84ed 100644 --- a/src/hardware/hw_main.c +++ b/src/hardware/hw_main.c @@ -110,15 +110,13 @@ boolean gl_shadersavailable = false; // Whether the internal state is set to palette rendering or not. static boolean gl_palette_rendering_state = false; +boolean gl_rendering_skybox = false; + // -------------------------------------------------------------------------- // STUFF FOR THE PROJECTION CODE // -------------------------------------------------------------------------- FTransform atransform; -// duplicates of the main code, set after R_SetupFrame() passed them into sharedstruct, -// copied here for local use -fixed_t dup_viewx, dup_viewy, dup_viewz; -angle_t dup_viewangle; float gl_viewx, gl_viewy, gl_viewz; float gl_viewsin, gl_viewcos; @@ -129,6 +127,161 @@ static float gl_fovlud; static angle_t gl_aimingangle; +/* Portal stuff */ + +// TODO probably put this to a separate file? + +typedef struct +{ + // Viewport. + fixed_t viewx; + fixed_t viewy; + fixed_t viewz; + angle_t viewangle; + + seg_t *seg; // seg that is used for drawing to the stencil buffer + line_t *clipline; + + // angles for the left and right edges of the portal + // relative to the viewpoint + angle_t angle1; + angle_t angle2; +} gl_portal_t; + +#define INIT_PORTAL_ARRAY_SIZE 4 +typedef struct +{ + gl_portal_t *portals; + unsigned int size; + unsigned int capacity; +} gl_portal_array_t; + +// TODO magic number 16 +gl_portal_array_t gl_portal_arrays[16] = {0}; + +INT32 gl_portal_level = 0; // portal recursion level + +boolean gl_drawing_stencil = false; // used when drawing segs to stencil buffer +sector_t *gl_portalcullsector = NULL; +line_t *gl_portalclipline = NULL; +INT32 gl_portalviewside = 0; + +// debug tools +boolean gl_printportals = false; // print info about portals on this frame +INT32 gl_debugportal = 0; // hide main viewpoint and only render this portal without stencil + +static void HWR_PortalFrame(gl_portal_t *portal, boolean set_culling) +{ + viewx = portal->viewx; + viewy = portal->viewy; + viewz = portal->viewz; + + viewangle = portal->viewangle; + viewsin = FINESINE(viewangle>>ANGLETOFINESHIFT); + viewcos = FINECOSINE(viewangle>>ANGLETOFINESHIFT); + + // set_culling will be true if RenderBSPNode is about to be called + // so need to enable portal culling for that + if (set_culling) + { + gl_portalcullsector = portal->clipline->frontsector; + gl_portalclipline = portal->clipline; + gl_portalviewside = P_PointOnLineSide(viewx, viewy, gl_portalclipline); + // TODO check is gl_portalviewside always the same value? + // if it is then it's not needed to have this variable + } +} + +// get currently used portal array +static gl_portal_array_t *HWR_GetPortalArray(void) +{ + INT32 level = gl_portal_level; + if (gl_rendering_skybox) + level++; + return &gl_portal_arrays[level]; +} + +boolean HWR_AddPortal(line_t *start, line_t *dest, seg_t *seg) +{ + gl_portal_array_t *array; + gl_portal_t *portal; + + angle_t dangle; + fixed_t disttopoint; + angle_t angtopoint; + vertex_t dest_c, start_c; + + if ((gl_portal_level + gl_rendering_skybox) >= cv_maxportals.value || + (gl_debugportal && + (gl_debugportal != (start-lines) || gl_portal_level))) + { + return false; + } + if (gl_debugportal) + gl_debugportal = -1; // skip other portal segs from the same line + + array = HWR_GetPortalArray(); + + // yet another dynamic array + if (!array->portals) + { + array->capacity = INIT_PORTAL_ARRAY_SIZE; + array->portals = Z_Malloc(sizeof(gl_portal_t) * array->capacity, PU_LEVEL, + &array->portals); + } + else if (array->size == array->capacity) + { + array->capacity *= 2; + array->portals = Z_Realloc(array->portals, sizeof(gl_portal_t) * array->capacity, + PU_LEVEL, &array->portals); + } + + portal = &array->portals[array->size++]; + + dangle = R_PointToAngle2(0,0,dest->dx,dest->dy) - R_PointToAngle2(start->dx,start->dy,0,0); + + // using this order of operations change fixes ante station island portal + + // looking glass center + //start_c.x = (start->v1->x + start->v2->x) / 2; + //start_c.y = (start->v1->y + start->v2->y) / 2; + start_c.x = start->v1->x/2 + start->v2->x/2; + start_c.y = start->v1->y/2 + start->v2->y/2; + + // other side center + //dest_c.x = (dest->v1->x + dest->v2->x) / 2; + //dest_c.y = (dest->v1->y + dest->v2->y) / 2; + dest_c.x = dest->v1->x/2 + dest->v2->x/2; + dest_c.y = dest->v1->y/2 + dest->v2->y/2; + + disttopoint = R_PointToDist2(start_c.x, start_c.y, viewx, viewy); + angtopoint = R_PointToAngle2(start_c.x, start_c.y, viewx, viewy); + angtopoint += dangle; + + // could using float or double here help with the slight impreciseness of + // the view coordinates? + //float fang = ((float)angtopoint / 4294967296.0f) * 2.0f * M_PI; + + portal->viewx = dest_c.x + FixedMul(FINECOSINE(angtopoint>>ANGLETOFINESHIFT), disttopoint); + portal->viewy = dest_c.y + FixedMul(FINESINE(angtopoint>>ANGLETOFINESHIFT), disttopoint); + //portal->viewx = dest_c.x + FixedMul(FLOAT_TO_FIXED(cos(fang)), disttopoint); + //portal->viewy = dest_c.y + FixedMul(FLOAT_TO_FIXED(sin(fang)), disttopoint); + portal->viewz = viewz + dest->frontsector->floorheight - start->frontsector->floorheight; + portal->viewangle = viewangle + dangle; + portal->seg = seg; + portal->clipline = dest; + + portal->angle1 = R_PointToAngle64(seg->v1->x, seg->v1->y) + dangle; + portal->angle2 = R_PointToAngle64(seg->v2->x, seg->v2->y) + dangle; + + return true; +} + +static void HWR_ClearPortals(void) +{ + HWR_GetPortalArray()->size = 0; +} + // ========================================================================== // Lighting // ========================================================================== @@ -415,26 +568,15 @@ boolean HWR_BlendMidtextureSurface(FSurfaceInfo *pSurf) // -----------------+ // HWR_ClearView : clear the viewwindow, with maximum z value // -----------------+ -static inline void HWR_ClearView(void) +/*static inline void HWR_ClearView(void) { - // 3--2 - // | /| - // |/ | - // 0--1 - - /// \bug faB - enable depth mask, disable color mask - GL_GClipRect((INT32)gl_viewwindowx, (INT32)gl_viewwindowy, (INT32)(gl_viewwindowx + gl_viewwidth), (INT32)(gl_viewwindowy + gl_viewheight), ZCLIP_PLANE); - GL_ClearBuffer(false, true, 0); - - //disable clip window - set to full size - // rem by Hurdler - // GL_GClipRect(0, 0, vid.width, vid.height); -} + GL_ClearBuffer(false, true, true, 0) +}*/ // -----------------+ @@ -499,8 +641,9 @@ static void HWR_ShiftViewPort(void) // Set view aiming, for the sky dome, the skybox, // and the normal view, all with a single function. -void HWR_SetTransformAiming(FTransform *trans, player_t *player, boolean skybox) +void HWR_SetTransformAiming(FTransform *trans, player_t *player, boolean skybox, boolean side_effect) { + angle_t temp_aimingangle; // 1 = always on // 2 = chasecam only if (cv_glshearing.value == 1 || (cv_glshearing.value == 2 && R_IsViewpointThirdPerson(player, skybox))) @@ -508,26 +651,20 @@ void HWR_SetTransformAiming(FTransform *trans, player_t *player, boolean skybox) fixed_t fixedaiming = AIMINGTODY(aimingangle); trans->viewaiming = FIXED_TO_FLOAT(fixedaiming); trans->shearing = true; - gl_aimingangle = 0; + temp_aimingangle = 0; } else { trans->shearing = false; - gl_aimingangle = aimingangle; + temp_aimingangle = aimingangle; } - trans->anglex = (float)(gl_aimingangle>>ANGLETOFINESHIFT)*(360.0f/(float)FINEANGLES); + trans->anglex = (float)(temp_aimingangle>>ANGLETOFINESHIFT)*(360.0f/(float)FINEANGLES); + if (side_effect) + gl_aimingangle = temp_aimingangle; } -// -// Sets the shader state. -// -static void HWR_SetShaderState(void) -{ - GL_SetSpecialState(HWD_SET_SHADERS, (INT32)HWR_UseShader()); -} - -static void HWR_ClearClipper(void) +static void HWR_ResetClipper(void) { angle_t a1 = gld_FrustumAngle(gl_aimingangle); gld_clipper_Clear(); @@ -537,39 +674,28 @@ static void HWR_ClearClipper(void) #endif } -static void HWR_RenderViewpoint(player_t *player, boolean drawSkyTexture, boolean timing) +// clip the area outside the portal destination window +static void HWR_PortalClipping(gl_portal_t *portal) +{ + gld_clipper_SafeAddClipRange(portal->angle1, portal->angle2); +} + +static void HWR_SetShaderState(void) +{ + GL_SetSpecialState(HWD_SET_SHADERS, (INT32)HWR_UseShader()); +} + +// prepare all transform related variables based on the current "frame" +// (R_SetupFrame etc) +static void HWR_PrepareTransform(player_t *player, boolean is_skybox) { UINT8 viewnum = R_GetViewNumber(); camera_t *thiscam = &camera[viewnum]; const float fpov = FIXED_TO_FLOAT(cv_fov[viewssnum].value+player->fovadd); - if (!HWR_ShouldUsePaletteRendering()) - { - // do we really need to save player (is it not the same)? - player_t *saved_player = stplyr; - stplyr = player; - ST_doPaletteStuff(); - stplyr = saved_player; -#ifdef ALAM_LIGHTING - HWR_SetLights(viewssnum); -#endif - } - - // copy view cam position for local use - dup_viewx = viewx; - dup_viewy = viewy; - dup_viewz = viewz; - dup_viewangle = viewangle; - - // set window position - HWR_ShiftViewPort(); - - // check for new console commands. - NetUpdate(); - - gl_viewx = FIXED_TO_FLOAT(dup_viewx); - gl_viewy = FIXED_TO_FLOAT(dup_viewy); - gl_viewz = FIXED_TO_FLOAT(dup_viewz); + gl_viewx = FIXED_TO_FLOAT(viewx); + gl_viewy = FIXED_TO_FLOAT(viewy); + gl_viewz = FIXED_TO_FLOAT(viewz); gl_viewsin = FIXED_TO_FLOAT(viewsin); gl_viewcos = FIXED_TO_FLOAT(viewcos); @@ -577,7 +703,7 @@ static void HWR_RenderViewpoint(player_t *player, boolean drawSkyTexture, boolea // It should replace all other gl_viewxxx when finished memset(&atransform, 0x00, sizeof(FTransform)); - HWR_SetTransformAiming(&atransform, player, false); + HWR_SetTransformAiming(&atransform, player, is_skybox, true); atransform.angley = (float)(viewangle>>ANGLETOFINESHIFT)*(360.0f/(float)FINEANGLES); gl_viewludsin = FIXED_TO_FLOAT(FINECOSINE(gl_aimingangle>>ANGLETOFINESHIFT)); @@ -608,23 +734,129 @@ static void HWR_RenderViewpoint(player_t *player, boolean drawSkyTexture, boolea atransform.splitscreen = r_splitscreen; gl_fovlud = (float)(1.0l/tan((double)(fpov*M_PIl/360l))); +} - //------------------------------------------------------------------------ - HWR_ClearView(); // Clears the depth buffer and resets the view I believe +static void HWR_EnterSkyboxState(void) +{ + HWR_PushBatchingState(); + HWR_PushSpriteState(); + HWR_PushDrawNodeState(); + // need to use the next level in the portal arrays + // so skybox doesn't interfere with previously collected portals + //gl_portal_level++; + gl_rendering_skybox = true; +} - if (drawSkyTexture) - HWR_DrawSkyBackground(player); +static void HWR_LeaveSkyboxState(void) +{ + HWR_PopBatchingState(); + HWR_PopSpriteState(); + HWR_PopDrawNodeState(); + // see above comment + //gl_portal_level--; + gl_rendering_skybox = false; +} + +static void HWR_RenderPortalSeg(seg_t *seg) +{ + gl_drawing_stencil = true; + gl_curline = seg; + gl_frontsector = seg->frontsector; + gl_backsector = seg->backsector; + HWR_ProcessSeg(); + gl_drawing_stencil = false; + // need to work around the r_opengl PF_Invisible bug with this call + // similarly as in the linkdraw hack in HWR_DrawSprites + // TODO not completely sure if this is needed, check it? (try without) + GL_SetBlend(PF_Translucent|PF_Occlude|PF_Masked); +} + +static void HWR_SetStencilState(INT32 state) +{ + // this order of calls must be used for the stencil + // level to take effect correctly + GL_SetSpecialState(HWD_SET_STENCIL_LEVEL, gl_portal_level); + GL_SetSpecialState(HWD_SET_STENCIL_MODE, state); +} + +// clear the depth buffer from the stenciled area so portal +// content doesn't get clipped by previous buffer content +// (glClear ignores the stencil buffer so can't be used for this purpose) +static void HWR_RenderDepthEraser(boolean visible) +{ + FOutVector verts[4] = {0}; // TODO not sure if PF_NoAlphaTest is needed? + FBITFIELD blendflags = PF_Occlude|PF_NoDepthTest|PF_NoTexture|PF_NoAlphaTest; + if (!visible) + blendflags |= PF_Invisible; + // so this is apparently how you draw the far clipping plane when + // pfnSetTransform(NULL) is active + const float a = FAR_CLIPPING_PLANE; + verts[0].x = -a; verts[0].y = -a; verts[0].z = a; + verts[1].x = -a; verts[1].y = a; verts[1].z = a; + verts[2].x = a; verts[2].y = a; verts[2].z = a; + verts[3].x = a; verts[3].y = -a; verts[3].z = a; + GL_SetTransform(NULL); + GL_DrawPolygon(NULL, verts, 4, blendflags); +} + +static void HWR_RenderViewpoint(player_t *player, boolean drawSkyTexture, boolean is_skybox, boolean timing, gl_portal_t *rootportal); + +static void HWR_RenderPortal(gl_portal_t *portal, gl_portal_t *rootportal, player_t *player, boolean is_skybox) +{ + HWR_PushSpriteState(); + HWR_PushDrawNodeState(); + + if (!gl_debugportal) + { + HWR_SetStencilState(HWD_STENCIL_PORTAL_BEGIN); + HWR_RenderPortalSeg(portal->seg); + } + + gl_portal_level++; + if (!gl_debugportal) + HWR_SetStencilState(HWD_STENCIL_PORTAL_INSIDE); + + HWR_RenderDepthEraser(true); + + HWR_PortalFrame(portal, true); + HWR_RenderViewpoint(player, true, is_skybox, false, portal); + // restore previous frame and transform + if (rootportal) + HWR_PortalFrame(rootportal, false); + else if (is_skybox) + R_SkyboxFrame(viewssnum); + else + R_SetupFrame(viewssnum, is_skybox); + HWR_PrepareTransform(player, is_skybox); + GL_SetTransform(&atransform); + + if (!gl_debugportal) + { + HWR_SetStencilState(HWD_STENCIL_PORTAL_FINISH); + HWR_RenderPortalSeg(portal->seg); + } + gl_portal_level--; + + HWR_PopSpriteState(); + HWR_PopDrawNodeState(); +} + +static void HWR_RenderViewpoint(player_t *player, boolean drawSkyTexture, boolean is_skybox, boolean timing, gl_portal_t *rootportal) +{ + unsigned int i; + gl_portal_array_t *portal_array; + + HWR_PrepareTransform(player, is_skybox); HWR_ClearSprites(); - //04/01/2000: Hurdler: added for T&L - // Actually it only works on Walls and Planes - GL_SetTransform(&atransform); + HWR_ResetClipper(); - HWR_ClearClipper(); + if (rootportal) + HWR_PortalClipping(rootportal); - // Reset the shader state. - HWR_SetShaderState(); + // check for new console commands. + NetUpdate(); if (timing) { @@ -634,21 +866,99 @@ static void HWR_RenderViewpoint(player_t *player, boolean drawSkyTexture, boolea } validcount++; + gl_sky_found = false; - if (LIKELY(cv_glbatching.value)) - HWR_StartBatching(); + HWR_StartBatching(); HWR_AddPrecipitationSprites(); HWR_RenderBSPNode((INT32)numnodes-1); + // restore portal clipping variables + gl_portalcullsector = NULL; + gl_portalclipline = NULL; if (timing) { ps_bsptime = I_GetPreciseTime() - ps_bsptime; } - if (LIKELY(cv_glbatching.value)) - HWR_RenderBatches(); + if (gl_printportals && !gl_portal_level && !gl_rendering_skybox) + CONS_Printf("Portal recursion summary:\n"); + + //if (gl_printportals) + // CONS_Printf("%d bsp calls\n", ps_numbspcalls); + + if (gl_sky_found) + { + // HWR_DrawSkyBackground is not able to set the texture without + // pausing batching first + HWR_PauseBatching(); + if (skyboxmo[0] && cv_skybox.value && !is_skybox && !rootportal && !gl_debugportal) + { + //if (gl_printportals) + // CONS_Printf("drawing a skybox\n"); + // TODO NOTE this probably wont set correct position under portals? + R_SkyboxFrame(viewssnum); + // render skybox while keeping batches, sprites and drawnodes + // from the regular viewpoint stashed in the state stacks + HWR_EnterSkyboxState(); + HWR_RenderViewpoint(player, true, true, false, rootportal); + HWR_LeaveSkyboxState(); + // restore (=clear) z-buffer, but only in the portal window + if (rootportal) + HWR_RenderDepthEraser(false); + else + GL_ClearBuffer(false, true, false, 0); + // restore transform + if (rootportal) + HWR_PortalFrame(rootportal, false); + else + R_SetupFrame(viewssnum, is_skybox); + HWR_PrepareTransform(player, is_skybox); + } + else + { + if (drawSkyTexture) + HWR_DrawSkyBackground(player); + } + // turn batching back on + HWR_StartBatching(); + } + + // apply transform to backend now when we're actually drawing to the screen + GL_SetTransform(&atransform); + + HWR_RenderBatches(); + + // this is a hacky way to get rid of the main viewpoint for the debugportal + // command but maybe simpler than other options + if (gl_debugportal && !gl_portal_level) + GL_ClearBuffer(true, true, true, 0); + + portal_array = HWR_GetPortalArray(); + for (i = 0; i < portal_array->size; i++) + HWR_RenderPortal(&portal_array->portals[i], rootportal, player, is_skybox); + HWR_ClearPortals(); + // if there was portals, restore stencil state since HWR_RenderPortal + // has altered it + if (i) + { + HWR_SetStencilState(gl_portal_level ? + HWD_STENCIL_PORTAL_INSIDE : HWD_STENCIL_INACTIVE); + if (gl_printportals && !gl_rendering_skybox) + { + CONS_Printf("%*c%d: %u portals rendered\n", gl_portal_level+1, 'L', + gl_portal_level, i); + } + } + + // hack for debugportal, see earlier comment + if (gl_debugportal && !gl_portal_level) + { + gl_portal_level = 15; // this will make the sprites and drawnodes get discarded + HWR_SetStencilState(HWD_STENCIL_PORTAL_INSIDE); + gl_portal_level = 0; + } // Check for new console commands. NetUpdate(); @@ -663,11 +973,14 @@ static void HWR_RenderViewpoint(player_t *player, boolean drawSkyTexture, boolea if (timing) { - ps_numsprites = gl_visspritecount; + ps_hw_spritesorttime = I_GetPreciseTime(); } - HWR_SortVisSprites(); + UINT32 count = HWR_SortVisSprites(); + + if (timing) + ps_numsprites = count; if (timing) { @@ -696,15 +1009,12 @@ static void HWR_RenderViewpoint(player_t *player, boolean drawSkyTexture, boolea HWR_RenderDrawNodes(); //Hurdler: render 3D water and transparent walls after everything - GL_SetTransform(NULL); - GL_UnSetShader(); + // hack for debugportal, see earlier comment + if (gl_debugportal && !gl_portal_level) + HWR_SetStencilState(HWD_STENCIL_INACTIVE); // Check for new console commands. NetUpdate(); - - // added by Hurdler for correct splitscreen - // moved here by hurdler so it works with the new near clipping plane - GL_GClipRect(0, 0, vid.width, vid.height, NZCLIP_PLANE); } // ========================================================================== @@ -715,7 +1025,7 @@ void HWR_RenderSkyboxView(player_t *player) // note: sets viewangle, viewx, viewy, viewz R_SkyboxFrame(viewssnum); - HWR_RenderViewpoint(player, true, false); + HWR_RenderViewpoint(player, true, true, false, NULL); } // ========================================================================== @@ -746,11 +1056,37 @@ void HWR_RenderPlayerView(void) ClearColor.blue = 0.0f; ClearColor.alpha = 1.0f; + GL_ClearBuffer(true, true, true, &ClearColor); + + if (!HWR_ShouldUsePaletteRendering()) + { + // do we really need to save player (is it not the same)? + player_t *saved_player = stplyr; + stplyr = player; + ST_doPaletteStuff(); + stplyr = saved_player; +#ifdef ALAM_LIGHTING + HWR_SetLights(viewssnum); +#endif + } + + // set window position + HWR_ShiftViewPort(); + + GL_GClipRect((INT32)gl_viewwindowx, + (INT32)gl_viewwindowy, + (INT32)(gl_viewwindowx + gl_viewwidth), + (INT32)(gl_viewwindowy + gl_viewheight), + ZCLIP_PLANE); + + // Reset the shader state. + HWR_SetShaderState(); + if (cv_glshaders.value) GL_SetShaderInfo(HWD_SHADERINFO_LEVELTIME, (INT32)leveltime); // The water surface shader needs the leveltime. if (viewssnum == 0) // Only do it if it's the first screen being rendered - GL_ClearBuffer(true, false, &ClearColor); // Clear the Color Buffer, stops HOMs. Also seems to fix the skybox issue on Intel GPUs. + GL_ClearBuffer(true, false, false, &ClearColor); // Clear the Color Buffer, stops HOMs. Also seems to fix the skybox issue on Intel GPUs. ps_hw_skyboxtime = I_GetPreciseTime(); if (skybox) // If there's a skybox and we should be drawing the sky, draw the skybox @@ -766,12 +1102,24 @@ void HWR_RenderPlayerView(void) HWR_RenderViewpoint(player, !skybox, // Don't draw the regular sky if there's a skybox - true); // Main view is profiled + false, + true, // Main view is profiled + NULL); + + gl_printportals = false; + gl_debugportal = 0; + + GL_SetTransform(NULL); + GL_UnSetShader(); HWR_DoPostProcessor(player); // Check for new console commands. NetUpdate(); + + // added by Hurdler for correct splitscreen + // moved here by hurdler so it works with the new near clipping plane + GL_GClipRect(0, 0, vid.width, vid.height, NZCLIP_PLANE); } // Returns whether palette rendering is "actually enabled." @@ -902,6 +1250,10 @@ void CV_glpalettedepth_OnChange(void); consvar_t cv_glpaletterendering = CVAR_INIT ("gr_paletteshader", "Off", CV_CALL|CV_SAVE, CV_OnOff, CV_glpaletterendering_OnChange); consvar_t cv_glpalettedepth = CVAR_INIT ("gr_palettedepth", "16 bits", CV_SAVE|CV_CALL, glpalettedepth_cons_t, CV_glpalettedepth_OnChange); +consvar_t cv_gldebugportal = CVAR_INIT ("gr_debugportal", "0", 0, CV_Unsigned, NULL); + +consvar_t cv_glskydebug = CVAR_INIT ("gr_skydebug", "0", 0, CV_Unsigned, NULL); + #define ONLY_IF_GL_LOADED if (vid.glstate != VID_GL_LIBRARY_LOADED) return; static void CV_glfiltermode_OnChange(void) @@ -956,6 +1308,19 @@ void CV_glshaders_OnChange(void) } } +static void Command_Glprintportals_f(void) +{ + if (cht_debug) + { + gl_printportals = true; + CONS_Printf("List of top level portals:\n"); + } + else + { + CONS_Printf("This command is only available in devmode.\n"); + } +} + //added by Hurdler: console varibale that are saved void HWR_AddCommands(void) { @@ -991,6 +1356,11 @@ void HWR_AddCommands(void) CV_RegisterVar(&cv_glpaletterendering); CV_RegisterVar(&cv_glpalettedepth); + + COM_AddCommand("gr_printportals", Command_Glprintportals_f); + CV_RegisterVar(&cv_gldebugportal); + + CV_RegisterVar(&cv_glskydebug); } void HWR_AddSessionCommands(void) diff --git a/src/hardware/hw_main.h b/src/hardware/hw_main.h index 87f0dd883..0f03d5132 100644 --- a/src/hardware/hw_main.h +++ b/src/hardware/hw_main.h @@ -38,7 +38,7 @@ void HWR_RenderSkyboxView(player_t *player); void HWR_RenderPlayerView(void); void HWR_DrawFlatFill(INT32 x, INT32 y, INT32 w, INT32 h, lumpnum_t flatlumpnum); void HWR_SetViewSize(void); -void HWR_SetTransformAiming(FTransform *trans, player_t *player, boolean skybox); +void HWR_SetTransformAiming(FTransform *trans, player_t *player, boolean skybox, boolean side_effect); void HWR_RollTransform(FTransform *tr, angle_t roll); boolean HWR_BlendMidtextureSurface(FSurfaceInfo *pSurf); void HWR_DrawStretchyFixedPatch(patch_t *gpatch, fixed_t x, fixed_t y, fixed_t pscale, fixed_t vscale, INT32 option, const UINT8 *colormap); @@ -118,6 +118,9 @@ extern consvar_t cv_glskydome; extern consvar_t cv_glbatching; extern consvar_t cv_glpaletterendering; extern consvar_t cv_glpalettedepth; +extern consvar_t cv_gldebugportal; + +extern consvar_t cv_glskydebug; extern float gl_viewwidth, gl_viewheight, gl_baseviewwindowy; @@ -133,9 +136,6 @@ extern sector_t *gl_backsector; extern FTransform atransform; extern float gl_viewsin, gl_viewcos; -extern fixed_t dup_viewx, dup_viewy, dup_viewz; -extern angle_t dup_viewangle; - extern float gl_viewx, gl_viewy, gl_viewz; extern float gl_viewsin, gl_viewcos; @@ -164,6 +164,24 @@ extern boolean gl_maploaded; extern boolean gl_sessioncommandsadded; extern boolean gl_shadersavailable; +extern boolean gl_rendering_skybox; + +/* Portal stuff */ + +// TODO probably put this to a separate file? + +boolean HWR_AddPortal(line_t *start, line_t *dest, seg_t *seg); + +extern INT32 gl_portal_level; + +extern boolean gl_drawing_stencil; +extern sector_t *gl_portalcullsector; +extern line_t *gl_portalclipline; +extern INT32 gl_portalviewside; + +extern boolean gl_printportals; +extern INT32 gl_debugportal; + #ifdef __cplusplus } // extern "C" #endif diff --git a/src/hardware/hw_plane.c b/src/hardware/hw_plane.c index a65f4d5a1..179895f1d 100644 --- a/src/hardware/hw_plane.c +++ b/src/hardware/hw_plane.c @@ -218,7 +218,7 @@ void HWR_RenderPlane(subsector_t *subsector, extrasubsector_t *xsub, boolean isc for (i = 0; i < subsector->numlines; i++, line++) { - if (!line->glseg && line->linedef->special == HORIZONSPECIAL && R_PointOnSegSide(dup_viewx, dup_viewy, line) == 0) + if (!line->glseg && line->linedef->special == HORIZONSPECIAL && R_PointOnSegSide(viewx, viewy, line) == 0) { P_ClosestPointOnLine(viewx, viewy, line->linedef, &v); dist = FIXED_TO_FLOAT(R_PointToDist(v.x, v.y)); diff --git a/src/hardware/hw_segs.c b/src/hardware/hw_segs.c index e8e6066bb..e8f42d593 100644 --- a/src/hardware/hw_segs.c +++ b/src/hardware/hw_segs.c @@ -92,6 +92,13 @@ static void HWR_ProjectWall(FOutVector *wallVerts, FSurfaceInfo *pSurf, FBITFIEL blendmode |= PF_ColorMapped; } + // don't draw to color buffer when drawing to stencil + if (gl_drawing_stencil) + { + blendmode |= PF_Invisible|PF_NoAlphaTest; // TODO not sure if any others than PF_Invisible are needed?? + blendmode &= ~PF_Masked; + } + HWR_ProcessPolygon(pSurf, wallVerts, 4, blendmode|PF_Modulated|PF_Occlude, shader, false); } @@ -287,6 +294,17 @@ static void HWR_SplitWall(sector_t *sector, FOutVector *wallVerts, INT32 texnum, // Draw walls into the depth buffer so that anything behind is culled properly static void HWR_DrawSkyWall(FOutVector *wallVerts, FSurfaceInfo *Surf) { + if (cv_glskydebug.value) + { + wallVerts[3].t = wallVerts[2].t = 4; + wallVerts[0].t = wallVerts[1].t = 0; + wallVerts[0].s = wallVerts[3].s = 0.1; + wallVerts[2].s = wallVerts[1].s = 0; + HWR_GetTexture(cv_glskydebug.value, false); + HWR_ProjectWall(wallVerts, Surf, 0, 255, NULL); + return; + } + HWR_SetCurrentTexture(NULL); // no texture wallVerts[3].t = wallVerts[2].t = 0; @@ -373,9 +391,9 @@ static void HWR_ProcessTwoSidedSegTop(FOutVector *wallVerts, FSurfaceInfo *Surf, if (grTex->mipmap.flags & TF_TRANSPARENT) polyflags = PF_Environment; - if (gl_frontsector->numlights) + if (!gl_drawing_stencil && gl_frontsector->numlights) HWR_SplitWall(gl_frontsector, wallVerts, gl_toptexture, noencore, Surf, FOF_CUTLEVEL, NULL, polyflags); - else if (grTex->mipmap.flags & TF_TRANSPARENT) + else if (!gl_drawing_stencil && (grTex->mipmap.flags & TF_TRANSPARENT)) HWR_AddTransparentWall(wallVerts, Surf, gl_toptexture, noencore, polyflags, false, lightnum, gl_frontsector->extra_colormap); else HWR_ProjectWall(wallVerts, Surf, polyflags, lightnum, gl_frontsector->extra_colormap); @@ -441,14 +459,17 @@ static void HWR_ProcessTwoSidedSegBottom(FOutVector *wallVerts, FSurfaceInfo *Su if (grTex->mipmap.flags & TF_TRANSPARENT) polyflags = PF_Environment; - if (gl_frontsector->numlights) + if (!gl_drawing_stencil && gl_frontsector->numlights) HWR_SplitWall(gl_frontsector, wallVerts, gl_bottomtexture, noencore, Surf, FOF_CUTLEVEL, NULL, polyflags); - else if (grTex->mipmap.flags & TF_TRANSPARENT) + else if (!gl_drawing_stencil && (grTex->mipmap.flags & TF_TRANSPARENT)) HWR_AddTransparentWall(wallVerts, Surf, gl_bottomtexture, noencore, polyflags, false, lightnum, gl_frontsector->extra_colormap); else HWR_ProjectWall(wallVerts, Surf, polyflags, lightnum, gl_frontsector->extra_colormap); } + +// gl_midtexture can be inactive for this function +// when rendering twosided portal midtextures static void HWR_ProcessTwoSidedSegMiddle(FOutVector *wallVerts, FSurfaceInfo *Surf, gl_seg_bounds *b, float cliplow, float cliphigh, FUINT lightnum, INT32 gl_midtexture, boolean noencore, boolean tripwire) { @@ -462,6 +483,8 @@ static void HWR_ProcessTwoSidedSegMiddle(FOutVector *wallVerts, FSurfaceInfo *Su fixed_t popentopslope, popenbottomslope, polytopslope, polybottomslope, lowcutslope, highcutslope; fixed_t texturevpeg = 0; INT32 repeats; + fixed_t midtexheight = 0; + fixed_t texturevpegslope = 0; if (gl_linedef->frontsector->heightsec != -1) front = §ors[gl_linedef->frontsector->heightsec]; @@ -473,28 +496,31 @@ static void HWR_ProcessTwoSidedSegMiddle(FOutVector *wallVerts, FSurfaceInfo *Su else back = gl_linedef->backsector; - if (gl_sidedef->repeatcnt) - repeats = 1 + gl_sidedef->repeatcnt; - else if (gl_linedef->flags & ML_WRAPMIDTEX) + if (gl_midtexture) { - fixed_t high, low; + if (gl_sidedef->repeatcnt) + repeats = 1 + gl_sidedef->repeatcnt; + else if (gl_linedef->flags & ML_WRAPMIDTEX) + { + fixed_t high, low; - if (front->ceilingheight > back->ceilingheight) - high = back->ceilingheight; + if (front->ceilingheight > back->ceilingheight) + high = back->ceilingheight; + else + high = front->ceilingheight; + + if (front->floorheight > back->floorheight) + low = front->floorheight; + else + low = back->floorheight; + + repeats = (high - low) / textureheight[gl_midtexture]; + if ((high - low) % textureheight[gl_midtexture]) + repeats++; // tile an extra time to fill the gap -- Monster Iestyn + } else - high = front->ceilingheight; - - if (front->floorheight > back->floorheight) - low = front->floorheight; - else - low = back->floorheight; - - repeats = (high - low) / textureheight[gl_midtexture]; - if ((high - low) % textureheight[gl_midtexture]) - repeats++; // tile an extra time to fill the gap -- Monster Iestyn + repeats = 1; } - else - repeats = 1; // NOTE: With polyobjects, whenever you need to check the properties of the polyobject sector it belongs to, // you must use the linedef's backsector to be correct @@ -512,40 +538,43 @@ static void HWR_ProcessTwoSidedSegMiddle(FOutVector *wallVerts, FSurfaceInfo *Su popenbottomslope = max(b->worldbottomslope, b->worldlowslope); } - // Find the wall's coordinates - fixed_t midtexheight = textureheight[gl_midtexture] * repeats; - - // Texture is not skewed - if (gl_linedef->flags & ML_NOSKEW) + if (gl_midtexture) { - if (gl_linedef->flags & ML_MIDPEG) // Peg it to the floor + // Find the wall's coordinates + midtexheight = textureheight[gl_midtexture] * repeats; + + // Texture is not skewed + if (gl_linedef->flags & ML_NOSKEW) { - polybottom = max(front->floorheight, back->floorheight) + gl_sidedef->rowoffset + gl_sidedef->offsety_mid; + if (gl_linedef->flags & ML_MIDPEG) // Peg it to the floor + { + polybottom = max(front->floorheight, back->floorheight) + gl_sidedef->rowoffset + gl_sidedef->offsety_mid; + polytop = polybottom + midtexheight; + } + else // Peg it to the ceiling + { + polytop = min(front->ceilingheight, back->ceilingheight) + gl_sidedef->rowoffset + gl_sidedef->offsety_mid; + polybottom = polytop - midtexheight; + } + + // The right side's coordinates are the the same as the left side + polytopslope = polytop; + polybottomslope = polybottom; + } + else if (gl_linedef->flags & ML_MIDPEG) // Skew the texture, but peg it to the floor + { + polybottom = popenbottom + gl_sidedef->rowoffset + gl_sidedef->offsety_mid; polytop = polybottom + midtexheight; + polybottomslope = popenbottomslope + gl_sidedef->rowoffset + gl_sidedef->offsety_mid; + polytopslope = polybottomslope + midtexheight; } - else // Peg it to the ceiling + else // Skew it according to the ceiling's slope { - polytop = min(front->ceilingheight, back->ceilingheight) + gl_sidedef->rowoffset + gl_sidedef->offsety_mid; + polytop = popentop + gl_sidedef->rowoffset; polybottom = polytop - midtexheight; + polytopslope = popentopslope + gl_sidedef->rowoffset; + polybottomslope = polytopslope - midtexheight; } - - // The right side's coordinates are the the same as the left side - polytopslope = polytop; - polybottomslope = polybottom; - } - else if (gl_linedef->flags & ML_MIDPEG) // Skew the texture, but peg it to the floor - { - polybottom = popenbottom + gl_sidedef->rowoffset + gl_sidedef->offsety_mid; - polytop = polybottom + midtexheight; - polybottomslope = popenbottomslope + gl_sidedef->rowoffset + gl_sidedef->offsety_mid; - polytopslope = polybottomslope + midtexheight; - } - else // Skew it according to the ceiling's slope - { - polytop = popentop + gl_sidedef->rowoffset; - polybottom = polytop - midtexheight; - polytopslope = popentopslope + gl_sidedef->rowoffset; - polybottomslope = polytopslope - midtexheight; } // CB @@ -572,18 +601,22 @@ static void HWR_ProcessTwoSidedSegMiddle(FOutVector *wallVerts, FSurfaceInfo *Su hS = min(highcutslope, polytopslope); lS = max(polybottomslope, lowcutslope); - // PEGGING - fixed_t texturevpegslope; + // gl_midtexture can be inactive when rendering twosided portal midtextures + if (gl_midtexture) + { + // PEGGING + + if (gl_linedef->flags & ML_MIDPEG) + { + texturevpeg = midtexheight - h + polybottom; + texturevpegslope = midtexheight - hS + polybottomslope; + } + else + { + texturevpeg = polytop - h; + texturevpegslope = polytopslope - hS; + } - if (gl_linedef->flags & ML_MIDPEG) - { - texturevpeg = midtexheight - h + polybottom; - texturevpegslope = midtexheight - hS + polybottomslope; - } - else - { - texturevpeg = polytop - h; - texturevpegslope = polytopslope - hS; } grTex = HWR_GetTexture(gl_midtexture, noencore); @@ -639,6 +672,7 @@ static void HWR_ProcessTwoSidedSegMiddle(FOutVector *wallVerts, FSurfaceInfo *Su h = min(highcut, polytop); l = max(polybottom, lowcut); + if (grTex) { // PEGGING if (gl_linedef->flags & ML_MIDPEG) @@ -657,7 +691,10 @@ static void HWR_ProcessTwoSidedSegMiddle(FOutVector *wallVerts, FSurfaceInfo *Su // This will cause the midtexture appear on top, if a FOF overlaps with it. blendmode |= PF_Decal; - if (tripwire == false && gl_frontsector->numlights) + if (!grTex) + blendmode |= PF_NoTexture; + + if (!gl_drawing_stencil && tripwire == false && gl_frontsector->numlights) { if (!(blendmode & PF_Masked)) HWR_SplitWall(gl_frontsector, wallVerts, gl_midtexture, noencore, Surf, FOF_TRANSLUCENT, NULL, blendmode); // vanilla just uses PF_Masked here - if we run into any issues, maybe change to that @@ -666,7 +703,7 @@ static void HWR_ProcessTwoSidedSegMiddle(FOutVector *wallVerts, FSurfaceInfo *Su HWR_SplitWall(gl_frontsector, wallVerts, gl_midtexture, noencore, Surf, FOF_CUTLEVEL, NULL, blendmode); // vanilla just uses PF_Masked here - if we run into any issues, maybe change to that } } - else if (!(blendmode & PF_Masked)) + else if (!gl_drawing_stencil && !(blendmode & PF_Masked)) HWR_AddTransparentWall(wallVerts, Surf, gl_midtexture, noencore, blendmode, false, lightnum, gl_frontsector->extra_colormap); else HWR_ProjectWall(wallVerts, Surf, blendmode, lightnum, gl_frontsector->extra_colormap); @@ -803,7 +840,7 @@ static void HWR_ProcessTwoSidedSeg(FOutVector *wallVerts, gl_seg_bounds *b, } gl_midtexture = R_GetTextureNum(gl_sidedef->midtexture); - if (gl_midtexture && HWR_BlendMidtextureSurface(&Surf)) + if ((gl_midtexture && HWR_BlendMidtextureSurface(&Surf)) || gl_drawing_stencil) { HWR_ProcessTwoSidedSegMiddle(wallVerts, &Surf, b, cliplow, cliphigh, lightnum, gl_midtexture, noencore, tripwire); } @@ -888,7 +925,7 @@ static void HWR_ProcessSingleSidedSeg(FOutVector *wallVerts, gl_seg_bounds *b, wallVerts[1].y = FIXED_TO_FLOAT(b->worldbottomslope); // I don't think that solid walls can use translucent linedef types... - if (gl_frontsector->numlights) + if (!gl_drawing_stencil && gl_frontsector->numlights) HWR_SplitWall(gl_frontsector, wallVerts, gl_midtexture, noencore, &Surf, FOF_CUTLEVEL, NULL, 0); else { @@ -901,7 +938,7 @@ static void HWR_ProcessSingleSidedSeg(FOutVector *wallVerts, gl_seg_bounds *b, HWR_SplitWall(gl_frontsector, wallVerts, gl_midtexture, noencore, &Surf, FOF_CUTLEVEL, NULL, blendmode); else { - if (grTex->mipmap.flags & TF_TRANSPARENT) + if (!gl_drawing_stencil && (grTex->mipmap.flags & TF_TRANSPARENT)) HWR_AddTransparentWall(wallVerts, &Surf, gl_midtexture, noencore, blendmode, false, lightnum, gl_frontsector->extra_colormap); else HWR_ProjectWall(wallVerts, &Surf, blendmode, lightnum, gl_frontsector->extra_colormap); @@ -1188,7 +1225,7 @@ static void HWR_ProcessSegFOFs(FOutVector *wallVerts, gl_seg_bounds *b, void HWR_ProcessSeg(void) // Sort of like GLWall::Process in GZDoom { - FOutVector wallVerts[4]; + FOutVector wallVerts[4] = {0}; v2d_t vs, ve; // start, end vertices of 2d line (view from above) gl_seg_bounds b = {0}; @@ -1275,7 +1312,7 @@ void HWR_ProcessSeg(void) // Sort of like GLWall::Process in GZDoom HWR_ProcessSingleSidedSeg(wallVerts, &b, cliplow, cliphigh, lightnum, noencore); } - if (gl_backsector && !Tag_Compare(&gl_frontsector->tags, &gl_backsector->tags) && (gl_backsector->ffloors || gl_frontsector->ffloors)) + if (!gl_drawing_stencil && gl_backsector && !Tag_Compare(&gl_frontsector->tags, &gl_backsector->tags) && (gl_backsector->ffloors || gl_frontsector->ffloors)) { HWR_ProcessSegFOFs(wallVerts, &b, cliplow, cliphigh, lightnum, v1x, v1y, v2x, v2y, noencore); diff --git a/src/hardware/hw_sky.c b/src/hardware/hw_sky.c index 9721a9893..fbd040a6e 100644 --- a/src/hardware/hw_sky.c +++ b/src/hardware/hw_sky.c @@ -175,9 +175,7 @@ void HWR_DrawSkyBackground(player_t *player) memset(&dometransform, 0x00, sizeof(FTransform)); - //04/01/2000: Hurdler: added for T&L - // It should replace all other gl_viewxxx when finished - HWR_SetTransformAiming(&dometransform, player, false); + HWR_SetTransformAiming(&dometransform, player, false, true); dometransform.angley = (float)((viewangle-ANGLE_270)>>ANGLETOFINESHIFT)*(360.0f/(float)FINEANGLES); dometransform.flip = false; @@ -247,7 +245,7 @@ void HWR_DrawSkyBackground(player_t *player) // software doesn't draw any further than 1024 for skies anyway, but this doesn't overlap properly // The only time this will probably be an issue is when a sky wider than 1024 is used as a sky AND a regular wall texture - angle = (dup_viewangle + ANGLE_45); + angle = (viewangle + ANGLE_45); dimensionmultiply = ((float)textures[texturetranslation[skytexture]]->width/256.0f); diff --git a/src/hardware/hw_things.c b/src/hardware/hw_things.c index a1bbb176b..83f50709b 100644 --- a/src/hardware/hw_things.c +++ b/src/hardware/hw_things.c @@ -28,16 +28,57 @@ static void HWR_ProjectPrecipitationSprite(precipmobj_t *thing); // sprites are drawn after all wall and planes are rendered, so that // sprite translucency effects apply on the rendered view (instead of the background sky!!) -UINT32 gl_visspritecount; -static gl_vissprite_t *gl_visspritechunks[MAXVISSPRITES >> VISSPRITECHUNKBITS] = {NULL}; +//UINT32 gl_visspritecount; +//static gl_vissprite_t *gl_visspritechunks[MAXVISSPRITES >> VISSPRITECHUNKBITS] = {NULL}; + +typedef struct +{ + FOutVector verts[4]; + gl_vissprite_t *spr; +} zbuffersprite_t; + +typedef struct +{ + UINT32 gl_visspritecount; + gl_vissprite_t *gl_visspritechunks[MAXVISSPRITES >> VISSPRITECHUNKBITS]; + zbuffersprite_t linkdrawlist[MAXVISSPRITES]; + UINT32 linkdrawcount; +} gl_sprite_state_t; + +// TODO this array is ~3 megabytes because of linkdrawlist... +// maybe turn that into a dynamic array +// TODO magic number 16 +static gl_sprite_state_t state_stack[16] = {0}; +static int stack_level = 0; +static gl_sprite_state_t *cst = &state_stack[0]; // current state // -------------------------------------------------------------------------- // HWR_ClearSprites -// Called at frame start. +// Called at viewpoint start. // -------------------------------------------------------------------------- void HWR_ClearSprites(void) { - gl_visspritecount = 0; + cst->gl_visspritecount = 0; +} + +// pushes all sprite rendering state to stack +void HWR_PushSpriteState(void) +{ + // todo magic number 16 + if (stack_level == 15) + I_Error("HWR_PushSpriteState: State stack overflow"); + + stack_level++; + cst++; +} + +void HWR_PopSpriteState(void) +{ + if (stack_level == 0) + I_Error("HWR_PopSpriteState: State stack underflow"); + + stack_level--; + cst--; } // -------------------------------------------------------------------------- @@ -50,18 +91,18 @@ static gl_vissprite_t *HWR_GetVisSprite(UINT32 num) UINT32 chunk = num >> VISSPRITECHUNKBITS; // Allocate chunk if necessary - if (!gl_visspritechunks[chunk]) - Z_Malloc(sizeof(gl_vissprite_t) * VISSPRITESPERCHUNK, PU_LEVEL, &gl_visspritechunks[chunk]); + if (!cst->gl_visspritechunks[chunk]) + Z_Malloc(sizeof(gl_vissprite_t) * VISSPRITESPERCHUNK, PU_LEVEL, &cst->gl_visspritechunks[chunk]); - return gl_visspritechunks[chunk] + (num & VISSPRITEINDEXMASK); + return cst->gl_visspritechunks[chunk] + (num & VISSPRITEINDEXMASK); } static gl_vissprite_t *HWR_NewVisSprite(void) { - if (gl_visspritecount == MAXVISSPRITES) + if (cst->gl_visspritecount == MAXVISSPRITES) return &gl_overflowsprite; - return HWR_GetVisSprite(gl_visspritecount++); + return HWR_GetVisSprite(cst->gl_visspritecount++); } // A hack solution for transparent surfaces appearing on top of linkdraw sprites. @@ -70,24 +111,19 @@ static gl_vissprite_t *HWR_NewVisSprite(void) // NOTE: This will no longer be necessary once full translucent sorting is implemented, where // translucent sprites and surfaces are sorted together. -typedef struct -{ - FOutVector verts[4]; - gl_vissprite_t *spr; -} zbuffersprite_t; - +/* // this list is used to store data about linkdraw sprites zbuffersprite_t linkdrawlist[MAXVISSPRITES]; -UINT32 linkdrawcount = 0; +UINT32 linkdrawcount = 0;*/ // add the necessary data to the list for delayed z-buffer drawing static void HWR_LinkDrawHackAdd(FOutVector *verts, gl_vissprite_t *spr) { - if (linkdrawcount < MAXVISSPRITES) + if (cst->linkdrawcount < MAXVISSPRITES) { - memcpy(linkdrawlist[linkdrawcount].verts, verts, sizeof(FOutVector) * 4); - linkdrawlist[linkdrawcount].spr = spr; - linkdrawcount++; + memcpy(cst->linkdrawlist[cst->linkdrawcount].verts, verts, sizeof(FOutVector) * 4); + cst->linkdrawlist[cst->linkdrawcount].spr = spr; + cst->linkdrawcount++; } } @@ -103,14 +139,14 @@ static void HWR_LinkDrawHackFinish(void) surf.LightInfo.fade_start = 0; surf.LightInfo.fade_end = 31; surf.LightInfo.newfade = false; - for (i = 0; i < linkdrawcount; i++) + for (i = 0; i < cst->linkdrawcount; i++) { // draw sprite shape, only to z-buffer - HWR_GetPatch(linkdrawlist[i].spr->gpatch); - HWR_ProcessPolygon(&surf, linkdrawlist[i].verts, 4, PF_Translucent|PF_Occlude|PF_Invisible, 0, false); + HWR_GetPatch(cst->linkdrawlist[i].spr->gpatch); + HWR_ProcessPolygon(&surf, cst->linkdrawlist[i].verts, 4, PF_Translucent|PF_Occlude|PF_Invisible, 0, false); } // reset list - linkdrawcount = 0; + cst->linkdrawcount = 0; } // @@ -1352,14 +1388,15 @@ static int CompareVisSprites(const void *p1, const void *p2) return -1; } -void HWR_SortVisSprites(void) +UINT32 HWR_SortVisSprites(void) { UINT32 i; - for (i = 0; i < gl_visspritecount; i++) + for (i = 0; i < cst->gl_visspritecount; i++) { gl_vsprorder[i] = HWR_GetVisSprite(i); } - qs22j(gl_vsprorder, gl_visspritecount, sizeof(gl_vissprite_t*), CompareVisSprites); + qs22j(gl_vsprorder, cst->gl_visspritecount, sizeof(gl_vissprite_t*), CompareVisSprites); + return cst->gl_visspritecount; } // -------------------------------------------------------------------------- @@ -1378,7 +1415,7 @@ void HWR_DrawSprites(void) GL_SetSpecialState(HWD_SET_MODEL_LIGHTING, 1); #endif - for (i = 0; i < gl_visspritecount; i++) + for (i = 0; i < cst->gl_visspritecount; i++) { gl_vissprite_t *spr = gl_vsprorder[i]; if (spr->bbox) diff --git a/src/hardware/r_opengl/r_opengl.c b/src/hardware/r_opengl/r_opengl.c index 1e5dc802b..a2a015578 100644 --- a/src/hardware/r_opengl/r_opengl.c +++ b/src/hardware/r_opengl/r_opengl.c @@ -62,7 +62,6 @@ static GLuint NOTEXTURE_NUM = 0; #define N_PI_DEMI (M_PIl/2.0f) //(1.5707963268f) #define ASPECT_RATIO (1.0f) //(320.0f/200.0f) -#define FAR_CLIPPING_PLANE 32768.0f // Draw further! Tails 01-21-2001 static float NEAR_CLIPPING_PLANE = NZCLIP_PLANE; // ************************************************************************** @@ -74,6 +73,7 @@ static GLuint tex_downloaded = 0; static GLuint lt_downloaded = 0; // currently bound lighttable texture static GLfloat fov = 90.0f; static FBITFIELD CurrentPolyFlags; +static GLint gl_stencil_ref = 0; // Linked list of all textures. static FTextureInfo *TexCacheTail = NULL; @@ -339,6 +339,12 @@ static PFNglDepthMask pglDepthMask; typedef void (APIENTRY * PFNglDepthRange) (GLclampd near_val, GLclampd far_val); static PFNglDepthRange pglDepthRange; +/* Stencil Buffer */ +typedef void (APIENTRY * PFNglStencilFunc) (GLenum func, GLint ref, GLuint mask); +static PFNglStencilFunc pglStencilFunc; +typedef void (APIENTRY * PFNglStencilOp) (GLenum sfail, GLenum dpfail, GLenum dppass); +static PFNglStencilOp pglStencilOp; + /* Transformation */ typedef void (APIENTRY * PFNglMatrixMode) (GLenum mode); static PFNglMatrixMode pglMatrixMode; @@ -521,6 +527,9 @@ boolean SetupGLfunc(void) GETOPENGLFUNC(pglDepthMask, glDepthMask) GETOPENGLFUNC(pglDepthRange, glDepthRange) + GETOPENGLFUNC(pglStencilFunc, glStencilFunc) + GETOPENGLFUNC(pglStencilOp, glStencilOp) + GETOPENGLFUNC(pglMatrixMode, glMatrixMode) GETOPENGLFUNC(pglViewport, glViewport) GETOPENGLFUNC(pglPushMatrix, glPushMatrix) @@ -1107,6 +1116,7 @@ void SetStates(void) //pglDisable(GL_DITHER); // faB: ??? (undocumented in OpenGL 1.1) // Hurdler: yes, it is! + pglEnable(GL_STENCIL_TEST); pglEnable(GL_DEPTH_TEST); // check the depth buffer pglDepthMask(GL_TRUE); // enable writing to depth buffer pglClearDepth(1.0f); @@ -1344,6 +1354,7 @@ void GL_GClipRect(INT32 minx, INT32 miny, INT32 maxx, INT32 maxy, float nearclip // -----------------+ void GL_ClearBuffer(FBOOLEAN ColorMask, FBOOLEAN DepthMask, + FBOOLEAN StencilMask, FRGBAFloat * ClearColor) { // GL_DBG_Printf ("ClearBuffer(%d)\n", alpha); @@ -1365,6 +1376,8 @@ void GL_ClearBuffer(FBOOLEAN ColorMask, pglDepthFunc(GL_LEQUAL); ClearMask |= GL_DEPTH_BUFFER_BIT; } + if (StencilMask) + ClearMask |= GL_STENCIL_BUFFER_BIT; GL_SetBlend(DepthMask ? PF_Occlude | CurrentPolyFlags : CurrentPolyFlags&~PF_Occlude); @@ -2387,6 +2400,32 @@ void GL_SetSpecialState(hwdspecialstate_t IdState, INT32 Value) Flush(); //??? if we want to change filter mode by texture, remove this break; + case HWD_SET_STENCIL_MODE: + switch (Value) + { + case HWD_STENCIL_INACTIVE: + pglStencilFunc(GL_ALWAYS, gl_stencil_ref, 0xFF); + pglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + break; + case HWD_STENCIL_PORTAL_BEGIN: + pglStencilFunc(GL_EQUAL, gl_stencil_ref, 0xFF); + pglStencilOp(GL_KEEP, GL_KEEP, GL_INCR); + break; + case HWD_STENCIL_PORTAL_INSIDE: + pglStencilFunc(GL_EQUAL, gl_stencil_ref, 0xFF); + pglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + break; + case HWD_STENCIL_PORTAL_FINISH: + pglStencilFunc(GL_EQUAL, gl_stencil_ref, 0xFF); + pglStencilOp(GL_KEEP, GL_KEEP, GL_DECR); + break; + } + break; + + case HWD_SET_STENCIL_LEVEL: + gl_stencil_ref = Value; + break; + default: break; } @@ -3384,7 +3423,7 @@ void GL_DrawScreenFinalTexture(int tex, int width, int height) clearColour.red = clearColour.green = clearColour.blue = 0; clearColour.alpha = 1; - GL_ClearBuffer(true, false, &clearColour); + GL_ClearBuffer(true, false, false, &clearColour); pglBindTexture(GL_TEXTURE_2D, screenTextures[tex]); pglColor4ubv(white); diff --git a/src/sdl/i_video.cpp b/src/sdl/i_video.cpp index 695873dad..1e2024ac5 100644 --- a/src/sdl/i_video.cpp +++ b/src/sdl/i_video.cpp @@ -1655,6 +1655,7 @@ static SDL_Window *Impl_CreateWindow(void) // Some GPU drivers may give us a 16-bit depth buffer since the // default value for SDL_GL_DEPTH_SIZE is 16. SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); + SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 4); #endif SDL_Window *win = NULL;