mirror of
https://github.com/ZDoom/raze-gles.git
synced 2025-01-03 23:10:48 +00:00
47dce8de8c
y-panning is still not correct for all cases but better than before. The aspect factor is not needed, it looks like an artifact of both the software renderer and Polymost. We get the same dimensions with proper matrices by setting this to 1.
1098 lines
30 KiB
C++
1098 lines
30 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/
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
|
|
#include "texturemanager.h"
|
|
#include "hw_dynlightdata.h"
|
|
#include "hw_material.h"
|
|
#include "hw_cvars.h"
|
|
#include "hw_clock.h"
|
|
//#include "hw_lighting.h"
|
|
//#include "hwrenderer/scene/hw_drawinfo.h"
|
|
//#include "hwrenderer/scene/hw_drawstructs.h"
|
|
//#include "hwrenderer/scene/hw_portal.h"
|
|
#include "hw_lightbuffer.h"
|
|
#include "hw_renderstate.h"
|
|
#include "hw_skydome.h"
|
|
#include "hw_drawstructs.h"
|
|
#include "render.h"
|
|
#include "cmdlib.h"
|
|
|
|
#include "v_video.h"
|
|
#include "flatvertices.h"
|
|
#include "glbackend/glbackend.h"
|
|
|
|
|
|
//==========================================================================
|
|
//
|
|
// General purpose wall rendering function
|
|
// everything goes through here
|
|
//
|
|
//==========================================================================
|
|
|
|
void HWWall::RenderWall(HWDrawInfo *di, FRenderState &state, int textured)
|
|
{
|
|
assert(vertcount > 0);
|
|
state.SetLightIndex(dynlightindex);
|
|
state.Draw(DT_TriangleFan, vertindex, vertcount);
|
|
vertexcount += vertcount;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================
|
|
|
|
void HWWall::RenderFogBoundary(HWDrawInfo *di, FRenderState &state)
|
|
{
|
|
#if 0
|
|
if (gl_fogmode && !di->isFullbrightScene())
|
|
{
|
|
int rel = rellight + getExtraLight();
|
|
state.EnableDrawBufferAttachments(false);
|
|
di->SetFog(state, lightlevel, rel, false, &Colormap, false);
|
|
state.SetEffect(EFF_FOGBOUNDARY);
|
|
state.AlphaFunc(Alpha_GEqual, 0.f);
|
|
state.SetDepthBias(-1, -128);
|
|
RenderWall(di, state, HWWall::RWF_BLANK);
|
|
state.ClearDepthBias();
|
|
state.SetEffect(EFF_NONE);
|
|
state.EnableDrawBufferAttachments(true);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
//==========================================================================
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================
|
|
void HWWall::RenderMirrorSurface(HWDrawInfo *di, FRenderState &state)
|
|
{
|
|
#if 0
|
|
if (!TexMan.mirrorTexture.isValid()) return;
|
|
|
|
state.SetDepthFunc(DF_LEqual);
|
|
|
|
// we use texture coordinates and texture matrix to pass the normal stuff to the shader so that the default vertex buffer format can be used as is.
|
|
state.EnableTextureMatrix(true);
|
|
|
|
// Use sphere mapping for this
|
|
state.SetEffect(EFF_SPHEREMAP);
|
|
di->SetColor(state, lightlevel, 0, di->isFullbrightScene(), Colormap, 0.1f);
|
|
di->SetFog(state, lightlevel, 0, di->isFullbrightScene(), &Colormap, true);
|
|
state.SetRenderStyle(STYLE_Add);
|
|
state.AlphaFunc(Alpha_Greater, 0);
|
|
|
|
auto tex = TexMan.GetGameTexture(TexMan.mirrorTexture, false);
|
|
state.SetMaterial(tex, UF_None, 0, CLAMP_NONE, 0, -1); // do not upscale the mirror texture.
|
|
|
|
flags &= ~HWWall::HWF_GLOW;
|
|
RenderWall(di, state, HWWall::RWF_BLANK);
|
|
|
|
state.EnableTextureMatrix(false);
|
|
state.SetEffect(EFF_NONE);
|
|
state.AlphaFunc(Alpha_GEqual, gl_mask_sprite_threshold);
|
|
|
|
state.SetDepthFunc(DF_Less);
|
|
|
|
// This is drawn in the translucent pass which is done after the decal pass
|
|
// As a result the decals have to be drawn here, right after the wall they are on,
|
|
// because the depth buffer won't get set by translucent items.
|
|
if (seg->sidedef->AttachedDecals)
|
|
{
|
|
DrawDecalsForMirror(di, state, di->Decals[1]);
|
|
}
|
|
state.SetRenderStyle(STYLE_Translucent);
|
|
#endif
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================
|
|
|
|
void HWWall::RenderTexturedWall(HWDrawInfo *di, FRenderState &state, int rflags)
|
|
{
|
|
#if 0
|
|
int tmode = state.GetTextureMode();
|
|
|
|
state.SetMaterial(texture, UF_Texture, 0, flags & 3, 0, -1);
|
|
|
|
|
|
int32_t size = xs_CRoundToInt(texture->GetDisplayHeight());
|
|
int32_t size2;
|
|
for (size2 = 1; size2 < size; size2 += size2) {}
|
|
if (size == size2)
|
|
state.SetNpotEmulation(0.f, 0.f);
|
|
else
|
|
{
|
|
float xOffset = 1.f / texture->GetDisplayWidth();
|
|
state.SetNpotEmulation((1.f * size2) / size, xOffset);
|
|
}
|
|
|
|
|
|
float absalpha = fabsf(alpha);
|
|
if (type != RENDERWALL_M2SNF) di->SetFog(state, lightlevel, rel, di->isFullbrightScene(), &Colormap, RenderStyle == STYLE_Add);
|
|
di->SetColor(state, lightlevel, rel, di->isFullbrightScene(), Colormap, absalpha);
|
|
RenderWall(di, state, rflags);
|
|
|
|
state.SetNpotEmulation(0.f, 0.f);
|
|
state.SetObjectColor(0xffffffff);
|
|
state.SetObjectColor2(0);
|
|
state.SetAddColor(0);
|
|
state.SetTextureMode(tmode);
|
|
state.EnableGlow(false);
|
|
state.EnableGradient(false);
|
|
state.ApplyTextureManipulation(nullptr);
|
|
#endif
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================
|
|
|
|
void HWWall::RenderTranslucentWall(HWDrawInfo *di, FRenderState &state)
|
|
{
|
|
#if 0
|
|
state.SetRenderStyle(RenderStyle);
|
|
if (!texture->GetTranslucency()) state.AlphaFunc(Alpha_GEqual, gl_mask_threshold);
|
|
else state.AlphaFunc(Alpha_GEqual, 0.f);
|
|
RenderTexturedWall(di, state, HWWall::RWF_TEXTURED);
|
|
state.SetRenderStyle(STYLE_Translucent);
|
|
#endif
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================
|
|
void HWWall::DrawWall(HWDrawInfo *di, FRenderState &state, bool translucent)
|
|
{
|
|
/*
|
|
if (screen->BuffersArePersistent())
|
|
{
|
|
if (di->Level->HasDynamicLights && !di->isFullbrightScene() && texture != nullptr)
|
|
{
|
|
SetupLights(di, lightdata);
|
|
}
|
|
MakeVertices(di, !!(flags & HWWall::HWF_TRANSLUCENT));
|
|
}
|
|
*/
|
|
|
|
state.SetNormal(glseg.Normal());
|
|
if (!translucent)
|
|
{
|
|
RenderTexturedWall(di, state, HWWall::RWF_TEXTURED);
|
|
}
|
|
else
|
|
{
|
|
switch (type)
|
|
{
|
|
case RENDERWALL_MIRRORSURFACE:
|
|
RenderMirrorSurface(di, state);
|
|
break;
|
|
|
|
case RENDERWALL_FOGBOUNDARY:
|
|
RenderFogBoundary(di, state);
|
|
break;
|
|
|
|
default:
|
|
RenderTranslucentWall(di, state);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// Collect lights for shader
|
|
//
|
|
//==========================================================================
|
|
#if 0
|
|
void HWWall::SetupLights(HWDrawInfo *di, FDynLightData &lightdata)
|
|
{
|
|
lightdata.Clear();
|
|
|
|
if (RenderStyle == STYLE_Add && !di->Level->lightadditivesurfaces) return; // no lights on additively blended surfaces.
|
|
|
|
// check for wall types which cannot have dynamic lights on them (portal types never get here so they don't need to be checked.)
|
|
switch (type)
|
|
{
|
|
case RENDERWALL_FOGBOUNDARY:
|
|
case RENDERWALL_MIRRORSURFACE:
|
|
case RENDERWALL_COLOR:
|
|
return;
|
|
}
|
|
|
|
float vtx[]={glseg.x1,zbottom[0],glseg.y1, glseg.x1,ztop[0],glseg.y1, glseg.x2,ztop[1],glseg.y2, glseg.x2,zbottom[1],glseg.y2};
|
|
Plane p;
|
|
|
|
|
|
auto normal = glseg.Normal();
|
|
p.Set(normal, -normal.X * glseg.x1 - normal.Z * glseg.y1);
|
|
|
|
FLightNode *node;
|
|
if (seg->sidedef == NULL)
|
|
{
|
|
node = NULL;
|
|
}
|
|
else if (!(seg->sidedef->Flags & WALLF_POLYOBJ))
|
|
{
|
|
node = seg->sidedef->lighthead;
|
|
}
|
|
else if (sub)
|
|
{
|
|
// Polobject segs cannot be checked per sidedef so use the subsector instead.
|
|
node = sub->section->lighthead;
|
|
}
|
|
else node = NULL;
|
|
|
|
// Iterate through all dynamic lights which touch this wall and render them
|
|
while (node)
|
|
{
|
|
if (node->lightsource->IsActive())
|
|
{
|
|
iter_dlight++;
|
|
|
|
DVector3 posrel = node->lightsource->PosRelative(seg->frontsector->PortalGroup);
|
|
float x = posrel.X;
|
|
float y = posrel.Y;
|
|
float z = posrel.Z;
|
|
float dist = fabsf(p.DistToPoint(x, z, y));
|
|
float radius = node->lightsource->GetRadius();
|
|
float scale = 1.0f / ((2.f * radius) - dist);
|
|
FVector3 fn, pos;
|
|
|
|
if (radius > 0.f && dist < radius)
|
|
{
|
|
FVector3 nearPt, up, right;
|
|
|
|
pos = { x, z, y };
|
|
fn = p.Normal();
|
|
|
|
fn.GetRightUp(right, up);
|
|
|
|
FVector3 tmpVec = fn * dist;
|
|
nearPt = pos + tmpVec;
|
|
|
|
FVector3 t1;
|
|
int outcnt[4]={0,0,0,0};
|
|
texcoord tcs[4];
|
|
|
|
// do a quick check whether the light touches this polygon
|
|
for(int i=0;i<4;i++)
|
|
{
|
|
t1 = FVector3(&vtx[i*3]);
|
|
FVector3 nearToVert = t1 - nearPt;
|
|
tcs[i].u = ((nearToVert | right) * scale) + 0.5f;
|
|
tcs[i].v = ((nearToVert | up) * scale) + 0.5f;
|
|
|
|
if (tcs[i].u<0) outcnt[0]++;
|
|
if (tcs[i].u>1) outcnt[1]++;
|
|
if (tcs[i].v<0) outcnt[2]++;
|
|
if (tcs[i].v>1) outcnt[3]++;
|
|
|
|
}
|
|
if (outcnt[0]!=4 && outcnt[1]!=4 && outcnt[2]!=4 && outcnt[3]!=4)
|
|
{
|
|
draw_dlight += GetLight(lightdata, seg->frontsector->PortalGroup, p, node->lightsource, true);
|
|
}
|
|
}
|
|
}
|
|
node = node->nextLight;
|
|
}
|
|
dynlightindex = screen->mLights->UploadLights(lightdata);
|
|
}
|
|
#endif
|
|
|
|
//==========================================================================
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================
|
|
void HWWall::PutWall(HWDrawInfo *di, bool translucent)
|
|
{
|
|
if (translucent || (texture && texture->GetTranslucency() && type == RENDERWALL_M2S))
|
|
{
|
|
flags |= HWF_TRANSLUCENT;
|
|
//ViewDistance = (di->Viewpoint.Pos - (seg->linedef->v1->fPos() + seg->linedef->Delta() / 2)).XY().LengthSquared();
|
|
}
|
|
|
|
/*
|
|
if (!screen->BuffersArePersistent())
|
|
{
|
|
if (di->Level->HasDynamicLights && !di->isFullbrightScene() && texture != nullptr)
|
|
{
|
|
SetupLights(di, lightdata);
|
|
}
|
|
MakeVertices(di, translucent);
|
|
}
|
|
|
|
bool hasDecals = type != RENDERWALL_M2S && seg->sidedef->AttachedDecals;
|
|
if (hasDecals)
|
|
{
|
|
// If we want to use the light infos for the decal we cannot delay the creation until the render pass.
|
|
if (screen->BuffersArePersistent())
|
|
{
|
|
if (di->Level->HasDynamicLights && !di->isFullbrightScene() && texture != nullptr)
|
|
{
|
|
SetupLights(di, lightdata);
|
|
}
|
|
}
|
|
ProcessDecals(di);
|
|
}
|
|
*/
|
|
|
|
#if 0
|
|
di->AddWall(this);
|
|
#else
|
|
// route it through the GLInterface for now for easier testing
|
|
int palid = TRANSLATION(Translation_Remap + curbasepal, globalpal);
|
|
GLInterface.SetFade(globalfloorpal);
|
|
GLInterface.SetTexture(texture, TRANSLATION(Translation_Remap + curbasepal, palette), 0);
|
|
|
|
GLInterface.SetShade(shade, numshades);
|
|
|
|
int h = texture->GetTexelHeight();
|
|
int h2 = 1 << sizeToBits(h);
|
|
if (h != h2)
|
|
{
|
|
float xOffset = 1.f / texture->GetTexelWidth();
|
|
GLInterface.SetNpotEmulation(float(h2) / h, xOffset);
|
|
}
|
|
else
|
|
{
|
|
GLInterface.SetNpotEmulation(0.f, 0.f);
|
|
}
|
|
GLInterface.SetTextureMode(TM_OPAQUE);
|
|
GLInterface.SetVisibility(visibility);
|
|
|
|
// The shade rgb from the tint is ignored here.
|
|
float pc[4];
|
|
pc[0] = (float)globalr * (1.f / 255.f);
|
|
pc[1] = (float)globalg * (1.f / 255.f);
|
|
pc[2] = (float)globalb * (1.f / 255.f);
|
|
pc[3] = alpha;
|
|
GLInterface.SetColor(pc[0], pc[1], pc[2], pc[3]);
|
|
|
|
auto data = screen->mVertexData->AllocVertices(4);
|
|
auto vt = data.first;
|
|
vt[0].SetTexCoord(tcs[LOLFT].u, tcs[LOLFT].v);
|
|
vt[0].SetVertex(glseg.x1, zbottom[0], glseg.y1);
|
|
vt[1].SetTexCoord(tcs[UPLFT].u, tcs[UPLFT].v);
|
|
vt[1].SetVertex(glseg.x1, ztop[0], glseg.y1);
|
|
vt[2].SetTexCoord(tcs[UPRGT].u, tcs[UPRGT].v);
|
|
vt[2].SetVertex(glseg.x2, ztop[1], glseg.y2);
|
|
vt[3].SetTexCoord(tcs[LORGT].u, tcs[LORGT].v);
|
|
vt[3].SetVertex(glseg.x2, zbottom[1], glseg.y2);
|
|
|
|
GLInterface.Draw(DT_TriangleFan, data.second, 4);
|
|
|
|
GLInterface.SetNpotEmulation(0.f, 0.f);
|
|
GLInterface.SetTextureMode(TM_NORMAL);
|
|
|
|
#endif
|
|
// make sure that following parts of the same linedef do not get this one's vertex and lighting info.
|
|
vertcount = 0;
|
|
dynlightindex = -1;
|
|
flags &= ~HWF_TRANSLUCENT;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// will be done later.
|
|
//
|
|
//==========================================================================
|
|
|
|
void HWWall::PutPortal(HWDrawInfo *di, int ptype, int plane)
|
|
{
|
|
#if 0
|
|
HWPortal * portal = nullptr;
|
|
|
|
MakeVertices(di, false);
|
|
switch (ptype)
|
|
{
|
|
// portals don't go into the draw list.
|
|
// Instead they are added to the portal manager
|
|
case PORTALTYPE_HORIZON:
|
|
horizon = portalState.UniqueHorizons.Get(horizon);
|
|
portal = di->FindPortal(horizon);
|
|
if (!portal)
|
|
{
|
|
portal = new HWHorizonPortal(&portalState, horizon, di->Viewpoint);
|
|
di->Portals.Push(portal);
|
|
}
|
|
portal->AddLine(this);
|
|
break;
|
|
|
|
case PORTALTYPE_SKYBOX:
|
|
portal = di->FindPortal(secportal);
|
|
if (!portal)
|
|
{
|
|
// either a regular skybox or an Eternity-style horizon
|
|
if (secportal->mType != PORTS_SKYVIEWPOINT) portal = new HWEEHorizonPortal(&portalState, secportal);
|
|
else
|
|
{
|
|
portal = new HWSkyboxPortal(&portalState, secportal);
|
|
di->Portals.Push(portal);
|
|
}
|
|
}
|
|
portal->AddLine(this);
|
|
break;
|
|
|
|
case PORTALTYPE_SECTORSTACK:
|
|
portal = di->FindPortal(this->portal);
|
|
if (!portal)
|
|
{
|
|
portal = new HWSectorStackPortal(&portalState, this->portal);
|
|
di->Portals.Push(portal);
|
|
}
|
|
portal->AddLine(this);
|
|
break;
|
|
|
|
case PORTALTYPE_PLANEMIRROR:
|
|
if (portalState.PlaneMirrorMode * planemirror->fC() <= 0)
|
|
{
|
|
planemirror = portalState.UniquePlaneMirrors.Get(planemirror);
|
|
portal = di->FindPortal(planemirror);
|
|
if (!portal)
|
|
{
|
|
portal = new HWPlaneMirrorPortal(&portalState, planemirror);
|
|
di->Portals.Push(portal);
|
|
}
|
|
portal->AddLine(this);
|
|
}
|
|
break;
|
|
|
|
case PORTALTYPE_MIRROR:
|
|
portal = di->FindPortal(seg->linedef);
|
|
if (!portal)
|
|
{
|
|
portal = new HWMirrorPortal(&portalState, seg->linedef);
|
|
di->Portals.Push(portal);
|
|
}
|
|
portal->AddLine(this);
|
|
if (gl_mirror_envmap)
|
|
{
|
|
// draw a reflective layer over the mirror
|
|
di->AddMirrorSurface(this);
|
|
}
|
|
break;
|
|
|
|
case PORTALTYPE_LINETOLINE:
|
|
if (!lineportal)
|
|
return;
|
|
portal = di->FindPortal(lineportal);
|
|
if (!portal)
|
|
{
|
|
line_t *otherside = lineportal->lines[0]->mDestination;
|
|
if (otherside != nullptr && otherside->portalindex < di->Level->linePortals.Size())
|
|
{
|
|
di->ProcessActorsInPortal(otherside->getPortal()->mGroup, di->in_area);
|
|
}
|
|
portal = new HWLineToLinePortal(&portalState, lineportal);
|
|
di->Portals.Push(portal);
|
|
}
|
|
portal->AddLine(this);
|
|
break;
|
|
|
|
case PORTALTYPE_SKY:
|
|
sky = portalState.UniqueSkies.Get(sky);
|
|
portal = di->FindPortal(sky);
|
|
if (!portal)
|
|
{
|
|
portal = new HWSkyPortal(screen->mSkyData, &portalState, sky);
|
|
di->Portals.Push(portal);
|
|
}
|
|
portal->AddLine(this);
|
|
break;
|
|
}
|
|
vertcount = 0;
|
|
|
|
if (plane != -1 && portal)
|
|
{
|
|
portal->planesused |= (1<<plane);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// Build does not have horizon effects.
|
|
//
|
|
//==========================================================================
|
|
bool HWWall::DoHorizon(HWDrawInfo* di, walltype* seg, sectortype* fs, DVector2& v1, DVector2& v2)
|
|
{
|
|
#if 0
|
|
HWHorizonInfo hi;
|
|
lightlist_t * light;
|
|
|
|
// ZDoom doesn't support slopes in a horizon sector so I won't either!
|
|
ztop[1] = ztop[0] = fs->GetPlaneTexZ(sector_t::ceiling);
|
|
zbottom[1] = zbottom[0] = fs->GetPlaneTexZ(sector_t::floor);
|
|
|
|
auto vpz = di->Viewpoint.Pos.Z;
|
|
if (vpz < fs->GetPlaneTexZ(sector_t::ceiling))
|
|
{
|
|
if (vpz > fs->GetPlaneTexZ(sector_t::floor))
|
|
zbottom[1] = zbottom[0] = vpz;
|
|
|
|
if (fs->GetTexture(sector_t::ceiling) == skyflatnum)
|
|
{
|
|
SkyPlane(di, fs, sector_t::ceiling, false);
|
|
}
|
|
else
|
|
{
|
|
hi.plane.GetFromSector(fs, sector_t::ceiling);
|
|
hi.lightlevel = hw_ClampLight(fs->GetCeilingLight());
|
|
hi.colormap = fs->Colormap;
|
|
hi.specialcolor = fs->SpecialColors[sector_t::ceiling];
|
|
|
|
if (di->isFullbrightScene()) hi.colormap.Clear();
|
|
horizon = &hi;
|
|
PutPortal(di, PORTALTYPE_HORIZON, -1);
|
|
}
|
|
ztop[1] = ztop[0] = zbottom[0];
|
|
}
|
|
|
|
if (vpz > fs->GetPlaneTexZ(sector_t::floor))
|
|
{
|
|
zbottom[1] = zbottom[0] = fs->GetPlaneTexZ(sector_t::floor);
|
|
if (fs->GetTexture(sector_t::floor) == skyflatnum)
|
|
{
|
|
SkyPlane(di, fs, sector_t::floor, false);
|
|
}
|
|
else
|
|
{
|
|
hi.plane.GetFromSector(fs, sector_t::floor);
|
|
hi.lightlevel = hw_ClampLight(fs->GetFloorLight());
|
|
hi.colormap = fs->Colormap;
|
|
hi.specialcolor = fs->SpecialColors[sector_t::floor];
|
|
|
|
horizon = &hi;
|
|
PutPortal(di, PORTALTYPE_HORIZON, -1);
|
|
}
|
|
}
|
|
#endif
|
|
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.
|
|
// Panning is calculated in NPOT dimensions only, the next largest one
|
|
// to the texture height applies, so the panning needs to be scaled accordingly.
|
|
//
|
|
//==========================================================================
|
|
|
|
static float GetYPanning(float curypanning, FGameTexture* tex/*, bool aligned*/)
|
|
{
|
|
// get next largest POT size.
|
|
int th = tex->GetTexelHeight();
|
|
int pow2size = 1 << sizeToBits(th);
|
|
if (pow2size < th) pow2size *= 2;
|
|
|
|
/* crap for lack of NPOT emulation. Should not be needed anymore
|
|
if (aligned)
|
|
{
|
|
float yoffs = (pow2size - th) * (255.0f / pow2size);
|
|
if (curypanning > 256 - yoffs)
|
|
curypanning -= yoffs;
|
|
}
|
|
*/
|
|
|
|
// scale the panning factor and scale to texture coordinates.
|
|
return pow2size * curypanning / (256.0f * th);
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================
|
|
|
|
bool HWWall::SetWallCoordinates(walltype * seg, float topleft, float topright, float bottomleft, float bottomright)
|
|
{
|
|
//
|
|
//
|
|
// set up coordinates for the left side of the polygon
|
|
//
|
|
// check left side for intersections
|
|
if (topleft >= bottomleft)
|
|
{
|
|
// normal case
|
|
ztop[0] = topleft;
|
|
zbottom[0] = bottomleft;
|
|
}
|
|
else
|
|
{
|
|
// ceiling below floor - clip to the visible part of the wall
|
|
float dch = topright - topleft;
|
|
float dfh = bottomright - bottomleft;
|
|
float inter_x = (bottomleft - topleft) / (dch - dfh);
|
|
float inter_y = topleft + inter_x * dch;
|
|
|
|
glseg.x1 = glseg.x1 + inter_x * (glseg.x2 - glseg.x1);
|
|
glseg.y1 = glseg.y1 + inter_x * (glseg.y2 - glseg.y1);
|
|
glseg.fracleft = inter_x;
|
|
|
|
zbottom[0] = ztop[0] = inter_y;
|
|
}
|
|
|
|
//
|
|
//
|
|
// set up coordinates for the right side of the polygon
|
|
//
|
|
// check left side for intersections
|
|
if (topright >= bottomright)
|
|
{
|
|
// normal case
|
|
ztop[1] = topright;
|
|
zbottom[1] = bottomright;
|
|
}
|
|
else
|
|
{
|
|
// ceiling below floor - clip to the visible part of the wall
|
|
float dch = topright - topleft;
|
|
float dfh = bottomright - bottomleft;
|
|
float inter_x = (bottomleft - topleft) / (dch - dfh);
|
|
|
|
float inter_y = topleft + inter_x * dch;
|
|
|
|
glseg.x2 = glseg.x1 + inter_x * (glseg.x2 - glseg.x1);
|
|
glseg.y2 = glseg.y1 + inter_x * (glseg.y2 - glseg.y1);
|
|
glseg.fracright = inter_x;
|
|
|
|
zbottom[1] = ztop[1] = inter_y;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// Do some tweaks with the texture coordinates to reduce visual glitches
|
|
//
|
|
//==========================================================================
|
|
|
|
void HWWall::CheckTexturePosition()
|
|
{
|
|
float sub;
|
|
|
|
if (texture->isHardwareCanvas()) return;
|
|
|
|
// clamp texture coordinates to a reasonable range.
|
|
// Extremely large values can cause visual problems
|
|
if (tcs[UPLFT].v > tcs[LOLFT].v || tcs[UPRGT].v > tcs[LORGT].v)
|
|
{
|
|
if (tcs[UPLFT].v < tcs[UPRGT].v)
|
|
{
|
|
sub = float(xs_FloorToInt(tcs[UPLFT].v));
|
|
}
|
|
else
|
|
{
|
|
sub = float(xs_FloorToInt(tcs[UPRGT].v));
|
|
}
|
|
tcs[UPLFT].v -= sub;
|
|
tcs[UPRGT].v -= sub;
|
|
tcs[LOLFT].v -= sub;
|
|
tcs[LORGT].v -= sub;
|
|
|
|
if ((tcs[UPLFT].v == 0.f && tcs[UPRGT].v == 0.f && tcs[LOLFT].v <= 1.f && tcs[LORGT].v <= 1.f) ||
|
|
(tcs[UPLFT].v >= 0.f && tcs[UPRGT].v >= 0.f && tcs[LOLFT].v == 1.f && tcs[LORGT].v == 1.f))
|
|
{
|
|
flags |= HWF_CLAMPY;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (tcs[LOLFT].v < tcs[LORGT].v)
|
|
{
|
|
sub = float(xs_FloorToInt(tcs[LOLFT].v));
|
|
}
|
|
else
|
|
{
|
|
sub = float(xs_FloorToInt(tcs[LORGT].v));
|
|
}
|
|
tcs[UPLFT].v -= sub;
|
|
tcs[UPRGT].v -= sub;
|
|
tcs[LOLFT].v -= sub;
|
|
tcs[LORGT].v -= sub;
|
|
|
|
if ((tcs[LOLFT].v == 0.f && tcs[LORGT].v == 0.f && tcs[UPLFT].v <= 1.f && tcs[UPRGT].v <= 1.f) ||
|
|
(tcs[LOLFT].v >= 0.f && tcs[LORGT].v >= 0.f && tcs[UPLFT].v == 1.f && tcs[UPRGT].v == 1.f))
|
|
{
|
|
flags |= HWF_CLAMPY;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//==========================================================================
|
|
//
|
|
// Common part of wall drawers
|
|
//
|
|
//==========================================================================
|
|
|
|
void HWWall::DoTexture(HWDrawInfo* di, walltype* wal, walltype* refwall, float refheight, float topleft, float topright, float bottomleft, float bottomright)
|
|
{
|
|
auto glsave = glseg;
|
|
float ypanning = wal->ypan_ ? GetYPanning(wal->ypan_, texture) : 0;
|
|
SetWallCoordinates(wal, topleft, topright, bottomleft, bottomright);
|
|
|
|
bool xflipped = (wal->cstat & CSTAT_WALL_XFLIP);
|
|
float leftdist = xflipped ? 1.f - glseg.fracleft : glseg.fracleft;
|
|
float rightdist = xflipped ? 1.f - glseg.fracright : glseg.fracright;
|
|
|
|
float tw = texture->GetTexelWidth();
|
|
float th = texture->GetTexelHeight();
|
|
tcs[LOLFT].u = tcs[UPLFT].u = ((leftdist * 8.f * wal->xrepeat) + wal->xpan_) / tw;
|
|
tcs[LORGT].u = tcs[UPRGT].u = ((rightdist * 8.f * wal->xrepeat) + wal->xpan_) / tw;
|
|
|
|
auto setv = [=](float hl, float hr, float frac) -> float
|
|
{
|
|
float h = hl + (hr - hl) * frac;
|
|
h = (-(float)(refheight + (h * 256)) / ((th * 2048.0f) / (float)(wal->yrepeat))) + ypanning;
|
|
if (wal->cstat & CSTAT_WALL_YFLIP) h = 1.f - h;
|
|
return h;
|
|
};
|
|
|
|
tcs[UPLFT].v = setv(topleft, topright, glseg.fracleft);
|
|
tcs[LOLFT].v = setv(bottomleft, bottomright, glseg.fracleft);
|
|
tcs[UPRGT].v = setv(topleft, topright, glseg.fracright);
|
|
tcs[LORGT].v = setv(bottomleft, bottomright, glseg.fracright);
|
|
CheckTexturePosition();
|
|
bool trans = type == RENDERWALL_M2S && (wal->cstat & CSTAT_WALL_TRANSLUCENT);
|
|
if (trans)
|
|
{
|
|
SetRenderStyleFromBlend(!!(wal->cstat & CSTAT_WALL_TRANSLUCENT), 0, !!(wal->cstat & CSTAT_WALL_TRANS_FLIP));
|
|
alpha = GetAlphaFromBlend((wal->cstat & CSTAT_WALL_TRANS_FLIP) ? DAMETH_TRANS2 : DAMETH_TRANS1, 0);
|
|
}
|
|
PutWall(di, trans);
|
|
glseg = glsave;
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
// Handle one sided walls
|
|
//
|
|
//==========================================================================
|
|
|
|
void HWWall::DoOneSidedTexture(HWDrawInfo* di, walltype* wal, sectortype* frontsector, sectortype* backsector,
|
|
float topleft, float topright, float bottomleft, float bottomright)
|
|
{
|
|
// get the alignment reference position.
|
|
int refheight;
|
|
|
|
if ((wal->cstat & CSTAT_WALL_1WAY) && backsector)
|
|
{
|
|
if ((!(wal->cstat & CSTAT_WALL_BOTTOM_SWAP) && (wal->cstat & CSTAT_WALL_1WAY)) ||
|
|
((wal->cstat & CSTAT_WALL_BOTTOM_SWAP) && (wall[wal->nextwall].cstat & CSTAT_WALL_ALIGN_BOTTOM)))
|
|
refheight = frontsector->ceilingz;
|
|
else
|
|
refheight = backsector->floorz;
|
|
}
|
|
else
|
|
{
|
|
refheight = (wal->cstat & CSTAT_WALL_ALIGN_BOTTOM) ? frontsector->floorz : frontsector->ceilingz;
|
|
}
|
|
|
|
type = RENDERWALL_M1S;
|
|
DoTexture(di, wal, wal, refheight, topleft, topright, bottomleft, bottomright);
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================
|
|
|
|
void HWWall::DoUpperTexture(HWDrawInfo* di, walltype* wal, sectortype* frontsector, sectortype* backsector,
|
|
float topleft, float topright, float bottomleft, float bottomright)
|
|
{
|
|
// get the alignment reference position.
|
|
int refheight = (wal->cstat & CSTAT_WALL_ALIGN_BOTTOM) ? frontsector->floorz : frontsector->ceilingz;
|
|
|
|
type = RENDERWALL_BOTTOM;
|
|
DoTexture(di, wal, wal, refheight, topleft, topright, bottomleft, bottomright);
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================
|
|
|
|
void HWWall::DoLowerTexture(HWDrawInfo* di, walltype* wal, sectortype* frontsector, sectortype* backsector,
|
|
float topleft, float topright, float bottomleft, float bottomright)
|
|
{
|
|
// get the alignment reference position.
|
|
int refheight;
|
|
auto refwall = (wall->cstat & CSTAT_WALL_BOTTOM_SWAP) ? &wall[wal->nextwall] : wal;
|
|
|
|
if ((wal->cstat & CSTAT_WALL_1WAY) && backsector)
|
|
{
|
|
if ((!(wal->cstat & CSTAT_WALL_BOTTOM_SWAP) && (wal->cstat & CSTAT_WALL_1WAY)) ||
|
|
((wal->cstat & CSTAT_WALL_BOTTOM_SWAP) && (wall[wal->nextwall].cstat & CSTAT_WALL_ALIGN_BOTTOM)))
|
|
refheight = frontsector->ceilingz;
|
|
else
|
|
refheight = backsector->floorz;
|
|
}
|
|
else
|
|
{
|
|
refheight = (wal->cstat & CSTAT_WALL_ALIGN_BOTTOM) ? frontsector->floorz : frontsector->ceilingz;
|
|
}
|
|
|
|
shade = refwall->shade;
|
|
palette = refwall->pal;
|
|
type = RENDERWALL_BOTTOM;
|
|
DoTexture(di, wal, refwall, refheight, topleft, topright, bottomleft, bottomright);
|
|
}
|
|
|
|
//==========================================================================
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================
|
|
|
|
void HWWall::DoMidTexture(HWDrawInfo* di, walltype* wal,
|
|
sectortype* front, sectortype* back,
|
|
float fch1, float fch2, float ffh1, float ffh2,
|
|
float bch1, float bch2, float bfh1, float bfh2)
|
|
{
|
|
float topleft,bottomleft,topright,bottomright;
|
|
float refheight = (wal->cstat & CSTAT_WALL_ALIGN_BOTTOM) ? frontsector->ceilingz : backsector->ceilingz;
|
|
|
|
topleft = std::min(bch1,fch1);
|
|
topright = std::min(bch2,fch2);
|
|
bottomleft = std::max(bfh1,ffh1);
|
|
bottomright = std::max(bfh2,ffh2);
|
|
if (topleft<=bottomleft && topright<=bottomright) return;
|
|
type = seg->cstat & CSTAT_WALL_1WAY ? RENDERWALL_M1S : RENDERWALL_M2S;
|
|
|
|
// todo: transparency.
|
|
|
|
DoTexture(di, wal, wal, refheight, topleft, topright, bottomleft, bottomright);
|
|
RenderStyle = STYLE_Normal;
|
|
alpha = 1.f;
|
|
}
|
|
|
|
|
|
//==========================================================================
|
|
//
|
|
//
|
|
//
|
|
//==========================================================================
|
|
void HWWall::Process(HWDrawInfo *di, walltype *wal, sectortype* frontsector, sectortype* backsector)
|
|
{
|
|
auto backwall = wall->nextwall >= 0 && wall->nextwall < numwalls? &wall[wal->nextwall] : nullptr;
|
|
auto p2wall = &wall[wal->point2];
|
|
|
|
float fch1;
|
|
float ffh1;
|
|
float fch2;
|
|
float ffh2;
|
|
|
|
FVector2 v1(WallStartX(wal), WallStartY(wal));
|
|
FVector2 v2(WallEndX(wal), WallEndY(wal));
|
|
|
|
PlanesAtPoint(frontsector, wal->x, wal->y, &fch1, &ffh1);
|
|
PlanesAtPoint(frontsector, p2wall->x, p2wall->y, &fch2, &ffh2);
|
|
|
|
#ifdef _DEBUG
|
|
if (wal - wall == 14454)
|
|
{
|
|
int a = 0;
|
|
}
|
|
if (fabs(v1.X) == 400 && fabs(v1.Y) == 3968)
|
|
{
|
|
int a = 0;
|
|
}
|
|
#endif
|
|
|
|
// note: we always have a valid sidedef and linedef reference when getting here.
|
|
|
|
this->seg = wal;
|
|
this->frontsector = frontsector;
|
|
this->backsector = backsector;
|
|
vertindex = 0;
|
|
vertcount = 0;
|
|
|
|
//vertexes[0] = v1;
|
|
//vertexes[1] = v2;
|
|
|
|
glseg.x1 = v1.X;
|
|
glseg.y1 = v1.Y;
|
|
glseg.x2 = v2.X;
|
|
glseg.y2 = v2.Y;
|
|
glseg.fracleft = 0;
|
|
glseg.fracright = 1;
|
|
flags = 0;
|
|
dynlightindex = -1;
|
|
shade = wal->shade;
|
|
palette = wal->pal;
|
|
floorpal = frontsector->floorpal;
|
|
visibility = sectorVisibility(frontsector);
|
|
|
|
alpha = 1.0f;
|
|
RenderStyle = STYLE_Normal;
|
|
texture = NULL;
|
|
|
|
/*
|
|
if (wal->linedef->special == Line_Horizon)
|
|
{
|
|
SkyNormal(di, frontsector, v1, v2);
|
|
DoHorizon(di, wal, frontsector, v1, v2);
|
|
return;
|
|
}
|
|
*/
|
|
|
|
bool isportal = false;// wal->linedef->isVisualPortal() && wal->sidedef == wal->linedef->sidedef[0];
|
|
|
|
if (!backsector || !backwall)
|
|
{
|
|
// sector's sky
|
|
SkyNormal(di, frontsector, v1, v2);
|
|
|
|
/*
|
|
if (isportal)
|
|
{
|
|
lineportal = wal->linedef->getPortal()->mGroup;
|
|
ztop[0] = zceil[0];
|
|
ztop[1] = zceil[1];
|
|
zbottom[0] = zfloor[0];
|
|
zbottom[1] = zfloor[1];
|
|
PutPortal(di, PORTALTYPE_LINETOLINE, -1);
|
|
}
|
|
else
|
|
*/
|
|
{
|
|
// normal texture
|
|
|
|
int tilenum = (wal->cstat & CSTAT_WALL_1WAY) ? wal->overpicnum : wal->picnum;
|
|
tileUpdatePicnum(&tilenum, int(wal-wall) + 16384, wall->cstat);
|
|
texture = tileGetTexture(tilenum);
|
|
if (texture && texture->isValid())
|
|
{
|
|
DoOneSidedTexture(di, wal, frontsector, backsector, fch1, fch2, ffh1, ffh2);
|
|
}
|
|
}
|
|
}
|
|
else // two sided
|
|
{
|
|
float bfh1;
|
|
float bfh2;
|
|
float bch1;
|
|
float bch2;
|
|
PlanesAtPoint(backsector, wal->x, wal->y, &bch1, &bfh1);
|
|
PlanesAtPoint(backsector, p2wall->x, p2wall->y, &bch2, &bfh2);
|
|
|
|
float zalign = 0.f;
|
|
|
|
SkyTop(di, wal, frontsector, backsector, v1, v2);
|
|
SkyBottom(di, wal, frontsector, backsector, v1, v2);
|
|
|
|
// upper texture
|
|
if (!(frontsector->ceilingstat & backsector->ceilingstat & CSTAT_SECTOR_SKY))
|
|
{
|
|
float bch1a = bch1;
|
|
float bch2a = bch2;
|
|
if (ffh1 > bch1 && ffh2 > bch2)
|
|
{
|
|
// the back sector's floor obstructs part of this wall
|
|
bch2a = ffh2;
|
|
bch1a = ffh1;
|
|
}
|
|
|
|
if (bch1a < fch1 || bch2a < fch2)
|
|
{
|
|
int tilenum = wal->picnum;
|
|
tileUpdatePicnum(&tilenum, int(wal - wall) + 16384, wall->cstat);
|
|
texture = tileGetTexture(tilenum);
|
|
if (texture && texture->isValid())
|
|
{
|
|
DoUpperTexture(di, wal, frontsector, backsector, fch1, fch2, bch1a, bch2a);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
if (isportal)
|
|
{
|
|
lineportal = wal->linedef->getPortal()->mGroup;
|
|
ztop[0] = bch1;
|
|
ztop[1] = bch2;
|
|
zbottom[0] = bfh1;
|
|
zbottom[1] = bfh2;
|
|
PutPortal(di, PORTALTYPE_LINETOLINE, -1);
|
|
}
|
|
else*/
|
|
|
|
if (wall->cstat & (CSTAT_WALL_MASKED | CSTAT_WALL_1WAY))
|
|
{
|
|
int tilenum = wal->overpicnum;
|
|
tileUpdatePicnum(&tilenum, int(wal - wall) + 16384, wall->cstat);
|
|
texture = tileGetTexture(tilenum);
|
|
if (texture && texture->isValid())
|
|
{
|
|
DoMidTexture(di, wal, frontsector, backsector, fch1, fch2, ffh1, ffh2, bch1, bch2, bfh1, bfh2);
|
|
}
|
|
}
|
|
|
|
// lower texture
|
|
if (!(frontsector->floorstat & backsector->floorstat & CSTAT_SECTOR_SKY))
|
|
{
|
|
if (fch1 < bfh1 && fch2 < bfh2)
|
|
{
|
|
// the back sector's ceiling obstructs part of this wall.
|
|
bfh1 = fch1;
|
|
bfh2 = fch2;
|
|
}
|
|
|
|
if (bfh1 > ffh1 || bfh2 > ffh2)
|
|
{
|
|
auto w = (wal->cstat & CSTAT_WALL_BOTTOM_SWAP) ? backwall : wal;
|
|
int tilenum = w->picnum;
|
|
tileUpdatePicnum(&tilenum, int(wal - wall) + 16384, w->cstat);
|
|
texture = tileGetTexture(tilenum);
|
|
if (texture && texture->isValid())
|
|
{
|
|
DoLowerTexture(di, wal, frontsector, backsector, bfh1, bfh2, ffh1, ffh2);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|