// //--------------------------------------------------------------------------- // // Copyright(C) 2000-2016 Christoph Oelckers // All rights reserved. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser 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 Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License // along with this program. If not, see http://www.gnu.org/licenses/ // //-------------------------------------------------------------------------- // /* ** gl_flat.cpp ** Flat rendering ** */ #include "gl/system/gl_system.h" #include "a_sharedglobal.h" #include "r_defs.h" #include "r_sky.h" #include "r_utility.h" #include "doomstat.h" #include "d_player.h" #include "g_levellocals.h" #include "actorinlines.h" #include "p_lnspec.h" #include "hwrenderer/dynlights/hw_dynlightdata.h" #include "gl/system/gl_interface.h" #include "hwrenderer/utility/hw_cvars.h" #include "gl/renderer/gl_renderer.h" #include "gl/renderer/gl_lightdata.h" #include "gl/renderer/gl_renderstate.h" #include "gl/data/gl_vertexbuffer.h" #include "gl/dynlights/gl_lightbuffer.h" #include "gl/scene/gl_drawinfo.h" #include "gl/scene/gl_scenedrawer.h" #include "gl/renderer/gl_quaddrawer.h" //========================================================================== // // Flats // //========================================================================== extern FDynLightData lightdata; void FDrawInfo::SetupSubsectorLights(GLFlat *flat, int pass, subsector_t * sub, int *dli) { if (dli != NULL && *dli != -1) { gl_RenderState.ApplyLightIndex(GLRenderer->mLights->GetIndex(*dli)); (*dli)++; return; } if (flat->SetupSubsectorLights(pass, sub)) { int d = GLRenderer->mLights->UploadLights(lightdata); if (pass == GLPASS_LIGHTSONLY) { GLRenderer->mLights->StoreIndex(d); } else { gl_RenderState.ApplyLightIndex(d); } } } //========================================================================== // // // //========================================================================== void FDrawInfo::DrawSubsector(GLFlat *flat, subsector_t * sub) { if (gl.buffermethod != BM_DEFERRED) { FFlatVertex *ptr = GLRenderer->mVBO->GetBuffer(); for (unsigned int k = 0; k < sub->numlines; k++) { vertex_t *vt = sub->firstline[k].v1; ptr->x = vt->fX(); ptr->z = flat->plane.plane.ZatPoint(vt) + flat->dz; ptr->y = vt->fY(); ptr->u = vt->fX() / 64.f; ptr->v = -vt->fY() / 64.f; ptr++; } GLRenderer->mVBO->RenderCurrent(ptr, GL_TRIANGLE_FAN); } else { // if we cannot access the buffer, use the quad drawer as fallback by splitting the subsector into quads. // Trying to get this into the vertex buffer in the processing pass is too costly and this is only used for render hacks. FQuadDrawer qd; unsigned int vi[4]; vi[0] = 0; for (unsigned int i = 0; i < sub->numlines - 2; i += 2) { for (unsigned int j = 1; j < 4; j++) { vi[j] = MIN(i + j, sub->numlines - 1); } for (unsigned int x = 0; x < 4; x++) { vertex_t *vt = sub->firstline[vi[x]].v1; qd.Set(x, vt->fX(), flat->plane.plane.ZatPoint(vt) + flat->dz, vt->fY(), vt->fX() / 64.f, -vt->fY() / 64.f); } qd.Render(GL_TRIANGLE_FAN); } } flatvertices += sub->numlines; flatprimitives++; } //========================================================================== // // this is only used by LM_DEFERRED // //========================================================================== void FDrawInfo::ProcessLights(GLFlat *flat, bool istrans) { flat->dynlightindex = GLRenderer->mLights->GetIndexPtr(); // Draw the subsectors belonging to this sector for (int i=0; i< flat->sector->subsectorcount; i++) { subsector_t * sub = flat->sector->subsectors[i]; if (gl_drawinfo->ss_renderflags[sub->Index()]& flat->renderflags || istrans) { SetupSubsectorLights(flat, GLPASS_LIGHTSONLY, sub, nullptr); } } // Draw the subsectors assigned to it due to missing textures if (!(flat->renderflags&SSRF_RENDER3DPLANES)) { gl_subsectorrendernode * node = (flat->renderflags&SSRF_RENDERFLOOR)? gl_drawinfo->GetOtherFloorPlanes(flat->sector->sectornum) : gl_drawinfo->GetOtherCeilingPlanes(flat->sector->sectornum); while (node) { SetupSubsectorLights(flat, GLPASS_LIGHTSONLY, node->sub, nullptr); node = node->next; } } } //========================================================================== // // // //========================================================================== void FDrawInfo::DrawSubsectors(GLFlat *flat, int pass, bool processlights, bool istrans) { int dli = flat->dynlightindex; gl_RenderState.Apply(); if (flat->vboindex >= 0) { int index = flat->vboindex; for (int i=0; isector->subsectorcount; i++) { subsector_t * sub = flat->sector->subsectors[i]; if (gl_drawinfo->ss_renderflags[sub->Index()]& flat->renderflags || istrans) { if (processlights) SetupSubsectorLights(flat, GLPASS_ALL, sub, &dli); drawcalls.Clock(); glDrawArrays(GL_TRIANGLE_FAN, index, sub->numlines); drawcalls.Unclock(); flatvertices += sub->numlines; flatprimitives++; } index += sub->numlines; } } else { // Draw the subsectors belonging to this sector // (can this case even happen?) for (int i=0; isector->subsectorcount; i++) { subsector_t * sub = flat->sector->subsectors[i]; if (gl_drawinfo->ss_renderflags[sub->Index()]& flat->renderflags || istrans) { if (processlights) SetupSubsectorLights(flat, GLPASS_ALL, sub, &dli); DrawSubsector(flat, sub); } } } // Draw the subsectors assigned to it due to missing textures if (!(flat->renderflags&SSRF_RENDER3DPLANES)) { gl_subsectorrendernode * node = (flat->renderflags&SSRF_RENDERFLOOR)? gl_drawinfo->GetOtherFloorPlanes(flat->sector->sectornum) : gl_drawinfo->GetOtherCeilingPlanes(flat->sector->sectornum); while (node) { if (processlights) SetupSubsectorLights(flat, GLPASS_ALL, node->sub, &dli); DrawSubsector(flat, node->sub); node = node->next; } } } //========================================================================== // // special handling for skyboxes which need texture clamping. // This will find the bounding rectangle of the sector and just // draw one single polygon filling that rectangle with a clamped // texture. // //========================================================================== void FDrawInfo::DrawSkyboxSector(GLFlat *flat, int pass, bool processlights) { float minx = FLT_MAX, miny = FLT_MAX; float maxx = -FLT_MAX, maxy = -FLT_MAX; for (auto ln : flat->sector->Lines) { float x = ln->v1->fX(); float y = ln->v1->fY(); if (x < minx) minx = x; if (y < miny) miny = y; if (x > maxx) maxx = x; if (y > maxy) maxy = y; x = ln->v2->fX(); y = ln->v2->fY(); if (x < minx) minx = x; if (y < miny) miny = y; if (x > maxx) maxx = x; if (y > maxy) maxy = y; } float z = flat->plane.plane.ZatPoint(0., 0.) + flat->dz; static float uvals[] = { 0, 0, 1, 1 }; static float vvals[] = { 1, 0, 0, 1 }; int rot = -xs_FloorToInt(flat->plane.Angle / 90.f); FQuadDrawer qd; qd.Set(0, minx, z, miny, uvals[rot & 3], vvals[rot & 3]); qd.Set(1, minx, z, maxy, uvals[(rot + 1) & 3], vvals[(rot + 1) & 3]); qd.Set(2, maxx, z, maxy, uvals[(rot + 2) & 3], vvals[(rot + 2) & 3]); qd.Set(3, maxx, z, miny, uvals[(rot + 3) & 3], vvals[(rot + 3) & 3]); qd.Render(GL_TRIANGLE_FAN); flatvertices += 4; flatprimitives++; } //========================================================================== // // // //========================================================================== void FDrawInfo::DrawFlat(GLFlat *flat, int pass, bool trans) // trans only has meaning for GLPASS_LIGHTSONLY { int rel = getExtraLight(); auto &plane = flat->plane; gl_RenderState.SetNormal(plane.plane.Normal().X, plane.plane.Normal().Z, plane.plane.Normal().Y); switch (pass) { case GLPASS_PLAIN: // Single-pass rendering case GLPASS_ALL: // Same, but also creates the dynlight data. mDrawer->SetColor(flat->lightlevel, rel, flat->Colormap,1.0f); mDrawer->SetFog(flat->lightlevel, rel, &flat->Colormap, false); if (!flat->gltexture->tex->isFullbright()) gl_RenderState.SetObjectColor(flat->FlatColor | 0xff000000); if (flat->sector->special != GLSector_Skybox) { gl_RenderState.SetMaterial(flat->gltexture, CLAMP_NONE, 0, -1, false); gl_RenderState.SetPlaneTextureRotation(&plane, flat->gltexture); DrawSubsectors(flat, pass, (pass == GLPASS_ALL || flat->dynlightindex > -1), false); gl_RenderState.EnableTextureMatrix(false); } else { gl_RenderState.SetMaterial(flat->gltexture, CLAMP_XY, 0, -1, false); DrawSkyboxSector(flat, pass, (pass == GLPASS_ALL || flat->dynlightindex > -1)); } gl_RenderState.SetObjectColor(0xffffffff); break; case GLPASS_LIGHTSONLY: if (!trans || flat->gltexture) { ProcessLights(flat, trans); } break; case GLPASS_TRANSLUCENT: if (flat->renderstyle==STYLE_Add) gl_RenderState.BlendFunc(GL_SRC_ALPHA, GL_ONE); mDrawer->SetColor(flat->lightlevel, rel, flat->Colormap, flat->alpha); mDrawer->SetFog(flat->lightlevel, rel, &flat->Colormap, false); if (!flat->gltexture || !flat->gltexture->tex->isFullbright()) gl_RenderState.SetObjectColor(flat->FlatColor | 0xff000000); if (!flat->gltexture) { gl_RenderState.AlphaFunc(GL_GEQUAL, 0.f); gl_RenderState.EnableTexture(false); DrawSubsectors(flat, pass, false, true); gl_RenderState.EnableTexture(true); } else { if (!flat->gltexture->tex->GetTranslucency()) gl_RenderState.AlphaFunc(GL_GEQUAL, gl_mask_threshold); else gl_RenderState.AlphaFunc(GL_GEQUAL, 0.f); gl_RenderState.SetMaterial(flat->gltexture, CLAMP_NONE, 0, -1, false); gl_RenderState.SetPlaneTextureRotation(&plane, flat->gltexture); DrawSubsectors(flat, pass, !gl.legacyMode && (gl.lightmethod == LM_DIRECT || flat->dynlightindex > -1), true); gl_RenderState.EnableTextureMatrix(false); } if (flat->renderstyle==STYLE_Add) gl_RenderState.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); gl_RenderState.SetObjectColor(0xffffffff); break; case GLPASS_LIGHTTEX: case GLPASS_LIGHTTEX_ADDITIVE: case GLPASS_LIGHTTEX_FOGGY: DrawLightsCompat(flat, pass); break; case GLPASS_TEXONLY: gl_RenderState.SetMaterial(flat->gltexture, CLAMP_NONE, 0, -1, false); gl_RenderState.SetPlaneTextureRotation(&plane, flat->gltexture); DrawSubsectors(flat, pass, false, false); gl_RenderState.EnableTextureMatrix(false); break; } } //========================================================================== // // FDrawInfo::AddFlat // // Checks texture, lighting and translucency settings and puts this // plane in the appropriate render list. // //========================================================================== void FDrawInfo::AddFlat(GLFlat *flat, bool fog) { int list; if (gl.legacyMode) { if (PutFlatCompat(flat, fog)) return; } if (flat->renderstyle != STYLE_Translucent || flat->alpha < 1.f - FLT_EPSILON || fog || flat->gltexture == nullptr) { // translucent 3D floors go into the regular translucent list, translucent portals go into the translucent border list. list = (flat->renderflags&SSRF_RENDER3DPLANES) ? GLDL_TRANSLUCENT : GLDL_TRANSLUCENTBORDER; } else if (flat->gltexture->tex->GetTranslucency()) { if (flat->stack) { list = GLDL_TRANSLUCENTBORDER; } else if ((flat->renderflags&SSRF_RENDER3DPLANES) && !flat->plane.plane.isSlope()) { list = GLDL_TRANSLUCENT; } else { list = GLDL_PLAINFLATS; } } else { bool masked = flat->gltexture->isMasked() && ((flat->renderflags&SSRF_RENDER3DPLANES) || flat->stack); list = masked ? GLDL_MASKEDFLATS : GLDL_PLAINFLATS; } auto newflat = gl_drawinfo->drawlists[list].NewFlat(); *newflat = *flat; }