- remove softpoly scene drawer

This commit is contained in:
Magnus Norddahl 2019-05-22 17:16:07 +02:00
parent 0eda298db2
commit 48d2d423f6
44 changed files with 48 additions and 6693 deletions

View file

@ -713,7 +713,6 @@ file( GLOB HEADER_FILES
rendering/polyrenderer/*.h
rendering/polyrenderer/math/*.h
rendering/polyrenderer/drawers/*.h
rendering/polyrenderer/scene/*.h
rendering/polyrenderer/backend/*.h
rendering/hwrenderer/data/*.h
rendering/hwrenderer/dynlights/*.h
@ -786,21 +785,6 @@ set ( SWRENDER_SOURCES
)
set( POLYRENDER_SOURCES
rendering/polyrenderer/poly_renderer.cpp
rendering/polyrenderer/poly_renderthread.cpp
rendering/polyrenderer/scene/poly_scene.cpp
rendering/polyrenderer/scene/poly_portal.cpp
rendering/polyrenderer/scene/poly_cull.cpp
rendering/polyrenderer/scene/poly_decal.cpp
rendering/polyrenderer/scene/poly_particle.cpp
rendering/polyrenderer/scene/poly_plane.cpp
rendering/polyrenderer/scene/poly_playersprite.cpp
rendering/polyrenderer/scene/poly_wall.cpp
rendering/polyrenderer/scene/poly_wallsprite.cpp
rendering/polyrenderer/scene/poly_sprite.cpp
rendering/polyrenderer/scene/poly_model.cpp
rendering/polyrenderer/scene/poly_sky.cpp
rendering/polyrenderer/scene/poly_light.cpp
rendering/polyrenderer/drawers/poly_buffer.cpp
rendering/polyrenderer/drawers/poly_triangle.cpp
rendering/polyrenderer/drawers/poly_draw_args.cpp
@ -1564,7 +1548,6 @@ source_group("Rendering\\Software Renderer\\Viewport" REGULAR_EXPRESSION "^${CMA
source_group("Rendering\\Poly Renderer" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/rendering/polyrenderer/.+")
source_group("Rendering\\Poly Renderer\\Math" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/rendering/polyrenderer/math/.+")
source_group("Rendering\\Poly Renderer\\Drawers" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/rendering/polyrenderer/drawers/.+")
source_group("Rendering\\Poly Renderer\\Scene" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/rendering/polyrenderer/scene/.+")
source_group("Rendering\\Poly Renderer\\Backend" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/rendering/polyrenderer/backend/.+")
source_group("Render Data" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/r_data/.+")
source_group("Render Data\\Models" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/r_data/models/.+")

View file

@ -36,7 +36,7 @@
#include "swrenderer/r_swcolormaps.h"
#include "poly_draw_args.h"
#include "swrenderer/viewport/r_viewport.h"
#include "polyrenderer/poly_renderthread.h"
#include "polyrenderer/drawers/poly_triangle.h"
void PolyDrawArgs::SetTexture(const uint8_t *texels, int width, int height)
{
@ -97,13 +97,6 @@ void PolyDrawArgs::SetLight(FSWColormap *base_colormap, uint32_t lightlevel, dou
{
mGlobVis = (float)globVis;
PolyCameraLight *cameraLight = PolyCameraLight::Instance();
if (cameraLight->FixedLightLevel() >= 0 || cameraLight->FixedColormap())
{
lightlevel = cameraLight->FixedLightLevel() >= 0 ? cameraLight->FixedLightLevel() : 255;
fixed = true;
}
mLight = clamp<uint32_t>(lightlevel, 0, 255);
mFixedLight = fixed;
mLightRed = base_colormap->Color.r;
@ -203,6 +196,7 @@ void PolyDrawArgs::SetStyle(const FRenderStyle &renderstyle, double alpha, uint3
/////////////////////////////////////////////////////////////////////////////
#if 0
void RectDrawArgs::SetTexture(FSoftwareTexture *texture, FRenderStyle style)
{
mTexture = texture;
@ -351,3 +345,4 @@ void RectDrawArgs::SetStyle(const FRenderStyle &renderstyle, double alpha, uint3
SetStyle(Translation() ? TriBlendMode::AddShadedTranslated : TriBlendMode::AddShaded, alpha);
}
}
#endif

View file

@ -167,6 +167,7 @@ private:
uint32_t mDynLightColor = 0;
};
#if 0
class RectDrawArgs
{
public:
@ -233,3 +234,4 @@ private:
bool mSimpleShade = true;
float mX0, mX1, mY0, mY1, mU0, mU1, mV0, mV1;
};
#endif

View file

@ -35,7 +35,6 @@
#include "v_palette.h"
#include "r_data/colormaps.h"
#include "poly_triangle.h"
#include "polyrenderer/poly_renderer.h"
#include "swrenderer/drawers/r_draw_rgba.h"
#include "screen_triangle.h"
#include "x86.h"
@ -724,6 +723,7 @@ void DrawPolyTrianglesCommand::Execute(DrawerThread *thread)
/////////////////////////////////////////////////////////////////////////////
#if 0
void DrawRectCommand::Execute(DrawerThread *thread)
{
auto renderTarget = PolyRenderer::Instance()->RenderTarget;
@ -737,3 +737,4 @@ void DrawRectCommand::Execute(DrawerThread *thread)
else
ScreenTriangle::RectDrawers8[blendmode](destOrg, destWidth, destHeight, destPitch, &args, PolyTriangleThreadData::Get(thread));
}
#endif

View file

@ -240,6 +240,7 @@ private:
PolyDrawMode mode;
};
#if 0
class DrawRectCommand : public PolyDrawerCommand
{
public:
@ -250,3 +251,4 @@ public:
private:
RectDrawArgs args;
};
#endif

View file

@ -1588,6 +1588,7 @@ void DrawSpan8(int y, int x0, int x1, const TriDrawTriangleArgs *args, PolyTrian
}
}
#if 0
template<typename ModeT>
void DrawRect8(const void *destOrg, int destWidth, int destHeight, int destPitch, const RectDrawArgs *args, PolyTriangleThreadData *thread)
{
@ -2115,6 +2116,7 @@ void DrawRect32(const void *destOrg, int destWidth, int destHeight, int destPitc
else
DrawRectOpt32<ModeT, DrawerOptCF>(destOrg, destWidth, destHeight, destPitch, args, thread);
}
#endif
void(*ScreenTriangle::SpanDrawers8[])(int, int, int, const TriDrawTriangleArgs *, PolyTriangleThreadData *) =
{
@ -2180,6 +2182,7 @@ void(*ScreenTriangle::SpanDrawers32[])(int, int, int, const TriDrawTriangleArgs
&DrawSpan32<TriScreenDrawerModes::StyleAddShadedTranslated>
};
#if 0
void(*ScreenTriangle::RectDrawers8[])(const void *, int, int, int, const RectDrawArgs *, PolyTriangleThreadData *) =
{
&DrawRect8<TriScreenDrawerModes::StyleOpaque>,
@ -2243,6 +2246,7 @@ void(*ScreenTriangle::RectDrawers32[])(const void *, int, int, int, const RectDr
&DrawRect32<TriScreenDrawerModes::StyleAddStencilTranslated>,
&DrawRect32<TriScreenDrawerModes::StyleAddShadedTranslated>
};
#endif
void(*ScreenTriangle::TriangleDrawers[])(const TriDrawTriangleArgs *args, PolyTriangleThreadData *thread, int16_t *edges, int topY, int bottomY) =
{

View file

@ -25,6 +25,7 @@
#include <cstdint>
#include <vector>
#include "r_data/renderstyle.h"
#include "rendering/swrenderer/drawers/r_draw.h"
class FString;
class PolyDrawArgs;
@ -138,8 +139,10 @@ public:
static void(*SpanDrawers8[])(int y, int x0, int x1, const TriDrawTriangleArgs *args, PolyTriangleThreadData *thread);
static void(*SpanDrawers32[])(int y, int x0, int x1, const TriDrawTriangleArgs *args, PolyTriangleThreadData *thread);
#if 0
static void(*RectDrawers8[])(const void *, int, int, int, const RectDrawArgs *, PolyTriangleThreadData *);
static void(*RectDrawers32[])(const void *, int, int, int, const RectDrawArgs *, PolyTriangleThreadData *);
#endif
static int FuzzStart;
};

View file

@ -1,21 +1,6 @@
#include "../swrenderer/textures/r_swtexture.h"
#include "poly_renderer.cpp"
#include "poly_renderthread.cpp"
#include "drawers/poly_buffer.cpp"
#include "drawers/poly_draw_args.cpp"
#include "drawers/poly_triangle.cpp"
#include "drawers/screen_triangle.cpp"
#include "math/gpu_types.cpp"
#include "scene/poly_cull.cpp"
#include "scene/poly_decal.cpp"
#include "scene/poly_particle.cpp"
#include "scene/poly_plane.cpp"
#include "scene/poly_playersprite.cpp"
#include "scene/poly_portal.cpp"
#include "scene/poly_scene.cpp"
#include "scene/poly_sky.cpp"
#include "scene/poly_sprite.cpp"
#include "scene/poly_model.cpp"
#include "scene/poly_wall.cpp"
#include "scene/poly_wallsprite.cpp"
#include "scene/poly_light.cpp"

View file

@ -1,262 +0,0 @@
/*
** Polygon Doom software renderer
** Copyright (c) 2016 Magnus Norddahl
**
** This software is provided 'as-is', without any express or implied
** warranty. In no event will the authors be held liable for any damages
** arising from the use of this software.
**
** Permission is granted to anyone to use this software for any purpose,
** including commercial applications, and to alter it and redistribute it
** freely, subject to the following restrictions:
**
** 1. The origin of this software must not be misrepresented; you must not
** claim that you wrote the original software. If you use this software
** in a product, an acknowledgment in the product documentation would be
** appreciated but is not required.
** 2. Altered source versions must be plainly marked as such, and must not be
** misrepresented as being the original software.
** 3. This notice may not be removed or altered from any source distribution.
**
*/
#include <stdlib.h>
#include "templates.h"
#include "doomdef.h"
#include "sbar.h"
#include "st_stuff.h"
#include "r_data/r_translate.h"
#include "r_data/r_interpolate.h"
#include "r_data/models/models.h"
#include "poly_renderer.h"
#include "d_net.h"
#include "po_man.h"
#include "st_stuff.h"
#include "g_levellocals.h"
#include "p_effect.h"
#include "actorinlines.h"
#include "polyrenderer/scene/poly_light.h"
#include "swrenderer/scene/r_scene.h"
#include "swrenderer/drawers/r_draw_rgba.h"
#include "swrenderer/viewport/r_viewport.h"
#include "swrenderer/r_swcolormaps.h"
EXTERN_CVAR(Int, screenblocks)
EXTERN_CVAR(Float, r_visibility)
EXTERN_CVAR(Bool, r_models)
extern bool r_modelscene;
/////////////////////////////////////////////////////////////////////////////
PolyRenderer *PolyRenderer::Instance()
{
static PolyRenderer scene;
return &scene;
}
PolyRenderer::PolyRenderer()
{
}
void PolyRenderer::RenderView(player_t *player, DCanvas *target, void *videobuffer, int bufferpitch)
{
using namespace swrenderer;
R_ExecuteSetViewSize(Viewpoint, Viewwindow);
RenderTarget = target;
RenderToCanvas = false;
RenderActorView(player->mo, true, false);
Threads.MainThread()->FlushDrawQueue();
auto copyqueue = std::make_shared<DrawerCommandQueue>(Threads.MainThread()->FrameMemory.get());
copyqueue->Push<MemcpyCommand>(videobuffer, bufferpitch, target->GetPixels(), target->GetWidth(), target->GetHeight(), target->GetPitch(), target->IsBgra() ? 4 : 1);
DrawerThreads::Execute(copyqueue);
PolyDrawerWaitCycles.Clock();
DrawerThreads::WaitForWorkers();
PolyDrawerWaitCycles.Unclock();
}
void PolyRenderer::RenderViewToCanvas(AActor *actor, DCanvas *canvas, int x, int y, int width, int height, bool dontmaplines)
{
// Save a bunch of silly globals:
auto savedViewpoint = Viewpoint;
auto savedViewwindow = Viewwindow;
auto savedviewwindowx = viewwindowx;
auto savedviewwindowy = viewwindowy;
auto savedviewwidth = viewwidth;
auto savedviewheight = viewheight;
auto savedviewactive = viewactive;
auto savedRenderTarget = RenderTarget;
// Setup the view:
RenderTarget = canvas;
RenderToCanvas = true;
R_SetWindow(Viewpoint, Viewwindow, 12, width, height, height, true);
viewwindowx = x;
viewwindowy = y;
viewactive = true;
// Render:
RenderActorView(actor, false, dontmaplines);
Threads.MainThread()->FlushDrawQueue();
DrawerThreads::WaitForWorkers();
RenderToCanvas = false;
// Restore silly globals:
Viewpoint = savedViewpoint;
Viewwindow = savedViewwindow;
viewwindowx = savedviewwindowx;
viewwindowy = savedviewwindowy;
viewwidth = savedviewwidth;
viewheight = savedviewheight;
viewactive = savedviewactive;
RenderTarget = savedRenderTarget;
}
void PolyRenderer::RenderActorView(AActor *actor, bool drawpsprites, bool dontmaplines)
{
PolyTotalBatches = 0;
PolyTotalTriangles = 0;
PolyTotalDrawCalls = 0;
PolyCullCycles.Reset();
PolyOpaqueCycles.Reset();
PolyMaskedCycles.Reset();
PolyDrawerWaitCycles.Reset();
DontMapLines = dontmaplines;
R_SetupFrame(Viewpoint, Viewwindow, actor);
Level = Viewpoint.ViewLevel;
static bool firstcall = true;
if (firstcall)
{
swrenderer::R_InitFuzzTable(RenderTarget->GetPitch());
firstcall = false;
}
swrenderer::R_UpdateFuzzPosFrameStart();
if (APART(R_OldBlend)) NormalLight.Maps = realcolormaps.Maps;
else NormalLight.Maps = realcolormaps.Maps + NUMCOLORMAPS * 256 * R_OldBlend;
Light.SetVisibility(Viewwindow, r_visibility);
PolyCameraLight::Instance()->SetCamera(Viewpoint, RenderTarget, actor);
//Viewport->SetupFreelook();
ActorRenderFlags savedflags = 0;
if (Viewpoint.camera)
{
savedflags = Viewpoint.camera->renderflags;
// Never draw the player unless in chasecam mode
if (!Viewpoint.showviewer)
Viewpoint.camera->renderflags |= RF_INVISIBLE;
}
ScreenTriangle::FuzzStart = (ScreenTriangle::FuzzStart + 14) % FUZZTABLE;
r_modelscene = r_models && Models.Size() > 0;
NextStencilValue = 0;
Threads.Clear();
Threads.MainThread()->SectorPortals.clear();
Threads.MainThread()->LinePortals.clear();
Threads.MainThread()->TranslucentObjects.clear();
PolyTriangleDrawer::ResizeBuffers(RenderTarget);
PolyTriangleDrawer::ClearStencil(Threads.MainThread()->DrawQueue, 0);
SetSceneViewport();
PolyPortalViewpoint mainViewpoint = SetupPerspectiveMatrix();
mainViewpoint.StencilValue = GetNextStencilValue();
Scene.CurrentViewpoint = &mainViewpoint;
Scene.Render(&mainViewpoint);
if (drawpsprites)
PlayerSprites.Render(Threads.MainThread());
Scene.CurrentViewpoint = nullptr;
if (Viewpoint.camera)
Viewpoint.camera->renderflags = savedflags;
}
void PolyRenderer::RenderRemainingPlayerSprites()
{
PlayerSprites.RenderRemainingSprites();
}
void PolyRenderer::SetSceneViewport()
{
using namespace swrenderer;
if (!RenderToCanvas) // Rendering to screen
{
int height;
if (screenblocks >= 10)
height = SCREENHEIGHT;
else
height = (screenblocks*SCREENHEIGHT / 10) & ~7;
int bottom = SCREENHEIGHT - (height + viewwindowy - ((height - viewheight) / 2));
PolyTriangleDrawer::SetViewport(Threads.MainThread()->DrawQueue, viewwindowx, SCREENHEIGHT - bottom - height, viewwidth, height, RenderTarget);
}
else // Rendering to camera texture
{
PolyTriangleDrawer::SetViewport(Threads.MainThread()->DrawQueue, 0, 0, RenderTarget->GetWidth(), RenderTarget->GetHeight(), RenderTarget);
}
}
PolyPortalViewpoint PolyRenderer::SetupPerspectiveMatrix(bool mirror)
{
// We have to scale the pitch to account for the pixel stretching, because the playsim doesn't know about this and treats it as 1:1.
double radPitch = Viewpoint.Angles.Pitch.Normalized180().Radians();
double angx = cos(radPitch);
double angy = sin(radPitch) * PolyRenderer::Instance()->Level->info->pixelstretch;
double alen = sqrt(angx*angx + angy*angy);
float adjustedPitch = (float)asin(angy / alen);
float adjustedViewAngle = (float)(Viewpoint.Angles.Yaw - 90).Radians();
float ratio = Viewwindow.WidescreenRatio;
float fovratio = (Viewwindow.WidescreenRatio >= 1.3f) ? 1.333333f : ratio;
float fovy = (float)(2 * DAngle::ToDegrees(atan(tan(Viewpoint.FieldOfView.Radians() / 2) / fovratio)).Degrees);
PolyPortalViewpoint portalViewpoint;
portalViewpoint.WorldToView =
Mat4f::Rotate((float)Viewpoint.Angles.Roll.Radians(), 0.0f, 0.0f, 1.0f) *
Mat4f::Rotate(adjustedPitch, 1.0f, 0.0f, 0.0f) *
Mat4f::Rotate(adjustedViewAngle, 0.0f, -1.0f, 0.0f) *
Mat4f::Scale(1.0f, PolyRenderer::Instance()->Level->info->pixelstretch, 1.0f) *
Mat4f::SwapYZ() *
Mat4f::Translate((float)-Viewpoint.Pos.X, (float)-Viewpoint.Pos.Y, (float)-Viewpoint.Pos.Z);
portalViewpoint.Mirror = mirror;
if (mirror)
portalViewpoint.WorldToView = Mat4f::Scale(-1.0f, 1.0f, 1.0f) * portalViewpoint.WorldToView;
portalViewpoint.WorldToClip = Mat4f::Perspective(fovy, ratio, 5.0f, 65535.0f, Handedness::Right, ClipZRange::NegativePositiveW) * portalViewpoint.WorldToView;
return portalViewpoint;
}
cycle_t PolyCullCycles, PolyOpaqueCycles, PolyMaskedCycles, PolyDrawerWaitCycles;
int PolyTotalBatches, PolyTotalTriangles, PolyTotalDrawCalls;
ADD_STAT(polyfps)
{
FString out;
out.Format("frame=%04.1f ms cull=%04.1f ms opaque=%04.1f ms masked=%04.1f ms drawers=%04.1f ms",
FrameCycles.TimeMS(), PolyCullCycles.TimeMS(), PolyOpaqueCycles.TimeMS(), PolyMaskedCycles.TimeMS(), PolyDrawerWaitCycles.TimeMS());
out.AppendFormat("\nbatches drawn: %d triangles drawn: %d drawcalls: %d", PolyTotalBatches, PolyTotalTriangles, PolyTotalDrawCalls);
return out;
}

View file

@ -1,79 +0,0 @@
/*
** Polygon Doom software renderer
** Copyright (c) 2016 Magnus Norddahl
**
** This software is provided 'as-is', without any express or implied
** warranty. In no event will the authors be held liable for any damages
** arising from the use of this software.
**
** Permission is granted to anyone to use this software for any purpose,
** including commercial applications, and to alter it and redistribute it
** freely, subject to the following restrictions:
**
** 1. The origin of this software must not be misrepresented; you must not
** claim that you wrote the original software. If you use this software
** in a product, an acknowledgment in the product documentation would be
** appreciated but is not required.
** 2. Altered source versions must be plainly marked as such, and must not be
** misrepresented as being the original software.
** 3. This notice may not be removed or altered from any source distribution.
**
*/
#pragma once
#include <vector>
#include <memory>
#include <algorithm>
#include <functional>
#include "doomdata.h"
#include "r_utility.h"
#include "scene/poly_portal.h"
#include "scene/poly_playersprite.h"
#include "scene/poly_light.h"
#include "swrenderer/r_memory.h"
#include "poly_renderthread.h"
#include "stats.h"
class AActor;
class DCanvas;
class PolyPortalViewpoint;
class DrawerCommandQueue;
typedef std::shared_ptr<DrawerCommandQueue> DrawerCommandQueuePtr;
extern cycle_t PolyCullCycles, PolyOpaqueCycles, PolyMaskedCycles, PolyDrawerWaitCycles;
extern int PolyTotalBatches, PolyTotalTriangles, PolyTotalDrawCalls;
class PolyRenderer
{
public:
PolyRenderer();
void RenderView(player_t *player, DCanvas *target, void *videobuffer, int bufferpitch);
void RenderViewToCanvas(AActor *actor, DCanvas *canvas, int x, int y, int width, int height, bool dontmaplines);
void RenderRemainingPlayerSprites();
static PolyRenderer *Instance();
PolyPortalViewpoint SetupPerspectiveMatrix(bool mirror = false);
uint32_t GetNextStencilValue() { uint32_t value = NextStencilValue; NextStencilValue += 2; return value; }
bool DontMapLines = false;
PolyRenderThreads Threads;
DCanvas *RenderTarget = nullptr;
bool RenderToCanvas = false;
FViewWindow Viewwindow;
FRenderViewpoint Viewpoint;
PolyLightVisibility Light;
RenderPolyScene Scene;
FLevelLocals *Level;
private:
void RenderActorView(AActor *actor, bool drawpsprites, bool dontmaplines);
void SetSceneViewport();
RenderPolyPlayerSprites PlayerSprites;
uint32_t NextStencilValue = 0;
};

View file

@ -1,268 +0,0 @@
/*
** Polygon Doom software renderer
** Copyright (c) 2016 Magnus Norddahl
**
** This software is provided 'as-is', without any express or implied
** warranty. In no event will the authors be held liable for any damages
** arising from the use of this software.
**
** Permission is granted to anyone to use this software for any purpose,
** including commercial applications, and to alter it and redistribute it
** freely, subject to the following restrictions:
**
** 1. The origin of this software must not be misrepresented; you must not
** claim that you wrote the original software. If you use this software
** in a product, an acknowledgment in the product documentation would be
** appreciated but is not required.
** 2. Altered source versions must be plainly marked as such, and must not be
** misrepresented as being the original software.
** 3. This notice may not be removed or altered from any source distribution.
**
*/
#include <stdlib.h>
#include "templates.h"
#include "doomdef.h"
#include "m_bbox.h"
#include "p_lnspec.h"
#include "p_setup.h"
#include "a_sharedglobal.h"
#include "g_level.h"
#include "p_effect.h"
#include "doomstat.h"
#include "r_state.h"
#include "v_palette.h"
#include "r_sky.h"
#include "po_man.h"
#include "r_data/colormaps.h"
#include "poly_renderthread.h"
#include "poly_renderer.h"
#include <mutex>
#ifdef WIN32
void PeekThreadedErrorPane();
#endif
EXTERN_CVAR(Int, r_scene_multithreaded);
PolyRenderThread::PolyRenderThread(int threadIndex) : MainThread(threadIndex == 0), ThreadIndex(threadIndex)
{
FrameMemory.reset(new RenderMemory());
DrawQueue = std::make_shared<DrawerCommandQueue>(FrameMemory.get());
}
PolyRenderThread::~PolyRenderThread()
{
}
void PolyRenderThread::FlushDrawQueue()
{
DrawerThreads::Execute(DrawQueue);
UsedDrawQueues.push_back(DrawQueue);
DrawQueue.reset();
if (!FreeDrawQueues.empty())
{
DrawQueue = FreeDrawQueues.back();
FreeDrawQueues.pop_back();
}
else
{
DrawQueue = std::make_shared<DrawerCommandQueue>(FrameMemory.get());
}
}
static std::mutex loadmutex;
void PolyRenderThread::PrepareTexture(FSoftwareTexture *texture, FRenderStyle style)
{
if (texture == nullptr)
return;
// Textures may not have loaded/refreshed yet. The shared code doing
// this is not thread safe. By calling GetPixels in a mutex lock we
// make sure that only one thread is loading a texture at any given
// time.
//
// It is critical that this function is called before any direct
// calls to GetPixels for this to work.
std::unique_lock<std::mutex> lock(loadmutex);
const FSoftwareTextureSpan *spans;
if (PolyRenderer::Instance()->RenderTarget->IsBgra())
{
texture->GetPixelsBgra();
texture->GetColumnBgra(0, &spans);
}
else
{
bool alpha = !!(style.Flags & STYLEF_RedIsAlpha);
texture->GetPixels(alpha);
texture->GetColumn(alpha, 0, &spans);
}
}
static std::mutex polyobjmutex;
void PolyRenderThread::PreparePolyObject(subsector_t *sub)
{
std::unique_lock<std::mutex> lock(polyobjmutex);
if (sub->BSP == nullptr || sub->BSP->bDirty)
{
sub->BuildPolyBSP();
}
}
/////////////////////////////////////////////////////////////////////////////
PolyRenderThreads::PolyRenderThreads()
{
std::unique_ptr<PolyRenderThread> thread(new PolyRenderThread(0));
Threads.push_back(std::move(thread));
}
PolyRenderThreads::~PolyRenderThreads()
{
StopThreads();
}
void PolyRenderThreads::Clear()
{
for (auto &thread : Threads)
{
thread->FrameMemory->Clear();
thread->DrawQueue->Clear();
while (!thread->UsedDrawQueues.empty())
{
auto queue = thread->UsedDrawQueues.back();
thread->UsedDrawQueues.pop_back();
queue->Clear();
thread->FreeDrawQueues.push_back(queue);
}
}
}
void PolyRenderThreads::RenderThreadSlices(int totalcount, std::function<void(PolyRenderThread *)> workerCallback, std::function<void(PolyRenderThread *)> collectCallback)
{
WorkerCallback = workerCallback;
int numThreads = std::thread::hardware_concurrency();
if (numThreads == 0)
numThreads = 1;
if (r_scene_multithreaded == 0 || r_multithreaded == 0)
numThreads = 1;
else if (r_scene_multithreaded != 1)
numThreads = r_scene_multithreaded;
if (numThreads != (int)Threads.size())
{
StopThreads();
StartThreads(numThreads);
}
// Setup threads:
std::unique_lock<std::mutex> start_lock(start_mutex);
for (int i = 0; i < numThreads; i++)
{
Threads[i]->Start = totalcount * i / numThreads;
Threads[i]->End = totalcount * (i + 1) / numThreads;
}
run_id++;
start_lock.unlock();
// Notify threads to run
if (Threads.size() > 1)
{
start_condition.notify_all();
}
// Do the main thread ourselves:
RenderThreadSlice(MainThread());
// Wait for everyone to finish:
if (Threads.size() > 1)
{
using namespace std::chrono_literals;
std::unique_lock<std::mutex> end_lock(end_mutex);
finished_threads++;
if (!end_condition.wait_for(end_lock, 5s, [&]() { return finished_threads == Threads.size(); }))
{
#ifdef WIN32
PeekThreadedErrorPane();
#endif
// Invoke the crash reporter so that we can capture the call stack of whatever the hung worker thread is doing
int *threadCrashed = nullptr;
*threadCrashed = 0xdeadbeef;
}
finished_threads = 0;
}
for (int i = 0; i < numThreads; i++)
{
Threads[i]->FlushDrawQueue();
}
WorkerCallback = {};
for (int i = 1; i < numThreads; i++)
{
collectCallback(Threads[i].get());
}
}
void PolyRenderThreads::RenderThreadSlice(PolyRenderThread *thread)
{
WorkerCallback(thread);
}
void PolyRenderThreads::StartThreads(size_t numThreads)
{
while (Threads.size() < (size_t)numThreads)
{
std::unique_ptr<PolyRenderThread> thread(new PolyRenderThread((int)Threads.size()));
auto renderthread = thread.get();
int start_run_id = run_id;
thread->thread = std::thread([=]()
{
int last_run_id = start_run_id;
while (true)
{
// Wait until we are signalled to run:
std::unique_lock<std::mutex> start_lock(start_mutex);
start_condition.wait(start_lock, [&]() { return run_id != last_run_id || shutdown_flag; });
if (shutdown_flag)
break;
last_run_id = run_id;
start_lock.unlock();
RenderThreadSlice(renderthread);
// Notify main thread that we finished:
std::unique_lock<std::mutex> end_lock(end_mutex);
finished_threads++;
end_lock.unlock();
end_condition.notify_all();
}
});
Threads.push_back(std::move(thread));
}
}
void PolyRenderThreads::StopThreads()
{
std::unique_lock<std::mutex> lock(start_mutex);
shutdown_flag = true;
lock.unlock();
start_condition.notify_all();
while (Threads.size() > 1)
{
Threads.back()->thread.join();
Threads.pop_back();
}
lock.lock();
shutdown_flag = false;
}

View file

@ -1,102 +0,0 @@
/*
** Polygon Doom software renderer
** Copyright (c) 2016 Magnus Norddahl
**
** This software is provided 'as-is', without any express or implied
** warranty. In no event will the authors be held liable for any damages
** arising from the use of this software.
**
** Permission is granted to anyone to use this software for any purpose,
** including commercial applications, and to alter it and redistribute it
** freely, subject to the following restrictions:
**
** 1. The origin of this software must not be misrepresented; you must not
** claim that you wrote the original software. If you use this software
** in a product, an acknowledgment in the product documentation would be
** appreciated but is not required.
** 2. Altered source versions must be plainly marked as such, and must not be
** misrepresented as being the original software.
** 3. This notice may not be removed or altered from any source distribution.
**
*/
#pragma once
#include <memory>
#include <thread>
#include "swrenderer/r_memory.h"
class DrawerCommandQueue;
typedef std::shared_ptr<DrawerCommandQueue> DrawerCommandQueuePtr;
class RenderMemory;
class PolyTranslucentObject;
class PolyDrawSectorPortal;
class PolyDrawLinePortal;
struct FDynamicLight;
class PolyRenderThread
{
public:
PolyRenderThread(int threadIndex);
~PolyRenderThread();
void FlushDrawQueue();
int Start = 0;
int End = 0;
bool MainThread = false;
int ThreadIndex = 0;
std::unique_ptr<RenderMemory> FrameMemory;
DrawerCommandQueuePtr DrawQueue;
std::vector<PolyTranslucentObject *> TranslucentObjects;
std::vector<std::unique_ptr<PolyDrawSectorPortal>> SectorPortals;
std::vector<std::unique_ptr<PolyDrawLinePortal>> LinePortals;
TArray<FDynamicLight*> AddedLightsArray;
// Make sure texture can accessed safely
void PrepareTexture(FSoftwareTexture *texture, FRenderStyle style);
// Setup poly object in a threadsafe manner
void PreparePolyObject(subsector_t *sub);
private:
std::thread thread;
std::vector<DrawerCommandQueuePtr> UsedDrawQueues;
std::vector<DrawerCommandQueuePtr> FreeDrawQueues;
friend class PolyRenderThreads;
};
class PolyRenderThreads
{
public:
PolyRenderThreads();
~PolyRenderThreads();
void Clear();
void RenderThreadSlices(int totalcount, std::function<void(PolyRenderThread *)> workerCallback, std::function<void(PolyRenderThread *)> collectCallback);
PolyRenderThread *MainThread() { return Threads.front().get(); }
int NumThreads() const { return (int)Threads.size(); }
std::vector<std::unique_ptr<PolyRenderThread>> Threads;
private:
void RenderThreadSlice(PolyRenderThread *thread);
void StartThreads(size_t numThreads);
void StopThreads();
std::function<void(PolyRenderThread *)> WorkerCallback;
std::mutex start_mutex;
std::condition_variable start_condition;
bool shutdown_flag = false;
int run_id = 0;
std::mutex end_mutex;
std::condition_variable end_condition;
size_t finished_threads = 0;
};

View file

@ -1,421 +0,0 @@
/*
** Polygon Doom software renderer
** Copyright (c) 2016 Magnus Norddahl
**
** This software is provided 'as-is', without any express or implied
** warranty. In no event will the authors be held liable for any damages
** arising from the use of this software.
**
** Permission is granted to anyone to use this software for any purpose,
** including commercial applications, and to alter it and redistribute it
** freely, subject to the following restrictions:
**
** 1. The origin of this software must not be misrepresented; you must not
** claim that you wrote the original software. If you use this software
** in a product, an acknowledgment in the product documentation would be
** appreciated but is not required.
** 2. Altered source versions must be plainly marked as such, and must not be
** misrepresented as being the original software.
** 3. This notice may not be removed or altered from any source distribution.
**
*/
#include <stdlib.h>
#include "templates.h"
#include "doomdef.h"
#include "sbar.h"
#include "r_data/r_translate.h"
#include "poly_cull.h"
#include "polyrenderer/poly_renderer.h"
void PolyCull::CullScene(sector_t *portalSector, line_t *portalLine)
{
for (uint32_t sub : PvsSubsectors)
SubsectorDepths[sub] = 0xffffffff;
SubsectorDepths.resize(PolyRenderer::Instance()->Level->subsectors.Size(), 0xffffffff);
for (uint32_t sector : SeenSectors)
SectorSeen[sector] = false;
SectorSeen.resize(PolyRenderer::Instance()->Level->sectors.Size());
PvsSubsectors.clear();
SeenSectors.clear();
NextPvsLineStart = 0;
PvsLineStart.clear();
PvsLineVisible.resize(PolyRenderer::Instance()->Level->segs.Size());
PortalSector = portalSector;
PortalLine = portalLine;
SolidSegments.clear();
if (portalLine)
{
DVector3 viewpos = PolyRenderer::Instance()->Viewpoint.Pos;
DVector2 pt1 = portalLine->v1->fPos() - viewpos;
DVector2 pt2 = portalLine->v2->fPos() - viewpos;
if (pt1.Y * (pt1.X - pt2.X) + pt1.X * (pt2.Y - pt1.Y) >= 0)
{
angle_t angle1 = PointToPseudoAngle(portalLine->v1->fX(), portalLine->v1->fY());
angle_t angle2 = PointToPseudoAngle(portalLine->v2->fX(), portalLine->v2->fY());
MarkSegmentCulled(angle1, angle2);
}
else
{
angle_t angle2 = PointToPseudoAngle(portalLine->v1->fX(), portalLine->v1->fY());
angle_t angle1 = PointToPseudoAngle(portalLine->v2->fX(), portalLine->v2->fY());
MarkSegmentCulled(angle1, angle2);
}
InvertSegments();
}
else
{
MarkViewFrustum();
}
// Cull front to back
FirstSkyHeight = true;
MaxCeilingHeight = 0.0;
MinFloorHeight = 0.0;
if (PolyRenderer::Instance()->Level->nodes.Size() == 0)
CullSubsector(&PolyRenderer::Instance()->Level->subsectors[0]);
else
CullNode(PolyRenderer::Instance()->Level->HeadNode());
}
void PolyCull::CullNode(void *node)
{
while (!((size_t)node & 1)) // Keep going until found a subsector
{
node_t *bsp = (node_t *)node;
// Decide which side the view point is on.
int side = PointOnSide(PolyRenderer::Instance()->Viewpoint.Pos, bsp);
// Recursively divide front space (toward the viewer).
CullNode(bsp->children[side]);
// Possibly divide back space (away from the viewer).
side ^= 1;
if (!CheckBBox(bsp->bbox[side]))
return;
node = bsp->children[side];
}
subsector_t *sub = (subsector_t *)((uint8_t *)node - 1);
CullSubsector(sub);
}
void PolyCull::CullSubsector(subsector_t *sub)
{
// Ignore everything in front of the portal
if (PortalSector)
{
if (sub->sector != PortalSector)
return;
PortalSector = nullptr;
}
// Update sky heights for the scene
if (!FirstSkyHeight)
{
MaxCeilingHeight = MAX(MaxCeilingHeight, sub->sector->ceilingplane.Zat0());
MinFloorHeight = MIN(MinFloorHeight, sub->sector->floorplane.Zat0());
}
else
{
MaxCeilingHeight = sub->sector->ceilingplane.Zat0();
MinFloorHeight = sub->sector->floorplane.Zat0();
FirstSkyHeight = false;
}
uint32_t subsectorDepth = (uint32_t)PvsSubsectors.size();
// Mark that we need to render this
PvsSubsectors.push_back(sub->Index());
PvsLineStart.push_back(NextPvsLineStart);
DVector3 viewpos = PolyRenderer::Instance()->Viewpoint.Pos;
// Update culling info for further bsp clipping
for (uint32_t i = 0; i < sub->numlines; i++)
{
seg_t *line = &sub->firstline[i];
// Skip lines not facing viewer
DVector2 pt1 = line->v1->fPos() - viewpos;
DVector2 pt2 = line->v2->fPos() - viewpos;
if (pt1.Y * (pt1.X - pt2.X) + pt1.X * (pt2.Y - pt1.Y) >= 0)
{
PvsLineVisible[NextPvsLineStart++] = false;
continue;
}
// Do not draw the portal line
if (line->linedef == PortalLine)
{
PvsLineVisible[NextPvsLineStart++] = false;
continue;
}
angle_t angle2 = PointToPseudoAngle(line->v1->fX(), line->v1->fY());
angle_t angle1 = PointToPseudoAngle(line->v2->fX(), line->v2->fY());
bool lineVisible = !IsSegmentCulled(angle1, angle2);
if (lineVisible && IsSolidLine(line))
{
MarkSegmentCulled(angle1, angle2);
}
// Mark if this line was visible
PvsLineVisible[NextPvsLineStart++] = lineVisible;
}
if (!SectorSeen[sub->sector->Index()])
{
SectorSeen[sub->sector->Index()] = true;
SeenSectors.push_back(sub->sector->Index());
}
SubsectorDepths[sub->Index()] = subsectorDepth;
}
bool PolyCull::IsSolidLine(seg_t *line)
{
// One-sided
if (!line->backsector) return true;
// Portal
if (line->linedef && line->linedef->isVisualPortal() && line->sidedef == line->linedef->sidedef[0]) return true;
double frontCeilingZ1 = line->frontsector->ceilingplane.ZatPoint(line->v1);
double frontFloorZ1 = line->frontsector->floorplane.ZatPoint(line->v1);
double frontCeilingZ2 = line->frontsector->ceilingplane.ZatPoint(line->v2);
double frontFloorZ2 = line->frontsector->floorplane.ZatPoint(line->v2);
double backCeilingZ1 = line->backsector->ceilingplane.ZatPoint(line->v1);
double backFloorZ1 = line->backsector->floorplane.ZatPoint(line->v1);
double backCeilingZ2 = line->backsector->ceilingplane.ZatPoint(line->v2);
double backFloorZ2 = line->backsector->floorplane.ZatPoint(line->v2);
// Closed door.
if (backCeilingZ1 <= frontFloorZ1 && backCeilingZ2 <= frontFloorZ2) return true;
if (backFloorZ1 >= frontCeilingZ1 && backFloorZ2 >= frontCeilingZ2) return true;
// properly render skies (consider door "open" if both ceilings are sky)
if (line->backsector->GetTexture(sector_t::ceiling) == skyflatnum && line->frontsector->GetTexture(sector_t::ceiling) == skyflatnum) return false;
// if door is closed because back is shut:
if (!(backCeilingZ1 <= backFloorZ1 && backCeilingZ2 <= backFloorZ2)) return false;
// preserve a kind of transparent door/lift special effect:
if (((backCeilingZ1 >= frontCeilingZ1 && backCeilingZ2 >= frontCeilingZ2) || line->sidedef->GetTexture(side_t::top).isValid())
&& ((backFloorZ1 <= frontFloorZ1 && backFloorZ2 <= frontFloorZ2) || line->sidedef->GetTexture(side_t::bottom).isValid()))
{
// killough 1/18/98 -- This function is used to fix the automap bug which
// showed lines behind closed doors simply because the door had a dropoff.
//
// It assumes that Doom has already ruled out a door being closed because
// of front-back closure (e.g. front floor is taller than back ceiling).
// This fixes the automap floor height bug -- killough 1/18/98:
// killough 4/7/98: optimize: save result in doorclosed for use in r_segs.c
return true;
}
return false;
}
bool PolyCull::IsSegmentCulled(angle_t startAngle, angle_t endAngle) const
{
if (startAngle > endAngle)
{
return IsSegmentCulled(startAngle, ANGLE_MAX) && IsSegmentCulled(0, endAngle);
}
for (const auto &segment : SolidSegments)
{
if (startAngle >= segment.Start && endAngle <= segment.End)
return true;
else if (endAngle < segment.Start)
return false;
}
return false;
}
void PolyCull::MarkSegmentCulled(angle_t startAngle, angle_t endAngle)
{
if (startAngle > endAngle)
{
MarkSegmentCulled(startAngle, ANGLE_MAX);
MarkSegmentCulled(0, endAngle);
return;
}
int count = (int)SolidSegments.size();
int cur = 0;
while (cur < count)
{
if (SolidSegments[cur].Start <= startAngle && SolidSegments[cur].End >= endAngle) // Already fully marked
{
return;
}
else if (SolidSegments[cur].End >= startAngle && SolidSegments[cur].Start <= endAngle) // Merge segments
{
// Find last segment
int merge = cur;
while (merge + 1 != count && SolidSegments[merge + 1].Start <= endAngle)
merge++;
// Apply new merged range
SolidSegments[cur].Start = MIN(SolidSegments[cur].Start, startAngle);
SolidSegments[cur].End = MAX(SolidSegments[merge].End, endAngle);
// Remove additional segments we merged with
if (merge > cur)
SolidSegments.erase(SolidSegments.begin() + (cur + 1), SolidSegments.begin() + (merge + 1));
return;
}
else if (SolidSegments[cur].Start > startAngle) // Insert new segment
{
SolidSegments.insert(SolidSegments.begin() + cur, { startAngle, endAngle });
return;
}
cur++;
}
SolidSegments.push_back({ startAngle, endAngle });
#if 0
count = (int)SolidSegments.size();
for (int i = 1; i < count; i++)
{
if (SolidSegments[i - 1].Start >= SolidSegments[i].Start ||
SolidSegments[i - 1].End >= SolidSegments[i].Start ||
SolidSegments[i - 1].End + 1 == SolidSegments[i].Start ||
SolidSegments[i].Start > SolidSegments[i].End)
{
I_FatalError("MarkSegmentCulled is broken!");
}
}
#endif
}
int PolyCull::PointOnSide(const DVector2 &pos, const node_t *node)
{
return DMulScale32(FLOAT2FIXED(pos.Y) - node->y, node->dx, node->x - FLOAT2FIXED(pos.X), node->dy) > 0;
}
bool PolyCull::CheckBBox(float *bspcoord)
{
// Occlusion test using solid segments:
static const uint8_t checkcoord[12][4] =
{
{ 3,0,2,1 },
{ 3,0,2,0 },
{ 3,1,2,0 },
{ 0 },
{ 2,0,2,1 },
{ 0,0,0,0 },
{ 3,1,3,0 },
{ 0 },
{ 2,0,3,1 },
{ 2,1,3,1 },
{ 2,1,3,0 }
};
// Find the corners of the box that define the edges from current viewpoint.
const auto &viewpoint = PolyRenderer::Instance()->Viewpoint;
int boxpos = (viewpoint.Pos.X <= bspcoord[BOXLEFT] ? 0 : viewpoint.Pos.X < bspcoord[BOXRIGHT] ? 1 : 2) +
(viewpoint.Pos.Y >= bspcoord[BOXTOP] ? 0 : viewpoint.Pos.Y > bspcoord[BOXBOTTOM] ? 4 : 8);
if (boxpos == 5) return true;
const uint8_t *check = checkcoord[boxpos];
angle_t angle1 = PointToPseudoAngle(bspcoord[check[0]], bspcoord[check[1]]);
angle_t angle2 = PointToPseudoAngle(bspcoord[check[2]], bspcoord[check[3]]);
return !IsSegmentCulled(angle2, angle1);
}
void PolyCull::InvertSegments()
{
TempInvertSolidSegments.swap(SolidSegments);
SolidSegments.clear();
angle_t cur = 0;
for (const auto &segment : TempInvertSolidSegments)
{
if (cur < segment.Start)
MarkSegmentCulled(cur, segment.Start - 1);
if (segment.End == ANGLE_MAX)
return;
cur = segment.End + 1;
}
MarkSegmentCulled(cur, ANGLE_MAX);
}
void PolyCull::MarkViewFrustum()
{
// Clips things outside the viewing frustum.
auto &viewpoint = PolyRenderer::Instance()->Viewpoint;
auto &viewwindow = PolyRenderer::Instance()->Viewwindow;
double tilt = fabs(viewpoint.Angles.Pitch.Degrees);
if (tilt > 46.0) // If the pitch is larger than this you can look all around
return;
double floatangle = 2.0 + (45.0 + ((tilt / 1.9)))*viewpoint.FieldOfView.Degrees*48.0 / AspectMultiplier(viewwindow.WidescreenRatio) / 90.0;
angle_t a1 = DAngle(floatangle).BAMs();
if (a1 < ANGLE_180)
{
MarkSegmentCulled(AngleToPseudo(viewpoint.Angles.Yaw.BAMs() + a1), AngleToPseudo(viewpoint.Angles.Yaw.BAMs() - a1));
}
}
//-----------------------------------------------------------------------------
//
// ! Returns the pseudoangle between the line p1 to (infinity, p1.y) and the
// line from p1 to p2. The pseudoangle has the property that the ordering of
// points by true angle around p1 and ordering of points by pseudoangle are the
// same.
//
// For clipping exact angles are not needed. Only the ordering matters.
// This is about as fast as the fixed point R_PointToAngle2 but without
// the precision issues associated with that function.
//
//-----------------------------------------------------------------------------
angle_t PolyCull::PointToPseudoAngle(double x, double y)
{
const auto &viewpoint = PolyRenderer::Instance()->Viewpoint;
double vecx = x - viewpoint.Pos.X;
double vecy = y - viewpoint.Pos.Y;
if (vecx == 0 && vecy == 0)
{
return 0;
}
else
{
double result = vecy / (fabs(vecx) + fabs(vecy));
if (vecx < 0)
{
result = 2. - result;
}
return xs_Fix<30>::ToFix(result);
}
}
angle_t PolyCull::AngleToPseudo(angle_t ang)
{
double vecx = cos(ang * M_PI / ANGLE_180);
double vecy = sin(ang * M_PI / ANGLE_180);
double result = vecy / (fabs(vecx) + fabs(vecy));
if (vecx < 0)
{
result = 2.f - result;
}
return xs_Fix<30>::ToFix(result);
}

View file

@ -1,86 +0,0 @@
/*
** Polygon Doom software renderer
** Copyright (c) 2016 Magnus Norddahl
**
** This software is provided 'as-is', without any express or implied
** warranty. In no event will the authors be held liable for any damages
** arising from the use of this software.
**
** Permission is granted to anyone to use this software for any purpose,
** including commercial applications, and to alter it and redistribute it
** freely, subject to the following restrictions:
**
** 1. The origin of this software must not be misrepresented; you must not
** claim that you wrote the original software. If you use this software
** in a product, an acknowledgment in the product documentation would be
** appreciated but is not required.
** 2. Altered source versions must be plainly marked as such, and must not be
** misrepresented as being the original software.
** 3. This notice may not be removed or altered from any source distribution.
**
*/
#pragma once
#include "polyrenderer/drawers/poly_triangle.h"
#include <set>
#include <unordered_map>
class PolyCull
{
public:
void CullScene(sector_t *portalSector, line_t *portalLine);
bool IsLineSegVisible(uint32_t subsectorDepth, uint32_t lineIndex)
{
return PvsLineVisible[PvsLineStart[subsectorDepth] + lineIndex];
}
std::vector<uint32_t> PvsSubsectors;
double MaxCeilingHeight = 0.0;
double MinFloorHeight = 0.0;
std::vector<uint32_t> SeenSectors;
std::vector<bool> SectorSeen;
std::vector<uint32_t> SubsectorDepths;
static angle_t PointToPseudoAngle(double x, double y);
private:
struct SolidSegment
{
SolidSegment(angle_t start, angle_t end) : Start(start), End(end) { }
angle_t Start, End;
};
void MarkViewFrustum();
void InvertSegments();
static bool IsSolidLine(seg_t *line);
bool IsSegmentCulled(angle_t angle1, angle_t angle2) const;
void CullNode(void *node);
void CullSubsector(subsector_t *sub);
int PointOnSide(const DVector2 &pos, const node_t *node);
// Checks BSP node/subtree bounding box.
// Returns true if some part of the bbox might be visible.
bool CheckBBox(float *bspcoord);
void MarkSegmentCulled(angle_t angle1, angle_t angle2);
std::vector<SolidSegment> SolidSegments;
std::vector<SolidSegment> TempInvertSolidSegments;
std::vector<SolidSegment> PortalVisibility;
bool FirstSkyHeight = true;
sector_t *PortalSector = nullptr;
line_t *PortalLine = nullptr;
std::vector<uint32_t> PvsLineStart;
std::vector<bool> PvsLineVisible;
uint32_t NextPvsLineStart = 0;
static angle_t AngleToPseudo(angle_t ang);
};

View file

@ -1,284 +0,0 @@
/*
** Polygon Doom software renderer
** Copyright (c) 2016 Magnus Norddahl
**
** This software is provided 'as-is', without any express or implied
** warranty. In no event will the authors be held liable for any damages
** arising from the use of this software.
**
** Permission is granted to anyone to use this software for any purpose,
** including commercial applications, and to alter it and redistribute it
** freely, subject to the following restrictions:
**
** 1. The origin of this software must not be misrepresented; you must not
** claim that you wrote the original software. If you use this software
** in a product, an acknowledgment in the product documentation would be
** appreciated but is not required.
** 2. Altered source versions must be plainly marked as such, and must not be
** misrepresented as being the original software.
** 3. This notice may not be removed or altered from any source distribution.
**
*/
#include <stdlib.h>
#include "templates.h"
#include "doomdef.h"
#include "sbar.h"
#include "r_data/r_translate.h"
#include "poly_decal.h"
#include "polyrenderer/poly_renderer.h"
#include "polyrenderer/scene/poly_light.h"
#include "polyrenderer/poly_renderthread.h"
#include "a_sharedglobal.h"
#include "swrenderer/scene/r_scene.h"
void RenderPolyDecal::RenderWallDecals(PolyRenderThread *thread, const seg_t *line, uint32_t stencilValue)
{
if (line->linedef == nullptr && line->sidedef == nullptr)
return;
for (DBaseDecal *decal = line->sidedef->AttachedDecals; decal != nullptr; decal = decal->WallNext)
{
RenderPolyDecal render;
render.Render(thread, decal, line, stencilValue);
}
}
void RenderPolyDecal::Render(PolyRenderThread *thread, DBaseDecal *decal, const seg_t *line, uint32_t stencilValue)
{
if (decal->RenderFlags & RF_INVISIBLE || !viewactive || !decal->PicNum.isValid())
return;
FTexture *ttex = TexMan.GetPalettedTexture(decal->PicNum, true);
if (ttex == nullptr || !ttex->isValid())
return;
FSoftwareTexture *tex = ttex->GetSoftwareTexture();
sector_t *front, *back;
GetDecalSectors(decal, line, &front, &back);
// Calculate unclipped position and UV coordinates
// decals should not use renderer specific offsets.
double edge_left = tex->GetLeftOffset(0) * decal->ScaleX;
double edge_right = (tex->GetWidth() - tex->GetLeftOffset(0)) * decal->ScaleX;
DVector2 angvec = (line->v2->fPos() - line->v1->fPos()).Unit();
DVector2 normal = { angvec.Y, -angvec.X };
double dcx, dcy;
decal->GetXY(line->sidedef, dcx, dcy);
DVector2 decal_pos = DVector2(dcx, dcy) + normal;
DVector2 decal_left = decal_pos - edge_left * angvec;
DVector2 decal_right = decal_pos + edge_right * angvec;
bool flipTextureX = (decal->RenderFlags & RF_XFLIP) == RF_XFLIP;
double u_left = flipTextureX ? 1.0 : 0.0;
double u_right = flipTextureX ? 1.0 - tex->GetScale().X : tex->GetScale().X;
double u_unit = (u_right - u_left) / (edge_left + edge_right);
double zpos = GetDecalZ(decal, line, front, back);
double spriteHeight = decal->ScaleY / tex->GetScale().Y * tex->GetHeight();
double ztop = zpos + spriteHeight - spriteHeight * 0.5;
double zbottom = zpos - spriteHeight * 0.5;
double v_top = 0.0;
double v_bottom = tex->GetScale().Y;
double v_unit = (v_bottom - v_top) / (zbottom - ztop);
// Clip decal to wall part
double walltopz, wallbottomz;
GetWallZ(decal, line, front, back, walltopz, wallbottomz);
double clip_left_v1 = (decal_left - line->v1->fPos()) | angvec;
double clip_right_v1 = (decal_right - line->v1->fPos()) | angvec;
double clip_left_v2 = (decal_left - line->v2->fPos()) | angvec;
double clip_right_v2 = (decal_right - line->v2->fPos()) | angvec;
if ((clip_left_v1 <= 0.0 && clip_right_v1 <= 0.0) || (clip_left_v2 >= 0.0 && clip_right_v2 >= 0.0))
return;
if (clip_left_v1 < 0.0)
{
decal_left -= angvec * clip_left_v1;
u_left -= u_unit * clip_left_v1;
}
if (clip_right_v1 < 0.0)
{
decal_right -= angvec * clip_right_v1;
u_right -= u_unit * clip_right_v1;
}
if (clip_left_v2 > 0.0)
{
decal_left -= angvec * clip_left_v2;
u_left -= u_unit * clip_left_v2;
}
if (clip_right_v2 > 0.0)
{
decal_right -= angvec * clip_right_v2;
u_right -= u_unit * clip_right_v2;
}
double clip_top_floor = ztop - wallbottomz;
double clip_bottom_floor = zbottom - wallbottomz;
double clip_top_ceiling = ztop - walltopz;
double clip_bottom_ceiling = zbottom - walltopz;
if ((clip_top_floor <= 0.0 && clip_bottom_floor <= 0.0) || (clip_top_ceiling >= 0.0 && clip_bottom_ceiling >= 0.0))
return;
if (clip_top_floor < 0.0)
{
ztop -= clip_top_floor;
v_top -= v_unit * clip_top_floor;
}
if (clip_bottom_floor < 0.0)
{
zbottom -= clip_bottom_floor;
v_bottom -= v_unit * clip_bottom_floor;
}
if (clip_top_ceiling > 0.0)
{
ztop -= clip_top_ceiling;
v_top -= v_unit * clip_top_ceiling;
}
if (clip_bottom_ceiling > 0.0)
{
zbottom -= clip_bottom_ceiling;
v_bottom -= v_unit * clip_bottom_ceiling;
}
// Generate vertices for the decal
TriVertex *vertices = thread->FrameMemory->AllocMemory<TriVertex>(4);
vertices[0].x = (float)decal_left.X;
vertices[0].y = (float)decal_left.Y;
vertices[0].z = (float)ztop;
vertices[0].w = 1.0f;
vertices[0].u = (float)u_left;
vertices[0].v = 1.0f - (float)v_top;
vertices[1].x = (float)decal_right.X;
vertices[1].y = (float)decal_right.Y;
vertices[1].z = (float)ztop;
vertices[1].w = 1.0f;
vertices[1].u = (float)u_right;
vertices[1].v = 1.0f - (float)v_top;
vertices[2].x = (float)decal_right.X;
vertices[2].y = (float)decal_right.Y;
vertices[2].z = (float)zbottom;
vertices[2].w = 1.0f;
vertices[2].u = (float)u_right;
vertices[2].v = 1.0f - (float)v_bottom;
vertices[3].x = (float)decal_left.X;
vertices[3].y = (float)decal_left.Y;
vertices[3].z = (float)zbottom;
vertices[3].w = 1.0f;
vertices[3].u = (float)u_left;
vertices[3].v = 1.0f - (float)v_bottom;
// Light calculations
bool foggy = false;
int actualextralight = foggy ? 0 : PolyRenderer::Instance()->Viewpoint.extralight << 4;
bool fullbrightSprite = (decal->RenderFlags & RF_FULLBRIGHT) == RF_FULLBRIGHT;
int lightlevel = fullbrightSprite ? 255 : front->lightlevel + actualextralight;
PolyDrawArgs args;
args.SetLight(GetColorTable(front->Colormap), lightlevel, PolyRenderer::Instance()->Light.WallGlobVis(foggy), fullbrightSprite);
args.SetColor(0xff000000 | decal->AlphaColor, decal->AlphaColor >> 24);
args.SetStyle(decal->RenderStyle, decal->Alpha, decal->AlphaColor, decal->Translation, tex, false);
args.SetStencilTestValue(stencilValue);
args.SetDepthTest(true);
args.SetWriteStencil(false);
args.SetWriteDepth(false);
PolyTriangleDrawer::DrawArray(thread->DrawQueue, args, vertices, 4, PolyDrawMode::TriangleFan);
}
void RenderPolyDecal::GetDecalSectors(DBaseDecal *decal, const seg_t *line, sector_t **front, sector_t **back)
{
// for 3d-floor segments use the model sector as reference
if ((decal->RenderFlags&RF_CLIPMASK) == RF_CLIPMID)
*front = decal->Sector;
else
*front = line->frontsector;
*back = (line->backsector != nullptr) ? line->backsector : line->frontsector;
}
double RenderPolyDecal::GetDecalZ(DBaseDecal *decal, const seg_t *line, sector_t *front, sector_t *back)
{
switch (decal->RenderFlags & RF_RELMASK)
{
default:
return decal->Z;
case RF_RELUPPER:
if (line->linedef->flags & ML_DONTPEGTOP)
return decal->Z + front->GetPlaneTexZ(sector_t::ceiling);
else
return decal->Z + back->GetPlaneTexZ(sector_t::ceiling);
case RF_RELLOWER:
if (line->linedef->flags & ML_DONTPEGBOTTOM)
return decal->Z + front->GetPlaneTexZ(sector_t::ceiling);
else
return decal->Z + back->GetPlaneTexZ(sector_t::floor);
break;
case RF_RELMID:
if (line->linedef->flags & ML_DONTPEGBOTTOM)
return decal->Z + front->GetPlaneTexZ(sector_t::floor);
else
return decal->Z + front->GetPlaneTexZ(sector_t::ceiling);
}
}
void RenderPolyDecal::GetWallZ(DBaseDecal *decal, const seg_t *line, sector_t *front, sector_t *back, double &walltopz, double &wallbottomz)
{
double frontceilz1 = front->ceilingplane.ZatPoint(line->v1);
double frontfloorz1 = front->floorplane.ZatPoint(line->v1);
double frontceilz2 = front->ceilingplane.ZatPoint(line->v2);
double frontfloorz2 = front->floorplane.ZatPoint(line->v2);
if (back == nullptr)
{
walltopz = MAX(frontceilz1, frontceilz2);
wallbottomz = MIN(frontfloorz1, frontfloorz2);
}
else
{
double backceilz1 = back->ceilingplane.ZatPoint(line->v1);
double backfloorz1 = back->floorplane.ZatPoint(line->v1);
double backceilz2 = back->ceilingplane.ZatPoint(line->v2);
double backfloorz2 = back->floorplane.ZatPoint(line->v2);
double topceilz1 = frontceilz1;
double topceilz2 = frontceilz2;
double topfloorz1 = MAX(MIN(backceilz1, frontceilz1), frontfloorz1);
double topfloorz2 = MAX(MIN(backceilz2, frontceilz2), frontfloorz2);
double bottomceilz1 = MIN(MAX(frontfloorz1, backfloorz1), frontceilz1);
double bottomceilz2 = MIN(MAX(frontfloorz2, backfloorz2), frontceilz2);
double bottomfloorz1 = frontfloorz1;
double bottomfloorz2 = frontfloorz2;
double middleceilz1 = topfloorz1;
double middleceilz2 = topfloorz2;
double middlefloorz1 = MIN(bottomceilz1, middleceilz1);
double middlefloorz2 = MIN(bottomceilz2, middleceilz2);
switch (decal->RenderFlags & RF_RELMASK)
{
default:
walltopz = MAX(frontceilz1, frontceilz2);
wallbottomz = MIN(frontfloorz1, frontfloorz2);
break;
case RF_RELUPPER:
walltopz = MAX(topceilz1, topceilz2);
wallbottomz = MIN(topfloorz1, topfloorz2);
break;
case RF_RELLOWER:
walltopz = MAX(bottomceilz1, bottomceilz2);
wallbottomz = MIN(bottomfloorz1, bottomfloorz2);
break;
case RF_RELMID:
walltopz = MAX(middleceilz1, middleceilz2);
wallbottomz = MIN(middlefloorz1, middlefloorz2);
}
}
}

View file

@ -1,38 +0,0 @@
/*
** Polygon Doom software renderer
** Copyright (c) 2016 Magnus Norddahl
**
** This software is provided 'as-is', without any express or implied
** warranty. In no event will the authors be held liable for any damages
** arising from the use of this software.
**
** Permission is granted to anyone to use this software for any purpose,
** including commercial applications, and to alter it and redistribute it
** freely, subject to the following restrictions:
**
** 1. The origin of this software must not be misrepresented; you must not
** claim that you wrote the original software. If you use this software
** in a product, an acknowledgment in the product documentation would be
** appreciated but is not required.
** 2. Altered source versions must be plainly marked as such, and must not be
** misrepresented as being the original software.
** 3. This notice may not be removed or altered from any source distribution.
**
*/
#pragma once
#include "polyrenderer/drawers/poly_triangle.h"
class RenderPolyDecal
{
public:
static void RenderWallDecals(PolyRenderThread *thread, const seg_t *line, uint32_t stencilValue);
private:
void Render(PolyRenderThread *thread, DBaseDecal *decal, const seg_t *line, uint32_t stencilValue);
void GetDecalSectors(DBaseDecal *decal, const seg_t *line, sector_t **front, sector_t **back);
double GetDecalZ(DBaseDecal *decal, const seg_t *line, sector_t *front, sector_t *back);
void GetWallZ(DBaseDecal *decal, const seg_t *line, sector_t *front, sector_t *back, double &walltopz, double &wallbottomz);
};

View file

@ -1,49 +0,0 @@
/*
** Polygon Doom software renderer
** Copyright (c) 2016 Magnus Norddahl
**
** This software is provided 'as-is', without any express or implied
** warranty. In no event will the authors be held liable for any damages
** arising from the use of this software.
**
** Permission is granted to anyone to use this software for any purpose,
** including commercial applications, and to alter it and redistribute it
** freely, subject to the following restrictions:
**
** 1. The origin of this software must not be misrepresented; you must not
** claim that you wrote the original software. If you use this software
** in a product, an acknowledgment in the product documentation would be
** appreciated but is not required.
** 2. Altered source versions must be plainly marked as such, and must not be
** misrepresented as being the original software.
** 3. This notice may not be removed or altered from any source distribution.
**
*/
#include <stdlib.h>
#include "templates.h"
#include "doomdef.h"
#include "sbar.h"
#include "poly_light.h"
#include "polyrenderer/poly_renderer.h"
void PolyLightVisibility::SetVisibility(FViewWindow &viewwindow, float vis)
{
GlobVis = R_GetGlobVis(viewwindow, vis);
}
fixed_t PolyLightVisibility::LightLevelToShade(int lightlevel, bool foggy)
{
bool nolightfade = !foggy && ((PolyRenderer::Instance()->Level->flags3 & LEVEL3_NOLIGHTFADE));
if (nolightfade)
{
return (MAX(255 - lightlevel, 0) * NUMCOLORMAPS) << (FRACBITS - 8);
}
else
{
// Convert a light level into an unbounded colormap index (shade). Result is
// fixed point. Why the +12? I wish I knew, but experimentation indicates it
// is necessary in order to best reproduce Doom's original lighting.
return (NUMCOLORMAPS * 2 * FRACUNIT) - ((lightlevel + 12) * (FRACUNIT*NUMCOLORMAPS / 128));
}
}

View file

@ -1,52 +0,0 @@
/*
** Polygon Doom software renderer
** Copyright (c) 2016 Magnus Norddahl
**
** This software is provided 'as-is', without any express or implied
** warranty. In no event will the authors be held liable for any damages
** arising from the use of this software.
**
** Permission is granted to anyone to use this software for any purpose,
** including commercial applications, and to alter it and redistribute it
** freely, subject to the following restrictions:
**
** 1. The origin of this software must not be misrepresented; you must not
** claim that you wrote the original software. If you use this software
** in a product, an acknowledgment in the product documentation would be
** appreciated but is not required.
** 2. Altered source versions must be plainly marked as such, and must not be
** misrepresented as being the original software.
** 3. This notice may not be removed or altered from any source distribution.
**
*/
#pragma once
#include "swrenderer/scene/r_light.h"
struct FViewWindow;
// Keep using the software renderer's camera light class, for now.
// The DFrameBuffer abstraction relies on this being globally shared
typedef swrenderer::CameraLight PolyCameraLight;
class PolyLightVisibility
{
public:
void SetVisibility(FViewWindow &viewwindow, float vis);
double WallGlobVis(bool foggy) const { return (NoLightFade && !foggy) ? 0.0 : GlobVis; }
double SpriteGlobVis(bool foggy) const { return (NoLightFade && !foggy) ? 0.0 : GlobVis; }
double ParticleGlobVis(bool foggy) const { return (NoLightFade && !foggy) ? 0.0 : GlobVis * 0.5; }
// The vis value to pass into the GETPALOOKUP or LIGHTSCALE macros
double WallVis(double screenZ, bool foggy) const { return WallGlobVis(foggy) / screenZ; }
double SpriteVis(double screenZ, bool foggy) const { return SpriteGlobVis(foggy) / screenZ; }
double ParticleVis(double screenZ, bool foggy) const { return ParticleGlobVis(foggy) / screenZ; }
static fixed_t LightLevelToShade(int lightlevel, bool foggy);
private:
double GlobVis = 0.0f;
bool NoLightFade = false;
};

View file

@ -1,321 +0,0 @@
/*
** Polygon Doom software renderer
** Copyright (c) 2016 Magnus Norddahl
**
** This software is provided 'as-is', without any express or implied
** warranty. In no event will the authors be held liable for any damages
** arising from the use of this software.
**
** Permission is granted to anyone to use this software for any purpose,
** including commercial applications, and to alter it and redistribute it
** freely, subject to the following restrictions:
**
** 1. The origin of this software must not be misrepresented; you must not
** claim that you wrote the original software. If you use this software
** in a product, an acknowledgment in the product documentation would be
** appreciated but is not required.
** 2. Altered source versions must be plainly marked as such, and must not be
** misrepresented as being the original software.
** 3. This notice may not be removed or altered from any source distribution.
**
*/
#include <stdlib.h>
#include "templates.h"
#include "doomdef.h"
#include "sbar.h"
#include "r_data/r_translate.h"
#include "poly_model.h"
#include "polyrenderer/poly_renderer.h"
#include "polyrenderer/scene/poly_light.h"
#include "polyrenderer/poly_renderthread.h"
#include "r_data/r_vanillatrans.h"
#include "actorinlines.h"
#include "i_time.h"
void PolyRenderModel(PolyRenderThread *thread, const Mat4f &worldToClip, uint32_t stencilValue, float x, float y, float z, FSpriteModelFrame *smf, AActor *actor)
{
PolyModelRenderer renderer(thread, worldToClip, stencilValue);
renderer.sector = actor->Sector;
renderer.RenderStyle = actor->RenderStyle;
renderer.RenderAlpha = (float)actor->Alpha;
if (!renderer.RenderStyle.IsVisible(renderer.RenderAlpha))
return;
bool foggy = false;
int actualextralight = foggy ? 0 : PolyRenderer::Instance()->Viewpoint.extralight << 4;
bool fullbrightSprite = ((actor->renderflags & RF_FULLBRIGHT) || (actor->flags5 & MF5_BRIGHT));
renderer.lightlevel = fullbrightSprite ? 255 : actor->Sector->lightlevel + actualextralight;
renderer.visibility = PolyRenderer::Instance()->Light.SpriteGlobVis(foggy);
renderer.fillcolor = actor->fillcolor;
renderer.Translation = actor->Translation;
renderer.AddLights(actor);
renderer.RenderModel(x, y, z, smf, actor, PolyRenderer::Instance()->Viewpoint.TicFrac);
PolyTriangleDrawer::SetModelVertexShader(thread->DrawQueue, -1, -1, 0.0f);
PolyTriangleDrawer::SetTransform(thread->DrawQueue, thread->FrameMemory->NewObject<Mat4f>(worldToClip), nullptr);
}
static bool isBright(DPSprite *psp)
{
if (psp != nullptr && psp->GetState() != nullptr)
{
bool disablefullbright = false;
FTextureID lump = sprites[psp->GetSprite()].GetSpriteFrame(psp->GetFrame(), 0, 0., nullptr);
if (lump.isValid())
{
FTexture * tex = TexMan.GetPalettedTexture(lump, true);
if (tex) disablefullbright = tex->isFullbrightDisabled();
}
return psp->GetState()->GetFullbright() && !disablefullbright;
}
return false;
}
void PolyRenderHUDModel(PolyRenderThread *thread, const Mat4f &worldToClip, uint32_t stencilValue, DPSprite *psp, float ofsx, float ofsy)
{
PolyModelRenderer renderer(thread, worldToClip, stencilValue);
AActor *playermo = players[consoleplayer].camera;
auto rs = psp->GetRenderStyle(playermo->RenderStyle, playermo->Alpha);
renderer.sector = playermo->Sector;
renderer.RenderStyle = rs.first;
renderer.RenderAlpha = rs.second;
if (psp->Flags & PSPF_FORCEALPHA) renderer.RenderAlpha = 0.0f;
if (!renderer.RenderStyle.IsVisible(renderer.RenderAlpha))
return;
bool foggy = false;
int actualextralight = foggy ? 0 : PolyRenderer::Instance()->Viewpoint.extralight << 4;
bool fullbrightSprite = isBright(psp);
renderer.lightlevel = fullbrightSprite ? 255 : playermo->Sector->lightlevel + actualextralight;
renderer.visibility = PolyRenderer::Instance()->Light.SpriteGlobVis(foggy);
PalEntry ThingColor = (playermo->RenderStyle.Flags & STYLEF_ColorIsFixed) ? playermo->fillcolor : 0xffffff;
ThingColor.a = 255;
renderer.fillcolor = fullbrightSprite ? ThingColor : ThingColor.Modulate(playermo->Sector->SpecialColors[sector_t::sprites]);
renderer.Translation = 0xffffffff;// playermo->Translation;
renderer.RenderHUDModel(psp, ofsx, ofsy);
PolyTriangleDrawer::SetModelVertexShader(thread->DrawQueue, -1, -1, 0.0f);
}
/////////////////////////////////////////////////////////////////////////////
PolyModelRenderer::PolyModelRenderer(PolyRenderThread *thread, const Mat4f &worldToClip, uint32_t stencilValue) : Thread(thread), WorldToClip(worldToClip), StencilValue(stencilValue)
{
}
void PolyModelRenderer::AddLights(AActor *actor)
{
if (r_dynlights && actor)
{
auto &addedLights = Thread->AddedLightsArray;
addedLights.Clear();
float x = (float)actor->X();
float y = (float)actor->Y();
float z = (float)actor->Center();
float actorradius = (float)actor->RenderRadius();
float radiusSquared = actorradius * actorradius;
BSPWalkCircle(actor->Level, x, y, radiusSquared, [&](subsector_t *subsector) // Iterate through all subsectors potentially touched by actor
{
FLightNode * node = subsector->section->lighthead;
while (node) // check all lights touching a subsector
{
FDynamicLight *light = node->lightsource;
if (light->ShouldLightActor(actor))
{
int group = subsector->sector->PortalGroup;
DVector3 pos = light->PosRelative(group);
float radius = (float)(light->GetRadius() + actorradius);
double dx = pos.X - x;
double dy = pos.Y - y;
double dz = pos.Z - z;
double distSquared = dx * dx + dy * dy + dz * dz;
if (distSquared < radius * radius) // Light and actor touches
{
if (std::find(addedLights.begin(), addedLights.end(), light) == addedLights.end()) // Check if we already added this light from a different subsector
{
addedLights.Push(light);
}
}
}
node = node->nextLight;
}
});
NumLights = addedLights.Size();
Lights = Thread->FrameMemory->AllocMemory<PolyLight>(NumLights);
for (int i = 0; i < NumLights; i++)
{
FDynamicLight *lightsource = addedLights[i];
bool is_point_light = lightsource->IsAttenuated();
uint32_t red = lightsource->GetRed();
uint32_t green = lightsource->GetGreen();
uint32_t blue = lightsource->GetBlue();
PolyLight &light = Lights[i];
light.x = (float)lightsource->X();
light.y = (float)lightsource->Y();
light.z = (float)lightsource->Z();
light.radius = 256.0f / lightsource->GetRadius();
light.color = (red << 16) | (green << 8) | blue;
if (is_point_light)
light.radius = -light.radius;
}
}
}
void PolyModelRenderer::BeginDrawModel(AActor *actor, FSpriteModelFrame *smf, const VSMatrix &objectToWorldMatrix, bool mirrored)
{
const_cast<VSMatrix &>(objectToWorldMatrix).copy(ObjectToWorld.Matrix);
SetTransform();
if (actor->RenderStyle == LegacyRenderStyles[STYLE_Normal] || !!(smf->flags & MDL_DONTCULLBACKFACES))
PolyTriangleDrawer::SetTwoSided(Thread->DrawQueue, true);
PolyTriangleDrawer::SetCullCCW(Thread->DrawQueue, !mirrored);
}
void PolyModelRenderer::EndDrawModel(AActor *actor, FSpriteModelFrame *smf)
{
if (actor->RenderStyle == LegacyRenderStyles[STYLE_Normal] || !!(smf->flags & MDL_DONTCULLBACKFACES))
PolyTriangleDrawer::SetTwoSided(Thread->DrawQueue, false);
PolyTriangleDrawer::SetCullCCW(Thread->DrawQueue, true);
}
IModelVertexBuffer *PolyModelRenderer::CreateVertexBuffer(bool needindex, bool singleframe)
{
return new PolyModelVertexBuffer(needindex, singleframe);
}
VSMatrix PolyModelRenderer::GetViewToWorldMatrix()
{
Mat4f swapYZ = Mat4f::Null();
swapYZ.Matrix[0 + 0 * 4] = 1.0f;
swapYZ.Matrix[1 + 2 * 4] = 1.0f;
swapYZ.Matrix[2 + 1 * 4] = 1.0f;
swapYZ.Matrix[3 + 3 * 4] = 1.0f;
VSMatrix worldToView;
worldToView.loadMatrix((PolyRenderer::Instance()->Scene.CurrentViewpoint->WorldToView * swapYZ).Matrix);
VSMatrix objectToWorld;
worldToView.inverseMatrix(objectToWorld);
return objectToWorld;
}
void PolyModelRenderer::BeginDrawHUDModel(AActor *actor, const VSMatrix &objectToWorldMatrix, bool mirrored)
{
const_cast<VSMatrix &>(objectToWorldMatrix).copy(ObjectToWorld.Matrix);
SetTransform();
PolyTriangleDrawer::SetWeaponScene(Thread->DrawQueue, true);
if (actor->RenderStyle == LegacyRenderStyles[STYLE_Normal])
PolyTriangleDrawer::SetTwoSided(Thread->DrawQueue, true);
PolyTriangleDrawer::SetCullCCW(Thread->DrawQueue, mirrored);
}
void PolyModelRenderer::EndDrawHUDModel(AActor *actor)
{
PolyTriangleDrawer::SetWeaponScene(Thread->DrawQueue, false);
if (actor->RenderStyle == LegacyRenderStyles[STYLE_Normal])
PolyTriangleDrawer::SetTwoSided(Thread->DrawQueue, false);
PolyTriangleDrawer::SetCullCCW(Thread->DrawQueue, true);
}
void PolyModelRenderer::SetInterpolation(double interpolation)
{
InterpolationFactor = (float)interpolation;
}
void PolyModelRenderer::SetMaterial(FTexture *skin, bool clampNoFilter, int translation)
{
SkinTexture = skin? skin->GetSoftwareTexture() : nullptr;
}
void PolyModelRenderer::SetTransform()
{
Mat4f swapYZ = Mat4f::Null();
swapYZ.Matrix[0 + 0 * 4] = 1.0f;
swapYZ.Matrix[1 + 2 * 4] = 1.0f;
swapYZ.Matrix[2 + 1 * 4] = 1.0f;
swapYZ.Matrix[3 + 3 * 4] = 1.0f;
ObjectToWorld = swapYZ * ObjectToWorld;
PolyTriangleDrawer::SetTransform(Thread->DrawQueue, Thread->FrameMemory->NewObject<Mat4f>(WorldToClip * ObjectToWorld), Thread->FrameMemory->NewObject<Mat4f>(ObjectToWorld));
}
void PolyModelRenderer::DrawArrays(int start, int count)
{
PolyDrawArgs args;
auto nc = !!(sector->Level->flags3 & LEVEL3_NOCOLOREDSPRITELIGHTING);
args.SetLight(GetSpriteColorTable(sector->Colormap, sector->SpecialColors[sector_t::sprites], nc), lightlevel, visibility, fullbrightSprite); args.SetLights(Lights, NumLights);
args.SetStencilTestValue(StencilValue);
args.SetClipPlane(0, PolyClipPlane());
args.SetStyle(RenderStyle, RenderAlpha, fillcolor, Translation, SkinTexture, fullbrightSprite);
args.SetDepthTest(true);
args.SetWriteDepth(true);
args.SetWriteStencil(false);
PolyTriangleDrawer::DrawArray(Thread->DrawQueue, args, VertexBuffer + start, count);
}
void PolyModelRenderer::DrawElements(int numIndices, size_t offset)
{
PolyDrawArgs args;
auto nc = !!(sector->Level->flags3 & LEVEL3_NOCOLOREDSPRITELIGHTING);
args.SetLight(GetSpriteColorTable(sector->Colormap, sector->SpecialColors[sector_t::sprites], nc), lightlevel, visibility, fullbrightSprite); args.SetLights(Lights, NumLights);
args.SetStencilTestValue(StencilValue);
args.SetClipPlane(0, PolyClipPlane());
args.SetStyle(RenderStyle, RenderAlpha, fillcolor, Translation, SkinTexture, fullbrightSprite);
args.SetDepthTest(true);
args.SetWriteDepth(true);
args.SetWriteStencil(false);
PolyTriangleDrawer::DrawElements(Thread->DrawQueue, args, VertexBuffer, IndexBuffer + offset / sizeof(unsigned int), numIndices);
}
/////////////////////////////////////////////////////////////////////////////
PolyModelVertexBuffer::PolyModelVertexBuffer(bool needindex, bool singleframe)
{
}
PolyModelVertexBuffer::~PolyModelVertexBuffer()
{
}
FModelVertex *PolyModelVertexBuffer::LockVertexBuffer(unsigned int size)
{
mVertexBuffer.Resize(size);
return &mVertexBuffer[0];
}
void PolyModelVertexBuffer::UnlockVertexBuffer()
{
}
unsigned int *PolyModelVertexBuffer::LockIndexBuffer(unsigned int size)
{
mIndexBuffer.Resize(size);
return &mIndexBuffer[0];
}
void PolyModelVertexBuffer::UnlockIndexBuffer()
{
}
void PolyModelVertexBuffer::SetupFrame(FModelRenderer *renderer, unsigned int frame1, unsigned int frame2, unsigned int size)
{
PolyModelRenderer *polyrenderer = (PolyModelRenderer *)renderer;
polyrenderer->VertexBuffer = mVertexBuffer.Size() ? &mVertexBuffer[0] : nullptr;
polyrenderer->IndexBuffer = mIndexBuffer.Size() ? &mIndexBuffer[0] : nullptr;
PolyTriangleDrawer::SetModelVertexShader(polyrenderer->Thread->DrawQueue, frame1, frame2, polyrenderer->InterpolationFactor);
}

View file

@ -1,93 +0,0 @@
/*
** Polygon Doom software renderer
** Copyright (c) 2016 Magnus Norddahl
**
** This software is provided 'as-is', without any express or implied
** warranty. In no event will the authors be held liable for any damages
** arising from the use of this software.
**
** Permission is granted to anyone to use this software for any purpose,
** including commercial applications, and to alter it and redistribute it
** freely, subject to the following restrictions:
**
** 1. The origin of this software must not be misrepresented; you must not
** claim that you wrote the original software. If you use this software
** in a product, an acknowledgment in the product documentation would be
** appreciated but is not required.
** 2. Altered source versions must be plainly marked as such, and must not be
** misrepresented as being the original software.
** 3. This notice may not be removed or altered from any source distribution.
**
*/
#pragma once
#include "polyrenderer/drawers/poly_triangle.h"
#include "r_data/matrix.h"
#include "r_data/models/models.h"
void PolyRenderModel(PolyRenderThread *thread, const Mat4f &worldToClip, uint32_t stencilValue, float x, float y, float z, FSpriteModelFrame *smf, AActor *actor);
void PolyRenderHUDModel(PolyRenderThread *thread, const Mat4f &worldToClip, uint32_t stencilValue, DPSprite *psp, float ofsx, float ofsy);
class PolyModelRenderer : public FModelRenderer
{
public:
PolyModelRenderer(PolyRenderThread *thread, const Mat4f &worldToClip, uint32_t stencilValue);
void AddLights(AActor *actor);
ModelRendererType GetType() const override { return PolyModelRendererType; }
void BeginDrawModel(AActor *actor, FSpriteModelFrame *smf, const VSMatrix &objectToWorldMatrix, bool mirrored) override;
void EndDrawModel(AActor *actor, FSpriteModelFrame *smf) override;
IModelVertexBuffer *CreateVertexBuffer(bool needindex, bool singleframe) override;
VSMatrix GetViewToWorldMatrix() override;
void BeginDrawHUDModel(AActor *actor, const VSMatrix &objectToWorldMatrix, bool mirrored) override;
void EndDrawHUDModel(AActor *actor) override;
void SetInterpolation(double interpolation) override;
void SetMaterial(FTexture *skin, bool clampNoFilter, int translation) override;
void DrawArrays(int start, int count) override;
void DrawElements(int numIndices, size_t offset) override;
void SetTransform();
PolyRenderThread *Thread = nullptr;
const Mat4f &WorldToClip;
uint32_t StencilValue = 0;
FRenderStyle RenderStyle;
float RenderAlpha;
sector_t *sector;
bool fullbrightSprite;
int lightlevel;
double visibility;
uint32_t fillcolor;
uint32_t Translation;
Mat4f ObjectToWorld;
FSoftwareTexture *SkinTexture = nullptr;
unsigned int *IndexBuffer = nullptr;
FModelVertex *VertexBuffer = nullptr;
float InterpolationFactor = 0.0;
PolyLight *Lights = nullptr;
int NumLights = 0;
};
class PolyModelVertexBuffer : public IModelVertexBuffer
{
public:
PolyModelVertexBuffer(bool needindex, bool singleframe);
~PolyModelVertexBuffer();
FModelVertex *LockVertexBuffer(unsigned int size) override;
void UnlockVertexBuffer() override;
unsigned int *LockIndexBuffer(unsigned int size) override;
void UnlockIndexBuffer() override;
void SetupFrame(FModelRenderer *renderer, unsigned int frame1, unsigned int frame2, unsigned int size) override;
private:
TArray<FModelVertex> mVertexBuffer;
TArray<unsigned int> mIndexBuffer;
};

View file

@ -1,117 +0,0 @@
/*
** Polygon Doom software renderer
** Copyright (c) 2016 Magnus Norddahl
**
** This software is provided 'as-is', without any express or implied
** warranty. In no event will the authors be held liable for any damages
** arising from the use of this software.
**
** Permission is granted to anyone to use this software for any purpose,
** including commercial applications, and to alter it and redistribute it
** freely, subject to the following restrictions:
**
** 1. The origin of this software must not be misrepresented; you must not
** claim that you wrote the original software. If you use this software
** in a product, an acknowledgment in the product documentation would be
** appreciated but is not required.
** 2. Altered source versions must be plainly marked as such, and must not be
** misrepresented as being the original software.
** 3. This notice may not be removed or altered from any source distribution.
**
*/
#include <stdlib.h>
#include "templates.h"
#include "doomdef.h"
#include "sbar.h"
#include "r_data/r_translate.h"
#include "poly_particle.h"
#include "polyrenderer/poly_renderer.h"
#include "polyrenderer/scene/poly_light.h"
#include "polyrenderer/poly_renderthread.h"
EXTERN_CVAR(Int, gl_particles_style)
void RenderPolyParticle::Render(PolyRenderThread *thread, particle_t *particle, subsector_t *sub, uint32_t stencilValue)
{
double timefrac = PolyRenderer::Instance()->Viewpoint.TicFrac;
if (paused || PolyRenderer::Instance()->Viewpoint.ViewLevel->isFrozen())
timefrac = 0.;
DVector3 pos = particle->Pos + (particle->Vel * timefrac);
double psize = particle->size / 8.0;
double zpos = pos.Z;
const auto &viewpoint = PolyRenderer::Instance()->Viewpoint;
DVector2 points[2] =
{
{ pos.X - viewpoint.Sin * psize, pos.Y + viewpoint.Cos * psize },
{ pos.X + viewpoint.Sin * psize, pos.Y - viewpoint.Cos * psize }
};
TriVertex *vertices = thread->FrameMemory->AllocMemory<TriVertex>(4);
bool foggy = false;
int actualextralight = foggy ? 0 : viewpoint.extralight << 4;
std::pair<float, float> offsets[4] =
{
{ 0.0f, 1.0f },
{ 1.0f, 1.0f },
{ 1.0f, 0.0f },
{ 0.0f, 0.0f },
};
for (int i = 0; i < 4; i++)
{
auto &p = (i == 0 || i == 3) ? points[0] : points[1];
vertices[i].x = (float)p.X;
vertices[i].y = (float)p.Y;
vertices[i].z = (float)(zpos + psize * (2.0 * offsets[i].second - 1.0));
vertices[i].w = 1.0f;
vertices[i].u = (float)(offsets[i].first);
vertices[i].v = (float)(1.0f - offsets[i].second);
}
bool fullbrightSprite = particle->bright != 0;
int lightlevel = fullbrightSprite ? 255 : sub->sector->lightlevel + actualextralight;
PolyDrawArgs args;
args.SetLight(GetColorTable(sub->sector->Colormap), lightlevel, PolyRenderer::Instance()->Light.ParticleGlobVis(foggy), fullbrightSprite);
args.SetDepthTest(true);
args.SetColor(particle->color | 0xff000000, particle->color >> 24);
args.SetStyle(TriBlendMode::Shaded, particle->alpha);
args.SetStencilTestValue(stencilValue);
args.SetWriteStencil(false);
args.SetWriteDepth(false);
args.SetTexture(GetParticleTexture(), ParticleTextureSize, ParticleTextureSize);
PolyTriangleDrawer::DrawArray(thread->DrawQueue, args, vertices, 4, PolyDrawMode::TriangleFan);
}
uint8_t *RenderPolyParticle::GetParticleTexture()
{
static uint8_t particle_texture[NumParticleTextures][ParticleTextureSize * ParticleTextureSize];
static bool first_call = true;
if (first_call)
{
double center = ParticleTextureSize * 0.5f;
for (int y = 0; y < ParticleTextureSize; y++)
{
for (int x = 0; x < ParticleTextureSize; x++)
{
double dx = (center - x - 0.5f) / center;
double dy = (center - y - 0.5f) / center;
double dist2 = dx * dx + dy * dy;
double round_alpha = clamp<double>(1.7f - dist2 * 1.7f, 0.0f, 1.0f);
double smooth_alpha = clamp<double>(1.1f - dist2 * 1.1f, 0.0f, 1.0f);
particle_texture[0][x + y * ParticleTextureSize] = 255;
particle_texture[1][x + y * ParticleTextureSize] = (int)(round_alpha * 255.0f + 0.5f);
particle_texture[2][x + y * ParticleTextureSize] = (int)(smooth_alpha * 255.0f + 0.5f);
}
}
first_call = false;
}
return particle_texture[MIN<int>(gl_particles_style, NumParticleTextures)];
}

View file

@ -1,57 +0,0 @@
/*
** Polygon Doom software renderer
** Copyright (c) 2016 Magnus Norddahl
**
** This software is provided 'as-is', without any express or implied
** warranty. In no event will the authors be held liable for any damages
** arising from the use of this software.
**
** Permission is granted to anyone to use this software for any purpose,
** including commercial applications, and to alter it and redistribute it
** freely, subject to the following restrictions:
**
** 1. The origin of this software must not be misrepresented; you must not
** claim that you wrote the original software. If you use this software
** in a product, an acknowledgment in the product documentation would be
** appreciated but is not required.
** 2. Altered source versions must be plainly marked as such, and must not be
** misrepresented as being the original software.
** 3. This notice may not be removed or altered from any source distribution.
**
*/
#pragma once
#include "polyrenderer/drawers/poly_triangle.h"
#include "p_effect.h"
class RenderPolyParticle
{
public:
void Render(PolyRenderThread *thread, particle_t *particle, subsector_t *sub, uint32_t stencilValue);
private:
static uint8_t *GetParticleTexture();
enum
{
NumParticleTextures = 3,
ParticleTextureSize = 64
};
};
class PolyTranslucentParticle : public PolyTranslucentObject
{
public:
PolyTranslucentParticle(particle_t *particle, subsector_t *sub, uint32_t subsectorDepth, uint32_t stencilValue) : PolyTranslucentObject(subsectorDepth, 0.0), particle(particle), sub(sub), StencilValue(stencilValue) { }
void Render(PolyRenderThread *thread) override
{
RenderPolyParticle spr;
spr.Render(thread, particle, sub, StencilValue + 1);
}
particle_t *particle = nullptr;
subsector_t *sub = nullptr;
uint32_t StencilValue = 0;
};

View file

@ -1,565 +0,0 @@
/*
** Polygon Doom software renderer
** Copyright (c) 2016 Magnus Norddahl
**
** This software is provided 'as-is', without any express or implied
** warranty. In no event will the authors be held liable for any damages
** arising from the use of this software.
**
** Permission is granted to anyone to use this software for any purpose,
** including commercial applications, and to alter it and redistribute it
** freely, subject to the following restrictions:
**
** 1. The origin of this software must not be misrepresented; you must not
** claim that you wrote the original software. If you use this software
** in a product, an acknowledgment in the product documentation would be
** appreciated but is not required.
** 2. Altered source versions must be plainly marked as such, and must not be
** misrepresented as being the original software.
** 3. This notice may not be removed or altered from any source distribution.
**
*/
#include <stdlib.h>
#include "templates.h"
#include "doomdef.h"
#include "sbar.h"
#include "r_data/r_translate.h"
#include "poly_plane.h"
#include "poly_portal.h"
#include "polyrenderer/poly_renderer.h"
#include "r_sky.h"
#include "polyrenderer/scene/poly_light.h"
#include "polyrenderer/poly_renderthread.h"
#include "p_lnspec.h"
#include "a_dynlight.h"
EXTERN_CVAR(Int, r_3dfloors)
void RenderPolyPlane::RenderPlanes(PolyRenderThread *thread, const PolyTransferHeights &fakeflat, uint32_t stencilValue, double skyCeilingHeight, double skyFloorHeight, std::vector<std::unique_ptr<PolyDrawSectorPortal>> &sectorPortals, size_t sectorPortalsStart)
{
if (fakeflat.FrontSector->CenterFloor() == fakeflat.FrontSector->CenterCeiling())
return;
RenderPolyPlane plane;
plane.Render(thread, fakeflat, stencilValue, true, skyCeilingHeight, sectorPortals, sectorPortalsStart);
plane.Render(thread, fakeflat, stencilValue, false, skyFloorHeight, sectorPortals, sectorPortalsStart);
}
void RenderPolyPlane::Render(PolyRenderThread *thread, const PolyTransferHeights &fakeflat, uint32_t stencilValue, bool ceiling, double skyHeight, std::vector<std::unique_ptr<PolyDrawSectorPortal>> &sectorPortals, size_t sectorPortalsStart)
{
FSectorPortal *portal = fakeflat.FrontSector->ValidatePortal(ceiling ? sector_t::ceiling : sector_t::floor);
if (!portal || (portal->mFlags & PORTSF_INSKYBOX) == PORTSF_INSKYBOX) // Do not recurse into portals we already recursed into
{
RenderNormal(thread, fakeflat, stencilValue, ceiling, skyHeight);
}
else
{
RenderPortal(thread, fakeflat, stencilValue, ceiling, skyHeight, portal, sectorPortals, sectorPortalsStart);
}
}
void RenderPolyPlane::RenderNormal(PolyRenderThread *thread, const PolyTransferHeights &fakeflat, uint32_t stencilValue, bool ceiling, double skyHeight)
{
const auto &viewpoint = PolyRenderer::Instance()->Viewpoint;
FTextureID picnum = fakeflat.FrontSector->GetTexture(ceiling ? sector_t::ceiling : sector_t::floor);
if (picnum != skyflatnum)
{
FTexture *tex = TexMan.GetPalettedTexture(picnum, true);
if (!tex || !tex->isValid())
return;
PolyPlaneUVTransform transform = PolyPlaneUVTransform(ceiling ? fakeflat.FrontSector->planes[sector_t::ceiling].xform : fakeflat.FrontSector->planes[sector_t::floor].xform, tex->GetSoftwareTexture());
TriVertex *vertices = CreatePlaneVertices(thread, fakeflat.Subsector, transform, ceiling ? fakeflat.FrontSector->ceilingplane : fakeflat.FrontSector->floorplane);
PolyDrawArgs args;
SetLightLevel(thread, args, fakeflat, ceiling);
SetDynLights(thread, args, fakeflat.Subsector, ceiling);
args.SetStencilTestValue(stencilValue);
args.SetWriteStencil(true, stencilValue + 1);
args.SetTexture(tex->GetSoftwareTexture(), DefaultRenderStyle());
args.SetStyle(TriBlendMode::Opaque);
PolyTriangleDrawer::DrawArray(thread->DrawQueue, args, vertices, fakeflat.Subsector->numlines, PolyDrawMode::TriangleFan);
}
else
{
TriVertex *vertices = CreateSkyPlaneVertices(thread, fakeflat.Subsector, skyHeight);
PolyDrawArgs args;
args.SetStencilTestValue(stencilValue);
args.SetWriteStencil(true, 255);
args.SetWriteColor(false);
args.SetWriteDepth(false);
PolyTriangleDrawer::DrawArray(thread->DrawQueue, args, vertices, fakeflat.Subsector->numlines, PolyDrawMode::TriangleFan);
RenderSkyWalls(thread, args, fakeflat.Subsector, nullptr, ceiling, skyHeight);
}
}
void RenderPolyPlane::RenderPortal(PolyRenderThread *thread, const PolyTransferHeights &fakeflat, uint32_t stencilValue, bool ceiling, double skyHeight, FSectorPortal *portal, std::vector<std::unique_ptr<PolyDrawSectorPortal>> &sectorPortals, size_t sectorPortalsStart)
{
const auto &viewpoint = PolyRenderer::Instance()->Viewpoint;
PolyDrawSectorPortal *polyportal = nullptr;
std::vector<PolyPortalSegment> portalSegments;
// Skip portals not facing the camera
if ((ceiling && fakeflat.FrontSector->ceilingplane.PointOnSide(viewpoint.Pos) < 0) ||
(!ceiling && fakeflat.FrontSector->floorplane.PointOnSide(viewpoint.Pos) < 0))
{
return;
}
for (size_t i = sectorPortalsStart; i < sectorPortals.size(); i++)
{
if (sectorPortals[i]->Portal == portal) // To do: what other criteria do we need to check for?
{
polyportal = sectorPortals[i].get();
break;
}
}
if (!polyportal)
{
sectorPortals.push_back(std::unique_ptr<PolyDrawSectorPortal>(new PolyDrawSectorPortal(portal, ceiling)));
polyportal = sectorPortals.back().get();
}
#if 0
// Calculate portal clipping
portalSegments.reserve(sub->numlines);
for (uint32_t i = 0; i < sub->numlines; i++)
{
seg_t *line = &sub->firstline[i];
DVector2 pt1 = line->v1->fPos() - viewpoint.Pos;
DVector2 pt2 = line->v2->fPos() - viewpoint.Pos;
bool backside = pt1.Y * (pt1.X - pt2.X) + pt1.X * (pt2.Y - pt1.Y) >= 0;
if (!backside)
{
angle_t angle1, angle2;
if (cull.GetAnglesForLine(line->v1->fX(), line->v1->fY(), line->v2->fX(), line->v2->fY(), angle1, angle2))
portalSegments.push_back({ angle1, angle2 });
}
else
{
angle_t angle1, angle2;
if (cull.GetAnglesForLine(line->v2->fX(), line->v2->fY(), line->v1->fX(), line->v1->fY(), angle1, angle2))
portalSegments.push_back({ angle1, angle2 });
}
}
#endif
TriVertex *vertices = CreateSkyPlaneVertices(thread, fakeflat.Subsector, skyHeight);
PolyDrawArgs args;
args.SetStencilTestValue(stencilValue);
args.SetWriteStencil(true, polyportal->StencilValue);
args.SetWriteColor(false);
args.SetWriteDepth(false);
PolyTriangleDrawer::DrawArray(thread->DrawQueue, args, vertices, fakeflat.Subsector->numlines, PolyDrawMode::TriangleFan);
RenderSkyWalls(thread, args, fakeflat.Subsector, polyportal, ceiling, skyHeight);
polyportal->Shape.push_back({ vertices, (int)fakeflat.Subsector->numlines });
}
void RenderPolyPlane::RenderSkyWalls(PolyRenderThread *thread, PolyDrawArgs &args, subsector_t *sub, PolyDrawSectorPortal *polyportal, bool ceiling, double skyHeight)
{
sector_t *frontsector = sub->sector;
for (uint32_t i = 0; i < sub->numlines; i++)
{
seg_t *line = &sub->firstline[i];
double skyBottomz1 = frontsector->ceilingplane.ZatPoint(line->v1);
double skyBottomz2 = frontsector->ceilingplane.ZatPoint(line->v2);
if (line->backsector)
{
sector_t *backsector = line->backsector;
double backceilz1 = backsector->ceilingplane.ZatPoint(line->v1);
double backfloorz1 = backsector->floorplane.ZatPoint(line->v1);
double backceilz2 = backsector->ceilingplane.ZatPoint(line->v2);
double backfloorz2 = backsector->floorplane.ZatPoint(line->v2);
bool bothSkyCeiling = frontsector->GetTexture(sector_t::ceiling) == skyflatnum && backsector->GetTexture(sector_t::ceiling) == skyflatnum;
bool closedSector = backceilz1 == backfloorz1 && backceilz2 == backfloorz2;
if (ceiling && bothSkyCeiling && closedSector)
{
double frontceilz1 = frontsector->ceilingplane.ZatPoint(line->v1);
double frontfloorz1 = frontsector->floorplane.ZatPoint(line->v1);
double frontceilz2 = frontsector->ceilingplane.ZatPoint(line->v2);
double frontfloorz2 = frontsector->floorplane.ZatPoint(line->v2);
double topceilz1 = frontceilz1;
double topceilz2 = frontceilz2;
double topfloorz1 = MIN(backceilz1, frontceilz1);
double topfloorz2 = MIN(backceilz2, frontceilz2);
double bottomceilz1 = MAX(frontfloorz1, backfloorz1);
double bottomceilz2 = MAX(frontfloorz2, backfloorz2);
double middleceilz1 = topfloorz1;
double middleceilz2 = topfloorz2;
double middlefloorz1 = MIN(bottomceilz1, middleceilz1);
double middlefloorz2 = MIN(bottomceilz2, middleceilz2);
skyBottomz1 = middlefloorz1;
skyBottomz2 = middlefloorz2;
}
else if (bothSkyCeiling)
{
continue;
}
}
else if (polyportal && line->linedef && line->linedef->special == Line_Horizon)
{
// Not entirely correct as this closes the line horizon rather than allowing the floor to continue to infinity
skyBottomz1 = frontsector->floorplane.ZatPoint(line->v1);
skyBottomz2 = frontsector->floorplane.ZatPoint(line->v2);
}
TriVertex *wallvert = thread->FrameMemory->AllocMemory<TriVertex>(4);
if (ceiling)
{
wallvert[0] = GetSkyVertex(line->v1, skyHeight);
wallvert[1] = GetSkyVertex(line->v2, skyHeight);
wallvert[2] = GetSkyVertex(line->v2, skyBottomz2);
wallvert[3] = GetSkyVertex(line->v1, skyBottomz1);
}
else
{
wallvert[0] = GetSkyVertex(line->v1, frontsector->floorplane.ZatPoint(line->v1));
wallvert[1] = GetSkyVertex(line->v2, frontsector->floorplane.ZatPoint(line->v2));
wallvert[2] = GetSkyVertex(line->v2, skyHeight);
wallvert[3] = GetSkyVertex(line->v1, skyHeight);
}
PolyTriangleDrawer::DrawArray(thread->DrawQueue, args, wallvert, 4, PolyDrawMode::TriangleFan);
if (polyportal)
{
polyportal->Shape.push_back({ wallvert, 4 });
}
}
}
void RenderPolyPlane::SetLightLevel(PolyRenderThread *thread, PolyDrawArgs &args, const PolyTransferHeights &fakeflat, bool ceiling)
{
bool foggy = PolyRenderer::Instance()->Level->fadeto || fakeflat.FrontSector->Colormap.FadeColor || (PolyRenderer::Instance()->Level->flags & LEVEL_HASFADETABLE);
int lightlevel = ceiling ? fakeflat.CeilingLightLevel : fakeflat.FloorLightLevel;
int actualextralight = foggy ? 0 : PolyRenderer::Instance()->Viewpoint.extralight << 4;
lightlevel = clamp(lightlevel + actualextralight, 0, 255);
PolyCameraLight *cameraLight = PolyCameraLight::Instance();
FDynamicColormap *basecolormap = GetColorTable(fakeflat.FrontSector->Colormap, fakeflat.FrontSector->SpecialColors[ceiling ? sector_t::ceiling : sector_t::floor]);
if (cameraLight->FixedLightLevel() < 0 && fakeflat.FrontSector->e && fakeflat.FrontSector->e->XFloor.lightlist.Size())
{
lightlist_t *light = P_GetPlaneLight(fakeflat.FrontSector, ceiling ? &fakeflat.FrontSector->ceilingplane : &fakeflat.FrontSector->floorplane, false);
basecolormap = GetColorTable(light->extra_colormap, fakeflat.FrontSector->SpecialColors[ceiling ? sector_t::ceiling : sector_t::floor]);
if (light->p_lightlevel != &fakeflat.FrontSector->lightlevel) // If this is the real ceiling, don't discard plane lighting R_FakeFlat() accounted for.
{
lightlevel = *light->p_lightlevel;
}
}
args.SetLight(basecolormap, lightlevel, PolyRenderer::Instance()->Light.WallGlobVis(foggy), false);
}
void RenderPolyPlane::SetDynLights(PolyRenderThread *thread, PolyDrawArgs &args, subsector_t *sub, bool ceiling)
{
if (!r_dynlights)
{
args.SetLights(nullptr, 0);
return;
}
FLightNode *light_list = sub->section->lighthead;
auto cameraLight = PolyCameraLight::Instance();
if ((cameraLight->FixedLightLevel() >= 0) || (cameraLight->FixedColormap() != nullptr))
{
args.SetLights(nullptr, 0); // [SP] Don't draw dynlights if invul/lightamp active
return;
}
// Calculate max lights that can touch the wall so we can allocate memory for the list
int max_lights = 0;
FLightNode *cur_node = light_list;
while (cur_node)
{
if (cur_node->lightsource->IsActive())
max_lights++;
cur_node = cur_node->nextLight;
}
if (max_lights == 0)
{
args.SetLights(nullptr, 0);
return;
}
int dc_num_lights = 0;
PolyLight *dc_lights = thread->FrameMemory->AllocMemory<PolyLight>(max_lights);
// Setup lights
cur_node = light_list;
while (cur_node)
{
if (cur_node->lightsource->IsActive())
{
bool is_point_light = cur_node->lightsource->IsAttenuated();
// To do: cull lights not touching subsector
uint32_t red = cur_node->lightsource->GetRed();
uint32_t green = cur_node->lightsource->GetGreen();
uint32_t blue = cur_node->lightsource->GetBlue();
auto &light = dc_lights[dc_num_lights++];
light.x = (float)cur_node->lightsource->X();
light.y = (float)cur_node->lightsource->Y();
light.z = (float)cur_node->lightsource->Z();
light.radius = 256.0f / cur_node->lightsource->GetRadius();
light.color = (red << 16) | (green << 8) | blue;
if (is_point_light)
light.radius = -light.radius;
}
cur_node = cur_node->nextLight;
}
args.SetLights(dc_lights, dc_num_lights);
DVector3 normal = ceiling ? sub->sector->ceilingplane.Normal() : sub->sector->floorplane.Normal();
args.SetNormal({ (float)normal.X, (float)normal.Y, (float)normal.Z });
}
TriVertex *RenderPolyPlane::CreatePlaneVertices(PolyRenderThread *thread, subsector_t *sub, const PolyPlaneUVTransform &transform, const secplane_t &plane)
{
TriVertex *vertices = thread->FrameMemory->AllocMemory<TriVertex>(sub->numlines);
const auto &viewpoint = PolyRenderer::Instance()->Viewpoint;
double planeZ = plane.ZatPoint(viewpoint.Pos.XY());
if (viewpoint.Pos.Z < planeZ)
{
for (uint32_t i = 0; i < sub->numlines; i++)
{
seg_t *line = &sub->firstline[sub->numlines - 1 - i];
vertices[i] = transform.GetVertex(line->v1, plane.ZatPoint(line->v1));
}
}
else
{
for (uint32_t i = 0; i < sub->numlines; i++)
{
seg_t *line = &sub->firstline[i];
vertices[i] = transform.GetVertex(line->v1, plane.ZatPoint(line->v1));
}
}
return vertices;
}
TriVertex *RenderPolyPlane::CreateSkyPlaneVertices(PolyRenderThread *thread, subsector_t *sub, double skyHeight)
{
TriVertex *vertices = thread->FrameMemory->AllocMemory<TriVertex>(sub->numlines);
const auto &viewpoint = PolyRenderer::Instance()->Viewpoint;
if (viewpoint.Pos.Z < skyHeight)
{
for (uint32_t i = 0; i < sub->numlines; i++)
{
seg_t *line = &sub->firstline[sub->numlines - 1 - i];
vertices[i] = GetSkyVertex(line->v1, skyHeight);
}
}
else
{
for (uint32_t i = 0; i < sub->numlines; i++)
{
seg_t *line = &sub->firstline[i];
vertices[i] = GetSkyVertex(line->v1, skyHeight);
}
}
return vertices;
}
/////////////////////////////////////////////////////////////////////////////
PolyPlaneUVTransform::PolyPlaneUVTransform(const FTransform &transform, FSoftwareTexture *tex)
{
if (tex)
{
xscale = (float)(transform.xScale * tex->GetScale().X / tex->GetWidth());
yscale = (float)(transform.yScale * tex->GetScale().Y / tex->GetHeight());
double planeang = (transform.Angle + transform.baseAngle).Radians();
cosine = (float)cos(planeang);
sine = (float)sin(planeang);
xOffs = (float)transform.xOffs;
yOffs = (float)transform.yOffs;
}
else
{
xscale = 1.0f / 64.0f;
yscale = 1.0f / 64.0f;
cosine = 1.0f;
sine = 0.0f;
xOffs = 0.0f;
yOffs = 0.0f;
}
}
/////////////////////////////////////////////////////////////////////////////
void Render3DFloorPlane::RenderPlanes(PolyRenderThread *thread, subsector_t *sub, uint32_t stencilValue, uint32_t subsectorDepth, std::vector<PolyTranslucentObject *> &translucentObjects)
{
if (!r_3dfloors || sub->sector->CenterFloor() == sub->sector->CenterCeiling())
return;
const auto &viewpoint = PolyRenderer::Instance()->Viewpoint;
auto frontsector = sub->sector;
auto &ffloors = frontsector->e->XFloor.ffloors;
// 3D floor floors
for (int i = 0; i < (int)ffloors.Size(); i++)
{
F3DFloor *fakeFloor = ffloors[i];
F3DFloor *prevFloor = i > 0 ? ffloors[i - 1] : nullptr;
if (!(fakeFloor->flags & FF_EXISTS)) continue;
if (!fakeFloor->model) continue;
if (!(fakeFloor->flags & FF_RENDERPLANES)) continue;
if (fakeFloor->alpha == 0) continue;
if (prevFloor && (prevFloor->flags & fakeFloor->flags & FF_SWIMMABLE)) continue;
double fakeHeight = fakeFloor->top.plane->ZatPoint(frontsector->centerspot);
if (fakeFloor->top.plane->isSlope() || (fakeHeight < viewpoint.Pos.Z && fakeHeight > frontsector->floorplane.ZatPoint(frontsector->centerspot)))
{
Render3DFloorPlane plane;
plane.sub = sub;
plane.stencilValue = stencilValue;
plane.ceiling = false;
plane.fakeFloor = fakeFloor;
plane.Additive = !!(fakeFloor->flags & FF_ADDITIVETRANS);
if (!plane.Additive && fakeFloor->alpha == 255)
{
plane.Masked = false;
plane.Alpha = 1.0;
}
else
{
plane.Masked = true;
plane.Alpha = fakeFloor->alpha / 255.0;
}
if (!plane.Masked)
plane.Render(thread);
else
translucentObjects.push_back(thread->FrameMemory->NewObject<PolyTranslucent3DFloorPlane>(plane, subsectorDepth));
}
}
// 3D floor ceilings
for (int i = 0; i < (int)ffloors.Size(); i++)
{
F3DFloor *fakeFloor = ffloors[i];
F3DFloor *prevFloor = i > 0 ? ffloors[i - 1] : nullptr;
if (!(fakeFloor->flags & FF_EXISTS)) continue;
if (!fakeFloor->model) continue;
if (!(fakeFloor->flags & FF_RENDERPLANES)) continue;
if (fakeFloor->alpha == 0) continue;
if (prevFloor && (prevFloor->flags & fakeFloor->flags & FF_SWIMMABLE)) continue;
double fakeHeight = fakeFloor->bottom.plane->ZatPoint(frontsector->centerspot);
if (fakeFloor->bottom.plane->isSlope() || (fakeHeight > viewpoint.Pos.Z && fakeHeight < frontsector->ceilingplane.ZatPoint(frontsector->centerspot)))
{
Render3DFloorPlane plane;
plane.sub = sub;
plane.stencilValue = stencilValue;
plane.ceiling = true;
plane.fakeFloor = fakeFloor;
plane.Additive = !!(fakeFloor->flags & FF_ADDITIVETRANS);
if (!plane.Additive && fakeFloor->alpha == 255)
{
plane.Masked = false;
plane.Alpha = 1.0;
}
else
{
plane.Masked = true;
plane.Alpha = fakeFloor->alpha / 255.0;
}
if (!plane.Masked)
plane.Render(thread);
else
translucentObjects.push_back(thread->FrameMemory->NewObject<PolyTranslucent3DFloorPlane>(plane, subsectorDepth));
}
}
}
void Render3DFloorPlane::Render(PolyRenderThread *thread)
{
FTextureID picnum = ceiling ? *fakeFloor->bottom.texture : *fakeFloor->top.texture;
auto tex = TexMan.GetPalettedTexture(picnum, true);
if (!tex->isValid())
return;
PolyCameraLight *cameraLight = PolyCameraLight::Instance();
int lightlevel = 255;
bool foggy = false;
if (cameraLight->FixedLightLevel() < 0 && sub->sector->e->XFloor.lightlist.Size())
{
lightlist_t *light = P_GetPlaneLight(sub->sector, ceiling ? fakeFloor->bottom.plane : fakeFloor->top.plane, ceiling);
//basecolormap = light->extra_colormap;
lightlevel = *light->p_lightlevel;
}
int actualextralight = foggy ? 0 : PolyRenderer::Instance()->Viewpoint.extralight << 4;
lightlevel = clamp(lightlevel + actualextralight, 0, 255);
PolyPlaneUVTransform xform(ceiling ? fakeFloor->top.model->planes[sector_t::ceiling].xform : fakeFloor->top.model->planes[sector_t::floor].xform, tex->GetSoftwareTexture());
TriVertex *vertices = thread->FrameMemory->AllocMemory<TriVertex>(sub->numlines);
if (ceiling)
{
for (uint32_t i = 0; i < sub->numlines; i++)
{
seg_t *line = &sub->firstline[i];
vertices[sub->numlines - 1 - i] = xform.GetVertex(line->v1, fakeFloor->bottom.plane->ZatPoint(line->v1));
}
}
else
{
for (uint32_t i = 0; i < sub->numlines; i++)
{
seg_t *line = &sub->firstline[i];
vertices[i] = xform.GetVertex(line->v1, fakeFloor->top.plane->ZatPoint(line->v1));
}
}
PolyDrawArgs args;
args.SetLight(GetColorTable(sub->sector->Colormap), lightlevel, PolyRenderer::Instance()->Light.WallGlobVis(foggy), false);
if (!Masked)
{
args.SetStyle(TriBlendMode::Opaque);
args.SetStencilTestValue(stencilValue);
args.SetWriteStencil(true, stencilValue + 1);
}
else
{
args.SetStyle(Additive ? TriBlendMode::Add : TriBlendMode::Normal, MIN(Alpha, 1.0));
args.SetStencilTestValue(stencilValue + 1);
args.SetWriteStencil(false);
args.SetDepthTest(true);
args.SetWriteDepth(true);
}
args.SetTexture(tex->GetSoftwareTexture(), DefaultRenderStyle());
PolyTriangleDrawer::DrawArray(thread->DrawQueue, args, vertices, sub->numlines, PolyDrawMode::TriangleFan);
}

View file

@ -1,106 +0,0 @@
/*
** Polygon Doom software renderer
** Copyright (c) 2016 Magnus Norddahl
**
** This software is provided 'as-is', without any express or implied
** warranty. In no event will the authors be held liable for any damages
** arising from the use of this software.
**
** Permission is granted to anyone to use this software for any purpose,
** including commercial applications, and to alter it and redistribute it
** freely, subject to the following restrictions:
**
** 1. The origin of this software must not be misrepresented; you must not
** claim that you wrote the original software. If you use this software
** in a product, an acknowledgment in the product documentation would be
** appreciated but is not required.
** 2. Altered source versions must be plainly marked as such, and must not be
** misrepresented as being the original software.
** 3. This notice may not be removed or altered from any source distribution.
**
*/
#pragma once
#include "polyrenderer/drawers/poly_triangle.h"
class PolyDrawSectorPortal;
class PolyPlaneUVTransform
{
public:
PolyPlaneUVTransform(const FTransform &transform, FSoftwareTexture *tex);
TriVertex GetVertex(vertex_t *v1, double height) const
{
TriVertex v;
v.x = (float)v1->fX();
v.y = (float)v1->fY();
v.z = (float)height;
v.w = 1.0f;
v.u = GetU(v.x, v.y);
v.v = GetV(v.x, v.y);
return v;
}
private:
float GetU(float x, float y) const { return (xOffs + x * cosine - y * sine) * xscale; }
float GetV(float x, float y) const { return (yOffs - x * sine - y * cosine) * yscale; }
float xscale;
float yscale;
float cosine;
float sine;
float xOffs, yOffs;
};
class RenderPolyPlane
{
public:
static void RenderPlanes(PolyRenderThread *thread, const PolyTransferHeights &fakeflat, uint32_t stencilValue, double skyCeilingHeight, double skyFloorHeight, std::vector<std::unique_ptr<PolyDrawSectorPortal>> &sectorPortals, size_t sectorPortalsStart);
private:
void Render(PolyRenderThread *thread, const PolyTransferHeights &fakeflat, uint32_t stencilValue, bool ceiling, double skyHeight, std::vector<std::unique_ptr<PolyDrawSectorPortal>> &sectorPortals, size_t sectorPortalsStart);
void RenderPortal(PolyRenderThread *thread, const PolyTransferHeights &fakeflat, uint32_t stencilValue, bool ceiling, double skyHeight, FSectorPortal *portal, std::vector<std::unique_ptr<PolyDrawSectorPortal>> &sectorPortals, size_t sectorPortalsStart);
void RenderNormal(PolyRenderThread *thread, const PolyTransferHeights &fakeflat, uint32_t stencilValue, bool ceiling, double skyHeight);
void RenderSkyWalls(PolyRenderThread *thread, PolyDrawArgs &args, subsector_t *sub, PolyDrawSectorPortal *polyportal, bool ceiling, double skyHeight);
void SetLightLevel(PolyRenderThread *thread, PolyDrawArgs &args, const PolyTransferHeights &fakeflat, bool ceiling);
void SetDynLights(PolyRenderThread *thread, PolyDrawArgs &args, subsector_t *sub, bool ceiling);
TriVertex *CreatePlaneVertices(PolyRenderThread *thread, subsector_t *sub, const PolyPlaneUVTransform &transform, const secplane_t &plane);
TriVertex *CreateSkyPlaneVertices(PolyRenderThread *thread, subsector_t *sub, double skyHeight);
static TriVertex GetSkyVertex(vertex_t *v, double height) { return { (float)v->fX(), (float)v->fY(), (float)height, 1.0f, 0.0f, 0.0f }; }
};
class Render3DFloorPlane
{
public:
static void RenderPlanes(PolyRenderThread *thread, subsector_t *sub, uint32_t stencilValue, uint32_t subsectorDepth, std::vector<PolyTranslucentObject *> &translucentObjects);
void Render(PolyRenderThread *thread);
subsector_t *sub = nullptr;
uint32_t stencilValue = 0;
bool ceiling = false;
F3DFloor *fakeFloor = nullptr;
bool Masked = false;
bool Additive = false;
double Alpha = 1.0;
};
class PolyTranslucent3DFloorPlane : public PolyTranslucentObject
{
public:
PolyTranslucent3DFloorPlane(Render3DFloorPlane plane, uint32_t subsectorDepth) : PolyTranslucentObject(subsectorDepth, 1e7), plane(plane) { }
void Render(PolyRenderThread *thread) override
{
plane.Render(thread);
}
Render3DFloorPlane plane;
};

View file

@ -1,548 +0,0 @@
/*
** Polygon Doom software renderer
** Copyright (c) 2016 Magnus Norddahl
**
** This software is provided 'as-is', without any express or implied
** warranty. In no event will the authors be held liable for any damages
** arising from the use of this software.
**
** Permission is granted to anyone to use this software for any purpose,
** including commercial applications, and to alter it and redistribute it
** freely, subject to the following restrictions:
**
** 1. The origin of this software must not be misrepresented; you must not
** claim that you wrote the original software. If you use this software
** in a product, an acknowledgment in the product documentation would be
** appreciated but is not required.
** 2. Altered source versions must be plainly marked as such, and must not be
** misrepresented as being the original software.
** 3. This notice may not be removed or altered from any source distribution.
**
*/
#include <stdlib.h>
#include "templates.h"
#include "doomdef.h"
#include "sbar.h"
#include "r_data/r_translate.h"
#include "poly_playersprite.h"
#include "polyrenderer/poly_renderer.h"
#include "d_player.h"
#include "polyrenderer/scene/poly_light.h"
#include "polyrenderer/scene/poly_model.h"
EXTERN_CVAR(Bool, r_drawplayersprites)
EXTERN_CVAR(Bool, r_deathcamera)
EXTERN_CVAR(Bool, r_fullbrightignoresectorcolor)
extern bool r_modelscene;
void RenderPolyPlayerSprites::Render(PolyRenderThread *thread)
{
// This code cannot be moved directly to RenderRemainingSprites because the engine
// draws the canvas textures between this call and the final call to RenderRemainingSprites..
//
// We also can't move it because the model render code relies on it
const auto &viewpoint = PolyRenderer::Instance()->Viewpoint;
int i;
int lightnum;
DPSprite* psp;
DPSprite* weapon;
sector_t* sec = nullptr;
int floorlight, ceilinglight;
F3DFloor *rover;
if (!r_drawplayersprites ||
!viewpoint.camera ||
!viewpoint.camera->player ||
(players[consoleplayer].cheats & CF_CHASECAM) ||
(r_deathcamera && viewpoint.camera->health <= 0))
return;
renderHUDModel = r_modelscene && IsHUDModelForPlayerAvailable(players[consoleplayer].camera->player);
PolyTransferHeights fakeflat(viewpoint.camera->subsector);
FDynamicColormap *basecolormap;
PolyCameraLight *cameraLight = PolyCameraLight::Instance();
bool nc = !!(viewpoint.camera->Level->flags3 & LEVEL3_NOCOLOREDSPRITELIGHTING);
if (cameraLight->FixedLightLevel() < 0 && viewpoint.sector->e && viewpoint.sector->e->XFloor.lightlist.Size())
{
for (i = viewpoint.sector->e->XFloor.lightlist.Size() - 1; i >= 0; i--)
{
if (viewpoint.Pos.Z <= viewpoint.sector->e->XFloor.lightlist[i].plane.Zat0())
{
rover = viewpoint.sector->e->XFloor.lightlist[i].caster;
if (rover)
{
if (rover->flags & FF_DOUBLESHADOW && viewpoint.Pos.Z <= rover->bottom.plane->Zat0())
break;
sec = rover->model;
if (rover->flags & FF_FADEWALLS)
basecolormap = GetSpriteColorTable(sec->Colormap, sec->SpecialColors[sector_t::sprites], nc);
else
basecolormap = GetSpriteColorTable(viewpoint.sector->e->XFloor.lightlist[i].extra_colormap, sec->SpecialColors[sector_t::sprites], nc);
}
break;
}
}
if (!sec)
{
sec = viewpoint.sector;
basecolormap = GetSpriteColorTable(sec->Colormap, sec->SpecialColors[sector_t::sprites], nc);
}
floorlight = ceilinglight = sec->lightlevel;
}
else
{ // This used to use camera->Sector but due to interpolation that can be incorrect
// when the interpolated viewpoint is in a different sector than the camera.
sec = fakeflat.FrontSector;
floorlight = fakeflat.FloorLightLevel;
ceilinglight = fakeflat.CeilingLightLevel;
// [RH] set basecolormap
basecolormap = GetSpriteColorTable(sec->Colormap, sec->SpecialColors[sector_t::sprites], nc);
}
// [RH] set foggy flag
bool foggy = (PolyRenderer::Instance()->Level->fadeto || basecolormap->Fade || (PolyRenderer::Instance()->Level->flags & LEVEL_HASFADETABLE));
// get light level
lightnum = ((floorlight + ceilinglight) >> 1) + (foggy ? 0 : viewpoint.extralight << 4);
int spriteshade = LightLevelToShade(lightnum, foggy) - 24 * FRACUNIT;
if (viewpoint.camera->player != nullptr)
{
double wx, wy;
float bobx, boby;
P_BobWeapon(viewpoint.camera->player, &bobx, &boby, viewpoint.TicFrac);
// Interpolate the main weapon layer once so as to be able to add it to other layers.
if ((weapon = viewpoint.camera->player->FindPSprite(PSP_WEAPON)) != nullptr)
{
if (weapon->firstTic)
{
wx = weapon->x;
wy = weapon->y;
}
else
{
wx = weapon->oldx + (weapon->x - weapon->oldx) * viewpoint.TicFrac;
wy = weapon->oldy + (weapon->y - weapon->oldy) * viewpoint.TicFrac;
}
}
else
{
wx = 0;
wy = 0;
}
// add all active psprites
psp = viewpoint.camera->player->psprites;
while (psp)
{
// [RH] Don't draw the targeter's crosshair if the player already has a crosshair set.
// It's possible this psprite's caller is now null but the layer itself hasn't been destroyed
// because it didn't tick yet (if we typed 'take all' while in the console for example).
// In this case let's simply not draw it to avoid crashing.
if ((psp->GetID() != PSP_TARGETCENTER || CrosshairImage == nullptr) && psp->GetCaller() != nullptr)
{
RenderSprite(thread, psp, viewpoint.camera, bobx, boby, wx, wy, viewpoint.TicFrac, spriteshade, basecolormap, foggy);
}
psp = psp->GetNext();
}
}
}
void RenderPolyPlayerSprites::RenderRemainingSprites()
{
for (const PolyHWAccelPlayerSprite &sprite : AcceleratedSprites)
{
screen->DrawTexture(sprite.pic->GetTexture(),
viewwindowx + sprite.x1,
viewwindowy + viewheight / 2 - sprite.texturemid * sprite.yscale - 0.5,
DTA_DestWidthF, FIXED2DBL(sprite.pic->GetWidth() * sprite.xscale),
DTA_DestHeightF, sprite.pic->GetHeight() * sprite.yscale,
DTA_TranslationIndex, sprite.Translation,
DTA_FlipX, sprite.flip,
DTA_TopOffset, 0,
DTA_LeftOffset, 0,
DTA_ClipLeft, viewwindowx,
DTA_ClipTop, viewwindowy,
DTA_ClipRight, viewwindowx + viewwidth,
DTA_ClipBottom, viewwindowy + viewheight,
DTA_Alpha, sprite.Alpha,
DTA_RenderStyle, sprite.RenderStyle,
DTA_FillColor, sprite.FillColor,
DTA_SpecialColormap, sprite.special,
DTA_ColorOverlay, sprite.overlay.d,
DTA_Color, sprite.LightColor | 0xff000000, // the color here does not have a valid alpha component.
DTA_Desaturate, sprite.Desaturate,
TAG_DONE);
}
AcceleratedSprites.Clear();
}
void RenderPolyPlayerSprites::RenderSprite(PolyRenderThread *thread, DPSprite *pspr, AActor *owner, float bobx, float boby, double wx, double wy, double ticfrac, int spriteshade, FDynamicColormap *basecolormap, bool foggy)
{
double tx;
int x1;
int x2;
double sx, sy;
spritedef_t* sprdef;
spriteframe_t* sprframe;
FTextureID picnum;
uint16_t flip;
FTexture* ttex;
FSoftwareTexture* tex;
bool noaccel;
double alpha = owner->Alpha;
// decide which patch to use
if ((unsigned)pspr->GetSprite() >= (unsigned)sprites.Size())
{
DPrintf(DMSG_ERROR, "R_DrawPSprite: invalid sprite number %i\n", pspr->GetSprite());
return;
}
sprdef = &sprites[pspr->GetSprite()];
if (pspr->GetFrame() >= sprdef->numframes)
{
DPrintf(DMSG_ERROR, "R_DrawPSprite: invalid sprite frame %i : %i\n", pspr->GetSprite(), pspr->GetFrame());
return;
}
const auto &viewpoint = PolyRenderer::Instance()->Viewpoint;
const auto &viewwindow = PolyRenderer::Instance()->Viewwindow;
DCanvas *renderTarget = PolyRenderer::Instance()->RenderTarget;
// Force it to use software rendering when drawing to a canvas texture.
bool renderToCanvas = PolyRenderer::Instance()->RenderToCanvas;
sprframe = &SpriteFrames[sprdef->spriteframes + pspr->GetFrame()];
picnum = sprframe->Texture[0];
flip = sprframe->Flip & 1;
ttex = TexMan.GetTexture(picnum);
if (!ttex->isValid())
return;
tex = ttex->GetSoftwareTexture();
if (pspr->firstTic)
{ // Can't interpolate the first tic.
pspr->firstTic = false;
pspr->oldx = pspr->x;
pspr->oldy = pspr->y;
}
sx = pspr->oldx + (pspr->x - pspr->oldx) * ticfrac;
sy = pspr->oldy + (pspr->y - pspr->oldy) * ticfrac + WEAPON_FUDGE_Y;
if (pspr->Flags & PSPF_ADDBOB)
{
sx += (pspr->Flags & PSPF_MIRROR) ? -bobx : bobx;
sy += boby;
}
if (pspr->Flags & PSPF_ADDWEAPON && pspr->GetID() != PSP_WEAPON)
{
sx += wx;
sy += wy;
}
if (renderHUDModel)
{
PolyRenderHUDModel(thread, PolyRenderer::Instance()->Scene.CurrentViewpoint->WorldToClip, 1, pspr, (float)sx, (float)sy);
return;
}
double yaspectMul = 1.2 * ((double)SCREENHEIGHT / SCREENWIDTH) * r_viewwindow.WidescreenRatio;
double pspritexscale = viewwindow.centerxwide / 160.0;
double pspriteyscale = pspritexscale * yaspectMul;
double pspritexiscale = 1 / pspritexscale;
int tleft = tex->GetScaledLeftOffsetPo();
int twidth = tex->GetScaledWidth();
// calculate edges of the shape
//tx = sx - BASEXCENTER;
tx = (pspr->Flags & PSPF_MIRROR) ? ((BASEXCENTER - twidth) - (sx - tleft)) : ((sx - BASEXCENTER) - tleft);
x1 = xs_RoundToInt(viewwindow.centerx + tx * pspritexscale);
// off the right side
if (x1 > viewwidth)
return;
tx += twidth;
x2 = xs_RoundToInt(viewwindow.centerx + tx * pspritexscale);
// off the left side
if (x2 <= 0)
return;
// store information in a vissprite
PolyNoAccelPlayerSprite vis;
vis.renderflags = owner->renderflags;
vis.texturemid = (BASEYCENTER - sy) * tex->GetScale().Y + tex->GetTopOffsetPo();
if (viewpoint.camera->player && (renderToCanvas ||
viewheight == renderTarget->GetHeight() ||
(renderTarget->GetWidth() > (BASEXCENTER * 2))))
{ // Adjust PSprite for fullscreen views
vis.texturemid -= pspr->GetYAdjust(renderToCanvas || viewheight == renderTarget->GetHeight());
}
if (pspr->GetID() < PSP_TARGETCENTER)
{ // Move the weapon down for 1280x1024.
vis.texturemid -= AspectPspriteOffset(viewwindow.WidescreenRatio);
}
vis.x1 = x1 < 0 ? 0 : x1;
vis.x2 = x2 >= viewwidth ? viewwidth : x2;
vis.xscale = FLOAT2FIXED(pspritexscale / tex->GetScale().X);
vis.yscale = float(pspriteyscale / tex->GetScale().Y);
vis.pic = tex;
// If flip is used, provided that it's not already flipped (that would just invert itself)
// (It's an XOR...)
if (!(flip) != !(pspr->Flags & PSPF_FLIP))
{
vis.xiscale = -FLOAT2FIXED(pspritexiscale * tex->GetScale().X);
vis.startfrac = (tex->GetWidth() << FRACBITS) - 1;
}
else
{
vis.xiscale = FLOAT2FIXED(pspritexiscale * tex->GetScale().X);
vis.startfrac = 0;
}
if (vis.x1 > x1)
vis.startfrac += vis.xiscale*(vis.x1 - x1);
noaccel = false;
FDynamicColormap *colormap_to_use = nullptr;
if (pspr->GetID() < PSP_TARGETCENTER)
{
auto rs = pspr->GetRenderStyle(owner->RenderStyle, owner->Alpha);
vis.RenderStyle = rs.first;
vis.Alpha = rs.second;
if (!vis.RenderStyle.IsVisible(vis.Alpha))
return;
//-----------------------------------------------------------------------------
// The software renderer cannot invert the source without inverting the overlay
// too. That means if the source is inverted, we need to do the reverse of what
// the invert overlay flag says to do.
bool invertcolormap = (vis.RenderStyle.Flags & STYLEF_InvertOverlay) != 0;
if (vis.RenderStyle.Flags & STYLEF_InvertSource)
{
invertcolormap = !invertcolormap;
}
const FState* const psprState = pspr->GetState();
bool fullbright = !foggy && (psprState == nullptr ? false : psprState->GetFullbright());
bool fadeToBlack = (vis.RenderStyle.Flags & STYLEF_FadeToBlack) != 0;
vis.Light.SetColormap(0, spriteshade, basecolormap, fullbright, invertcolormap, fadeToBlack);
colormap_to_use = (FDynamicColormap*)vis.Light.BaseColormap;
if (viewpoint.camera->Inventory != nullptr)
{
visstyle_t visstyle;
visstyle.Alpha = vis.Alpha;
visstyle.RenderStyle = STYLE_Count;
visstyle.Invert = false;
viewpoint.camera->Inventory->AlterWeaponSprite(&visstyle);
if (!(pspr->Flags & PSPF_FORCEALPHA)) vis.Alpha = visstyle.Alpha;
if (visstyle.RenderStyle != STYLE_Count && !(pspr->Flags & PSPF_FORCESTYLE))
{
vis.RenderStyle = visstyle.RenderStyle;
}
if (visstyle.Invert)
{
vis.Light.BaseColormap = &SpecialSWColormaps[INVERSECOLORMAP];
vis.Light.ColormapNum = 0;
noaccel = true;
}
}
// If drawing with a BOOM colormap, disable acceleration.
if (vis.Light.BaseColormap == &NormalLight && NormalLight.Maps != realcolormaps.Maps)
{
noaccel = true;
}
#if 0
// The HW 2D drawer should be able to handle this without problems
// If the main colormap has fixed lights, and this sprite is being drawn with that
// colormap, disable acceleration so that the lights can remain fixed.
PolyCameraLight *cameraLight = PolyCameraLight::Instance();
if (!noaccel && cameraLight->ShaderColormap() == nullptr &&
NormalLightHasFixedLights && vis.Light.BaseColormap == &NormalLight &&
vis.pic->UseBasePalette())
{
noaccel = true;
}
#endif
}
else
{
colormap_to_use = basecolormap;
vis.Light.BaseColormap = basecolormap;
vis.Light.ColormapNum = 0;
}
// Check for hardware-assisted 2D. If it's available, and this sprite is not
// fuzzy, don't draw it until after the switch to 2D mode.
if (!noaccel && !renderToCanvas)
{
FRenderStyle style = vis.RenderStyle;
style.CheckFuzz();
if (style.BlendOp != STYLEOP_Fuzz)
{
PolyHWAccelPlayerSprite accelSprite;
accelSprite.pic = vis.pic;
accelSprite.texturemid = vis.texturemid;
accelSprite.yscale = vis.yscale;
accelSprite.xscale = vis.xscale;
accelSprite.Alpha = vis.Alpha;
accelSprite.RenderStyle = vis.RenderStyle;
accelSprite.Translation = vis.Translation;
accelSprite.FillColor = vis.FillColor;
accelSprite.basecolormap = colormap_to_use;
accelSprite.x1 = x1;
accelSprite.flip = vis.xiscale < 0;
if (vis.Light.BaseColormap >= &SpecialSWColormaps[0] &&
vis.Light.BaseColormap < &SpecialSWColormaps[SpecialColormaps.Size()])
{
accelSprite.special = &SpecialColormaps[vis.Light.BaseColormap - &SpecialSWColormaps[0]];
}
else if (PolyCameraLight::Instance()->ShaderColormap())
{
accelSprite.special = PolyCameraLight::Instance()->ShaderColormap();
}
else
{
accelSprite.overlay = colormap_to_use->Fade;
accelSprite.overlay.a = uint8_t(vis.Light.ColormapNum * 255 / NUMCOLORMAPS);
accelSprite.LightColor = colormap_to_use->Color;
accelSprite.Desaturate = (uint8_t)clamp(colormap_to_use->Desaturate, 0, 255);
}
AcceleratedSprites.Push(accelSprite);
return;
}
}
vis.Render(thread);
}
fixed_t RenderPolyPlayerSprites::LightLevelToShade(int lightlevel, bool foggy)
{
bool nolightfade = !foggy && ((PolyRenderer::Instance()->Level->flags3 & LEVEL3_NOLIGHTFADE));
if (nolightfade)
{
return (MAX(255 - lightlevel, 0) * NUMCOLORMAPS) << (FRACBITS - 8);
}
else
{
// Convert a light level into an unbounded colormap index (shade). Result is
// fixed point. Why the +12? I wish I knew, but experimentation indicates it
// is necessary in order to best reproduce Doom's original lighting.
return (NUMCOLORMAPS * 2 * FRACUNIT) - ((lightlevel + 12) * (FRACUNIT*NUMCOLORMAPS / 128));
}
}
/////////////////////////////////////////////////////////////////////////
void PolyNoAccelPlayerSprite::Render(PolyRenderThread *thread)
{
if (xscale == 0 || fabs(yscale) < (1.0f / 32000.0f))
{ // scaled to 0; can't see
return;
}
RectDrawArgs args;
args.SetStyle(RenderStyle, Alpha, FillColor, Translation, pic, false);
args.SetLight(Light.BaseColormap, 255 - (Light.ColormapNum << 3));
double centerY = viewheight / 2;
double y1, y2;
if (renderflags & RF_YFLIP)
{
y1 = centerY + (texturemid - pic->GetHeight()) * (-yscale);
y2 = y1 + pic->GetHeight() * (-yscale);
}
else
{
y1 = centerY - texturemid * yscale;
y2 = y1 + pic->GetHeight() * yscale;
}
args.Draw(thread, viewwindowx + x1, viewwindowx + x2, viewwindowy + y1, viewwindowy + y2, 0.0f, 1.0f, 0.0f, 1.0f);
}
/////////////////////////////////////////////////////////////////////////////
void PolyColormapLight::SetColormap(double visibility, int shade, FDynamicColormap *basecolormap, bool fullbright, bool invertColormap, bool fadeToBlack)
{
if (fadeToBlack)
{
if (invertColormap) // Fade to white
{
basecolormap = GetSpecialLights(basecolormap->Color, MAKERGB(255, 255, 255), basecolormap->Desaturate);
invertColormap = false;
}
else // Fade to black
{
basecolormap = GetSpecialLights(basecolormap->Color, MAKERGB(0, 0, 0), basecolormap->Desaturate);
}
}
if (invertColormap)
{
basecolormap = GetSpecialLights(basecolormap->Color, basecolormap->Fade.InverseColor(), basecolormap->Desaturate);
}
PolyCameraLight *cameraLight = PolyCameraLight::Instance();
if (cameraLight->FixedColormap())
{
BaseColormap = cameraLight->FixedColormap();
ColormapNum = 0;
}
else if (cameraLight->FixedLightLevel() >= 0)
{
BaseColormap = (r_fullbrightignoresectorcolor) ? &FullNormalLight : basecolormap;
ColormapNum = cameraLight->FixedLightLevel() >> COLORMAPSHIFT;
}
else if (fullbright)
{
BaseColormap = (r_fullbrightignoresectorcolor) ? &FullNormalLight : basecolormap;
ColormapNum = 0;
}
else
{
BaseColormap = basecolormap;
ColormapNum = GETPALOOKUP(visibility, shade);
}
}

View file

@ -1,106 +0,0 @@
/*
** Polygon Doom software renderer
** Copyright (c) 2016 Magnus Norddahl
**
** This software is provided 'as-is', without any express or implied
** warranty. In no event will the authors be held liable for any damages
** arising from the use of this software.
**
** Permission is granted to anyone to use this software for any purpose,
** including commercial applications, and to alter it and redistribute it
** freely, subject to the following restrictions:
**
** 1. The origin of this software must not be misrepresented; you must not
** claim that you wrote the original software. If you use this software
** in a product, an acknowledgment in the product documentation would be
** appreciated but is not required.
** 2. Altered source versions must be plainly marked as such, and must not be
** misrepresented as being the original software.
** 3. This notice may not be removed or altered from any source distribution.
**
*/
#pragma once
#include "r_defs.h"
class DPSprite;
struct FDynamicColormap;
class PolyColormapLight
{
public:
int ColormapNum = 0;
FSWColormap *BaseColormap = nullptr;
void SetColormap(double visibility, int shade, FDynamicColormap *basecolormap, bool fullbright, bool invertColormap, bool fadeToBlack);
};
class PolyNoAccelPlayerSprite
{
public:
short x1 = 0;
short x2 = 0;
double texturemid = 0.0;
fixed_t xscale = 0;
float yscale = 0.0f;
FSoftwareTexture *pic = nullptr;
fixed_t xiscale = 0;
fixed_t startfrac = 0;
float Alpha = 0.0f;
FRenderStyle RenderStyle;
uint32_t Translation = 0;
uint32_t FillColor = 0;
PolyColormapLight Light;
short renderflags = 0;
void Render(PolyRenderThread *thread);
};
class PolyHWAccelPlayerSprite
{
public:
FSoftwareTexture *pic = nullptr;
double texturemid = 0.0;
float yscale = 0.0f;
fixed_t xscale = 0;
float Alpha = 0.0f;
FRenderStyle RenderStyle;
uint32_t Translation = 0;
uint32_t FillColor = 0;
FDynamicColormap *basecolormap = nullptr;
int x1 = 0;
bool flip = false;
FSpecialColormap *special = nullptr;
PalEntry overlay = 0;
PalEntry LightColor = 0xffffffff;
uint8_t Desaturate = 0;
};
class RenderPolyPlayerSprites
{
public:
void Render(PolyRenderThread *thread);
void RenderRemainingSprites();
private:
void RenderSprite(PolyRenderThread *thread, DPSprite *pspr, AActor *owner, float bobx, float boby, double wx, double wy, double ticfrac, int spriteshade, FDynamicColormap *basecolormap, bool foggy);
static fixed_t LightLevelToShade(int lightlevel, bool foggy);
enum { BASEXCENTER = 160 };
enum { BASEYCENTER = 100 };
TArray<PolyHWAccelPlayerSprite> AcceleratedSprites;
sector_t tempsec;
bool renderHUDModel = false;
};

View file

@ -1,258 +0,0 @@
/*
** Polygon Doom software renderer
** Copyright (c) 2016 Magnus Norddahl
**
** This software is provided 'as-is', without any express or implied
** warranty. In no event will the authors be held liable for any damages
** arising from the use of this software.
**
** Permission is granted to anyone to use this software for any purpose,
** including commercial applications, and to alter it and redistribute it
** freely, subject to the following restrictions:
**
** 1. The origin of this software must not be misrepresented; you must not
** claim that you wrote the original software. If you use this software
** in a product, an acknowledgment in the product documentation would be
** appreciated but is not required.
** 2. Altered source versions must be plainly marked as such, and must not be
** misrepresented as being the original software.
** 3. This notice may not be removed or altered from any source distribution.
**
*/
#include <stdlib.h>
#include "templates.h"
#include "doomdef.h"
#include "p_maputl.h"
#include "sbar.h"
#include "g_levellocals.h"
#include "r_data/r_translate.h"
#include "poly_portal.h"
#include "polyrenderer/poly_renderer.h"
#include "polyrenderer/scene/poly_light.h"
#include "polyrenderer/scene/poly_scene.h"
/////////////////////////////////////////////////////////////////////////////
PolyDrawSectorPortal::PolyDrawSectorPortal(FSectorPortal *portal, bool ceiling) : Portal(portal), Ceiling(ceiling)
{
StencilValue = PolyRenderer::Instance()->GetNextStencilValue();
}
void PolyDrawSectorPortal::Render(int portalDepth)
{
if (Portal->mType == PORTS_HORIZON || Portal->mType == PORTS_PLANE)
return;
/*angle_t angle1 = PolyCull::PointToPseudoAngle(v1->fX(), v1->fY());
angle_t angle2 = PolyCull::PointToPseudoAngle(v2->fX(), v2->fY());
Segments.clear();
Segments.push_back({ angle1, angle2 });*/
SaveGlobals();
PortalViewpoint = PolyRenderer::Instance()->SetupPerspectiveMatrix();
PortalViewpoint.StencilValue = StencilValue;
PortalViewpoint.PortalDepth = portalDepth;
PortalViewpoint.PortalEnterSector = Portal->mDestination;
PolyRenderer::Instance()->Scene.Render(&PortalViewpoint);
RestoreGlobals();
}
void PolyDrawSectorPortal::SaveGlobals()
{
auto &viewpoint = PolyRenderer::Instance()->Viewpoint;
const auto &viewwindow = PolyRenderer::Instance()->Viewwindow;
SavedViewpoint = viewpoint;
SavedInvisibility = viewpoint.camera ? (viewpoint.camera->renderflags & RF_INVISIBLE) == RF_INVISIBLE : false;
if (Portal->mType == PORTS_SKYVIEWPOINT)
{
// Don't let gun flashes brighten the sky box
AActor *sky = Portal->mSkybox;
viewpoint.extralight = 0;
//PolyRenderer::Instance()->Light.SetVisibility(sky->args[0] * 0.25f);
viewpoint.Pos = sky->InterpolatedPosition(viewpoint.TicFrac);
viewpoint.Angles.Yaw = SavedViewpoint.Angles.Yaw + (sky->PrevAngles.Yaw + deltaangle(sky->PrevAngles.Yaw, sky->Angles.Yaw) * viewpoint.TicFrac);
}
else //if (Portal->mType == PORTS_STACKEDSECTORTHING || Portal->mType == PORTS_PORTAL || Portal->mType == PORTS_LINKEDPORTAL)
{
//extralight = pl->extralight;
//SetVisibility(pl->visibility);
viewpoint.Pos.X += Portal->mDisplacement.X;
viewpoint.Pos.Y += Portal->mDisplacement.Y;
}
viewpoint.camera = nullptr;
viewpoint.sector = Portal->mDestination;
viewpoint.SetViewAngle(viewwindow);
Portal->mFlags |= PORTSF_INSKYBOX;
if (Portal->mPartner > 0) PolyRenderer::Instance()->Level->sectorPortals[Portal->mPartner].mFlags |= PORTSF_INSKYBOX;
}
void PolyDrawSectorPortal::RestoreGlobals()
{
Portal->mFlags &= ~PORTSF_INSKYBOX;
if (Portal->mPartner > 0) PolyRenderer::Instance()->Level->sectorPortals[Portal->mPartner].mFlags &= ~PORTSF_INSKYBOX;
auto &viewpoint = PolyRenderer::Instance()->Viewpoint;
const auto &viewwindow = PolyRenderer::Instance()->Viewwindow;
viewpoint = SavedViewpoint;
if (viewpoint.camera)
{
if (SavedInvisibility)
viewpoint.camera->renderflags |= RF_INVISIBLE;
else
viewpoint.camera->renderflags &= ~RF_INVISIBLE;
}
//PolyRenderer::Instance()->Light.SetVisibility(savedvisibility);
viewpoint.SetViewAngle(viewwindow);
}
/////////////////////////////////////////////////////////////////////////////
PolyDrawLinePortal::PolyDrawLinePortal(FLinePortal *portal) : Portal(portal)
{
StencilValue = PolyRenderer::Instance()->GetNextStencilValue();
}
PolyDrawLinePortal::PolyDrawLinePortal(line_t *mirror) : Mirror(mirror)
{
StencilValue = PolyRenderer::Instance()->GetNextStencilValue();
}
void PolyDrawLinePortal::Render(int portalDepth)
{
SaveGlobals();
// Find portal destination line and make sure it faces the right way
line_t *clipLine = Portal ? Portal->mDestination : Mirror;
DVector2 pt1 = clipLine->v1->fPos() - PolyRenderer::Instance()->Viewpoint.Pos;
DVector2 pt2 = clipLine->v2->fPos() - PolyRenderer::Instance()->Viewpoint.Pos;
bool backfacing = (pt1.Y * (pt1.X - pt2.X) + pt1.X * (pt2.Y - pt1.Y) >= 0);
PortalViewpoint = PolyRenderer::Instance()->SetupPerspectiveMatrix(Mirror != nullptr);
PortalViewpoint.StencilValue = StencilValue;
PortalViewpoint.PortalDepth = portalDepth;
PortalViewpoint.PortalEnterLine = clipLine;
PortalViewpoint.PortalEnterSector = backfacing ? clipLine->frontsector : clipLine->backsector;
PolyRenderer::Instance()->Scene.Render(&PortalViewpoint);
RestoreGlobals();
}
void PolyDrawLinePortal::SaveGlobals()
{
auto &viewpoint = PolyRenderer::Instance()->Viewpoint;
const auto &viewwindow = PolyRenderer::Instance()->Viewwindow;
SavedViewpoint = viewpoint;
SavedInvisibility = viewpoint.camera ? (viewpoint.camera->renderflags & RF_INVISIBLE) == RF_INVISIBLE : false;
if (Mirror)
{
DAngle startang = viewpoint.Angles.Yaw;
DVector3 startpos = viewpoint.Pos;
vertex_t *v1 = Mirror->v1;
// Reflect the current view behind the mirror.
if (Mirror->Delta().X == 0)
{ // vertical mirror
viewpoint.Pos.X = v1->fX() - startpos.X + v1->fX();
}
else if (Mirror->Delta().Y == 0)
{ // horizontal mirror
viewpoint.Pos.Y = v1->fY() - startpos.Y + v1->fY();
}
else
{ // any mirror
vertex_t *v2 = Mirror->v2;
double dx = v2->fX() - v1->fX();
double dy = v2->fY() - v1->fY();
double x1 = v1->fX();
double y1 = v1->fY();
double x = startpos.X;
double y = startpos.Y;
// the above two cases catch len == 0
double r = ((x - x1)*dx + (y - y1)*dy) / (dx*dx + dy*dy);
viewpoint.Pos.X = (x1 + r * dx) * 2 - x;
viewpoint.Pos.Y = (y1 + r * dy) * 2 - y;
}
viewpoint.Angles.Yaw = Mirror->Delta().Angle() * 2 - startang;
if (viewpoint.camera)
viewpoint.camera->renderflags &= ~RF_INVISIBLE;
}
else
{
auto src = Portal->mOrigin;
auto dst = Portal->mDestination;
P_TranslatePortalXY(src, viewpoint.Pos.X, viewpoint.Pos.Y);
P_TranslatePortalZ(src, viewpoint.Pos.Z);
P_TranslatePortalAngle(src, viewpoint.Angles.Yaw);
P_TranslatePortalXY(src, viewpoint.Path[0].X, viewpoint.Path[0].Y);
P_TranslatePortalXY(src, viewpoint.Path[1].X, viewpoint.Path[1].Y);
if (viewpoint.camera && !viewpoint.showviewer)
viewpoint.camera->renderflags |= RF_INVISIBLE;
/* What is this code trying to do?
if (viewpoint.camera)
{
viewpoint.camera->renderflags &= ~RF_INVISIBLE;
if (!viewpoint.showviewer && P_PointOnLineSidePrecise(viewpoint.Path[0], dst) != P_PointOnLineSidePrecise(viewpoint.Path[1], dst))
{
double distp = (viewpoint.Path[0] - viewpoint.Path[1]).Length();
if (distp > EQUAL_EPSILON)
{
double dist1 = (viewpoint.Pos - viewpoint.Path[0]).Length();
double dist2 = (viewpoint.Pos - viewpoint.Path[1]).Length();
if (dist1 + dist2 < distp + 1)
{
viewpoint.camera->renderflags |= RF_INVISIBLE;
}
}
}
}
*/
}
viewpoint.camera = nullptr;
viewpoint.sector = viewpoint.ViewLevel->PointInRenderSubsector(viewpoint.Pos)->sector;
viewpoint.SetViewAngle(viewwindow);
}
void PolyDrawLinePortal::RestoreGlobals()
{
auto &viewpoint = PolyRenderer::Instance()->Viewpoint;
const auto &viewwindow = PolyRenderer::Instance()->Viewwindow;
viewpoint = SavedViewpoint;
if (viewpoint.camera)
{
if (SavedInvisibility)
viewpoint.camera->renderflags |= RF_INVISIBLE;
else
viewpoint.camera->renderflags &= ~RF_INVISIBLE;
}
viewpoint.SetViewAngle(viewwindow);
}

View file

@ -1,84 +0,0 @@
/*
** Polygon Doom software renderer
** Copyright (c) 2016 Magnus Norddahl
**
** This software is provided 'as-is', without any express or implied
** warranty. In no event will the authors be held liable for any damages
** arising from the use of this software.
**
** Permission is granted to anyone to use this software for any purpose,
** including commercial applications, and to alter it and redistribute it
** freely, subject to the following restrictions:
**
** 1. The origin of this software must not be misrepresented; you must not
** claim that you wrote the original software. If you use this software
** in a product, an acknowledgment in the product documentation would be
** appreciated but is not required.
** 2. Altered source versions must be plainly marked as such, and must not be
** misrepresented as being the original software.
** 3. This notice may not be removed or altered from any source distribution.
**
*/
#pragma once
#include "poly_scene.h"
struct PolyPortalVertexRange
{
PolyPortalVertexRange(const TriVertex *vertices, int count) : Vertices(vertices), Count(count) { }
const TriVertex *Vertices;
int Count;
};
class PolyPortalSegment
{
public:
PolyPortalSegment(angle_t start, angle_t end) : Start(start), End(end) { }
angle_t Start, End;
};
class PolyDrawSectorPortal
{
public:
PolyDrawSectorPortal(FSectorPortal *portal, bool ceiling);
void Render(int portalDepth);
FSectorPortal *Portal = nullptr;
uint32_t StencilValue = 0;
std::vector<PolyPortalVertexRange> Shape;
private:
void SaveGlobals();
void RestoreGlobals();
bool Ceiling;
PolyPortalViewpoint PortalViewpoint;
FRenderViewpoint SavedViewpoint;
bool SavedInvisibility;
};
class PolyDrawLinePortal
{
public:
PolyDrawLinePortal(FLinePortal *portal);
PolyDrawLinePortal(line_t *mirror);
void Render(int portalDepth);
FLinePortal *Portal = nullptr;
line_t *Mirror = nullptr;
uint32_t StencilValue = 0;
std::vector<PolyPortalVertexRange> Shape;
private:
void SaveGlobals();
void RestoreGlobals();
PolyPortalViewpoint PortalViewpoint;
FRenderViewpoint SavedViewpoint;
bool SavedInvisibility;
};

View file

@ -1,594 +0,0 @@
/*
** Polygon Doom software renderer
** Copyright (c) 2016 Magnus Norddahl
**
** This software is provided 'as-is', without any express or implied
** warranty. In no event will the authors be held liable for any damages
** arising from the use of this software.
**
** Permission is granted to anyone to use this software for any purpose,
** including commercial applications, and to alter it and redistribute it
** freely, subject to the following restrictions:
**
** 1. The origin of this software must not be misrepresented; you must not
** claim that you wrote the original software. If you use this software
** in a product, an acknowledgment in the product documentation would be
** appreciated but is not required.
** 2. Altered source versions must be plainly marked as such, and must not be
** misrepresented as being the original software.
** 3. This notice may not be removed or altered from any source distribution.
**
*/
#include <stdlib.h>
#include "templates.h"
#include "doomdef.h"
#include "p_maputl.h"
#include "sbar.h"
#include "r_data/r_translate.h"
#include "polyrenderer/poly_renderer.h"
#include "polyrenderer/scene/poly_scene.h"
#include "polyrenderer/scene/poly_light.h"
#include "polyrenderer/scene/poly_wall.h"
#include "polyrenderer/scene/poly_wallsprite.h"
#include "polyrenderer/scene/poly_plane.h"
#include "polyrenderer/scene/poly_particle.h"
#include "polyrenderer/scene/poly_sprite.h"
EXTERN_CVAR(Int, r_portal_recursions)
extern double model_distance_cull;
/////////////////////////////////////////////////////////////////////////////
RenderPolyScene::RenderPolyScene()
{
}
RenderPolyScene::~RenderPolyScene()
{
}
void RenderPolyScene::Render(PolyPortalViewpoint *viewpoint)
{
PolyPortalViewpoint *oldviewpoint = CurrentViewpoint;
CurrentViewpoint = viewpoint;
PolyRenderThread *thread = PolyRenderer::Instance()->Threads.MainThread();
CurrentViewpoint->ObjectsStart = thread->TranslucentObjects.size();
CurrentViewpoint->SectorPortalsStart = thread->SectorPortals.size();
CurrentViewpoint->LinePortalsStart = thread->LinePortals.size();
PolyCullCycles.Clock();
Cull.CullScene(CurrentViewpoint->PortalEnterSector, CurrentViewpoint->PortalEnterLine);
PolyCullCycles.Unclock();
RenderSectors();
PolyMaskedCycles.Clock();
const auto &rviewpoint = PolyRenderer::Instance()->Viewpoint;
for (uint32_t sectorIndex : Cull.SeenSectors)
{
sector_t *sector = &PolyRenderer::Instance()->Level->sectors[sectorIndex];
for (AActor *thing = sector->thinglist; thing != nullptr; thing = thing->snext)
{
if (!RenderPolySprite::IsThingCulled(thing))
{
int spritenum = thing->sprite;
bool isPicnumOverride = thing->picnum.isValid();
FSpriteModelFrame *modelframe = isPicnumOverride ? nullptr : FindModelFrame(thing->GetClass(), spritenum, thing->frame, !!(thing->flags & MF_DROPPED));
double distanceSquared = (thing->Pos() - rviewpoint.Pos).LengthSquared();
if (r_modelscene && modelframe && distanceSquared < model_distance_cull)
{
AddModel(thread, thing, distanceSquared, thing->Pos());
}
else
{
DVector2 left, right;
if (!RenderPolySprite::GetLine(thing, left, right))
continue;
AddSprite(thread, thing, distanceSquared, left, right);
}
}
}
}
PolyMaskedCycles.Unclock();
CurrentViewpoint->ObjectsEnd = thread->TranslucentObjects.size();
CurrentViewpoint->SectorPortalsEnd = thread->SectorPortals.size();
CurrentViewpoint->LinePortalsEnd = thread->LinePortals.size();
Skydome.Render(thread, CurrentViewpoint->WorldToView, CurrentViewpoint->WorldToClip);
RenderPortals();
RenderTranslucent();
CurrentViewpoint = oldviewpoint;
}
void RenderPolyScene::RenderSectors()
{
PolyRenderThread *mainthread = PolyRenderer::Instance()->Threads.MainThread();
int totalcount = (int)Cull.PvsSubsectors.size();
uint32_t *subsectors = Cull.PvsSubsectors.data();
PolyOpaqueCycles.Clock();
PolyRenderer::Instance()->Threads.RenderThreadSlices(totalcount, [&](PolyRenderThread *thread)
{
PolyTriangleDrawer::SetCullCCW(thread->DrawQueue, !CurrentViewpoint->Mirror);
PolyTriangleDrawer::SetTransform(thread->DrawQueue, thread->FrameMemory->NewObject<Mat4f>(CurrentViewpoint->WorldToClip), nullptr);
if (thread != mainthread)
{
thread->TranslucentObjects.clear();
thread->SectorPortals.clear();
thread->LinePortals.clear();
}
int start = thread->Start;
int end = thread->End;
for (int i = start; i < end; i++)
{
RenderSubsector(thread, &PolyRenderer::Instance()->Level->subsectors[subsectors[i]], i);
}
}, [&](PolyRenderThread *thread)
{
const auto &objects = thread->TranslucentObjects;
mainthread->TranslucentObjects.insert(mainthread->TranslucentObjects.end(), objects.begin(), objects.end());
});
PolyOpaqueCycles.Unclock();
}
void RenderPolyScene::RenderSubsector(PolyRenderThread *thread, subsector_t *sub, uint32_t subsectorDepth)
{
sector_t *frontsector = sub->sector;
auto Level = frontsector->Level;
frontsector->MoreFlags |= SECMF_DRAWN;
if (sub->polys)
{
if (sub->BSP == nullptr || sub->BSP->bDirty)
{
sub->BuildPolyBSP();
// This is done by the GL renderer, but not the sw renderer. No idea what the purpose is..
for (unsigned i = 0; i < sub->BSP->Segs.Size(); i++)
{
sub->BSP->Segs[i].Subsector = sub;
sub->BSP->Segs[i].PartnerSeg = nullptr;
}
}
if (sub->BSP->Nodes.Size() == 0)
{
RenderPolySubsector(thread, &sub->BSP->Subsectors[0], subsectorDepth, frontsector);
}
else
{
RenderPolyNode(thread, &sub->BSP->Nodes.Last(), subsectorDepth, frontsector);
}
}
PolyTransferHeights fakeflat(sub);
Render3DFloorPlane::RenderPlanes(thread, sub, CurrentViewpoint->StencilValue, subsectorDepth, thread->TranslucentObjects);
RenderPolyPlane::RenderPlanes(thread, fakeflat, CurrentViewpoint->StencilValue, Cull.MaxCeilingHeight, Cull.MinFloorHeight, thread->SectorPortals, CurrentViewpoint->SectorPortalsStart);
for (uint32_t i = 0; i < sub->numlines; i++)
{
if (Cull.IsLineSegVisible(subsectorDepth, i))
{
seg_t *line = &sub->firstline[i];
RenderLine(thread, sub, line, fakeflat.FrontSector, subsectorDepth);
}
}
int subsectorIndex = sub->Index();
for (int i = Level->ParticlesInSubsec[subsectorIndex]; i != NO_PARTICLE; i = Level->Particles[i].snext)
{
particle_t *particle = &Level->Particles[i];
thread->TranslucentObjects.push_back(thread->FrameMemory->NewObject<PolyTranslucentParticle>(particle, sub, subsectorDepth, CurrentViewpoint->StencilValue));
}
}
void RenderPolyScene::RenderPolyNode(PolyRenderThread *thread, void *node, uint32_t subsectorDepth, sector_t *frontsector)
{
while (!((size_t)node & 1)) // Keep going until found a subsector
{
node_t *bsp = (node_t *)node;
// Decide which side the view point is on.
int side = PointOnSide(PolyRenderer::Instance()->Viewpoint.Pos, bsp);
// Recursively divide front space (toward the viewer).
RenderPolyNode(thread, bsp->children[side], subsectorDepth, frontsector);
// Possibly divide back space (away from the viewer).
side ^= 1;
// Don't bother culling on poly objects
//if (!CheckBBox(bsp->bbox[side]))
// return;
node = bsp->children[side];
}
subsector_t *sub = (subsector_t *)((uint8_t *)node - 1);
RenderPolySubsector(thread, sub, subsectorDepth, frontsector);
}
void RenderPolyScene::RenderPolySubsector(PolyRenderThread *thread, subsector_t *sub, uint32_t subsectorDepth, sector_t *frontsector)
{
const auto &viewpoint = PolyRenderer::Instance()->Viewpoint;
for (uint32_t i = 0; i < sub->numlines; i++)
{
seg_t *line = &sub->firstline[i];
if (line->linedef)
{
// Reject lines not facing viewer
DVector2 pt1 = line->v1->fPos() - viewpoint.Pos;
DVector2 pt2 = line->v2->fPos() - viewpoint.Pos;
if (pt1.Y * (pt1.X - pt2.X) + pt1.X * (pt2.Y - pt1.Y) >= 0)
continue;
// Tell automap we saw this
if (!PolyRenderer::Instance()->DontMapLines && line->linedef)
{
line->linedef->flags |= ML_MAPPED;
sub->flags |= SSECMF_DRAWN;
}
RenderPolyWall::RenderLine(thread, line, frontsector, subsectorDepth, CurrentViewpoint->StencilValue, thread->TranslucentObjects, thread->LinePortals, CurrentViewpoint->LinePortalsStart, CurrentViewpoint->PortalEnterLine);
}
}
}
int RenderPolyScene::PointOnSide(const DVector2 &pos, const node_t *node)
{
return DMulScale32(FLOAT2FIXED(pos.Y) - node->y, node->dx, node->x - FLOAT2FIXED(pos.X), node->dy) > 0;
}
void RenderPolyScene::AddSprite(PolyRenderThread *thread, AActor *thing, double sortDistance, const DVector2 &left, const DVector2 &right)
{
if (PolyRenderer::Instance()->Level->nodes.Size() == 0)
{
subsector_t *sub = &PolyRenderer::Instance()->Level->subsectors[0];
if (Cull.SubsectorDepths[sub->Index()] != 0xffffffff)
thread->TranslucentObjects.push_back(thread->FrameMemory->NewObject<PolyTranslucentThing>(thing, sub, Cull.SubsectorDepths[sub->Index()], sortDistance, 0.0f, 1.0f, CurrentViewpoint->StencilValue));
}
else
{
AddSprite(thread, thing, sortDistance, left, right, 0.0, 1.0, PolyRenderer::Instance()->Level->HeadNode());
}
}
void RenderPolyScene::AddSprite(PolyRenderThread *thread, AActor *thing, double sortDistance, DVector2 left, DVector2 right, double t1, double t2, void *node)
{
while (!((size_t)node & 1)) // Keep going until found a subsector
{
node_t *bsp = (node_t *)node;
DVector2 planePos(FIXED2DBL(bsp->x), FIXED2DBL(bsp->y));
DVector2 planeNormal = DVector2(FIXED2DBL(-bsp->dy), FIXED2DBL(bsp->dx));
double planeD = planeNormal | planePos;
int sideLeft = (left | planeNormal) > planeD;
int sideRight = (right | planeNormal) > planeD;
if (sideLeft != sideRight)
{
double dotLeft = planeNormal | left;
double dotRight = planeNormal | right;
double t = (planeD - dotLeft) / (dotRight - dotLeft);
DVector2 mid = left * (1.0 - t) + right * t;
double tmid = t1 * (1.0 - t) + t2 * t;
AddSprite(thread, thing, sortDistance, mid, right, tmid, t2, bsp->children[sideRight]);
right = mid;
t2 = tmid;
}
node = bsp->children[sideLeft];
}
subsector_t *sub = (subsector_t *)((uint8_t *)node - 1);
if (Cull.SubsectorDepths[sub->Index()] != 0xffffffff)
thread->TranslucentObjects.push_back(thread->FrameMemory->NewObject<PolyTranslucentThing>(thing, sub, Cull.SubsectorDepths[sub->Index()], sortDistance, (float)t1, (float)t2, CurrentViewpoint->StencilValue));
}
void RenderPolyScene::AddModel(PolyRenderThread *thread, AActor *thing, double sortDistance, DVector2 pos)
{
if (PolyRenderer::Instance()->Level->nodes.Size() == 0)
{
subsector_t *sub = &PolyRenderer::Instance()->Level->subsectors[0];
if (Cull.SubsectorDepths[sub->Index()] != 0xffffffff)
thread->TranslucentObjects.push_back(thread->FrameMemory->NewObject<PolyTranslucentThing>(thing, sub, Cull.SubsectorDepths[sub->Index()], sortDistance, 0.0f, 1.0f, CurrentViewpoint->StencilValue));
}
else
{
void *node = PolyRenderer::Instance()->Level->HeadNode();
while (!((size_t)node & 1)) // Keep going until found a subsector
{
node_t *bsp = (node_t *)node;
DVector2 planePos(FIXED2DBL(bsp->x), FIXED2DBL(bsp->y));
DVector2 planeNormal = DVector2(FIXED2DBL(-bsp->dy), FIXED2DBL(bsp->dx));
double planeD = planeNormal | planePos;
int side = (pos | planeNormal) > planeD;
node = bsp->children[side];
}
subsector_t *sub = (subsector_t *)((uint8_t *)node - 1);
if (Cull.SubsectorDepths[sub->Index()] != 0xffffffff)
thread->TranslucentObjects.push_back(thread->FrameMemory->NewObject<PolyTranslucentThing>(thing, sub, Cull.SubsectorDepths[sub->Index()], sortDistance, 0.0f, 1.0f, CurrentViewpoint->StencilValue));
}
}
void RenderPolyScene::RenderLine(PolyRenderThread *thread, subsector_t *sub, seg_t *line, sector_t *frontsector, uint32_t subsectorDepth)
{
// Tell automap we saw this
if (!PolyRenderer::Instance()->DontMapLines && line->linedef)
{
line->linedef->flags |= ML_MAPPED;
sub->flags |= SSECMF_DRAWN;
}
// Render 3D floor sides
if (line->sidedef && line->backsector && line->backsector->e && line->backsector->e->XFloor.ffloors.Size())
{
for (unsigned int i = 0; i < line->backsector->e->XFloor.ffloors.Size(); i++)
{
F3DFloor *fakeFloor = line->backsector->e->XFloor.ffloors[i];
RenderPolyWall::Render3DFloorLine(thread, line, frontsector, subsectorDepth, CurrentViewpoint->StencilValue, fakeFloor, thread->TranslucentObjects);
}
}
// Render wall, and update culling info if its an occlusion blocker
RenderPolyWall::RenderLine(thread, line, frontsector, subsectorDepth, CurrentViewpoint->StencilValue, thread->TranslucentObjects, thread->LinePortals, CurrentViewpoint->LinePortalsStart, CurrentViewpoint->PortalEnterLine);
}
void RenderPolyScene::RenderPortals()
{
PolyRenderThread *thread = PolyRenderer::Instance()->Threads.MainThread();
bool enterPortals = CurrentViewpoint->PortalDepth < r_portal_recursions;
if (enterPortals)
{
for (size_t i = CurrentViewpoint->SectorPortalsStart; i < CurrentViewpoint->SectorPortalsEnd; i++)
thread->SectorPortals[i]->Render(CurrentViewpoint->PortalDepth + 1);
for (size_t i = CurrentViewpoint->LinePortalsStart; i < CurrentViewpoint->LinePortalsEnd; i++)
thread->LinePortals[i]->Render(CurrentViewpoint->PortalDepth + 1);
}
Mat4f *transform = thread->FrameMemory->NewObject<Mat4f>(CurrentViewpoint->WorldToClip);
PolyTriangleDrawer::SetCullCCW(thread->DrawQueue, !CurrentViewpoint->Mirror);
PolyTriangleDrawer::SetTransform(thread->DrawQueue, transform, nullptr);
PolyDrawArgs args;
args.SetWriteColor(!enterPortals);
args.SetDepthTest(false);
if (!enterPortals) // Fill with black
{
bool foggy = false;
args.SetLight(&NormalLight, 255, PolyRenderer::Instance()->Light.WallGlobVis(foggy), true);
args.SetStyle(TriBlendMode::Fill);
args.SetColor(0, 0);
}
for (size_t i = CurrentViewpoint->SectorPortalsStart; i < CurrentViewpoint->SectorPortalsEnd; i++)
{
const auto &portal = thread->SectorPortals[i];
args.SetStencilTestValue(enterPortals ? portal->StencilValue + 1 : portal->StencilValue);
args.SetWriteStencil(true, CurrentViewpoint->StencilValue + 1);
for (const auto &verts : portal->Shape)
{
PolyTriangleDrawer::DrawArray(thread->DrawQueue, args, verts.Vertices, verts.Count, PolyDrawMode::TriangleFan);
}
}
for (size_t i = CurrentViewpoint->LinePortalsStart; i < CurrentViewpoint->LinePortalsEnd; i++)
{
const auto &portal = thread->LinePortals[i];
args.SetStencilTestValue(enterPortals ? portal->StencilValue + 1 : portal->StencilValue);
args.SetWriteStencil(true, CurrentViewpoint->StencilValue + 1);
for (const auto &verts : portal->Shape)
{
PolyTriangleDrawer::DrawArray(thread->DrawQueue, args, verts.Vertices, verts.Count, PolyDrawMode::TriangleFan);
}
}
}
void RenderPolyScene::RenderTranslucent()
{
PolyRenderThread *thread = PolyRenderer::Instance()->Threads.MainThread();
Mat4f *transform = thread->FrameMemory->NewObject<Mat4f>(CurrentViewpoint->WorldToClip);
PolyTriangleDrawer::SetCullCCW(thread->DrawQueue, !CurrentViewpoint->Mirror);
PolyTriangleDrawer::SetTransform(thread->DrawQueue, transform, nullptr);
PolyMaskedCycles.Clock();
// Draw all translucent objects back to front
std::stable_sort(
thread->TranslucentObjects.begin() + CurrentViewpoint->ObjectsStart,
thread->TranslucentObjects.begin() + CurrentViewpoint->ObjectsEnd,
[](auto a, auto b) { return *a < *b; });
auto objects = thread->TranslucentObjects.data();
for (size_t i = CurrentViewpoint->ObjectsEnd; i > CurrentViewpoint->ObjectsStart; i--)
{
PolyTranslucentObject *obj = objects[i - 1];
obj->Render(thread);
obj->~PolyTranslucentObject();
}
PolyMaskedCycles.Unclock();
}
/////////////////////////////////////////////////////////////////////////////
PolyTransferHeights::PolyTransferHeights(subsector_t *sub) : Subsector(sub)
{
sector_t *sec = sub->sector;
// If player's view height is underneath fake floor, lower the
// drawn ceiling to be just under the floor height, and replace
// the drawn floor and ceiling textures, and light PolyRenderer::Instance()->Level->, with
// the control sector's.
//
// Similar for ceiling, only reflected.
// [RH] allow per-plane lighting
FloorLightLevel = sec->GetFloorLight();
CeilingLightLevel = sec->GetCeilingLight();
FakeSide = PolyWaterFakeSide::Center;
const sector_t *s = sec->GetHeightSec();
if (s != nullptr)
{
sector_t *heightsec = PolyRenderer::Instance()->Viewpoint.sector->heightsec;
bool underwater = (heightsec && heightsec->floorplane.PointOnSide(PolyRenderer::Instance()->Viewpoint.Pos) <= 0);
bool doorunderwater = false;
int diffTex = (s->MoreFlags & SECMF_CLIPFAKEPLANES);
// Replace sector being drawn with a copy to be hacked
tempsec = *sec;
// Replace floor and ceiling height with control sector's heights.
if (diffTex)
{
if (s->floorplane.CopyPlaneIfValid(&tempsec.floorplane, &sec->ceilingplane))
{
tempsec.SetTexture(sector_t::floor, s->GetTexture(sector_t::floor), false);
}
else if (s->MoreFlags & SECMF_FAKEFLOORONLY)
{
if (underwater)
{
tempsec.Colormap = s->Colormap;
if (!(s->MoreFlags & SECMF_NOFAKELIGHT))
{
tempsec.lightlevel = s->lightlevel;
FloorLightLevel = s->GetFloorLight();
CeilingLightLevel = s->GetCeilingLight();
}
FakeSide = PolyWaterFakeSide::BelowFloor;
FrontSector = &tempsec;
return;
}
FrontSector = sec;
return;
}
}
else
{
tempsec.floorplane = s->floorplane;
}
if (!(s->MoreFlags & SECMF_FAKEFLOORONLY))
{
if (diffTex)
{
if (s->ceilingplane.CopyPlaneIfValid(&tempsec.ceilingplane, &sec->floorplane))
{
tempsec.SetTexture(sector_t::ceiling, s->GetTexture(sector_t::ceiling), false);
}
}
else
{
tempsec.ceilingplane = s->ceilingplane;
}
}
double refceilz = s->ceilingplane.ZatPoint(PolyRenderer::Instance()->Viewpoint.Pos);
double orgceilz = sec->ceilingplane.ZatPoint(PolyRenderer::Instance()->Viewpoint.Pos);
if (underwater || doorunderwater)
{
tempsec.floorplane = sec->floorplane;
tempsec.ceilingplane = s->floorplane;
tempsec.ceilingplane.FlipVert();
tempsec.ceilingplane.ChangeHeight(-1 / 65536.);
tempsec.Colormap = s->Colormap;
}
// killough 11/98: prevent sudden light changes from non-water sectors:
if (underwater || doorunderwater)
{
// head-below-floor hack
tempsec.SetTexture(sector_t::floor, diffTex ? sec->GetTexture(sector_t::floor) : s->GetTexture(sector_t::floor), false);
tempsec.planes[sector_t::floor].xform = s->planes[sector_t::floor].xform;
tempsec.ceilingplane = s->floorplane;
tempsec.ceilingplane.FlipVert();
tempsec.ceilingplane.ChangeHeight(-1 / 65536.);
if (s->GetTexture(sector_t::ceiling) == skyflatnum)
{
tempsec.floorplane = tempsec.ceilingplane;
tempsec.floorplane.FlipVert();
tempsec.floorplane.ChangeHeight(+1 / 65536.);
tempsec.SetTexture(sector_t::ceiling, tempsec.GetTexture(sector_t::floor), false);
tempsec.planes[sector_t::ceiling].xform = tempsec.planes[sector_t::floor].xform;
}
else
{
tempsec.SetTexture(sector_t::ceiling, diffTex ? s->GetTexture(sector_t::floor) : s->GetTexture(sector_t::ceiling), false);
tempsec.planes[sector_t::ceiling].xform = s->planes[sector_t::ceiling].xform;
}
if (!(s->MoreFlags & SECMF_NOFAKELIGHT))
{
tempsec.lightlevel = s->lightlevel;
FloorLightLevel = s->GetFloorLight();
CeilingLightLevel = s->GetCeilingLight();
}
FakeSide = PolyWaterFakeSide::BelowFloor;
}
else if (heightsec && heightsec->ceilingplane.PointOnSide(PolyRenderer::Instance()->Viewpoint.Pos) <= 0 && orgceilz > refceilz && !(s->MoreFlags & SECMF_FAKEFLOORONLY))
{
// Above-ceiling hack
tempsec.ceilingplane = s->ceilingplane;
tempsec.floorplane = s->ceilingplane;
tempsec.floorplane.FlipVert();
tempsec.floorplane.ChangeHeight(+1 / 65536.);
tempsec.Colormap = s->Colormap;
tempsec.SetTexture(sector_t::ceiling, diffTex ? sec->GetTexture(sector_t::ceiling) : s->GetTexture(sector_t::ceiling), false);
tempsec.SetTexture(sector_t::floor, s->GetTexture(sector_t::ceiling), false);
tempsec.planes[sector_t::ceiling].xform = tempsec.planes[sector_t::floor].xform = s->planes[sector_t::ceiling].xform;
if (s->GetTexture(sector_t::floor) != skyflatnum)
{
tempsec.ceilingplane = sec->ceilingplane;
tempsec.SetTexture(sector_t::floor, s->GetTexture(sector_t::floor), false);
tempsec.planes[sector_t::floor].xform = s->planes[sector_t::floor].xform;
}
if (!(s->MoreFlags & SECMF_NOFAKELIGHT))
{
tempsec.lightlevel = s->lightlevel;
FloorLightLevel = s->GetFloorLight();
CeilingLightLevel = s->GetCeilingLight();
}
FakeSide = PolyWaterFakeSide::AboveCeiling;
}
sec = &tempsec;
}
FrontSector = sec;
}

View file

@ -1,129 +0,0 @@
/*
** Polygon Doom software renderer
** Copyright (c) 2016 Magnus Norddahl
**
** This software is provided 'as-is', without any express or implied
** warranty. In no event will the authors be held liable for any damages
** arising from the use of this software.
**
** Permission is granted to anyone to use this software for any purpose,
** including commercial applications, and to alter it and redistribute it
** freely, subject to the following restrictions:
**
** 1. The origin of this software must not be misrepresented; you must not
** claim that you wrote the original software. If you use this software
** in a product, an acknowledgment in the product documentation would be
** appreciated but is not required.
** 2. Altered source versions must be plainly marked as such, and must not be
** misrepresented as being the original software.
** 3. This notice may not be removed or altered from any source distribution.
**
*/
#pragma once
#include <vector>
#include <memory>
#include <algorithm>
#include <functional>
#include "doomdata.h"
#include "r_utility.h"
#include "polyrenderer/drawers/poly_triangle.h"
#include "polyrenderer/math/gpu_types.h"
#include "poly_playersprite.h"
#include "poly_cull.h"
#include "poly_sky.h"
class PolyTranslucentObject
{
public:
PolyTranslucentObject(uint32_t subsectorDepth = 0, double distanceSquared = 0.0) : subsectorDepth(subsectorDepth), DistanceSquared(distanceSquared) { }
virtual ~PolyTranslucentObject() { }
virtual void Render(PolyRenderThread *thread) = 0;
bool operator<(const PolyTranslucentObject &other) const
{
return subsectorDepth != other.subsectorDepth ? subsectorDepth < other.subsectorDepth : DistanceSquared < other.DistanceSquared;
}
uint32_t subsectorDepth;
double DistanceSquared;
};
class PolyDrawSectorPortal;
class PolyDrawLinePortal;
class PolyPortalSegment;
class PolyPortalViewpoint
{
public:
Mat4f WorldToView;
Mat4f WorldToClip;
uint32_t StencilValue = 0;
int PortalDepth = 0;
bool Mirror = false;
line_t *PortalEnterLine = nullptr;
sector_t *PortalEnterSector = nullptr;
size_t ObjectsStart = 0;
size_t ObjectsEnd = 0;
size_t SectorPortalsStart = 0;
size_t SectorPortalsEnd = 0;
size_t LinePortalsStart = 0;
size_t LinePortalsEnd = 0;
};
// Renders everything from a specific viewpoint
class RenderPolyScene
{
public:
RenderPolyScene();
~RenderPolyScene();
void Render(PolyPortalViewpoint *viewpoint);
static const uint32_t SkySubsectorDepth = 0x7fffffff;
PolyPortalViewpoint *CurrentViewpoint = nullptr;
private:
void RenderPortals();
void RenderTranslucent();
void RenderSectors();
void RenderSubsector(PolyRenderThread *thread, subsector_t *sub, uint32_t subsectorDepth);
void RenderLine(PolyRenderThread *thread, subsector_t *sub, seg_t *line, sector_t *frontsector, uint32_t subsectorDepth);
void AddSprite(PolyRenderThread *thread, AActor *thing, double sortDistance, const DVector2 &left, const DVector2 &right);
void AddSprite(PolyRenderThread *thread, AActor *thing, double sortDistance, DVector2 left, DVector2 right, double t1, double t2, void *node);
void AddModel(PolyRenderThread *thread, AActor *thing, double sortDistance, DVector2 pos);
void RenderPolySubsector(PolyRenderThread *thread, subsector_t *sub, uint32_t subsectorDepth, sector_t *frontsector);
void RenderPolyNode(PolyRenderThread *thread, void *node, uint32_t subsectorDepth, sector_t *frontsector);
static int PointOnSide(const DVector2 &pos, const node_t *node);
PolyCull Cull;
PolySkyDome Skydome;
};
enum class PolyWaterFakeSide
{
Center,
BelowFloor,
AboveCeiling
};
class PolyTransferHeights
{
public:
PolyTransferHeights(subsector_t *sub);
subsector_t *Subsector = nullptr;
sector_t *FrontSector = nullptr;
PolyWaterFakeSide FakeSide = PolyWaterFakeSide::Center;
int FloorLightLevel = 0;
int CeilingLightLevel = 0;
private:
sector_t tempsec;
};

View file

@ -1,429 +0,0 @@
/*
** Sky dome rendering
** Copyright(C) 2003-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/
**
** Loosely based on the JDoom sky and the ZDoomGL 0.66.2 sky.
*/
#include <stdlib.h>
#include "templates.h"
#include "doomdef.h"
#include "sbar.h"
#include "r_data/r_translate.h"
#include "poly_sky.h"
#include "poly_portal.h"
#include "r_sky.h" // for skyflatnum
#include "g_levellocals.h"
#include "polyrenderer/scene/poly_light.h"
EXTERN_CVAR(Float, skyoffset)
EXTERN_CVAR(Int, r_skymode)
PolySkyDome::PolySkyDome()
{
CreateDome();
}
void PolySkyDome::Render(PolyRenderThread *thread, const Mat4f &worldToView, const Mat4f &worldToClip)
{
#ifdef USE_GL_DOME_MATH
Mat4f modelMatrix = GLSkyMath();
#else
Mat4f modelMatrix = Mat4f::Identity();
PolySkySetup frameSetup;
frameSetup.Update();
if (frameSetup != mCurrentSetup)
{
// frontcyl = pixels for full 360 degrees, front texture
// backcyl = pixels for full 360 degrees, back texture
// skymid = Y scaled pixel offset
// sky1pos = unscaled X offset, front
// sky2pos = unscaled X offset, back
// frontpos = scaled X pixel offset (fixed point)
// backpos = scaled X pixel offset (fixed point)
// skyflip = flip X direction
float scaleBaseV = 1.42f;
float offsetBaseV = 0.25f;
float scaleFrontU = frameSetup.frontcyl / (float)frameSetup.frontskytex->GetWidth();
float scaleFrontV = (float)frameSetup.frontskytex->GetScale().Y * scaleBaseV;
float offsetFrontU = (float)((frameSetup.frontpos / 65536.0 + frameSetup.frontcyl / 2) / frameSetup.frontskytex->GetWidth());
float offsetFrontV = (float)((frameSetup.skymid / frameSetup.frontskytex->GetHeight() + offsetBaseV) * scaleBaseV);
unsigned int count = mVertices.Size();
for (unsigned int i = 0; i < count; i++)
{
mVertices[i].u = offsetFrontU + mInitialUV[i].X * scaleFrontU;
mVertices[i].v = offsetFrontV + mInitialUV[i].Y * scaleFrontV;
}
mCurrentSetup = frameSetup;
}
#endif
const auto &viewpoint = PolyRenderer::Instance()->Viewpoint;
Mat4f objectToWorld = Mat4f::Translate((float)viewpoint.Pos.X, (float)viewpoint.Pos.Y, (float)viewpoint.Pos.Z) * modelMatrix;
int rc = mRows + 1;
PolyTriangleDrawer::SetTransform(thread->DrawQueue, thread->FrameMemory->NewObject<Mat4f>(worldToClip * objectToWorld), nullptr);
PolyDrawArgs args;
args.SetLight(&NormalLight, 255, PolyRenderer::Instance()->Light.WallGlobVis(false), true);
args.SetStencilTestValue(255);
args.SetWriteStencil(true, 1);
args.SetClipPlane(0, PolyClipPlane(0.0f, 0.0f, 0.0f, 1.0f));
RenderCapColorRow(thread, args, mCurrentSetup.frontskytex, 0, false);
RenderCapColorRow(thread, args, mCurrentSetup.frontskytex, rc, true);
args.SetTexture(mCurrentSetup.frontskytex, DefaultRenderStyle());
uint32_t topcapcolor = mCurrentSetup.frontskytex->GetSkyCapColor(false);
uint32_t bottomcapcolor = mCurrentSetup.frontskytex->GetSkyCapColor(true);
uint8_t topcapindex = RGB256k.All[((RPART(topcapcolor) >> 2) << 12) | ((GPART(topcapcolor) >> 2) << 6) | (BPART(topcapcolor) >> 2)];
uint8_t bottomcapindex = RGB256k.All[((RPART(bottomcapcolor) >> 2) << 12) | ((GPART(bottomcapcolor) >> 2) << 6) | (BPART(bottomcapcolor) >> 2)];
for (int i = 1; i <= mRows; i++)
{
RenderRow(thread, args, i, topcapcolor, topcapindex);
RenderRow(thread, args, rc + i, bottomcapcolor, bottomcapindex);
}
}
void PolySkyDome::RenderRow(PolyRenderThread *thread, PolyDrawArgs &args, int row, uint32_t capcolor, uint8_t capcolorindex)
{
args.SetColor(capcolor, capcolorindex);
args.SetStyle(TriBlendMode::Skycap);
PolyTriangleDrawer::DrawArray(thread->DrawQueue, args, &mVertices[mPrimStart[row]], mPrimStart[row + 1] - mPrimStart[row], PolyDrawMode::TriangleStrip);
}
void PolySkyDome::RenderCapColorRow(PolyRenderThread *thread, PolyDrawArgs &args, FSoftwareTexture *skytex, int row, bool bottomCap)
{
uint32_t solid = skytex->GetSkyCapColor(bottomCap);
uint8_t palsolid = RGB32k.RGB[(RPART(solid) >> 3)][(GPART(solid) >> 3)][(BPART(solid) >> 3)];
args.SetColor(solid, palsolid);
args.SetStyle(TriBlendMode::Fill);
PolyTriangleDrawer::DrawArray(thread->DrawQueue, args, &mVertices[mPrimStart[row]], mPrimStart[row + 1] - mPrimStart[row], PolyDrawMode::TriangleFan);
}
void PolySkyDome::CreateDome()
{
mColumns = 16;// 128;
mRows = 4;
CreateSkyHemisphere(false);
CreateSkyHemisphere(true);
mPrimStart.Push(mVertices.Size());
}
void PolySkyDome::CreateSkyHemisphere(bool zflip)
{
int r, c;
mPrimStart.Push(mVertices.Size());
for (c = 0; c < mColumns; c++)
{
SkyVertex(1, zflip ? c : (mColumns - 1 - c), zflip);
}
// The total number of triangles per hemisphere can be calculated
// as follows: rows * columns * 2 + 2 (for the top cap).
for (r = 0; r < mRows; r++)
{
mPrimStart.Push(mVertices.Size());
for (c = 0; c <= mColumns; c++)
{
SkyVertex(r + 1 - zflip, c, zflip);
SkyVertex(r + zflip, c, zflip);
}
}
}
TriVertex PolySkyDome::SetVertexXYZ(float xx, float yy, float zz, float uu, float vv)
{
TriVertex v;
v.x = xx;
v.y = zz;
v.z = yy;
v.w = 1.0f;
v.u = uu;
v.v = vv;
return v;
}
void PolySkyDome::SkyVertex(int r, int c, bool zflip)
{
static const FAngle maxSideAngle = 60.f;
static const float scale = 10000.;
FAngle topAngle = (c / (float)mColumns * 360.f);
FAngle sideAngle = maxSideAngle * (float)(mRows - r) / (float)mRows;
float height = sideAngle.Sin();
float realRadius = scale * sideAngle.Cos();
FVector2 pos = topAngle.ToVector(realRadius);
float z = (!zflip) ? scale * height : -scale * height;
float u, v;
// And the texture coordinates.
if (!zflip) // Flipped Y is for the lower hemisphere.
{
u = (-c / (float)mColumns);
v = (r / (float)mRows);
}
else
{
u = (-c / (float)mColumns);
v = 1.0f + ((mRows - r) / (float)mRows);
}
if (r != 4) z += 300;
// And finally the vertex.
TriVertex vert;
vert = SetVertexXYZ(-pos.X, z - 1.f, pos.Y, u, v - 0.5f);
mVertices.Push(vert);
mInitialUV.Push({ vert.u, vert.v });
}
Mat4f PolySkyDome::GLSkyMath()
{
PolySkySetup frameSetup;
frameSetup.Update();
mCurrentSetup = frameSetup;
float x_offset = 0.0f;
float y_offset = 0.0f;
bool mirror = false;
FSoftwareTexture *tex = mCurrentSetup.frontskytex;
int texh = 0;
int texw = 0;
Mat4f modelMatrix = Mat4f::Identity();
if (tex)
{
texw = tex->GetWidth();
texh = tex->GetHeight();
modelMatrix = Mat4f::Rotate(-180.0f + x_offset, 0.f, 0.f, 1.f);
float xscale = texw < 1024.f ? floor(1024.f / float(texw)) : 1.f;
float yscale = 1.f;
if (texh <= 128 && (PolyRenderer::Instance()->Level->flags & LEVEL_FORCETILEDSKY))
{
modelMatrix = modelMatrix * Mat4f::Translate(0.f, 0.f, (-40 + tex->GetSkyOffset() + skyoffset)*skyoffsetfactor);
modelMatrix = modelMatrix * Mat4f::Scale(1.f, 1.f, 1.2f * 1.17f);
yscale = 240.f / texh;
}
else if (texh < 128)
{
// smaller sky textures must be tiled. We restrict it to 128 sky pixels, though
modelMatrix = modelMatrix * Mat4f::Translate(0.f, 0.f, -1250.f);
modelMatrix = modelMatrix * Mat4f::Scale(1.f, 1.f, 128 / 230.f);
yscale = (float)(128 / texh); // intentionally left as integer.
}
else if (texh < 200)
{
modelMatrix = modelMatrix * Mat4f::Translate(0.f, 0.f, -1250.f);
modelMatrix = modelMatrix * Mat4f::Scale(1.f, 1.f, texh / 230.f);
}
else if (texh <= 240)
{
modelMatrix = modelMatrix * Mat4f::Translate(0.f, 0.f, (200 - texh + tex->GetSkyOffset() + skyoffset)*skyoffsetfactor);
modelMatrix = modelMatrix * Mat4f::Scale(1.f, 1.f, 1.f + ((texh - 200.f) / 200.f) * 1.17f);
}
else
{
modelMatrix = modelMatrix * Mat4f::Translate(0.f, 0.f, (-40 + tex->GetSkyOffset() + skyoffset)*skyoffsetfactor);
modelMatrix = modelMatrix * Mat4f::Scale(1.f, 1.f, 1.2f * 1.17f);
yscale = 240.f / texh;
}
float offsetU = 1.0f;
float offsetV = y_offset / texh;
float scaleU = mirror ? -xscale : xscale;
float scaleV = yscale;
unsigned int count = mVertices.Size();
for (unsigned int i = 0; i < count; i++)
{
mVertices[i].u = offsetU + mInitialUV[i].X * scaleU;
mVertices[i].v = offsetV + mInitialUV[i].Y * scaleV;
}
}
return modelMatrix;
}
/////////////////////////////////////////////////////////////////////////////
static FSoftwareTexture *GetSWTex(FTextureID texid, bool allownull = true)
{
auto tex = TexMan.GetPalettedTexture(texid, true);
if (tex == nullptr) return nullptr;
if (!allownull && !tex->isValid()) return nullptr;
return tex->GetSoftwareTexture();
}
void PolySkySetup::Update()
{
double skytexturemid = 0.0;
double skyscale = 0.0;
float skyiscale = 0.0f;
fixed_t sky1cyl = 0, sky2cyl = 0;
auto Level = PolyRenderer::Instance()->Level;
auto skytex1 = TexMan.GetPalettedTexture(Level->skytexture1, true);
auto skytex2 = TexMan.GetPalettedTexture(Level->skytexture2, true);
if (skytex1)
{
FSoftwareTexture *sskytex1 = skytex1->GetSoftwareTexture();
FSoftwareTexture *sskytex2 = skytex2->GetSoftwareTexture();
skytexturemid = 0;
int skyheight = skytex1->GetDisplayHeight();
if (skyheight >= 128 && skyheight < 200)
{
skytexturemid = -28;
}
else if (skyheight > 200)
{
skytexturemid = (200 - skyheight) * sskytex1->GetScale().Y + ((r_skymode == 2 && !(Level->flags & LEVEL_FORCETILEDSKY)) ? skytex1->GetSkyOffset() : 0);
}
if (viewwidth != 0 && viewheight != 0)
{
skyiscale = float(r_Yaspect / freelookviewheight);
skyscale = freelookviewheight / r_Yaspect;
skyiscale *= float(PolyRenderer::Instance()->Viewpoint.FieldOfView.Degrees / 90.);
skyscale *= float(90. / PolyRenderer::Instance()->Viewpoint.FieldOfView.Degrees);
}
if (Level->skystretch)
{
skyscale *= (double)SKYSTRETCH_HEIGHT / skyheight;
skyiscale *= skyheight / (float)SKYSTRETCH_HEIGHT;
skytexturemid *= skyheight / (double)SKYSTRETCH_HEIGHT;
}
// The standard Doom sky texture is 256 pixels wide, repeated 4 times over 360 degrees,
// giving a total sky width of 1024 pixels. So if the sky texture is no wider than 1024,
// we map it to a cylinder with circumfrence 1024. For larger ones, we use the width of
// the texture as the cylinder's circumfrence.
sky1cyl = MAX(sskytex1->GetWidth(), fixed_t(sskytex1->GetScale().X * 1024));
sky2cyl = MAX(sskytex2->GetWidth(), fixed_t(sskytex2->GetScale().Y * 1024));
}
FTextureID sky1tex, sky2tex;
double frontdpos = 0, backdpos = 0;
if ((PolyRenderer::Instance()->Level->flags & LEVEL_SWAPSKIES) && !(PolyRenderer::Instance()->Level->flags & LEVEL_DOUBLESKY))
{
sky1tex = Level->skytexture2;
}
else
{
sky1tex = Level->skytexture1;
}
sky2tex = Level->skytexture2;
skymid = skytexturemid;
skyangle = 0;
int sectorSky = 0;// sector->sky;
if (!(sectorSky & PL_SKYFLAT))
{ // use sky1
sky1:
frontskytex = GetSWTex(sky1tex);
if (PolyRenderer::Instance()->Level->flags & LEVEL_DOUBLESKY)
backskytex = GetSWTex(sky2tex);
else
backskytex = nullptr;
skyflip = false;
frontdpos = Level->sky1pos;
backdpos = Level->sky2pos;
frontcyl = sky1cyl;
backcyl = sky2cyl;
}
else if (sectorSky == PL_SKYFLAT)
{ // use sky2
frontskytex = GetSWTex(sky2tex);
backskytex = nullptr;
frontcyl = sky2cyl;
skyflip = false;
frontdpos = Level->sky2pos;
}
else
{ // MBF's linedef-controlled skies
// Sky Linedef
const line_t *l = &PolyRenderer::Instance()->Level->lines[(sectorSky & ~PL_SKYFLAT) - 1];
// Sky transferred from first sidedef
const side_t *s = l->sidedef[0];
int pos;
// Texture comes from upper texture of reference sidedef
// [RH] If swapping skies, then use the lower sidedef
if (PolyRenderer::Instance()->Level->flags & LEVEL_SWAPSKIES && s->GetTexture(side_t::bottom).isValid())
{
pos = side_t::bottom;
}
else
{
pos = side_t::top;
}
frontskytex = GetSWTex(s->GetTexture(pos), false);
if (frontskytex == nullptr)
{ // [RH] The blank texture: Use normal sky instead.
goto sky1;
}
backskytex = nullptr;
// Horizontal offset is turned into an angle offset,
// to allow sky rotation as well as careful positioning.
// However, the offset is scaled very small, so that it
// allows a long-period of sky rotation.
skyangle += FLOAT2FIXED(s->GetTextureXOffset(pos));
// Vertical offset allows careful sky positioning.
skymid = s->GetTextureYOffset(pos);
// We sometimes flip the picture horizontally.
//
// Doom always flipped the picture, so we make it optional,
// to make it easier to use the new feature, while to still
// allow old sky textures to be used.
skyflip = l->args[2] ? false : true;
int frontxscale = int(frontskytex->GetScale().X * 1024);
frontcyl = MAX(frontskytex->GetWidth(), frontxscale);
}
frontpos = int(fmod(frontdpos, sky1cyl * 65536.0));
if (backskytex != nullptr)
{
backpos = int(fmod(backdpos, sky2cyl * 65536.0));
}
}

View file

@ -1,68 +0,0 @@
/*
** Sky dome rendering
** Copyright(C) 2003-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/
**
** Loosely based on the JDoom sky and the ZDoomGL 0.66.2 sky.
*/
#pragma once
#include "polyrenderer/drawers/poly_triangle.h"
class PolySkySetup
{
public:
void Update();
bool operator==(const PolySkySetup &that) const { return memcmp(this, &that, sizeof(PolySkySetup)) == 0; }
bool operator!=(const PolySkySetup &that) const { return memcmp(this, &that, sizeof(PolySkySetup)) != 0; }
FSoftwareTexture *frontskytex = nullptr;
FSoftwareTexture *backskytex = nullptr;
bool skyflip = 0;
int frontpos = 0;
int backpos = 0;
fixed_t frontcyl = 0;
fixed_t backcyl = 0;
double skymid = 0.0;
angle_t skyangle = 0;
};
class PolySkyDome
{
public:
PolySkyDome();
void Render(PolyRenderThread *thread, const Mat4f &worldToView, const Mat4f &worldToClip);
private:
TArray<FVector2> mInitialUV;
TArray<TriVertex> mVertices;
TArray<unsigned int> mPrimStart;
int mRows, mColumns;
void SkyVertex(int r, int c, bool yflip);
void CreateSkyHemisphere(bool zflip);
void CreateDome();
void RenderRow(PolyRenderThread *thread, PolyDrawArgs &args, int row, uint32_t capcolor, uint8_t capcolorindex);
void RenderCapColorRow(PolyRenderThread *thread, PolyDrawArgs &args, FSoftwareTexture *skytex, int row, bool bottomCap);
TriVertex SetVertexXYZ(float xx, float yy, float zz, float uu = 0, float vv = 0);
Mat4f GLSkyMath();
PolySkySetup mCurrentSetup;
};

View file

@ -1,432 +0,0 @@
/*
** Polygon Doom software renderer
** Copyright (c) 2016 Magnus Norddahl
**
** This software is provided 'as-is', without any express or implied
** warranty. In no event will the authors be held liable for any damages
** arising from the use of this software.
**
** Permission is granted to anyone to use this software for any purpose,
** including commercial applications, and to alter it and redistribute it
** freely, subject to the following restrictions:
**
** 1. The origin of this software must not be misrepresented; you must not
** claim that you wrote the original software. If you use this software
** in a product, an acknowledgment in the product documentation would be
** appreciated but is not required.
** 2. Altered source versions must be plainly marked as such, and must not be
** misrepresented as being the original software.
** 3. This notice may not be removed or altered from any source distribution.
**
*/
#include <stdlib.h>
#include "templates.h"
#include "doomdef.h"
#include "sbar.h"
#include "r_data/r_translate.h"
#include "poly_sprite.h"
#include "polyrenderer/poly_renderer.h"
#include "polyrenderer/scene/poly_light.h"
#include "polyrenderer/poly_renderthread.h"
#include "polyrenderer/scene/poly_model.h"
#include "r_data/r_vanillatrans.h"
#include "actorinlines.h"
EXTERN_CVAR(Float, transsouls)
EXTERN_CVAR(Int, r_drawfuzz)
EXTERN_CVAR (Bool, r_debug_disable_vis_filter)
EXTERN_CVAR(Int, gl_spriteclip)
EXTERN_CVAR(Float, gl_sclipthreshold)
EXTERN_CVAR(Float, gl_sclipfactor)
extern uint32_t r_renderercaps;
extern double model_distance_cull;
bool RenderPolySprite::GetLine(AActor *thing, DVector2 &left, DVector2 &right)
{
const auto &viewpoint = PolyRenderer::Instance()->Viewpoint;
DVector3 pos = thing->InterpolatedPosition(viewpoint.TicFrac);
bool flipTextureX = false;
FSoftwareTexture *tex = GetSpriteTexture(thing, flipTextureX);
if (tex == nullptr)
return false;
DVector2 spriteScale = thing->Scale;
double thingxscalemul = spriteScale.X / tex->GetScale().X;
double thingyscalemul = spriteScale.Y / tex->GetScale().Y;
double spriteWidth = thingxscalemul * tex->GetWidth();
double spriteHeight = thingyscalemul * tex->GetHeight();
double offsetX;
if (flipTextureX)
offsetX = (tex->GetWidth() - tex->GetLeftOffsetPo()) * thingxscalemul;
else
offsetX = tex->GetLeftOffsetPo() * thingxscalemul;
left = DVector2(pos.X - viewpoint.Sin * offsetX, pos.Y + viewpoint.Cos * offsetX);
right = DVector2(left.X + viewpoint.Sin * spriteWidth, left.Y - viewpoint.Cos * spriteWidth);
return true;
}
void RenderPolySprite::Render(PolyRenderThread *thread, AActor *thing, subsector_t *sub, uint32_t stencilValue, float t1, float t2)
{
if (r_modelscene)
{
const auto &viewpoint = PolyRenderer::Instance()->Viewpoint;
int spritenum = thing->sprite;
bool isPicnumOverride = thing->picnum.isValid();
FSpriteModelFrame *modelframe = isPicnumOverride ? nullptr : FindModelFrame(thing->GetClass(), spritenum, thing->frame, !!(thing->flags & MF_DROPPED));
if (modelframe && (thing->Pos() - viewpoint.Pos).LengthSquared() < model_distance_cull)
{
DVector3 pos = thing->InterpolatedPosition(viewpoint.TicFrac);
PolyRenderModel(thread, PolyRenderer::Instance()->Scene.CurrentViewpoint->WorldToClip, stencilValue, (float)pos.X, (float)pos.Y, (float)pos.Z, modelframe, thing);
return;
}
}
DVector2 line[2];
if (!GetLine(thing, line[0], line[1]))
return;
const auto &viewpoint = PolyRenderer::Instance()->Viewpoint;
DVector3 thingpos = thing->InterpolatedPosition(viewpoint.TicFrac);
double posZ = thingpos.Z;
uint32_t spritetype = (thing->renderflags & RF_SPRITETYPEMASK);
if (spritetype == RF_FACESPRITE)
posZ -= thing->Floorclip;
if (thing->flags2 & MF2_FLOATBOB)
posZ += thing->GetBobOffset(viewpoint.TicFrac);
bool flipTextureX = false;
FSoftwareTexture *tex = GetSpriteTexture(thing, flipTextureX);
if (tex == nullptr)
return;
double thingyscalemul = thing->Scale.Y / tex->GetScale().Y;
double spriteHeight = thingyscalemul * tex->GetHeight();
posZ -= (tex->GetHeight() - tex->GetTopOffsetPo()) * thingyscalemul;
posZ = PerformSpriteClipAdjustment(thing, thingpos, spriteHeight, posZ);
//double depth = 1.0;
//visstyle_t visstyle = GetSpriteVisStyle(thing, depth);
// Rumor has it that AlterWeaponSprite needs to be called with visstyle passed in somewhere around here..
//R_SetColorMapLight(visstyle.BaseColormap, 0, visstyle.ColormapNum << FRACBITS);
TriVertex *vertices = thread->FrameMemory->AllocMemory<TriVertex>(4);
bool foggy = false;
int actualextralight = foggy ? 0 : viewpoint.extralight << 4;
std::pair<float, float> offsets[4] =
{
{ t1, 1.0f },
{ t2, 1.0f },
{ t2, 0.0f },
{ t1, 0.0f },
};
DVector2 points[2] =
{
line[0] * (1.0 - t1) + line[1] * t1,
line[0] * (1.0 - t2) + line[1] * t2
};
for (int i = 0; i < 4; i++)
{
auto &p = (i == 0 || i == 3) ? points[0] : points[1];
vertices[i].x = (float)p.X;
vertices[i].y = (float)p.Y;
vertices[i].z = (float)(posZ + spriteHeight * offsets[i].second);
vertices[i].w = 1.0f;
vertices[i].u = (float)offsets[i].first;
vertices[i].v = (float)(1.0f - offsets[i].second);
if (flipTextureX)
vertices[i].u = 1.0f - vertices[i].u;
}
bool fullbrightSprite = ((thing->renderflags & RF_FULLBRIGHT) || (thing->flags5 & MF5_BRIGHT));
int lightlevel = fullbrightSprite ? 255 : thing->Sector->lightlevel + actualextralight;
PolyDrawArgs args;
SetDynlight(thing, args);
auto nc = !!(thing->Level->flags3 & LEVEL3_NOCOLOREDSPRITELIGHTING);
args.SetLight(GetSpriteColorTable(sub->sector->Colormap, sub->sector->SpecialColors[sector_t::sprites], nc), lightlevel, PolyRenderer::Instance()->Light.SpriteGlobVis(foggy), fullbrightSprite); args.SetStencilTestValue(stencilValue);
if ((thing->renderflags & RF_ZDOOMTRANS) && r_UseVanillaTransparency)
args.SetStyle(LegacyRenderStyles[STYLE_Normal], 1.0f, thing->fillcolor, thing->Translation, tex, fullbrightSprite);
else
args.SetStyle(thing->RenderStyle, thing->Alpha, thing->fillcolor, thing->Translation, tex, fullbrightSprite);
args.SetDepthTest(true);
args.SetWriteDepth(false);
args.SetWriteStencil(false);
PolyTriangleDrawer::DrawArray(thread->DrawQueue, args, vertices, 4, PolyDrawMode::TriangleFan);
}
double RenderPolySprite::GetSpriteFloorZ(AActor *thing, const DVector2 &thingpos)
{
extsector_t::xfloor &x = thing->Sector->e->XFloor;
for (unsigned int i = 0; i < x.ffloors.Size(); i++)
{
F3DFloor *ff = x.ffloors[i];
double floorh = ff->top.plane->ZatPoint(thingpos);
if (floorh == thing->floorz)
return floorh;
}
if (thing->Sector->GetHeightSec())
{
if (thing->flags2&MF2_ONMOBJ && thing->floorz == thing->Sector->heightsec->floorplane.ZatPoint(thingpos))
{
return thing->floorz;
}
}
return thing->Sector->floorplane.ZatPoint(thing) - thing->Floorclip;
}
double RenderPolySprite::GetSpriteCeilingZ(AActor *thing, const DVector2 &thingpos)
{
extsector_t::xfloor &x = thing->Sector->e->XFloor;
for (unsigned int i = 0; i < x.ffloors.Size(); i++)
{
F3DFloor *ff = x.ffloors[i];
double ceilingh = ff->bottom.plane->ZatPoint(thingpos);
if (ceilingh == thing->ceilingz)
return ceilingh;
}
if (thing->Sector->GetHeightSec())
{
if (thing->flags2&MF2_ONMOBJ && thing->ceilingz == thing->Sector->heightsec->ceilingplane.ZatPoint(thingpos))
{
return thing->ceilingz;
}
}
return thing->Sector->ceilingplane.ZatPoint(thingpos);
}
double RenderPolySprite::PerformSpriteClipAdjustment(AActor *thing, const DVector2 &thingpos, double spriteheight, double z2)
{
int spriteclip = 2; // gl_spriteclip, but use 'always' mode for now
double z1 = z2 + spriteheight;
// Tests show that this doesn't look good for many decorations and corpses
uint32_t spritetype = (thing->renderflags & RF_SPRITETYPEMASK);
if (!(spriteheight > 0 && spriteclip > 0 && spritetype == RF_FACESPRITE))
return z2;
bool clipthing = (thing->player || thing->flags3&MF3_ISMONSTER || thing->IsKindOf(NAME_Inventory)) && (thing->flags&MF_ICECORPSE || !(thing->flags&MF_CORPSE));
bool smarterclip = !clipthing && spriteclip == 3;
if (clipthing || spriteclip > 1)
{
double diffb = MIN(z2 - GetSpriteFloorZ(thing, thingpos), 0.0);
// Adjust sprites clipping into ceiling and adjust clipping adjustment for tall graphics
if (smarterclip)
{
// Reduce slightly clipping adjustment of corpses
if (thing->flags & MF_CORPSE || spriteheight > fabs(diffb))
{
double ratio = clamp<double>((fabs(diffb) * (double)gl_sclipfactor / (spriteheight + 1)), 0.5, 1.0);
diffb *= ratio;
}
if (!diffb)
{
double difft = MAX(z1 - GetSpriteCeilingZ(thing, thingpos), 0.0);
if (difft >= (double)gl_sclipthreshold)
{
// dumb copy of the above.
if (!(thing->flags3&MF3_ISMONSTER) || (thing->flags&MF_NOGRAVITY) || (thing->flags&MF_CORPSE) || difft > (double)gl_sclipthreshold)
{
difft = 0;
}
}
if (spriteheight > fabs(difft))
{
double ratio = clamp<double>((fabs(difft) * (double)gl_sclipfactor / (spriteheight + 1)), 0.5, 1.0);
difft *= ratio;
}
z2 -= difft;
}
}
if (diffb <= (0 - (double)gl_sclipthreshold)) // such a large displacement can't be correct!
{
// for living monsters standing on the floor allow a little more.
if (!(thing->flags3&MF3_ISMONSTER) || (thing->flags&MF_NOGRAVITY) || (thing->flags&MF_CORPSE) || diffb < (-1.8*(double)gl_sclipthreshold))
{
diffb = 0;
}
}
z2 -= diffb;
}
return z2;
}
bool RenderPolySprite::IsThingCulled(AActor *thing)
{
FIntCVar *cvar = thing->GetInfo()->distancecheck;
if (cvar != nullptr && *cvar >= 0)
{
double dist = (thing->Pos() - PolyRenderer::Instance()->Viewpoint.Pos).LengthSquared();
double check = (double)**cvar;
if (dist >= check * check)
return true;
}
// Don't waste time projecting sprites that are definitely not visible.
if (thing == nullptr ||
(thing->renderflags & RF_INVISIBLE) ||
!thing->RenderStyle.IsVisible(thing->Alpha) ||
!thing->IsVisibleToPlayer())
{
return true;
}
// check renderrequired vs ~r_rendercaps, if anything matches we don't support that feature,
// check renderhidden vs r_rendercaps, if anything matches we do support that feature and should hide it.
if ((!r_debug_disable_vis_filter && !!(thing->RenderRequired & ~r_renderercaps)) ||
(!!(thing->RenderHidden & r_renderercaps)))
return true;
return false;
}
FSoftwareTexture *RenderPolySprite::GetSpriteTexture(AActor *thing, /*out*/ bool &flipX)
{
const auto &viewpoint = PolyRenderer::Instance()->Viewpoint;
flipX = false;
if (thing->renderflags & RF_FLATSPRITE)
return nullptr; // do not draw flat sprites.
if (thing->picnum.isValid())
{
FTexture *ttex = TexMan.GetPalettedTexture(thing->picnum, true);
if (!ttex || !ttex->isValid())
{
return nullptr;
}
FSoftwareTexture *tex = ttex->GetSoftwareTexture();
if (ttex->GetRotations() != 0xFFFF)
{
// choose a different rotation based on player view
spriteframe_t *sprframe = &SpriteFrames[ttex->GetRotations()];
DVector3 pos = thing->InterpolatedPosition(viewpoint.TicFrac);
pos.Z += thing->GetBobOffset(viewpoint.TicFrac);
DAngle ang = (pos - viewpoint.Pos).Angle();
angle_t rot;
if (sprframe->Texture[0] == sprframe->Texture[1])
{
rot = (ang - thing->Angles.Yaw + 45.0 / 2 * 9).BAMs() >> 28;
}
else
{
rot = (ang - thing->Angles.Yaw + (45.0 / 2 * 9 - 180.0 / 16)).BAMs() >> 28;
}
flipX = (sprframe->Flip & (1 << rot)) != 0;
ttex = TexMan.GetPalettedTexture(sprframe->Texture[rot], false); // Do not animate the rotation
tex = ttex->GetSoftwareTexture();
if (!ttex || !ttex->isValid())
{
return nullptr;
}
FSoftwareTexture *tex = ttex->GetSoftwareTexture();
}
return tex;
}
else
{
// decide which texture to use for the sprite
int spritenum = thing->sprite;
if (spritenum >= (signed)sprites.Size() || spritenum < 0)
return nullptr;
spritedef_t *sprdef = &sprites[spritenum];
if (thing->frame >= sprdef->numframes)
{
// If there are no frames at all for this sprite, don't draw it.
return nullptr;
}
else
{
//picnum = SpriteFrames[sprdef->spriteframes + thing->frame].Texture[0];
// choose a different rotation based on player view
DVector3 pos = thing->InterpolatedPosition(viewpoint.TicFrac);
pos.Z += thing->GetBobOffset(viewpoint.TicFrac);
DAngle ang = (pos - viewpoint.Pos).Angle();
DAngle sprangle = thing->GetSpriteAngle((pos - viewpoint.Pos).Angle(), viewpoint.TicFrac);
FTextureID tex = sprdef->GetSpriteFrame(thing->frame, -1, sprangle, &flipX);
if (!tex.isValid()) return nullptr;
return TexMan.GetPalettedTexture(tex, false)->GetSoftwareTexture();
}
}
}
void RenderPolySprite::SetDynlight(AActor *thing, PolyDrawArgs &args)
{
bool fullbrightSprite = ((thing->renderflags & RF_FULLBRIGHT) || (thing->flags5 & MF5_BRIGHT));
if (fullbrightSprite || !r_dynlights)
{
args.SetDynLightColor(0);
return;
}
float lit_red = 0;
float lit_green = 0;
float lit_blue = 0;
auto node = thing->section->lighthead;
while (node != nullptr)
{
FDynamicLight *light = node->lightsource;
if (light->ShouldLightActor(thing))
{
float lx = (float)(light->X() - thing->X());
float ly = (float)(light->Y() - thing->Y());
float lz = (float)(light->Z() - thing->Center());
float LdotL = lx * lx + ly * ly + lz * lz;
float radius = node->lightsource->GetRadius();
if (radius * radius >= LdotL)
{
float distance = sqrt(LdotL);
float attenuation = 1.0f - distance / radius;
if (attenuation > 0.0f)
{
float red = light->GetRed() * (1.0f / 255.0f);
float green = light->GetGreen() * (1.0f / 255.0f);
float blue = light->GetBlue() * (1.0f / 255.0f);
/*if (light->IsSubtractive())
{
float bright = FVector3(lr, lg, lb).Length();
FVector3 lightColor(lr, lg, lb);
red = (bright - lr) * -1;
green = (bright - lg) * -1;
blue = (bright - lb) * -1;
}*/
lit_red += red * attenuation;
lit_green += green * attenuation;
lit_blue += blue * attenuation;
}
}
}
node = node->nextLight;
}
lit_red = clamp(lit_red * 255.0f, 0.0f, 255.0f);
lit_green = clamp(lit_green * 255.0f, 0.0f, 255.0f);
lit_blue = clamp(lit_blue * 255.0f, 0.0f, 255.0f);
args.SetDynLightColor((((uint32_t)lit_red) << 16) | (((uint32_t)lit_green) << 8) | ((uint32_t)lit_blue));
}

View file

@ -1,67 +0,0 @@
/*
** Polygon Doom software renderer
** Copyright (c) 2016 Magnus Norddahl
**
** This software is provided 'as-is', without any express or implied
** warranty. In no event will the authors be held liable for any damages
** arising from the use of this software.
**
** Permission is granted to anyone to use this software for any purpose,
** including commercial applications, and to alter it and redistribute it
** freely, subject to the following restrictions:
**
** 1. The origin of this software must not be misrepresented; you must not
** claim that you wrote the original software. If you use this software
** in a product, an acknowledgment in the product documentation would be
** appreciated but is not required.
** 2. Altered source versions must be plainly marked as such, and must not be
** misrepresented as being the original software.
** 3. This notice may not be removed or altered from any source distribution.
**
*/
#pragma once
#include "polyrenderer/drawers/poly_triangle.h"
class RenderPolySprite
{
public:
void Render(PolyRenderThread *thread, AActor *thing, subsector_t *sub, uint32_t stencilValue, float t1, float t2);
static bool GetLine(AActor *thing, DVector2 &left, DVector2 &right);
static bool IsThingCulled(AActor *thing);
static FSoftwareTexture *GetSpriteTexture(AActor *thing, /*out*/ bool &flipX);
private:
static double PerformSpriteClipAdjustment(AActor *thing, const DVector2 &thingpos, double spriteheight, double z);
static double GetSpriteFloorZ(AActor *thing, const DVector2 &thingpos);
static double GetSpriteCeilingZ(AActor *thing, const DVector2 &thingpos);
static void SetDynlight(AActor *thing, PolyDrawArgs &args);
};
class PolyTranslucentThing : public PolyTranslucentObject
{
public:
PolyTranslucentThing(AActor *thing, subsector_t *sub, uint32_t subsectorDepth, double dist, float t1, float t2, uint32_t stencilValue) : PolyTranslucentObject(subsectorDepth, dist), thing(thing), sub(sub), SpriteLeft(t1), SpriteRight(t2), StencilValue(stencilValue) { }
void Render(PolyRenderThread *thread) override
{
if ((thing->renderflags & RF_SPRITETYPEMASK) == RF_WALLSPRITE)
{
RenderPolyWallSprite wallspr;
wallspr.Render(thread, thing, sub, StencilValue + 1);
}
else
{
RenderPolySprite spr;
spr.Render(thread, thing, sub, StencilValue + 1, SpriteLeft, SpriteRight);
}
}
AActor *thing = nullptr;
subsector_t *sub = nullptr;
float SpriteLeft = 0.0f;
float SpriteRight = 1.0f;
uint32_t StencilValue = 0;
};

View file

@ -1,721 +0,0 @@
/*
** Polygon Doom software renderer
** Copyright (c) 2016 Magnus Norddahl
**
** This software is provided 'as-is', without any express or implied
** warranty. In no event will the authors be held liable for any damages
** arising from the use of this software.
**
** Permission is granted to anyone to use this software for any purpose,
** including commercial applications, and to alter it and redistribute it
** freely, subject to the following restrictions:
**
** 1. The origin of this software must not be misrepresented; you must not
** claim that you wrote the original software. If you use this software
** in a product, an acknowledgment in the product documentation would be
** appreciated but is not required.
** 2. Altered source versions must be plainly marked as such, and must not be
** misrepresented as being the original software.
** 3. This notice may not be removed or altered from any source distribution.
**
*/
#include <stdlib.h>
#include "templates.h"
#include "doomdef.h"
#include "doomstat.h"
#include "doomdata.h"
#include "p_lnspec.h"
#include "sbar.h"
#include "r_data/r_translate.h"
#include "poly_wall.h"
#include "poly_decal.h"
#include "polyrenderer/poly_renderer.h"
#include "r_sky.h"
#include "polyrenderer/scene/poly_light.h"
#include "polyrenderer/poly_renderthread.h"
#include "g_levellocals.h"
#include "a_dynlight.h"
EXTERN_CVAR(Bool, r_drawmirrors)
EXTERN_CVAR(Bool, r_fogboundary)
bool RenderPolyWall::RenderLine(PolyRenderThread *thread, seg_t *line, sector_t *frontsector, uint32_t subsectorDepth, uint32_t stencilValue, std::vector<PolyTranslucentObject*> &translucentWallsOutput, std::vector<std::unique_ptr<PolyDrawLinePortal>> &linePortals, size_t linePortalsStart, line_t *portalEnterLine)
{
double frontceilz1 = frontsector->ceilingplane.ZatPoint(line->v1);
double frontfloorz1 = frontsector->floorplane.ZatPoint(line->v1);
double frontceilz2 = frontsector->ceilingplane.ZatPoint(line->v2);
double frontfloorz2 = frontsector->floorplane.ZatPoint(line->v2);
double topTexZ = frontsector->GetPlaneTexZ(sector_t::ceiling);
double bottomTexZ = frontsector->GetPlaneTexZ(sector_t::floor);
PolyDrawLinePortal *polyportal = nullptr;
if (line->backsector == nullptr && line->linedef && line->sidedef == line->linedef->sidedef[0] && (line->linedef->special == Line_Mirror && r_drawmirrors))
{
if (portalEnterLine == line->linedef)
{
return false;
}
linePortals.push_back(std::unique_ptr<PolyDrawLinePortal>(new PolyDrawLinePortal(line->linedef)));
polyportal = linePortals.back().get();
}
else if (line->linedef && line->linedef->isVisualPortal() && line->sidedef == line->linedef->sidedef[0])
{
if (portalEnterLine == line->linedef)
{
return false;
}
FLinePortal *portal = line->linedef->getPortal();
for (size_t i = linePortalsStart; i < linePortals.size(); i++)
{
if (linePortals[i]->Portal == portal) // To do: what other criteria do we need to check for?
{
polyportal = linePortals[i].get();
break;
}
}
if (!polyportal)
{
linePortals.push_back(std::unique_ptr<PolyDrawLinePortal>(new PolyDrawLinePortal(portal)));
polyportal = linePortals.back().get();
}
}
RenderPolyWall wall;
wall.LineSeg = line;
wall.Line = line->linedef;
wall.Side = line->sidedef;
wall.LineSegLine = line->linedef;
wall.Masked = false;
wall.SubsectorDepth = subsectorDepth;
wall.StencilValue = stencilValue;
wall.SectorLightLevel = frontsector->lightlevel;
if (line->backsector == nullptr)
{
if (line->sidedef)
{
wall.SetCoords(line->v1->fPos(), line->v2->fPos(), frontceilz1, frontfloorz1, frontceilz2, frontfloorz2);
wall.TopTexZ = topTexZ;
wall.BottomTexZ = bottomTexZ;
wall.Wallpart = side_t::mid;
wall.Colormap = GetColorTable(frontsector->Colormap, wall.Side->GetSpecialColor(wall.Wallpart, side_t::walltop, frontsector));
wall.Texture = GetTexture(wall.Line, wall.Side, side_t::mid);
wall.Polyportal = polyportal;
wall.Render(thread);
return true;
}
}
else if (line->PartnerSeg && line->PartnerSeg->Subsector)
{
PolyTransferHeights fakeback(line->PartnerSeg->Subsector);
sector_t *backsector = fakeback.FrontSector;
double backceilz1 = backsector->ceilingplane.ZatPoint(line->v1);
double backfloorz1 = backsector->floorplane.ZatPoint(line->v1);
double backceilz2 = backsector->ceilingplane.ZatPoint(line->v2);
double backfloorz2 = backsector->floorplane.ZatPoint(line->v2);
double topceilz1 = frontceilz1;
double topceilz2 = frontceilz2;
double topfloorz1 = MAX(MIN(backceilz1, frontceilz1), frontfloorz1);
double topfloorz2 = MAX(MIN(backceilz2, frontceilz2), frontfloorz2);
double bottomceilz1 = MIN(MAX(frontfloorz1, backfloorz1), frontceilz1);
double bottomceilz2 = MIN(MAX(frontfloorz2, backfloorz2), frontceilz2);
double bottomfloorz1 = frontfloorz1;
double bottomfloorz2 = frontfloorz2;
double middleceilz1 = topfloorz1;
double middleceilz2 = topfloorz2;
double middlefloorz1 = MIN(bottomceilz1, middleceilz1);
double middlefloorz2 = MIN(bottomceilz2, middleceilz2);
bool bothSkyCeiling = frontsector->GetTexture(sector_t::ceiling) == skyflatnum && backsector->GetTexture(sector_t::ceiling) == skyflatnum;
bool bothSkyFloor = frontsector->GetTexture(sector_t::floor) == skyflatnum && backsector->GetTexture(sector_t::floor) == skyflatnum;
if ((topceilz1 > topfloorz1 || topceilz2 > topfloorz2) && line->sidedef && !bothSkyCeiling)
{
wall.SetCoords(line->v1->fPos(), line->v2->fPos(), topceilz1, topfloorz1, topceilz2, topfloorz2);
wall.TopTexZ = topTexZ;
wall.BottomTexZ = MIN(MIN(backceilz1, frontceilz1), MIN(backceilz2, frontceilz2));
wall.Wallpart = side_t::top;
wall.Colormap = GetColorTable(frontsector->Colormap, wall.Side->GetSpecialColor(wall.Wallpart, side_t::walltop, frontsector));
wall.Texture = GetTexture(wall.Line, wall.Side, side_t::top);
wall.Render(thread);
}
if ((bottomfloorz1 < bottomceilz1 || bottomfloorz2 < bottomceilz2) && line->sidedef && !bothSkyFloor)
{
wall.SetCoords(line->v1->fPos(), line->v2->fPos(), bottomceilz1, bottomfloorz1, bottomceilz2, bottomfloorz2);
wall.TopTexZ = MAX(MAX(frontfloorz1, backfloorz1), MAX(frontfloorz2, backfloorz2));
wall.BottomTexZ = bottomTexZ;
wall.UnpeggedCeil1 = topceilz1;
wall.UnpeggedCeil2 = topceilz2;
wall.Wallpart = side_t::bottom;
wall.Colormap = GetColorTable(frontsector->Colormap, wall.Side->GetSpecialColor(wall.Wallpart, side_t::walltop, frontsector));
wall.Texture = GetTexture(wall.Line, wall.Side, side_t::bottom);
wall.Render(thread);
}
if (line->sidedef)
{
wall.SetCoords(line->v1->fPos(), line->v2->fPos(), middleceilz1, middlefloorz1, middleceilz2, middlefloorz2);
wall.TopTexZ = MAX(middleceilz1, middleceilz2);
wall.BottomTexZ = MIN(middlefloorz1, middlefloorz2);
wall.Wallpart = side_t::mid;
wall.Colormap = GetColorTable(frontsector->Colormap, wall.Side->GetSpecialColor(wall.Wallpart, side_t::walltop, frontsector));
wall.Texture = GetTexture(wall.Line, wall.Side, side_t::mid);
wall.Masked = true;
wall.Additive = !!(wall.Line->flags & ML_ADDTRANS);
wall.Alpha = wall.Line->alpha;
wall.FogBoundary = IsFogBoundary(frontsector, backsector);
FTexture *midtex = TexMan.GetPalettedTexture(line->sidedef->GetTexture(side_t::mid), true);
if ((midtex && midtex->isValid()) || wall.FogBoundary)
translucentWallsOutput.push_back(thread->FrameMemory->NewObject<PolyTranslucentWall>(wall));
if (polyportal)
{
wall.Polyportal = polyportal;
wall.Render(thread);
}
}
}
return polyportal != nullptr;
}
bool RenderPolyWall::IsFogBoundary(sector_t *front, sector_t *back)
{
return r_fogboundary && PolyCameraLight::Instance()->FixedColormap() == nullptr && front->Colormap.FadeColor &&
front->Colormap.FadeColor != back->Colormap.FadeColor &&
(front->GetTexture(sector_t::ceiling) != skyflatnum || back->GetTexture(sector_t::ceiling) != skyflatnum);
}
void RenderPolyWall::Render3DFloorLine(PolyRenderThread *thread, seg_t *line, sector_t *frontsector, uint32_t subsectorDepth, uint32_t stencilValue, F3DFloor *fakeFloor, std::vector<PolyTranslucentObject*> &translucentWallsOutput)
{
if (!(fakeFloor->flags & FF_EXISTS)) return;
if (!(fakeFloor->flags & FF_RENDERPLANES)) return;
if (!fakeFloor->model) return;
if (fakeFloor->alpha == 0) return;
double frontceilz1 = fakeFloor->top.plane->ZatPoint(line->v1);
double frontfloorz1 = fakeFloor->bottom.plane->ZatPoint(line->v1);
double frontceilz2 = fakeFloor->top.plane->ZatPoint(line->v2);
double frontfloorz2 = fakeFloor->bottom.plane->ZatPoint(line->v2);
double topTexZ = fakeFloor->model->GetPlaneTexZ(sector_t::ceiling);
double bottomTexZ = fakeFloor->model->GetPlaneTexZ(sector_t::floor);
if (frontceilz1 <= frontfloorz1 || frontceilz2 <= frontfloorz2)
return;
if (fakeFloor->flags & FF_SWIMMABLE) // Only draw swimmable boundary if not swimmable on both sides
{
DVector2 c = (line->v1->fPos() + line->v2->fPos()) * 0.5;
double cz = (frontceilz1 + frontceilz2 + frontfloorz1 + frontfloorz2) * 0.25;
for (unsigned i = 0; i < frontsector->e->XFloor.ffloors.Size(); i++)
{
F3DFloor *frontFloor = frontsector->e->XFloor.ffloors[i];
if (!(frontFloor->flags & FF_EXISTS)) continue;
if (!(frontFloor->flags & FF_RENDERPLANES)) continue;
if (!frontFloor->model) continue;
if (frontFloor->alpha == 0) continue;
if (frontFloor->top.plane->ZatPoint(c) >= cz && frontFloor->bottom.plane->ZatPoint(c) <= cz && (frontFloor->flags & FF_SWIMMABLE))
{
return;
}
}
}
RenderPolyWall wall;
wall.LineSeg = line;
wall.LineSegLine = line->linedef;
wall.Line = fakeFloor->master;
wall.Side = fakeFloor->master->sidedef[0];
wall.Additive = !!(fakeFloor->flags & FF_ADDITIVETRANS);
if (!wall.Additive && fakeFloor->alpha == 255)
{
wall.Masked = false;
wall.Alpha = 1.0;
}
else
{
wall.Masked = true;
wall.Alpha = fakeFloor->alpha / 255.0;
}
wall.SubsectorDepth = subsectorDepth;
wall.StencilValue = stencilValue;
wall.SetCoords(line->v1->fPos(), line->v2->fPos(), frontceilz1, frontfloorz1, frontceilz2, frontfloorz2);
wall.TopTexZ = topTexZ;
wall.BottomTexZ = bottomTexZ;
wall.Wallpart = side_t::mid;
wall.Colormap = GetColorTable(frontsector->Colormap, wall.Side->GetSpecialColor(wall.Wallpart, side_t::walltop, frontsector));
if (fakeFloor->flags & FF_UPPERTEXTURE)
wall.Texture = GetTexture(line->linedef, line->sidedef, side_t::top);
else if (fakeFloor->flags & FF_LOWERTEXTURE)
wall.Texture = GetTexture(line->linedef, line->sidedef, side_t::bottom);
else
wall.Texture = GetTexture(wall.Line, wall.Side, side_t::mid);
if (frontsector->e->XFloor.lightlist.Size())
{
lightlist_t *light = P_GetPlaneLight(frontsector, fakeFloor->top.plane, true);
wall.Colormap = GetColorTable(light->extra_colormap, wall.Side->GetSpecialColor(wall.Wallpart, side_t::walltop, frontsector));
wall.SectorLightLevel = *light->p_lightlevel;
}
else
{
wall.SectorLightLevel = frontsector->lightlevel;
}
if (!wall.Masked)
wall.Render(thread);
else
translucentWallsOutput.push_back(thread->FrameMemory->NewObject<PolyTranslucentWall>(wall));
}
void RenderPolyWall::SetCoords(const DVector2 &v1, const DVector2 &v2, double ceil1, double floor1, double ceil2, double floor2)
{
this->v1 = v1;
this->v2 = v2;
this->ceil1 = ceil1;
this->floor1 = floor1;
this->ceil2 = ceil2;
this->floor2 = floor2;
}
void RenderPolyWall::Render(PolyRenderThread *thread)
{
bool foggy = false;
if (!Texture && !Polyportal && !FogBoundary)
return;
TriVertex *vertices = thread->FrameMemory->AllocMemory<TriVertex>(4);
vertices[0].x = (float)v1.X;
vertices[0].y = (float)v1.Y;
vertices[0].z = (float)ceil1;
vertices[0].w = 1.0f;
vertices[1].x = (float)v2.X;
vertices[1].y = (float)v2.Y;
vertices[1].z = (float)ceil2;
vertices[1].w = 1.0f;
vertices[2].x = (float)v2.X;
vertices[2].y = (float)v2.Y;
vertices[2].z = (float)floor2;
vertices[2].w = 1.0f;
vertices[3].x = (float)v1.X;
vertices[3].y = (float)v1.Y;
vertices[3].z = (float)floor1;
vertices[3].w = 1.0f;
if (Texture)
{
PolyWallTextureCoordsU texcoordsU(Texture, LineSeg, LineSegLine, Side, Wallpart);
PolyWallTextureCoordsV texcoordsVLeft(Texture, Line, Side, Wallpart, ceil1, floor1, UnpeggedCeil1, TopTexZ, BottomTexZ);
PolyWallTextureCoordsV texcoordsVRght(Texture, Line, Side, Wallpart, ceil2, floor2, UnpeggedCeil2, TopTexZ, BottomTexZ);
vertices[0].u = (float)texcoordsU.u1;
vertices[0].v = (float)texcoordsVLeft.v1;
vertices[1].u = (float)texcoordsU.u2;
vertices[1].v = (float)texcoordsVRght.v1;
vertices[2].u = (float)texcoordsU.u2;
vertices[2].v = (float)texcoordsVRght.v2;
vertices[3].u = (float)texcoordsU.u1;
vertices[3].v = (float)texcoordsVLeft.v2;
}
else
{
for (int i = 0; i < 4; i++)
{
vertices[i].u = 0.0f;
vertices[i].v = 0.0f;
}
}
// Masked walls clamp to the 0-1 range (no texture repeat)
if (Masked)
{
bool wrap = (Line->flags & ML_WRAP_MIDTEX) || (Side->Flags & WALLF_WRAP_MIDTEX);
if (!wrap)
{
ClampHeight(vertices[0], vertices[3]);
ClampHeight(vertices[1], vertices[2]);
}
}
PolyDrawArgs args;
args.SetLight(Colormap, GetLightLevel(), PolyRenderer::Instance()->Light.WallGlobVis(foggy), false);
if (Texture && !Polyportal)
args.SetTexture(Texture, DefaultRenderStyle());
SetDynLights(thread, args);
if (FogBoundary)
{
args.SetStencilTestValue(StencilValue + 1);
args.SetStyle(TriBlendMode::FogBoundary);
args.SetColor(0xffffffff, 254);
args.SetDepthTest(true);
args.SetWriteDepth(true);
args.SetWriteStencil(false);
PolyTriangleDrawer::DrawArray(thread->DrawQueue, args, vertices, 4, PolyDrawMode::TriangleFan);
if (!Texture)
return;
}
if (Polyportal)
{
args.SetStencilTestValue(StencilValue);
args.SetWriteStencil(true, Polyportal->StencilValue);
args.SetWriteColor(false);
args.SetWriteDepth(false);
PolyTriangleDrawer::DrawArray(thread->DrawQueue, args, vertices, 4, PolyDrawMode::TriangleFan);
Polyportal->Shape.push_back({ vertices, 4 });
}
else if (!Masked)
{
args.SetStencilTestValue(StencilValue);
args.SetWriteStencil(true, StencilValue + 1);
args.SetStyle(TriBlendMode::Opaque);
DrawStripes(thread, args, vertices);
}
else
{
double a = MIN(Alpha, 1.0);
if (Additive)
args.SetStyle(TriBlendMode::Add, a);
else if (a < 1.0)
args.SetStyle(TriBlendMode::Translucent, a);
else
args.SetStyle(TriBlendMode::Normal);
args.SetStencilTestValue(StencilValue + 1);
args.SetDepthTest(true);
args.SetWriteDepth(true);
args.SetWriteStencil(false);
DrawStripes(thread, args, vertices);
}
RenderPolyDecal::RenderWallDecals(thread, LineSeg, StencilValue + 1);
}
void RenderPolyWall::SetDynLights(PolyRenderThread *thread, PolyDrawArgs &args)
{
if (!r_dynlights)
{
args.SetLights(nullptr, 0);
return;
}
FLightNode *light_list = (LineSeg && LineSeg->sidedef) ? LineSeg->sidedef->lighthead : nullptr;
auto cameraLight = PolyCameraLight::Instance();
if ((cameraLight->FixedLightLevel() >= 0) || (cameraLight->FixedColormap() != nullptr))
{
args.SetLights(nullptr, 0); // [SP] Don't draw dynlights if invul/lightamp active
return;
}
// Calculate max lights that can touch the wall so we can allocate memory for the list
int max_lights = 0;
FLightNode *cur_node = light_list;
while (cur_node)
{
if (cur_node->lightsource->IsActive())
max_lights++;
cur_node = cur_node->nextLight;
}
if (max_lights == 0)
{
args.SetLights(nullptr, 0);
return;
}
int dc_num_lights = 0;
PolyLight *dc_lights = thread->FrameMemory->AllocMemory<PolyLight>(max_lights);
// Setup lights
cur_node = light_list;
while (cur_node)
{
if (cur_node->lightsource->IsActive())
{
bool is_point_light = cur_node->lightsource->IsAttenuated();
// To do: cull lights not touching wall
uint32_t red = cur_node->lightsource->GetRed();
uint32_t green = cur_node->lightsource->GetGreen();
uint32_t blue = cur_node->lightsource->GetBlue();
auto &light = dc_lights[dc_num_lights++];
light.x = (float)cur_node->lightsource->X();
light.y = (float)cur_node->lightsource->Y();
light.z = (float)cur_node->lightsource->Z();
light.radius = 256.0f / cur_node->lightsource->GetRadius();
light.color = (red << 16) | (green << 8) | blue;
if (is_point_light)
light.radius = -light.radius;
}
cur_node = cur_node->nextLight;
}
args.SetLights(dc_lights, dc_num_lights);
// Face normal:
float dx = (float)(v2.X - v1.X);
float dy = (float)(v2.Y - v1.Y);
float nx = dy;
float ny = -dx;
float lensqr = nx * nx + ny * ny;
float rcplen = 1.0f / sqrt(lensqr);
nx *= rcplen;
ny *= rcplen;
args.SetNormal({ nx, ny, 0.0f });
}
void RenderPolyWall::DrawStripes(PolyRenderThread *thread, PolyDrawArgs &args, TriVertex *vertices)
{
const auto &lightlist = Line->frontsector->e->XFloor.lightlist;
if (lightlist.Size() > 0)
{
PolyClipPlane topPlane;
for (unsigned int i = 0; i < lightlist.Size(); i++)
{
lightlist_t *lit = &lightlist[i];
DVector3 normal = lit->plane.Normal();
double d = lit->plane.fD();
if (normal.Z < 0.0)
{
normal = -normal;
d = -d;
}
PolyClipPlane bottomPlane = { (float)normal.X, (float)normal.Y, (float)normal.Z, (float)d };
args.SetClipPlane(1, topPlane);
args.SetClipPlane(2, bottomPlane);
PolyTriangleDrawer::DrawArray(thread->DrawQueue, args, vertices, 4, PolyDrawMode::TriangleFan);
FDynamicColormap *basecolormap = GetColorTable(lit->extra_colormap, Line->frontsector->SpecialColors[sector_t::walltop]);
bool foggy = false;
int lightlevel;
PolyCameraLight *cameraLight = PolyCameraLight::Instance();
if (cameraLight->FixedLightLevel() >= 0 || cameraLight->FixedColormap())
{
lightlevel = 255;
}
else
{
int actualextralight = foggy ? 0 : PolyRenderer::Instance()->Viewpoint.extralight << 4;
lightlevel = clamp(Side->GetLightLevel(foggy, *lit->p_lightlevel) + actualextralight, 0, 255);
}
args.SetLight(basecolormap, lightlevel, PolyRenderer::Instance()->Light.WallGlobVis(foggy), false);
topPlane = { (float)-normal.X, (float)-normal.Y, (float)-normal.Z, (float)-d };
}
args.SetClipPlane(1, topPlane);
args.SetClipPlane(2, PolyClipPlane());
PolyTriangleDrawer::DrawArray(thread->DrawQueue, args, vertices, 4, PolyDrawMode::TriangleFan);
}
else
{
PolyTriangleDrawer::DrawArray(thread->DrawQueue, args, vertices, 4, PolyDrawMode::TriangleFan);
}
}
void RenderPolyWall::ClampHeight(TriVertex &v1, TriVertex &v2)
{
float top = v1.z;
float bottom = v2.z;
float texv1 = v1.v;
float texv2 = v2.v;
float delta = (texv2 - texv1);
float t1 = texv1 < 0.0f ? -texv1 / delta : 0.0f;
float t2 = texv2 > 1.0f ? (1.0f - texv1) / delta : 1.0f;
float inv_t1 = 1.0f - t1;
float inv_t2 = 1.0f - t2;
v1.z = top * inv_t1 + bottom * t1;
v1.v = texv1 * inv_t1 + texv2 * t1;
v2.z = top * inv_t2 + bottom * t2;
v2.v = texv1 * inv_t2 + texv2 * t2;
}
FSoftwareTexture *RenderPolyWall::GetTexture(const line_t *line, const side_t *side, side_t::ETexpart texpart)
{
FTexture *tex = TexMan.GetPalettedTexture(side->GetTexture(texpart), true);
if (tex == nullptr || !tex->isValid())
{
// Mapping error. Doom floodfills this with a plane.
// This code doesn't do that, but at least it uses the "right" texture..
if (line && line->backsector && line->sidedef[0] == side)
{
if (texpart == side_t::top)
tex = TexMan.GetPalettedTexture(line->backsector->GetTexture(sector_t::ceiling), true);
else if (texpart == side_t::bottom)
tex = TexMan.GetPalettedTexture(line->backsector->GetTexture(sector_t::floor), true);
}
if (line && line->backsector && line->sidedef[1] == side)
{
if (texpart == side_t::top)
tex = TexMan.GetPalettedTexture(line->frontsector->GetTexture(sector_t::ceiling), true);
else if (texpart == side_t::bottom)
tex = TexMan.GetPalettedTexture(line->frontsector->GetTexture(sector_t::floor), true);
}
if (tex == nullptr || !tex->isValid())
return nullptr;
}
return tex? tex->GetSoftwareTexture() : nullptr;
}
int RenderPolyWall::GetLightLevel()
{
PolyCameraLight *cameraLight = PolyCameraLight::Instance();
if (cameraLight->FixedLightLevel() >= 0 || cameraLight->FixedColormap())
{
return 255;
}
else
{
bool foggy = false;
int actualextralight = foggy ? 0 : PolyRenderer::Instance()->Viewpoint.extralight << 4;
return clamp(Side->GetLightLevel(foggy, SectorLightLevel) + actualextralight, 0, 255);
}
}
/////////////////////////////////////////////////////////////////////////////
PolyWallTextureCoordsU::PolyWallTextureCoordsU(FSoftwareTexture *tex, const seg_t *lineseg, const line_t *line, const side_t *side, side_t::ETexpart wallpart)
{
// Calculate the U texture coordinate for the line
double lineu1 = side->GetTextureXOffset(wallpart);
double lineu2 = side->GetTextureXOffset(wallpart) + line->sidedef[0]->TexelLength * side->GetTextureXScale(wallpart);
lineu1 *= tex->GetScale().X / tex->GetWidth();
lineu2 *= tex->GetScale().X / tex->GetWidth();
// Calculate where we are on the lineseg
double t1, t2;
if (fabs(line->delta.X) > fabs(line->delta.Y))
{
t1 = (lineseg->v1->fX() - line->v1->fX()) / line->delta.X;
t2 = (lineseg->v2->fX() - line->v1->fX()) / line->delta.X;
}
else
{
t1 = (lineseg->v1->fY() - line->v1->fY()) / line->delta.Y;
t2 = (lineseg->v2->fY() - line->v1->fY()) / line->delta.Y;
}
// Check if lineseg is the backside of the line
if (t2 < t1)
{
std::swap(lineu1, lineu2);
}
// Calculate texture coordinates for the lineseg
u1 = (1.0 - t1) * lineu1 + t1 * lineu2;
u2 = (1.0 - t2) * lineu1 + t2 * lineu2;
}
/////////////////////////////////////////////////////////////////////////////
PolyWallTextureCoordsV::PolyWallTextureCoordsV(FSoftwareTexture *tex, const line_t *line, const side_t *side, side_t::ETexpart wallpart, double topz, double bottomz, double unpeggedceil, double topTexZ, double bottomTexZ)
{
double yoffset = side->GetTextureYOffset(wallpart);
if (tex->useWorldPanning(line->GetLevel()))
yoffset *= side->GetTextureYScale(wallpart) * tex->GetScale().Y;
switch (wallpart)
{
default:
case side_t::mid:
CalcVMidPart(tex, line, side, topTexZ, bottomTexZ, yoffset);
break;
case side_t::top:
CalcVTopPart(tex, line, side, topTexZ, bottomTexZ, yoffset);
break;
case side_t::bottom:
CalcVBottomPart(tex, line, side, topTexZ, bottomTexZ, unpeggedceil, yoffset);
break;
}
v1 *= tex->GetScale().Y / tex->GetHeight();
v2 *= tex->GetScale().Y / tex->GetHeight();
double texZHeight = (bottomTexZ - topTexZ);
if (texZHeight > 0.0f || texZHeight < -0.0f)
{
double t1 = (topz - topTexZ) / texZHeight;
double t2 = (bottomz - topTexZ) / texZHeight;
double vorig1 = v1;
double vorig2 = v2;
v1 = vorig1 * (1.0f - t1) + vorig2 * t1;
v2 = vorig1 * (1.0f - t2) + vorig2 * t2;
}
}
void PolyWallTextureCoordsV::CalcVTopPart(FSoftwareTexture *tex, const line_t *line, const side_t *side, double topz, double bottomz, double yoffset)
{
bool pegged = (line->flags & ML_DONTPEGTOP) == 0;
if (pegged) // bottom to top
{
double texHeight = tex->GetHeight() / tex->GetScale().Y;
v1 = (topz - bottomz) * side->GetTextureYScale(side_t::top) - yoffset;
v2 = -yoffset;
v1 = texHeight - v1;
v2 = texHeight - v2;
}
else // top to bottom
{
v1 = yoffset;
v2 = (topz - bottomz) * side->GetTextureYScale(side_t::top) + yoffset;
}
}
void PolyWallTextureCoordsV::CalcVMidPart(FSoftwareTexture *tex, const line_t *line, const side_t *side, double topz, double bottomz, double yoffset)
{
bool pegged = (line->flags & ML_DONTPEGBOTTOM) == 0;
if (pegged) // top to bottom
{
v1 = yoffset;
v2 = (topz - bottomz) * side->GetTextureYScale(side_t::mid) + yoffset;
}
else // bottom to top
{
double texHeight = tex->GetHeight() / tex->GetScale().Y;
v1 = yoffset - (topz - bottomz) * side->GetTextureYScale(side_t::mid);
v2 = yoffset;
v1 = texHeight + v1;
v2 = texHeight + v2;
}
}
void PolyWallTextureCoordsV::CalcVBottomPart(FSoftwareTexture *tex, const line_t *line, const side_t *side, double topz, double bottomz, double unpeggedceil, double yoffset)
{
bool pegged = (line->flags & ML_DONTPEGBOTTOM) == 0;
if (pegged) // top to bottom
{
v1 = yoffset;
v2 = yoffset + (topz - bottomz) * side->GetTextureYScale(side_t::bottom);
}
else
{
v1 = yoffset + (unpeggedceil - topz) * side->GetTextureYScale(side_t::bottom);
v2 = yoffset + (unpeggedceil - bottomz) * side->GetTextureYScale(side_t::bottom);
}
}

View file

@ -1,110 +0,0 @@
/*
** Polygon Doom software renderer
** Copyright (c) 2016 Magnus Norddahl
**
** This software is provided 'as-is', without any express or implied
** warranty. In no event will the authors be held liable for any damages
** arising from the use of this software.
**
** Permission is granted to anyone to use this software for any purpose,
** including commercial applications, and to alter it and redistribute it
** freely, subject to the following restrictions:
**
** 1. The origin of this software must not be misrepresented; you must not
** claim that you wrote the original software. If you use this software
** in a product, an acknowledgment in the product documentation would be
** appreciated but is not required.
** 2. Altered source versions must be plainly marked as such, and must not be
** misrepresented as being the original software.
** 3. This notice may not be removed or altered from any source distribution.
**
*/
#pragma once
#include "polyrenderer/drawers/poly_triangle.h"
class PolyTranslucentObject;
class PolyDrawLinePortal;
class PolyCull;
class RenderPolyWall
{
public:
static bool RenderLine(PolyRenderThread *thread, seg_t *line, sector_t *frontsector, uint32_t subsectorDepth, uint32_t stencilValue, std::vector<PolyTranslucentObject*> &translucentWallsOutput, std::vector<std::unique_ptr<PolyDrawLinePortal>> &linePortals, size_t linePortalsStart, line_t *portalEnterLine);
static void Render3DFloorLine(PolyRenderThread *thread, seg_t *line, sector_t *frontsector, uint32_t subsectorDepth, uint32_t stencilValue, F3DFloor *fakeFloor, std::vector<PolyTranslucentObject*> &translucentWallsOutput);
void SetCoords(const DVector2 &v1, const DVector2 &v2, double ceil1, double floor1, double ceil2, double floor2);
void Render(PolyRenderThread *thread);
DVector2 v1;
DVector2 v2;
double ceil1 = 0.0;
double floor1 = 0.0;
double ceil2 = 0.0;
double floor2 = 0.0;
const seg_t *LineSeg = nullptr;
const line_t *LineSegLine = nullptr;
const line_t *Line = nullptr;
const side_t *Side = nullptr;
FSoftwareTexture *Texture = nullptr;
side_t::ETexpart Wallpart = side_t::mid;
double TopTexZ = 0.0;
double BottomTexZ = 0.0;
double UnpeggedCeil1 = 0.0;
double UnpeggedCeil2 = 0.0;
FSWColormap *Colormap = nullptr;
int SectorLightLevel = 0;
bool Masked = false;
bool Additive = false;
double Alpha = 1.0;
bool FogBoundary = false;
uint32_t SubsectorDepth = 0;
uint32_t StencilValue = 0;
PolyDrawLinePortal *Polyportal = nullptr;
private:
void ClampHeight(TriVertex &v1, TriVertex &v2);
int GetLightLevel();
void DrawStripes(PolyRenderThread *thread, PolyDrawArgs &args, TriVertex *vertices);
void SetDynLights(PolyRenderThread *thread, PolyDrawArgs &args);
static bool IsFogBoundary(sector_t *front, sector_t *back);
static FSoftwareTexture *GetTexture(const line_t *Line, const side_t *Side, side_t::ETexpart texpart);
};
class PolyWallTextureCoordsU
{
public:
PolyWallTextureCoordsU(FSoftwareTexture *tex, const seg_t *lineseg, const line_t *linesegline, const side_t *side, side_t::ETexpart wallpart);
double u1, u2;
};
class PolyWallTextureCoordsV
{
public:
PolyWallTextureCoordsV(FSoftwareTexture *tex, const line_t *line, const side_t *side, side_t::ETexpart wallpart, double topz, double bottomz, double unpeggedceil, double topTexZ, double bottomTexZ);
double v1, v2;
private:
void CalcVTopPart(FSoftwareTexture *tex, const line_t *line, const side_t *side, double topz, double bottomz, double yoffset);
void CalcVMidPart(FSoftwareTexture *tex, const line_t *line, const side_t *side, double topz, double bottomz, double yoffset);
void CalcVBottomPart(FSoftwareTexture *tex, const line_t *line, const side_t *side, double topz, double bottomz, double unpeggedceil, double yoffset);
};
class PolyTranslucentWall : public PolyTranslucentObject
{
public:
PolyTranslucentWall(RenderPolyWall wall) : PolyTranslucentObject(wall.SubsectorDepth, 1e6), wall(wall) { }
void Render(PolyRenderThread *thread) override
{
wall.Render(thread);
}
RenderPolyWall wall;
};

View file

@ -1,112 +0,0 @@
/*
** Polygon Doom software renderer
** Copyright (c) 2016 Magnus Norddahl
**
** This software is provided 'as-is', without any express or implied
** warranty. In no event will the authors be held liable for any damages
** arising from the use of this software.
**
** Permission is granted to anyone to use this software for any purpose,
** including commercial applications, and to alter it and redistribute it
** freely, subject to the following restrictions:
**
** 1. The origin of this software must not be misrepresented; you must not
** claim that you wrote the original software. If you use this software
** in a product, an acknowledgment in the product documentation would be
** appreciated but is not required.
** 2. Altered source versions must be plainly marked as such, and must not be
** misrepresented as being the original software.
** 3. This notice may not be removed or altered from any source distribution.
**
*/
#include <stdlib.h>
#include "templates.h"
#include "doomdef.h"
#include "sbar.h"
#include "r_data/r_translate.h"
#include "poly_wallsprite.h"
#include "polyrenderer/poly_renderer.h"
#include "polyrenderer/scene/poly_light.h"
#include "polyrenderer/poly_renderthread.h"
void RenderPolyWallSprite::Render(PolyRenderThread *thread, AActor *thing, subsector_t *sub, uint32_t stencilValue)
{
if (RenderPolySprite::IsThingCulled(thing))
return;
const auto &viewpoint = PolyRenderer::Instance()->Viewpoint;
DVector3 pos = thing->InterpolatedPosition(viewpoint.TicFrac);
pos.Z += thing->GetBobOffset(viewpoint.TicFrac);
bool flipTextureX = false;
FSoftwareTexture *tex = RenderPolySprite::GetSpriteTexture(thing, flipTextureX);
if (tex == nullptr)
return;
DVector2 spriteScale = thing->Scale;
double thingxscalemul = spriteScale.X / tex->GetScale().X;
double thingyscalemul = spriteScale.Y / tex->GetScale().Y;
double spriteHeight = thingyscalemul * tex->GetHeight();
DAngle ang = thing->Angles.Yaw + 90;
double angcos = ang.Cos();
double angsin = ang.Sin();
// Determine left and right edges of sprite. The sprite's angle is its normal,
// so the edges are 90 degrees each side of it.
double x2 = tex->GetScaledWidth() * spriteScale.X;
double x1 = tex->GetScaledLeftOffsetPo() * spriteScale.X;
DVector2 left, right;
left.X = pos.X - x1 * angcos;
left.Y = pos.Y - x1 * angsin;
right.X = left.X + x2 * angcos;
right.Y = left.Y + x2 * angsin;
//int scaled_to = tex->GetScaledTopOffset();
//int scaled_bo = scaled_to - tex->GetScaledHeight();
//gzt = pos.Z + scale.Y * scaled_to;
//gzb = pos.Z + scale.Y * scaled_bo;
DVector2 points[2] = { left, right };
TriVertex *vertices = thread->FrameMemory->AllocMemory<TriVertex>(4);
bool foggy = false;
int actualextralight = foggy ? 0 : viewpoint.extralight << 4;
std::pair<float, float> offsets[4] =
{
{ 0.0f, 1.0f },
{ 1.0f, 1.0f },
{ 1.0f, 0.0f },
{ 0.0f, 0.0f },
};
for (int i = 0; i < 4; i++)
{
auto &p = (i == 0 || i == 3) ? points[0] : points[1];
vertices[i].x = (float)p.X;
vertices[i].y = (float)p.Y;
vertices[i].z = (float)(pos.Z + spriteHeight * offsets[i].second);
vertices[i].w = 1.0f;
vertices[i].u = (float)(offsets[i].first * tex->GetScale().X);
vertices[i].v = (float)((1.0f - offsets[i].second) * tex->GetScale().Y);
if (flipTextureX)
vertices[i].u = 1.0f - vertices[i].u;
}
bool fullbrightSprite = ((thing->renderflags & RF_FULLBRIGHT) || (thing->flags5 & MF5_BRIGHT));
int lightlevel = fullbrightSprite ? 255 : thing->Sector->lightlevel + actualextralight;
PolyDrawArgs args;
auto nc = !!(thing->Level->flags3 & LEVEL3_NOCOLOREDSPRITELIGHTING);
args.SetLight(GetSpriteColorTable(sub->sector->Colormap, sub->sector->SpecialColors[sector_t::sprites], nc), lightlevel, PolyRenderer::Instance()->Light.WallGlobVis(foggy), fullbrightSprite); args.SetStencilTestValue(stencilValue);
args.SetTexture(tex, thing->RenderStyle);
args.SetDepthTest(true);
args.SetWriteDepth(false);
args.SetWriteStencil(false);
args.SetStyle(TriBlendMode::Normal);
PolyTriangleDrawer::DrawArray(thread->DrawQueue, args, vertices, 4, PolyDrawMode::TriangleFan);
}

View file

@ -1,31 +0,0 @@
/*
** Polygon Doom software renderer
** Copyright (c) 2016 Magnus Norddahl
**
** This software is provided 'as-is', without any express or implied
** warranty. In no event will the authors be held liable for any damages
** arising from the use of this software.
**
** Permission is granted to anyone to use this software for any purpose,
** including commercial applications, and to alter it and redistribute it
** freely, subject to the following restrictions:
**
** 1. The origin of this software must not be misrepresented; you must not
** claim that you wrote the original software. If you use this software
** in a product, an acknowledgment in the product documentation would be
** appreciated but is not required.
** 2. Altered source versions must be plainly marked as such, and must not be
** misrepresented as being the original software.
** 3. This notice may not be removed or altered from any source distribution.
**
*/
#pragma once
#include "polyrenderer/drawers/poly_triangle.h"
class RenderPolyWallSprite
{
public:
void Render(PolyRenderThread *thread, AActor *thing, subsector_t *sub, uint32_t stencilValue);
};

View file

@ -51,6 +51,7 @@
#include "r_draw_pal.h"
#include "r_thread.h"
#include "swrenderer/scene/r_light.h"
#include "polyrenderer/drawers/poly_buffer.h"
CVAR(Bool, r_dynlights, 1, CVAR_ARCHIVE | CVAR_GLOBALCONFIG);
CVAR(Bool, r_fuzzscale, 1, CVAR_ARCHIVE | CVAR_GLOBALCONFIG);

View file

@ -33,6 +33,7 @@
#include "r_thread.h"
#include "swrenderer/r_memory.h"
#include "swrenderer/r_renderthread.h"
#include "polyrenderer/drawers/poly_triangle.h"
#include <chrono>
#ifdef WIN32

View file

@ -48,7 +48,6 @@
#include "textures/textures.h"
#include "r_data/voxels.h"
#include "drawers/r_draw_rgba.h"
#include "polyrenderer/poly_renderer.h"
#include "p_setup.h"
#include "g_levellocals.h"
#include "image.h"
@ -185,22 +184,11 @@ void FSoftwareRenderer::Precache(uint8_t *texhitlist, TMap<PClassActor*, bool> &
void FSoftwareRenderer::RenderView(player_t *player, DCanvas *target, void *videobuffer, int bufferpitch)
{
if (V_IsPolyRenderer())
{
PolyRenderer::Instance()->Viewpoint = r_viewpoint;
PolyRenderer::Instance()->Viewwindow = r_viewwindow;
PolyRenderer::Instance()->RenderView(player, target, videobuffer, bufferpitch);
r_viewpoint = PolyRenderer::Instance()->Viewpoint;
r_viewwindow = PolyRenderer::Instance()->Viewwindow;
}
else
{
mScene.MainThread()->Viewport->viewpoint = r_viewpoint;
mScene.MainThread()->Viewport->viewwindow = r_viewwindow;
mScene.RenderView(player, target, videobuffer, bufferpitch);
r_viewpoint = mScene.MainThread()->Viewport->viewpoint;
r_viewwindow = mScene.MainThread()->Viewport->viewwindow;
}
mScene.MainThread()->Viewport->viewpoint = r_viewpoint;
mScene.MainThread()->Viewport->viewwindow = r_viewwindow;
mScene.RenderView(player, target, videobuffer, bufferpitch);
r_viewpoint = mScene.MainThread()->Viewport->viewpoint;
r_viewwindow = mScene.MainThread()->Viewport->viewwindow;
r_viewpoint.ViewLevel->canvasTextureInfo.UpdateAll([&](AActor *camera, FCanvasTexture *camtex, double fov)
{
@ -215,43 +203,22 @@ void FSoftwareRenderer::WriteSavePic (player_t *player, FileWriter *file, int wi
DCanvas pic(width, height, false);
// Take a snapshot of the player's view
if (V_IsPolyRenderer())
{
PolyRenderer::Instance()->Viewpoint = r_viewpoint;
PolyRenderer::Instance()->Viewwindow = r_viewwindow;
PolyRenderer::Instance()->RenderViewToCanvas(player->mo, &pic, 0, 0, width, height, true);
r_viewpoint = PolyRenderer::Instance()->Viewpoint;
r_viewwindow = PolyRenderer::Instance()->Viewwindow;
}
else
{
mScene.MainThread()->Viewport->viewpoint = r_viewpoint;
mScene.MainThread()->Viewport->viewwindow = r_viewwindow;
mScene.RenderViewToCanvas(player->mo, &pic, 0, 0, width, height);
r_viewpoint = mScene.MainThread()->Viewport->viewpoint;
r_viewwindow = mScene.MainThread()->Viewport->viewwindow;
}
mScene.MainThread()->Viewport->viewpoint = r_viewpoint;
mScene.MainThread()->Viewport->viewwindow = r_viewwindow;
mScene.RenderViewToCanvas(player->mo, &pic, 0, 0, width, height);
r_viewpoint = mScene.MainThread()->Viewport->viewpoint;
r_viewwindow = mScene.MainThread()->Viewport->viewwindow;
DoWriteSavePic(file, SS_PAL, pic.GetPixels(), width, height, r_viewpoint.sector, false);
}
void FSoftwareRenderer::DrawRemainingPlayerSprites()
{
if (!V_IsPolyRenderer())
{
mScene.MainThread()->Viewport->viewpoint = r_viewpoint;
mScene.MainThread()->Viewport->viewwindow = r_viewwindow;
mScene.MainThread()->PlayerSprites->RenderRemaining();
r_viewpoint = mScene.MainThread()->Viewport->viewpoint;
r_viewwindow = mScene.MainThread()->Viewport->viewwindow;
}
else
{
PolyRenderer::Instance()->Viewpoint = r_viewpoint;
PolyRenderer::Instance()->Viewwindow = r_viewwindow;
PolyRenderer::Instance()->RenderRemainingPlayerSprites();
r_viewpoint = PolyRenderer::Instance()->Viewpoint;
r_viewwindow = PolyRenderer::Instance()->Viewwindow;
}
mScene.MainThread()->Viewport->viewpoint = r_viewpoint;
mScene.MainThread()->Viewport->viewwindow = r_viewwindow;
mScene.MainThread()->PlayerSprites->RenderRemaining();
r_viewpoint = mScene.MainThread()->Viewport->viewpoint;
r_viewwindow = mScene.MainThread()->Viewport->viewwindow;
}
void FSoftwareRenderer::SetClearColor(int color)
@ -261,9 +228,9 @@ void FSoftwareRenderer::SetClearColor(int color)
void FSoftwareRenderer::RenderTextureView (FCanvasTexture *camtex, AActor *viewpoint, double fov)
{
auto renderTarget = V_IsPolyRenderer() ? PolyRenderer::Instance()->RenderTarget : mScene.MainThread()->Viewport->RenderTarget;
auto &cameraViewpoint = V_IsPolyRenderer() ? PolyRenderer::Instance()->Viewpoint : mScene.MainThread()->Viewport->viewpoint;
auto &cameraViewwindow = V_IsPolyRenderer() ? PolyRenderer::Instance()->Viewwindow : mScene.MainThread()->Viewport->viewwindow;
auto renderTarget = mScene.MainThread()->Viewport->RenderTarget;
auto &cameraViewpoint = mScene.MainThread()->Viewport->viewpoint;
auto &cameraViewwindow = mScene.MainThread()->Viewport->viewwindow;
// Grab global state shared with rest of zdoom
cameraViewpoint = r_viewpoint;
@ -280,10 +247,7 @@ void FSoftwareRenderer::RenderTextureView (FCanvasTexture *camtex, AActor *viewp
DAngle savedfov = cameraViewpoint.FieldOfView;
R_SetFOV (cameraViewpoint, fov);
if (V_IsPolyRenderer())
PolyRenderer::Instance()->RenderViewToCanvas(viewpoint, Canvas, 0, 0, tex->GetWidth(), tex->GetHeight(), camtex->bFirstUpdate);
else
mScene.RenderViewToCanvas(viewpoint, Canvas, 0, 0, tex->GetWidth(), tex->GetHeight(), camtex->bFirstUpdate);
mScene.RenderViewToCanvas(viewpoint, Canvas, 0, 0, tex->GetWidth(), tex->GetHeight(), camtex->bFirstUpdate);
R_SetFOV (cameraViewpoint, savedfov);

View file

@ -86,6 +86,7 @@ namespace swrenderer
double FlatPlaneVis(int screenY, double planeheight, bool foggy, RenderViewport *viewport) const { return FlatPlaneGlobVis(foggy) / planeheight * fabs(viewport->CenterY - screenY); }
double SlopePlaneGlobVis(bool foggy) const { return (NoLightFade && !foggy) ? 0.0f : TiltVisibility; }
double SpriteGlobVis(bool foggy) const { return (NoLightFade && !foggy) ? 0.0f : WallVisibility; }
static fixed_t LightLevelToShade(int lightlevel, bool foggy, RenderViewport *viewport) { return LightLevelToShadeImpl(viewport, lightlevel + ActualExtraLight(foggy, viewport), foggy); }
@ -93,7 +94,6 @@ namespace swrenderer
private:
double WallGlobVis(bool foggy) const { return (NoLightFade && !foggy) ? 0.0f : WallVisibility; }
double SpriteGlobVis(bool foggy) const { return (NoLightFade && !foggy) ? 0.0f : WallVisibility; }
double FlatPlaneGlobVis(bool foggy) const { return (NoLightFade && !foggy) ? 0.0f : FloorVisibility; }
static fixed_t LightLevelToShadeImpl(RenderViewport *viewport, int lightlevel, bool foggy);

View file

@ -83,10 +83,10 @@ namespace swrenderer
return;
bool foggy = false;
int actualextralight = foggy ? 0 : PolyRenderer::Instance()->Viewpoint.extralight << 4;
int actualextralight = foggy ? 0 : thread->Viewport->viewpoint.extralight << 4;
bool fullbrightSprite = ((actor->renderflags & RF_FULLBRIGHT) || (actor->flags5 & MF5_BRIGHT));
renderer.lightlevel = fullbrightSprite ? 255 : actor->Sector->lightlevel + actualextralight;
renderer.visibility = PolyRenderer::Instance()->Light.SpriteGlobVis(foggy);
renderer.visibility = thread->Light->SpriteGlobVis(foggy);
renderer.fillcolor = actor->fillcolor;
renderer.Translation = actor->Translation;
@ -128,10 +128,10 @@ namespace swrenderer
return;
bool foggy = false;
int actualextralight = foggy ? 0 : PolyRenderer::Instance()->Viewpoint.extralight << 4;
int actualextralight = foggy ? 0 : thread->Viewport->viewpoint.extralight << 4;
bool fullbrightSprite = isBright(psp);
renderer.lightlevel = fullbrightSprite ? 255 : playermo->Sector->lightlevel + actualextralight;
renderer.visibility = PolyRenderer::Instance()->Light.SpriteGlobVis(foggy);
renderer.visibility = thread->Light->SpriteGlobVis(foggy);
PalEntry ThingColor = (playermo->RenderStyle.Flags & STYLEF_ColorIsFixed) ? playermo->fillcolor : 0xffffff;
ThingColor.a = 255;
@ -354,7 +354,8 @@ namespace swrenderer
{
PolyDrawArgs args;
auto nc = !!(sector->Level->flags3 & LEVEL3_NOCOLOREDSPRITELIGHTING);
args.SetLight(GetSpriteColorTable(sector->Colormap, sector->SpecialColors[sector_t::sprites], nc), lightlevel, visibility, fullbrightSprite); args.SetLights(Lights, NumLights);
args.SetLight(GetSpriteColorTable(sector->Colormap, sector->SpecialColors[sector_t::sprites], nc), lightlevel, visibility, fullbrightSprite);
args.SetLights(Lights, NumLights);
args.SetNormal(FVector3(0.0f, 0.0f, 0.0f));
args.SetStyle(RenderStyle, RenderAlpha, fillcolor, Translation, SkinTexture->GetSoftwareTexture(), fullbrightSprite);
args.SetDepthTest(true);
@ -371,7 +372,8 @@ namespace swrenderer
{
PolyDrawArgs args;
auto nc = !!(sector->Level->flags3 & LEVEL3_NOCOLOREDSPRITELIGHTING);
args.SetLight(GetSpriteColorTable(sector->Colormap, sector->SpecialColors[sector_t::sprites], nc), lightlevel, visibility, fullbrightSprite); args.SetLights(Lights, NumLights);
args.SetLight(GetSpriteColorTable(sector->Colormap, sector->SpecialColors[sector_t::sprites], nc), lightlevel, visibility, fullbrightSprite);
args.SetLights(Lights, NumLights);
args.SetNormal(FVector3(0.0f, 0.0f, 0.0f));
args.SetStyle(RenderStyle, RenderAlpha, fillcolor, Translation, SkinTexture->GetSoftwareTexture(), fullbrightSprite);
args.SetDepthTest(true);

View file

@ -5,6 +5,7 @@
#include <memory>
#include "v_video.h"
#include "r_defs.h"
#include "r_utility.h"
#include "actorinlines.h"
#include "polyrenderer/math/gpu_types.h"