/* ** gl_setup.cpp ** Initializes the data structures required by the GL renderer to handle ** a level ** **--------------------------------------------------------------------------- ** Copyright 2005 Christoph Oelckers ** All rights reserved. ** ** Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions ** are met: ** ** 1. Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** 2. Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in the ** documentation and/or other materials provided with the distribution. ** 3. The name of the author may not be used to endorse or promote products ** derived from this software without specific prior written permission. ** 4. When not used as part of GZDoom or a GZDoom derivative, this code will be ** covered by the terms of the GNU Lesser General Public License as published ** by the Free Software Foundation; either version 2.1 of the License, or (at ** your option) any later version. ** 5. Full disclosure of the entire project's source code, except for third ** party libraries is mandatory. (NOTE: This clause is non-negotiable!) ** ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **--------------------------------------------------------------------------- ** */ #include "doomtype.h" #include "colormatcher.h" #include "i_system.h" #include "p_local.h" #include "p_lnspec.h" #include "c_dispatch.h" #include "r_sky.h" #include "sc_man.h" #include "w_wad.h" #include "gi.h" #include "g_level.h" #include "a_sharedglobal.h" #include "gl/renderer/gl_renderer.h" #include "gl/data/gl_data.h" #include "gl/data/gl_vertexbuffer.h" #include "gl/scene/gl_clipper.h" #include "gl/scene/gl_portal.h" #include "gl/dynlights/gl_dynlight.h" #include "gl/dynlights/gl_glow.h" #include "gl/utility/gl_clock.h" #include "gl/gl_functions.h" struct FPortalID { fixed_t mXDisplacement; fixed_t mYDisplacement; // for the hash code operator intptr_t() const { return (mXDisplacement >> 8) + (mYDisplacement << 8); } bool operator != (const FPortalID &other) const { return mXDisplacement != other.mXDisplacement || mYDisplacement != other.mYDisplacement; } }; struct FPortalSector { sector_t *mSub; int mPlane; }; typedef TArray FPortalSectors; typedef TMap FPortalMap; TArray portals; TArray linePortalToGL; TArray glLinePortals; //========================================================================== // // // //========================================================================== GLSectorStackPortal *FPortal::GetGLPortal() { if (glportal == NULL) glportal = new GLSectorStackPortal(this); return glportal; } //========================================================================== // // // //========================================================================== struct FCoverageVertex { fixed_t x, y; bool operator !=(FCoverageVertex &other) { return x != other.x || y != other.y; } }; struct FCoverageLine { FCoverageVertex v[2]; }; struct FCoverageBuilder { subsector_t *target; FPortal *portal; TArray collect; FCoverageVertex center; //========================================================================== // // // //========================================================================== FCoverageBuilder(subsector_t *sub, FPortal *port) { target = sub; portal = port; } //========================================================================== // // GetIntersection // // adapted from P_InterceptVector // //========================================================================== bool GetIntersection(FCoverageVertex *v1, FCoverageVertex *v2, node_t *bsp, FCoverageVertex *v) { double frac; double num; double den; double v2x = (double)v1->x; double v2y = (double)v1->y; double v2dx = (double)(v2->x - v1->x); double v2dy = (double)(v2->y - v1->y); double v1x = (double)bsp->x; double v1y = (double)bsp->y; double v1dx = (double)bsp->dx; double v1dy = (double)bsp->dy; den = v1dy*v2dx - v1dx*v2dy; if (den == 0) return false; // parallel num = (v1x - v2x)*v1dy + (v2y - v1y)*v1dx; frac = num / den; if (frac < 0. || frac > 1.) return false; v->x = xs_RoundToInt(v2x + frac * v2dx); v->y = xs_RoundToInt(v2y + frac * v2dy); return true; } //========================================================================== // // // //========================================================================== double PartitionDistance(FCoverageVertex *vt, node_t *node) { return fabs(double(-node->dy) * (vt->x - node->x) + double(node->dx) * (vt->y - node->y)) / node->len; } //========================================================================== // // // //========================================================================== int PointOnSide(FCoverageVertex *vt, node_t *node) { return R_PointOnSide(vt->x, vt->y, node); } //========================================================================== // // adapted from polyobject splitter // //========================================================================== void CollectNode(void *node, TArray &shape) { static TArray lists[2]; const double COVERAGE_EPSILON = 6.; // same epsilon as the node builder if (!((size_t)node & 1)) // Keep going until found a subsector { node_t *bsp = (node_t *)node; int centerside = R_PointOnSide(center.x, center.y, bsp); lists[0].Clear(); lists[1].Clear(); for(unsigned i=0;ichildren[0], shape); } else if (lists[0].Size() == 0) { CollectNode(bsp->children[1], shape); } else { // copy the static arrays into local ones TArray locallists[2]; for(int l=0;l<2;l++) { for (unsigned i=0;ichildren[0], locallists[0]); CollectNode(bsp->children[1], locallists[1]); } } else { // we reached a subsector so we can link the node with this subsector subsector_t *sub = (subsector_t *)((BYTE *)node - 1); collect.Push(int(sub-subsectors)); } } }; //========================================================================== // // Calculate portal coverage for a single subsector // //========================================================================== void gl_BuildPortalCoverage(FPortalCoverage *coverage, subsector_t *subsector, FPortal *portal) { TArray shape; double centerx=0, centery=0; shape.Resize(subsector->numlines); for(unsigned i=0; inumlines; i++) { centerx += (shape[i].x = subsector->firstline[i].v1->fixX() + portal->xDisplacement); centery += (shape[i].y = subsector->firstline[i].v1->fixY() + portal->yDisplacement); } FCoverageBuilder build(subsector, portal); build.center.x = xs_CRoundToInt(centerx / subsector->numlines); build.center.y = xs_CRoundToInt(centery / subsector->numlines); build.CollectNode(nodes + numnodes - 1, shape); coverage->subsectors = new DWORD[build.collect.Size()]; coverage->sscount = build.collect.Size(); memcpy(coverage->subsectors, &build.collect[0], build.collect.Size() * sizeof(DWORD)); } //========================================================================== // // portal initialization // //========================================================================== static void CollectPortalSectors(FPortalMap &collection) { for (int i=0;i(sec->SkyBoxes[j]); if (SkyBox != NULL && SkyBox->bAlways && SkyBox->Mate != NULL) { FPortalID id = { SkyBox->_f_X() - SkyBox->Mate->_f_X(), SkyBox->_f_Y() - SkyBox->Mate->_f_Y() }; FPortalSectors &sss = collection[id]; FPortalSector ss = { sec, j }; sss.Push(ss); } } } } void gl_InitPortals() { FPortalMap collection; if (numnodes == 0) return; for(int i=0;idx; double fdy = (double)no->dy; no->len = (float)sqrt(fdx * fdx + fdy * fdy); } CollectPortalSectors(collection); portals.Clear(); FPortalMap::Iterator it(collection); FPortalMap::Pair *pair; int c = 0; int planeflags = 0; while (it.NextPair(pair)) { for(unsigned i=0;iValue.Size(); i++) { if (pair->Value[i].mPlane == sector_t::floor) planeflags |= 1; else if (pair->Value[i].mPlane == sector_t::ceiling) planeflags |= 2; } for (int i=1;i<=2;i<<=1) { // For now, add separate portals for floor and ceiling. They can be merged once // proper plane clipping is in. if (planeflags & i) { FPortal *portal = new FPortal; portal->xDisplacement = pair->Key.mXDisplacement; portal->yDisplacement = pair->Key.mYDisplacement; portal->plane = (i==1? sector_t::floor : sector_t::ceiling); /**/ portal->glportal = NULL; portals.Push(portal); for(unsigned j=0;jValue.Size(); j++) { sector_t *sec = pair->Value[j].mSub; int plane = pair->Value[j].mPlane; if (portal->plane == plane) { for(int k=0;ksubsectorcount; k++) { subsector_t *sub = sec->subsectors[k]; gl_BuildPortalCoverage(&sub->portalcoverage[plane], sub, portal); } sec->portals[plane] = portal; } } } } } // Now group the line portals (each group must be a continuous set of colinear linedefs with no gaps) glLinePortals.Clear(); linePortalToGL.Clear(); TArray tempindex; tempindex.Reserve(linePortals.Size()); memset(&tempindex[0], -1, linePortals.Size() * sizeof(int)); for (unsigned i = 0; i < linePortals.Size(); i++) { auto port = linePortals[i]; bool gotsome; if (tempindex[i] == -1) { tempindex[i] = glLinePortals.Size(); line_t *pSrcLine = linePortals[i].mOrigin; line_t *pLine = linePortals[i].mDestination; FGLLinePortal glport = { pLine->v1, pLine->v2, 0, 0, &linePortals[i] }; glLinePortals.Push(glport); // We cannot do this grouping for non-linked portals because they can be changed at run time. if (linePortals[i].mType == PORTT_LINKED) do { // now collect all other colinear lines connected to this one. We run this loop as long as it still finds a match gotsome = false; for (unsigned j = 0; j < linePortals.Size(); j++) { if (tempindex[j] == -1) { line_t *pSrcLine2 = linePortals[j].mOrigin; line_t *pLine2 = linePortals[j].mDestination; // angular precision is intentionally reduced to 32 bit BAM to account for precision problems (otherwise many not perfectly horizontal or vertical portals aren't found here.) unsigned srcang = pSrcLine->Delta().Angle().BAMs(); unsigned dstang = pLine->Delta().Angle().BAMs(); if ((pSrcLine->v2 == pSrcLine2->v1 && pLine->v1 == pLine2->v2) || (pSrcLine->v1 == pSrcLine2->v2 && pLine->v2 == pLine2->v1)) { // The line connects, now check the translation unsigned srcang2 = pSrcLine2->Delta().Angle().BAMs(); unsigned dstang2 = pLine2->Delta().Angle().BAMs(); if (srcang == srcang2 && dstang == dstang2) { // The lines connect and both source and destination are colinear, so this is a match gotsome = true; tempindex[j] = tempindex[i]; if (pLine->v1 == pLine2->v2) glLinePortals[tempindex[i]].v1 = pLine2->v1; else glLinePortals[tempindex[i]].v2 = pLine2->v2; } } } } } while (gotsome); } } for (auto glport : glLinePortals) { glport.dx = glport.v2->fixX() - glport.v1->fixX(); glport.dy = glport.v2->fixY() - glport.v1->fixY(); } linePortalToGL.Resize(linePortals.Size()); for (unsigned i = 0; i < linePortals.Size(); i++) { linePortalToGL[i] = &glLinePortals[tempindex[i]]; /* Printf("portal at line %d translates to GL portal %d, range = %f,%f to %f,%f\n", int(linePortals[i].mOrigin - lines), tempindex[i], linePortalToGL[i]->v1->fixX() / 65536., linePortalToGL[i]->v1->fixY() / 65536., linePortalToGL[i]->v2->fixX() / 65536., linePortalToGL[i]->v2->fixY() / 65536.); */ } } CCMD(dumpportals) { for(unsigned i=0;ixDisplacement/65536.; double ydisp = portals[i]->yDisplacement/65536.; Printf(PRINT_LOG, "Portal #%d, %s, displacement = (%f,%f)\n", i, portals[i]->plane==0? "floor":"ceiling", xdisp, ydisp); Printf(PRINT_LOG, "Coverage:\n"); for(int j=0;jrender_sector->portals[portals[i]->plane]; if (port == portals[i]) { Printf(PRINT_LOG, "\tSubsector %d (%d):\n\t\t", j, sub->render_sector->sectornum); for(unsigned k = 0;k< sub->numlines; k++) { Printf(PRINT_LOG, "(%.3f,%.3f), ", sub->firstline[k].v1->fixX()/65536. + xdisp, sub->firstline[k].v1->fixY()/65536. + ydisp); } Printf(PRINT_LOG, "\n\t\tCovered by subsectors:\n"); FPortalCoverage *cov = &sub->portalcoverage[portals[i]->plane]; for(int l = 0;l< cov->sscount; l++) { subsector_t *csub = &subsectors[cov->subsectors[l]]; Printf(PRINT_LOG, "\t\t\t%5d (%4d): ", cov->subsectors[l], csub->render_sector->sectornum); for(unsigned m = 0;m< csub->numlines; m++) { Printf(PRINT_LOG, "(%.3f,%.3f), ", csub->firstline[m].v1->fixX()/65536., csub->firstline[m].v1->fixY()/65536.); } Printf(PRINT_LOG, "\n"); } } } } }