- flat rendering WIP.

Still buggy triangulation and no texture alignment, but it renders something.
This commit is contained in:
Christoph Oelckers 2021-03-18 21:50:02 +01:00
parent 0579368626
commit 7dd5b508c8
9 changed files with 461 additions and 31 deletions

View file

@ -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

View file

@ -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);
}

View file

@ -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!

View file

@ -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, &sector[sectnum]);
SetupFlat.Unclock();
//Todo: process subsectors
inbunch = false;

View file

@ -250,7 +250,7 @@ void HWDrawList::SortPlaneIntoPlane(SortNode * head,SortNode * sort)
if (fh->z==fs->z)
head->AddToEqual(sort);
else if ( (fh->z<fs->z && fh->ceiling) || (fh->z>fs->z && !fh->ceiling))
else if ( (fh->z<fs->z && fh->plane) || (fh->z>fs->z && !fh->plane))
head->AddToLeft(sort);
else
head->AddToRight(sort);

View file

@ -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
}

View file

@ -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;
}

View file

@ -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 ((planeh<light->Z() && 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<FVector3> points(numvertices, true);
using Point = std::pair<float, float>;
std::vector<std::vector<Point>> polygon;
std::vector<Point>* 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);
}
}
}
}

View file

@ -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.