From 7dd5b508c89d3887e1d8fec8a13efd5946870276 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Thu, 18 Mar 2021 21:50:02 +0100 Subject: [PATCH] - flat rendering WIP. Still buggy triangulation and no texture alignment, but it renders something. --- source/CMakeLists.txt | 1 + source/core/gamefuncs.cpp | 21 + source/core/gamefuncs.h | 8 + .../core/rendering/scene/hw_bunchdrawer.cpp | 12 +- source/core/rendering/scene/hw_drawlist.cpp | 2 +- .../core/rendering/scene/hw_drawlistadd.cpp | 10 +- source/core/rendering/scene/hw_drawstructs.h | 30 +- source/core/rendering/scene/hw_flats.cpp | 401 ++++++++++++++++++ source/core/rendering/scene/hw_walls.cpp | 7 - 9 files changed, 461 insertions(+), 31 deletions(-) create mode 100644 source/core/rendering/scene/hw_flats.cpp diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index ee4a6bdcf..14278c015 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -1074,6 +1074,7 @@ set (PCH_SOURCES core/rendering/hw_entrypoint.cpp core/rendering/scene/hw_clipper.cpp core/rendering/scene/hw_walls.cpp + core/rendering/scene/hw_flats.cpp core/rendering/scene/hw_drawlistadd.cpp core/rendering/scene/hw_drawlist.cpp core/rendering/scene/hw_drawinfo.cpp diff --git a/source/core/gamefuncs.cpp b/source/core/gamefuncs.cpp index 42373dda7..92b8f534e 100644 --- a/source/core/gamefuncs.cpp +++ b/source/core/gamefuncs.cpp @@ -201,3 +201,24 @@ void PlanesAtPoint(usectorptr_t sec, float dax, float day, float* pceilz, float* if (pflorz) *pflorz = florz * -(1.f / 256.f); } +// variant that allows to pass precalculated info for the first line in. For cases where multiple points in a sector need to be checked. +void PlanesAtPoint(usectorptr_t sec, PlaneParam *pp, float dax, float day, float* pceilz, float* pflorz) +{ + float ceilz = float(sec->ceilingz); + float florz = float(sec->floorz); + + if (((sec->ceilingstat | sec->floorstat) & CSTAT_SECTOR_SLOPE) == CSTAT_SECTOR_SLOPE) + { + if (pp->length != 0) + { + auto wal = &wall[sec->wallptr]; + float const j = (pp->dx * (day - wal->y) - pp->dy * (dax - wal->x)) * (1.f / 8.f); + if (sec->ceilingstat & CSTAT_SECTOR_SLOPE) ceilz += (sec->ceilingheinum * j) / pp->length; + if (sec->floorstat & CSTAT_SECTOR_SLOPE) florz += (sec->floorheinum * j) / pp->length; + } + } + // Scale to render coordinates. + if (pceilz) *pceilz = ceilz * -(1.f / 256.f); + if (pflorz) *pflorz = florz * -(1.f / 256.f); +} + diff --git a/source/core/gamefuncs.h b/source/core/gamefuncs.h index 0f390ccda..f7ca8e8de 100644 --- a/source/core/gamefuncs.h +++ b/source/core/gamefuncs.h @@ -10,6 +10,14 @@ bool calcChaseCamPos(int* px, int* py, int* pz, spritetype* pspr, short *psectnu bool spriteIsModelOrVoxel(const spritetype* tspr); void PlanesAtPoint(usectorptr_t sec, float dax, float day, float* ceilz, float* florz); +struct PlaneParam +{ + float dx, dy; + int length; +}; + +void PlanesAtPoint(usectorptr_t sec, PlaneParam* pp, float dax, float day, float* ceilz, float* florz); + // y is negated so that the orientation is the same as in GZDoom, in order to use its utilities. // The render code should NOT use Build coordinates for anything! diff --git a/source/core/rendering/scene/hw_bunchdrawer.cpp b/source/core/rendering/scene/hw_bunchdrawer.cpp index 6c40c3217..792caeaa0 100644 --- a/source/core/rendering/scene/hw_bunchdrawer.cpp +++ b/source/core/rendering/scene/hw_bunchdrawer.cpp @@ -2,7 +2,7 @@ ** hw_bunchdrawer.cpp ** **--------------------------------------------------------------------------- -** Copyright 2008-2921 Christoph Oelckers +** Copyright 2008-2021 Christoph Oelckers ** All rights reserved. ** ** Redistribution and use in source and binary forms, with or without @@ -426,12 +426,10 @@ void BunchDrawer::ProcessSector(int sectnum) bool inbunch; angle_t startangle; - //if (sect->validcount == StartTime) return; - //sect->validcount = StartTime; - -#if 0//ndef BUILD_TEST - DoSector(sectnum, false); -#endif + SetupFlat.Clock(); + HWFlat flat; + flat.ProcessSector(di, §or[sectnum]); + SetupFlat.Unclock(); //Todo: process subsectors inbunch = false; diff --git a/source/core/rendering/scene/hw_drawlist.cpp b/source/core/rendering/scene/hw_drawlist.cpp index 7ac13ff16..207726b59 100644 --- a/source/core/rendering/scene/hw_drawlist.cpp +++ b/source/core/rendering/scene/hw_drawlist.cpp @@ -250,7 +250,7 @@ void HWDrawList::SortPlaneIntoPlane(SortNode * head,SortNode * sort) if (fh->z==fs->z) head->AddToEqual(sort); - else if ( (fh->zz && fh->ceiling) || (fh->z>fs->z && !fh->ceiling)) + else if ( (fh->zz && fh->plane) || (fh->z>fs->z && !fh->plane)) head->AddToLeft(sort); else head->AddToRight(sort); diff --git a/source/core/rendering/scene/hw_drawlistadd.cpp b/source/core/rendering/scene/hw_drawlistadd.cpp index 50473b49c..5f3e76bbe 100644 --- a/source/core/rendering/scene/hw_drawlistadd.cpp +++ b/source/core/rendering/scene/hw_drawlistadd.cpp @@ -108,21 +108,23 @@ void HWDrawInfo::AddMirrorSurface(HWWall *w) void HWDrawInfo::AddFlat(HWFlat *flat) { -#if 0 - int list; + int list = GLDL_PLAINFLATS; - if (flat->renderstyle != STYLE_Translucent || flat->alpha < 1.f - FLT_EPSILON || flat->texture == nullptr) +#if 0 + if (flat->renderstyle != STYLE_Translucent || flat->alpha < 1.f - FLT_EPSILON) { // translucent portals go into the translucent border list. list = GLDL_TRANSLUCENTBORDER; } else if (flat->texture->GetTranslucency()) { + /* if (flat->stack) { list = GLDL_TRANSLUCENTBORDER; } else + */ { list = GLDL_PLAINFLATS; } @@ -132,9 +134,9 @@ void HWDrawInfo::AddFlat(HWFlat *flat) bool masked = flat->texture->isMasked() && flat->stack; list = masked ? GLDL_MASKEDFLATS : GLDL_PLAINFLATS; } +#endif auto newflat = drawlists[list].NewFlat(); *newflat = *flat; -#endif } diff --git a/source/core/rendering/scene/hw_drawstructs.h b/source/core/rendering/scene/hw_drawstructs.h index 4bf17ce2c..f40ddbd81 100644 --- a/source/core/rendering/scene/hw_drawstructs.h +++ b/source/core/rendering/scene/hw_drawstructs.h @@ -8,6 +8,7 @@ #include "textures.h" #include "fcolormap.h" #include "build.h" +#include "gamefuncs.h" #ifdef _MSC_VER #pragma warning(disable:4244) @@ -252,36 +253,33 @@ public: class HWFlat { public: - sectortype * sector; + sectortype * sec; FGameTexture *texture; float z; // the z position of the flat (only valid for non-sloped planes) FColormap Colormap; // light and fog - PalEntry FlatColor; - PalEntry AddColor; ERenderStyle renderstyle; + PalEntry fade; + int shade, palette, visibility; float alpha; - HWSectorPlane plane; - int lightlevel; - bool stack; - bool ceiling; - uint8_t renderflags; - uint8_t hacktype; int iboindex; //int vboheight; + int plane; + int vertindex, vertcount; // this should later use a static vertex buffer, but that'd hinder the development phase, so for now vertex data gets created on the fly. + void MakeVertices(); + int dynlightindex; void CreateSkyboxVertices(FFlatVertex *buffer); //void SetupLights(HWDrawInfo *di, FLightNode *head, FDynLightData &lightdata, int portalgroup); - void PutFlat(HWDrawInfo *di, bool fog = false); - void Process(HWDrawInfo *di, sectortype * model, int whichplane, bool notexture); + void PutFlat(HWDrawInfo* di, int whichplane); void ProcessSector(HWDrawInfo *di, sectortype * frontsector, int which = 7 /*SSRF_RENDERALL*/); // cannot use constant due to circular dependencies. void DrawSubsectors(HWDrawInfo *di, FRenderState &state); - void DrawFlat(HWDrawInfo* di, FRenderState& state, bool translucent) {} + void DrawFlat(HWDrawInfo* di, FRenderState& state, bool translucent); }; //========================================================================== @@ -388,3 +386,11 @@ struct FDynLightData; struct FDynamicLight; bool GetLight(FDynLightData& dld, int group, Plane& p, FDynamicLight* light, bool checkside); void AddLightToList(FDynLightData &dld, int group, FDynamicLight* light, bool forceAttenuate); + +inline float sectorVisibility(sectortype* sec) +{ + // Beware of wraparound madness... + int v = sec->visibility; + return v ? ((uint8_t)(v + 16)) / 16.f : 1.f; +} + diff --git a/source/core/rendering/scene/hw_flats.cpp b/source/core/rendering/scene/hw_flats.cpp new file mode 100644 index 000000000..833207eaf --- /dev/null +++ b/source/core/rendering/scene/hw_flats.cpp @@ -0,0 +1,401 @@ +// +//--------------------------------------------------------------------------- +// +// 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 processing +** +*/ + +#include "matrix.h" +#include "hw_dynlightdata.h" +#include "hw_cvars.h" +#include "hw_clock.h" +#include "hw_material.h" +#include "hw_drawinfo.h" +#include "flatvertices.h" +#include "hw_lightbuffer.h" +#include "hw_drawstructs.h" +#include "hw_renderstate.h" +#include "texturemanager.h" +#include "earcut.hpp" + +#ifdef _DEBUG +CVAR(Int, gl_breaksec, -1, 0) +#endif + +extern PalEntry GlobalMapFog; +extern float GlobalFogDensity; + +//========================================================================== +// +// +// +//========================================================================== +#if 0 +void HWFlat::SetupLights(HWDrawInfo *di, FLightNode * node, FDynLightData &lightdata, int portalgroup) +{ + Plane p; + + lightdata.Clear(); + if (renderstyle == STYLE_Add && !di->Level->lightadditivesurfaces) + { + dynlightindex = -1; + return; // no lights on additively blended surfaces. + } + while (node) + { + FDynamicLight * light = node->lightsource; + + if (!light->IsActive()) + { + node = node->nextLight; + continue; + } + iter_dlightf++; + + // we must do the side check here because gl_GetLight needs the correct plane orientation + // which we don't have for Legacy-style 3D-floors + double planeh = plane.plane.ZatPoint(light->Pos); + if ((planehZ() && ceiling) || (planeh>light->Z() && !ceiling)) + { + node = node->nextLight; + continue; + } + + p.Set(plane.plane.Normal(), plane.plane.fD()); + draw_dlightf += GetLight(lightdata, portalgroup, p, light, false); + node = node->nextLight; + } + + dynlightindex = screen->mLights->UploadLights(lightdata); +} +#endif + +//========================================================================== +// +// CalcPlane fixme - this should be stored in the sector, not be recalculated each frame. +// +//========================================================================== + +static FVector3 CalcNormal(sectortype* sector, int plane) +{ + FVector3 pt[3]; + + auto wal = &wall[sector->wallptr]; + auto wal2 = &wall[wal->point2]; + + pt[0] = { (float)WallStartX(wal), (float)WallStartY(wal), 0 }; + pt[1] = { (float)WallEndX(wal), (float)WallEndY(wal), 0 }; + PlanesAtPoint(sector, wal->x, wal->y, plane ? &pt[0].Z : nullptr, plane? nullptr : &pt[0].Z); + PlanesAtPoint(sector, wal2->x, wal2->y, plane ? &pt[1].Z : nullptr, plane ? nullptr : &pt[1].Z); + + if (pt[0].X == pt[1].X) + { + if (pt[0].Y == pt[1].Y) return { 0.f, 0.f, plane ? -1.f : 1.f }; + pt[2].X = pt[0].X + 4; + pt[2].Y = pt[0].Y; + } + else + { + pt[2].X = pt[0].X; + pt[2].Y = pt[0].Y + 4; + } + PlanesAtPoint(sector, pt[2].X * 16, pt[2].Y * 16, plane ? &pt[2].Z : nullptr, plane ? nullptr : &pt[2].Z); + + auto normal = (pt[2] - pt[0]) ^ (pt[1] - pt[0]); + + if ((pt[2].Z < 0 && !plane) || (pt[2].Z > 0 && plane)) return -pt[2]; + return pt[2]; +} + + +//========================================================================== +// +// this should be buffered later. +// +//========================================================================== + +void HWFlat::MakeVertices() +{ + int numvertices = sec->wallnum; + + TArray points(numvertices, true); + using Point = std::pair; + std::vector> polygon; + std::vector* curPoly; + + polygon.resize(1); + curPoly = &polygon.back(); + + for (int i = 0; i < numvertices; i++) + { + auto wal = &wall[sec->wallptr + i]; + + float X = WallStartX(wal); + float Y = WallStartY(wal); + curPoly->push_back(std::make_pair(X, Y)); + if (wal->point2 != sec->wallptr+i+1 && i < numvertices - 1) + { + polygon.resize(polygon.size() + 1); + curPoly = &polygon.back(); + } + } + // Now make sure that the outer boundary is the first polygon by picking a point that's as much to the outside as possible. + int outer = 0; + float minx = FLT_MAX; + float miny = FLT_MAX; + for (size_t a = 0; a < polygon.size(); a++) + { + for (auto& pt : polygon[a]) + { + if (pt.first < minx || (pt.first == minx && pt.second < miny)) + { + minx = pt.first; + miny = pt.second; + outer = a; + } + } + } + if (outer != 0) std::swap(polygon[0], polygon[outer]); + auto indices = mapbox::earcut(polygon); + + int p = 0; + for (size_t a = 0; a < polygon.size(); a++) + { + for (auto& pt : polygon[a]) + { + float planez; + PlanesAtPoint(sec, (pt.first * 16), (pt.second * -16), plane ? &planez : nullptr, !plane ? &planez : nullptr); + FVector3 point = { pt.first, pt.second, planez }; + points[p++] = point; + } + } + + auto ret = screen->mVertexData->AllocVertices(indices.size()); + auto vp = ret.first; + for (auto i : indices) + { + auto& pt = points[indices[i]]; + vp->SetVertex(pt.X, pt.Z, pt.Y); + vp->SetTexCoord(pt.X / 64.f, pt.Y / 64.f); // todo: align + vp++; + } + vertindex = ret.second; + vertcount = indices.size(); +} + +//========================================================================== +// +// +// +//========================================================================== +void HWFlat::DrawFlat(HWDrawInfo *di, FRenderState &state, bool translucent) +{ + if (screen->BuffersArePersistent()) + { + MakeVertices(); + } + +#ifdef _DEBUG + if (sec - sector == gl_breaksec) + { + int a = 0; + } +#endif + + state.SetNormal(CalcNormal(sector, plane)); + + // Fog must be done before the texture so that the texture selector can override it. + bool foggy = (GlobalMapFog || (fade & 0xffffff)); + auto ShadeDiv = lookups.tables[palette].ShadeFactor; + // Disable brightmaps if non-black fog is used. + if (ShadeDiv >= 1 / 1000.f && foggy) + { + state.EnableFog(1); + float density = GlobalMapFog ? GlobalFogDensity : 350.f - Scale(numshades - shade, 150, numshades); + state.SetFog((GlobalMapFog) ? GlobalMapFog : fade, density); + state.SetSoftLightLevel(255); + state.SetLightParms(128.f, 1 / 1000.f); + } + else + { + state.EnableFog(0); + state.SetFog(0, 0); + state.SetSoftLightLevel(ShadeDiv >= 1 / 1000.f ? 255 - Scale(shade, 255, numshades) : 255); + state.SetLightParms(visibility, ShadeDiv / (numshades - 2)); + } + + // The shade rgb from the tint is ignored here. + state.SetColor(PalEntry(255, globalr, globalg, globalb)); + + if (translucent) + { + state.SetRenderStyle(renderstyle); + if (!texture->GetTranslucency()) state.AlphaFunc(Alpha_GEqual, gl_mask_threshold); + else state.AlphaFunc(Alpha_GEqual, 0.f); + } + state.SetMaterial(texture, UF_Texture, 0, CLAMP_NONE, 0, -1); + + state.SetLightIndex(dynlightindex); + state.Draw(DT_Triangles, vertindex, vertcount); + vertexcount += vertcount; + + state.EnableTextureMatrix(false); + + state.SetRenderStyle(DefaultRenderStyle()); + //state.SetObjectColor(0xffffffff); + //state.SetAddColor(0); + //state.ApplyTextureManipulation(nullptr); +} + +//========================================================================== +// +// HWFlat::PutFlat +// +// submit to the renderer +// +//========================================================================== + +void HWFlat::PutFlat(HWDrawInfo *di, int whichplane) +{ + if (!screen->BuffersArePersistent()) // should be made static buffer content later (when the logic is working) + { +#if 0 + if (di->Level->HasDynamicLights && texture != nullptr && !di->isFullbrightScene() && !(hacktype & (SSRF_PLANEHACK | SSRF_FLOODHACK))) + { + SetupLights(di, section->lighthead, lightdata, sector->PortalGroup); + } +#endif + MakeVertices(); + } + plane = whichplane; + di->AddFlat(this); + rendered_flats++; +} + +//========================================================================== +// +// Process a sector's flats for rendering +// This function is only called once per sector. +// Subsequent subsectors are just quickly added to the ss_renderflags array +// +//========================================================================== + +void HWFlat::ProcessSector(HWDrawInfo *di, sectortype * frontsector, int which) +{ +#ifdef _DEBUG + if (frontsector - sector == gl_breaksec) + { + int a = 0; + } +#endif + + dynlightindex = -1; + + const auto &vp = di->Viewpoint; + + float florz, ceilz; + PlanesAtPoint(frontsector, vp.Pos.X, vp.Pos.Y, &ceilz, &florz); + + fade = lookups.getFade(frontsector->floorpal); // fog is per sector. + visibility = sectorVisibility(frontsector); + sec = frontsector; + + // + // + // + // do floors + // + // + // + if ((which & SSRF_RENDERFLOOR) && !(frontsector->floorstat & CSTAT_SECTOR_SKY) && florz <= vp.Pos.Z) + { + // process the original floor first. + + shade = frontsector->floorshade; + palette = frontsector->floorpal; + + //port = frontsector->ValidatePortal(sector_t::floor); +#if 0 + if ((stack = (port != NULL))) + { + alpha = frontsector->GetAlpha(sector_t::floor); + } + else +#endif + alpha = 1.0f; + + if (alpha != 0.f) + { + int tilenum = frontsector->floorpicnum; + tileUpdatePicnum(&tilenum, tilenum, 0); + texture = tileGetTexture(tilenum); + if (texture && texture->isValid()) + { + //iboindex = frontsector->iboindex[sector_t::floor]; + renderstyle = STYLE_Translucent; + PutFlat(di, 0); + } + } + } + + // + // + // + // do ceilings + // + // + // + if ((which & SSRF_RENDERCEILING) && !(frontsector->ceilingstat & CSTAT_SECTOR_SKY) && ceilz >= vp.Pos.Z) + { + // process the original ceiling first. + + shade = frontsector->ceilingshade; + palette = frontsector->ceilingpal; + + + /* + port = frontsector->ValidatePortal(sector_t::ceiling); + if ((stack = (port != NULL))) + { + alpha = frontsector->GetAlpha(sector_t::ceiling); + } + else*/ + alpha = 1.0f; + + if (alpha != 0.f) + { + //iboindex = frontsector->iboindex[sector_t::ceiling]; + + int tilenum = frontsector->ceilingpicnum; + tileUpdatePicnum(&tilenum, tilenum, 0); + texture = tileGetTexture(tilenum); + if (texture && texture->isValid()) + { + //iboindex = frontsector->iboindex[sector_t::floor]; + renderstyle = STYLE_Translucent; + PutFlat(di, 1); + } + } + } +} + diff --git a/source/core/rendering/scene/hw_walls.cpp b/source/core/rendering/scene/hw_walls.cpp index bffd46d75..eb5837a33 100644 --- a/source/core/rendering/scene/hw_walls.cpp +++ b/source/core/rendering/scene/hw_walls.cpp @@ -626,13 +626,6 @@ bool HWWall::DoHorizon(HWDrawInfo* di, walltype* seg, sectortype* fs, DVector2& return true; } -static float sectorVisibility(sectortype *sec) -{ - // Beware of wraparound madness... - int v = sec->visibility; - return v ? ((uint8_t)(v + 16)) / 16.f : 1.f; -} - //========================================================================== // // Build math sucks. This would be easier if NPOT was handled properly.