vkdoom_m/src/rendering/hwrenderer/scene/hw_flats.cpp
dileepvr 95b264bdb6
Isometric Mode and Robin (#2618)
* Feature-complete isometric mode fork.

* Dithered transparency condition tweaks.

* Dithered transparency for non-corpse monsters only (and missiles).

* SpectatorCamera vertical shift.

* Including math.h in hw_sprites.cpp to keep visual studio happy (it couldn't find M_SQRT2 definition).

* Defining MY_SQRT2 in hw_sprites.cpp to keep visual studio happy (it couldn't find M_SQRT2 definition).

* Defining MY_SQRT2 in r_utility.cpp also to keep visual studio happy.

* retrigger checks

* Have correct sprite angle-frame face the camera with orthographic projection enabled.

* Dithered Transparency now works properly on 3D floors. Moved that dither-trans flag setting code within hw_bsp.cpp to handle double-processing of linedefs. Added helper functions to FRenderViewpoint class 'bool IsOrtho()' and 'bool IsAllowedOoB()' to clean up checks everywhere in the code.

* Fixed indents. Added bbox property to subsector struct and use it instead of BSP nodes and Clippers (creating a bbox around viewpoint and checking for overlap) in orthographic mode when no fog of war is active. Turns out to be much faster. Though you need really big maps (Winter's Fury MAP01) to see a difference in fps.

* Non-linux checks don't like uint. Changed to unsigned int.

* Small change of a float to camera.zs. Ignore for testing. Should make no difference.

* Update actor.h to remain mergeable

RF2_NOMIPMAP was introduced, so I had to displace RF_ISOMETRICSPRITES to next bit.
2024-08-11 10:04:40 -04:00

775 lines
22 KiB
C++

//
//---------------------------------------------------------------------------
//
// 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 "a_sharedglobal.h"
#include "a_dynlight.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 "matrix.h"
#include "hw_dynlightdata.h"
#include "hw_cvars.h"
#include "hw_clock.h"
#include "hw_lighting.h"
#include "hw_material.h"
#include "hw_drawcontext.h"
#include "hwrenderer/scene/hw_drawinfo.h"
#include "flatvertices.h"
#include "hw_drawstructs.h"
#include "hw_renderstate.h"
#include "texturemanager.h"
#include "hw_flatdispatcher.h"
#ifdef _DEBUG
CVAR(Int, gl_breaksec, -1, 0)
#endif
//==========================================================================
//
// Sets the texture matrix according to the plane's texture positioning
// information
//
//==========================================================================
VSMatrix GetPlaneTextureRotationMatrix(FGameTexture* gltexture, const FVector2& uvOffset, float angle, const FVector2& scale)
{
float uoffs = uvOffset.X / gltexture->GetDisplayWidth();
float voffs = uvOffset.Y / gltexture->GetDisplayHeight();
float yscale1 = scale.Y;
if (gltexture->isHardwareCanvas())
{
yscale1 = 0 - yscale1;
}
float xscale2 = 64.f / gltexture->GetDisplayWidth();
float yscale2 = 64.f / gltexture->GetDisplayHeight();
VSMatrix mat;
mat.loadIdentity();
mat.scale(scale.X, yscale1, 1.0f);
mat.translate(uoffs, voffs, 0.0f);
mat.scale(xscale2, yscale2, 1.0f);
mat.rotate(angle, 0.0f, 0.0f, 1.0f);
return mat;
}
VSMatrix GetPlaneTextureRotationMatrix(FGameTexture* gltexture, const HWSectorPlane& secplane)
{
return GetPlaneTextureRotationMatrix(gltexture, secplane.Offs, -secplane.Angle, secplane.Scale);
}
VSMatrix GetPlaneTextureRotationMatrix(FGameTexture* gltexture, const sector_t* sector, int plane)
{
HWSectorPlane splane;
splane.GetFromSector(sector, plane);
return GetPlaneTextureRotationMatrix(gltexture, splane);
}
bool SetPlaneTextureRotation(FRenderState& state, HWSectorPlane* secplane, FGameTexture* gltexture)
{
// only manipulate the texture matrix if needed.
if (!secplane->Offs.isZero() ||
secplane->Scale.X != 1. || secplane->Scale.Y != 1 ||
secplane->Angle != 0 ||
gltexture->GetDisplayWidth() != 64 ||
gltexture->GetDisplayHeight() != 64)
{
state.SetTextureMatrix(GetPlaneTextureRotationMatrix(gltexture, *secplane));
return true;
}
return false;
}
//==========================================================================
//
// 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 HWFlat::CreateSkyboxVertices(FFlatVertex *vert)
{
float minx = FLT_MAX, miny = FLT_MAX;
float maxx = -FLT_MAX, maxy = -FLT_MAX;
for (auto ln : 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;
}
static float uvals[] = { 0, 0, 1, 1 };
static float vvals[] = { 1, 0, 0, 1 };
int rot = -xs_FloorToInt(plane.Angle / 90.f);
vert[0].Set(minx, z, miny, uvals[rot & 3], vvals[rot & 3]);
vert[1].Set(minx, z, maxy, uvals[(rot + 1) & 3], vvals[(rot + 1) & 3]);
vert[2].Set(maxx, z, miny, uvals[(rot + 3) & 3], vvals[(rot + 3) & 3]);
vert[3].Set(maxx, z, maxy, uvals[(rot + 2) & 3], vvals[(rot + 2) & 3]);
}
//==========================================================================
//
//
//
//==========================================================================
void HWFlat::SetupLights(HWFlatDispatcher *di, FRenderState& state, FLightNode * node, FDynLightData &lightdata, int portalgroup)
{
if (!di->di)
{
dynlightindex = -1;
return;
}
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() || light->DontLightMap())
{
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 = state.UploadLights(lightdata);
}
//==========================================================================
//
//
//
//==========================================================================
void HWFlat::DrawSubsectors(HWFlatDispatcher *di, FRenderState &state)
{
if (di->Level->HasDynamicLights && !di->isFullbrightScene())
{
SetupLights(di, state, section->lighthead, lightdata, sector->PortalGroup);
}
state.SetLightIndex(dynlightindex);
state.DrawIndexed(DT_Triangles, iboindex + section->vertexindex, section->vertexcount);
flatvertices += section->vertexcount;
flatprimitives++;
}
//==========================================================================
//
// Drawer for render hacks
//
//==========================================================================
void HWFlat::DrawOtherPlanes(HWDrawInfo *di, FRenderState &state)
{
state.SetMaterial(texture, UF_Texture, 0, CLAMP_NONE, NO_TRANSLATION, -1);
// Draw the subsectors assigned to it due to missing textures
auto pNode = (renderflags&SSRF_RENDERFLOOR) ?
di->otherFloorPlanes.CheckKey(sector->sectornum) : di->otherCeilingPlanes.CheckKey(sector->sectornum);
if (!pNode) return;
auto node = *pNode;
while (node)
{
state.SetLightIndex(node->lightindex);
auto num = node->sub->numlines;
flatvertices += num;
flatprimitives++;
state.Draw(DT_TriangleFan,node->vertexindex, num);
node = node->next;
}
}
//==========================================================================
//
// Drawer for render hacks
//
//==========================================================================
void HWFlat::DrawFloodPlanes(HWDrawInfo *di, FRenderState &state)
{
// Flood gaps with the back side's ceiling/floor texture
// This requires a stencil because the projected plane interferes with
// the depth buffer
state.SetMaterial(texture, UF_Texture, 0, CLAMP_NONE, NO_TRANSLATION, -1);
// Draw the subsectors assigned to it due to missing textures
auto pNode = (renderflags&SSRF_RENDERFLOOR) ?
di->floodFloorSegs.CheckKey(sector->sectornum) : di->floodCeilingSegs.CheckKey(sector->sectornum);
if (!pNode) return;
auto fnode = *pNode;
state.SetLightIndex(-1);
while (fnode)
{
flatvertices += 12;
flatprimitives += 3;
// Push bleeding floor/ceiling textures back a little in the z-buffer
// so they don't interfere with overlapping mid textures.
state.SetDepthBias(1, 128);
// Create stencil
state.SetEffect(EFF_STENCIL);
state.EnableTexture(false);
state.SetStencil(0, SOP_Increment, SF_ColorMaskOff);
state.Draw(DT_TriangleStrip, fnode->vertexindex, 4);
// Draw projected plane into stencil
state.EnableTexture(true);
state.SetEffect(EFF_NONE);
state.SetStencil(1, SOP_Keep, SF_DepthMaskOff);
state.EnableDepthTest(false);
state.Draw(DT_TriangleStrip, fnode->vertexindex + 4, 4);
// clear stencil
state.SetEffect(EFF_STENCIL);
state.EnableTexture(false);
state.SetStencil(1, SOP_Decrement, SF_ColorMaskOff | SF_DepthMaskOff);
state.Draw(DT_TriangleStrip, fnode->vertexindex, 4);
// restore old stencil op.
state.EnableTexture(true);
state.EnableDepthTest(true);
state.SetEffect(EFF_NONE);
state.SetDepthBias(0, 0);
state.SetStencil(0, SOP_Keep, SF_AllOn);
fnode = fnode->next;
}
}
//==========================================================================
//
//
//
//==========================================================================
void HWFlat::DrawFlat(HWFlatDispatcher *di, FRenderState &state, bool translucent)
{
#ifdef _DEBUG
if (sector->sectornum == gl_breaksec)
{
int a = 0;
}
#endif
int rel = getExtraLight();
state.SetNormal(plane.plane.Normal().X, plane.plane.Normal().Z, plane.plane.Normal().Y);
SetColor(state, di->Level, di->lightmode, lightlevel, rel, di->isFullbrightScene(), Colormap, alpha);
SetFog(state, di->Level, di->lightmode, lightlevel, rel, di->isFullbrightScene(), &Colormap, false, di->di ? di->di->drawctx->portalState.inskybox : false);
state.SetObjectColor(FlatColor | 0xff000000);
state.SetAddColor(AddColor | 0xff000000);
state.ApplyTextureManipulation(TextureFx);
if (plane.plane.dithertransflag) state.SetEffect(EFF_DITHERTRANS);
if (hacktype & SSRF_PLANEHACK)
{
if (di->di)
DrawOtherPlanes(di->di, state);
}
else if (hacktype & SSRF_FLOODHACK)
{
if (di->di)
DrawFloodPlanes(di->di, state);
}
else if (!translucent)
{
if (sector->special != GLSector_Skybox)
{
state.SetMaterial(texture, UF_Texture, 0, CLAMP_NONE, NO_TRANSLATION, -1);
bool texmatrix = SetPlaneTextureRotation(state, &plane, texture);
DrawSubsectors(di, state);
if (texmatrix)
state.SetTextureMatrix(VSMatrix::identity());
}
else if (!hacktype)
{
state.SetMaterial(texture, UF_Texture, 0, CLAMP_XY, NO_TRANSLATION, -1);
state.SetLightIndex(dynlightindex);
state.Draw(DT_TriangleStrip,iboindex, 4);
flatvertices += 4;
flatprimitives++;
}
}
else
{
state.SetRenderStyle(renderstyle);
if (!texture || !texture->isValid())
{
state.AlphaFunc(Alpha_GEqual, 0.f);
state.EnableTexture(false);
DrawSubsectors(di, state);
state.EnableTexture(true);
}
else
{
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, NO_TRANSLATION, -1);
bool texmatrix = SetPlaneTextureRotation(state, &plane, texture);
DrawSubsectors(di, state);
if (texmatrix)
state.SetTextureMatrix(VSMatrix::identity());
}
state.SetRenderStyle(DefaultRenderStyle());
}
state.SetObjectColor(0xffffffff);
state.SetAddColor(0);
state.ApplyTextureManipulation(nullptr);
if (plane.plane.dithertransflag) state.SetEffect(EFF_NONE);
}
//==========================================================================
//
// HWFlat::PutFlat
//
// submit to the renderer
//
//==========================================================================
inline void HWFlat::PutFlat(HWFlatDispatcher *di, bool fog)
{
if (di->isFullbrightScene())
{
Colormap.Clear();
}
di->AddFlat(this, fog);
}
//==========================================================================
//
// This draws one flat
// The whichplane boolean indicates if the flat is a floor(false) or a ceiling(true)
//
//==========================================================================
void HWFlat::Process(HWFlatDispatcher *di, FRenderState& state, sector_t * model, int whichplane, bool fog)
{
plane.GetFromSector(model, whichplane);
model->ceilingplane.dithertransflag = false; // Resetting this every frame
model->floorplane.dithertransflag = false; // Resetting this every frame
if (whichplane != int(ceiling))
{
// Flip the normal if the source plane has a different orientation than what we are about to render.
plane.plane.FlipVert();
}
if (!fog)
{
texture = TexMan.GetGameTexture(plane.texture, true);
if (!texture || !texture->isValid()) return;
if (texture->isFullbright())
{
Colormap.MakeWhite();
lightlevel=255;
}
}
else
{
texture = NULL;
lightlevel = abs(lightlevel);
}
z = plane.plane.ZatPoint(0.f, 0.f);
if (sector->special == GLSector_Skybox)
{
auto vert = state.AllocVertices(4);
CreateSkyboxVertices(vert.first);
iboindex = vert.second;
}
// For hacks this won't go into a render list.
PutFlat(di, fog);
rendered_flats++;
}
//==========================================================================
//
// Sets 3D floor info. Common code for all 4 cases
//
//==========================================================================
void HWFlat::SetFrom3DFloor(F3DFloor *rover, bool top, bool underside)
{
F3DFloor::planeref & plane = top? rover->top : rover->bottom;
// FF_FOG requires an inverted logic where to get the light from
lightlist_t *light = P_GetPlaneLight(sector, plane.plane, underside);
lightlevel = hw_ClampLight(*light->p_lightlevel);
if (rover->flags & FF_FOG)
{
Colormap.LightColor = light->extra_colormap.FadeColor;
FlatColor = 0xffffffff;
AddColor = 0;
TextureFx = nullptr;
}
else
{
CopyFrom3DLight(Colormap, light);
FlatColor = plane.model->SpecialColors[plane.isceiling];
AddColor = plane.model->AdditiveColors[plane.isceiling];
TextureFx = &plane.model->planes[plane.isceiling].TextureFx;
}
alpha = rover->alpha/255.0f;
renderstyle = rover->flags&FF_ADDITIVETRANS? STYLE_Add : STYLE_Translucent;
iboindex = plane.vindex;
controlsector = rover;
}
//==========================================================================
//
// 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(HWFlatDispatcher *di, FRenderState& state, sector_t * frontsector, int which)
{
lightlist_t * light;
FSectorPortal *port;
#ifdef _DEBUG
if (frontsector->sectornum == gl_breaksec)
{
int a = 0;
}
#endif
controlsector = nullptr;
// Get the real sector for this one.
sector = &di->Level->sectors[frontsector->sectornum];
extsector_t::xfloor &x = sector->e->XFloor;
dynlightindex = -1;
hacktype = (which & (SSRF_PLANEHACK|SSRF_FLOODHACK));
uint8_t sink;
uint8_t &srf = hacktype? sink : (di->di ? di->di->section_renderflags[di->Level->sections.SectionIndex(section)] : di->mh->section_renderflags);
FRenderViewpoint* vp = di->di ? &di->di->Viewpoint : nullptr;
//
// Lightmaps
//
if (di->di && level.lightmaps)
{
for (int i = 0, count = sector->subsectorcount; i < count; ++i)
{
for (int plane = 0; plane < 2; ++plane)
{
unsigned int count = sector->e->XFloor.ffloors.Size() + 1;
for (unsigned int j = 0; j < count; j++)
{
if (int tile = sector->subsectors[i]->LightmapTiles[plane].Size() > j ? sector->subsectors[i]->LightmapTiles[plane][j] : -1)
{
di->di->PushVisibleTile(tile);
}
}
}
}
}
//
//
//
// do floors
//
//
//
if (((which & SSRF_RENDERFLOOR) && (!di->di || (((frontsector->floorplane.ZatPoint(vp->Pos) <= vp->Pos.Z) && (!section || !(section->flags & FSection::DONTRENDERFLOOR)))))) && !(di->di && vp->IsOrtho() && (vp->PitchSin < 0.0)))
{
// process the original floor first.
srf |= SSRF_RENDERFLOOR;
lightlevel = hw_ClampLight(frontsector->GetFloorLight());
Colormap = frontsector->Colormap;
FlatColor = frontsector->SpecialColors[sector_t::floor];
AddColor = frontsector->AdditiveColors[sector_t::floor];
TextureFx = &frontsector->planes[sector_t::floor].TextureFx;
port = frontsector->ValidatePortal(sector_t::floor);
if ((stack = (port != NULL)))
{
/* to be redone in a less invasive manner
if (port->mType == PORTS_STACKEDSECTORTHING)
{
di->AddFloorStack(sector); // stacked sector things require visplane merging.
}
*/
alpha = frontsector->GetAlpha(sector_t::floor);
}
else
{
alpha = 1.0f - frontsector->GetReflect(sector_t::floor);
}
if ((di->di && alpha != 0.f && frontsector->GetTexture(sector_t::floor) != skyflatnum) || (!di->di && (alpha != 0.f || stack)))
{
iboindex = frontsector->iboindex[sector_t::floor];
ceiling = false;
renderflags = SSRF_RENDERFLOOR;
if (x.ffloors.Size())
{
light = P_GetPlaneLight(sector, &frontsector->floorplane, false);
if ((!(sector->GetFlags(sector_t::floor)&PLANEF_ABSLIGHTING) || light->lightsource == NULL)
&& (light->p_lightlevel != &frontsector->lightlevel))
{
lightlevel = hw_ClampLight(*light->p_lightlevel);
}
CopyFrom3DLight(Colormap, light);
}
renderstyle = STYLE_Translucent;
Process(di, state, frontsector, sector_t::floor, false);
}
}
//
//
//
// do ceilings
//
//
//
if (((which & SSRF_RENDERCEILING) && ((!di->di || ((frontsector->ceilingplane.ZatPoint(vp->Pos) >= vp->Pos.Z) && (!section || !(section->flags & FSection::DONTRENDERCEILING)))))) && !(di->di && vp->IsOrtho() && (vp->PitchSin > 0.0)))
{
// process the original ceiling first.
srf |= SSRF_RENDERCEILING;
lightlevel = hw_ClampLight(frontsector->GetCeilingLight());
Colormap = frontsector->Colormap;
FlatColor = frontsector->SpecialColors[sector_t::ceiling];
AddColor = frontsector->AdditiveColors[sector_t::ceiling];
TextureFx = &frontsector->planes[sector_t::ceiling].TextureFx;
port = frontsector->ValidatePortal(sector_t::ceiling);
if ((stack = (port != NULL)))
{
/* as above for floors
if (port->mType == PORTS_STACKEDSECTORTHING)
{
di->AddCeilingStack(sector);
}
*/
alpha = frontsector->GetAlpha(sector_t::ceiling);
}
else
{
alpha = 1.0f - frontsector->GetReflect(sector_t::ceiling);
}
if ((di->di && alpha != 0.f && frontsector->GetTexture(sector_t::ceiling) != skyflatnum) || (!di->di && (alpha != 0.f || stack)))
{
iboindex = frontsector->iboindex[sector_t::ceiling];
ceiling = true;
renderflags = SSRF_RENDERCEILING;
if (x.ffloors.Size())
{
light = P_GetPlaneLight(sector, &sector->ceilingplane, true);
if ((!(sector->GetFlags(sector_t::ceiling)&PLANEF_ABSLIGHTING))
&& (light->p_lightlevel != &frontsector->lightlevel))
{
lightlevel = hw_ClampLight(*light->p_lightlevel);
}
CopyFrom3DLight(Colormap, light);
}
renderstyle = STYLE_Translucent;
Process(di, state, frontsector, sector_t::ceiling, false);
}
}
//
//
//
// do 3D floors
//
//
//
stack = false;
if ((which & SSRF_RENDER3DPLANES) && x.ffloors.Size())
{
renderflags = SSRF_RENDER3DPLANES;
srf |= SSRF_RENDER3DPLANES;
// 3d-floors must not overlap!
double lastceilingheight = sector->CenterCeiling(); // render only in the range of the
double lastfloorheight = sector->CenterFloor(); // current sector part (if applicable)
F3DFloor * rover;
int k;
// floors are ordered now top to bottom so scanning the list for the best match
// is no longer necessary.
ceiling = true;
Colormap = frontsector->Colormap;
for (k = 0; k < (int)x.ffloors.Size(); k++)
{
rover = x.ffloors[k];
if ((rover->flags&(FF_EXISTS | FF_RENDERPLANES | FF_THISINSIDE)) == (FF_EXISTS | FF_RENDERPLANES))
{
if (rover->flags&FF_FOG && di->isFullbrightScene()) continue;
if (!rover->top.copied && rover->flags&(FF_INVERTPLANES | FF_BOTHPLANES))
{
double ff_top = rover->top.plane->ZatPoint(sector->centerspot);
if (ff_top < lastceilingheight)
{
if (!di->di || vp->Pos.Z <= rover->top.plane->ZatPoint(vp->Pos))
{
SetFrom3DFloor(rover, true, !!(rover->flags&FF_FOG));
Colormap.FadeColor = frontsector->Colormap.FadeColor;
Process(di, state, rover->top.model, rover->top.isceiling, !!(rover->flags&FF_FOG));
}
lastceilingheight = ff_top;
}
}
if (!rover->bottom.copied && !(rover->flags&FF_INVERTPLANES))
{
double ff_bottom = rover->bottom.plane->ZatPoint(sector->centerspot);
if (ff_bottom < lastceilingheight)
{
if (!di->di || vp->Pos.Z <= rover->bottom.plane->ZatPoint(vp->Pos))
{
SetFrom3DFloor(rover, false, !(rover->flags&FF_FOG));
Colormap.FadeColor = frontsector->Colormap.FadeColor;
Process(di, state, rover->bottom.model, rover->bottom.isceiling, !!(rover->flags&FF_FOG));
}
lastceilingheight = ff_bottom;
if (rover->alpha < 255) lastceilingheight += EQUAL_EPSILON;
}
}
}
}
ceiling = false;
for (k = x.ffloors.Size() - 1; k >= 0; k--)
{
rover = x.ffloors[k];
if ((rover->flags&(FF_EXISTS | FF_RENDERPLANES | FF_THISINSIDE)) == (FF_EXISTS | FF_RENDERPLANES))
{
if (rover->flags&FF_FOG && di->isFullbrightScene()) continue;
if (!rover->bottom.copied && rover->flags&(FF_INVERTPLANES | FF_BOTHPLANES))
{
double ff_bottom = rover->bottom.plane->ZatPoint(sector->centerspot);
if (ff_bottom > lastfloorheight || (rover->flags&FF_FIX))
{
if (!di->di || vp->Pos.Z >= rover->bottom.plane->ZatPoint(vp->Pos))
{
SetFrom3DFloor(rover, false, !(rover->flags&FF_FOG));
Colormap.FadeColor = frontsector->Colormap.FadeColor;
if (rover->flags&FF_FIX)
{
lightlevel = hw_ClampLight(rover->model->lightlevel);
Colormap = rover->GetColormap();
}
Process(di, state, rover->bottom.model, rover->bottom.isceiling, !!(rover->flags&FF_FOG));
}
lastfloorheight = ff_bottom;
}
}
if (!rover->top.copied && !(rover->flags&FF_INVERTPLANES))
{
double ff_top = rover->top.plane->ZatPoint(sector->centerspot);
if (ff_top > lastfloorheight)
{
if (!di->di || vp->Pos.Z >= rover->top.plane->ZatPoint(vp->Pos))
{
SetFrom3DFloor(rover, true, !!(rover->flags&FF_FOG));
Colormap.FadeColor = frontsector->Colormap.FadeColor;
Process(di, state, rover->top.model, rover->top.isceiling, !!(rover->flags&FF_FOG));
}
lastfloorheight = ff_top;
if (rover->alpha < 255) lastfloorheight -= EQUAL_EPSILON;
}
}
}
}
}
}