Merge remote-tracking branch 'origin/polybackend'

This commit is contained in:
Rachael Alexanderson 2019-12-06 09:30:55 -05:00
commit eaab122076
73 changed files with 3991 additions and 10161 deletions

View file

@ -462,7 +462,8 @@ set( PLAT_WIN32_SOURCES
win32/gl_sysfb.cpp
win32/base_sysfb.cpp
win32/win32basevideo.cpp
win32/win32glvideo.cpp)
win32/win32glvideo.cpp
win32/win32polyvideo.cpp)
if (HAVE_VULKAN)
set (PLAT_WIN32_SOURCES ${PLAT_WIN32_SOURCES} win32/win32vulkanvideo.cpp )
@ -634,7 +635,7 @@ 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
rendering/hwrenderer/models/*.h
@ -706,24 +707,7 @@ 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
rendering/polyrenderer/drawers/screen_triangle.cpp
rendering/polyrenderer/math/gpu_types.cpp
)
@ -822,6 +806,14 @@ if (HAVE_VULKAN)
set (FASTMATH_SOURCES ${FASTMATH_SOURCES} ${VULKAN_SOURCES})
endif()
set (POLYBACKEND_SOURCES
rendering/polyrenderer/backend/poly_framebuffer.cpp
rendering/polyrenderer/backend/poly_buffers.cpp
rendering/polyrenderer/backend/poly_hwtexture.cpp
rendering/polyrenderer/backend/poly_renderstate.cpp
)
set (FASTMATH_SOURCES ${FASTMATH_SOURCES} ${POLYBACKEND_SOURCES})
set (PCH_SOURCES
am_map.cpp
playsim/bots/b_bot.cpp
@ -1397,7 +1389,7 @@ 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/.+")
source_group("Render Interface" FILES r_defs.h r_renderer.h r_sky.cpp r_sky.h r_state.h r_utility.cpp r_utility.h)

View file

@ -294,9 +294,11 @@ class FTexture
friend class FMaterial;
friend class OpenGLRenderer::FGLRenderState; // For now this needs access to some fields in ApplyMaterial. This should be rerouted through the Material class
friend class VkRenderState;
friend class PolyRenderState;
friend struct FTexCoordInfo;
friend class OpenGLRenderer::FHardwareTexture;
friend class VkHardwareTexture;
friend class PolyHardwareTexture;
friend class FMultiPatchTexture;
friend class FSkyBox;
friend class FBrightmapTexture;

View file

@ -94,7 +94,7 @@
EXTERN_CVAR(Bool, vid_hidpi)
EXTERN_CVAR(Int, vid_defwidth)
EXTERN_CVAR(Int, vid_defheight)
EXTERN_CVAR(Int, vid_enablevulkan)
EXTERN_CVAR(Int, vid_preferbackend)
EXTERN_CVAR(Bool, vk_debug)
CVAR(Bool, mvk_debug, false, 0)
@ -348,7 +348,7 @@ class CocoaVideo : public IVideo
public:
CocoaVideo()
{
ms_isVulkanEnabled = vid_enablevulkan == 1 && NSAppKitVersionNumber >= 1404; // NSAppKitVersionNumber10_11
ms_isVulkanEnabled = vid_preferbackend == 1 && NSAppKitVersionNumber >= 1404; // NSAppKitVersionNumber10_11
}
~CocoaVideo()
@ -955,3 +955,20 @@ bool I_CreateVulkanSurface(VkInstance instance, VkSurfaceKHR *surface)
return result == VK_SUCCESS;
}
#endif
void I_PolyPresentInit()
{
}
uint8_t *I_PolyPresentLock(int w, int h, bool vsync, int &pitch)
{
return nullptr;
}
void I_PolyPresentUnlock(int x, int y, int w, int h)
{
}
void I_PolyPresentDeinit()
{
}

View file

@ -57,6 +57,8 @@
#include "rendering/vulkan/system/vk_framebuffer.h"
#endif
#include "rendering/polyrenderer/backend/poly_framebuffer.h"
// MACROS ------------------------------------------------------------------
// TYPES -------------------------------------------------------------------
@ -73,7 +75,7 @@ EXTERN_CVAR (Int, vid_adapter)
EXTERN_CVAR (Int, vid_displaybits)
EXTERN_CVAR (Int, vid_defwidth)
EXTERN_CVAR (Int, vid_defheight)
EXTERN_CVAR (Int, vid_enablevulkan)
EXTERN_CVAR (Int, vid_preferbackend)
EXTERN_CVAR (Bool, cl_capfps)
// PUBLIC DATA DEFINITIONS -------------------------------------------------
@ -113,6 +115,7 @@ namespace Priv
SDL_Window *window;
bool vulkanEnabled;
bool softpolyEnabled;
bool fullscreenSwitch;
void CreateWindow(uint32_t extraFlags)
@ -230,6 +233,101 @@ bool I_CreateVulkanSurface(VkInstance instance, VkSurfaceKHR *surface)
}
#endif
namespace
{
SDL_Surface* polysurface = nullptr;
}
void I_PolyPresentInit()
{
assert(Priv::softpolyEnabled);
assert(Priv::window != nullptr);
}
uint8_t *I_PolyPresentLock(int w, int h, bool vsync, int &pitch)
{
if (!polysurface || polysurface->w != w || polysurface->h != h)
{
if (polysurface)
{
SDL_FreeSurface(polysurface);
polysurface = nullptr;
}
polysurface = SDL_CreateRGBSurface(0, w, h, 32, 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000);
SDL_SetSurfaceBlendMode(polysurface, SDL_BLENDMODE_NONE);
}
SDL_LockSurface(polysurface);
pitch = polysurface->pitch;
return (uint8_t*)polysurface->pixels;
}
void I_PolyPresentUnlock(int x, int y, int width, int height)
{
SDL_UnlockSurface(polysurface);
SDL_Surface* windowsurface = SDL_GetWindowSurface(Priv::window);
int ClientWidth = windowsurface->w;
int ClientHeight = windowsurface->h;
SDL_Rect clearrects[4];
int count = 0;
if (y > 0)
{
clearrects[count].x = 0;
clearrects[count].y = 0;
clearrects[count].w = ClientWidth;
clearrects[count].h = y;
count++;
}
if (y + height < ClientHeight)
{
clearrects[count].x = 0;
clearrects[count].y = y + height;
clearrects[count].w = ClientWidth;
clearrects[count].h = ClientHeight - clearrects[count].y;
count++;
}
if (x > 0)
{
clearrects[count].x = 0;
clearrects[count].y = y;
clearrects[count].w = x;
clearrects[count].h = height;
count++;
}
if (x + width < ClientWidth)
{
clearrects[count].x = x + width;
clearrects[count].y = y;
clearrects[count].w = ClientWidth - clearrects[count].x;
clearrects[count].h = height;
count++;
}
if (count > 0)
SDL_FillRects(windowsurface, clearrects, count, SDL_MapRGBA(windowsurface->format, 0, 0, 0, 255));
SDL_Rect dstrect;
dstrect.x = x;
dstrect.y = y;
dstrect.w = width;
dstrect.h = height;
SDL_BlitScaled(polysurface, nullptr, windowsurface, &dstrect);
SDL_UpdateWindowSurface(Priv::window);
}
void I_PolyPresentDeinit()
{
if (polysurface)
{
SDL_FreeSurface(polysurface);
polysurface = nullptr;
}
}
SDLVideo::SDLVideo ()
{
@ -246,8 +344,9 @@ SDLVideo::SDLVideo ()
}
#ifdef HAVE_VULKAN
Priv::vulkanEnabled = vid_enablevulkan == 1
Priv::vulkanEnabled = vid_preferbackend == 1
&& Priv::Vulkan_GetDrawableSize && Priv::Vulkan_GetInstanceExtensions && Priv::Vulkan_CreateSurface;
Priv::softpolyEnabled = vid_preferbackend == 2;
if (Priv::vulkanEnabled)
{
@ -258,6 +357,10 @@ SDLVideo::SDLVideo ()
Priv::vulkanEnabled = false;
}
}
else if (Priv::softpolyEnabled)
{
Priv::CreateWindow(SDL_WINDOW_HIDDEN | SDL_WINDOW_OPENGL);
}
#endif
}
@ -294,6 +397,11 @@ DFrameBuffer *SDLVideo::CreateFrameBuffer ()
}
#endif
if (Priv::softpolyEnabled)
{
fb = new PolyFrameBuffer(nullptr, fullscreen);
}
if (fb == nullptr)
{
fb = new OpenGLRenderer::OpenGLFrameBuffer(0, fullscreen);
@ -325,6 +433,11 @@ int SystemBaseFrameBuffer::GetClientWidth()
{
int width = 0;
if (Priv::softpolyEnabled)
{
return SDL_GetWindowSurface(Priv::window)->w;
}
#ifdef HAVE_VULKAN
assert(Priv::vulkanEnabled);
Priv::Vulkan_GetDrawableSize(Priv::window, &width, nullptr);
@ -336,6 +449,11 @@ int SystemBaseFrameBuffer::GetClientWidth()
int SystemBaseFrameBuffer::GetClientHeight()
{
int height = 0;
if (Priv::softpolyEnabled)
{
return SDL_GetWindowSurface(Priv::window)->h;
}
#ifdef HAVE_VULKAN
assert(Priv::vulkanEnabled);

View file

@ -44,7 +44,7 @@ FLightBuffer::FLightBuffer()
// Hack alert: On Intel's GL driver SSBO's perform quite worse than UBOs.
// We only want to disable using SSBOs for lights but not disable the feature entirely.
// Note that using an uniform buffer here will limit the number of lights per surface so it isn't done for NVidia and AMD.
if (screen->IsVulkan() || ((screen->hwcaps & RFL_SHADER_STORAGE_BUFFER) && !strstr(screen->vendorstring, "Intel")))
if (screen->IsVulkan() || screen->IsPoly() || ((screen->hwcaps & RFL_SHADER_STORAGE_BUFFER) && !strstr(screen->vendorstring, "Intel")))
{
mBufferType = true;
mBlockAlign = 0;

View file

@ -1,6 +1,7 @@
#ifndef __I_VIDEO_H__
#define __I_VIDEO_H__
#include <cstdint>
class DFrameBuffer;
@ -22,6 +23,11 @@ void I_ShutdownGraphics();
extern IVideo *Video;
void I_PolyPresentInit();
uint8_t *I_PolyPresentLock(int w, int h, bool vsync, int &pitch);
void I_PolyPresentUnlock(int x, int y, int w, int h);
void I_PolyPresentDeinit();
// Pause a bit.
// [RH] Despite the name, it apparently never waited for the VBL, even in

View file

@ -0,0 +1,143 @@
#include "poly_buffers.h"
#include "poly_framebuffer.h"
#include "poly_renderstate.h"
#include "doomerrors.h"
PolyBuffer *PolyBuffer::First = nullptr;
PolyBuffer::PolyBuffer()
{
Next = First;
First = this;
if (Next) Next->Prev = this;
}
PolyBuffer::~PolyBuffer()
{
if (Next) Next->Prev = Prev;
if (Prev) Prev->Next = Next;
else First = Next;
auto fb = GetPolyFrameBuffer();
if (fb && !mData.empty())
fb->FrameDeleteList.Buffers.push_back(std::move(mData));
}
void PolyBuffer::ResetAll()
{
for (PolyBuffer *cur = PolyBuffer::First; cur; cur = cur->Next)
cur->Reset();
}
void PolyBuffer::Reset()
{
}
void PolyBuffer::SetData(size_t size, const void *data, bool staticdata)
{
mData.resize(size);
map = mData.data();
if (data)
memcpy(map, data, size);
buffersize = size;
}
void PolyBuffer::SetSubData(size_t offset, size_t size, const void *data)
{
memcpy(static_cast<uint8_t*>(map) + offset, data, size);
}
void PolyBuffer::Resize(size_t newsize)
{
mData.resize(newsize);
buffersize = newsize;
map = mData.data();
}
void PolyBuffer::Map()
{
}
void PolyBuffer::Unmap()
{
}
void *PolyBuffer::Lock(unsigned int size)
{
if (mData.size() < (size_t)size)
Resize(size);
return map;
}
void PolyBuffer::Unlock()
{
}
/////////////////////////////////////////////////////////////////////////////
void PolyVertexBuffer::SetFormat(int numBindingPoints, int numAttributes, size_t stride, const FVertexBufferAttribute *attrs)
{
VertexFormat = GetPolyFrameBuffer()->GetRenderState()->GetVertexFormat(numBindingPoints, numAttributes, stride, attrs);
}
/////////////////////////////////////////////////////////////////////////////
void PolyVertexInputAssembly::Load(PolyTriangleThreadData *thread, const void *vertices, int index)
{
const uint8_t *vertex = static_cast<const uint8_t*>(vertices) + mStride * index;
const float *attrVertex = reinterpret_cast<const float*>(vertex + mOffsets[VATTR_VERTEX]);
const float *attrTexcoord = reinterpret_cast<const float*>(vertex + mOffsets[VATTR_TEXCOORD]);
const uint8_t *attrColor = reinterpret_cast<const uint8_t*>(vertex + mOffsets[VATTR_COLOR]);
const uint32_t* attrNormal = reinterpret_cast<const uint32_t*>(vertex + mOffsets[VATTR_NORMAL]);
const uint32_t* attrNormal2 = reinterpret_cast<const uint32_t*>(vertex + mOffsets[VATTR_NORMAL2]);
thread->mainVertexShader.aPosition = { attrVertex[0], attrVertex[1], attrVertex[2], 1.0f };
thread->mainVertexShader.aTexCoord = { attrTexcoord[0], attrTexcoord[1] };
if ((UseVertexData & 1) == 0)
{
const auto &c = thread->mainVertexShader.Data.uVertexColor;
thread->mainVertexShader.aColor = MAKEARGB(
static_cast<uint32_t>(c.W * 255.0f + 0.5f),
static_cast<uint32_t>(c.X * 255.0f + 0.5f),
static_cast<uint32_t>(c.Y * 255.0f + 0.5f),
static_cast<uint32_t>(c.Z * 255.0f + 0.5f)
);
}
else
{
uint32_t r = attrColor[0];
uint32_t g = attrColor[1];
uint32_t b = attrColor[2];
uint32_t a = attrColor[3];
thread->mainVertexShader.aColor = MAKEARGB(a, r, g, b);
}
if ((UseVertexData & 2) == 0)
{
const auto &n = thread->mainVertexShader.Data.uVertexNormal;
thread->mainVertexShader.aNormal = Vec4f(n.X, n.Y, n.Z, 1.0);
thread->mainVertexShader.aNormal2 = thread->mainVertexShader.aNormal;
}
else
{
int n = *attrNormal;
int n2 = *attrNormal2;
float x = ((n << 22) >> 22) / 512.0f;
float y = ((n << 12) >> 22) / 512.0f;
float z = ((n << 2) >> 22) / 512.0f;
float x2 = ((n2 << 22) >> 22) / 512.0f;
float y2 = ((n2 << 12) >> 22) / 512.0f;
float z2 = ((n2 << 2) >> 22) / 512.0f;
thread->mainVertexShader.aNormal = Vec4f(x, y, z, 0.0f);
thread->mainVertexShader.aNormal2 = Vec4f(x2, y2, z2, 0.0f);
}
}
/////////////////////////////////////////////////////////////////////////////
void PolyDataBuffer::BindRange(FRenderState *state, size_t start, size_t length)
{
static_cast<PolyRenderState*>(state)->Bind(this, (uint32_t)start, (uint32_t)length);
}

View file

@ -0,0 +1,79 @@
#pragma once
#include "hwrenderer/data/buffers.h"
#include "polyrenderer/drawers/poly_triangle.h"
#include "utility/tarray.h"
#include <vector>
#ifdef _MSC_VER
// silence bogus warning C4250: 'PolyVertexBuffer': inherits 'PolyBuffer::PolyBuffer::SetData' via dominance
// According to internet infos, the warning is erroneously emitted in this case.
#pragma warning(disable:4250)
#endif
class PolyBuffer : virtual public IBuffer
{
public:
PolyBuffer();
~PolyBuffer();
static void ResetAll();
void Reset();
void SetData(size_t size, const void *data, bool staticdata) override;
void SetSubData(size_t offset, size_t size, const void *data) override;
void Resize(size_t newsize) override;
void Map() override;
void Unmap() override;
void *Lock(unsigned int size) override;
void Unlock() override;
private:
static PolyBuffer *First;
PolyBuffer *Prev = nullptr;
PolyBuffer *Next = nullptr;
std::vector<uint32_t> mData;
};
class PolyVertexInputAssembly : public PolyInputAssembly
{
public:
size_t mOffsets[VATTR_MAX] = {};
size_t mStride = 0;
int NumBindingPoints;
size_t Stride;
std::vector<FVertexBufferAttribute> Attrs;
int UseVertexData;
void Load(PolyTriangleThreadData *thread, const void *vertices, int index) override;
};
class PolyVertexBuffer : public IVertexBuffer, public PolyBuffer
{
public:
PolyVertexBuffer() { }
void SetFormat(int numBindingPoints, int numAttributes, size_t stride, const FVertexBufferAttribute *attrs) override;
PolyVertexInputAssembly *VertexFormat = nullptr;
};
class PolyIndexBuffer : public IIndexBuffer, public PolyBuffer
{
public:
PolyIndexBuffer() { }
};
class PolyDataBuffer : public IDataBuffer, public PolyBuffer
{
public:
PolyDataBuffer(int bindingpoint, bool ssbo, bool needresize) : bindingpoint(bindingpoint)
{
}
void BindRange(FRenderState *state, size_t start, size_t length) override;
int bindingpoint;
};

View file

@ -0,0 +1,578 @@
#include "v_video.h"
#include "m_png.h"
#include "templates.h"
#include "r_videoscale.h"
#include "actor.h"
#include "i_time.h"
#include "g_game.h"
#include "gamedata/fonts/v_text.h"
#include "rendering/i_video.h"
#include "hwrenderer/utility/hw_clock.h"
#include "hwrenderer/utility/hw_vrmodes.h"
#include "hwrenderer/utility/hw_cvars.h"
#include "hwrenderer/models/hw_models.h"
#include "hwrenderer/scene/hw_skydome.h"
#include "hwrenderer/scene/hw_fakeflat.h"
#include "hwrenderer/scene/hw_drawinfo.h"
#include "hwrenderer/scene/hw_portal.h"
#include "hwrenderer/data/hw_viewpointbuffer.h"
#include "hwrenderer/data/flatvertices.h"
#include "hwrenderer/data/shaderuniforms.h"
#include "hwrenderer/dynlights/hw_lightbuffer.h"
#include "hwrenderer/postprocessing/hw_postprocess.h"
#include "swrenderer/r_swscene.h"
#include "poly_framebuffer.h"
#include "poly_buffers.h"
#include "poly_renderstate.h"
#include "poly_hwtexture.h"
#include "doomerrors.h"
void Draw2D(F2DDrawer *drawer, FRenderState &state);
void DoWriteSavePic(FileWriter *file, ESSType ssformat, uint8_t *scr, int width, int height, sector_t *viewsector, bool upsidedown);
EXTERN_CVAR(Bool, r_drawvoxels)
EXTERN_CVAR(Int, gl_tonemap)
EXTERN_CVAR(Int, screenblocks)
EXTERN_CVAR(Bool, cl_capfps)
EXTERN_CVAR(Bool, gl_no_skyclear)
extern bool NoInterpolateView;
extern int rendered_commandbuffers;
extern int current_rendered_commandbuffers;
extern bool gpuStatActive;
extern bool keepGpuStatActive;
extern FString gpuStatOutput;
PolyFrameBuffer::PolyFrameBuffer(void *hMonitor, bool fullscreen) : Super(hMonitor, fullscreen)
{
I_PolyPresentInit();
}
PolyFrameBuffer::~PolyFrameBuffer()
{
// screen is already null at this point, but PolyHardwareTexture::ResetAll needs it during clean up. Is there a better way we can do this?
auto tmp = screen;
screen = this;
PolyHardwareTexture::ResetAll();
PolyBuffer::ResetAll();
PPResource::ResetAll();
delete mVertexData;
delete mSkyData;
delete mViewpoints;
delete mLights;
mShadowMap.Reset();
screen = tmp;
I_PolyPresentDeinit();
}
void PolyFrameBuffer::InitializeState()
{
vendorstring = "Poly";
hwcaps = RFL_SHADER_STORAGE_BUFFER | RFL_BUFFER_STORAGE;
glslversion = 4.50f;
uniformblockalignment = 1;
maxuniformblock = 0x7fffffff;
mRenderState.reset(new PolyRenderState());
mVertexData = new FFlatVertexBuffer(GetWidth(), GetHeight());
mSkyData = new FSkyVertexBuffer;
mViewpoints = new HWViewpointBuffer;
mLights = new FLightBuffer();
PolyTriangleDrawer::SetLightBuffer(GetDrawCommands(), mLightBuffer->Memory());
CheckCanvas();
}
void PolyFrameBuffer::CheckCanvas()
{
if (!mCanvas || mCanvas->GetWidth() != GetWidth() || mCanvas->GetHeight() != GetHeight())
{
DrawerThreads::WaitForWorkers();
mCanvas.reset(new DCanvas(0, 0, true));
mCanvas->Resize(GetWidth(), GetHeight(), false);
mDepthStencil.reset();
mDepthStencil.reset(new PolyDepthStencil(GetWidth(), GetHeight()));
mRenderState->SetRenderTarget(GetCanvas(), GetDepthStencil());
}
}
const DrawerCommandQueuePtr &PolyFrameBuffer::GetDrawCommands()
{
if (!mDrawCommands)
mDrawCommands = std::make_shared<DrawerCommandQueue>(&mFrameMemory);
return mDrawCommands;
}
void PolyFrameBuffer::FlushDrawCommands()
{
if (mDrawCommands)
{
DrawerThreads::Execute(mDrawCommands);
mDrawCommands.reset();
}
}
void PolyFrameBuffer::Update()
{
twoD.Reset();
Flush3D.Reset();
Flush3D.Clock();
Draw2D();
Clear2D();
Flush3D.Unclock();
FlushDrawCommands();
if (mCanvas)
{
int w = mCanvas->GetWidth();
int h = mCanvas->GetHeight();
int pixelsize = 4;
const uint8_t *src = (const uint8_t*)mCanvas->GetPixels();
int pitch = 0;
uint8_t *dst = I_PolyPresentLock(w, h, cur_vsync, pitch);
if (dst)
{
#if 1
auto copyqueue = std::make_shared<DrawerCommandQueue>(&mFrameMemory);
copyqueue->Push<MemcpyCommand>(dst, pitch / pixelsize, src, w, h, w, pixelsize);
DrawerThreads::Execute(copyqueue);
#else
for (int y = 0; y < h; y++)
{
memcpy(dst + y * pitch, src + y * w * pixelsize, w * pixelsize);
}
#endif
DrawerThreads::WaitForWorkers();
I_PolyPresentUnlock(mOutputLetterbox.left, mOutputLetterbox.top, mOutputLetterbox.width, mOutputLetterbox.height);
}
}
DrawerThreads::WaitForWorkers();
mFrameMemory.Clear();
FrameDeleteList.Buffers.clear();
FrameDeleteList.Images.clear();
CheckCanvas();
Super::Update();
}
void PolyFrameBuffer::WriteSavePic(player_t *player, FileWriter *file, int width, int height)
{
if (!V_IsHardwareRenderer())
{
Super::WriteSavePic(player, file, width, height);
}
else
{
}
}
sector_t *PolyFrameBuffer::RenderView(player_t *player)
{
// To do: this is virtually identical to FGLRenderer::RenderView and should be merged.
mRenderState->SetVertexBuffer(mVertexData);
mVertexData->Reset();
sector_t *retsec;
if (!V_IsHardwareRenderer())
{
if (!swdrawer) swdrawer.reset(new SWSceneDrawer);
retsec = swdrawer->RenderView(player);
}
else
{
hw_ClearFakeFlat();
iter_dlightf = iter_dlight = draw_dlight = draw_dlightf = 0;
checkBenchActive();
// reset statistics counters
ResetProfilingData();
// Get this before everything else
if (cl_capfps || r_NoInterpolate) r_viewpoint.TicFrac = 1.;
else r_viewpoint.TicFrac = I_GetTimeFrac();
mLights->Clear();
mViewpoints->Clear();
// NoInterpolateView should have no bearing on camera textures, but needs to be preserved for the main view below.
bool saved_niv = NoInterpolateView;
NoInterpolateView = false;
// Shader start time does not need to be handled per level. Just use the one from the camera to render from.
GetRenderState()->CheckTimer(player->camera->Level->ShaderStartTime);
// prepare all camera textures that have been used in the last frame.
// This must be done for all levels, not just the primary one!
for (auto Level : AllLevels())
{
Level->canvasTextureInfo.UpdateAll([&](AActor *camera, FCanvasTexture *camtex, double fov)
{
RenderTextureView(camtex, camera, fov);
});
}
NoInterpolateView = saved_niv;
// now render the main view
float fovratio;
float ratio = r_viewwindow.WidescreenRatio;
if (r_viewwindow.WidescreenRatio >= 1.3f)
{
fovratio = 1.333333f;
}
else
{
fovratio = ratio;
}
retsec = RenderViewpoint(r_viewpoint, player->camera, NULL, r_viewpoint.FieldOfView.Degrees, ratio, fovratio, true, true);
}
All.Unclock();
return retsec;
}
sector_t *PolyFrameBuffer::RenderViewpoint(FRenderViewpoint &mainvp, AActor * camera, IntRect * bounds, float fov, float ratio, float fovratio, bool mainview, bool toscreen)
{
// To do: this is virtually identical to FGLRenderer::RenderViewpoint and should be merged.
R_SetupFrame(mainvp, r_viewwindow, camera);
if (mainview && toscreen)
UpdateShadowMap();
// Update the attenuation flag of all light defaults for each viewpoint.
// This function will only do something if the setting differs.
FLightDefaults::SetAttenuationForLevel(!!(camera->Level->flags3 & LEVEL3_ATTENUATE));
// Render (potentially) multiple views for stereo 3d
// Fixme. The view offsetting should be done with a static table and not require setup of the entire render state for the mode.
auto vrmode = VRMode::GetVRMode(mainview && toscreen);
for (int eye_ix = 0; eye_ix < vrmode->mEyeCount; ++eye_ix)
{
const auto &eye = vrmode->mEyes[eye_ix];
SetViewportRects(bounds);
if (mainview) // Bind the scene frame buffer and turn on draw buffers used by ssao
{
//mRenderState->SetRenderTarget(GetBuffers()->SceneColor.View.get(), GetBuffers()->SceneDepthStencil.View.get(), GetBuffers()->GetWidth(), GetBuffers()->GetHeight(), Poly_FORMAT_R16G16B16A16_SFLOAT, GetBuffers()->GetSceneSamples());
bool useSSAO = (gl_ssao != 0);
GetRenderState()->SetPassType(useSSAO ? GBUFFER_PASS : NORMAL_PASS);
GetRenderState()->EnableDrawBuffers(GetRenderState()->GetPassDrawBufferCount());
}
auto di = HWDrawInfo::StartDrawInfo(mainvp.ViewLevel, nullptr, mainvp, nullptr);
auto &vp = di->Viewpoint;
di->Set3DViewport(*GetRenderState());
di->SetViewArea();
auto cm = di->SetFullbrightFlags(mainview ? vp.camera->player : nullptr);
di->Viewpoint.FieldOfView = fov; // Set the real FOV for the current scene (it's not necessarily the same as the global setting in r_viewpoint)
// Stereo mode specific perspective projection
di->VPUniforms.mProjectionMatrix = eye.GetProjection(fov, ratio, fovratio);
// Stereo mode specific viewpoint adjustment
vp.Pos += eye.GetViewShift(vp.HWAngles.Yaw.Degrees);
di->SetupView(*GetRenderState(), vp.Pos.X, vp.Pos.Y, vp.Pos.Z, false, false);
// std::function until this can be done better in a cross-API fashion.
di->ProcessScene(toscreen, [&](HWDrawInfo *di, int mode) {
DrawScene(di, mode);
});
if (mainview)
{
PostProcess.Clock();
if (toscreen) di->EndDrawScene(mainvp.sector, *GetRenderState()); // do not call this for camera textures.
if (GetRenderState()->GetPassType() == GBUFFER_PASS) // Turn off ssao draw buffers
{
GetRenderState()->SetPassType(NORMAL_PASS);
GetRenderState()->EnableDrawBuffers(1);
}
//mPostprocess->BlitSceneToPostprocess(); // Copy the resulting scene to the current post process texture
PostProcessScene(cm, [&]() { di->DrawEndScene2D(mainvp.sector, *GetRenderState()); });
PostProcess.Unclock();
}
di->EndDrawInfo();
#if 0
if (vrmode->mEyeCount > 1)
mBuffers->BlitToEyeTexture(eye_ix);
#endif
}
return mainvp.sector;
}
void PolyFrameBuffer::RenderTextureView(FCanvasTexture *tex, AActor *Viewpoint, double FOV)
{
// This doesn't need to clear the fake flat cache. It can be shared between camera textures and the main view of a scene.
FMaterial *mat = FMaterial::ValidateTexture(tex, false);
auto BaseLayer = static_cast<PolyHardwareTexture*>(mat->GetLayer(0, 0));
int width = mat->TextureWidth();
int height = mat->TextureHeight();
DCanvas *image = BaseLayer->GetImage(tex, 0, 0);
PolyDepthStencil *depthStencil = BaseLayer->GetDepthStencil(tex);
mRenderState->SetRenderTarget(image, depthStencil);
IntRect bounds;
bounds.left = bounds.top = 0;
bounds.width = MIN(mat->GetWidth(), image->GetWidth());
bounds.height = MIN(mat->GetHeight(), image->GetHeight());
FRenderViewpoint texvp;
RenderViewpoint(texvp, Viewpoint, &bounds, FOV, (float)width / height, (float)width / height, false, false);
DrawerThreads::WaitForWorkers();
mRenderState->SetRenderTarget(GetCanvas(), GetDepthStencil());
tex->SetUpdated(true);
}
void PolyFrameBuffer::DrawScene(HWDrawInfo *di, int drawmode)
{
// To do: this is virtually identical to FGLRenderer::DrawScene and should be merged.
static int recursion = 0;
static int ssao_portals_available = 0;
const auto &vp = di->Viewpoint;
bool applySSAO = false;
if (drawmode == DM_MAINVIEW)
{
ssao_portals_available = gl_ssao_portals;
applySSAO = true;
}
else if (drawmode == DM_OFFSCREEN)
{
ssao_portals_available = 0;
}
else if (drawmode == DM_PORTAL && ssao_portals_available > 0)
{
applySSAO = true;
ssao_portals_available--;
}
if (vp.camera != nullptr)
{
ActorRenderFlags savedflags = vp.camera->renderflags;
di->CreateScene(drawmode == DM_MAINVIEW);
vp.camera->renderflags = savedflags;
}
else
{
di->CreateScene(false);
}
GetRenderState()->SetDepthMask(true);
if (!gl_no_skyclear) mPortalState->RenderFirstSkyPortal(recursion, di, *GetRenderState());
di->RenderScene(*GetRenderState());
if (applySSAO && GetRenderState()->GetPassType() == GBUFFER_PASS)
{
//mPostprocess->AmbientOccludeScene(di->VPUniforms.mProjectionMatrix.get()[5]);
//mViewpoints->Bind(*GetRenderState(), di->vpIndex);
}
// Handle all portals after rendering the opaque objects but before
// doing all translucent stuff
recursion++;
mPortalState->EndFrame(di, *GetRenderState());
recursion--;
di->RenderTranslucent(*GetRenderState());
}
void PolyFrameBuffer::PostProcessScene(int fixedcm, const std::function<void()> &afterBloomDrawEndScene2D)
{
afterBloomDrawEndScene2D();
}
uint32_t PolyFrameBuffer::GetCaps()
{
if (!V_IsHardwareRenderer())
return Super::GetCaps();
// describe our basic feature set
ActorRenderFeatureFlags FlagSet = RFF_FLATSPRITES | RFF_MODELS | RFF_SLOPE3DFLOORS |
RFF_TILTPITCH | RFF_ROLLSPRITES | RFF_POLYGONAL | RFF_MATSHADER | RFF_POSTSHADER | RFF_BRIGHTMAP;
if (r_drawvoxels)
FlagSet |= RFF_VOXELS;
if (gl_tonemap != 5) // not running palette tonemap shader
FlagSet |= RFF_TRUECOLOR;
return (uint32_t)FlagSet;
}
void PolyFrameBuffer::SetVSync(bool vsync)
{
cur_vsync = vsync;
}
void PolyFrameBuffer::CleanForRestart()
{
// force recreation of the SW scene drawer to ensure it gets a new set of resources.
swdrawer.reset();
}
void PolyFrameBuffer::PrecacheMaterial(FMaterial *mat, int translation)
{
auto tex = mat->tex;
if (tex->isSWCanvas()) return;
// Textures that are already scaled in the texture lump will not get replaced by hires textures.
int flags = mat->isExpanded() ? CTF_Expand : (gl_texture_usehires && !tex->isScaled()) ? CTF_CheckHires : 0;
auto base = static_cast<PolyHardwareTexture*>(mat->GetLayer(0, translation));
base->Precache(mat, translation, flags);
}
IHardwareTexture *PolyFrameBuffer::CreateHardwareTexture()
{
return new PolyHardwareTexture();
}
FModelRenderer *PolyFrameBuffer::CreateModelRenderer(int mli)
{
return new FHWModelRenderer(nullptr, *GetRenderState(), mli);
}
IVertexBuffer *PolyFrameBuffer::CreateVertexBuffer()
{
return new PolyVertexBuffer();
}
IIndexBuffer *PolyFrameBuffer::CreateIndexBuffer()
{
return new PolyIndexBuffer();
}
IDataBuffer *PolyFrameBuffer::CreateDataBuffer(int bindingpoint, bool ssbo, bool needsresize)
{
IDataBuffer *buffer = new PolyDataBuffer(bindingpoint, ssbo, needsresize);
if (bindingpoint == LIGHTBUF_BINDINGPOINT)
mLightBuffer = buffer;
return buffer;
}
void PolyFrameBuffer::SetTextureFilterMode()
{
TextureFilterChanged();
}
void PolyFrameBuffer::TextureFilterChanged()
{
}
void PolyFrameBuffer::StartPrecaching()
{
}
void PolyFrameBuffer::BlurScene(float amount)
{
}
void PolyFrameBuffer::UpdatePalette()
{
}
FTexture *PolyFrameBuffer::WipeStartScreen()
{
SetViewportRects(nullptr);
auto tex = new FWrapperTexture(mScreenViewport.width, mScreenViewport.height, 1);
auto systex = static_cast<PolyHardwareTexture*>(tex->GetSystemTexture());
systex->CreateWipeTexture(mScreenViewport.width, mScreenViewport.height, "WipeStartScreen");
return tex;
}
FTexture *PolyFrameBuffer::WipeEndScreen()
{
Draw2D();
Clear2D();
auto tex = new FWrapperTexture(mScreenViewport.width, mScreenViewport.height, 1);
auto systex = static_cast<PolyHardwareTexture*>(tex->GetSystemTexture());
systex->CreateWipeTexture(mScreenViewport.width, mScreenViewport.height, "WipeEndScreen");
return tex;
}
TArray<uint8_t> PolyFrameBuffer::GetScreenshotBuffer(int &pitch, ESSType &color_type, float &gamma)
{
int w = SCREENWIDTH;
int h = SCREENHEIGHT;
IntRect box;
box.left = 0;
box.top = 0;
box.width = w;
box.height = h;
//mPostprocess->DrawPresentTexture(box, true, true);
TArray<uint8_t> ScreenshotBuffer(w * h * 3, true);
//CopyScreenToBuffer(w, h, ScreenshotBuffer.Data());
pitch = w * 3;
color_type = SS_RGB;
gamma = 1.0f;
return ScreenshotBuffer;
}
void PolyFrameBuffer::BeginFrame()
{
SetViewportRects(nullptr);
CheckCanvas();
swrenderer::R_InitFuzzTable(GetCanvas()->GetPitch());
static int next_random = 0;
swrenderer::fuzzpos = (swrenderer::fuzzpos + swrenderer::fuzz_random_x_offset[next_random] * FUZZTABLE / 100) % FUZZTABLE;
next_random++;
if (next_random == FUZZ_RANDOM_X_SIZE)
next_random = 0;
}
void PolyFrameBuffer::Draw2D()
{
::Draw2D(&m2DDrawer, *mRenderState);
}
unsigned int PolyFrameBuffer::GetLightBufferBlockSize() const
{
return mLights->GetBlockSize();
}
void PolyFrameBuffer::UpdateShadowMap()
{
}

View file

@ -0,0 +1,90 @@
#pragma once
#include "gl_sysfb.h"
#include "rendering/swrenderer/r_memory.h"
#include "rendering/swrenderer/drawers/r_thread.h"
#include "rendering/polyrenderer/drawers/poly_triangle.h"
struct FRenderViewpoint;
class PolyDataBuffer;
class PolyRenderState;
class SWSceneDrawer;
class PolyFrameBuffer : public SystemBaseFrameBuffer
{
typedef SystemBaseFrameBuffer Super;
public:
RenderMemory *GetFrameMemory() { return &mFrameMemory; }
PolyRenderState *GetRenderState() { return mRenderState.get(); }
DCanvas *GetCanvas() override { return mCanvas.get(); }
PolyDepthStencil *GetDepthStencil() { return mDepthStencil.get(); }
const DrawerCommandQueuePtr &GetDrawCommands();
void FlushDrawCommands();
unsigned int GetLightBufferBlockSize() const;
std::unique_ptr<SWSceneDrawer> swdrawer;
PolyFrameBuffer(void *hMonitor, bool fullscreen);
~PolyFrameBuffer();
void Update();
bool IsPoly() override { return true; }
void InitializeState() override;
void CleanForRestart() override;
void PrecacheMaterial(FMaterial *mat, int translation) override;
void UpdatePalette() override;
uint32_t GetCaps() override;
void WriteSavePic(player_t *player, FileWriter *file, int width, int height) override;
sector_t *RenderView(player_t *player) override;
void SetTextureFilterMode() override;
void TextureFilterChanged() override;
void StartPrecaching() override;
void BeginFrame() override;
void BlurScene(float amount) override;
void PostProcessScene(int fixedcm, const std::function<void()> &afterBloomDrawEndScene2D) override;
IHardwareTexture *CreateHardwareTexture() override;
FModelRenderer *CreateModelRenderer(int mli) override;
IVertexBuffer *CreateVertexBuffer() override;
IIndexBuffer *CreateIndexBuffer() override;
IDataBuffer *CreateDataBuffer(int bindingpoint, bool ssbo, bool needsresize) override;
FTexture *WipeStartScreen() override;
FTexture *WipeEndScreen() override;
TArray<uint8_t> GetScreenshotBuffer(int &pitch, ESSType &color_type, float &gamma) override;
void SetVSync(bool vsync) override;
void Draw2D() override;
struct DeleteList
{
std::vector<std::vector<uint32_t>> Buffers;
std::vector<std::unique_ptr<DCanvas>> Images;
} FrameDeleteList;
private:
sector_t *RenderViewpoint(FRenderViewpoint &mainvp, AActor * camera, IntRect * bounds, float fov, float ratio, float fovratio, bool mainview, bool toscreen);
void RenderTextureView(FCanvasTexture *tex, AActor *Viewpoint, double FOV);
void DrawScene(HWDrawInfo *di, int drawmode);
void UpdateShadowMap();
void CheckCanvas();
IDataBuffer *mLightBuffer = nullptr;
std::unique_ptr<PolyRenderState> mRenderState;
std::unique_ptr<DCanvas> mCanvas;
std::unique_ptr<PolyDepthStencil> mDepthStencil;
std::shared_ptr<DrawerCommandQueue> mDrawCommands;
RenderMemory mFrameMemory;
bool cur_vsync = false;
};
inline PolyFrameBuffer *GetPolyFrameBuffer() { return static_cast<PolyFrameBuffer*>(screen); }

View file

@ -0,0 +1,169 @@
#include "templates.h"
#include "c_cvars.h"
#include "r_data/colormaps.h"
#include "hwrenderer/textures/hw_material.h"
#include "hwrenderer/utility/hw_cvars.h"
#include "hwrenderer/scene/hw_renderstate.h"
#include "poly_framebuffer.h"
#include "poly_hwtexture.h"
PolyHardwareTexture *PolyHardwareTexture::First = nullptr;
PolyHardwareTexture::PolyHardwareTexture()
{
Next = First;
First = this;
if (Next) Next->Prev = this;
}
PolyHardwareTexture::~PolyHardwareTexture()
{
if (Next) Next->Prev = Prev;
if (Prev) Prev->Next = Next;
else First = Next;
Reset();
}
void PolyHardwareTexture::ResetAll()
{
for (PolyHardwareTexture *cur = PolyHardwareTexture::First; cur; cur = cur->Next)
cur->Reset();
}
void PolyHardwareTexture::Reset()
{
if (auto fb = GetPolyFrameBuffer())
{
auto &deleteList = fb->FrameDeleteList;
if (mCanvas) deleteList.Images.push_back(std::move(mCanvas));
}
}
void PolyHardwareTexture::Precache(FMaterial *mat, int translation, int flags)
{
int numLayers = mat->GetLayers();
GetImage(mat->tex, translation, flags);
for (int i = 1; i < numLayers; i++)
{
FTexture *layer;
auto systex = static_cast<PolyHardwareTexture*>(mat->GetLayer(i, 0, &layer));
systex->GetImage(layer, 0, mat->isExpanded() ? CTF_Expand : 0);
}
}
DCanvas *PolyHardwareTexture::GetImage(const FMaterialState &state)
{
FTexture *tex = state.mMaterial->tex;
if (tex->isHardwareCanvas()) static_cast<FCanvasTexture*>(tex)->NeedUpdate();
if (!mCanvas)
{
FMaterial *mat = state.mMaterial;
int clampmode = state.mClampMode;
int translation = state.mTranslation;
if (tex->UseType == ETextureType::SWCanvas) clampmode = CLAMP_NOFILTER;
if (tex->isHardwareCanvas()) clampmode = CLAMP_CAMTEX;
else if ((tex->isWarped() || tex->shaderindex >= FIRST_USER_SHADER) && clampmode <= CLAMP_XY) clampmode = CLAMP_NONE;
// Textures that are already scaled in the texture lump will not get replaced by hires textures.
int flags = state.mMaterial->isExpanded() ? CTF_Expand : (gl_texture_usehires && !tex->isScaled() && clampmode <= CLAMP_XY) ? CTF_CheckHires : 0;
return GetImage(tex, translation, flags);
}
return mCanvas.get();
}
DCanvas *PolyHardwareTexture::GetImage(FTexture *tex, int translation, int flags)
{
if (!mCanvas)
CreateImage(tex, translation, flags);
return mCanvas.get();
}
PolyDepthStencil *PolyHardwareTexture::GetDepthStencil(FTexture *tex)
{
if (!mDepthStencil)
{
int w = tex->GetWidth();
int h = tex->GetHeight();
mDepthStencil.reset(new PolyDepthStencil(w, h));
}
return mDepthStencil.get();
}
void PolyHardwareTexture::AllocateBuffer(int w, int h, int texelsize)
{
if (!mCanvas || mCanvas->GetWidth() != w || mCanvas->GetHeight() != h)
{
mCanvas.reset(new DCanvas(0, 0, texelsize == 4));
mCanvas->Resize(w, h, false);
bufferpitch = mCanvas->GetPitch();
}
}
uint8_t *PolyHardwareTexture::MapBuffer()
{
return mCanvas->GetPixels();
}
unsigned int PolyHardwareTexture::CreateTexture(unsigned char * buffer, int w, int h, int texunit, bool mipmap, int translation, const char *name)
{
return 0;
}
void PolyHardwareTexture::CreateWipeTexture(int w, int h, const char *name)
{
if (!mCanvas || mCanvas->GetWidth() != w || mCanvas->GetHeight() != h)
{
mCanvas.reset(new DCanvas(0, 0, true));
mCanvas->Resize(w, h, false);
}
auto fb = static_cast<PolyFrameBuffer*>(screen);
fb->FlushDrawCommands();
DrawerThreads::WaitForWorkers();
uint32_t* dest = (uint32_t*)mCanvas->GetPixels();
uint32_t* src = (uint32_t*)fb->GetCanvas()->GetPixels();
int dpitch = mCanvas->GetPitch();
int spitch = fb->GetCanvas()->GetPitch();
int pixelsize = 4;
for (int y = 0; y < h; y++)
{
memcpy(dest + dpitch * (h - 1 - y), src + spitch * y, w * pixelsize);
}
}
void PolyHardwareTexture::CreateImage(FTexture *tex, int translation, int flags)
{
mCanvas.reset(new DCanvas(0, 0, true));
if (!tex->isHardwareCanvas())
{
if (translation <= 0)
{
translation = -translation;
}
else
{
auto remap = TranslationToTable(translation);
translation = remap == nullptr ? 0 : remap->GetUniqueIndex();
}
FTextureBuffer texbuffer = tex->CreateTexBuffer(translation, flags | CTF_ProcessData);
mCanvas->Resize(texbuffer.mWidth, texbuffer.mHeight, false);
memcpy(mCanvas->GetPixels(), texbuffer.mBuffer, texbuffer.mWidth * texbuffer.mHeight * 4);
}
else
{
int w = tex->GetWidth();
int h = tex->GetHeight();
mCanvas->Resize(w, h, false);
}
}

View file

@ -0,0 +1,48 @@
#pragma once
#ifdef LoadImage
#undef LoadImage
#endif
#define SHADED_TEXTURE -1
#define DIRECT_PALETTE -2
#include "tarray.h"
#include "hwrenderer/textures/hw_ihwtexture.h"
#include "volk/volk.h"
struct FMaterialState;
class PolyBuffer;
class PolyHardwareTexture : public IHardwareTexture
{
public:
PolyHardwareTexture();
~PolyHardwareTexture();
static void ResetAll();
void Reset();
void Precache(FMaterial *mat, int translation, int flags);
DCanvas *GetImage(const FMaterialState &state);
DCanvas *GetImage(FTexture *tex, int translation, int flags);
PolyDepthStencil *GetDepthStencil(FTexture *tex);
// Software renderer stuff
void AllocateBuffer(int w, int h, int texelsize) override;
uint8_t *MapBuffer() override;
unsigned int CreateTexture(unsigned char * buffer, int w, int h, int texunit, bool mipmap, int translation, const char *name) override;
// Wipe screen
void CreateWipeTexture(int w, int h, const char *name);
private:
void CreateImage(FTexture *tex, int translation, int flags);
static PolyHardwareTexture *First;
PolyHardwareTexture *Prev = nullptr;
PolyHardwareTexture *Next = nullptr;
std::unique_ptr<DCanvas> mCanvas;
std::unique_ptr<PolyDepthStencil> mDepthStencil;
};

View file

@ -0,0 +1,360 @@
#include "polyrenderer/backend/poly_renderstate.h"
#include "polyrenderer/backend/poly_framebuffer.h"
#include "polyrenderer/backend/poly_hwtexture.h"
#include "templates.h"
#include "doomstat.h"
#include "r_data/colormaps.h"
#include "hwrenderer/scene/hw_skydome.h"
#include "hwrenderer/scene/hw_viewpointuniforms.h"
#include "hwrenderer/dynlights/hw_lightbuffer.h"
#include "hwrenderer/utility/hw_cvars.h"
#include "hwrenderer/utility/hw_clock.h"
#include "hwrenderer/data/flatvertices.h"
#include "hwrenderer/data/hw_viewpointbuffer.h"
#include "hwrenderer/data/shaderuniforms.h"
#include "swrenderer/r_swcolormaps.h"
static PolyDrawMode dtToDrawMode[] =
{
PolyDrawMode::Points,
PolyDrawMode::Lines,
PolyDrawMode::Triangles,
PolyDrawMode::TriangleFan,
PolyDrawMode::TriangleStrip,
};
PolyRenderState::PolyRenderState()
{
mIdentityMatrix.loadIdentity();
Reset();
}
void PolyRenderState::ClearScreen()
{
screen->mViewpoints->Set2D(*this, SCREENWIDTH, SCREENHEIGHT);
SetColor(0, 0, 0);
Draw(DT_TriangleStrip, FFlatVertexBuffer::FULLSCREEN_INDEX, 4, true);
}
void PolyRenderState::Draw(int dt, int index, int count, bool apply)
{
if (apply)
Apply();
PolyTriangleDrawer::Draw(GetPolyFrameBuffer()->GetDrawCommands(), index, count, dtToDrawMode[dt]);
}
void PolyRenderState::DrawIndexed(int dt, int index, int count, bool apply)
{
if (apply)
Apply();
PolyTriangleDrawer::DrawIndexed(GetPolyFrameBuffer()->GetDrawCommands(), index, count, dtToDrawMode[dt]);
}
bool PolyRenderState::SetDepthClamp(bool on)
{
bool lastValue = mDepthClamp;
mDepthClamp = on;
PolyTriangleDrawer::SetDepthClamp(GetPolyFrameBuffer()->GetDrawCommands(), on);
return lastValue;
}
void PolyRenderState::SetDepthMask(bool on)
{
PolyTriangleDrawer::SetDepthMask(GetPolyFrameBuffer()->GetDrawCommands(), on);
}
void PolyRenderState::SetDepthFunc(int func)
{
PolyTriangleDrawer::SetDepthFunc(GetPolyFrameBuffer()->GetDrawCommands(), func);
}
void PolyRenderState::SetDepthRange(float min, float max)
{
PolyTriangleDrawer::SetDepthRange(GetPolyFrameBuffer()->GetDrawCommands(), min, max);
}
void PolyRenderState::SetColorMask(bool r, bool g, bool b, bool a)
{
PolyTriangleDrawer::SetColorMask(GetPolyFrameBuffer()->GetDrawCommands(), r, g, b, a);
}
void PolyRenderState::SetStencil(int offs, int op, int flags)
{
PolyTriangleDrawer::SetStencil(GetPolyFrameBuffer()->GetDrawCommands(), screen->stencilValue + offs, op);
if (flags != -1)
{
bool cmon = !(flags & SF_ColorMaskOff);
SetColorMask(cmon, cmon, cmon, cmon); // don't write to the graphics buffer
SetDepthMask(!(flags & SF_DepthMaskOff));
}
}
void PolyRenderState::SetCulling(int mode)
{
PolyTriangleDrawer::SetCulling(GetPolyFrameBuffer()->GetDrawCommands(), mode);
}
void PolyRenderState::EnableClipDistance(int num, bool state)
{
PolyTriangleDrawer::EnableClipDistance(GetPolyFrameBuffer()->GetDrawCommands(), num, state);
}
void PolyRenderState::Clear(int targets)
{
//if (targets & CT_Color)
// PolyTriangleDrawer::ClearColor(GetPolyFrameBuffer()->GetDrawCommands());
if (targets & CT_Depth)
PolyTriangleDrawer::ClearDepth(GetPolyFrameBuffer()->GetDrawCommands(), 65535.0f);
if (targets & CT_Stencil)
PolyTriangleDrawer::ClearStencil(GetPolyFrameBuffer()->GetDrawCommands(), 0);
}
void PolyRenderState::EnableStencil(bool on)
{
PolyTriangleDrawer::EnableStencil(GetPolyFrameBuffer()->GetDrawCommands(), on);
}
void PolyRenderState::SetScissor(int x, int y, int w, int h)
{
auto fb = GetPolyFrameBuffer();
if (w < 0)
{
x = 0;
y = 0;
w = mRenderTarget.Canvas->GetWidth();
h = mRenderTarget.Canvas->GetHeight();
}
PolyTriangleDrawer::SetScissor(fb->GetDrawCommands(), x, mRenderTarget.Canvas->GetHeight() - y - h, w, h);
}
void PolyRenderState::SetViewport(int x, int y, int w, int h)
{
auto fb = GetPolyFrameBuffer();
if (w < 0)
{
x = 0;
y = 0;
w = mRenderTarget.Canvas->GetWidth();
h = mRenderTarget.Canvas->GetHeight();
}
PolyTriangleDrawer::SetViewport(fb->GetDrawCommands(), x, mRenderTarget.Canvas->GetHeight() - y - h, w, h, mRenderTarget.Canvas, mRenderTarget.DepthStencil);
}
void PolyRenderState::EnableDepthTest(bool on)
{
PolyTriangleDrawer::EnableDepthTest(GetPolyFrameBuffer()->GetDrawCommands(), on);
}
void PolyRenderState::EnableMultisampling(bool on)
{
}
void PolyRenderState::EnableLineSmooth(bool on)
{
}
void PolyRenderState::EnableDrawBuffers(int count)
{
}
void PolyRenderState::Apply()
{
drawcalls.Clock();
auto fb = GetPolyFrameBuffer();
int fogset = 0;
if (mFogEnabled)
{
if (mFogEnabled == 2)
{
fogset = -3; // 2D rendering with 'foggy' overlay.
}
else if ((GetFogColor() & 0xffffff) == 0)
{
fogset = gl_fogmode;
}
else
{
fogset = -gl_fogmode;
}
}
ApplyMaterial();
if (mVertexBuffer) PolyTriangleDrawer::SetVertexBuffer(fb->GetDrawCommands(), mVertexBuffer->Memory());
if (mIndexBuffer) PolyTriangleDrawer::SetIndexBuffer(fb->GetDrawCommands(), mIndexBuffer->Memory());
PolyTriangleDrawer::SetInputAssembly(fb->GetDrawCommands(), static_cast<PolyVertexBuffer*>(mVertexBuffer)->VertexFormat);
PolyTriangleDrawer::SetRenderStyle(fb->GetDrawCommands(), mRenderStyle);
if (mSpecialEffect > EFF_NONE)
{
PolyTriangleDrawer::SetShader(fb->GetDrawCommands(), mSpecialEffect, 0, false);
}
else
{
int effectState = mMaterial.mOverrideShader >= 0 ? mMaterial.mOverrideShader : (mMaterial.mMaterial ? mMaterial.mMaterial->GetShaderIndex() : 0);
PolyTriangleDrawer::SetShader(fb->GetDrawCommands(), EFF_NONE, mTextureEnabled ? effectState : SHADER_NoTexture, mAlphaThreshold >= 0.f);
}
PolyPushConstants constants;
constants.uFogEnabled = fogset;
constants.uTextureMode = mTextureMode == TM_NORMAL && mTempTM == TM_OPAQUE ? TM_OPAQUE : mTextureMode;
constants.uLightDist = mLightParms[0];
constants.uLightFactor = mLightParms[1];
constants.uFogDensity = mLightParms[2];
constants.uLightLevel = mLightParms[3];
constants.uAlphaThreshold = mAlphaThreshold;
constants.uClipSplit = { mClipSplit[0], mClipSplit[1] };
constants.uLightIndex = mLightIndex;
PolyTriangleDrawer::PushStreamData(fb->GetDrawCommands(), mStreamData, constants);
ApplyMatrices();
if (mBias.mChanged)
{
PolyTriangleDrawer::SetDepthBias(fb->GetDrawCommands(), mBias.mUnits, mBias.mFactor);
mBias.mChanged = false;
}
drawcalls.Unclock();
}
void PolyRenderState::ApplyMaterial()
{
if (mMaterial.mChanged && mMaterial.mMaterial)
{
mTempTM = mMaterial.mMaterial->tex->isHardwareCanvas() ? TM_OPAQUE : TM_NORMAL;
auto base = static_cast<PolyHardwareTexture*>(mMaterial.mMaterial->GetLayer(0, mMaterial.mTranslation));
if (base)
{
DCanvas *texcanvas = base->GetImage(mMaterial);
PolyTriangleDrawer::SetTexture(GetPolyFrameBuffer()->GetDrawCommands(), 0, texcanvas->GetPixels(), texcanvas->GetHeight(), texcanvas->GetWidth(), texcanvas->IsBgra());
int numLayers = mMaterial.mMaterial->GetLayers();
for (int i = 1; i < numLayers; i++)
{
FTexture* layer;
auto systex = static_cast<PolyHardwareTexture*>(mMaterial.mMaterial->GetLayer(i, 0, &layer));
texcanvas = systex->GetImage(layer, 0, mMaterial.mMaterial->isExpanded() ? CTF_Expand : 0);
PolyTriangleDrawer::SetTexture(GetPolyFrameBuffer()->GetDrawCommands(), i, texcanvas->GetPixels(), texcanvas->GetHeight(), texcanvas->GetWidth(), texcanvas->IsBgra());
}
}
mMaterial.mChanged = false;
}
}
template<typename T>
static void BufferedSet(bool &modified, T &dst, const T &src)
{
if (dst == src)
return;
dst = src;
modified = true;
}
static void BufferedSet(bool &modified, VSMatrix &dst, const VSMatrix &src)
{
if (memcmp(dst.get(), src.get(), sizeof(FLOATTYPE) * 16) == 0)
return;
dst = src;
modified = true;
}
void PolyRenderState::ApplyMatrices()
{
bool modified = mFirstMatrixApply;
if (mTextureMatrixEnabled)
{
BufferedSet(modified, mMatrices.TextureMatrix, mTextureMatrix);
}
else
{
BufferedSet(modified, mMatrices.TextureMatrix, mIdentityMatrix);
}
if (mModelMatrixEnabled)
{
BufferedSet(modified, mMatrices.ModelMatrix, mModelMatrix);
if (modified)
mMatrices.NormalModelMatrix.computeNormalMatrix(mModelMatrix);
}
else
{
BufferedSet(modified, mMatrices.ModelMatrix, mIdentityMatrix);
BufferedSet(modified, mMatrices.NormalModelMatrix, mIdentityMatrix);
}
if (modified)
{
mFirstMatrixApply = false;
PolyTriangleDrawer::PushMatrices(GetPolyFrameBuffer()->GetDrawCommands(), mMatrices.ModelMatrix, mMatrices.NormalModelMatrix, mMatrices.TextureMatrix);
}
}
void PolyRenderState::SetRenderTarget(DCanvas *canvas, PolyDepthStencil *depthStencil)
{
mRenderTarget.Canvas = canvas;
mRenderTarget.DepthStencil = depthStencil;
PolyTriangleDrawer::SetViewport(GetPolyFrameBuffer()->GetDrawCommands(), 0, 0, canvas->GetWidth(), canvas->GetHeight(), canvas, depthStencil);
}
void PolyRenderState::Bind(PolyDataBuffer *buffer, uint32_t offset, uint32_t length)
{
if (buffer->bindingpoint == VIEWPOINT_BINDINGPOINT)
{
mViewpointUniforms = reinterpret_cast<HWViewpointUniforms*>(static_cast<uint8_t*>(buffer->Memory()) + offset);
PolyTriangleDrawer::SetViewpointUniforms(GetPolyFrameBuffer()->GetDrawCommands(), mViewpointUniforms);
}
}
PolyVertexInputAssembly *PolyRenderState::GetVertexFormat(int numBindingPoints, int numAttributes, size_t stride, const FVertexBufferAttribute *attrs)
{
for (size_t i = 0; i < mVertexFormats.size(); i++)
{
auto f = mVertexFormats[i].get();
if (f->Attrs.size() == (size_t)numAttributes && f->NumBindingPoints == numBindingPoints && f->Stride == stride)
{
bool matches = true;
for (int j = 0; j < numAttributes; j++)
{
if (memcmp(&f->Attrs[j], &attrs[j], sizeof(FVertexBufferAttribute)) != 0)
{
matches = false;
break;
}
}
if (matches)
return f;
}
}
auto fmt = std::make_unique<PolyVertexInputAssembly>();
fmt->NumBindingPoints = numBindingPoints;
fmt->Stride = stride;
fmt->UseVertexData = 0;
for (int j = 0; j < numAttributes; j++)
{
if (attrs[j].location == VATTR_COLOR)
fmt->UseVertexData |= 1;
else if (attrs[j].location == VATTR_NORMAL)
fmt->UseVertexData |= 2;
fmt->Attrs.push_back(attrs[j]);
}
for (int j = 0; j < numAttributes; j++)
{
fmt->mOffsets[attrs[j].location] = attrs[j].offset;
}
fmt->mStride = stride;
mVertexFormats.push_back(std::move(fmt));
return mVertexFormats.back().get();
}

View file

@ -0,0 +1,72 @@
#pragma once
#include "polyrenderer/backend/poly_buffers.h"
#include "rendering/polyrenderer/drawers/poly_triangle.h"
#include "name.h"
#include "hwrenderer/scene/hw_drawstructs.h"
#include "hwrenderer/scene/hw_renderstate.h"
#include "hwrenderer/textures/hw_material.h"
struct HWViewpointUniforms;
class PolyRenderState : public FRenderState
{
public:
PolyRenderState();
// Draw commands
void ClearScreen() override;
void Draw(int dt, int index, int count, bool apply = true) override;
void DrawIndexed(int dt, int index, int count, bool apply = true) override;
// Immediate render state change commands. These only change infrequently and should not clutter the render state.
bool SetDepthClamp(bool on) override;
void SetDepthMask(bool on) override;
void SetDepthFunc(int func) override;
void SetDepthRange(float min, float max) override;
void SetColorMask(bool r, bool g, bool b, bool a) override;
void SetStencil(int offs, int op, int flags = -1) override;
void SetCulling(int mode) override;
void EnableClipDistance(int num, bool state) override;
void Clear(int targets) override;
void EnableStencil(bool on) override;
void SetScissor(int x, int y, int w, int h) override;
void SetViewport(int x, int y, int w, int h) override;
void EnableDepthTest(bool on) override;
void EnableMultisampling(bool on) override;
void EnableLineSmooth(bool on) override;
void EnableDrawBuffers(int count) override;
void SetRenderTarget(DCanvas *canvas, PolyDepthStencil *depthStencil);
void Bind(PolyDataBuffer *buffer, uint32_t offset, uint32_t length);
PolyVertexInputAssembly *GetVertexFormat(int numBindingPoints, int numAttributes, size_t stride, const FVertexBufferAttribute *attrs);
private:
void Apply();
void ApplyMaterial();
void ApplyMatrices();
struct Matrices
{
VSMatrix ModelMatrix;
VSMatrix NormalModelMatrix;
VSMatrix TextureMatrix;
} mMatrices;
VSMatrix mIdentityMatrix;
bool mFirstMatrixApply = true;
HWViewpointUniforms *mViewpointUniforms = nullptr;
std::vector<std::unique_ptr<PolyVertexInputAssembly>> mVertexFormats;
bool mDepthClamp = true;
int mTempTM = TM_NORMAL;
struct RenderTarget
{
DCanvas *Canvas = nullptr;
PolyDepthStencil *DepthStencil = nullptr;
} mRenderTarget;
};

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.
**
*/
#include <stddef.h>
#include "templates.h"
#include "doomdef.h"
#include "w_wad.h"
#include "v_video.h"
#include "doomstat.h"
#include "st_stuff.h"
#include "g_game.h"
#include "g_level.h"
#include "r_data/r_translate.h"
#include "v_palette.h"
#include "r_data/colormaps.h"
#include "poly_buffer.h"
#include "screen_triangle.h"
/////////////////////////////////////////////////////////////////////////////
PolyZBuffer *PolyZBuffer::Instance()
{
static PolyZBuffer buffer;
return &buffer;
}
void PolyZBuffer::Resize(int newwidth, int newheight)
{
width = newwidth;
height = newheight;
values.resize(width * height);
}
/////////////////////////////////////////////////////////////////////////////
PolyStencilBuffer *PolyStencilBuffer::Instance()
{
static PolyStencilBuffer buffer;
return &buffer;
}
void PolyStencilBuffer::Resize(int newwidth, int newheight)
{
width = newwidth;
height = newheight;
values.resize(width * height);
}

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 <vector>
struct TriVertex;
class PolyZBuffer
{
public:
static PolyZBuffer *Instance();
void Resize(int newwidth, int newheight);
int Width() const { return width; }
int Height() const { return height; }
float *Values() { return values.data(); }
private:
int width;
int height;
std::vector<float> values;
};
class PolyStencilBuffer
{
public:
static PolyStencilBuffer *Instance();
void Resize(int newwidth, int newheight);
int Width() const { return width; }
int Height() const { return height; }
uint8_t *Values() { return values.data(); }
private:
int width;
int height;
std::vector<uint8_t> values;
};

View file

@ -1,353 +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 <stddef.h>
#include "templates.h"
#include "doomdef.h"
#include "w_wad.h"
#include "v_video.h"
#include "doomstat.h"
#include "st_stuff.h"
#include "g_game.h"
#include "g_level.h"
#include "r_data/r_translate.h"
#include "v_palette.h"
#include "r_data/colormaps.h"
#include "swrenderer/r_swcolormaps.h"
#include "poly_draw_args.h"
#include "swrenderer/viewport/r_viewport.h"
#include "polyrenderer/poly_renderthread.h"
void PolyDrawArgs::SetTexture(const uint8_t *texels, int width, int height)
{
mTexture = nullptr;
mTexturePixels = texels;
mTextureWidth = width;
mTextureHeight = height;
mTranslation = nullptr;
}
void PolyDrawArgs::SetTexture(FSoftwareTexture *texture, FRenderStyle style)
{
mTexture = texture;
mTextureWidth = texture->GetPhysicalWidth();
mTextureHeight = texture->GetPhysicalHeight();
if (PolyTriangleDrawer::IsBgra())
mTexturePixels = (const uint8_t *)texture->GetPixelsBgra();
else
mTexturePixels = texture->GetPixels(style);
mTranslation = nullptr;
}
void PolyDrawArgs::SetTexture(FSoftwareTexture *texture, uint32_t translationID, FRenderStyle style)
{
// Alphatexture overrides translations.
if (translationID != 0xffffffff && translationID != 0 && !(style.Flags & STYLEF_RedIsAlpha))
{
FRemapTable *table = TranslationToTable(translationID);
if (table != nullptr && !table->Inactive)
{
if (PolyTriangleDrawer::IsBgra())
mTranslation = (uint8_t*)table->Palette;
else
mTranslation = table->Remap;
mTexture = texture;
mTextureWidth = texture->GetPhysicalWidth();
mTextureHeight = texture->GetPhysicalHeight();
mTexturePixels = texture->GetPixels(style);
return;
}
}
if (style.Flags & STYLEF_RedIsAlpha)
{
mTexture = texture;
mTextureWidth = texture->GetPhysicalWidth();
mTextureHeight = texture->GetPhysicalHeight();
mTexturePixels = texture->GetPixels(style);
}
else
{
SetTexture(texture, style);
}
}
void PolyDrawArgs::SetLight(FSWColormap *base_colormap, uint32_t lightlevel, double globVis, bool fixed)
{
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;
mLightRed += mLightRed >> 7;
mLightGreen = base_colormap->Color.g;
mLightGreen += mLightGreen >> 7;
mLightBlue = base_colormap->Color.b;
mLightBlue += mLightBlue >> 7;
mLightAlpha = base_colormap->Color.a;
mLightAlpha += mLightAlpha >> 7;
mFadeRed = base_colormap->Fade.r;
mFadeRed += mFadeRed >> 7;
mFadeGreen = base_colormap->Fade.g;
mFadeGreen += mFadeGreen >> 7;
mFadeBlue = base_colormap->Fade.b;
mFadeBlue += mFadeBlue >> 7;
mFadeAlpha = base_colormap->Fade.a;
mFadeAlpha += mFadeAlpha >> 7;
mDesaturate = MIN(abs(base_colormap->Desaturate), 255);
mDesaturate += mDesaturate >> 7;
mSimpleShade = (base_colormap->Color.d == 0x00ffffff && base_colormap->Fade.d == 0x00000000 && base_colormap->Desaturate == 0);
mColormaps = base_colormap->Maps;
}
void PolyDrawArgs::SetColor(uint32_t bgra, uint8_t palindex)
{
if (PolyTriangleDrawer::IsBgra())
{
mColor = bgra;
}
else
{
mColor = palindex;
}
}
void PolyDrawArgs::SetStyle(const FRenderStyle &renderstyle, double alpha, uint32_t fillcolor, uint32_t translationID, FSoftwareTexture *tex, bool fullbright)
{
SetTexture(tex, translationID, renderstyle);
SetColor(0xff000000 | fillcolor, fillcolor >> 24);
if (renderstyle == LegacyRenderStyles[STYLE_Normal] || (r_drawfuzz == 0 && renderstyle == LegacyRenderStyles[STYLE_OptFuzzy]))
{
SetStyle(Translation() ? TriBlendMode::NormalTranslated : TriBlendMode::Normal, alpha);
}
else if (renderstyle == LegacyRenderStyles[STYLE_Add] && fullbright && alpha == 1.0 && !Translation())
{
SetStyle(TriBlendMode::SrcColor, alpha);
}
else if (renderstyle == LegacyRenderStyles[STYLE_SoulTrans])
{
SetStyle(Translation() ? TriBlendMode::AddTranslated : TriBlendMode::Add, transsouls);
}
else if (renderstyle == LegacyRenderStyles[STYLE_Fuzzy] || (r_drawfuzz == 1 && renderstyle == LegacyRenderStyles[STYLE_OptFuzzy]))
{
SetColor(0xff000000, 0);
SetStyle(TriBlendMode::Fuzzy);
}
else if (renderstyle == LegacyRenderStyles[STYLE_Shadow] || (r_drawfuzz == 2 && renderstyle == LegacyRenderStyles[STYLE_OptFuzzy]))
{
SetColor(0xff000000, 0);
SetStyle(Translation() ? TriBlendMode::TranslucentStencilTranslated : TriBlendMode::TranslucentStencil, 1.0 - 160 / 255.0);
}
else if (renderstyle == LegacyRenderStyles[STYLE_Stencil])
{
SetStyle(Translation() ? TriBlendMode::StencilTranslated : TriBlendMode::Stencil, alpha);
}
else if (renderstyle == LegacyRenderStyles[STYLE_Translucent])
{
SetStyle(Translation() ? TriBlendMode::TranslucentTranslated : TriBlendMode::Translucent, alpha);
}
else if (renderstyle == LegacyRenderStyles[STYLE_Add])
{
SetStyle(Translation() ? TriBlendMode::AddTranslated : TriBlendMode::Add, alpha);
}
else if (renderstyle == LegacyRenderStyles[STYLE_Shaded])
{
SetStyle(Translation() ? TriBlendMode::ShadedTranslated : TriBlendMode::Shaded, alpha);
}
else if (renderstyle == LegacyRenderStyles[STYLE_TranslucentStencil])
{
SetStyle(Translation() ? TriBlendMode::TranslucentStencilTranslated : TriBlendMode::TranslucentStencil, alpha);
}
else if (renderstyle == LegacyRenderStyles[STYLE_Subtract])
{
SetStyle(Translation() ? TriBlendMode::SubtractTranslated : TriBlendMode::Subtract, alpha);
}
else if (renderstyle == LegacyRenderStyles[STYLE_AddStencil])
{
SetStyle(Translation() ? TriBlendMode::AddStencilTranslated : TriBlendMode::AddStencil, alpha);
}
else if (renderstyle == LegacyRenderStyles[STYLE_AddShaded])
{
SetStyle(Translation() ? TriBlendMode::AddShadedTranslated : TriBlendMode::AddShaded, alpha);
}
}
/////////////////////////////////////////////////////////////////////////////
void RectDrawArgs::SetTexture(FSoftwareTexture *texture, FRenderStyle style)
{
mTexture = texture;
mTextureWidth = texture->GetWidth();
mTextureHeight = texture->GetHeight();
if (PolyTriangleDrawer::IsBgra())
mTexturePixels = (const uint8_t *)texture->GetPixelsBgra();
else
mTexturePixels = texture->GetPixels(style);
mTranslation = nullptr;
}
void RectDrawArgs::SetTexture(FSoftwareTexture *texture, uint32_t translationID, FRenderStyle style)
{
// Alphatexture overrides translations.
if (translationID != 0xffffffff && translationID != 0 && !(style.Flags & STYLEF_RedIsAlpha))
{
FRemapTable *table = TranslationToTable(translationID);
if (table != nullptr && !table->Inactive)
{
if (PolyTriangleDrawer::IsBgra())
mTranslation = (uint8_t*)table->Palette;
else
mTranslation = table->Remap;
mTexture = texture;
mTextureWidth = texture->GetWidth();
mTextureHeight = texture->GetHeight();
mTexturePixels = texture->GetPixels(style);
return;
}
}
if (style.Flags & STYLEF_RedIsAlpha)
{
mTexture = texture;
mTextureWidth = texture->GetWidth();
mTextureHeight = texture->GetHeight();
mTexturePixels = texture->GetPixels(style);
}
else
{
SetTexture(texture, style);
}
}
void RectDrawArgs::SetLight(FSWColormap *base_colormap, uint32_t lightlevel)
{
mLight = clamp<uint32_t>(lightlevel, 0, 255);
mLightRed = base_colormap->Color.r * 256 / 255;
mLightGreen = base_colormap->Color.g * 256 / 255;
mLightBlue = base_colormap->Color.b * 256 / 255;
mLightAlpha = base_colormap->Color.a * 256 / 255;
mFadeRed = base_colormap->Fade.r;
mFadeGreen = base_colormap->Fade.g;
mFadeBlue = base_colormap->Fade.b;
mFadeAlpha = base_colormap->Fade.a;
mDesaturate = MIN(abs(base_colormap->Desaturate), 255) * 255 / 256;
mSimpleShade = (base_colormap->Color.d == 0x00ffffff && base_colormap->Fade.d == 0x00000000 && base_colormap->Desaturate == 0);
mColormaps = base_colormap->Maps;
}
void RectDrawArgs::SetColor(uint32_t bgra, uint8_t palindex)
{
if (PolyTriangleDrawer::IsBgra())
{
mColor = bgra;
}
else
{
mColor = palindex;
}
}
void RectDrawArgs::Draw(PolyRenderThread *thread, double x0, double x1, double y0, double y1, double u0, double u1, double v0, double v1)
{
mX0 = (float)x0;
mX1 = (float)x1;
mY0 = (float)y0;
mY1 = (float)y1;
mU0 = (float)u0;
mU1 = (float)u1;
mV0 = (float)v0;
mV1 = (float)v1;
thread->DrawQueue->Push<DrawRectCommand>(*this);
}
void RectDrawArgs::SetStyle(const FRenderStyle &renderstyle, double alpha, uint32_t fillcolor, uint32_t translationID, FSoftwareTexture *tex, bool fullbright)
{
SetTexture(tex, translationID, renderstyle);
SetColor(0xff000000 | fillcolor, fillcolor >> 24);
if (renderstyle == LegacyRenderStyles[STYLE_Normal] || (r_drawfuzz == 0 && renderstyle == LegacyRenderStyles[STYLE_OptFuzzy]))
{
SetStyle(Translation() ? TriBlendMode::NormalTranslated : TriBlendMode::Normal, alpha);
}
else if (renderstyle == LegacyRenderStyles[STYLE_Add] && fullbright && alpha == 1.0 && !Translation())
{
SetStyle(TriBlendMode::SrcColor, alpha);
}
else if (renderstyle == LegacyRenderStyles[STYLE_SoulTrans])
{
SetStyle(Translation() ? TriBlendMode::AddTranslated : TriBlendMode::Add, transsouls);
}
else if (renderstyle == LegacyRenderStyles[STYLE_Fuzzy] || (r_drawfuzz == 1 && renderstyle == LegacyRenderStyles[STYLE_OptFuzzy]))
{
SetColor(0xff000000, 0);
SetStyle(TriBlendMode::Fuzzy);
}
else if (renderstyle == LegacyRenderStyles[STYLE_Shadow] || (r_drawfuzz == 2 && renderstyle == LegacyRenderStyles[STYLE_OptFuzzy]))
{
SetColor(0xff000000, 0);
SetStyle(Translation() ? TriBlendMode::TranslucentStencilTranslated : TriBlendMode::TranslucentStencil, 1.0 - 160 / 255.0);
}
else if (renderstyle == LegacyRenderStyles[STYLE_Stencil])
{
SetStyle(Translation() ? TriBlendMode::StencilTranslated : TriBlendMode::Stencil, alpha);
}
else if (renderstyle == LegacyRenderStyles[STYLE_Translucent])
{
SetStyle(Translation() ? TriBlendMode::TranslucentTranslated : TriBlendMode::Translucent, alpha);
}
else if (renderstyle == LegacyRenderStyles[STYLE_Add])
{
SetStyle(Translation() ? TriBlendMode::AddTranslated : TriBlendMode::Add, alpha);
}
else if (renderstyle == LegacyRenderStyles[STYLE_Shaded])
{
SetStyle(Translation() ? TriBlendMode::ShadedTranslated : TriBlendMode::Shaded, alpha);
}
else if (renderstyle == LegacyRenderStyles[STYLE_TranslucentStencil])
{
SetStyle(Translation() ? TriBlendMode::TranslucentStencilTranslated : TriBlendMode::TranslucentStencil, alpha);
}
else if (renderstyle == LegacyRenderStyles[STYLE_Subtract])
{
SetStyle(Translation() ? TriBlendMode::SubtractTranslated : TriBlendMode::Subtract, alpha);
}
else if (renderstyle == LegacyRenderStyles[STYLE_AddStencil])
{
SetStyle(Translation() ? TriBlendMode::AddStencilTranslated : TriBlendMode::AddStencil, alpha);
}
else if (renderstyle == LegacyRenderStyles[STYLE_AddShaded])
{
SetStyle(Translation() ? TriBlendMode::AddShadedTranslated : TriBlendMode::AddShaded, alpha);
}
}

View file

@ -1,231 +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_data/r_translate.h"
#include "r_data/colormaps.h"
#include "screen_triangle.h"
class PolyRenderThread;
class FSoftwareTexture;
class Mat4f;
enum class PolyDrawMode
{
Triangles,
TriangleFan,
TriangleStrip
};
class PolyClipPlane
{
public:
PolyClipPlane() : A(0.0f), B(0.0f), C(0.0f), D(1.0f) { }
PolyClipPlane(float a, float b, float c, float d) : A(a), B(b), C(c), D(d) { }
float A, B, C, D;
};
struct TriVertex
{
TriVertex() { }
TriVertex(float x, float y, float z, float w, float u, float v) : x(x), y(y), z(z), w(w), u(u), v(v) { }
float x, y, z, w;
float u, v;
};
struct PolyLight
{
uint32_t color;
float x, y, z;
float radius;
};
class PolyDrawArgs
{
public:
void SetClipPlane(int index, const PolyClipPlane &plane) { mClipPlane[index] = plane; }
void SetTexture(const uint8_t *texels, int width, int height);
void SetTexture(FSoftwareTexture *texture, FRenderStyle style);
void SetTexture(FSoftwareTexture *texture, uint32_t translationID, FRenderStyle style);
void SetLight(FSWColormap *basecolormap, uint32_t lightlevel, double globVis, bool fixed);
void SetDepthTest(bool enable) { mDepthTest = enable; }
void SetStencilTestValue(uint8_t stencilTestValue) { mStencilTestValue = stencilTestValue; }
void SetWriteColor(bool enable) { mWriteColor = enable; }
void SetWriteStencil(bool enable, uint8_t stencilWriteValue = 0) { mWriteStencil = enable; mStencilWriteValue = stencilWriteValue; }
void SetWriteDepth(bool enable) { mWriteDepth = enable; }
void SetStyle(TriBlendMode blendmode, double alpha = 1.0) { mBlendMode = blendmode; mAlpha = (uint32_t)(alpha * 256.0 + 0.5); }
void SetStyle(const FRenderStyle &renderstyle, double alpha, uint32_t fillcolor, uint32_t translationID, FSoftwareTexture *texture, bool fullbright);
void SetColor(uint32_t bgra, uint8_t palindex);
void SetLights(PolyLight *lights, int numLights) { mLights = lights; mNumLights = numLights; }
void SetDynLightColor(uint32_t color) { mDynLightColor = color; }
const PolyClipPlane &ClipPlane(int index) const { return mClipPlane[index]; }
bool WriteColor() const { return mWriteColor; }
FSoftwareTexture *Texture() const { return mTexture; }
const uint8_t *TexturePixels() const { return mTexturePixels; }
int TextureWidth() const { return mTextureWidth; }
int TextureHeight() const { return mTextureHeight; }
const uint8_t *Translation() const { return mTranslation; }
bool WriteStencil() const { return mWriteStencil; }
uint8_t StencilTestValue() const { return mStencilTestValue; }
uint8_t StencilWriteValue() const { return mStencilWriteValue; }
bool DepthTest() const { return mDepthTest; }
bool WriteDepth() const { return mWriteDepth; }
TriBlendMode BlendMode() const { return mBlendMode; }
uint32_t Color() const { return mColor; }
uint32_t Alpha() const { return mAlpha; }
float GlobVis() const { return mGlobVis; }
uint32_t Light() const { return mLight; }
const uint8_t *BaseColormap() const { return mColormaps; }
uint16_t ShadeLightAlpha() const { return mLightAlpha; }
uint16_t ShadeLightRed() const { return mLightRed; }
uint16_t ShadeLightGreen() const { return mLightGreen; }
uint16_t ShadeLightBlue() const { return mLightBlue; }
uint16_t ShadeFadeAlpha() const { return mFadeAlpha; }
uint16_t ShadeFadeRed() const { return mFadeRed; }
uint16_t ShadeFadeGreen() const { return mFadeGreen; }
uint16_t ShadeFadeBlue() const { return mFadeBlue; }
uint16_t ShadeDesaturate() const { return mDesaturate; }
bool SimpleShade() const { return mSimpleShade; }
bool NearestFilter() const { return mNearestFilter; }
bool FixedLight() const { return mFixedLight; }
PolyLight *Lights() const { return mLights; }
int NumLights() const { return mNumLights; }
uint32_t DynLightColor() const { return mDynLightColor; }
const FVector3 &Normal() const { return mNormal; }
void SetNormal(const FVector3 &normal) { mNormal = normal; }
private:
bool mDepthTest = false;
bool mWriteStencil = true;
bool mWriteColor = true;
bool mWriteDepth = true;
FSoftwareTexture *mTexture = nullptr;
const uint8_t *mTexturePixels = nullptr;
int mTextureWidth = 0;
int mTextureHeight = 0;
const uint8_t *mTranslation = nullptr;
uint8_t mStencilTestValue = 0;
uint8_t mStencilWriteValue = 0;
const uint8_t *mColormaps = nullptr;
PolyClipPlane mClipPlane[3];
TriBlendMode mBlendMode = TriBlendMode::Fill;
uint32_t mLight = 0;
uint32_t mColor = 0;
uint32_t mAlpha = 0;
uint16_t mLightAlpha = 0;
uint16_t mLightRed = 0;
uint16_t mLightGreen = 0;
uint16_t mLightBlue = 0;
uint16_t mFadeAlpha = 0;
uint16_t mFadeRed = 0;
uint16_t mFadeGreen = 0;
uint16_t mFadeBlue = 0;
uint16_t mDesaturate = 0;
float mGlobVis = 0.0f;
bool mSimpleShade = true;
bool mNearestFilter = true;
bool mFixedLight = false;
PolyLight *mLights = nullptr;
int mNumLights = 0;
FVector3 mNormal;
uint32_t mDynLightColor = 0;
};
class RectDrawArgs
{
public:
void SetTexture(FSoftwareTexture *texture, FRenderStyle style);
void SetTexture(FSoftwareTexture *texture, uint32_t translationID, FRenderStyle style);
void SetLight(FSWColormap *basecolormap, uint32_t lightlevel);
void SetStyle(TriBlendMode blendmode, double alpha = 1.0) { mBlendMode = blendmode; mAlpha = (uint32_t)(alpha * 256.0 + 0.5); }
void SetStyle(const FRenderStyle &renderstyle, double alpha, uint32_t fillcolor, uint32_t translationID, FSoftwareTexture *texture, bool fullbright);
void SetColor(uint32_t bgra, uint8_t palindex);
void Draw(PolyRenderThread *thread, double x0, double x1, double y0, double y1, double u0, double u1, double v0, double v1);
FSoftwareTexture *Texture() const { return mTexture; }
const uint8_t *TexturePixels() const { return mTexturePixels; }
int TextureWidth() const { return mTextureWidth; }
int TextureHeight() const { return mTextureHeight; }
const uint8_t *Translation() const { return mTranslation; }
TriBlendMode BlendMode() const { return mBlendMode; }
uint32_t Color() const { return mColor; }
uint32_t Alpha() const { return mAlpha; }
uint32_t Light() const { return mLight; }
const uint8_t *BaseColormap() const { return mColormaps; }
uint16_t ShadeLightAlpha() const { return mLightAlpha; }
uint16_t ShadeLightRed() const { return mLightRed; }
uint16_t ShadeLightGreen() const { return mLightGreen; }
uint16_t ShadeLightBlue() const { return mLightBlue; }
uint16_t ShadeFadeAlpha() const { return mFadeAlpha; }
uint16_t ShadeFadeRed() const { return mFadeRed; }
uint16_t ShadeFadeGreen() const { return mFadeGreen; }
uint16_t ShadeFadeBlue() const { return mFadeBlue; }
uint16_t ShadeDesaturate() const { return mDesaturate; }
bool SimpleShade() const { return mSimpleShade; }
float X0() const { return mX0; }
float X1() const { return mX1; }
float Y0() const { return mY0; }
float Y1() const { return mY1; }
float U0() const { return mU0; }
float U1() const { return mU1; }
float V0() const { return mV0; }
float V1() const { return mV1; }
private:
FSoftwareTexture *mTexture = nullptr;
const uint8_t *mTexturePixels = nullptr;
int mTextureWidth = 0;
int mTextureHeight = 0;
const uint8_t *mTranslation = nullptr;
const uint8_t *mColormaps = nullptr;
TriBlendMode mBlendMode = TriBlendMode::Fill;
uint32_t mLight = 0;
uint32_t mColor = 0;
uint32_t mAlpha = 0;
uint16_t mLightAlpha = 0;
uint16_t mLightRed = 0;
uint16_t mLightGreen = 0;
uint16_t mLightBlue = 0;
uint16_t mFadeAlpha = 0;
uint16_t mFadeRed = 0;
uint16_t mFadeGreen = 0;
uint16_t mFadeBlue = 0;
uint16_t mDesaturate = 0;
bool mSimpleShade = true;
float mX0, mX1, mY0, mY1, mU0, mU1, mV0, mV1;
};

File diff suppressed because it is too large Load diff

View file

@ -26,42 +26,138 @@
#include "swrenderer/drawers/r_thread.h"
#include "polyrenderer/drawers/screen_triangle.h"
#include "polyrenderer/math/gpu_types.h"
#include "polyrenderer/drawers/poly_buffer.h"
#include "polyrenderer/drawers/poly_draw_args.h"
#include "polyrenderer/drawers/poly_vertex_shader.h"
class DCanvas;
class PolyDrawerCommand;
class PolyInputAssembly;
class PolyDepthStencil;
struct PolyPushConstants;
enum class PolyDrawMode
{
Points,
Lines,
Triangles,
TriangleFan,
TriangleStrip
};
class PolyTriangleDrawer
{
public:
static void ResizeBuffers(DCanvas *canvas);
static void SetViewport(const DrawerCommandQueuePtr &queue, int x, int y, int width, int height, DCanvas *canvas, PolyDepthStencil *depthStencil);
static void SetInputAssembly(const DrawerCommandQueuePtr &queue, PolyInputAssembly *input);
static void SetVertexBuffer(const DrawerCommandQueuePtr &queue, const void *vertices);
static void SetIndexBuffer(const DrawerCommandQueuePtr &queue, const void *elements);
static void SetLightBuffer(const DrawerCommandQueuePtr& queue, const void *lights);
static void SetViewpointUniforms(const DrawerCommandQueuePtr &queue, const HWViewpointUniforms *uniforms);
static void SetDepthClamp(const DrawerCommandQueuePtr &queue, bool on);
static void SetDepthMask(const DrawerCommandQueuePtr &queue, bool on);
static void SetDepthFunc(const DrawerCommandQueuePtr &queue, int func);
static void SetDepthRange(const DrawerCommandQueuePtr &queue, float min, float max);
static void SetDepthBias(const DrawerCommandQueuePtr &queue, float depthBiasConstantFactor, float depthBiasSlopeFactor);
static void SetColorMask(const DrawerCommandQueuePtr &queue, bool r, bool g, bool b, bool a);
static void SetStencil(const DrawerCommandQueuePtr &queue, int stencilRef, int op);
static void SetCulling(const DrawerCommandQueuePtr &queue, int mode);
static void EnableClipDistance(const DrawerCommandQueuePtr &queue, int num, bool state);
static void EnableStencil(const DrawerCommandQueuePtr &queue, bool on);
static void SetScissor(const DrawerCommandQueuePtr &queue, int x, int y, int w, int h);
static void EnableDepthTest(const DrawerCommandQueuePtr &queue, bool on);
static void SetRenderStyle(const DrawerCommandQueuePtr &queue, FRenderStyle style);
static void SetTexture(const DrawerCommandQueuePtr &queue, int unit, void *pixels, int width, int height, bool bgra);
static void SetShader(const DrawerCommandQueuePtr &queue, int specialEffect, int effectState, bool alphaTest);
static void PushStreamData(const DrawerCommandQueuePtr &queue, const StreamData &data, const PolyPushConstants &constants);
static void PushMatrices(const DrawerCommandQueuePtr &queue, const VSMatrix &modelMatrix, const VSMatrix &normalModelMatrix, const VSMatrix &textureMatrix);
static void ClearDepth(const DrawerCommandQueuePtr &queue, float value);
static void ClearStencil(const DrawerCommandQueuePtr &queue, uint8_t value);
static void SetViewport(const DrawerCommandQueuePtr &queue, int x, int y, int width, int height, DCanvas *canvas);
static void SetCullCCW(const DrawerCommandQueuePtr &queue, bool ccw);
static void SetTwoSided(const DrawerCommandQueuePtr &queue, bool twosided);
static void SetWeaponScene(const DrawerCommandQueuePtr &queue, bool enable);
static void SetModelVertexShader(const DrawerCommandQueuePtr &queue, int frame1, int frame2, float interpolationFactor);
static void SetTransform(const DrawerCommandQueuePtr &queue, const Mat4f *objectToClip, const Mat4f *objectToWorld);
static void DrawArray(const DrawerCommandQueuePtr &queue, const PolyDrawArgs &args, const void *vertices, int vcount, PolyDrawMode mode = PolyDrawMode::Triangles);
static void DrawElements(const DrawerCommandQueuePtr &queue, const PolyDrawArgs &args, const void *vertices, const unsigned int *elements, int count, PolyDrawMode mode = PolyDrawMode::Triangles);
static bool IsBgra();
static void Draw(const DrawerCommandQueuePtr &queue, int index, int vcount, PolyDrawMode mode = PolyDrawMode::Triangles);
static void DrawIndexed(const DrawerCommandQueuePtr &queue, int index, int count, PolyDrawMode mode = PolyDrawMode::Triangles);
};
class PolyDepthStencil
{
public:
PolyDepthStencil(int width, int height) : width(width), height(height), depthbuffer(width * height), stencilbuffer(width * height) { }
int Width() const { return width; }
int Height() const { return height; }
float *DepthValues() { return depthbuffer.data(); }
uint8_t *StencilValues() { return stencilbuffer.data(); }
private:
int width;
int height;
std::vector<float> depthbuffer;
std::vector<uint8_t> stencilbuffer;
};
struct PolyPushConstants
{
int uTextureMode;
float uAlphaThreshold;
Vec2f uClipSplit;
// Lighting + Fog
float uLightLevel;
float uFogDensity;
float uLightFactor;
float uLightDist;
int uFogEnabled;
// dynamic lights
int uLightIndex;
};
class PolyInputAssembly
{
public:
virtual void Load(PolyTriangleThreadData *thread, const void *vertices, int index) = 0;
};
class PolyTriangleThreadData
{
public:
PolyTriangleThreadData(int32_t core, int32_t num_cores, int32_t numa_node, int32_t num_numa_nodes, int numa_start_y, int numa_end_y) : core(core), num_cores(num_cores), numa_node(numa_node), num_numa_nodes(num_numa_nodes), numa_start_y(numa_start_y), numa_end_y(numa_end_y) { }
PolyTriangleThreadData(int32_t core, int32_t num_cores, int32_t numa_node, int32_t num_numa_nodes, int numa_start_y, int numa_end_y)
: core(core), num_cores(num_cores), numa_node(numa_node), num_numa_nodes(num_numa_nodes), numa_start_y(numa_start_y), numa_end_y(numa_end_y)
{
}
void ClearDepth(float value);
void ClearStencil(uint8_t value);
void SetViewport(int x, int y, int width, int height, uint8_t *dest, int dest_width, int dest_height, int dest_pitch, bool dest_bgra);
void SetTransform(const Mat4f *objectToClip, const Mat4f *objectToWorld);
void SetViewport(int x, int y, int width, int height, uint8_t *dest, int dest_width, int dest_height, int dest_pitch, bool dest_bgra, PolyDepthStencil *depthstencil);
void SetCullCCW(bool value) { ccw = value; }
void SetTwoSided(bool value) { twosided = value; }
void SetWeaponScene(bool value) { weaponScene = value; }
void SetModelVertexShader(int frame1, int frame2, float interpolationFactor) { modelFrame1 = frame1; modelFrame2 = frame2; modelInterpolationFactor = interpolationFactor; }
void DrawElements(const PolyDrawArgs &args, const void *vertices, const unsigned int *elements, int count, PolyDrawMode mode);
void DrawArray(const PolyDrawArgs &args, const void *vertices, int vcount, PolyDrawMode mode);
void SetInputAssembly(PolyInputAssembly *input) { inputAssembly = input; }
void SetVertexBuffer(const void *data) { vertices = data; }
void SetIndexBuffer(const void *data) { elements = (const unsigned int *)data; }
void SetLightBuffer(const void *data) { lights = (const FVector4 *)data; }
void SetViewpointUniforms(const HWViewpointUniforms *uniforms);
void SetDepthClamp(bool on);
void SetDepthMask(bool on);
void SetDepthFunc(int func);
void SetDepthRange(float min, float max);
void SetDepthBias(float depthBiasConstantFactor, float depthBiasSlopeFactor);
void SetColorMask(bool r, bool g, bool b, bool a);
void SetStencil(int stencilRef, int op);
void SetCulling(int mode);
void EnableClipDistance(int num, bool state);
void EnableStencil(bool on);
void SetScissor(int x, int y, int w, int h);
void EnableDepthTest(bool on);
void SetRenderStyle(FRenderStyle style);
void SetTexture(int unit, const void *pixels, int width, int height, bool bgra);
void SetShader(int specialEffect, int effectState, bool alphaTest);
void UpdateClip();
void PushStreamData(const StreamData &data, const PolyPushConstants &constants);
void PushMatrices(const VSMatrix &modelMatrix, const VSMatrix &normalModelMatrix, const VSMatrix &textureMatrix);
void DrawIndexed(int index, int count, PolyDrawMode mode);
void Draw(int index, int vcount, PolyDrawMode mode);
int32_t core;
int32_t num_cores;
@ -90,14 +186,18 @@ public:
return MAX(c, 0);
}
// Varyings
float worldposX[MAXWIDTH];
float worldposY[MAXWIDTH];
float worldposZ[MAXWIDTH];
uint32_t texel[MAXWIDTH];
int32_t texelV[MAXWIDTH];
uint16_t lightarray[MAXWIDTH];
uint32_t dynlights[MAXWIDTH];
struct Scanline
{
float W[MAXWIDTH];
float U[MAXWIDTH];
float V[MAXWIDTH];
float WorldX[MAXWIDTH];
float WorldY[MAXWIDTH];
float WorldZ[MAXWIDTH];
uint32_t FragColor[MAXWIDTH];
uint16_t lightarray[MAXWIDTH];
//uint32_t dynlights[MAXWIDTH];
} scanline;
static PolyTriangleThreadData *Get(DrawerThread *thread);
@ -106,29 +206,78 @@ public:
int dest_height = 0;
bool dest_bgra = false;
uint8_t *dest = nullptr;
bool weaponScene = false;
PolyDepthStencil *depthstencil = nullptr;
float depthbias = 0.0f;
int viewport_y = 0;
struct ClipRect
{
int left = 0;
int top = 0;
int right = 0;
int bottom = 0;
} clip, scissor;
FRenderStyle RenderStyle;
int SpecialEffect = EFF_NONE;
int EffectState = 0;
bool AlphaTest = false;
const PolyPushConstants* PushConstants = nullptr;
const void *vertices = nullptr;
const unsigned int *elements = nullptr;
const FVector4 *lights = nullptr;
/*struct PolyLight
{
uint32_t color;
float x, y, z;
float radius;
};
enum { maxPolyLights = 16 };
PolyLight polyLights[maxPolyLights];*/
PolyMainVertexShader mainVertexShader;
struct TextureUnit
{
const void* pixels = nullptr;
int width = 0;
int height = 0;
bool bgra = true;
} textures[16];
bool DepthTest = false;
bool StencilTest = true;
bool WriteStencil = true;
bool WriteColor = true;
bool WriteDepth = true;
uint8_t StencilTestValue = 0;
uint8_t StencilWriteValue = 0;
private:
ShadedTriVertex ShadeVertex(const PolyDrawArgs &drawargs, const void *vertices, int index);
void DrawShadedTriangle(const ShadedTriVertex *vertices, bool ccw, TriDrawTriangleArgs *args);
static bool IsDegenerate(const ShadedTriVertex *vertices);
ShadedTriVertex ShadeVertex(int index);
void DrawShadedPoint(const ShadedTriVertex *const* vertex);
void DrawShadedLine(const ShadedTriVertex *const* vertices);
void DrawShadedTriangle(const ShadedTriVertex *const* vertices, bool ccw, TriDrawTriangleArgs *args);
static bool IsDegenerate(const ShadedTriVertex *const* vertices);
static bool IsFrontfacing(TriDrawTriangleArgs *args);
static int ClipEdge(const ShadedTriVertex *verts, ShadedTriVertex *clippedvert);
int ClipEdge(const ShadedTriVertex *const* verts);
int viewport_x = 0;
int viewport_width = 0;
int viewport_height = 0;
bool ccw = true;
bool twosided = false;
const Mat4f *objectToClip = nullptr;
const Mat4f *objectToWorld = nullptr;
int modelFrame1 = -1;
int modelFrame2 = -1;
float modelInterpolationFactor = 0.0f;
bool twosided = true;
PolyInputAssembly *inputAssembly = nullptr;
enum { max_additional_vertices = 16 };
float weightsbuffer[max_additional_vertices * 3 * 2];
float *weights = nullptr;
};
class PolyDrawerCommand : public DrawerCommand
@ -136,70 +285,227 @@ class PolyDrawerCommand : public DrawerCommand
public:
};
class PolySetTransformCommand : public PolyDrawerCommand
class PolySetDepthClampCommand : public PolyDrawerCommand
{
public:
PolySetTransformCommand(const Mat4f *objectToClip, const Mat4f *objectToWorld);
void Execute(DrawerThread *thread) override;
PolySetDepthClampCommand(bool on) : on(on) { }
void Execute(DrawerThread *thread) override { PolyTriangleThreadData::Get(thread)->SetDepthClamp(on); }
private:
const Mat4f *objectToClip;
const Mat4f *objectToWorld;
bool on;
};
class PolySetCullCCWCommand : public PolyDrawerCommand
class PolySetDepthMaskCommand : public PolyDrawerCommand
{
public:
PolySetCullCCWCommand(bool ccw);
void Execute(DrawerThread *thread) override;
PolySetDepthMaskCommand(bool on) : on(on) { }
void Execute(DrawerThread *thread) override { PolyTriangleThreadData::Get(thread)->SetDepthMask(on); }
private:
bool ccw;
bool on;
};
class PolySetTwoSidedCommand : public PolyDrawerCommand
class PolySetDepthFuncCommand : public PolyDrawerCommand
{
public:
PolySetTwoSidedCommand(bool twosided);
void Execute(DrawerThread *thread) override;
PolySetDepthFuncCommand(int func) : func(func) { }
void Execute(DrawerThread *thread) override { PolyTriangleThreadData::Get(thread)->SetDepthFunc(func); }
private:
bool twosided;
int func;
};
class PolySetWeaponSceneCommand : public PolyDrawerCommand
class PolySetDepthRangeCommand : public PolyDrawerCommand
{
public:
PolySetWeaponSceneCommand(bool value);
void Execute(DrawerThread *thread) override;
PolySetDepthRangeCommand(float min, float max) : min(min), max(max) { }
void Execute(DrawerThread *thread) override { PolyTriangleThreadData::Get(thread)->SetDepthRange(min, max); }
private:
bool value;
float min;
float max;
};
class PolySetModelVertexShaderCommand : public PolyDrawerCommand
class PolySetDepthBiasCommand : public PolyDrawerCommand
{
public:
PolySetModelVertexShaderCommand(int frame1, int frame2, float interpolationFactor);
void Execute(DrawerThread *thread) override;
PolySetDepthBiasCommand(float depthBiasConstantFactor, float depthBiasSlopeFactor) : depthBiasConstantFactor(depthBiasConstantFactor), depthBiasSlopeFactor(depthBiasSlopeFactor) { }
void Execute(DrawerThread *thread) override { PolyTriangleThreadData::Get(thread)->SetDepthBias(depthBiasConstantFactor, depthBiasSlopeFactor); }
private:
int frame1;
int frame2;
float interpolationFactor;
float depthBiasConstantFactor;
float depthBiasSlopeFactor;
};
class PolySetColorMaskCommand : public PolyDrawerCommand
{
public:
PolySetColorMaskCommand(bool r, bool g, bool b, bool a) : r(r), g(g), b(b), a(a) { }
void Execute(DrawerThread *thread) override { PolyTriangleThreadData::Get(thread)->SetColorMask(r, g, b, a); }
private:
bool r;
bool g;
bool b;
bool a;
};
class PolySetStencilCommand : public PolyDrawerCommand
{
public:
PolySetStencilCommand(int stencilRef, int op) : stencilRef(stencilRef), op(op) { }
void Execute(DrawerThread *thread) override { PolyTriangleThreadData::Get(thread)->SetStencil(stencilRef, op); }
private:
int stencilRef;
int op;
};
class PolySetCullingCommand : public PolyDrawerCommand
{
public:
PolySetCullingCommand(int mode) : mode(mode) { }
void Execute(DrawerThread *thread) override { PolyTriangleThreadData::Get(thread)->SetCulling(mode); }
private:
int mode;
};
class PolyEnableClipDistanceCommand : public PolyDrawerCommand
{
public:
PolyEnableClipDistanceCommand(int num, bool state) : num(num), state(state) { }
void Execute(DrawerThread *thread) override { PolyTriangleThreadData::Get(thread)->EnableClipDistance(num, state); }
private:
int num;
bool state;
};
class PolyEnableStencilCommand : public PolyDrawerCommand
{
public:
PolyEnableStencilCommand(bool on) : on(on) { }
void Execute(DrawerThread *thread) override { PolyTriangleThreadData::Get(thread)->EnableStencil(on); }
private:
bool on;
};
class PolySetScissorCommand : public PolyDrawerCommand
{
public:
PolySetScissorCommand(int x, int y, int w, int h) : x(x), y(y), w(w), h(h) { }
void Execute(DrawerThread *thread) override { PolyTriangleThreadData::Get(thread)->SetScissor(x, y, w, h); }
private:
int x;
int y;
int w;
int h;
};
class PolyEnableDepthTestCommand : public PolyDrawerCommand
{
public:
PolyEnableDepthTestCommand(bool on) : on(on) { }
void Execute(DrawerThread *thread) override { PolyTriangleThreadData::Get(thread)->EnableDepthTest(on); }
private:
bool on;
};
class PolySetRenderStyleCommand : public PolyDrawerCommand
{
public:
PolySetRenderStyleCommand(FRenderStyle style) : style(style) { }
void Execute(DrawerThread *thread) override { PolyTriangleThreadData::Get(thread)->SetRenderStyle(style); }
private:
FRenderStyle style;
};
class PolySetTextureCommand : public PolyDrawerCommand
{
public:
PolySetTextureCommand(int unit, void *pixels, int width, int height, bool bgra) : unit(unit), pixels(pixels), width(width), height(height), bgra(bgra) { }
void Execute(DrawerThread *thread) override { PolyTriangleThreadData::Get(thread)->SetTexture(unit, pixels, width, height, bgra); }
private:
int unit;
void *pixels;
int width;
int height;
bool bgra;
};
class PolySetShaderCommand : public PolyDrawerCommand
{
public:
PolySetShaderCommand(int specialEffect, int effectState, bool alphaTest) : specialEffect(specialEffect), effectState(effectState), alphaTest(alphaTest) { }
void Execute(DrawerThread *thread) override { PolyTriangleThreadData::Get(thread)->SetShader(specialEffect, effectState, alphaTest); }
private:
int specialEffect;
int effectState;
bool alphaTest;
};
class PolySetVertexBufferCommand : public PolyDrawerCommand
{
public:
PolySetVertexBufferCommand(const void *vertices) : vertices(vertices) { }
void Execute(DrawerThread *thread) override { PolyTriangleThreadData::Get(thread)->SetVertexBuffer(vertices); }
private:
const void *vertices;
};
class PolySetIndexBufferCommand : public PolyDrawerCommand
{
public:
PolySetIndexBufferCommand(const void *indices) : indices(indices) { }
void Execute(DrawerThread *thread) override { PolyTriangleThreadData::Get(thread)->SetIndexBuffer(indices); }
private:
const void *indices;
};
class PolySetLightBufferCommand : public PolyDrawerCommand
{
public:
PolySetLightBufferCommand(const void *lights) : lights(lights) { }
void Execute(DrawerThread *thread) override { PolyTriangleThreadData::Get(thread)->SetLightBuffer(lights); }
private:
const void *lights;
};
class PolySetInputAssemblyCommand : public PolyDrawerCommand
{
public:
PolySetInputAssemblyCommand(PolyInputAssembly *input) : input(input) { }
void Execute(DrawerThread *thread) override { PolyTriangleThreadData::Get(thread)->SetInputAssembly(input); }
private:
PolyInputAssembly *input;
};
class PolyClearDepthCommand : public PolyDrawerCommand
{
public:
PolyClearDepthCommand(float value) : value(value) { }
void Execute(DrawerThread *thread) override { PolyTriangleThreadData::Get(thread)->ClearDepth(value); }
private:
float value;
};
class PolyClearStencilCommand : public PolyDrawerCommand
{
public:
PolyClearStencilCommand(uint8_t value);
void Execute(DrawerThread *thread) override;
PolyClearStencilCommand(uint8_t value) : value(value) { }
void Execute(DrawerThread *thread) override { PolyTriangleThreadData::Get(thread)->ClearStencil(value); }
private:
uint8_t value;
@ -208,9 +514,9 @@ private:
class PolySetViewportCommand : public PolyDrawerCommand
{
public:
PolySetViewportCommand(int x, int y, int width, int height, uint8_t *dest, int dest_width, int dest_height, int dest_pitch, bool dest_bgra);
void Execute(DrawerThread *thread) override;
PolySetViewportCommand(int x, int y, int width, int height, uint8_t *dest, int dest_width, int dest_height, int dest_pitch, bool dest_bgra, PolyDepthStencil *depthstencil)
: x(x), y(y), width(width), height(height), dest(dest), dest_width(dest_width), dest_height(dest_height), dest_pitch(dest_pitch), dest_bgra(dest_bgra), depthstencil(depthstencil) { }
void Execute(DrawerThread *thread) override { PolyTriangleThreadData::Get(thread)->SetViewport(x, y, width, height, dest, dest_width, dest_height, dest_pitch, dest_bgra, depthstencil); }
private:
int x;
@ -222,30 +528,63 @@ private:
int dest_height;
int dest_pitch;
bool dest_bgra;
PolyDepthStencil *depthstencil;
};
class DrawPolyTrianglesCommand : public PolyDrawerCommand
class PolySetViewpointUniformsCommand : public PolyDrawerCommand
{
public:
DrawPolyTrianglesCommand(const PolyDrawArgs &args, const void *vertices, const unsigned int *elements, int count, PolyDrawMode mode);
void Execute(DrawerThread *thread) override;
PolySetViewpointUniformsCommand(const HWViewpointUniforms *uniforms) : uniforms(uniforms) {}
void Execute(DrawerThread *thread) override { PolyTriangleThreadData::Get(thread)->SetViewpointUniforms(uniforms); }
private:
PolyDrawArgs args;
const void *vertices;
const unsigned int *elements;
const HWViewpointUniforms *uniforms;
};
class PolyPushMatricesCommand : public PolyDrawerCommand
{
public:
PolyPushMatricesCommand(const VSMatrix &modelMatrix, const VSMatrix &normalModelMatrix, const VSMatrix &textureMatrix)
: modelMatrix(modelMatrix), normalModelMatrix(normalModelMatrix), textureMatrix(textureMatrix) { }
void Execute(DrawerThread *thread) override { PolyTriangleThreadData::Get(thread)->PushMatrices(modelMatrix, normalModelMatrix, textureMatrix); }
private:
VSMatrix modelMatrix;
VSMatrix normalModelMatrix;
VSMatrix textureMatrix;
};
class PolyPushStreamDataCommand : public PolyDrawerCommand
{
public:
PolyPushStreamDataCommand(const StreamData &data, const PolyPushConstants &constants) : data(data), constants(constants) { }
void Execute(DrawerThread *thread) override { PolyTriangleThreadData::Get(thread)->PushStreamData(data, constants); }
private:
StreamData data;
PolyPushConstants constants;
};
class PolyDrawCommand : public PolyDrawerCommand
{
public:
PolyDrawCommand(int index, int count, PolyDrawMode mode) : index(index), count(count), mode(mode) { }
void Execute(DrawerThread *thread) override { PolyTriangleThreadData::Get(thread)->Draw(index, count, mode); }
private:
int index;
int count;
PolyDrawMode mode;
};
class DrawRectCommand : public PolyDrawerCommand
class PolyDrawIndexedCommand : public PolyDrawerCommand
{
public:
DrawRectCommand(const RectDrawArgs &args) : args(args) { }
void Execute(DrawerThread *thread) override;
PolyDrawIndexedCommand(int index, int count, PolyDrawMode mode) : index(index), count(count), mode(mode) { }
void Execute(DrawerThread *thread) override { PolyTriangleThreadData::Get(thread)->DrawIndexed(index, count, mode); }
private:
RectDrawArgs args;
int index;
int count;
PolyDrawMode mode;
};

View file

@ -0,0 +1,199 @@
#pragma once
#include "polyrenderer/math/gpu_types.h"
#include "hwrenderer/scene/hw_viewpointuniforms.h"
#include "hwrenderer/scene/hw_renderstate.h"
#ifndef NO_SSE
#include <xmmintrin.h>
#endif
class ShadedTriVertex
{
public:
Vec4f gl_Position;
float gl_ClipDistance[5];
Vec4f vTexCoord;
uint32_t vColor;
Vec4f pixelpos;
Vec4f vWorldNormal;
};
class PolyMainVertexShader : public ShadedTriVertex
{
public:
// Input
Vec4f aPosition;
Vec2f aTexCoord;
uint32_t aColor;
Vec4f aVertex2;
Vec4f aNormal;
Vec4f aNormal2;
// Output
Vec3f glowdist;
Vec3f gradientdist;
Vec4f vEyeNormal;
// Defines
bool SIMPLE = false;
bool SPHEREMAP = false;
// Uniforms
VSMatrix ModelMatrix;
VSMatrix NormalModelMatrix;
VSMatrix TextureMatrix;
StreamData Data;
Vec2f uClipSplit;
const HWViewpointUniforms *Viewpoint = nullptr;
void main()
{
Vec2f parmTexCoord = aTexCoord;
Vec4f parmPosition = aPosition;
Vec4f worldcoord;
if (SIMPLE)
worldcoord = mul(ModelMatrix, mix(parmPosition, aVertex2, Data.uInterpolationFactor));
else
worldcoord = mul(ModelMatrix, parmPosition);
Vec4f eyeCoordPos = mul(Viewpoint->mViewMatrix, worldcoord);
vColor = aColor;
if (!SIMPLE)
{
pixelpos.X = worldcoord.X;
pixelpos.Y = worldcoord.Y;
pixelpos.Z = worldcoord.Z;
pixelpos.W = -eyeCoordPos.Z / eyeCoordPos.W;
if (Data.uGlowTopColor.W > 0 || Data.uGlowBottomColor.W > 0)
{
float topatpoint = (Data.uGlowTopPlane.W + Data.uGlowTopPlane.X * worldcoord.X + Data.uGlowTopPlane.Y * worldcoord.Z) * Data.uGlowTopPlane.Z;
float bottomatpoint = (Data.uGlowBottomPlane.W + Data.uGlowBottomPlane.X * worldcoord.X + Data.uGlowBottomPlane.Y * worldcoord.Z) * Data.uGlowBottomPlane.Z;
glowdist.X = topatpoint - worldcoord.Y;
glowdist.Y = worldcoord.Y - bottomatpoint;
glowdist.Z = clamp(glowdist.X / (topatpoint - bottomatpoint), 0.0f, 1.0f);
}
if (Data.uObjectColor2.a != 0)
{
float topatpoint = (Data.uGradientTopPlane.W + Data.uGradientTopPlane.X * worldcoord.X + Data.uGradientTopPlane.Y * worldcoord.Z) * Data.uGradientTopPlane.Z;
float bottomatpoint = (Data.uGradientBottomPlane.W + Data.uGradientBottomPlane.X * worldcoord.X + Data.uGradientBottomPlane.Y * worldcoord.Z) * Data.uGradientBottomPlane.Z;
gradientdist.X = topatpoint - worldcoord.Y;
gradientdist.Y = worldcoord.Y - bottomatpoint;
gradientdist.Z = clamp(gradientdist.X / (topatpoint - bottomatpoint), 0.0f, 1.0f);
}
if (Data.uSplitBottomPlane.Z != 0.0f)
{
gl_ClipDistance[3] = ((Data.uSplitTopPlane.W + Data.uSplitTopPlane.X * worldcoord.X + Data.uSplitTopPlane.Y * worldcoord.Z) * Data.uSplitTopPlane.Z) - worldcoord.Y;
gl_ClipDistance[4] = worldcoord.Y - ((Data.uSplitBottomPlane.W + Data.uSplitBottomPlane.X * worldcoord.X + Data.uSplitBottomPlane.Y * worldcoord.Z) * Data.uSplitBottomPlane.Z);
}
vWorldNormal = mul(NormalModelMatrix, Vec4f(normalize(mix3(aNormal, aNormal2, Data.uInterpolationFactor)), 1.0f));
vEyeNormal = mul(Viewpoint->mNormalViewMatrix, vWorldNormal);
}
if (!SPHEREMAP)
{
vTexCoord = mul(TextureMatrix, Vec4f(parmTexCoord, 0.0f, 1.0f));
}
else
{
Vec3f u = normalize3(eyeCoordPos);
Vec3f n = normalize3(mul(Viewpoint->mNormalViewMatrix, Vec4f(parmTexCoord.X, 0.0f, parmTexCoord.Y, 0.0f)));
Vec3f r = reflect(u, n);
float m = 2.0f * sqrt(r.X*r.X + r.Y*r.Y + (r.Z + 1.0f)*(r.Z + 1.0f));
vTexCoord.X = r.X / m + 0.5f;
vTexCoord.Y = r.Y / m + 0.5f;
}
gl_Position = mul(Viewpoint->mProjectionMatrix, eyeCoordPos);
if (Viewpoint->mClipHeightDirection != 0.0f) // clip planes used for reflective flats
{
gl_ClipDistance[0] = (worldcoord.Y - Viewpoint->mClipHeight) * Viewpoint->mClipHeightDirection;
}
else if (Viewpoint->mClipLine.X > -1000000.0f) // and for line portals - this will never be active at the same time as the reflective planes clipping so it can use the same hardware clip plane.
{
gl_ClipDistance[0] = -((worldcoord.Z - Viewpoint->mClipLine.Y) * Viewpoint->mClipLine.Z + (Viewpoint->mClipLine.X - worldcoord.X) * Viewpoint->mClipLine.W) + 1.0f / 32768.0f; // allow a tiny bit of imprecisions for colinear linedefs.
}
else
{
gl_ClipDistance[0] = 1.0f;
}
// clip planes used for translucency splitting
gl_ClipDistance[1] = worldcoord.Y - uClipSplit.X;
gl_ClipDistance[2] = uClipSplit.Y - worldcoord.Y;
if (Data.uSplitTopPlane == FVector4(0.0f, 0.0f, 0.0f, 0.0f))
{
gl_ClipDistance[3] = 1.0f;
gl_ClipDistance[4] = 1.0f;
}
std::swap(vTexCoord.X, vTexCoord.Y); // textures are transposed because the software renderer did them this way
}
private:
static Vec3f normalize(const Vec3f &a)
{
float rcplen = 1.0f / sqrt(a.X * a.X + a.Y * a.Y + a.Z * a.Z);
return Vec3f(a.X * rcplen, a.Y * rcplen, a.Z * rcplen);
}
static Vec3f normalize3(const Vec4f &a)
{
float rcplen = 1.0f / sqrt(a.X * a.X + a.Y * a.Y + a.Z * a.Z);
return Vec3f(a.X * rcplen, a.Y * rcplen, a.Z * rcplen);
}
static Vec4f mix(const Vec4f &a, const Vec4f &b, float t)
{
float invt = 1.0f - t;
return Vec4f(a.X * invt + b.X * t, a.Y * invt + b.Y * t, a.Z * invt + b.Z * t, a.W * invt + b.W * t);
}
static Vec3f mix3(const Vec4f &a, const Vec4f &b, float t)
{
float invt = 1.0f - t;
return Vec3f(a.X * invt + b.X * t, a.Y * invt + b.Y * t, a.Z * invt + b.Z * t);
}
static Vec3f reflect(const Vec3f &u, const Vec3f &n)
{
float d = 2.0f * (n.X * u.X + n.Y * u.Y + n.Z * u.Z);
return Vec3f(u.X - d * n.X, u.Y - d * n.Y, u.Z - d * n.Z);
}
static Vec4f mul(const VSMatrix &mat, const Vec4f &v)
{
const float *m = mat.get();
Vec4f result;
#ifdef NO_SSE
result.X = m[0 * 4 + 0] * v.X + m[1 * 4 + 0] * v.Y + m[2 * 4 + 0] * v.Z + m[3 * 4 + 0] * v.W;
result.Y = m[0 * 4 + 1] * v.X + m[1 * 4 + 1] * v.Y + m[2 * 4 + 1] * v.Z + m[3 * 4 + 1] * v.W;
result.Z = m[0 * 4 + 2] * v.X + m[1 * 4 + 2] * v.Y + m[2 * 4 + 2] * v.Z + m[3 * 4 + 2] * v.W;
result.W = m[0 * 4 + 3] * v.X + m[1 * 4 + 3] * v.Y + m[2 * 4 + 3] * v.Z + m[3 * 4 + 3] * v.W;
#else
__m128 m0 = _mm_loadu_ps(m);
__m128 m1 = _mm_loadu_ps(m + 4);
__m128 m2 = _mm_loadu_ps(m + 8);
__m128 m3 = _mm_loadu_ps(m + 12);
__m128 mv = _mm_loadu_ps(&v.X);
m0 = _mm_mul_ps(m0, _mm_shuffle_ps(mv, mv, _MM_SHUFFLE(0, 0, 0, 0)));
m1 = _mm_mul_ps(m1, _mm_shuffle_ps(mv, mv, _MM_SHUFFLE(1, 1, 1, 1)));
m2 = _mm_mul_ps(m2, _mm_shuffle_ps(mv, mv, _MM_SHUFFLE(2, 2, 2, 2)));
m3 = _mm_mul_ps(m3, _mm_shuffle_ps(mv, mv, _MM_SHUFFLE(3, 3, 3, 3)));
mv = _mm_add_ps(_mm_add_ps(_mm_add_ps(m0, m1), m2), m3);
_mm_storeu_ps(&result.X, mv);
#endif
return result;
}
};

File diff suppressed because it is too large Load diff

View file

@ -24,16 +24,16 @@
#include <cstdint>
#include <vector>
#include "r_data/renderstyle.h"
#include "rendering/swrenderer/drawers/r_draw.h"
class FString;
class PolyDrawArgs;
class PolyTriangleThreadData;
struct ShadedTriVertex
struct ScreenTriVertex
{
float x, y, z, w;
float u, v;
float clipDistance[3];
float worldX, worldY, worldZ;
};
@ -45,10 +45,9 @@ struct ScreenTriangleStepVariables
struct TriDrawTriangleArgs
{
ShadedTriVertex *v1;
ShadedTriVertex *v2;
ShadedTriVertex *v3;
const PolyDrawArgs *uniforms;
ScreenTriVertex *v1;
ScreenTriVertex *v2;
ScreenTriVertex *v3;
ScreenTriangleStepVariables gradientX;
ScreenTriangleStepVariables gradientY;
@ -94,157 +93,25 @@ private:
}
};
class RectDrawArgs;
enum class TriBlendMode
{
Opaque,
Skycap,
FogBoundary,
SrcColor,
Fill,
Normal,
Fuzzy,
Stencil,
Translucent,
Add,
Shaded,
TranslucentStencil,
Shadow,
Subtract,
AddStencil,
AddShaded,
OpaqueTranslated,
SrcColorTranslated,
NormalTranslated,
StencilTranslated,
TranslucentTranslated,
AddTranslated,
ShadedTranslated,
TranslucentStencilTranslated,
ShadowTranslated,
SubtractTranslated,
AddStencilTranslated,
AddShadedTranslated
};
class ScreenTriangle
{
public:
static void Draw(const TriDrawTriangleArgs *args, PolyTriangleThreadData *thread);
static void Draw(const TriDrawTriangleArgs* args, PolyTriangleThreadData* thread);
static void(*TriangleDrawers[])(const TriDrawTriangleArgs *args, PolyTriangleThreadData *thread, int16_t *edges, int topY, int bottomY);
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);
static void(*RectDrawers8[])(const void *, int, int, int, const RectDrawArgs *, PolyTriangleThreadData *);
static void(*RectDrawers32[])(const void *, int, int, int, const RectDrawArgs *, PolyTriangleThreadData *);
static int FuzzStart;
private:
static void(*TestSpanOpts[])(int y, int x0, int x1, const TriDrawTriangleArgs* args, PolyTriangleThreadData* thread);
};
namespace TriScreenDrawerModes
{
enum SWStyleFlags
{
SWSTYLEF_Translated = 1,
SWSTYLEF_Skycap = 2,
SWSTYLEF_FogBoundary = 4,
SWSTYLEF_Fill = 8,
SWSTYLEF_SrcColorOneMinusSrcColor = 16
};
struct StyleOpaque { static const int BlendOp = STYLEOP_Add, BlendSrc = STYLEALPHA_One, BlendDest = STYLEALPHA_Zero, Flags = STYLEF_Alpha1, SWFlags = 0; };
struct StyleSkycap { static const int BlendOp = STYLEOP_Add, BlendSrc = STYLEALPHA_One, BlendDest = STYLEALPHA_Zero, Flags = STYLEF_Alpha1, SWFlags = SWSTYLEF_Skycap; };
struct StyleFogBoundary { static const int BlendOp = STYLEOP_Add, BlendSrc = STYLEALPHA_One, BlendDest = STYLEALPHA_Zero, Flags = STYLEF_Alpha1, SWFlags = SWSTYLEF_FogBoundary; };
struct StyleSrcColor { static const int BlendOp = STYLEOP_Add, BlendSrc = STYLEALPHA_Src, BlendDest = STYLEALPHA_InvSrc, Flags = STYLEF_Alpha1, SWFlags = SWSTYLEF_SrcColorOneMinusSrcColor; };
struct StyleFill { static const int BlendOp = STYLEOP_Add, BlendSrc = STYLEALPHA_One, BlendDest = STYLEALPHA_Zero, Flags = STYLEF_Alpha1, SWFlags = SWSTYLEF_Fill; };
struct StyleNormal { static const int BlendOp = STYLEOP_Add, BlendSrc = STYLEALPHA_Src, BlendDest = STYLEALPHA_InvSrc, Flags = STYLEF_Alpha1, SWFlags = 0; };
struct StyleFuzzy { static const int BlendOp = STYLEOP_Fuzz, BlendSrc = STYLEALPHA_Src, BlendDest = STYLEALPHA_InvSrc, Flags = 0, SWFlags = 0; };
struct StyleStencil { static const int BlendOp = STYLEOP_Add, BlendSrc = STYLEALPHA_Src, BlendDest = STYLEALPHA_InvSrc, Flags = STYLEF_Alpha1 | STYLEF_ColorIsFixed, SWFlags = 0; };
struct StyleTranslucent { static const int BlendOp = STYLEOP_Add, BlendSrc = STYLEALPHA_Src, BlendDest = STYLEALPHA_InvSrc, Flags = 0, SWFlags = 0; };
struct StyleAdd { static const int BlendOp = STYLEOP_Add, BlendSrc = STYLEALPHA_Src, BlendDest = STYLEALPHA_One, Flags = 0, SWFlags = 0; };
struct StyleShaded { static const int BlendOp = STYLEOP_Add, BlendSrc = STYLEALPHA_Src, BlendDest = STYLEALPHA_InvSrc, Flags = STYLEF_RedIsAlpha | STYLEF_ColorIsFixed, SWFlags = 0; };
struct StyleTranslucentStencil { static const int BlendOp = STYLEOP_Add, BlendSrc = STYLEALPHA_Src, BlendDest = STYLEALPHA_InvSrc, Flags = STYLEF_ColorIsFixed, SWFlags = 0; };
struct StyleShadow { static const int BlendOp = STYLEOP_Shadow, BlendSrc = STYLEALPHA_Src, BlendDest = STYLEALPHA_InvSrc, Flags = 0, SWFlags = 0; };
struct StyleSubtract { static const int BlendOp = STYLEOP_RevSub, BlendSrc = STYLEALPHA_Src, BlendDest = STYLEALPHA_One, Flags = 0, SWFlags = 0; };
struct StyleAddStencil { static const int BlendOp = STYLEOP_Add, BlendSrc = STYLEALPHA_Src, BlendDest = STYLEALPHA_One, Flags = STYLEF_ColorIsFixed, SWFlags = 0; };
struct StyleAddShaded { static const int BlendOp = STYLEOP_Add, BlendSrc = STYLEALPHA_Src, BlendDest = STYLEALPHA_One, Flags = STYLEF_RedIsAlpha | STYLEF_ColorIsFixed, SWFlags = 0; };
struct StyleOpaqueTranslated { static const int BlendOp = STYLEOP_Add, BlendSrc = STYLEALPHA_One, BlendDest = STYLEALPHA_Zero, Flags = STYLEF_Alpha1, SWFlags = SWSTYLEF_Translated; };
struct StyleSrcColorTranslated { static const int BlendOp = STYLEOP_Add, BlendSrc = STYLEALPHA_Src, BlendDest = STYLEALPHA_InvSrc, Flags = STYLEF_Alpha1, SWFlags = SWSTYLEF_Translated|SWSTYLEF_SrcColorOneMinusSrcColor; };
struct StyleNormalTranslated { static const int BlendOp = STYLEOP_Add, BlendSrc = STYLEALPHA_Src, BlendDest = STYLEALPHA_InvSrc, Flags = STYLEF_Alpha1, SWFlags = SWSTYLEF_Translated; };
struct StyleStencilTranslated { static const int BlendOp = STYLEOP_Add, BlendSrc = STYLEALPHA_Src, BlendDest = STYLEALPHA_InvSrc, Flags = STYLEF_Alpha1 | STYLEF_ColorIsFixed, SWFlags = SWSTYLEF_Translated; };
struct StyleTranslucentTranslated { static const int BlendOp = STYLEOP_Add, BlendSrc = STYLEALPHA_Src, BlendDest = STYLEALPHA_InvSrc, Flags = 0, SWFlags = SWSTYLEF_Translated; };
struct StyleAddTranslated { static const int BlendOp = STYLEOP_Add, BlendSrc = STYLEALPHA_Src, BlendDest = STYLEALPHA_One, Flags = 0, SWFlags = SWSTYLEF_Translated; };
struct StyleShadedTranslated { static const int BlendOp = STYLEOP_Add, BlendSrc = STYLEALPHA_Src, BlendDest = STYLEALPHA_InvSrc, Flags = STYLEF_RedIsAlpha | STYLEF_ColorIsFixed, SWFlags = SWSTYLEF_Translated; };
struct StyleTranslucentStencilTranslated { static const int BlendOp = STYLEOP_Add, BlendSrc = STYLEALPHA_Src, BlendDest = STYLEALPHA_InvSrc, Flags = STYLEF_ColorIsFixed, SWFlags = SWSTYLEF_Translated; };
struct StyleShadowTranslated { static const int BlendOp = STYLEOP_Shadow, BlendSrc = STYLEALPHA_Src, BlendDest = STYLEALPHA_InvSrc, Flags = 0, SWFlags = SWSTYLEF_Translated; };
struct StyleSubtractTranslated { static const int BlendOp = STYLEOP_RevSub, BlendSrc = STYLEALPHA_Src, BlendDest = STYLEALPHA_One, Flags = 0, SWFlags = SWSTYLEF_Translated; };
struct StyleAddStencilTranslated { static const int BlendOp = STYLEOP_Add, BlendSrc = STYLEALPHA_Src, BlendDest = STYLEALPHA_One, Flags = STYLEF_ColorIsFixed, SWFlags = SWSTYLEF_Translated; };
struct StyleAddShadedTranslated { static const int BlendOp = STYLEOP_Add, BlendSrc = STYLEALPHA_Src, BlendDest = STYLEALPHA_One, Flags = STYLEF_RedIsAlpha | STYLEF_ColorIsFixed, SWFlags = SWSTYLEF_Translated; };
enum SWOptFlags
{
SWOPT_DynLights = 1,
SWOPT_ColoredFog = 2,
SWOPT_FixedLight = 4
};
struct DrawerOpt { static const int Flags = 0; };
struct DrawerOptF { static const int Flags = SWOPT_FixedLight; };
struct DrawerOptC { static const int Flags = SWOPT_ColoredFog; };
struct DrawerOptCF { static const int Flags = SWOPT_ColoredFog | SWOPT_FixedLight; };
struct DrawerOptL { static const int Flags = SWOPT_DynLights; };
struct DrawerOptLC { static const int Flags = SWOPT_DynLights | SWOPT_ColoredFog; };
struct DrawerOptLF { static const int Flags = SWOPT_DynLights | SWOPT_FixedLight; };
struct DrawerOptLCF { static const int Flags = SWOPT_DynLights | SWOPT_ColoredFog | SWOPT_FixedLight; };
static const int fuzzcolormap[FUZZTABLE] =
{
6, 11, 6, 11, 6, 6, 11, 6, 6, 11,
6, 6, 6, 11, 6, 6, 6, 11, 15, 18,
21, 6, 11, 15, 6, 6, 6, 6, 11, 6,
11, 6, 6, 11, 15, 6, 6, 11, 15, 18,
21, 6, 6, 6, 6, 11, 6, 6, 11, 6,
};
enum SWTriangleFlags
enum SWTestSpan
{
SWTRI_DepthTest = 1,
SWTRI_StencilTest = 2,
SWTRI_WriteColor = 4,
SWTRI_WriteDepth = 8,
SWTRI_WriteStencil = 16
SWTRI_StencilTest = 2
};
struct TriangleOpt4 { static const int Flags = 4; };
struct TriangleOpt5 { static const int Flags = 5; };
struct TriangleOpt6 { static const int Flags = 6; };
struct TriangleOpt7 { static const int Flags = 7; };
struct TriangleOpt8 { static const int Flags = 8; };
struct TriangleOpt9 { static const int Flags = 9; };
struct TriangleOpt10 { static const int Flags = 10; };
struct TriangleOpt11 { static const int Flags = 11; };
struct TriangleOpt12 { static const int Flags = 12; };
struct TriangleOpt13 { static const int Flags = 13; };
struct TriangleOpt14 { static const int Flags = 14; };
struct TriangleOpt15 { static const int Flags = 15; };
struct TriangleOpt16 { static const int Flags = 16; };
struct TriangleOpt17 { static const int Flags = 17; };
struct TriangleOpt18 { static const int Flags = 18; };
struct TriangleOpt19 { static const int Flags = 19; };
struct TriangleOpt20 { static const int Flags = 20; };
struct TriangleOpt21 { static const int Flags = 21; };
struct TriangleOpt22 { static const int Flags = 22; };
struct TriangleOpt23 { static const int Flags = 23; };
struct TriangleOpt24 { static const int Flags = 24; };
struct TriangleOpt25 { static const int Flags = 25; };
struct TriangleOpt26 { static const int Flags = 26; };
struct TriangleOpt27 { static const int Flags = 27; };
struct TriangleOpt28 { static const int Flags = 28; };
struct TriangleOpt29 { static const int Flags = 29; };
struct TriangleOpt30 { static const int Flags = 30; };
struct TriangleOpt31 { static const int Flags = 31; };
struct TestSpanOpt0 { static const int Flags = 0; };
struct TestSpanOpt1 { static const int Flags = 1; };
struct TestSpanOpt2 { static const int Flags = 2; };
struct TestSpanOpt3 { static const int Flags = 3; };
}

View file

@ -42,7 +42,7 @@ Mat4f Mat4f::Identity()
return m;
}
Mat4f Mat4f::FromValues(float *matrix)
Mat4f Mat4f::FromValues(const float *matrix)
{
Mat4f m;
memcpy(m.Matrix, matrix, sizeof(m.Matrix));

View file

@ -115,7 +115,7 @@ class Mat4f
public:
static Mat4f Null();
static Mat4f Identity();
static Mat4f FromValues(float *matrix);
static Mat4f FromValues(const float *matrix);
static Mat4f Transpose(const Mat4f &matrix);
static Mat4f Translate(float x, float y, float z);
static Mat4f Scale(float x, float y, float z);

View file

@ -1,21 +1,4 @@
#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 "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

@ -52,6 +52,7 @@
#include "r_thread.h"
#include "swrenderer/scene/r_light.h"
#include "playsim/a_dynlight.h"
#include "polyrenderer/drawers/poly_triangle.h"
CVAR(Bool, r_dynlights, 1, CVAR_ARCHIVE | CVAR_GLOBALCONFIG);
CVAR(Bool, r_fuzzscale, 1, CVAR_ARCHIVE | CVAR_GLOBALCONFIG);
@ -471,9 +472,9 @@ namespace swrenderer
}
count = args.Count();
auto zbuffer = PolyZBuffer::Instance();
int pitch = PolyStencilBuffer::Instance()->Width();
float* values = zbuffer->Values() + y * pitch + x;
auto zbuffer = PolyTriangleThreadData::Get(thread)->depthstencil;
int pitch = zbuffer->Width();
float* values = zbuffer->DepthValues() + y * pitch + x;
int cnt = count;
values = thread->dest_for_thread(y, pitch, values);
@ -583,9 +584,9 @@ namespace swrenderer
void Execute(DrawerThread *thread) override
{
auto zbuffer = PolyZBuffer::Instance();
int pitch = PolyStencilBuffer::Instance()->Width();
float *values = zbuffer->Values() + y * pitch + x;
auto zbuffer = PolyTriangleThreadData::Get(thread)->depthstencil;
int pitch = zbuffer->Width();
float *values = zbuffer->DepthValues() + y * pitch + x;
int cnt = count;
values = thread->dest_for_thread(y, pitch, values);
@ -625,9 +626,9 @@ namespace swrenderer
if (thread->skipped_by_thread(y))
return;
auto zbuffer = PolyZBuffer::Instance();
int pitch = PolyStencilBuffer::Instance()->Width();
float *values = zbuffer->Values() + y * pitch;
auto zbuffer = PolyTriangleThreadData::Get(thread)->depthstencil;
int pitch = zbuffer->Width();
float *values = zbuffer->DepthValues() + y * pitch;
int end = x2;
if (idepth1 == idepth2)

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,41 +86,53 @@ SWSceneDrawer::~SWSceneDrawer()
sector_t *SWSceneDrawer::RenderView(player_t *player)
{
// Avoid using the pixel buffer from the last frame
FBTextureIndex = (FBTextureIndex + 1) % 2;
auto &fbtex = FBTexture[FBTextureIndex];
if (fbtex == nullptr || fbtex->GetSystemTexture() == nullptr ||
fbtex->GetDisplayWidth() != screen->GetWidth() ||
fbtex->GetDisplayHeight() != screen->GetHeight() ||
(V_IsTrueColor() ? 1:0) != fbtex->GetColorFormat())
if (!V_IsTrueColor() || !screen->IsPoly())
{
// This manually constructs its own material here.
fbtex.reset();
fbtex.reset(new FWrapperTexture(screen->GetWidth(), screen->GetHeight(), V_IsTrueColor()));
fbtex->GetSystemTexture()->AllocateBuffer(screen->GetWidth(), screen->GetHeight(), V_IsTrueColor() ? 4 : 1);
auto mat = FMaterial::ValidateTexture(fbtex.get(), false);
mat->AddTextureLayer(PaletteTexture);
// Avoid using the pixel buffer from the last frame
FBTextureIndex = (FBTextureIndex + 1) % 2;
auto &fbtex = FBTexture[FBTextureIndex];
Canvas.reset();
Canvas.reset(new DCanvas(screen->GetWidth(), screen->GetHeight(), V_IsTrueColor()));
if (fbtex == nullptr || fbtex->GetSystemTexture() == nullptr ||
fbtex->GetDisplayWidth() != screen->GetWidth() ||
fbtex->GetDisplayHeight() != screen->GetHeight() ||
(V_IsTrueColor() ? 1:0) != fbtex->GetColorFormat())
{
// This manually constructs its own material here.
fbtex.reset();
fbtex.reset(new FWrapperTexture(screen->GetWidth(), screen->GetHeight(), V_IsTrueColor()));
fbtex->GetSystemTexture()->AllocateBuffer(screen->GetWidth(), screen->GetHeight(), V_IsTrueColor() ? 4 : 1);
auto mat = FMaterial::ValidateTexture(fbtex.get(), false);
mat->AddTextureLayer(PaletteTexture);
Canvas.reset();
Canvas.reset(new DCanvas(screen->GetWidth(), screen->GetHeight(), V_IsTrueColor()));
}
IHardwareTexture *systemTexture = fbtex->GetSystemTexture();
auto buf = systemTexture->MapBuffer();
if (!buf) I_FatalError("Unable to map buffer for software rendering");
SWRenderer->RenderView(player, Canvas.get(), buf, systemTexture->GetBufferPitch());
systemTexture->CreateTexture(nullptr, screen->GetWidth(), screen->GetHeight(), 0, false, 0, "swbuffer");
auto map = swrenderer::CameraLight::Instance()->ShaderColormap();
screen->DrawTexture(fbtex.get(), 0, 0, DTA_SpecialColormap, map, TAG_DONE);
screen->Draw2D();
screen->Clear2D();
screen->PostProcessScene(CM_DEFAULT, [&]() {
SWRenderer->DrawRemainingPlayerSprites();
screen->Draw2D();
screen->Clear2D();
});
}
IHardwareTexture *systemTexture = fbtex->GetSystemTexture();
auto buf = systemTexture->MapBuffer();
if (!buf) I_FatalError("Unable to map buffer for software rendering");
SWRenderer->RenderView(player, Canvas.get(), buf, systemTexture->GetBufferPitch());
systemTexture->CreateTexture(nullptr, screen->GetWidth(), screen->GetHeight(), 0, false, 0, "swbuffer");
auto map = swrenderer::CameraLight::Instance()->ShaderColormap();
screen->DrawTexture(fbtex.get(), 0, 0, DTA_SpecialColormap, map, TAG_DONE);
screen->Draw2D();
screen->Clear2D();
screen->PostProcessScene(CM_DEFAULT, [&]() {
else
{
DCanvas *canvas = screen->GetCanvas();
SWRenderer->RenderView(player, canvas, canvas->GetPixels(), canvas->GetPitch());
// To do: apply swrenderer::CameraLight::Instance()->ShaderColormap();
SWRenderer->DrawRemainingPlayerSprites();
screen->Draw2D();
screen->Clear2D();
});
}
return r_viewpoint.sector;
}

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

@ -107,7 +107,12 @@ namespace swrenderer
r_modelscene = r_models && Models.Size() > 0;
if (r_modelscene)
{
PolyTriangleDrawer::ResizeBuffers(viewport->RenderTarget);
if (!DepthStencil || DepthStencil->Width() != viewport->RenderTarget->GetWidth() || DepthStencil->Height() != viewport->RenderTarget->GetHeight())
{
DepthStencil.reset();
DepthStencil.reset(new PolyDepthStencil(viewport->RenderTarget->GetWidth(), viewport->RenderTarget->GetHeight()));
}
PolyTriangleDrawer::SetViewport(MainThread()->DrawQueue, 0, 0, viewport->RenderTarget->GetWidth(), viewport->RenderTarget->GetHeight(), viewport->RenderTarget, DepthStencil.get());
PolyTriangleDrawer::ClearStencil(MainThread()->DrawQueue, 0);
}
@ -131,9 +136,12 @@ namespace swrenderer
RenderActorView(player->mo, true, false);
auto copyqueue = std::make_shared<DrawerCommandQueue>(MainThread()->FrameMemory.get());
copyqueue->Push<MemcpyCommand>(videobuffer, bufferpitch, target->GetPixels(), target->GetWidth(), target->GetHeight(), target->GetPitch(), target->IsBgra() ? 4 : 1);
DrawerThreads::Execute(copyqueue);
if (videobuffer != target->GetPixels())
{
auto copyqueue = std::make_shared<DrawerCommandQueue>(MainThread()->FrameMemory.get());
copyqueue->Push<MemcpyCommand>(videobuffer, bufferpitch, target->GetPixels(), target->GetWidth(), target->GetHeight(), target->GetPitch(), target->IsBgra() ? 4 : 1);
DrawerThreads::Execute(copyqueue);
}
DrawerWaitCycles.Clock();
DrawerThreads::WaitForWorkers();
@ -275,7 +283,8 @@ namespace swrenderer
if (r_modelscene && thread->MainThread)
PolyTriangleDrawer::ClearStencil(MainThread()->DrawQueue, 0);
PolyTriangleDrawer::SetViewport(thread->DrawQueue, viewwindowx, viewwindowy, viewwidth, viewheight, thread->Viewport->RenderTarget);
PolyTriangleDrawer::SetViewport(thread->DrawQueue, viewwindowx, viewwindowy, viewwidth, viewheight, thread->Viewport->RenderTarget, DepthStencil.get());
PolyTriangleDrawer::SetScissor(thread->DrawQueue, viewwindowx, viewwindowy, viewwidth, viewheight);
// Cull things outside the range seen by this thread
VisibleSegmentRenderer visitor;
@ -371,7 +380,13 @@ namespace swrenderer
viewactive = true;
viewport->SetViewport(actor->Level, MainThread(), width, height, MainThread()->Viewport->viewwindow.WidescreenRatio);
if (r_modelscene)
PolyTriangleDrawer::ResizeBuffers(viewport->RenderTarget);
{
if (!DepthStencil || DepthStencil->Width() != viewport->RenderTarget->GetWidth() || DepthStencil->Height() != viewport->RenderTarget->GetHeight())
{
DepthStencil.reset();
DepthStencil.reset(new PolyDepthStencil(viewport->RenderTarget->GetWidth(), viewport->RenderTarget->GetHeight()));
}
}
// Render:
RenderActorView(actor, false, dontmaplines);

View file

@ -32,6 +32,8 @@
extern cycle_t FrameCycles;
class PolyDepthStencil;
namespace swrenderer
{
extern cycle_t WallCycles, PlaneCycles, MaskedCycles, DrawerWaitCycles;
@ -67,6 +69,7 @@ namespace swrenderer
bool dontmaplines = false;
int clearcolor = 0;
std::unique_ptr<PolyDepthStencil> DepthStencil;
std::vector<std::unique_ptr<RenderThread>> Threads;
std::mutex start_mutex;
std::condition_variable start_condition;

View file

@ -74,6 +74,7 @@ namespace swrenderer
void RenderModel::Render(RenderThread *thread, short *cliptop, short *clipbottom, int minZ, int maxZ, Fake3DTranslucent clip3DFloor)
{
#if 0
SWModelRenderer renderer(thread, clip3DFloor, &WorldToClip, MirrorWorldToClip);
renderer.sector = actor->Sector;
@ -83,10 +84,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;
@ -94,6 +95,7 @@ namespace swrenderer
renderer.AddLights(actor);
renderer.RenderModel(x, y, z, smf, actor, r_viewpoint.TicFrac);
PolyTriangleDrawer::SetModelVertexShader(thread->DrawQueue, -1, -1, 0.0f);
#endif
}
/////////////////////////////////////////////////////////////////////////////
@ -116,6 +118,7 @@ namespace swrenderer
void RenderHUDModel(RenderThread *thread, DPSprite *psp, float ofsx, float ofsy)
{
#if 0
SWModelRenderer renderer(thread, Fake3DTranslucent(), &thread->Viewport->WorldToClip, false);
AActor *playermo = players[consoleplayer].camera;
@ -128,10 +131,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;
@ -141,13 +144,17 @@ namespace swrenderer
renderer.RenderHUDModel(psp, ofsx, ofsy);
PolyTriangleDrawer::SetModelVertexShader(thread->DrawQueue, -1, -1, 0.0f);
#endif
}
/////////////////////////////////////////////////////////////////////////////
#if 0
SWModelRenderer::SWModelRenderer(RenderThread *thread, Fake3DTranslucent clip3DFloor, Mat4f *worldToClip, bool mirrorWorldToClip)
: Thread(thread), Clip3DFloor(clip3DFloor), WorldToClip(worldToClip), MirrorWorldToClip(mirrorWorldToClip)
{
static PolySWInputAssembly input;
PolyTriangleDrawer::SetInputAssembly(thread->DrawQueue, &input);
}
void SWModelRenderer::AddLights(AActor *actor)
@ -354,7 +361,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);
@ -364,14 +372,16 @@ namespace swrenderer
args.SetClipPlane(1, ClipTop);
args.SetClipPlane(2, ClipBottom);
PolyTriangleDrawer::DrawArray(Thread->DrawQueue, args, VertexBuffer + start, count);
PolyTriangleDrawer::PushDrawArgs(Thread->DrawQueue, args);
PolyTriangleDrawer::Draw(Thread->DrawQueue, start, count);
}
void SWModelRenderer::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.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);
@ -381,7 +391,8 @@ namespace swrenderer
args.SetClipPlane(1, ClipTop);
args.SetClipPlane(2, ClipBottom);
PolyTriangleDrawer::DrawElements(Thread->DrawQueue, args, VertexBuffer, IndexBuffer + offset / sizeof(unsigned int), numIndices);
PolyTriangleDrawer::PushDrawArgs(Thread->DrawQueue, args);
PolyTriangleDrawer::DrawIndexed(Thread->DrawQueue, static_cast<int>(offset / sizeof(unsigned int)), numIndices);
}
/////////////////////////////////////////////////////////////////////////////
@ -417,8 +428,13 @@ namespace swrenderer
void SWModelVertexBuffer::SetupFrame(FModelRenderer *renderer, unsigned int frame1, unsigned int frame2, unsigned int size)
{
SWModelRenderer *swrenderer = (SWModelRenderer *)renderer;
swrenderer->VertexBuffer = mVertexBuffer.Size() ? &mVertexBuffer[0] : nullptr;
swrenderer->IndexBuffer = mIndexBuffer.Size() ? &mIndexBuffer[0] : nullptr;
if (mVertexBuffer.Size() > 0)
PolyTriangleDrawer::SetVertexBuffer(swrenderer->Thread->DrawQueue, &mVertexBuffer[0]);
if (mIndexBuffer.Size() > 0)
PolyTriangleDrawer::SetIndexBuffer(swrenderer->Thread->DrawQueue, &mIndexBuffer[0]);
PolyTriangleDrawer::SetModelVertexShader(swrenderer->Thread->DrawQueue, frame1, frame2, swrenderer->InterpolationFactor);
}
#endif
}

View file

@ -53,6 +53,7 @@ namespace swrenderer
bool MirrorWorldToClip;
};
#if 0
class SWModelRenderer : public FModelRenderer
{
public:
@ -90,8 +91,6 @@ namespace swrenderer
Mat4f ObjectToWorld;
PolyClipPlane ClipTop, ClipBottom;
FTexture *SkinTexture = nullptr;
unsigned int *IndexBuffer = nullptr;
FModelVertex *VertexBuffer = nullptr;
float InterpolationFactor = 0.0;
Mat4f *WorldToClip = nullptr;
bool MirrorWorldToClip = false;
@ -117,4 +116,5 @@ namespace swrenderer
TArray<FModelVertex> mVertexBuffer;
TArray<unsigned int> mIndexBuffer;
};
#endif
}

View file

@ -5,11 +5,14 @@
#include <memory>
#include "v_video.h"
#include "r_defs.h"
#include "r_utility.h"
#include "actorinlines.h"
#include "polyrenderer/math/gpu_types.h"
#define MINZ double((2048*4) / double(1 << 20))
class PolyDepthStencil;
namespace swrenderer
{
class RenderThread;

View file

@ -108,12 +108,26 @@ CUSTOM_CVAR(Int, vid_rendermode, 4, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOIN
// No further checks needed. All this changes now is which scene drawer the render backend calls.
}
CUSTOM_CVAR(Int, vid_enablevulkan, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL)
CUSTOM_CVAR(Int, vid_preferbackend, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL)
{
// [SP] This may seem pointless - but I don't want to implement live switching just
// yet - I'm pretty sure it's going to require a lot of reinits and destructions to
// do it right without memory leaks
switch(self)
{
case 2:
Printf("Selecting SoftPoly backend...\n");
break;
#ifdef HAVE_VULKAN
case 1:
Printf("Selecting Vulkan backend...\n");
break;
#endif
default:
Printf("Selecting OpenGL backend...\n");
}
Printf("Changing the video backend requires a restart for " GAMENAME ".\n");
}
@ -202,7 +216,7 @@ DCanvas::~DCanvas ()
//
//==========================================================================
void DCanvas::Resize(int width, int height)
void DCanvas::Resize(int width, int height, bool optimizepitch)
{
Width = width;
Height = height;
@ -213,7 +227,7 @@ void DCanvas::Resize(int width, int height)
// longer than the width. The values used here are all based on
// empirical evidence.
if (width <= 640)
if (width <= 640 || !optimizepitch)
{
// For low resolutions, just keep the pitch the same as the width.
// Some speedup can be seen using the technique below, but the speedup

View file

@ -326,7 +326,7 @@ class DCanvas
public:
DCanvas (int width, int height, bool bgra);
~DCanvas ();
void Resize(int width, int height);
void Resize(int width, int height, bool optimizepitch = true);
// Member variable access
inline uint8_t *GetPixels () const { return Pixels.Data(); }
@ -394,6 +394,9 @@ public:
virtual ~DFrameBuffer();
virtual void InitializeState() = 0; // For stuff that needs 'screen' set.
virtual bool IsVulkan() { return false; }
virtual bool IsPoly() { return false; }
virtual DCanvas* GetCanvas() { return nullptr; }
void SetSize(int width, int height);
void SetVirtualSize(int width, int height)

View file

@ -43,6 +43,7 @@
#include "m_argv.h"
#include "version.h"
#include "win32glvideo.h"
#include "win32polyvideo.h"
#ifdef HAVE_VULKAN
#include "win32vulkanvideo.h"
#endif
@ -50,7 +51,7 @@
#include "i_system.h"
#include "swrenderer/r_swrenderer.h"
EXTERN_CVAR(Int, vid_enablevulkan)
EXTERN_CVAR(Int, vid_preferbackend)
extern HWND Window;
@ -128,8 +129,12 @@ void I_InitGraphics ()
// are the active app. Huh?
}
if (vid_preferbackend == 2)
{
Video = new Win32PolyVideo();
}
#ifdef HAVE_VULKAN
if (vid_enablevulkan == 1)
else if (vid_preferbackend == 1)
{
// first try Vulkan, if that fails OpenGL
try
@ -142,12 +147,16 @@ void I_InitGraphics ()
Video = new Win32GLVideo();
}
}
else
#endif
else
{
Video = new Win32GLVideo();
}
if (Video == NULL)
Video = new Win32PolyVideo();
// we somehow STILL don't have a display!!
if (Video == NULL)
I_FatalError ("Failed to initialize display");

View file

@ -119,7 +119,7 @@ EXTERN_CVAR (Bool, queryiwad);
EXTERN_CVAR (Bool, disableautoload)
EXTERN_CVAR (Bool, autoloadlights)
EXTERN_CVAR (Bool, autoloadbrightmaps)
EXTERN_CVAR (Int, vid_enablevulkan)
EXTERN_CVAR (Int, vid_preferbackend)
extern HWND Window, ConWindow, GameTitleWindow;
extern HANDLE StdOut;
@ -589,7 +589,7 @@ BOOL CALLBACK IWADBoxCallback(HWND hDlg, UINT message, WPARAM wParam, LPARAM lPa
// Check the current video settings.
//SendDlgItemMessage( hDlg, vid_renderer ? IDC_WELCOME_OPENGL : IDC_WELCOME_SOFTWARE, BM_SETCHECK, BST_CHECKED, 0 );
SendDlgItemMessage( hDlg, IDC_WELCOME_FULLSCREEN, BM_SETCHECK, fullscreen ? BST_CHECKED : BST_UNCHECKED, 0 );
SendDlgItemMessage( hDlg, IDC_WELCOME_VULKAN, BM_SETCHECK, vid_enablevulkan ? BST_CHECKED : BST_UNCHECKED, 0 );
SendDlgItemMessage( hDlg, IDC_WELCOME_VULKAN, BM_SETCHECK, (vid_preferbackend == 1) ? BST_CHECKED : BST_UNCHECKED, 0 );
// [SP] This is our's
SendDlgItemMessage( hDlg, IDC_WELCOME_NOAUTOLOAD, BM_SETCHECK, disableautoload ? BST_CHECKED : BST_UNCHECKED, 0 );
@ -635,7 +635,8 @@ BOOL CALLBACK IWADBoxCallback(HWND hDlg, UINT message, WPARAM wParam, LPARAM lPa
SetQueryIWad(hDlg);
// [SP] Upstreamed from Zandronum
fullscreen = SendDlgItemMessage( hDlg, IDC_WELCOME_FULLSCREEN, BM_GETCHECK, 0, 0 ) == BST_CHECKED;
vid_enablevulkan = SendDlgItemMessage( hDlg, IDC_WELCOME_VULKAN, BM_GETCHECK, 0, 0 ) == BST_CHECKED;
if (SendDlgItemMessage(hDlg, IDC_WELCOME_VULKAN, BM_GETCHECK, 0, 0) == BST_CHECKED) vid_preferbackend = 1;
else if (SendDlgItemMessage(hDlg, IDC_WELCOME_VULKAN, BM_GETCHECK, 0, 0) != BST_CHECKED && vid_preferbackend == 1) vid_preferbackend = 0;
// [SP] This is our's.
disableautoload = SendDlgItemMessage( hDlg, IDC_WELCOME_NOAUTOLOAD, BM_GETCHECK, 0, 0 ) == BST_CHECKED;

View file

@ -0,0 +1,186 @@
#include <assert.h>
#include "hardware.h"
#include "doomerrors.h"
#include <Windows.h>
EXTERN_CVAR(Bool, vid_vsync)
extern HWND Window;
#include <d3d9.h>
#pragma comment(lib, "d3d9.lib")
namespace
{
int SrcWidth = 0;
int SrcHeight = 0;
int ClientWidth = 0;
int ClientHeight = 0;
bool CurrentVSync = false;
IDirect3D9Ex *d3d9 = nullptr;
IDirect3DDevice9Ex *device = nullptr;
IDirect3DSurface9* surface = nullptr;
}
void I_PolyPresentInit()
{
Direct3DCreate9Ex(D3D_SDK_VERSION, &d3d9);
if (!d3d9)
{
I_FatalError("Direct3DCreate9 failed");
}
RECT rect = {};
GetClientRect(Window, &rect);
ClientWidth = rect.right;
ClientHeight = rect.bottom;
D3DPRESENT_PARAMETERS pp = {};
pp.Windowed = true;
pp.SwapEffect = D3DSWAPEFFECT_FLIPEX;
pp.BackBufferWidth = ClientWidth;
pp.BackBufferHeight = ClientHeight;
pp.BackBufferCount = 1;
pp.hDeviceWindow = Window;
pp.PresentationInterval = CurrentVSync ? D3DPRESENT_INTERVAL_DEFAULT : D3DPRESENT_INTERVAL_IMMEDIATE;
HRESULT result = d3d9->CreateDeviceEx(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, Window, D3DCREATE_HARDWARE_VERTEXPROCESSING, &pp, nullptr, &device);
if (FAILED(result))
{
I_FatalError("IDirect3D9.CreateDevice failed");
}
}
uint8_t *I_PolyPresentLock(int w, int h, bool vsync, int &pitch)
{
HRESULT result;
RECT rect = {};
GetClientRect(Window, &rect);
if (rect.right != ClientWidth || rect.bottom != ClientHeight || CurrentVSync != vsync)
{
if (surface)
{
surface->Release();
surface = nullptr;
}
CurrentVSync = vsync;
ClientWidth = rect.right;
ClientHeight = rect.bottom;
D3DPRESENT_PARAMETERS pp = {};
pp.Windowed = true;
pp.SwapEffect = D3DSWAPEFFECT_FLIPEX;
pp.BackBufferWidth = ClientWidth;
pp.BackBufferHeight = ClientHeight;
pp.BackBufferCount = 1;
pp.hDeviceWindow = Window;
pp.PresentationInterval = CurrentVSync ? D3DPRESENT_INTERVAL_DEFAULT : D3DPRESENT_INTERVAL_IMMEDIATE;
device->Reset(&pp);
}
if (SrcWidth != w || SrcHeight != h || !surface)
{
if (surface)
{
surface->Release();
surface = nullptr;
}
SrcWidth = w;
SrcHeight = h;
result = device->CreateOffscreenPlainSurface(SrcWidth, SrcHeight, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &surface, 0);
if (FAILED(result))
{
I_FatalError("IDirect3DDevice9.CreateOffscreenPlainSurface failed");
}
}
D3DLOCKED_RECT lockrect = {};
result = surface->LockRect(&lockrect, nullptr, D3DLOCK_DISCARD);
if (FAILED(result))
{
pitch = 0;
return nullptr;
}
pitch = lockrect.Pitch;
return (uint8_t*)lockrect.pBits;
}
void I_PolyPresentUnlock(int x, int y, int width, int height)
{
surface->UnlockRect();
IDirect3DSurface9 *backbuffer = nullptr;
HRESULT result = device->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &backbuffer);
if (FAILED(result))
return;
result = device->BeginScene();
if (SUCCEEDED(result))
{
int count = 0;
D3DRECT clearrects[4];
if (y > 0)
{
clearrects[count].x1 = 0;
clearrects[count].y1 = 0;
clearrects[count].x2 = ClientWidth;
clearrects[count].y2 = y;
count++;
}
if (y + height < ClientHeight)
{
clearrects[count].x1 = 0;
clearrects[count].y1 = y + height;
clearrects[count].x2 = ClientWidth;
clearrects[count].y2 = ClientHeight;
count++;
}
if (x > 0)
{
clearrects[count].x1 = 0;
clearrects[count].y1 = y;
clearrects[count].x2 = x;
clearrects[count].y2 = y + height;
count++;
}
if (x + width < ClientWidth)
{
clearrects[count].x1 = x + width;
clearrects[count].y1 = y;
clearrects[count].x2 = ClientWidth;
clearrects[count].y2 = y + height;
count++;
}
if (count > 0)
device->Clear(count, clearrects, D3DCLEAR_TARGET, 0, 0.0f, 0);
RECT srcrect = {}, dstrect = {};
srcrect.right = SrcWidth;
srcrect.bottom = SrcHeight;
dstrect.left = x;
dstrect.top = y;
dstrect.right = x + width;
dstrect.bottom = y + height;
device->StretchRect(surface, &srcrect, backbuffer, &dstrect, D3DTEXF_LINEAR);
result = device->EndScene();
if (SUCCEEDED(result))
device->PresentEx(nullptr, nullptr, 0, nullptr, CurrentVSync ? 0 : D3DPRESENT_FORCEIMMEDIATE);
}
backbuffer->Release();
}
void I_PolyPresentDeinit()
{
if (surface) surface->Release();
if (device) device->Release();
if (d3d9) d3d9->Release();
}

View file

@ -0,0 +1,21 @@
#pragma once
#include "win32basevideo.h"
#include "c_cvars.h"
#include "rendering/polyrenderer/backend/poly_framebuffer.h"
EXTERN_CVAR(Bool, fullscreen)
class Win32PolyVideo : public Win32BaseVideo
{
public:
void Shutdown() override
{
}
DFrameBuffer *CreateFrameBuffer() override
{
auto fb = new PolyFrameBuffer(m_hMonitor, fullscreen);
return fb;
}
};

View file

@ -885,6 +885,12 @@ OptionValue GPUSwitch
2.0, "$OPTVAL_INTEGRATED"
}
OptionValue PreferBackend
{
0, "$OPTVAL_OPENGL"
1, "$OPTVAL_VULKAN"
2, "$OPTVAL_SOFTPOLY"
}
OptionMenu "TrueColorOptions" protected
{
@ -918,6 +924,8 @@ OptionMenu "VideoOptions" protected
Submenu "$GLMNU_TEXOPT", "GLTextureGLOptions"
Submenu "$GLMNU_DYNLIGHT", "GLLightOptions"
StaticText " "
Option "$DSPLYMNU_PREFERBACKEND", "vid_preferbackend", "PreferBackend"
StaticText " "
Slider "$DSPLYMNU_SCREENSIZE", "screenblocks", 3.0, 12.0, 1.0, 0
Slider "$DSPLYMNU_GAMMA", "Gamma", 0.75, 3.0, 0.05, 2
@ -2759,7 +2767,6 @@ OptionMenu "vkoptions"
StaticText "$VK_WARNING"
StaticText "$VK_RESTART"
StaticText ""
Option "$VKMNU_ENABLE", "vid_enablevulkan", "OnOff"
TextField "$VKMNU_DEVICE", vk_device
Option "$VKMNU_HDR", "vk_hdr", "OnOff"
}