mirror of
https://github.com/ZDoom/gzdoom.git
synced 2025-05-31 01:01:44 +00:00
- moved all rendering code into a common subdirectory.
No changes to the files themselves was made.
This commit is contained in:
parent
c77487dab8
commit
89d607c9a6
256 changed files with 193 additions and 191 deletions
82
src/rendering/hwrenderer/data/buffers.h
Normal file
82
src/rendering/hwrenderer/data/buffers.h
Normal file
|
@ -0,0 +1,82 @@
|
|||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include <assert.h>
|
||||
|
||||
// The low level code needs to know which attributes exist.
|
||||
// OpenGL needs to change the state of all of them per buffer binding.
|
||||
// VAOs are mostly useless for this because they lump buffer and binding state together which the model code does not want.
|
||||
enum
|
||||
{
|
||||
VATTR_VERTEX,
|
||||
VATTR_TEXCOORD,
|
||||
VATTR_COLOR,
|
||||
VATTR_VERTEX2,
|
||||
VATTR_NORMAL,
|
||||
VATTR_NORMAL2,
|
||||
|
||||
VATTR_MAX
|
||||
};
|
||||
|
||||
enum EVertexAttributeFormat
|
||||
{
|
||||
VFmt_Float4,
|
||||
VFmt_Float3,
|
||||
VFmt_Float2,
|
||||
VFmt_Float,
|
||||
VFmt_Byte4,
|
||||
VFmt_Packed_A2R10G10B10,
|
||||
};
|
||||
|
||||
struct FVertexBufferAttribute
|
||||
{
|
||||
int binding;
|
||||
int location;
|
||||
int format;
|
||||
int offset;
|
||||
};
|
||||
|
||||
class IBuffer
|
||||
{
|
||||
protected:
|
||||
size_t buffersize = 0;
|
||||
void *map = nullptr;
|
||||
public:
|
||||
IBuffer() = default;
|
||||
IBuffer(const IBuffer &) = delete;
|
||||
IBuffer &operator=(const IBuffer &) = delete;
|
||||
virtual ~IBuffer() = default;
|
||||
|
||||
virtual void SetData(size_t size, const void *data, bool staticdata = true) = 0;
|
||||
virtual void SetSubData(size_t offset, size_t size, const void *data) = 0;
|
||||
virtual void *Lock(unsigned int size) = 0;
|
||||
virtual void Unlock() = 0;
|
||||
virtual void Resize(size_t newsize) = 0;
|
||||
virtual void Map() {} // Only needed by old OpenGL but this needs to be in the interface.
|
||||
virtual void Unmap() {}
|
||||
void *Memory() { assert(map); return map; }
|
||||
size_t Size() { return buffersize; }
|
||||
};
|
||||
|
||||
class IVertexBuffer : virtual public IBuffer
|
||||
{
|
||||
public:
|
||||
virtual void SetFormat(int numBindingPoints, int numAttributes, size_t stride, const FVertexBufferAttribute *attrs) = 0;
|
||||
};
|
||||
|
||||
// This merely exists to have a dedicated type for index buffers to inherit from.
|
||||
class IIndexBuffer : virtual public IBuffer
|
||||
{
|
||||
// Element size is fixed to 4, thanks to OpenGL requiring this info to be coded into the glDrawElements call.
|
||||
// This mostly prohibits a more flexible buffer setup but GZDoom doesn't use any other format anyway.
|
||||
// Ob Vulkam, element size is a buffer property and of no concern to the drawing functions (as it should be.)
|
||||
};
|
||||
|
||||
class IDataBuffer : virtual public IBuffer
|
||||
{
|
||||
// Can be either uniform or shader storage buffer, depending on its needs.
|
||||
public:
|
||||
virtual void BindRange(size_t start, size_t length) = 0;
|
||||
virtual void BindBase() = 0;
|
||||
|
||||
};
|
400
src/rendering/hwrenderer/data/flatvertices.cpp
Normal file
400
src/rendering/hwrenderer/data/flatvertices.cpp
Normal file
|
@ -0,0 +1,400 @@
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Copyright(C) 2005-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/
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
/*
|
||||
** hw_flatvertices.cpp
|
||||
** Creates flat vertex data for hardware rendering.
|
||||
**
|
||||
**/
|
||||
|
||||
#include "doomtype.h"
|
||||
#include "p_local.h"
|
||||
#include "r_state.h"
|
||||
#include "c_cvars.h"
|
||||
#include "g_levellocals.h"
|
||||
#include "flatvertices.h"
|
||||
#include "cmdlib.h"
|
||||
#include "hwrenderer/data/buffers.h"
|
||||
#include "hwrenderer/scene/hw_renderstate.h"
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FFlatVertexBuffer::FFlatVertexBuffer(int width, int height)
|
||||
{
|
||||
vbo_shadowdata.Resize(NUM_RESERVED);
|
||||
|
||||
// the first quad is reserved for handling coordinates through uniforms.
|
||||
vbo_shadowdata[0].Set(0, 0, 0, 0, 0);
|
||||
vbo_shadowdata[1].Set(1, 0, 0, 0, 0);
|
||||
vbo_shadowdata[2].Set(2, 0, 0, 0, 0);
|
||||
vbo_shadowdata[3].Set(3, 0, 0, 0, 0);
|
||||
|
||||
// and the second one for the fullscreen quad used for blend overlays.
|
||||
vbo_shadowdata[4].Set(0, 0, 0, 0, 0);
|
||||
vbo_shadowdata[5].Set(0, (float)height, 0, 0, 1);
|
||||
vbo_shadowdata[6].Set((float)width, 0, 0, 1, 0);
|
||||
vbo_shadowdata[7].Set((float)width, (float)height, 0, 1, 1);
|
||||
|
||||
// and this is for the postprocessing copy operation
|
||||
vbo_shadowdata[8].Set(-1.0f, -1.0f, 0, 0.0f, 0.0f);
|
||||
vbo_shadowdata[9].Set(-1.0f, 1.0f, 0, 0.0f, 1.f);
|
||||
vbo_shadowdata[10].Set(1.0f, -1.0f, 0, 1.f, 0.0f);
|
||||
vbo_shadowdata[11].Set(1.0f, 1.0f, 0, 1.f, 1.f);
|
||||
|
||||
// The next two are the stencil caps.
|
||||
vbo_shadowdata[12].Set(-32767.0f, 32767.0f, -32767.0f, 0, 0);
|
||||
vbo_shadowdata[13].Set(-32767.0f, 32767.0f, 32767.0f, 0, 0);
|
||||
vbo_shadowdata[14].Set(32767.0f, 32767.0f, 32767.0f, 0, 0);
|
||||
vbo_shadowdata[15].Set(32767.0f, 32767.0f, -32767.0f, 0, 0);
|
||||
|
||||
vbo_shadowdata[16].Set(-32767.0f, -32767.0f, -32767.0f, 0, 0);
|
||||
vbo_shadowdata[17].Set(-32767.0f, -32767.0f, 32767.0f, 0, 0);
|
||||
vbo_shadowdata[18].Set(32767.0f, -32767.0f, 32767.0f, 0, 0);
|
||||
vbo_shadowdata[19].Set(32767.0f, -32767.0f, -32767.0f, 0, 0);
|
||||
|
||||
mVertexBuffer = screen->CreateVertexBuffer();
|
||||
mIndexBuffer = screen->CreateIndexBuffer();
|
||||
|
||||
unsigned int bytesize = BUFFER_SIZE * sizeof(FFlatVertex);
|
||||
mVertexBuffer->SetData(bytesize, nullptr, false);
|
||||
|
||||
static const FVertexBufferAttribute format[] = {
|
||||
{ 0, VATTR_VERTEX, VFmt_Float3, (int)myoffsetof(FFlatVertex, x) },
|
||||
{ 0, VATTR_TEXCOORD, VFmt_Float2, (int)myoffsetof(FFlatVertex, u) }
|
||||
};
|
||||
mVertexBuffer->SetFormat(1, 2, sizeof(FFlatVertex), format);
|
||||
|
||||
mIndex = mCurIndex = 0;
|
||||
mNumReserved = NUM_RESERVED;
|
||||
Copy(0, NUM_RESERVED);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FFlatVertexBuffer::~FFlatVertexBuffer()
|
||||
{
|
||||
delete mIndexBuffer;
|
||||
delete mVertexBuffer;
|
||||
mIndexBuffer = nullptr;
|
||||
mVertexBuffer = nullptr;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void FFlatVertexBuffer::OutputResized(int width, int height)
|
||||
{
|
||||
vbo_shadowdata[4].Set(0, 0, 0, 0, 0);
|
||||
vbo_shadowdata[5].Set(0, (float)height, 0, 0, 1);
|
||||
vbo_shadowdata[6].Set((float)width, 0, 0, 1, 0);
|
||||
vbo_shadowdata[7].Set((float)width, (float)height, 0, 1, 1);
|
||||
Copy(4, 4);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Initialize a single vertex
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void FFlatVertex::SetFlatVertex(vertex_t *vt, const secplane_t & plane)
|
||||
{
|
||||
x = (float)vt->fX();
|
||||
y = (float)vt->fY();
|
||||
z = (float)plane.ZatPoint(vt);
|
||||
u = (float)vt->fX()/64.f;
|
||||
v = -(float)vt->fY()/64.f;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Find a 3D floor
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
static F3DFloor *Find3DFloor(sector_t *target, sector_t *model)
|
||||
{
|
||||
for(unsigned i=0; i<target->e->XFloor.ffloors.Size(); i++)
|
||||
{
|
||||
F3DFloor *ffloor = target->e->XFloor.ffloors[i];
|
||||
if (ffloor->model == model && !(ffloor->flags & FF_THISINSIDE)) return ffloor;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Creates the vertices for one plane in one subsector
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
int FFlatVertexBuffer::CreateIndexedSectorVertices(sector_t *sec, const secplane_t &plane, int floor, VertexContainer &verts)
|
||||
{
|
||||
unsigned vi = vbo_shadowdata.Reserve(verts.vertices.Size());
|
||||
float diff;
|
||||
|
||||
// Create the actual vertices.
|
||||
if (sec->transdoor && floor) diff = -1.f;
|
||||
else diff = 0.f;
|
||||
for (unsigned i = 0; i < verts.vertices.Size(); i++)
|
||||
{
|
||||
vbo_shadowdata[vi + i].SetFlatVertex(verts.vertices[i].vertex, plane);
|
||||
vbo_shadowdata[vi + i].z += diff;
|
||||
}
|
||||
|
||||
unsigned rt = ibo_data.Reserve(verts.indices.Size());
|
||||
for (unsigned i = 0; i < verts.indices.Size(); i++)
|
||||
{
|
||||
ibo_data[rt + i] = vi + verts.indices[i];
|
||||
}
|
||||
return (int)rt;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
int FFlatVertexBuffer::CreateIndexedVertices(int h, sector_t *sec, const secplane_t &plane, int floor, VertexContainers &verts)
|
||||
{
|
||||
sec->vboindex[h] = vbo_shadowdata.Size();
|
||||
// First calculate the vertices for the sector itself
|
||||
sec->vboheight[h] = sec->GetPlaneTexZ(h);
|
||||
sec->ibocount = verts[sec->Index()].indices.Size();
|
||||
sec->iboindex[h] = CreateIndexedSectorVertices(sec, plane, floor, verts[sec->Index()]);
|
||||
|
||||
// Next are all sectors using this one as heightsec
|
||||
TArray<sector_t *> &fakes = sec->e->FakeFloor.Sectors;
|
||||
for (unsigned g = 0; g < fakes.Size(); g++)
|
||||
{
|
||||
sector_t *fsec = fakes[g];
|
||||
fsec->iboindex[2 + h] = CreateIndexedSectorVertices(fsec, plane, false, verts[fsec->Index()]);
|
||||
}
|
||||
|
||||
// and finally all attached 3D floors
|
||||
TArray<sector_t *> &xf = sec->e->XFloor.attached;
|
||||
for (unsigned g = 0; g < xf.Size(); g++)
|
||||
{
|
||||
sector_t *fsec = xf[g];
|
||||
F3DFloor *ffloor = Find3DFloor(fsec, sec);
|
||||
|
||||
if (ffloor != NULL && ffloor->flags & FF_RENDERPLANES)
|
||||
{
|
||||
bool dotop = (ffloor->top.model == sec) && (ffloor->top.isceiling == h);
|
||||
bool dobottom = (ffloor->bottom.model == sec) && (ffloor->bottom.isceiling == h);
|
||||
|
||||
if (dotop || dobottom)
|
||||
{
|
||||
auto ndx = CreateIndexedSectorVertices(fsec, plane, false, verts[fsec->Index()]);
|
||||
if (dotop) ffloor->top.vindex = ndx;
|
||||
if (dobottom) ffloor->bottom.vindex = ndx;
|
||||
}
|
||||
}
|
||||
}
|
||||
sec->vbocount[h] = vbo_shadowdata.Size() - sec->vboindex[h];
|
||||
return sec->iboindex[h];
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void FFlatVertexBuffer::CreateIndexedFlatVertices(TArray<sector_t> §ors)
|
||||
{
|
||||
auto verts = BuildVertices(sectors);
|
||||
|
||||
int i = 0;
|
||||
/*
|
||||
for (auto &vert : verts)
|
||||
{
|
||||
Printf(PRINT_LOG, "Sector %d\n", i);
|
||||
Printf(PRINT_LOG, "%d vertices, %d indices\n", vert.vertices.Size(), vert.indices.Size());
|
||||
int j = 0;
|
||||
for (auto &v : vert.vertices)
|
||||
{
|
||||
Printf(PRINT_LOG, " %d: (%2.3f, %2.3f)\n", j++, v.vertex->fX(), v.vertex->fY());
|
||||
}
|
||||
for (unsigned i=0;i<vert.indices.Size();i+=3)
|
||||
{
|
||||
Printf(PRINT_LOG, " %d, %d, %d\n", vert.indices[i], vert.indices[i + 1], vert.indices[i + 2]);
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
for (int h = sector_t::floor; h <= sector_t::ceiling; h++)
|
||||
{
|
||||
for (auto &sec : sectors)
|
||||
{
|
||||
CreateIndexedVertices(h, &sec, sec.GetSecPlane(h), h == sector_t::floor, verts);
|
||||
}
|
||||
}
|
||||
|
||||
// We need to do a final check for Vavoom water and FF_FIX sectors.
|
||||
// No new vertices are needed here. The planes come from the actual sector
|
||||
for (auto &sec : sectors)
|
||||
{
|
||||
for (auto ff : sec.e->XFloor.ffloors)
|
||||
{
|
||||
if (ff->top.model == &sec)
|
||||
{
|
||||
ff->top.vindex = sec.iboindex[ff->top.isceiling];
|
||||
}
|
||||
if (ff->bottom.model == &sec)
|
||||
{
|
||||
ff->bottom.vindex = sec.iboindex[ff->top.isceiling];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void FFlatVertexBuffer::UpdatePlaneVertices(sector_t *sec, int plane)
|
||||
{
|
||||
int startvt = sec->vboindex[plane];
|
||||
int countvt = sec->vbocount[plane];
|
||||
secplane_t &splane = sec->GetSecPlane(plane);
|
||||
FFlatVertex *vt = &vbo_shadowdata[startvt];
|
||||
FFlatVertex *mapvt = GetBuffer(startvt);
|
||||
for(int i=0; i<countvt; i++, vt++, mapvt++)
|
||||
{
|
||||
vt->z = (float)splane.ZatPoint(vt->x, vt->y);
|
||||
if (plane == sector_t::floor && sec->transdoor) vt->z -= 1;
|
||||
mapvt->z = vt->z;
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void FFlatVertexBuffer::CreateVertices(TArray<sector_t> §ors)
|
||||
{
|
||||
vbo_shadowdata.Resize(NUM_RESERVED);
|
||||
CreateIndexedFlatVertices(sectors);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void FFlatVertexBuffer::CheckPlanes(sector_t *sector)
|
||||
{
|
||||
if (sector->GetPlaneTexZ(sector_t::ceiling) != sector->vboheight[sector_t::ceiling])
|
||||
{
|
||||
UpdatePlaneVertices(sector, sector_t::ceiling);
|
||||
sector->vboheight[sector_t::ceiling] = sector->GetPlaneTexZ(sector_t::ceiling);
|
||||
}
|
||||
if (sector->GetPlaneTexZ(sector_t::floor) != sector->vboheight[sector_t::floor])
|
||||
{
|
||||
UpdatePlaneVertices(sector, sector_t::floor);
|
||||
sector->vboheight[sector_t::floor] = sector->GetPlaneTexZ(sector_t::floor);
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// checks the validity of all planes attached to this sector
|
||||
// and updates them if possible.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void FFlatVertexBuffer::CheckUpdate(sector_t *sector)
|
||||
{
|
||||
CheckPlanes(sector);
|
||||
sector_t *hs = sector->GetHeightSec();
|
||||
if (hs != NULL) CheckPlanes(hs);
|
||||
for (unsigned i = 0; i < sector->e->XFloor.ffloors.Size(); i++)
|
||||
CheckPlanes(sector->e->XFloor.ffloors[i]->model);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
std::pair<FFlatVertex *, unsigned int> FFlatVertexBuffer::AllocVertices(unsigned int count)
|
||||
{
|
||||
FFlatVertex *p = GetBuffer();
|
||||
auto index = mCurIndex.fetch_add(count);
|
||||
auto offset = index;
|
||||
if (index + count >= BUFFER_SIZE_TO_USE)
|
||||
{
|
||||
// If a single scene needs 2'000'000 vertices there must be something very wrong.
|
||||
I_FatalError("Out of vertex memory. Tried to allocate more than %u vertices for a single frame", index + count);
|
||||
}
|
||||
return std::make_pair(p, index);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void FFlatVertexBuffer::Copy(int start, int count)
|
||||
{
|
||||
Map();
|
||||
memcpy(GetBuffer(start), &vbo_shadowdata[0], count * sizeof(FFlatVertex));
|
||||
Unmap();
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void FFlatVertexBuffer::CreateVBO(TArray<sector_t> §ors)
|
||||
{
|
||||
vbo_shadowdata.Resize(mNumReserved);
|
||||
FFlatVertexBuffer::CreateVertices(sectors);
|
||||
mCurIndex = mIndex = vbo_shadowdata.Size();
|
||||
Copy(0, mIndex);
|
||||
mIndexBuffer->SetData(ibo_data.Size() * sizeof(uint32_t), &ibo_data[0]);
|
||||
}
|
133
src/rendering/hwrenderer/data/flatvertices.h
Normal file
133
src/rendering/hwrenderer/data/flatvertices.h
Normal file
|
@ -0,0 +1,133 @@
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Copyright(C) 2005-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/
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
|
||||
#ifndef _HW__VERTEXBUFFER_H
|
||||
#define _HW__VERTEXBUFFER_H
|
||||
|
||||
#include "tarray.h"
|
||||
#include "hwrenderer/data/buffers.h"
|
||||
#include "hw_vertexbuilder.h"
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
|
||||
class FRenderState;
|
||||
|
||||
struct FFlatVertex
|
||||
{
|
||||
float x, z, y; // world position
|
||||
float u, v; // texture coordinates
|
||||
|
||||
void SetFlatVertex(vertex_t *vt, const secplane_t &plane);
|
||||
void Set(float xx, float zz, float yy, float uu, float vv)
|
||||
{
|
||||
x = xx;
|
||||
z = zz;
|
||||
y = yy;
|
||||
u = uu;
|
||||
v = vv;
|
||||
}
|
||||
};
|
||||
|
||||
class FFlatVertexBuffer
|
||||
{
|
||||
TArray<FFlatVertex> vbo_shadowdata;
|
||||
TArray<uint32_t> ibo_data;
|
||||
|
||||
IVertexBuffer *mVertexBuffer;
|
||||
IIndexBuffer *mIndexBuffer;
|
||||
|
||||
unsigned int mIndex;
|
||||
std::atomic<unsigned int> mCurIndex;
|
||||
unsigned int mNumReserved;
|
||||
|
||||
|
||||
static const unsigned int BUFFER_SIZE = 2000000;
|
||||
static const unsigned int BUFFER_SIZE_TO_USE = 1999500;
|
||||
|
||||
public:
|
||||
enum
|
||||
{
|
||||
QUAD_INDEX = 0,
|
||||
FULLSCREEN_INDEX = 4,
|
||||
PRESENT_INDEX = 8,
|
||||
STENCILTOP_INDEX = 12,
|
||||
STENCILBOTTOM_INDEX = 16,
|
||||
|
||||
NUM_RESERVED = 20
|
||||
};
|
||||
|
||||
FFlatVertexBuffer(int width, int height);
|
||||
~FFlatVertexBuffer();
|
||||
|
||||
void OutputResized(int width, int height);
|
||||
std::pair<IVertexBuffer *, IIndexBuffer *> GetBufferObjects() const
|
||||
{
|
||||
return std::make_pair(mVertexBuffer, mIndexBuffer);
|
||||
}
|
||||
|
||||
void CreateVBO(TArray<sector_t> §ors);
|
||||
void Copy(int start, int count);
|
||||
|
||||
FFlatVertex *GetBuffer(int index) const
|
||||
{
|
||||
FFlatVertex *ff = (FFlatVertex*)mVertexBuffer->Memory();
|
||||
return &ff[index];
|
||||
}
|
||||
|
||||
FFlatVertex *GetBuffer() const
|
||||
{
|
||||
return GetBuffer(mCurIndex);
|
||||
}
|
||||
|
||||
std::pair<FFlatVertex *, unsigned int> AllocVertices(unsigned int count);
|
||||
|
||||
void Reset()
|
||||
{
|
||||
mCurIndex = mIndex;
|
||||
}
|
||||
|
||||
void Map()
|
||||
{
|
||||
mVertexBuffer->Map();
|
||||
}
|
||||
|
||||
void Unmap()
|
||||
{
|
||||
mVertexBuffer->Unmap();
|
||||
}
|
||||
|
||||
private:
|
||||
int CreateIndexedSectionVertices(subsector_t *sub, const secplane_t &plane, int floor, VertexContainer &cont);
|
||||
int CreateIndexedSectorVertices(sector_t *sec, const secplane_t &plane, int floor, VertexContainer &cont);
|
||||
int CreateIndexedVertices(int h, sector_t *sec, const secplane_t &plane, int floor, VertexContainers &cont);
|
||||
void CreateIndexedFlatVertices(TArray<sector_t> §ors);
|
||||
|
||||
void UpdatePlaneVertices(sector_t *sec, int plane);
|
||||
protected:
|
||||
void CreateVertices(TArray<sector_t> §ors);
|
||||
void CheckPlanes(sector_t *sector);
|
||||
public:
|
||||
void CheckUpdate(sector_t *sector);
|
||||
|
||||
};
|
||||
|
||||
#endif
|
148
src/rendering/hwrenderer/data/hw_vertexbuilder.cpp
Normal file
148
src/rendering/hwrenderer/data/hw_vertexbuilder.cpp
Normal file
|
@ -0,0 +1,148 @@
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Copyright(C) 2015-2018 Christoph Oelckers
|
||||
// All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with this program. If not, see http://www.gnu.org/licenses/
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
|
||||
|
||||
|
||||
#include "g_levellocals.h"
|
||||
#include "hw_vertexbuilder.h"
|
||||
#include "earcut.hpp"
|
||||
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
// Creates vertex meshes for sector planes
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
static void CreateVerticesForSubsector(subsector_t *sub, VertexContainer &gen, int qualifier)
|
||||
{
|
||||
if (sub->numlines < 3) return;
|
||||
|
||||
uint32_t startindex = gen.indices.Size();
|
||||
|
||||
if ((sub->flags & SSECF_HOLE) && sub->numlines > 3)
|
||||
{
|
||||
// Hole filling "subsectors" are not necessarily convex so they require real triangulation.
|
||||
// These things are extremely rare so performance is secondary here.
|
||||
|
||||
using Point = std::pair<double, double>;
|
||||
std::vector<std::vector<Point>> polygon;
|
||||
std::vector<Point> *curPoly;
|
||||
|
||||
polygon.resize(1);
|
||||
curPoly = &polygon.back();
|
||||
curPoly->resize(sub->numlines);
|
||||
|
||||
for (unsigned i = 0; i < sub->numlines; i++)
|
||||
{
|
||||
(*curPoly)[i] = { sub->firstline[i].v1->fX(), sub->firstline[i].v1->fY() };
|
||||
}
|
||||
auto indices = mapbox::earcut(polygon);
|
||||
for (auto vti : indices)
|
||||
{
|
||||
gen.AddIndexForVertex(sub->firstline[vti].v1, qualifier);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int firstndx = gen.AddVertex(sub->firstline[0].v1, qualifier);
|
||||
int secondndx = gen.AddVertex(sub->firstline[1].v1, qualifier);
|
||||
for (unsigned int k = 2; k < sub->numlines; k++)
|
||||
{
|
||||
gen.AddIndex(firstndx);
|
||||
gen.AddIndex(secondndx);
|
||||
auto ndx = gen.AddVertex(sub->firstline[k].v1, qualifier);
|
||||
gen.AddIndex(ndx);
|
||||
secondndx = ndx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
static void TriangulateSection(FSection §, VertexContainer &gen, int qualifier)
|
||||
{
|
||||
if (sect.segments.Size() < 3) return;
|
||||
|
||||
// todo
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
|
||||
static void CreateVerticesForSection(FSection §ion, VertexContainer &gen, bool useSubsectors)
|
||||
{
|
||||
section.vertexindex = gen.indices.Size();
|
||||
|
||||
if (useSubsectors)
|
||||
{
|
||||
for (auto sub : section.subsectors)
|
||||
{
|
||||
CreateVerticesForSubsector(sub, gen, -1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
TriangulateSection(section, gen, -1);
|
||||
}
|
||||
section.vertexcount = gen.indices.Size() - section.vertexindex;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Creates the vertices for one plane in one subsector
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
static void CreateVerticesForSector(sector_t *sec, VertexContainer &gen)
|
||||
{
|
||||
auto sections = sec->Level->sections.SectionsForSector(sec);
|
||||
for (auto §ion :sections)
|
||||
{
|
||||
CreateVerticesForSection( section, gen, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TArray<VertexContainer> BuildVertices(TArray<sector_t> §ors)
|
||||
{
|
||||
TArray<VertexContainer> verticesPerSector(sectors.Size(), true);
|
||||
for (unsigned i=0; i< sectors.Size(); i++)
|
||||
{
|
||||
CreateVerticesForSector(§ors[i], verticesPerSector[i]);
|
||||
}
|
||||
return verticesPerSector;
|
||||
}
|
69
src/rendering/hwrenderer/data/hw_vertexbuilder.h
Normal file
69
src/rendering/hwrenderer/data/hw_vertexbuilder.h
Normal file
|
@ -0,0 +1,69 @@
|
|||
|
||||
#include "tarray.h"
|
||||
struct vertex_t;
|
||||
|
||||
struct FQualifiedVertex
|
||||
{
|
||||
vertex_t *vertex; // index into vertex array
|
||||
int qualifier; // some index to prevent vertices in different groups from being merged together.
|
||||
};
|
||||
|
||||
template<> struct THashTraits<FQualifiedVertex>
|
||||
{
|
||||
hash_t Hash(const FQualifiedVertex &key)
|
||||
{
|
||||
return (int)(((intptr_t)key.vertex) >> 4) ^ (key.qualifier << 16);
|
||||
}
|
||||
int Compare(const FQualifiedVertex &left, const FQualifiedVertex &right) { return left.vertex != right.vertex || left.qualifier != right.qualifier; }
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
// One sector's vertex data.
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
struct VertexContainer
|
||||
{
|
||||
TArray<FQualifiedVertex> vertices;
|
||||
TMap<FQualifiedVertex, uint32_t> vertexmap;
|
||||
bool perSubsector = false;
|
||||
|
||||
TArray<uint32_t> indices;
|
||||
|
||||
uint32_t AddVertex(FQualifiedVertex *vert)
|
||||
{
|
||||
auto check = vertexmap.CheckKey(*vert);
|
||||
if (check != nullptr) return *check;
|
||||
auto index = vertices.Push(*vert);
|
||||
vertexmap[*vert] = index;
|
||||
return index;
|
||||
}
|
||||
|
||||
uint32_t AddVertex(vertex_t *vert, int qualifier)
|
||||
{
|
||||
FQualifiedVertex vertx = { vert, qualifier};
|
||||
return AddVertex(&vertx);
|
||||
}
|
||||
|
||||
uint32_t AddIndexForVertex(FQualifiedVertex *vert)
|
||||
{
|
||||
return indices.Push(AddVertex(vert));
|
||||
}
|
||||
|
||||
uint32_t AddIndexForVertex(vertex_t *vert, int qualifier)
|
||||
{
|
||||
return indices.Push(AddVertex(vert, qualifier));
|
||||
}
|
||||
|
||||
uint32_t AddIndex(uint32_t indx)
|
||||
{
|
||||
return indices.Push(indx);
|
||||
}
|
||||
};
|
||||
|
||||
using VertexContainers = TArray<VertexContainer>;
|
||||
|
||||
VertexContainers BuildVertices(TArray<sector_t> §ors);
|
||||
|
||||
|
110
src/rendering/hwrenderer/data/hw_viewpointbuffer.cpp
Normal file
110
src/rendering/hwrenderer/data/hw_viewpointbuffer.cpp
Normal file
|
@ -0,0 +1,110 @@
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Copyright(C) 2018 Christoph Oelckers
|
||||
// All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with this program. If not, see http://www.gnu.org/licenses/
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
/*
|
||||
** gl_viewpointbuffer.cpp
|
||||
** Buffer data maintenance for per viewpoint uniform data
|
||||
**
|
||||
**/
|
||||
|
||||
#include "hwrenderer/data/shaderuniforms.h"
|
||||
#include "hwrenderer/scene/hw_viewpointuniforms.h"
|
||||
#include "hwrenderer/scene/hw_drawinfo.h"
|
||||
#include "hwrenderer/scene/hw_renderstate.h"
|
||||
#include "hw_viewpointbuffer.h"
|
||||
|
||||
static const int INITIAL_BUFFER_SIZE = 100; // 100 viewpoints per frame should nearly always be enough
|
||||
|
||||
GLViewpointBuffer::GLViewpointBuffer()
|
||||
{
|
||||
mBufferSize = INITIAL_BUFFER_SIZE;
|
||||
mBlockAlign = ((sizeof(HWViewpointUniforms) / screen->uniformblockalignment) + 1) * screen->uniformblockalignment;
|
||||
mByteSize = mBufferSize * mBlockAlign;
|
||||
mBuffer = screen->CreateDataBuffer(VIEWPOINT_BINDINGPOINT, false);
|
||||
mBuffer->SetData(mByteSize, nullptr, false);
|
||||
Clear();
|
||||
mLastMappedIndex = UINT_MAX;
|
||||
mClipPlaneInfo.Push(0);
|
||||
}
|
||||
|
||||
GLViewpointBuffer::~GLViewpointBuffer()
|
||||
{
|
||||
delete mBuffer;
|
||||
}
|
||||
|
||||
|
||||
void GLViewpointBuffer::CheckSize()
|
||||
{
|
||||
if (mUploadIndex >= mBufferSize)
|
||||
{
|
||||
mBufferSize *= 2;
|
||||
mByteSize *= 2;
|
||||
mBuffer->Resize(mByteSize);
|
||||
}
|
||||
}
|
||||
|
||||
int GLViewpointBuffer::Bind(FRenderState &di, unsigned int index)
|
||||
{
|
||||
if (index != mLastMappedIndex)
|
||||
{
|
||||
mLastMappedIndex = index;
|
||||
mBuffer->BindRange(index * mBlockAlign, mBlockAlign);
|
||||
di.EnableClipDistance(0, mClipPlaneInfo[index]);
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
void GLViewpointBuffer::Set2D(FRenderState &di, int width, int height)
|
||||
{
|
||||
if (width != m2DWidth || height != m2DHeight)
|
||||
{
|
||||
HWViewpointUniforms matrices;
|
||||
matrices.SetDefaults();
|
||||
matrices.mProjectionMatrix.ortho(0, (float)width, (float)height, 0, -1.0f, 1.0f);
|
||||
matrices.CalcDependencies();
|
||||
mBuffer->Map();
|
||||
memcpy(mBuffer->Memory(), &matrices, sizeof(matrices));
|
||||
mBuffer->Unmap();
|
||||
m2DWidth = width;
|
||||
m2DHeight = height;
|
||||
mLastMappedIndex = -1;
|
||||
}
|
||||
Bind(di, 0);
|
||||
}
|
||||
|
||||
int GLViewpointBuffer::SetViewpoint(FRenderState &di, HWViewpointUniforms *vp)
|
||||
{
|
||||
CheckSize();
|
||||
mBuffer->Map();
|
||||
memcpy(((char*)mBuffer->Memory()) + mUploadIndex * mBlockAlign, vp, sizeof(*vp));
|
||||
mBuffer->Unmap();
|
||||
|
||||
mClipPlaneInfo.Push(vp->mClipHeightDirection != 0.f || vp->mClipLine.X > -10000000.0f);
|
||||
return Bind(di, mUploadIndex++);
|
||||
}
|
||||
|
||||
void GLViewpointBuffer::Clear()
|
||||
{
|
||||
// Index 0 is reserved for the 2D projection.
|
||||
mUploadIndex = 1;
|
||||
mClipPlaneInfo.Resize(1);
|
||||
}
|
||||
|
35
src/rendering/hwrenderer/data/hw_viewpointbuffer.h
Normal file
35
src/rendering/hwrenderer/data/hw_viewpointbuffer.h
Normal file
|
@ -0,0 +1,35 @@
|
|||
|
||||
#include "tarray.h"
|
||||
#include "hwrenderer/data/buffers.h"
|
||||
|
||||
struct HWViewpointUniforms;
|
||||
class FRenderState;
|
||||
|
||||
class GLViewpointBuffer
|
||||
{
|
||||
IDataBuffer *mBuffer;
|
||||
|
||||
unsigned int mBufferSize;
|
||||
unsigned int mBlockAlign;
|
||||
unsigned int mUploadIndex;
|
||||
unsigned int mLastMappedIndex;
|
||||
unsigned int mByteSize;
|
||||
TArray<bool> mClipPlaneInfo;
|
||||
|
||||
int m2DWidth = -1, m2DHeight = -1;
|
||||
|
||||
unsigned int mBlockSize;
|
||||
|
||||
void CheckSize();
|
||||
|
||||
public:
|
||||
|
||||
GLViewpointBuffer();
|
||||
~GLViewpointBuffer();
|
||||
void Clear();
|
||||
int Bind(FRenderState &di, unsigned int index);
|
||||
void Set2D(FRenderState &di, int width, int height);
|
||||
int SetViewpoint(FRenderState &di, HWViewpointUniforms *vp);
|
||||
unsigned int GetBlockSize() const { return mBlockSize; }
|
||||
};
|
||||
|
33
src/rendering/hwrenderer/data/renderqueue.h
Normal file
33
src/rendering/hwrenderer/data/renderqueue.h
Normal file
|
@ -0,0 +1,33 @@
|
|||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include "tflags.h"
|
||||
|
||||
// A render queue is what contains all render commands.
|
||||
// On Vulkan there can be several of them so this interface is needed to allow for the needed parallelism.
|
||||
// On OpenGL the render state is global so all this will do is to translate the system independent calls into OpenGL API calls.
|
||||
|
||||
enum class ColormaskBits
|
||||
{
|
||||
RED = 1,
|
||||
GREEN = 2,
|
||||
BLUE = 4,
|
||||
ALPHA = 8
|
||||
};
|
||||
|
||||
typedef TFlags<ColormaskBits, uint8_t> Colormask;
|
||||
|
||||
class IRenderQueue
|
||||
{
|
||||
Colormask mColorMask;
|
||||
|
||||
|
||||
Colormask GetColorMask() const
|
||||
{
|
||||
return mColorMask;
|
||||
}
|
||||
|
||||
virtual void SetColorMask(Colormask mask) = 0;
|
||||
|
||||
|
||||
};
|
145
src/rendering/hwrenderer/data/shaderuniforms.h
Normal file
145
src/rendering/hwrenderer/data/shaderuniforms.h
Normal file
|
@ -0,0 +1,145 @@
|
|||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include "hwrenderer/data/buffers.h"
|
||||
#include "v_video.h"
|
||||
|
||||
enum
|
||||
{
|
||||
LIGHTBUF_BINDINGPOINT = 1,
|
||||
POSTPROCESS_BINDINGPOINT = 2,
|
||||
VIEWPOINT_BINDINGPOINT = 3
|
||||
};
|
||||
|
||||
enum class UniformType
|
||||
{
|
||||
Int,
|
||||
UInt,
|
||||
Float,
|
||||
Vec2,
|
||||
Vec3,
|
||||
Vec4,
|
||||
IVec2,
|
||||
IVec3,
|
||||
IVec4,
|
||||
UVec2,
|
||||
UVec3,
|
||||
UVec4,
|
||||
Mat4
|
||||
};
|
||||
|
||||
class UniformFieldDesc
|
||||
{
|
||||
public:
|
||||
UniformFieldDesc() { }
|
||||
UniformFieldDesc(const char *name, UniformType type, std::size_t offset) : Name(name), Type(type), Offset(offset) { }
|
||||
|
||||
const char *Name;
|
||||
UniformType Type;
|
||||
std::size_t Offset;
|
||||
};
|
||||
|
||||
class UniformBlockDecl
|
||||
{
|
||||
public:
|
||||
static FString Create(const char *name, const std::vector<UniformFieldDesc> &fields, int bindingpoint)
|
||||
{
|
||||
FString decl;
|
||||
FString layout;
|
||||
if (screen->glslversion < 4.20)
|
||||
{
|
||||
layout = "std140";
|
||||
}
|
||||
else
|
||||
{
|
||||
layout.Format("std140, binding = %d", bindingpoint);
|
||||
}
|
||||
decl.Format("layout(%s) uniform %s\n{\n", layout.GetChars(), name);
|
||||
for (size_t i = 0; i < fields.size(); i++)
|
||||
{
|
||||
decl.AppendFormat("\t%s %s;\n", GetTypeStr(fields[i].Type), fields[i].Name);
|
||||
}
|
||||
decl += "};\n";
|
||||
|
||||
return decl;
|
||||
}
|
||||
|
||||
private:
|
||||
static const char *GetTypeStr(UniformType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
default:
|
||||
case UniformType::Int: return "int";
|
||||
case UniformType::UInt: return "uint";
|
||||
case UniformType::Float: return "float";
|
||||
case UniformType::Vec2: return "vec2";
|
||||
case UniformType::Vec3: return "vec3";
|
||||
case UniformType::Vec4: return "vec4";
|
||||
case UniformType::IVec2: return "ivec2";
|
||||
case UniformType::IVec3: return "ivec3";
|
||||
case UniformType::IVec4: return "ivec4";
|
||||
case UniformType::UVec2: return "uvec2";
|
||||
case UniformType::UVec3: return "uvec3";
|
||||
case UniformType::UVec4: return "uvec4";
|
||||
case UniformType::Mat4: return "mat4";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, int bindingpoint>
|
||||
class ShaderUniforms
|
||||
{
|
||||
public:
|
||||
ShaderUniforms()
|
||||
{
|
||||
memset(&Values, 0, sizeof(Values));
|
||||
}
|
||||
|
||||
~ShaderUniforms()
|
||||
{
|
||||
if (mBuffer != nullptr)
|
||||
delete mBuffer;
|
||||
}
|
||||
|
||||
int BindingPoint() const
|
||||
{
|
||||
return bindingpoint;
|
||||
}
|
||||
|
||||
FString CreateDeclaration(const char *name, const std::vector<UniformFieldDesc> &fields)
|
||||
{
|
||||
mFields = fields;
|
||||
return UniformBlockDecl::Create(name, fields, bindingpoint);
|
||||
}
|
||||
|
||||
void Init()
|
||||
{
|
||||
if (mBuffer == nullptr)
|
||||
mBuffer = screen->CreateDataBuffer(bindingpoint, false);
|
||||
}
|
||||
|
||||
void Set(bool bind = true)
|
||||
{
|
||||
if (mBuffer != nullptr)
|
||||
mBuffer->SetData(sizeof(T), &Values);
|
||||
|
||||
// Let's hope this can be done better when things have moved further ahead.
|
||||
// This really is not the best place to add something that depends on API behavior.
|
||||
if (bind) mBuffer->BindBase();
|
||||
}
|
||||
|
||||
T *operator->() { return &Values; }
|
||||
const T *operator->() const { return &Values; }
|
||||
|
||||
T Values;
|
||||
|
||||
private:
|
||||
ShaderUniforms(const ShaderUniforms &) = delete;
|
||||
ShaderUniforms &operator=(const ShaderUniforms &) = delete;
|
||||
|
||||
IDataBuffer *mBuffer = nullptr;
|
||||
std::vector<UniformFieldDesc> mFields;
|
||||
};
|
||||
|
||||
|
410
src/rendering/hwrenderer/dynlights/hw_aabbtree.cpp
Normal file
410
src/rendering/hwrenderer/dynlights/hw_aabbtree.cpp
Normal file
|
@ -0,0 +1,410 @@
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
// AABB-tree used for ray testing
|
||||
// Copyright(C) 2017 Magnus Norddahl
|
||||
// All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with this program. If not, see http://www.gnu.org/licenses/
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
|
||||
#include "r_state.h"
|
||||
#include "g_levellocals.h"
|
||||
#include "hw_aabbtree.h"
|
||||
|
||||
namespace hwrenderer
|
||||
{
|
||||
|
||||
LevelAABBTree::LevelAABBTree(FLevelLocals *lev)
|
||||
{
|
||||
Level = lev;
|
||||
// Calculate the center of all lines
|
||||
TArray<FVector2> centroids;
|
||||
for (unsigned int i = 0; i < Level->lines.Size(); i++)
|
||||
{
|
||||
FVector2 v1 = { (float)Level->lines[i].v1->fX(), (float)Level->lines[i].v1->fY() };
|
||||
FVector2 v2 = { (float)Level->lines[i].v2->fX(), (float)Level->lines[i].v2->fY() };
|
||||
centroids.Push((v1 + v2) * 0.5f);
|
||||
}
|
||||
|
||||
// Create the static subtree
|
||||
if (!GenerateTree(¢roids[0], false))
|
||||
return;
|
||||
|
||||
int staticroot = nodes.Size() - 1;
|
||||
|
||||
dynamicStartNode = nodes.Size();
|
||||
dynamicStartLine = treelines.Size();
|
||||
|
||||
// Create the dynamic subtree
|
||||
if (GenerateTree(¢roids[0], true))
|
||||
{
|
||||
int dynamicroot = nodes.Size() - 1;
|
||||
|
||||
// Create a shared root node
|
||||
FVector2 aabb_min, aabb_max;
|
||||
const auto &left = nodes[staticroot];
|
||||
const auto &right = nodes[dynamicroot];
|
||||
aabb_min.X = MIN(left.aabb_left, right.aabb_left);
|
||||
aabb_min.Y = MIN(left.aabb_top, right.aabb_top);
|
||||
aabb_max.X = MAX(left.aabb_right, right.aabb_right);
|
||||
aabb_max.Y = MAX(left.aabb_bottom, right.aabb_bottom);
|
||||
nodes.Push({ aabb_min, aabb_max, staticroot, dynamicroot });
|
||||
}
|
||||
|
||||
// Add the lines referenced by the leaf nodes
|
||||
treelines.Resize(mapLines.Size());
|
||||
for (unsigned int i = 0; i < mapLines.Size(); i++)
|
||||
{
|
||||
const auto &line = Level->lines[mapLines[i]];
|
||||
auto &treeline = treelines[i];
|
||||
|
||||
treeline.x = (float)line.v1->fX();
|
||||
treeline.y = (float)line.v1->fY();
|
||||
treeline.dx = (float)line.v2->fX() - treeline.x;
|
||||
treeline.dy = (float)line.v2->fY() - treeline.y;
|
||||
}
|
||||
}
|
||||
|
||||
bool LevelAABBTree::GenerateTree(const FVector2 *centroids, bool dynamicsubtree)
|
||||
{
|
||||
// Create a list of level lines we want to add:
|
||||
TArray<int> line_elements;
|
||||
auto &maplines = Level->lines;
|
||||
for (unsigned int i = 0; i < maplines.Size(); i++)
|
||||
{
|
||||
if (!maplines[i].backsector)
|
||||
{
|
||||
bool isPolyLine = maplines[i].sidedef[0] && (maplines[i].sidedef[0]->Flags & WALLF_POLYOBJ);
|
||||
if (isPolyLine && dynamicsubtree)
|
||||
{
|
||||
line_elements.Push(mapLines.Size());
|
||||
mapLines.Push(i);
|
||||
}
|
||||
else if (!isPolyLine && !dynamicsubtree)
|
||||
{
|
||||
line_elements.Push(mapLines.Size());
|
||||
mapLines.Push(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (line_elements.Size() == 0)
|
||||
return false;
|
||||
|
||||
// GenerateTreeNode needs a buffer where it can store line indices temporarily when sorting lines into the left and right child AABB buckets
|
||||
TArray<int> work_buffer;
|
||||
work_buffer.Resize(line_elements.Size() * 2);
|
||||
|
||||
// Generate the AABB tree
|
||||
GenerateTreeNode(&line_elements[0], (int)line_elements.Size(), centroids, &work_buffer[0]);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LevelAABBTree::Update()
|
||||
{
|
||||
bool modified = false;
|
||||
for (unsigned int i = dynamicStartLine; i < mapLines.Size(); i++)
|
||||
{
|
||||
const auto &line = Level->lines[mapLines[i]];
|
||||
|
||||
AABBTreeLine treeline;
|
||||
treeline.x = (float)line.v1->fX();
|
||||
treeline.y = (float)line.v1->fY();
|
||||
treeline.dx = (float)line.v2->fX() - treeline.x;
|
||||
treeline.dy = (float)line.v2->fY() - treeline.y;
|
||||
|
||||
if (memcmp(&treelines[i], &treeline, sizeof(AABBTreeLine)))
|
||||
{
|
||||
TArray<int> path = FindNodePath(i, nodes.Size() - 1);
|
||||
if (path.Size())
|
||||
{
|
||||
float x1 = (float)line.v1->fX();
|
||||
float y1 = (float)line.v1->fY();
|
||||
float x2 = (float)line.v2->fX();
|
||||
float y2 = (float)line.v2->fY();
|
||||
|
||||
int nodeIndex = path[0];
|
||||
nodes[nodeIndex].aabb_left = MIN(x1, x2);
|
||||
nodes[nodeIndex].aabb_right = MAX(x1, x2);
|
||||
nodes[nodeIndex].aabb_top = MIN(y1, y2);
|
||||
nodes[nodeIndex].aabb_bottom = MAX(y1, y2);
|
||||
|
||||
for (unsigned int j = 1; j < path.Size(); j++)
|
||||
{
|
||||
auto &cur = nodes[path[j]];
|
||||
const auto &left = nodes[cur.left_node];
|
||||
const auto &right = nodes[cur.right_node];
|
||||
cur.aabb_left = MIN(left.aabb_left, right.aabb_left);
|
||||
cur.aabb_top = MIN(left.aabb_top, right.aabb_top);
|
||||
cur.aabb_right = MAX(left.aabb_right, right.aabb_right);
|
||||
cur.aabb_bottom = MAX(left.aabb_bottom, right.aabb_bottom);
|
||||
}
|
||||
|
||||
treelines[i] = treeline;
|
||||
modified = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return modified;
|
||||
}
|
||||
|
||||
TArray<int> LevelAABBTree::FindNodePath(unsigned int line, unsigned int node)
|
||||
{
|
||||
const AABBTreeNode &n = nodes[node];
|
||||
|
||||
if (n.aabb_left > treelines[line].x || n.aabb_right < treelines[line].x ||
|
||||
n.aabb_top > treelines[line].y || n.aabb_bottom < treelines[line].y)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
TArray<int> path;
|
||||
if (n.line_index == -1)
|
||||
{
|
||||
path = FindNodePath(line, n.left_node);
|
||||
if (path.Size() == 0)
|
||||
path = FindNodePath(line, n.right_node);
|
||||
|
||||
if (path.Size())
|
||||
path.Push(node);
|
||||
}
|
||||
else if (n.line_index == (int)line)
|
||||
{
|
||||
path.Push(node);
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
double LevelAABBTree::RayTest(const DVector3 &ray_start, const DVector3 &ray_end)
|
||||
{
|
||||
// Precalculate some of the variables used by the ray/line intersection test
|
||||
DVector2 raydelta = ray_end - ray_start;
|
||||
double raydist2 = raydelta | raydelta;
|
||||
DVector2 raynormal = DVector2(raydelta.Y, -raydelta.X);
|
||||
double rayd = raynormal | ray_start;
|
||||
if (raydist2 < 1.0)
|
||||
return 1.0f;
|
||||
|
||||
double hit_fraction = 1.0;
|
||||
|
||||
// Walk the tree nodes
|
||||
int stack[32];
|
||||
int stack_pos = 1;
|
||||
stack[0] = nodes.Size() - 1; // root node is the last node in the list
|
||||
while (stack_pos > 0)
|
||||
{
|
||||
int node_index = stack[stack_pos - 1];
|
||||
|
||||
if (!OverlapRayAABB(ray_start, ray_end, nodes[node_index]))
|
||||
{
|
||||
// If the ray doesn't overlap this node's AABB we're done for this subtree
|
||||
stack_pos--;
|
||||
}
|
||||
else if (nodes[node_index].line_index != -1) // isLeaf(node_index)
|
||||
{
|
||||
// We reached a leaf node. Do a ray/line intersection test to see if we hit the line.
|
||||
hit_fraction = MIN(IntersectRayLine(ray_start, ray_end, nodes[node_index].line_index, raydelta, rayd, raydist2), hit_fraction);
|
||||
stack_pos--;
|
||||
}
|
||||
else if (stack_pos == 32)
|
||||
{
|
||||
stack_pos--; // stack overflow - tree is too deep!
|
||||
}
|
||||
else
|
||||
{
|
||||
// The ray overlaps the node's AABB. Examine its child nodes.
|
||||
stack[stack_pos - 1] = nodes[node_index].left_node;
|
||||
stack[stack_pos] = nodes[node_index].right_node;
|
||||
stack_pos++;
|
||||
}
|
||||
}
|
||||
|
||||
return hit_fraction;
|
||||
}
|
||||
|
||||
bool LevelAABBTree::OverlapRayAABB(const DVector2 &ray_start2d, const DVector2 &ray_end2d, const AABBTreeNode &node)
|
||||
{
|
||||
// To do: simplify test to use a 2D test
|
||||
DVector3 ray_start = DVector3(ray_start2d, 0.0);
|
||||
DVector3 ray_end = DVector3(ray_end2d, 0.0);
|
||||
DVector3 aabb_min = DVector3(node.aabb_left, node.aabb_top, -1.0);
|
||||
DVector3 aabb_max = DVector3(node.aabb_right, node.aabb_bottom, 1.0);
|
||||
|
||||
// Standard 3D ray/AABB overlapping test.
|
||||
// The details for the math here can be found in Real-Time Rendering, 3rd Edition.
|
||||
// We could use a 2D test here instead, which would probably simplify the math.
|
||||
|
||||
DVector3 c = (ray_start + ray_end) * 0.5f;
|
||||
DVector3 w = ray_end - c;
|
||||
DVector3 h = (aabb_max - aabb_min) * 0.5f; // aabb.extents();
|
||||
|
||||
c -= (aabb_max + aabb_min) * 0.5f; // aabb.center();
|
||||
|
||||
DVector3 v = DVector3(fabs(w.X), fabs(w.Y), fabs(w.Z));
|
||||
|
||||
if (fabs(c.X) > v.X + h.X || fabs(c.Y) > v.Y + h.Y || fabs(c.Z) > v.Z + h.Z)
|
||||
return false; // disjoint;
|
||||
|
||||
if (fabs(c.Y * w.Z - c.Z * w.Y) > h.Y * v.Z + h.Z * v.Y ||
|
||||
fabs(c.X * w.Z - c.Z * w.X) > h.X * v.Z + h.Z * v.X ||
|
||||
fabs(c.X * w.Y - c.Y * w.X) > h.X * v.Y + h.Y * v.X)
|
||||
return false; // disjoint;
|
||||
|
||||
return true; // overlap;
|
||||
}
|
||||
|
||||
double LevelAABBTree::IntersectRayLine(const DVector2 &ray_start, const DVector2 &ray_end, int line_index, const DVector2 &raydelta, double rayd, double raydist2)
|
||||
{
|
||||
// Check if two line segments intersects (the ray and the line).
|
||||
// The math below does this by first finding the fractional hit for an infinitely long ray line.
|
||||
// If that hit is within the line segment (0 to 1 range) then it calculates the fractional hit for where the ray would hit.
|
||||
//
|
||||
// This algorithm is homemade - I would not be surprised if there's a much faster method out there.
|
||||
|
||||
const double epsilon = 0.0000001;
|
||||
const AABBTreeLine &line = treelines[line_index];
|
||||
|
||||
DVector2 raynormal = DVector2(raydelta.Y, -raydelta.X);
|
||||
|
||||
DVector2 line_pos(line.x, line.y);
|
||||
DVector2 line_delta(line.dx, line.dy);
|
||||
|
||||
double den = raynormal | line_delta;
|
||||
if (fabs(den) > epsilon)
|
||||
{
|
||||
double t_line = (rayd - (raynormal | line_pos)) / den;
|
||||
if (t_line >= 0.0 && t_line <= 1.0)
|
||||
{
|
||||
DVector2 linehitdelta = line_pos + line_delta * t_line - ray_start;
|
||||
double t = (raydelta | linehitdelta) / raydist2;
|
||||
return t > 0.0 ? t : 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
int LevelAABBTree::GenerateTreeNode(int *lines, int num_lines, const FVector2 *centroids, int *work_buffer)
|
||||
{
|
||||
if (num_lines == 0)
|
||||
return -1;
|
||||
|
||||
// Find bounding box and median of the lines
|
||||
FVector2 median = FVector2(0.0f, 0.0f);
|
||||
FVector2 aabb_min, aabb_max;
|
||||
auto &maplines = Level->lines;
|
||||
aabb_min.X = (float)maplines[mapLines[lines[0]]].v1->fX();
|
||||
aabb_min.Y = (float)maplines[mapLines[lines[0]]].v1->fY();
|
||||
aabb_max = aabb_min;
|
||||
for (int i = 0; i < num_lines; i++)
|
||||
{
|
||||
float x1 = (float)maplines[mapLines[lines[i]]].v1->fX();
|
||||
float y1 = (float)maplines[mapLines[lines[i]]].v1->fY();
|
||||
float x2 = (float)maplines[mapLines[lines[i]]].v2->fX();
|
||||
float y2 = (float)maplines[mapLines[lines[i]]].v2->fY();
|
||||
|
||||
aabb_min.X = MIN(aabb_min.X, x1);
|
||||
aabb_min.X = MIN(aabb_min.X, x2);
|
||||
aabb_min.Y = MIN(aabb_min.Y, y1);
|
||||
aabb_min.Y = MIN(aabb_min.Y, y2);
|
||||
aabb_max.X = MAX(aabb_max.X, x1);
|
||||
aabb_max.X = MAX(aabb_max.X, x2);
|
||||
aabb_max.Y = MAX(aabb_max.Y, y1);
|
||||
aabb_max.Y = MAX(aabb_max.Y, y2);
|
||||
|
||||
median += centroids[mapLines[lines[i]]];
|
||||
}
|
||||
median /= (float)num_lines;
|
||||
|
||||
if (num_lines == 1) // Leaf node
|
||||
{
|
||||
nodes.Push(AABBTreeNode(aabb_min, aabb_max, lines[0]));
|
||||
return (int)nodes.Size() - 1;
|
||||
}
|
||||
|
||||
// Find the longest axis
|
||||
float axis_lengths[2] =
|
||||
{
|
||||
aabb_max.X - aabb_min.X,
|
||||
aabb_max.Y - aabb_min.Y
|
||||
};
|
||||
int axis_order[2] = { 0, 1 };
|
||||
FVector2 axis_plane[2] = { FVector2(1.0f, 0.0f), FVector2(0.0f, 1.0f) };
|
||||
std::sort(axis_order, axis_order + 2, [&](int a, int b) { return axis_lengths[a] > axis_lengths[b]; });
|
||||
|
||||
// Try sort at longest axis, then if that fails then the other one.
|
||||
// We place the sorted lines into work_buffer and then move the result back to the lines list when done.
|
||||
int left_count, right_count;
|
||||
for (int attempt = 0; attempt < 2; attempt++)
|
||||
{
|
||||
// Find the sort plane for axis
|
||||
FVector2 axis = axis_plane[axis_order[attempt]];
|
||||
FVector3 plane(axis, -(median | axis));
|
||||
|
||||
// Sort lines into two based ib whether the line center is on the front or back side of a plane
|
||||
left_count = 0;
|
||||
right_count = 0;
|
||||
for (int i = 0; i < num_lines; i++)
|
||||
{
|
||||
int line_index = lines[i];
|
||||
|
||||
float side = FVector3(centroids[mapLines[lines[i]]], 1.0f) | plane;
|
||||
if (side >= 0.0f)
|
||||
{
|
||||
work_buffer[left_count] = line_index;
|
||||
left_count++;
|
||||
}
|
||||
else
|
||||
{
|
||||
work_buffer[num_lines + right_count] = line_index;
|
||||
right_count++;
|
||||
}
|
||||
}
|
||||
|
||||
if (left_count != 0 && right_count != 0)
|
||||
break;
|
||||
}
|
||||
|
||||
// Check if something went wrong when sorting and do a random sort instead
|
||||
if (left_count == 0 || right_count == 0)
|
||||
{
|
||||
left_count = num_lines / 2;
|
||||
right_count = num_lines - left_count;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Move result back into lines list:
|
||||
for (int i = 0; i < left_count; i++)
|
||||
lines[i] = work_buffer[i];
|
||||
for (int i = 0; i < right_count; i++)
|
||||
lines[i + left_count] = work_buffer[num_lines + i];
|
||||
}
|
||||
|
||||
// Create child nodes:
|
||||
int left_index = -1;
|
||||
int right_index = -1;
|
||||
if (left_count > 0)
|
||||
left_index = GenerateTreeNode(lines, left_count, centroids, work_buffer);
|
||||
if (right_count > 0)
|
||||
right_index = GenerateTreeNode(lines + left_count, right_count, centroids, work_buffer);
|
||||
|
||||
// Store resulting node and return its index
|
||||
nodes.Push(AABBTreeNode(aabb_min, aabb_max, left_index, right_index));
|
||||
return (int)nodes.Size() - 1;
|
||||
}
|
||||
|
||||
|
||||
}
|
91
src/rendering/hwrenderer/dynlights/hw_aabbtree.h
Normal file
91
src/rendering/hwrenderer/dynlights/hw_aabbtree.h
Normal file
|
@ -0,0 +1,91 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "tarray.h"
|
||||
#include "vectors.h"
|
||||
|
||||
struct FLevelLocals;
|
||||
|
||||
namespace hwrenderer
|
||||
{
|
||||
|
||||
// Node in a binary AABB tree
|
||||
struct AABBTreeNode
|
||||
{
|
||||
AABBTreeNode(const FVector2 &aabb_min, const FVector2 &aabb_max, int line_index) : aabb_left(aabb_min.X), aabb_top(aabb_min.Y), aabb_right(aabb_max.X), aabb_bottom(aabb_max.Y), left_node(-1), right_node(-1), line_index(line_index) { }
|
||||
AABBTreeNode(const FVector2 &aabb_min, const FVector2 &aabb_max, int left, int right) : aabb_left(aabb_min.X), aabb_top(aabb_min.Y), aabb_right(aabb_max.X), aabb_bottom(aabb_max.Y), left_node(left), right_node(right), line_index(-1) { }
|
||||
|
||||
// Axis aligned bounding box for the node
|
||||
float aabb_left, aabb_top;
|
||||
float aabb_right, aabb_bottom;
|
||||
|
||||
// Child node indices
|
||||
int left_node;
|
||||
int right_node;
|
||||
|
||||
// AABBTreeLine index if it is a leaf node. Index is -1 if it is not.
|
||||
int line_index;
|
||||
|
||||
// Padding to keep 16-byte length (this structure is uploaded to the GPU)
|
||||
int padding;
|
||||
};
|
||||
|
||||
// Line segment for leaf nodes in an AABB tree
|
||||
struct AABBTreeLine
|
||||
{
|
||||
float x, y;
|
||||
float dx, dy;
|
||||
};
|
||||
|
||||
// Axis aligned bounding box tree used for ray testing treelines.
|
||||
class LevelAABBTree
|
||||
{
|
||||
public:
|
||||
// Constructs a tree for the current level
|
||||
LevelAABBTree(FLevelLocals *lev);
|
||||
|
||||
// Shoot a ray from ray_start to ray_end and return the closest hit as a fractional value between 0 and 1. Returns 1 if no line was hit.
|
||||
double RayTest(const DVector3 &ray_start, const DVector3 &ray_end);
|
||||
|
||||
bool Update();
|
||||
|
||||
const void *Nodes() const { return nodes.Data(); }
|
||||
const void *Lines() const { return treelines.Data(); }
|
||||
size_t NodesSize() const { return nodes.Size() * sizeof(AABBTreeNode); }
|
||||
size_t LinesSize() const { return treelines.Size() * sizeof(AABBTreeLine); }
|
||||
|
||||
const void *DynamicNodes() const { return nodes.Data() + dynamicStartNode; }
|
||||
const void *DynamicLines() const { return treelines.Data() + dynamicStartLine; }
|
||||
size_t DynamicNodesSize() const { return (nodes.Size() - dynamicStartNode) * sizeof(AABBTreeNode); }
|
||||
size_t DynamicLinesSize() const { return (treelines.Size() - dynamicStartLine) * sizeof(AABBTreeLine); }
|
||||
size_t DynamicNodesOffset() const { return dynamicStartNode * sizeof(AABBTreeNode); }
|
||||
size_t DynamicLinesOffset() const { return dynamicStartLine * sizeof(AABBTreeLine); }
|
||||
|
||||
private:
|
||||
bool GenerateTree(const FVector2 *centroids, bool dynamicsubtree);
|
||||
|
||||
// Test if a ray overlaps an AABB node or not
|
||||
bool OverlapRayAABB(const DVector2 &ray_start2d, const DVector2 &ray_end2d, const AABBTreeNode &node);
|
||||
|
||||
// Intersection test between a ray and a line segment
|
||||
double IntersectRayLine(const DVector2 &ray_start, const DVector2 &ray_end, int line_index, const DVector2 &raydelta, double rayd, double raydist2);
|
||||
|
||||
// Generate a tree node and its children recursively
|
||||
int GenerateTreeNode(int *treelines, int num_lines, const FVector2 *centroids, int *work_buffer);
|
||||
|
||||
TArray<int> FindNodePath(unsigned int line, unsigned int node);
|
||||
|
||||
// Nodes in the AABB tree. Last node is the root node.
|
||||
TArray<AABBTreeNode> nodes;
|
||||
|
||||
// Line segments for the leaf nodes in the tree.
|
||||
TArray<AABBTreeLine> treelines;
|
||||
|
||||
int dynamicStartNode = 0;
|
||||
int dynamicStartLine = 0;
|
||||
|
||||
TArray<int> mapLines;
|
||||
FLevelLocals *Level;
|
||||
};
|
||||
|
||||
} // namespace
|
150
src/rendering/hwrenderer/dynlights/hw_dynlightdata.cpp
Normal file
150
src/rendering/hwrenderer/dynlights/hw_dynlightdata.cpp
Normal file
|
@ -0,0 +1,150 @@
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Copyright(C) 2002-2018 Christoph Oelckers
|
||||
// All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with this program. If not, see http://www.gnu.org/licenses/
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
/*
|
||||
** gl_dynlight1.cpp
|
||||
** dynamic light application
|
||||
**
|
||||
**/
|
||||
|
||||
#include "actorinlines.h"
|
||||
|
||||
#include "hw_dynlightdata.h"
|
||||
|
||||
// If we want to share the array to avoid constant allocations it needs to be thread local unless it'd be littered with expensive synchronization.
|
||||
thread_local FDynLightData lightdata;
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Light related CVARs
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
// These shouldn't be called 'gl...' anymore...
|
||||
CVAR (Bool, gl_light_sprites, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG);
|
||||
CVAR (Bool, gl_light_particles, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG);
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Sets up the parameters to render one dynamic light onto one plane
|
||||
//
|
||||
//==========================================================================
|
||||
bool FDynLightData::GetLight(int group, Plane & p, FDynamicLight * light, bool checkside)
|
||||
{
|
||||
DVector3 pos = light->PosRelative(group);
|
||||
float radius = (light->GetRadius());
|
||||
|
||||
auto dist = fabs(p.DistToPoint((float)pos.X, (float)pos.Z, (float)pos.Y));
|
||||
|
||||
if (radius <= 0.f) return false;
|
||||
if (dist > radius) return false;
|
||||
if (checkside && p.PointOnSide((float)pos.X, (float)pos.Z, (float)pos.Y))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
AddLightToList(group, light, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Add one dynamic light to the light data list
|
||||
//
|
||||
//==========================================================================
|
||||
void FDynLightData::AddLightToList(int group, FDynamicLight * light, bool forceAttenuate)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
DVector3 pos = light->PosRelative(group);
|
||||
float radius = light->GetRadius();
|
||||
|
||||
float cs;
|
||||
if (light->IsAdditive())
|
||||
{
|
||||
cs = 0.2f;
|
||||
i = 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
cs = 1.0f;
|
||||
}
|
||||
|
||||
float r = light->GetRed() / 255.0f * cs;
|
||||
float g = light->GetGreen() / 255.0f * cs;
|
||||
float b = light->GetBlue() / 255.0f * cs;
|
||||
|
||||
if (light->IsSubtractive())
|
||||
{
|
||||
DVector3 v(r, g, b);
|
||||
float length = (float)v.Length();
|
||||
|
||||
r = length - r;
|
||||
g = length - g;
|
||||
b = length - b;
|
||||
i = 1;
|
||||
}
|
||||
|
||||
float shadowIndex = light->mShadowmapIndex + 1.0f;
|
||||
|
||||
// Store attenuate flag in the sign bit of the float.
|
||||
if (light->IsAttenuated() || forceAttenuate) shadowIndex = -shadowIndex;
|
||||
|
||||
float lightType = 0.0f;
|
||||
float spotInnerAngle = 0.0f;
|
||||
float spotOuterAngle = 0.0f;
|
||||
float spotDirX = 0.0f;
|
||||
float spotDirY = 0.0f;
|
||||
float spotDirZ = 0.0f;
|
||||
if (light->IsSpot())
|
||||
{
|
||||
lightType = 1.0f;
|
||||
spotInnerAngle = (float)light->pSpotInnerAngle->Cos();
|
||||
spotOuterAngle = (float)light->pSpotOuterAngle->Cos();
|
||||
|
||||
DAngle negPitch = -*light->pPitch;
|
||||
DAngle Angle = light->target->Angles.Yaw;
|
||||
double xzLen = negPitch.Cos();
|
||||
spotDirX = float(-Angle.Cos() * xzLen);
|
||||
spotDirY = float(-negPitch.Sin());
|
||||
spotDirZ = float(-Angle.Sin() * xzLen);
|
||||
}
|
||||
|
||||
float *data = &arrays[i][arrays[i].Reserve(16)];
|
||||
data[0] = float(pos.X);
|
||||
data[1] = float(pos.Z);
|
||||
data[2] = float(pos.Y);
|
||||
data[3] = radius;
|
||||
data[4] = r;
|
||||
data[5] = g;
|
||||
data[6] = b;
|
||||
data[7] = shadowIndex;
|
||||
data[8] = spotDirX;
|
||||
data[9] = spotDirY;
|
||||
data[10] = spotDirZ;
|
||||
data[11] = lightType;
|
||||
data[12] = spotInnerAngle;
|
||||
data[13] = spotOuterAngle;
|
||||
data[14] = 0.0f; // unused
|
||||
data[15] = 0.0f; // unused
|
||||
}
|
||||
|
64
src/rendering/hwrenderer/dynlights/hw_dynlightdata.h
Normal file
64
src/rendering/hwrenderer/dynlights/hw_dynlightdata.h
Normal file
|
@ -0,0 +1,64 @@
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Copyright(C) 2005-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/
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
|
||||
#ifndef __GLC_DYNLIGHT_H
|
||||
#define __GLC_DYNLIGHT_H
|
||||
|
||||
#include "a_dynlight.h"
|
||||
|
||||
|
||||
struct FDynLightData
|
||||
{
|
||||
TArray<float> arrays[3];
|
||||
|
||||
void Clear()
|
||||
{
|
||||
arrays[0].Clear();
|
||||
arrays[1].Clear();
|
||||
arrays[2].Clear();
|
||||
}
|
||||
|
||||
void Combine(int *siz, int max)
|
||||
{
|
||||
siz[0] = arrays[0].Size();
|
||||
siz[1] = siz[0] + arrays[1].Size();
|
||||
siz[2] = siz[1] + arrays[2].Size();
|
||||
arrays[0].Resize(arrays[0].Size() + arrays[1].Size() + arrays[2].Size());
|
||||
memcpy(&arrays[0][siz[0]], &arrays[1][0], arrays[1].Size() * sizeof(float));
|
||||
memcpy(&arrays[0][siz[1]], &arrays[2][0], arrays[2].Size() * sizeof(float));
|
||||
siz[0]>>=2;
|
||||
siz[1]>>=2;
|
||||
siz[2]>>=2;
|
||||
if (siz[0] > max) siz[0] = max;
|
||||
if (siz[1] > max) siz[1] = max;
|
||||
if (siz[2] > max) siz[2] = max;
|
||||
}
|
||||
|
||||
bool GetLight(int group, Plane & p, FDynamicLight * light, bool checkside);
|
||||
void AddLightToList(int group, FDynamicLight * light, bool forceAttenuate);
|
||||
|
||||
};
|
||||
|
||||
extern thread_local FDynLightData lightdata;
|
||||
|
||||
|
||||
#endif
|
144
src/rendering/hwrenderer/dynlights/hw_lightbuffer.cpp
Normal file
144
src/rendering/hwrenderer/dynlights/hw_lightbuffer.cpp
Normal file
|
@ -0,0 +1,144 @@
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Copyright(C) 2014-2016 Christoph Oelckers
|
||||
// All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with this program. If not, see http://www.gnu.org/licenses/
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
/*
|
||||
** gl_lightbuffer.cpp
|
||||
** Buffer data maintenance for dynamic lights
|
||||
**
|
||||
**/
|
||||
|
||||
#include "hw_lightbuffer.h"
|
||||
#include "hwrenderer/utility/hw_clock.h"
|
||||
#include "hwrenderer/dynlights/hw_dynlightdata.h"
|
||||
#include "hwrenderer/data/shaderuniforms.h"
|
||||
|
||||
static const int ELEMENTS_PER_LIGHT = 4; // each light needs 4 vec4's.
|
||||
static const int ELEMENT_SIZE = (4*sizeof(float));
|
||||
|
||||
|
||||
FLightBuffer::FLightBuffer()
|
||||
{
|
||||
int maxNumberOfLights = 80000;
|
||||
|
||||
mBufferSize = maxNumberOfLights * ELEMENTS_PER_LIGHT;
|
||||
mByteSize = mBufferSize * ELEMENT_SIZE;
|
||||
|
||||
// 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->hwcaps & RFL_SHADER_STORAGE_BUFFER && !strstr(screen->gl_vendorstring, "Intel"))
|
||||
{
|
||||
mBufferType = true;
|
||||
mBlockAlign = 0;
|
||||
mBlockSize = mBufferSize;
|
||||
mMaxUploadSize = mBlockSize;
|
||||
}
|
||||
else
|
||||
{
|
||||
mBufferType = false;
|
||||
mBlockSize = screen->maxuniformblock / ELEMENT_SIZE;
|
||||
mBlockAlign = screen->uniformblockalignment / ELEMENT_SIZE;
|
||||
mMaxUploadSize = (mBlockSize - mBlockAlign);
|
||||
mByteSize += screen->maxuniformblock; // to avoid mapping beyond the end of the buffer.
|
||||
}
|
||||
|
||||
mBuffer = screen->CreateDataBuffer(LIGHTBUF_BINDINGPOINT, mBufferType);
|
||||
mBuffer->SetData(mByteSize, nullptr, false);
|
||||
|
||||
Clear();
|
||||
}
|
||||
|
||||
FLightBuffer::~FLightBuffer()
|
||||
{
|
||||
delete mBuffer;
|
||||
}
|
||||
|
||||
void FLightBuffer::Clear()
|
||||
{
|
||||
mIndex = 0;
|
||||
mLastMappedIndex = UINT_MAX;
|
||||
}
|
||||
|
||||
int FLightBuffer::UploadLights(FDynLightData &data)
|
||||
{
|
||||
// All meaasurements here are in vec4's.
|
||||
int size0 = data.arrays[0].Size()/4;
|
||||
int size1 = data.arrays[1].Size()/4;
|
||||
int size2 = data.arrays[2].Size()/4;
|
||||
int totalsize = size0 + size1 + size2 + 1;
|
||||
|
||||
if (totalsize > (int)mMaxUploadSize)
|
||||
{
|
||||
int diff = totalsize - (int)mMaxUploadSize;
|
||||
|
||||
size2 -= diff;
|
||||
if (size2 < 0)
|
||||
{
|
||||
size1 += size2;
|
||||
size2 = 0;
|
||||
}
|
||||
if (size1 < 0)
|
||||
{
|
||||
size0 += size1;
|
||||
size1 = 0;
|
||||
}
|
||||
totalsize = size0 + size1 + size2 + 1;
|
||||
}
|
||||
|
||||
float *mBufferPointer = (float*)mBuffer->Memory();
|
||||
assert(mBufferPointer != nullptr);
|
||||
if (mBufferPointer == nullptr) return -1;
|
||||
if (totalsize <= 1) return -1; // there are no lights
|
||||
|
||||
unsigned thisindex = mIndex.fetch_add(totalsize);
|
||||
float parmcnt[] = { 0, float(size0), float(size0 + size1), float(size0 + size1 + size2) };
|
||||
|
||||
if (thisindex + totalsize <= mBufferSize)
|
||||
{
|
||||
float *copyptr = mBufferPointer + thisindex*4;
|
||||
|
||||
memcpy(©ptr[0], parmcnt, ELEMENT_SIZE);
|
||||
memcpy(©ptr[4], &data.arrays[0][0], size0 * ELEMENT_SIZE);
|
||||
memcpy(©ptr[4 + 4*size0], &data.arrays[1][0], size1 * ELEMENT_SIZE);
|
||||
memcpy(©ptr[4 + 4*(size0 + size1)], &data.arrays[2][0], size2 * ELEMENT_SIZE);
|
||||
return thisindex;
|
||||
}
|
||||
else
|
||||
{
|
||||
return -1; // Buffer is full. Since it is being used live at the point of the upload we cannot do much here but to abort.
|
||||
}
|
||||
}
|
||||
|
||||
int FLightBuffer::DoBindUBO(unsigned int index)
|
||||
{
|
||||
// this function will only get called if a uniform buffer is used. For a shader storage buffer we only need to bind the buffer once at the start.
|
||||
unsigned int offset = (index / mBlockAlign) * mBlockAlign;
|
||||
|
||||
if (offset != mLastMappedIndex)
|
||||
{
|
||||
mLastMappedIndex = offset;
|
||||
mBuffer->BindRange(offset * ELEMENT_SIZE, mBlockSize * ELEMENT_SIZE);
|
||||
}
|
||||
return (index - offset);
|
||||
}
|
||||
|
||||
|
||||
|
69
src/rendering/hwrenderer/dynlights/hw_lightbuffer.h
Normal file
69
src/rendering/hwrenderer/dynlights/hw_lightbuffer.h
Normal file
|
@ -0,0 +1,69 @@
|
|||
#ifndef __GL_LIGHTBUFFER_H
|
||||
#define __GL_LIGHTBUFFER_H
|
||||
|
||||
#include "tarray.h"
|
||||
#include "hwrenderer/dynlights/hw_dynlightdata.h"
|
||||
#include "hwrenderer/data/buffers.h"
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
|
||||
class FRenderState;
|
||||
|
||||
class FLightBuffer
|
||||
{
|
||||
IDataBuffer *mBuffer;
|
||||
|
||||
bool mBufferType;
|
||||
std::atomic<unsigned int> mIndex;
|
||||
unsigned int mLastMappedIndex;
|
||||
unsigned int mBlockAlign;
|
||||
unsigned int mBlockSize;
|
||||
unsigned int mBufferSize;
|
||||
unsigned int mByteSize;
|
||||
unsigned int mMaxUploadSize;
|
||||
|
||||
void CheckSize();
|
||||
|
||||
public:
|
||||
|
||||
FLightBuffer();
|
||||
~FLightBuffer();
|
||||
void Clear();
|
||||
int UploadLights(FDynLightData &data);
|
||||
void Map() { mBuffer->Map(); }
|
||||
void Unmap() { mBuffer->Unmap(); }
|
||||
unsigned int GetBlockSize() const { return mBlockSize; }
|
||||
bool GetBufferType() const { return mBufferType; }
|
||||
|
||||
int DoBindUBO(unsigned int index);
|
||||
|
||||
int ShaderIndex(unsigned int index) const
|
||||
{
|
||||
if (mBlockAlign == 0) return index;
|
||||
// This must match the math in BindUBO.
|
||||
unsigned int offset = (index / mBlockAlign) * mBlockAlign;
|
||||
return int(index-offset);
|
||||
}
|
||||
|
||||
// Only relevant for OpenGL, so this does not need access to the render state.
|
||||
int BindUBO(int index)
|
||||
{
|
||||
if (!mBufferType && index > -1)
|
||||
{
|
||||
index = DoBindUBO(index);
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
// The parameter is a reminder for Vulkan.
|
||||
void BindBase()
|
||||
{
|
||||
mBuffer->BindBase();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
int gl_SetDynModelLight(AActor *self, int dynlightindex);
|
||||
|
||||
#endif
|
||||
|
220
src/rendering/hwrenderer/dynlights/hw_shadowmap.cpp
Normal file
220
src/rendering/hwrenderer/dynlights/hw_shadowmap.cpp
Normal file
|
@ -0,0 +1,220 @@
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
// 1D dynamic shadow maps (API independent part)
|
||||
// Copyright(C) 2017 Magnus Norddahl
|
||||
// All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with this program. If not, see http://www.gnu.org/licenses/
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
|
||||
#include "hwrenderer/dynlights/hw_shadowmap.h"
|
||||
#include "hwrenderer/utility/hw_cvars.h"
|
||||
#include "hwrenderer/dynlights/hw_dynlightdata.h"
|
||||
#include "hwrenderer/data/buffers.h"
|
||||
#include "stats.h"
|
||||
#include "g_levellocals.h"
|
||||
|
||||
/*
|
||||
The 1D shadow maps are stored in a 1024x1024 texture as float depth values (R32F).
|
||||
|
||||
Each line in the texture is assigned to a single light. For example, to grab depth values for light 20
|
||||
the fragment shader (main.fp) needs to sample from row 20. That is, the V texture coordinate needs
|
||||
to be 20.5/1024.
|
||||
|
||||
The texel row for each light is split into four parts. One for each direction, like a cube texture,
|
||||
but then only in 2D where this reduces itself to a square. When main.fp samples from the shadow map
|
||||
it first decides in which direction the fragment is (relative to the light), like cubemap sampling does
|
||||
for 3D, but once again just for the 2D case.
|
||||
|
||||
Texels 0-255 is Y positive, 256-511 is X positive, 512-767 is Y negative and 768-1023 is X negative.
|
||||
|
||||
Generating the shadow map itself is done by FShadowMap::Update(). The shadow map texture's FBO is
|
||||
bound and then a screen quad is drawn to make a fragment shader cover all texels. For each fragment
|
||||
it shoots a ray and collects the distance to what it hit.
|
||||
|
||||
The shadowmap.fp shader knows which light and texel it is processing by mapping gl_FragCoord.y back
|
||||
to the light index, and it knows which direction to ray trace by looking at gl_FragCoord.x. For
|
||||
example, if gl_FragCoord.y is 20.5, then it knows its processing light 20, and if gl_FragCoord.x is
|
||||
127.5, then it knows we are shooting straight ahead for the Y positive direction.
|
||||
|
||||
Ray testing is done by uploading two GPU storage buffers - one holding AABB tree nodes, and one with
|
||||
the line segments at the leaf nodes of the tree. The fragment shader then performs a test same way
|
||||
as on the CPU, except everything uses indexes as pointers are not allowed in GLSL.
|
||||
*/
|
||||
|
||||
cycle_t IShadowMap::UpdateCycles;
|
||||
int IShadowMap::LightsProcessed;
|
||||
int IShadowMap::LightsShadowmapped;
|
||||
|
||||
ADD_STAT(shadowmap)
|
||||
{
|
||||
FString out;
|
||||
out.Format("upload=%04.2f ms lights=%d shadowmapped=%d", IShadowMap::UpdateCycles.TimeMS(), IShadowMap::LightsProcessed, IShadowMap::LightsShadowmapped);
|
||||
return out;
|
||||
}
|
||||
|
||||
CUSTOM_CVAR(Int, gl_shadowmap_quality, 512, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||
{
|
||||
switch (self)
|
||||
{
|
||||
case 128:
|
||||
case 256:
|
||||
case 512:
|
||||
case 1024:
|
||||
break;
|
||||
default:
|
||||
self = 128;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
CUSTOM_CVAR (Bool, gl_light_shadowmap, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||
{
|
||||
if (!self) for (auto Level : AllLevels())
|
||||
{
|
||||
auto light = Level->lights;
|
||||
while (light)
|
||||
{
|
||||
light->mShadowmapIndex = 1024;
|
||||
light = light->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool IShadowMap::ShadowTest(FDynamicLight *light, const DVector3 &pos)
|
||||
{
|
||||
if (light->shadowmapped && light->GetRadius() > 0.0 && IsEnabled() && mAABBTree)
|
||||
return mAABBTree->RayTest(light->Pos, pos) >= 1.0f;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IShadowMap::IsEnabled() const
|
||||
{
|
||||
return gl_light_shadowmap && (screen->hwcaps & RFL_SHADER_STORAGE_BUFFER);
|
||||
}
|
||||
|
||||
void IShadowMap::CollectLights()
|
||||
{
|
||||
if (mLights.Size() != 1024 * 4) mLights.Resize(1024 * 4);
|
||||
int lightindex = 0;
|
||||
|
||||
// Todo: this should go through the blockmap in a spiral pattern around the player so that closer lights are preferred.
|
||||
for (auto light = level.lights; light; light = light->next)
|
||||
{
|
||||
LightsProcessed++;
|
||||
if (light->shadowmapped && light->IsActive() && lightindex < 1024 * 4)
|
||||
{
|
||||
LightsShadowmapped++;
|
||||
|
||||
light->mShadowmapIndex = lightindex >> 2;
|
||||
|
||||
mLights[lightindex] = (float)light->X();
|
||||
mLights[lightindex+1] = (float)light->Y();
|
||||
mLights[lightindex+2] = (float)light->Z();
|
||||
mLights[lightindex+3] = light->GetRadius();
|
||||
lightindex += 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
light->mShadowmapIndex = 1024;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for (; lightindex < 1024 * 4; lightindex++)
|
||||
{
|
||||
mLights[lightindex] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool IShadowMap::ValidateAABBTree(FLevelLocals *Level)
|
||||
{
|
||||
// Just comparing the level info is not enough. If two MAPINFO-less levels get played after each other,
|
||||
// they can both refer to the same default level info.
|
||||
if (Level->info != mLastLevel && (Level->nodes.Size() != mLastNumNodes || Level->segs.Size() != mLastNumSegs))
|
||||
{
|
||||
mAABBTree.reset();
|
||||
|
||||
mLastLevel = Level->info;
|
||||
mLastNumNodes = Level->nodes.Size();
|
||||
mLastNumSegs = Level->segs.Size();
|
||||
}
|
||||
|
||||
if (mAABBTree)
|
||||
return true;
|
||||
|
||||
mAABBTree.reset(new hwrenderer::LevelAABBTree(Level));
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IShadowMap::PerformUpdate()
|
||||
{
|
||||
UpdateCycles.Reset();
|
||||
|
||||
LightsProcessed = 0;
|
||||
LightsShadowmapped = 0;
|
||||
|
||||
if (IsEnabled())
|
||||
{
|
||||
UpdateCycles.Clock();
|
||||
UploadAABBTree();
|
||||
UploadLights();
|
||||
mLightList->BindBase();
|
||||
mNodesBuffer->BindBase();
|
||||
mLinesBuffer->BindBase();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void IShadowMap::UploadLights()
|
||||
{
|
||||
CollectLights();
|
||||
|
||||
if (mLightList == nullptr)
|
||||
mLightList = screen->CreateDataBuffer(4, true);
|
||||
|
||||
mLightList->SetData(sizeof(float) * mLights.Size(), &mLights[0]);
|
||||
}
|
||||
|
||||
|
||||
void IShadowMap::UploadAABBTree()
|
||||
{
|
||||
if (!ValidateAABBTree(&level))
|
||||
{
|
||||
if (!mNodesBuffer)
|
||||
mNodesBuffer = screen->CreateDataBuffer(2, true);
|
||||
mNodesBuffer->SetData(mAABBTree->NodesSize(), mAABBTree->Nodes());
|
||||
|
||||
if (!mLinesBuffer)
|
||||
mLinesBuffer = screen->CreateDataBuffer(3, true);
|
||||
mLinesBuffer->SetData(mAABBTree->LinesSize(), mAABBTree->Lines());
|
||||
}
|
||||
else if (mAABBTree->Update())
|
||||
{
|
||||
mNodesBuffer->SetSubData(mAABBTree->DynamicNodesOffset(), mAABBTree->DynamicNodesSize(), mAABBTree->DynamicNodes());
|
||||
mLinesBuffer->SetSubData(mAABBTree->DynamicLinesOffset(), mAABBTree->DynamicLinesSize(), mAABBTree->DynamicLines());
|
||||
}
|
||||
}
|
||||
|
||||
IShadowMap::~IShadowMap()
|
||||
{
|
||||
if (mLightList) delete mLightList;
|
||||
if (mNodesBuffer) delete mNodesBuffer;
|
||||
if (mLinesBuffer) delete mLinesBuffer;
|
||||
}
|
||||
|
66
src/rendering/hwrenderer/dynlights/hw_shadowmap.h
Normal file
66
src/rendering/hwrenderer/dynlights/hw_shadowmap.h
Normal file
|
@ -0,0 +1,66 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "hw_aabbtree.h"
|
||||
#include "stats.h"
|
||||
#include <memory>
|
||||
|
||||
struct FDynamicLight;
|
||||
struct level_info_t;
|
||||
class IDataBuffer;
|
||||
struct FLevelLocals;
|
||||
|
||||
class IShadowMap
|
||||
{
|
||||
public:
|
||||
IShadowMap() { }
|
||||
virtual ~IShadowMap();
|
||||
|
||||
// Test if a world position is in shadow relative to the specified light and returns false if it is
|
||||
bool ShadowTest(FDynamicLight *light, const DVector3 &pos);
|
||||
|
||||
// Returns true if gl_light_shadowmap is enabled and supported by the hardware
|
||||
bool IsEnabled() const;
|
||||
|
||||
static cycle_t UpdateCycles;
|
||||
static int LightsProcessed;
|
||||
static int LightsShadowmapped;
|
||||
|
||||
bool PerformUpdate();
|
||||
void FinishUpdate()
|
||||
{
|
||||
UpdateCycles.Clock();
|
||||
}
|
||||
|
||||
protected:
|
||||
void CollectLights();
|
||||
bool ValidateAABBTree(FLevelLocals *lev);
|
||||
|
||||
// Upload the AABB-tree to the GPU
|
||||
void UploadAABBTree();
|
||||
|
||||
// Upload light list to the GPU
|
||||
void UploadLights();
|
||||
|
||||
// Working buffer for creating the list of lights. Stored here to avoid allocating memory each frame
|
||||
TArray<float> mLights;
|
||||
|
||||
// Used to detect when a level change requires the AABB tree to be regenerated
|
||||
level_info_t *mLastLevel = nullptr;
|
||||
unsigned mLastNumNodes = 0;
|
||||
unsigned mLastNumSegs = 0;
|
||||
|
||||
// AABB-tree of the level, used for ray tests
|
||||
std::unique_ptr<hwrenderer::LevelAABBTree> mAABBTree;
|
||||
|
||||
IShadowMap(const IShadowMap &) = delete;
|
||||
IShadowMap &operator=(IShadowMap &) = delete;
|
||||
|
||||
// OpenGL storage buffer with the list of lights in the shadow map texture
|
||||
IDataBuffer *mLightList = nullptr;
|
||||
|
||||
// OpenGL storage buffers for the AABB tree
|
||||
IDataBuffer *mNodesBuffer = nullptr;
|
||||
IDataBuffer *mLinesBuffer = nullptr;
|
||||
|
||||
};
|
222
src/rendering/hwrenderer/models/hw_models.cpp
Normal file
222
src/rendering/hwrenderer/models/hw_models.cpp
Normal file
|
@ -0,0 +1,222 @@
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Copyright(C) 2005-2016 Christoph Oelckers
|
||||
// All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with this program. If not, see http://www.gnu.org/licenses/
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
/*
|
||||
** gl_models.cpp
|
||||
**
|
||||
** hardware renderer model handling code
|
||||
**
|
||||
**/
|
||||
|
||||
#include "w_wad.h"
|
||||
#include "g_game.h"
|
||||
#include "doomstat.h"
|
||||
#include "g_level.h"
|
||||
#include "r_state.h"
|
||||
#include "d_player.h"
|
||||
#include "g_levellocals.h"
|
||||
#include "i_time.h"
|
||||
#include "cmdlib.h"
|
||||
#include "hwrenderer/textures/hw_material.h"
|
||||
#include "hwrenderer/data/buffers.h"
|
||||
#include "hwrenderer/data/flatvertices.h"
|
||||
#include "hwrenderer/scene/hw_drawinfo.h"
|
||||
#include "hwrenderer/scene/hw_renderstate.h"
|
||||
#include "hwrenderer/scene/hw_portal.h"
|
||||
#include "hw_models.h"
|
||||
|
||||
CVAR(Bool, gl_light_models, true, CVAR_ARCHIVE)
|
||||
|
||||
VSMatrix FGLModelRenderer::GetViewToWorldMatrix()
|
||||
{
|
||||
VSMatrix objectToWorldMatrix;
|
||||
di->VPUniforms.mViewMatrix.inverseMatrix(objectToWorldMatrix);
|
||||
return objectToWorldMatrix;
|
||||
}
|
||||
|
||||
void FGLModelRenderer::BeginDrawModel(AActor *actor, FSpriteModelFrame *smf, const VSMatrix &objectToWorldMatrix, bool mirrored)
|
||||
{
|
||||
state.SetDepthFunc(DF_LEqual);
|
||||
state.EnableTexture(true);
|
||||
// [BB] In case the model should be rendered translucent, do back face culling.
|
||||
// This solves a few of the problems caused by the lack of depth sorting.
|
||||
// [Nash] Don't do back face culling if explicitly specified in MODELDEF
|
||||
// TO-DO: Implement proper depth sorting.
|
||||
if (!(actor->RenderStyle == DefaultRenderStyle()) && !(smf->flags & MDL_DONTCULLBACKFACES))
|
||||
{
|
||||
state.SetCulling((mirrored ^ screen->mPortalState->isMirrored()) ? Cull_CCW : Cull_CW);
|
||||
}
|
||||
|
||||
state.mModelMatrix = objectToWorldMatrix;
|
||||
state.EnableModelMatrix(true);
|
||||
}
|
||||
|
||||
void FGLModelRenderer::EndDrawModel(AActor *actor, FSpriteModelFrame *smf)
|
||||
{
|
||||
state.EnableModelMatrix(false);
|
||||
state.SetDepthFunc(DF_Less);
|
||||
if (!(actor->RenderStyle == DefaultRenderStyle()) && !(smf->flags & MDL_DONTCULLBACKFACES))
|
||||
state.SetCulling(Cull_None);
|
||||
}
|
||||
|
||||
void FGLModelRenderer::BeginDrawHUDModel(AActor *actor, const VSMatrix &objectToWorldMatrix, bool mirrored)
|
||||
{
|
||||
state.SetDepthFunc(DF_LEqual);
|
||||
|
||||
// [BB] In case the model should be rendered translucent, do back face culling.
|
||||
// This solves a few of the problems caused by the lack of depth sorting.
|
||||
// TO-DO: Implement proper depth sorting.
|
||||
if (!(actor->RenderStyle == DefaultRenderStyle()))
|
||||
{
|
||||
state.SetCulling((mirrored ^ screen->mPortalState->isMirrored()) ? Cull_CW : Cull_CCW);
|
||||
}
|
||||
|
||||
state.mModelMatrix = objectToWorldMatrix;
|
||||
state.EnableModelMatrix(true);
|
||||
}
|
||||
|
||||
void FGLModelRenderer::EndDrawHUDModel(AActor *actor)
|
||||
{
|
||||
state.EnableModelMatrix(false);
|
||||
|
||||
state.SetDepthFunc(DF_Less);
|
||||
if (!(actor->RenderStyle == DefaultRenderStyle()))
|
||||
state.SetCulling(Cull_None);
|
||||
}
|
||||
|
||||
IModelVertexBuffer *FGLModelRenderer::CreateVertexBuffer(bool needindex, bool singleframe)
|
||||
{
|
||||
return new FModelVertexBuffer(needindex, singleframe);
|
||||
}
|
||||
|
||||
void FGLModelRenderer::SetInterpolation(double inter)
|
||||
{
|
||||
state.SetInterpolationFactor((float)inter);
|
||||
}
|
||||
|
||||
void FGLModelRenderer::SetMaterial(FTexture *skin, bool clampNoFilter, int translation)
|
||||
{
|
||||
FMaterial * tex = FMaterial::ValidateTexture(skin, false);
|
||||
state.SetMaterial(tex, clampNoFilter ? CLAMP_NOFILTER : CLAMP_NONE, translation, -1);
|
||||
state.SetLightIndex(modellightindex);
|
||||
}
|
||||
|
||||
void FGLModelRenderer::DrawArrays(int start, int count)
|
||||
{
|
||||
state.Draw(DT_Triangles, start, count);
|
||||
}
|
||||
|
||||
void FGLModelRenderer::DrawElements(int numIndices, size_t offset)
|
||||
{
|
||||
state.DrawIndexed(DT_Triangles, int(offset / sizeof(unsigned int)), numIndices);
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
FModelVertexBuffer::FModelVertexBuffer(bool needindex, bool singleframe)
|
||||
{
|
||||
mVertexBuffer = screen->CreateVertexBuffer();
|
||||
mIndexBuffer = needindex ? screen->CreateIndexBuffer() : nullptr;
|
||||
|
||||
static const FVertexBufferAttribute format[] = {
|
||||
{ 0, VATTR_VERTEX, VFmt_Float3, (int)myoffsetof(FModelVertex, x) },
|
||||
{ 0, VATTR_TEXCOORD, VFmt_Float2, (int)myoffsetof(FModelVertex, u) },
|
||||
{ 0, VATTR_NORMAL, VFmt_Packed_A2R10G10B10, (int)myoffsetof(FModelVertex, packedNormal) },
|
||||
{ 1, VATTR_VERTEX2, VFmt_Float3, (int)myoffsetof(FModelVertex, x) },
|
||||
{ 1, VATTR_NORMAL2, VFmt_Packed_A2R10G10B10, (int)myoffsetof(FModelVertex, packedNormal) }
|
||||
};
|
||||
mVertexBuffer->SetFormat(2, 5, sizeof(FModelVertex), format);
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
FModelVertexBuffer::~FModelVertexBuffer()
|
||||
{
|
||||
if (mIndexBuffer) delete mIndexBuffer;
|
||||
delete mVertexBuffer;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
FModelVertex *FModelVertexBuffer::LockVertexBuffer(unsigned int size)
|
||||
{
|
||||
return static_cast<FModelVertex*>(mVertexBuffer->Lock(size * sizeof(FModelVertex)));
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
void FModelVertexBuffer::UnlockVertexBuffer()
|
||||
{
|
||||
mVertexBuffer->Unlock();
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
unsigned int *FModelVertexBuffer::LockIndexBuffer(unsigned int size)
|
||||
{
|
||||
if (mIndexBuffer) return static_cast<unsigned int*>(mIndexBuffer->Lock(size * sizeof(unsigned int)));
|
||||
else return nullptr;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
void FModelVertexBuffer::UnlockIndexBuffer()
|
||||
{
|
||||
if (mIndexBuffer) mIndexBuffer->Unlock();
|
||||
}
|
||||
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
void FModelVertexBuffer::SetupFrame(FModelRenderer *renderer, unsigned int frame1, unsigned int frame2, unsigned int size)
|
||||
{
|
||||
auto &state = static_cast<FGLModelRenderer*>(renderer)->state;
|
||||
state.SetVertexBuffer(mVertexBuffer, frame1, frame2);
|
||||
if (mIndexBuffer) state.SetIndexBuffer(mIndexBuffer);
|
||||
}
|
74
src/rendering/hwrenderer/models/hw_models.h
Normal file
74
src/rendering/hwrenderer/models/hw_models.h
Normal file
|
@ -0,0 +1,74 @@
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Copyright(C) 2005-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/
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "tarray.h"
|
||||
#include "p_pspr.h"
|
||||
#include "r_data/voxels.h"
|
||||
#include "r_data/models/models.h"
|
||||
|
||||
class GLSprite;
|
||||
struct HWDrawInfo;
|
||||
class FRenderState;
|
||||
|
||||
class FModelVertexBuffer : public IModelVertexBuffer
|
||||
{
|
||||
IVertexBuffer *mVertexBuffer;
|
||||
IIndexBuffer *mIndexBuffer;
|
||||
|
||||
public:
|
||||
|
||||
FModelVertexBuffer(bool needindex, bool singleframe);
|
||||
~FModelVertexBuffer();
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
class FGLModelRenderer : public FModelRenderer
|
||||
{
|
||||
friend class FModelVertexBuffer;
|
||||
int modellightindex = -1;
|
||||
HWDrawInfo *di;
|
||||
FRenderState &state;
|
||||
public:
|
||||
FGLModelRenderer(HWDrawInfo *d, FRenderState &st, int mli) : modellightindex(mli), di(d), state(st)
|
||||
{}
|
||||
ModelRendererType GetType() const override { return GLModelRendererType; }
|
||||
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;
|
||||
};
|
||||
|
856
src/rendering/hwrenderer/postprocessing/hw_postprocess.cpp
Normal file
856
src/rendering/hwrenderer/postprocessing/hw_postprocess.cpp
Normal file
|
@ -0,0 +1,856 @@
|
|||
|
||||
#include "v_video.h"
|
||||
#include "hw_postprocess.h"
|
||||
#include "hwrenderer/utility/hw_cvars.h"
|
||||
#include "hwrenderer/postprocessing/hw_postprocess_cvars.h"
|
||||
#include <random>
|
||||
|
||||
Postprocess hw_postprocess;
|
||||
|
||||
Postprocess::Postprocess()
|
||||
{
|
||||
Managers.Push(new PPBloom());
|
||||
Managers.Push(new PPLensDistort());
|
||||
Managers.Push(new PPFXAA());
|
||||
Managers.Push(new PPCameraExposure());
|
||||
Managers.Push(new PPColormap());
|
||||
Managers.Push(new PPTonemap());
|
||||
Managers.Push(new PPAmbientOcclusion());
|
||||
}
|
||||
|
||||
Postprocess::~Postprocess()
|
||||
{
|
||||
for (unsigned int i = 0; i < Managers.Size(); i++)
|
||||
delete Managers[i];
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void PPBloom::DeclareShaders()
|
||||
{
|
||||
hw_postprocess.Shaders["BloomCombine"] = { "shaders/glsl/bloomcombine.fp", "", {} };
|
||||
hw_postprocess.Shaders["BloomExtract"] = { "shaders/glsl/bloomextract.fp", "", ExtractUniforms::Desc() };
|
||||
hw_postprocess.Shaders["BlurVertical"] = { "shaders/glsl/blur.fp", "#define BLUR_VERTICAL\n", BlurUniforms::Desc() };
|
||||
hw_postprocess.Shaders["BlurHorizontal"] = { "shaders/glsl/blur.fp", "#define BLUR_HORIZONTAL\n", BlurUniforms::Desc() };
|
||||
}
|
||||
|
||||
void PPBloom::UpdateTextures()
|
||||
{
|
||||
int width = hw_postprocess.SceneWidth;
|
||||
int height = hw_postprocess.SceneHeight;
|
||||
|
||||
// No scene, no bloom!
|
||||
if (width <= 0 || height <= 0)
|
||||
return;
|
||||
|
||||
int bloomWidth = (width + 1) / 2;
|
||||
int bloomHeight = (height + 1) / 2;
|
||||
|
||||
for (int i = 0; i < NumBloomLevels; i++)
|
||||
{
|
||||
auto &blevel = levels[i];
|
||||
blevel.VTexture.Format("Bloom.VTexture.%d", i);
|
||||
blevel.HTexture.Format("Bloom.HTexture.%d", i);
|
||||
blevel.Viewport.left = 0;
|
||||
blevel.Viewport.top = 0;
|
||||
blevel.Viewport.width = (bloomWidth + 1) / 2;
|
||||
blevel.Viewport.height = (bloomHeight + 1) / 2;
|
||||
|
||||
PPTextureDesc texture = { blevel.Viewport.width, blevel.Viewport.height, PixelFormat::Rgba16f };
|
||||
hw_postprocess.Textures[blevel.VTexture] = texture;
|
||||
hw_postprocess.Textures[blevel.HTexture] = texture;
|
||||
|
||||
bloomWidth = blevel.Viewport.width;
|
||||
bloomHeight = blevel.Viewport.height;
|
||||
}
|
||||
}
|
||||
|
||||
void PPBloom::UpdateSteps()
|
||||
{
|
||||
UpdateBlurSteps();
|
||||
|
||||
// Only bloom things if enabled and no special fixed light mode is active
|
||||
if (!gl_bloom || hw_postprocess.fixedcm != CM_DEFAULT || gl_ssao_debug || hw_postprocess.SceneWidth <= 0 || hw_postprocess.SceneHeight <= 0)
|
||||
{
|
||||
hw_postprocess.Effects["BloomScene"] = {};
|
||||
return;
|
||||
}
|
||||
|
||||
TArray<PPStep> steps;
|
||||
PPStep step;
|
||||
|
||||
ExtractUniforms extractUniforms;
|
||||
extractUniforms.Scale = screen->SceneScale();
|
||||
extractUniforms.Offset = screen->SceneOffset();
|
||||
|
||||
auto &level0 = levels[0];
|
||||
|
||||
// Extract blooming pixels from scene texture:
|
||||
step.ShaderName = "BloomExtract";
|
||||
step.Uniforms.Set(extractUniforms);
|
||||
step.Viewport = level0.Viewport;
|
||||
step.SetInputCurrent(0, PPFilterMode::Linear);
|
||||
step.SetInputTexture(1, "Exposure.CameraTexture");
|
||||
step.SetOutputTexture(level0.VTexture);
|
||||
step.SetNoBlend();
|
||||
steps.Push(step);
|
||||
|
||||
const float blurAmount = gl_bloom_amount;
|
||||
BlurUniforms blurUniforms;
|
||||
ComputeBlurSamples(7, blurAmount, blurUniforms.SampleWeights);
|
||||
|
||||
// Blur and downscale:
|
||||
for (int i = 0; i < NumBloomLevels - 1; i++)
|
||||
{
|
||||
auto &blevel = levels[i];
|
||||
auto &next = levels[i + 1];
|
||||
|
||||
steps.Push(BlurStep(blurUniforms, blevel.VTexture, blevel.HTexture, blevel.Viewport, false));
|
||||
steps.Push(BlurStep(blurUniforms, blevel.HTexture, blevel.VTexture, blevel.Viewport, true));
|
||||
|
||||
// Linear downscale:
|
||||
step.ShaderName = "BloomCombine";
|
||||
step.Uniforms.Clear();
|
||||
step.Viewport = next.Viewport;
|
||||
step.SetInputTexture(0, blevel.VTexture, PPFilterMode::Linear);
|
||||
step.SetOutputTexture(next.VTexture);
|
||||
step.SetNoBlend();
|
||||
steps.Push(step);
|
||||
}
|
||||
|
||||
// Blur and upscale:
|
||||
for (int i = NumBloomLevels - 1; i > 0; i--)
|
||||
{
|
||||
auto &blevel = levels[i];
|
||||
auto &next = levels[i - 1];
|
||||
|
||||
steps.Push(BlurStep(blurUniforms, blevel.VTexture, blevel.HTexture, blevel.Viewport, false));
|
||||
steps.Push(BlurStep(blurUniforms, blevel.HTexture, blevel.VTexture, blevel.Viewport, true));
|
||||
|
||||
// Linear upscale:
|
||||
step.ShaderName = "BloomCombine";
|
||||
step.Uniforms.Clear();
|
||||
step.Viewport = next.Viewport;
|
||||
step.SetInputTexture(0, blevel.VTexture, PPFilterMode::Linear);
|
||||
step.SetOutputTexture(next.VTexture);
|
||||
step.SetNoBlend();
|
||||
steps.Push(step);
|
||||
}
|
||||
|
||||
steps.Push(BlurStep(blurUniforms, level0.VTexture, level0.HTexture, level0.Viewport, false));
|
||||
steps.Push(BlurStep(blurUniforms, level0.HTexture, level0.VTexture, level0.Viewport, true));
|
||||
|
||||
// Add bloom back to scene texture:
|
||||
step.ShaderName = "BloomCombine";
|
||||
step.Uniforms.Clear();
|
||||
step.Viewport = screen->mSceneViewport;
|
||||
step.SetInputTexture(0, level0.VTexture, PPFilterMode::Linear);
|
||||
step.SetOutputCurrent();
|
||||
step.SetAdditiveBlend();
|
||||
steps.Push(step);
|
||||
|
||||
hw_postprocess.Effects["BloomScene"] = steps;
|
||||
}
|
||||
|
||||
void PPBloom::UpdateBlurSteps()
|
||||
{
|
||||
// first, respect the CVar
|
||||
float blurAmount = gl_menu_blur;
|
||||
|
||||
// if CVar is negative, use the gameinfo entry
|
||||
if (gl_menu_blur < 0)
|
||||
blurAmount = hw_postprocess.gameinfobluramount;
|
||||
|
||||
// if blurAmount == 0 or somehow still returns negative, exit to prevent a crash, clearly we don't want this
|
||||
if (blurAmount <= 0.0)
|
||||
{
|
||||
hw_postprocess.Effects["BlurScene"] = {};
|
||||
return;
|
||||
}
|
||||
|
||||
TArray<PPStep> steps;
|
||||
PPStep step;
|
||||
|
||||
int numLevels = 3;
|
||||
assert(numLevels <= NumBloomLevels);
|
||||
|
||||
const auto &level0 = levels[0];
|
||||
|
||||
// Grab the area we want to bloom:
|
||||
step.ShaderName = "BloomCombine";
|
||||
step.Uniforms.Clear();
|
||||
step.Viewport = level0.Viewport;
|
||||
step.SetInputCurrent(0, PPFilterMode::Linear);
|
||||
step.SetOutputTexture(level0.VTexture);
|
||||
step.SetNoBlend();
|
||||
steps.Push(step);
|
||||
|
||||
BlurUniforms blurUniforms;
|
||||
ComputeBlurSamples(7, blurAmount, blurUniforms.SampleWeights);
|
||||
|
||||
// Blur and downscale:
|
||||
for (int i = 0; i < numLevels - 1; i++)
|
||||
{
|
||||
auto &blevel = levels[i];
|
||||
auto &next = levels[i + 1];
|
||||
|
||||
steps.Push(BlurStep(blurUniforms, blevel.VTexture, blevel.HTexture, blevel.Viewport, false));
|
||||
steps.Push(BlurStep(blurUniforms, blevel.HTexture, blevel.VTexture, blevel.Viewport, true));
|
||||
|
||||
// Linear downscale:
|
||||
step.ShaderName = "BloomCombine";
|
||||
step.Uniforms.Clear();
|
||||
step.Viewport = next.Viewport;
|
||||
step.SetInputTexture(0, blevel.VTexture, PPFilterMode::Linear);
|
||||
step.SetOutputTexture(next.VTexture);
|
||||
step.SetNoBlend();
|
||||
steps.Push(step);
|
||||
}
|
||||
|
||||
// Blur and upscale:
|
||||
for (int i = numLevels - 1; i > 0; i--)
|
||||
{
|
||||
auto &blevel = levels[i];
|
||||
auto &next = levels[i - 1];
|
||||
|
||||
steps.Push(BlurStep(blurUniforms, blevel.VTexture, blevel.HTexture, blevel.Viewport, false));
|
||||
steps.Push(BlurStep(blurUniforms, blevel.HTexture, blevel.VTexture, blevel.Viewport, true));
|
||||
|
||||
// Linear upscale:
|
||||
step.ShaderName = "BloomCombine";
|
||||
step.Uniforms.Clear();
|
||||
step.Viewport = next.Viewport;
|
||||
step.SetInputTexture(0, blevel.VTexture, PPFilterMode::Linear);
|
||||
step.SetOutputTexture(next.VTexture);
|
||||
step.SetNoBlend();
|
||||
steps.Push(step);
|
||||
}
|
||||
|
||||
steps.Push(BlurStep(blurUniforms, level0.VTexture, level0.HTexture, level0.Viewport, false));
|
||||
steps.Push(BlurStep(blurUniforms, level0.HTexture, level0.VTexture, level0.Viewport, true));
|
||||
|
||||
// Copy blur back to scene texture:
|
||||
step.ShaderName = "BloomCombine";
|
||||
step.Uniforms.Clear();
|
||||
step.Viewport = screen->mScreenViewport;
|
||||
step.SetInputTexture(0, level0.VTexture, PPFilterMode::Linear);
|
||||
step.SetOutputCurrent();
|
||||
step.SetNoBlend();
|
||||
steps.Push(step);
|
||||
|
||||
hw_postprocess.Effects["BlurScene"] = steps;
|
||||
}
|
||||
|
||||
PPStep PPBloom::BlurStep(const BlurUniforms &blurUniforms, PPTextureName input, PPTextureName output, PPViewport viewport, bool vertical)
|
||||
{
|
||||
PPStep step;
|
||||
step.ShaderName = vertical ? "BlurVertical" : "BlurHorizontal";
|
||||
step.Uniforms.Set(blurUniforms);
|
||||
step.Viewport = viewport;
|
||||
step.SetInputTexture(0, input);
|
||||
step.SetOutputTexture(output);
|
||||
step.SetNoBlend();
|
||||
return step;
|
||||
}
|
||||
|
||||
float PPBloom::ComputeBlurGaussian(float n, float theta) // theta = Blur Amount
|
||||
{
|
||||
return (float)((1.0f / sqrtf(2 * (float)M_PI * theta)) * expf(-(n * n) / (2.0f * theta * theta)));
|
||||
}
|
||||
|
||||
void PPBloom::ComputeBlurSamples(int sampleCount, float blurAmount, float *sampleWeights)
|
||||
{
|
||||
sampleWeights[0] = ComputeBlurGaussian(0, blurAmount);
|
||||
|
||||
float totalWeights = sampleWeights[0];
|
||||
|
||||
for (int i = 0; i < sampleCount / 2; i++)
|
||||
{
|
||||
float weight = ComputeBlurGaussian(i + 1.0f, blurAmount);
|
||||
|
||||
sampleWeights[i * 2 + 1] = weight;
|
||||
sampleWeights[i * 2 + 2] = weight;
|
||||
|
||||
totalWeights += weight * 2;
|
||||
}
|
||||
|
||||
for (int i = 0; i < sampleCount; i++)
|
||||
{
|
||||
sampleWeights[i] /= totalWeights;
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void PPLensDistort::DeclareShaders()
|
||||
{
|
||||
hw_postprocess.Shaders["Lens"] = { "shaders/glsl/lensdistortion.fp", "", LensUniforms::Desc() };
|
||||
}
|
||||
|
||||
void PPLensDistort::UpdateSteps()
|
||||
{
|
||||
if (gl_lens == 0)
|
||||
{
|
||||
hw_postprocess.Effects["LensDistortScene"] = {};
|
||||
return;
|
||||
}
|
||||
|
||||
float k[4] =
|
||||
{
|
||||
gl_lens_k,
|
||||
gl_lens_k * gl_lens_chromatic,
|
||||
gl_lens_k * gl_lens_chromatic * gl_lens_chromatic,
|
||||
0.0f
|
||||
};
|
||||
float kcube[4] =
|
||||
{
|
||||
gl_lens_kcube,
|
||||
gl_lens_kcube * gl_lens_chromatic,
|
||||
gl_lens_kcube * gl_lens_chromatic * gl_lens_chromatic,
|
||||
0.0f
|
||||
};
|
||||
|
||||
float aspect = screen->mSceneViewport.width / (float)screen->mSceneViewport.height;
|
||||
|
||||
// Scale factor to keep sampling within the input texture
|
||||
float r2 = aspect * aspect * 0.25f + 0.25f;
|
||||
float sqrt_r2 = sqrt(r2);
|
||||
float f0 = 1.0f + MAX(r2 * (k[0] + kcube[0] * sqrt_r2), 0.0f);
|
||||
float f2 = 1.0f + MAX(r2 * (k[2] + kcube[2] * sqrt_r2), 0.0f);
|
||||
float f = MAX(f0, f2);
|
||||
float scale = 1.0f / f;
|
||||
|
||||
LensUniforms uniforms;
|
||||
uniforms.AspectRatio = aspect;
|
||||
uniforms.Scale = scale;
|
||||
uniforms.LensDistortionCoefficient = k;
|
||||
uniforms.CubicDistortionValue = kcube;
|
||||
|
||||
TArray<PPStep> steps;
|
||||
|
||||
PPStep step;
|
||||
step.ShaderName = "Lens";
|
||||
step.Uniforms.Set(uniforms);
|
||||
step.Viewport = screen->mScreenViewport;
|
||||
step.SetInputCurrent(0, PPFilterMode::Linear);
|
||||
step.SetOutputNext();
|
||||
step.SetNoBlend();
|
||||
steps.Push(step);
|
||||
|
||||
hw_postprocess.Effects["LensDistortScene"] = steps;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void PPFXAA::DeclareShaders()
|
||||
{
|
||||
hw_postprocess.Shaders["FXAALuma"] = { "shaders/glsl/fxaa.fp", "#define FXAA_LUMA_PASS\n", {} };
|
||||
hw_postprocess.Shaders["FXAA"] = { "shaders/glsl/fxaa.fp", GetDefines(), FXAAUniforms::Desc(), GetMaxVersion() };
|
||||
}
|
||||
|
||||
void PPFXAA::UpdateSteps()
|
||||
{
|
||||
if (0 == gl_fxaa)
|
||||
{
|
||||
hw_postprocess.Effects["ApplyFXAA"] = {};
|
||||
return;
|
||||
}
|
||||
|
||||
FXAAUniforms uniforms;
|
||||
uniforms.ReciprocalResolution = { 1.0f / screen->mScreenViewport.width, 1.0f / screen->mScreenViewport.height };
|
||||
|
||||
TArray<PPStep> steps;
|
||||
|
||||
PPStep step;
|
||||
step.ShaderName = "FXAALuma";
|
||||
step.Uniforms.Clear();
|
||||
step.Viewport = screen->mScreenViewport;
|
||||
step.SetInputCurrent(0, PPFilterMode::Nearest);
|
||||
step.SetOutputNext();
|
||||
step.SetNoBlend();
|
||||
steps.Push(step);
|
||||
|
||||
step.ShaderName = "FXAA";
|
||||
step.Uniforms.Set(uniforms);
|
||||
step.Viewport = screen->mScreenViewport;
|
||||
step.SetInputCurrent(0, PPFilterMode::Linear);
|
||||
step.SetOutputNext();
|
||||
step.SetNoBlend();
|
||||
steps.Push(step);
|
||||
|
||||
hw_postprocess.Effects["ApplyFXAA"] = steps;
|
||||
}
|
||||
|
||||
int PPFXAA::GetMaxVersion()
|
||||
{
|
||||
return screen->glslversion >= 4.f ? 400 : 330;
|
||||
}
|
||||
|
||||
FString PPFXAA::GetDefines()
|
||||
{
|
||||
int quality;
|
||||
|
||||
switch (gl_fxaa)
|
||||
{
|
||||
default:
|
||||
case IFXAAShader::Low: quality = 10; break;
|
||||
case IFXAAShader::Medium: quality = 12; break;
|
||||
case IFXAAShader::High: quality = 29; break;
|
||||
case IFXAAShader::Extreme: quality = 39; break;
|
||||
}
|
||||
|
||||
const int gatherAlpha = GetMaxVersion() >= 400 ? 1 : 0;
|
||||
|
||||
// TODO: enable FXAA_GATHER4_ALPHA on OpenGL earlier than 4.0
|
||||
// when GL_ARB_gpu_shader5/GL_NV_gpu_shader5 extensions are supported
|
||||
|
||||
FString result;
|
||||
result.Format(
|
||||
"#define FXAA_QUALITY__PRESET %i\n"
|
||||
"#define FXAA_GATHER4_ALPHA %i\n",
|
||||
quality, gatherAlpha);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void PPCameraExposure::DeclareShaders()
|
||||
{
|
||||
hw_postprocess.Shaders["ExposureExtract"] = { "shaders/glsl/exposureextract.fp", "", ExposureExtractUniforms::Desc() };
|
||||
hw_postprocess.Shaders["ExposureAverage"] = { "shaders/glsl/exposureaverage.fp", "", {}, 400 };
|
||||
hw_postprocess.Shaders["ExposureCombine"] = { "shaders/glsl/exposurecombine.fp", "", ExposureCombineUniforms::Desc() };
|
||||
}
|
||||
|
||||
void PPCameraExposure::UpdateTextures()
|
||||
{
|
||||
int width = hw_postprocess.SceneWidth;
|
||||
int height = hw_postprocess.SceneHeight;
|
||||
|
||||
if (ExposureLevels.Size() > 0 && ExposureLevels[0].Viewport.width == width && ExposureLevels[0].Viewport.height == height)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ExposureLevels.Clear();
|
||||
|
||||
int i = 0;
|
||||
do
|
||||
{
|
||||
width = MAX(width / 2, 1);
|
||||
height = MAX(height / 2, 1);
|
||||
|
||||
PPExposureLevel blevel;
|
||||
blevel.Viewport.left = 0;
|
||||
blevel.Viewport.top = 0;
|
||||
blevel.Viewport.width = width;
|
||||
blevel.Viewport.height = height;
|
||||
blevel.Texture.Format("Exposure.Level.%d", i);
|
||||
ExposureLevels.Push(blevel);
|
||||
|
||||
PPTextureDesc texture = { blevel.Viewport.width, blevel.Viewport.height, PixelFormat::R32f };
|
||||
hw_postprocess.Textures[blevel.Texture] = texture;
|
||||
|
||||
i++;
|
||||
|
||||
} while (width > 1 || height > 1);
|
||||
|
||||
hw_postprocess.Textures["Exposure.CameraTexture"] = { 1, 1, PixelFormat::R32f };
|
||||
|
||||
FirstExposureFrame = true;
|
||||
}
|
||||
|
||||
void PPCameraExposure::UpdateSteps()
|
||||
{
|
||||
if (!gl_bloom)
|
||||
{
|
||||
hw_postprocess.Effects["UpdateCameraExposure"] = {};
|
||||
return;
|
||||
}
|
||||
|
||||
TArray<PPStep> steps;
|
||||
PPStep step;
|
||||
|
||||
ExposureExtractUniforms extractUniforms;
|
||||
extractUniforms.Scale = screen->SceneScale();
|
||||
extractUniforms.Offset = screen->SceneOffset();
|
||||
|
||||
ExposureCombineUniforms combineUniforms;
|
||||
combineUniforms.ExposureBase = gl_exposure_base;
|
||||
combineUniforms.ExposureMin = gl_exposure_min;
|
||||
combineUniforms.ExposureScale = gl_exposure_scale;
|
||||
combineUniforms.ExposureSpeed = gl_exposure_speed;
|
||||
|
||||
auto &level0 = ExposureLevels[0];
|
||||
|
||||
// Extract light blevel from scene texture:
|
||||
step.ShaderName = "ExposureExtract";
|
||||
step.Uniforms.Set(extractUniforms);
|
||||
step.Viewport = level0.Viewport;
|
||||
step.SetInputCurrent(0, PPFilterMode::Linear);
|
||||
step.SetOutputTexture(level0.Texture);
|
||||
step.SetNoBlend();
|
||||
steps.Push(step);
|
||||
|
||||
// Find the average value:
|
||||
for (unsigned int i = 0; i + 1 < ExposureLevels.Size(); i++)
|
||||
{
|
||||
auto &blevel = ExposureLevels[i];
|
||||
auto &next = ExposureLevels[i + 1];
|
||||
|
||||
step.ShaderName = "ExposureAverage";
|
||||
step.Uniforms.Clear();
|
||||
step.Viewport = next.Viewport;
|
||||
step.SetInputTexture(0, blevel.Texture, PPFilterMode::Linear);
|
||||
step.SetOutputTexture(next.Texture);
|
||||
step.SetNoBlend();
|
||||
steps.Push(step);
|
||||
}
|
||||
|
||||
// Combine average value with current camera exposure:
|
||||
step.ShaderName = "ExposureCombine";
|
||||
step.Uniforms.Set(combineUniforms);
|
||||
step.Viewport.left = 0;
|
||||
step.Viewport.top = 0;
|
||||
step.Viewport.width = 1;
|
||||
step.Viewport.height = 1;
|
||||
step.SetInputTexture(0, ExposureLevels.Last().Texture, PPFilterMode::Linear);
|
||||
step.SetOutputTexture("Exposure.CameraTexture");
|
||||
if (!FirstExposureFrame)
|
||||
step.SetAlphaBlend();
|
||||
else
|
||||
step.SetNoBlend();
|
||||
steps.Push(step);
|
||||
|
||||
FirstExposureFrame = false;
|
||||
|
||||
hw_postprocess.Effects["UpdateCameraExposure"] = steps;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void PPColormap::DeclareShaders()
|
||||
{
|
||||
hw_postprocess.Shaders["Colormap"] = { "shaders/glsl/colormap.fp", "", ColormapUniforms::Desc() };
|
||||
}
|
||||
|
||||
void PPColormap::UpdateSteps()
|
||||
{
|
||||
int fixedcm = hw_postprocess.fixedcm;
|
||||
|
||||
if (fixedcm < CM_FIRSTSPECIALCOLORMAP || fixedcm >= CM_MAXCOLORMAP)
|
||||
{
|
||||
hw_postprocess.Effects["ColormapScene"] = {};
|
||||
return;
|
||||
}
|
||||
|
||||
FSpecialColormap *scm = &SpecialColormaps[fixedcm - CM_FIRSTSPECIALCOLORMAP];
|
||||
float m[] = { scm->ColorizeEnd[0] - scm->ColorizeStart[0],
|
||||
scm->ColorizeEnd[1] - scm->ColorizeStart[1], scm->ColorizeEnd[2] - scm->ColorizeStart[2], 0.f };
|
||||
|
||||
ColormapUniforms uniforms;
|
||||
uniforms.MapStart = { scm->ColorizeStart[0], scm->ColorizeStart[1], scm->ColorizeStart[2], 0.f };
|
||||
uniforms.MapRange = m;
|
||||
|
||||
PPStep step;
|
||||
step.ShaderName = "Colormap";
|
||||
step.Uniforms.Set(uniforms);
|
||||
step.Viewport = screen->mScreenViewport;
|
||||
step.SetInputCurrent(0);
|
||||
step.SetOutputNext();
|
||||
step.SetNoBlend();
|
||||
|
||||
TArray<PPStep> steps;
|
||||
steps.Push(step);
|
||||
hw_postprocess.Effects["ColormapScene"] = steps;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void PPTonemap::DeclareShaders()
|
||||
{
|
||||
hw_postprocess.Shaders["Tonemap.Linear"] = { "shaders/glsl/tonemap.fp", "#define LINEAR\n", {} };
|
||||
hw_postprocess.Shaders["Tonemap.Reinhard"] = { "shaders/glsl/tonemap.fp", "#define REINHARD\n", {} };
|
||||
hw_postprocess.Shaders["Tonemap.HejlDawson"] = { "shaders/glsl/tonemap.fp", "#define HEJLDAWSON\n", {} };
|
||||
hw_postprocess.Shaders["Tonemap.Uncharted2"] = { "shaders/glsl/tonemap.fp", "#define UNCHARTED2\n", {} };
|
||||
hw_postprocess.Shaders["Tonemap.Palette"] = { "shaders/glsl/tonemap.fp", "#define PALETTE\n", {} };
|
||||
}
|
||||
|
||||
void PPTonemap::UpdateTextures()
|
||||
{
|
||||
if (gl_tonemap == Palette)
|
||||
{
|
||||
if (!hw_postprocess.Textures.CheckKey("Tonemap.Palette"))
|
||||
{
|
||||
std::shared_ptr<void> data(new uint32_t[512 * 512], [](void *p) { delete[](uint32_t*)p; });
|
||||
|
||||
uint8_t *lut = (uint8_t *)data.get();
|
||||
for (int r = 0; r < 64; r++)
|
||||
{
|
||||
for (int g = 0; g < 64; g++)
|
||||
{
|
||||
for (int b = 0; b < 64; b++)
|
||||
{
|
||||
PalEntry color = GPalette.BaseColors[(uint8_t)PTM_BestColor((uint32_t *)GPalette.BaseColors, (r << 2) | (r >> 4), (g << 2) | (g >> 4), (b << 2) | (b >> 4),
|
||||
gl_paltonemap_reverselookup, gl_paltonemap_powtable, 0, 256)];
|
||||
int index = ((r * 64 + g) * 64 + b) * 4;
|
||||
lut[index] = color.r;
|
||||
lut[index + 1] = color.g;
|
||||
lut[index + 2] = color.b;
|
||||
lut[index + 3] = 255;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hw_postprocess.Textures["Tonemap.Palette"] = { 512, 512, PixelFormat::Rgba8, data };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PPTonemap::UpdateSteps()
|
||||
{
|
||||
if (gl_tonemap == 0)
|
||||
{
|
||||
hw_postprocess.Effects["TonemapScene"] = {};
|
||||
return;
|
||||
}
|
||||
|
||||
PPShaderName shader;
|
||||
switch (gl_tonemap)
|
||||
{
|
||||
default:
|
||||
case Linear: shader = "Tonemap.Linear"; break;
|
||||
case Reinhard: shader = "Tonemap.Reinhard"; break;
|
||||
case HejlDawson: shader = "Tonemap.HejlDawson"; break;
|
||||
case Uncharted2: shader = "Tonemap.Uncharted2"; break;
|
||||
case Palette: shader = "Tonemap.Palette"; break;
|
||||
}
|
||||
|
||||
PPStep step;
|
||||
step.ShaderName = shader;
|
||||
step.Viewport = screen->mScreenViewport;
|
||||
step.SetInputCurrent(0);
|
||||
if (gl_tonemap == Palette)
|
||||
step.SetInputTexture(1, "Tonemap.Palette");
|
||||
step.SetOutputNext();
|
||||
step.SetNoBlend();
|
||||
|
||||
TArray<PPStep> steps;
|
||||
steps.Push(step);
|
||||
hw_postprocess.Effects["TonemapScene"] = steps;
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void PPAmbientOcclusion::DeclareShaders()
|
||||
{
|
||||
// Must match quality values in PPAmbientOcclusion::UpdateTextures
|
||||
int numDirections, numSteps;
|
||||
switch (gl_ssao)
|
||||
{
|
||||
default:
|
||||
case LowQuality: numDirections = 2; numSteps = 4; break;
|
||||
case MediumQuality: numDirections = 4; numSteps = 4; break;
|
||||
case HighQuality: numDirections = 8; numSteps = 4; break;
|
||||
}
|
||||
|
||||
FString defines;
|
||||
defines.Format(R"(
|
||||
#define USE_RANDOM_TEXTURE
|
||||
#define RANDOM_TEXTURE_WIDTH 4.0
|
||||
#define NUM_DIRECTIONS %d.0
|
||||
#define NUM_STEPS %d.0
|
||||
)", numDirections, numSteps);
|
||||
|
||||
hw_postprocess.Shaders["SSAO.LinearDepth"] = { "shaders/glsl/lineardepth.fp", "", LinearDepthUniforms::Desc() };
|
||||
hw_postprocess.Shaders["SSAO.LinearDepthMS"] = { "shaders/glsl/lineardepth.fp", "#define MULTISAMPLE\n", LinearDepthUniforms::Desc() };
|
||||
hw_postprocess.Shaders["SSAO.AmbientOcclude"] = { "shaders/glsl/ssao.fp", defines, SSAOUniforms::Desc() };
|
||||
hw_postprocess.Shaders["SSAO.AmbientOccludeMS"] = { "shaders/glsl/ssao.fp", defines + "\n#define MULTISAMPLE\n", SSAOUniforms::Desc() };
|
||||
hw_postprocess.Shaders["SSAO.BlurVertical"] = { "shaders/glsl/depthblur.fp", "#define BLUR_VERTICAL\n", DepthBlurUniforms::Desc() };
|
||||
hw_postprocess.Shaders["SSAO.BlurHorizontal"] = { "shaders/glsl/depthblur.fp", "#define BLUR_HORIZONTAL\n", DepthBlurUniforms::Desc() };
|
||||
hw_postprocess.Shaders["SSAO.Combine"] = { "shaders/glsl/ssaocombine.fp", "", AmbientCombineUniforms::Desc() };
|
||||
hw_postprocess.Shaders["SSAO.CombineMS"] = { "shaders/glsl/ssaocombine.fp", "#define MULTISAMPLE\n", AmbientCombineUniforms::Desc() };
|
||||
}
|
||||
|
||||
void PPAmbientOcclusion::UpdateTextures()
|
||||
{
|
||||
int width = hw_postprocess.SceneWidth;
|
||||
int height = hw_postprocess.SceneHeight;
|
||||
|
||||
if (width <= 0 || height <= 0)
|
||||
return;
|
||||
|
||||
AmbientWidth = (width + 1) / 2;
|
||||
AmbientHeight = (height + 1) / 2;
|
||||
|
||||
hw_postprocess.Textures["SSAO.LinearDepth"] = { AmbientWidth, AmbientHeight, PixelFormat::R32f };
|
||||
hw_postprocess.Textures["SSAO.Ambient0"] = { AmbientWidth, AmbientHeight, PixelFormat::Rg16f };
|
||||
hw_postprocess.Textures["SSAO.Ambient1"] = { AmbientWidth, AmbientHeight, PixelFormat::Rg16f };
|
||||
|
||||
// We only need to create the random texture once
|
||||
if (!hw_postprocess.Textures.CheckKey("SSAO.Random0"))
|
||||
{
|
||||
// Must match quality enum in PPAmbientOcclusion::DeclareShaders
|
||||
double numDirections[NumAmbientRandomTextures] = { 2.0, 4.0, 8.0 };
|
||||
|
||||
std::mt19937 generator(1337);
|
||||
std::uniform_real_distribution<double> distribution(0.0, 1.0);
|
||||
for (int quality = 0; quality < NumAmbientRandomTextures; quality++)
|
||||
{
|
||||
std::shared_ptr<void> data(new int16_t[16 * 4], [](void *p) { delete[](int16_t*)p; });
|
||||
int16_t *randomValues = (int16_t *)data.get();
|
||||
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
double angle = 2.0 * M_PI * distribution(generator) / numDirections[quality];
|
||||
double x = cos(angle);
|
||||
double y = sin(angle);
|
||||
double z = distribution(generator);
|
||||
double w = distribution(generator);
|
||||
|
||||
randomValues[i * 4 + 0] = (int16_t)clamp(x * 32767.0, -32768.0, 32767.0);
|
||||
randomValues[i * 4 + 1] = (int16_t)clamp(y * 32767.0, -32768.0, 32767.0);
|
||||
randomValues[i * 4 + 2] = (int16_t)clamp(z * 32767.0, -32768.0, 32767.0);
|
||||
randomValues[i * 4 + 3] = (int16_t)clamp(w * 32767.0, -32768.0, 32767.0);
|
||||
}
|
||||
|
||||
FString name;
|
||||
name.Format("SSAO.Random%d", quality);
|
||||
|
||||
hw_postprocess.Textures[name] = { 4, 4, PixelFormat::Rgba16_snorm, data };
|
||||
AmbientRandomTexture[quality] = name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PPAmbientOcclusion::UpdateSteps()
|
||||
{
|
||||
if (gl_ssao == 0 || hw_postprocess.SceneWidth == 0 || hw_postprocess.SceneHeight == 0)
|
||||
{
|
||||
hw_postprocess.Effects["AmbientOccludeScene"] = {};
|
||||
return;
|
||||
}
|
||||
|
||||
float bias = gl_ssao_bias;
|
||||
float aoRadius = gl_ssao_radius;
|
||||
const float blurAmount = gl_ssao_blur;
|
||||
float aoStrength = gl_ssao_strength;
|
||||
|
||||
//float tanHalfFovy = tan(fovy * (M_PI / 360.0f));
|
||||
float tanHalfFovy = 1.0f / hw_postprocess.m5;
|
||||
float invFocalLenX = tanHalfFovy * (hw_postprocess.SceneWidth / (float)hw_postprocess.SceneHeight);
|
||||
float invFocalLenY = tanHalfFovy;
|
||||
float nDotVBias = clamp(bias, 0.0f, 1.0f);
|
||||
float r2 = aoRadius * aoRadius;
|
||||
|
||||
float blurSharpness = 1.0f / blurAmount;
|
||||
|
||||
auto sceneScale = screen->SceneScale();
|
||||
auto sceneOffset = screen->SceneOffset();
|
||||
|
||||
int randomTexture = clamp(gl_ssao - 1, 0, NumAmbientRandomTextures - 1);
|
||||
|
||||
LinearDepthUniforms linearUniforms;
|
||||
linearUniforms.SampleIndex = 0;
|
||||
linearUniforms.LinearizeDepthA = 1.0f / screen->GetZFar() - 1.0f / screen->GetZNear();
|
||||
linearUniforms.LinearizeDepthB = MAX(1.0f / screen->GetZNear(), 1.e-8f);
|
||||
linearUniforms.InverseDepthRangeA = 1.0f;
|
||||
linearUniforms.InverseDepthRangeB = 0.0f;
|
||||
linearUniforms.Scale = sceneScale;
|
||||
linearUniforms.Offset = sceneOffset;
|
||||
|
||||
SSAOUniforms ssaoUniforms;
|
||||
ssaoUniforms.SampleIndex = 0;
|
||||
ssaoUniforms.UVToViewA = { 2.0f * invFocalLenX, 2.0f * invFocalLenY };
|
||||
ssaoUniforms.UVToViewB = { -invFocalLenX, -invFocalLenY };
|
||||
ssaoUniforms.InvFullResolution = { 1.0f / AmbientWidth, 1.0f / AmbientHeight };
|
||||
ssaoUniforms.NDotVBias = nDotVBias;
|
||||
ssaoUniforms.NegInvR2 = -1.0f / r2;
|
||||
ssaoUniforms.RadiusToScreen = aoRadius * 0.5f / tanHalfFovy * AmbientHeight;
|
||||
ssaoUniforms.AOMultiplier = 1.0f / (1.0f - nDotVBias);
|
||||
ssaoUniforms.AOStrength = aoStrength;
|
||||
ssaoUniforms.Scale = sceneScale;
|
||||
ssaoUniforms.Offset = sceneOffset;
|
||||
|
||||
DepthBlurUniforms blurUniforms;
|
||||
blurUniforms.BlurSharpness = blurSharpness;
|
||||
blurUniforms.InvFullResolution = { 1.0f / AmbientWidth, 1.0f / AmbientHeight };
|
||||
blurUniforms.PowExponent = gl_ssao_exponent;
|
||||
|
||||
AmbientCombineUniforms combineUniforms;
|
||||
combineUniforms.SampleCount = gl_multisample;
|
||||
combineUniforms.Scale = screen->SceneScale();
|
||||
combineUniforms.Offset = screen->SceneOffset();
|
||||
|
||||
IntRect ambientViewport;
|
||||
ambientViewport.left = 0;
|
||||
ambientViewport.top = 0;
|
||||
ambientViewport.width = AmbientWidth;
|
||||
ambientViewport.height = AmbientHeight;
|
||||
|
||||
TArray<PPStep> steps;
|
||||
|
||||
// Calculate linear depth values
|
||||
{
|
||||
PPStep step;
|
||||
step.ShaderName = gl_multisample > 1 ? "SSAO.LinearDepthMS" : "SSAO.LinearDepth";
|
||||
step.Uniforms.Set(linearUniforms);
|
||||
step.Viewport = ambientViewport;
|
||||
step.SetInputSceneDepth(0);
|
||||
step.SetInputSceneColor(1);
|
||||
step.SetOutputTexture("SSAO.LinearDepth");
|
||||
step.SetNoBlend();
|
||||
steps.Push(step);
|
||||
}
|
||||
|
||||
// Apply ambient occlusion
|
||||
{
|
||||
PPStep step;
|
||||
step.ShaderName = gl_multisample > 1 ? "SSAO.AmbientOccludeMS" : "SSAO.AmbientOcclude";
|
||||
step.Uniforms.Set(ssaoUniforms);
|
||||
step.Viewport = ambientViewport;
|
||||
step.SetInputTexture(0, "SSAO.LinearDepth");
|
||||
step.SetInputSceneNormal(1);
|
||||
step.SetInputTexture(2, AmbientRandomTexture[randomTexture], PPFilterMode::Nearest, PPWrapMode::Repeat);
|
||||
step.SetOutputTexture("SSAO.Ambient0");
|
||||
step.SetNoBlend();
|
||||
steps.Push(step);
|
||||
}
|
||||
|
||||
// Blur SSAO texture
|
||||
if (gl_ssao_debug < 2)
|
||||
{
|
||||
PPStep step;
|
||||
step.ShaderName = "SSAO.BlurHorizontal";
|
||||
step.Uniforms.Set(blurUniforms);
|
||||
step.Viewport = ambientViewport;
|
||||
step.SetInputTexture(0, "SSAO.Ambient0");
|
||||
step.SetOutputTexture("SSAO.Ambient1");
|
||||
step.SetNoBlend();
|
||||
steps.Push(step);
|
||||
|
||||
step.ShaderName = "SSAO.BlurVertical";
|
||||
step.SetInputTexture(0, "SSAO.Ambient1");
|
||||
step.SetOutputTexture("SSAO.Ambient0");
|
||||
steps.Push(step);
|
||||
}
|
||||
|
||||
// Add SSAO back to scene texture:
|
||||
{
|
||||
PPStep step;
|
||||
step.ShaderName = gl_multisample > 1 ? "SSAO.CombineMS" : "SSAO.Combine";
|
||||
step.Uniforms.Set(combineUniforms);
|
||||
step.Viewport = screen->mSceneViewport;
|
||||
step.SetInputTexture(0, "SSAO.Ambient0", PPFilterMode::Linear);
|
||||
step.SetInputSceneFog(1);
|
||||
step.SetOutputSceneColor();
|
||||
if (gl_ssao_debug != 0)
|
||||
step.SetNoBlend();
|
||||
else
|
||||
step.SetAlphaBlend();
|
||||
steps.Push(step);
|
||||
}
|
||||
|
||||
hw_postprocess.Effects["AmbientOccludeScene"] = steps;
|
||||
}
|
599
src/rendering/hwrenderer/postprocessing/hw_postprocess.h
Normal file
599
src/rendering/hwrenderer/postprocessing/hw_postprocess.h
Normal file
|
@ -0,0 +1,599 @@
|
|||
#pragma once
|
||||
|
||||
#include "hwrenderer/data/shaderuniforms.h"
|
||||
#include <memory>
|
||||
|
||||
typedef FString PPTextureName;
|
||||
typedef FString PPShaderName;
|
||||
|
||||
typedef FRenderStyle PPBlendMode;
|
||||
typedef IntRect PPViewport;
|
||||
|
||||
enum class PPFilterMode { Nearest, Linear };
|
||||
enum class PPWrapMode { Clamp, Repeat };
|
||||
enum class PPTextureType { CurrentPipelineTexture, NextPipelineTexture, PPTexture, SceneColor, SceneFog, SceneNormal, SceneDepth };
|
||||
|
||||
class PPTextureInput
|
||||
{
|
||||
public:
|
||||
PPFilterMode Filter;
|
||||
PPWrapMode Wrap;
|
||||
PPTextureType Type;
|
||||
PPTextureName Texture;
|
||||
};
|
||||
|
||||
class PPOutput
|
||||
{
|
||||
public:
|
||||
PPTextureType Type;
|
||||
PPTextureName Texture;
|
||||
};
|
||||
|
||||
class PPUniforms
|
||||
{
|
||||
public:
|
||||
PPUniforms()
|
||||
{
|
||||
}
|
||||
|
||||
PPUniforms(const PPUniforms &src)
|
||||
{
|
||||
Data = src.Data;
|
||||
}
|
||||
|
||||
~PPUniforms()
|
||||
{
|
||||
Clear();
|
||||
}
|
||||
|
||||
PPUniforms &operator=(const PPUniforms &src)
|
||||
{
|
||||
Data = src.Data;
|
||||
return *this;
|
||||
}
|
||||
|
||||
void Clear()
|
||||
{
|
||||
Data.Clear();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void Set(const T &v)
|
||||
{
|
||||
if (Data.Size() != (int)sizeof(T))
|
||||
{
|
||||
Data.Resize(sizeof(T));
|
||||
memcpy(Data.Data(), &v, Data.Size());
|
||||
}
|
||||
}
|
||||
|
||||
TArray<uint8_t> Data;
|
||||
};
|
||||
|
||||
class PPStep
|
||||
{
|
||||
public:
|
||||
void SetInputTexture(int index, PPTextureName texture, PPFilterMode filter = PPFilterMode::Nearest, PPWrapMode wrap = PPWrapMode::Clamp)
|
||||
{
|
||||
if ((int)Textures.Size() < index + 1)
|
||||
Textures.Resize(index + 1);
|
||||
auto &tex = Textures[index];
|
||||
tex.Filter = filter;
|
||||
tex.Wrap = wrap;
|
||||
tex.Type = PPTextureType::PPTexture;
|
||||
tex.Texture = texture;
|
||||
}
|
||||
|
||||
void SetInputCurrent(int index, PPFilterMode filter = PPFilterMode::Nearest, PPWrapMode wrap = PPWrapMode::Clamp)
|
||||
{
|
||||
SetInputSpecialType(index, PPTextureType::CurrentPipelineTexture, filter, wrap);
|
||||
}
|
||||
|
||||
void SetInputSceneColor(int index, PPFilterMode filter = PPFilterMode::Nearest, PPWrapMode wrap = PPWrapMode::Clamp)
|
||||
{
|
||||
SetInputSpecialType(index, PPTextureType::SceneColor, filter, wrap);
|
||||
}
|
||||
|
||||
void SetInputSceneFog(int index, PPFilterMode filter = PPFilterMode::Nearest, PPWrapMode wrap = PPWrapMode::Clamp)
|
||||
{
|
||||
SetInputSpecialType(index, PPTextureType::SceneFog, filter, wrap);
|
||||
}
|
||||
|
||||
void SetInputSceneNormal(int index, PPFilterMode filter = PPFilterMode::Nearest, PPWrapMode wrap = PPWrapMode::Clamp)
|
||||
{
|
||||
SetInputSpecialType(index, PPTextureType::SceneNormal, filter, wrap);
|
||||
}
|
||||
|
||||
void SetInputSceneDepth(int index, PPFilterMode filter = PPFilterMode::Nearest, PPWrapMode wrap = PPWrapMode::Clamp)
|
||||
{
|
||||
SetInputSpecialType(index, PPTextureType::SceneDepth, filter, wrap);
|
||||
}
|
||||
|
||||
void SetInputSpecialType(int index, PPTextureType type, PPFilterMode filter = PPFilterMode::Nearest, PPWrapMode wrap = PPWrapMode::Clamp)
|
||||
{
|
||||
if ((int)Textures.Size() < index + 1)
|
||||
Textures.Resize(index + 1);
|
||||
auto &tex = Textures[index];
|
||||
tex.Filter = filter;
|
||||
tex.Wrap = wrap;
|
||||
tex.Type = type;
|
||||
tex.Texture = {};
|
||||
}
|
||||
|
||||
void SetOutputTexture(PPTextureName texture)
|
||||
{
|
||||
Output.Type = PPTextureType::PPTexture;
|
||||
Output.Texture = texture;
|
||||
}
|
||||
|
||||
void SetOutputCurrent()
|
||||
{
|
||||
Output.Type = PPTextureType::CurrentPipelineTexture;
|
||||
Output.Texture = {};
|
||||
}
|
||||
|
||||
void SetOutputNext()
|
||||
{
|
||||
Output.Type = PPTextureType::NextPipelineTexture;
|
||||
Output.Texture = {};
|
||||
}
|
||||
|
||||
void SetOutputSceneColor()
|
||||
{
|
||||
Output.Type = PPTextureType::SceneColor;
|
||||
Output.Texture = {};
|
||||
}
|
||||
|
||||
void SetNoBlend()
|
||||
{
|
||||
BlendMode.BlendOp = STYLEOP_Add;
|
||||
BlendMode.SrcAlpha = STYLEALPHA_One;
|
||||
BlendMode.DestAlpha = STYLEALPHA_Zero;
|
||||
BlendMode.Flags = 0;
|
||||
}
|
||||
|
||||
void SetAdditiveBlend()
|
||||
{
|
||||
BlendMode.BlendOp = STYLEOP_Add;
|
||||
BlendMode.SrcAlpha = STYLEALPHA_One;
|
||||
BlendMode.DestAlpha = STYLEALPHA_One;
|
||||
BlendMode.Flags = 0;
|
||||
}
|
||||
|
||||
void SetAlphaBlend()
|
||||
{
|
||||
BlendMode.BlendOp = STYLEOP_Add;
|
||||
BlendMode.SrcAlpha = STYLEALPHA_Src;
|
||||
BlendMode.DestAlpha = STYLEALPHA_InvSrc;
|
||||
BlendMode.Flags = 0;
|
||||
}
|
||||
|
||||
PPShaderName ShaderName;
|
||||
TArray<PPTextureInput> Textures;
|
||||
PPUniforms Uniforms;
|
||||
PPViewport Viewport;
|
||||
PPBlendMode BlendMode;
|
||||
PPOutput Output;
|
||||
};
|
||||
|
||||
enum class PixelFormat
|
||||
{
|
||||
Rgba8,
|
||||
Rgba16f,
|
||||
R32f,
|
||||
Rg16f,
|
||||
Rgba16_snorm
|
||||
};
|
||||
|
||||
class PPTextureDesc
|
||||
{
|
||||
public:
|
||||
PPTextureDesc() { }
|
||||
PPTextureDesc(int width, int height, PixelFormat format, std::shared_ptr<void> data = {}) : Width(width), Height(height), Format(format), Data(data) { }
|
||||
|
||||
int Width;
|
||||
int Height;
|
||||
PixelFormat Format;
|
||||
std::shared_ptr<void> Data;
|
||||
};
|
||||
|
||||
class PPShader
|
||||
{
|
||||
public:
|
||||
PPShader() { }
|
||||
PPShader(const FString &fragment, const FString &defines, const std::vector<UniformFieldDesc> &uniforms, int version = 330) : FragmentShader(fragment), Defines(defines), Uniforms(uniforms), Version(version) { }
|
||||
|
||||
FString VertexShader = "shaders/glsl/screenquad.vp";
|
||||
FString FragmentShader;
|
||||
FString Defines;
|
||||
std::vector<UniformFieldDesc> Uniforms;
|
||||
int Version = 330;
|
||||
};
|
||||
|
||||
class PPEffectManager
|
||||
{
|
||||
public:
|
||||
virtual ~PPEffectManager() { }
|
||||
virtual void DeclareShaders() { }
|
||||
virtual void UpdateTextures() { }
|
||||
virtual void UpdateSteps() { }
|
||||
};
|
||||
|
||||
class Postprocess
|
||||
{
|
||||
public:
|
||||
Postprocess();
|
||||
~Postprocess();
|
||||
|
||||
void DeclareShaders() { for (unsigned int i = 0; i < Managers.Size(); i++) Managers[i]->DeclareShaders(); }
|
||||
void UpdateTextures() { for (unsigned int i = 0; i < Managers.Size(); i++) Managers[i]->UpdateTextures(); }
|
||||
void UpdateSteps() { for (unsigned int i = 0; i < Managers.Size(); i++) Managers[i]->UpdateSteps(); }
|
||||
|
||||
int SceneWidth = 0;
|
||||
int SceneHeight = 0;
|
||||
int fixedcm = 0;
|
||||
float gameinfobluramount = 0.0f;
|
||||
float m5 = 0.0f;
|
||||
|
||||
TMap<FString, TArray<PPStep>> Effects;
|
||||
TMap<FString, PPTextureDesc> Textures;
|
||||
TMap<FString, PPShader> Shaders;
|
||||
|
||||
TArray<PPEffectManager*> Managers;
|
||||
};
|
||||
|
||||
extern Postprocess hw_postprocess;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct ExtractUniforms
|
||||
{
|
||||
FVector2 Scale;
|
||||
FVector2 Offset;
|
||||
|
||||
static std::vector<UniformFieldDesc> Desc()
|
||||
{
|
||||
return
|
||||
{
|
||||
{ "Scale", UniformType::Vec2, offsetof(ExtractUniforms, Scale) },
|
||||
{ "Offset", UniformType::Vec2, offsetof(ExtractUniforms, Offset) }
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
struct BlurUniforms
|
||||
{
|
||||
float SampleWeights[8];
|
||||
|
||||
static std::vector<UniformFieldDesc> Desc()
|
||||
{
|
||||
return
|
||||
{
|
||||
{ "SampleWeights0", UniformType::Float, offsetof(BlurUniforms, SampleWeights[0]) },
|
||||
{ "SampleWeights1", UniformType::Float, offsetof(BlurUniforms, SampleWeights[1]) },
|
||||
{ "SampleWeights2", UniformType::Float, offsetof(BlurUniforms, SampleWeights[2]) },
|
||||
{ "SampleWeights3", UniformType::Float, offsetof(BlurUniforms, SampleWeights[3]) },
|
||||
{ "SampleWeights4", UniformType::Float, offsetof(BlurUniforms, SampleWeights[4]) },
|
||||
{ "SampleWeights5", UniformType::Float, offsetof(BlurUniforms, SampleWeights[5]) },
|
||||
{ "SampleWeights6", UniformType::Float, offsetof(BlurUniforms, SampleWeights[6]) },
|
||||
{ "SampleWeights7", UniformType::Float, offsetof(BlurUniforms, SampleWeights[7]) },
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
enum { NumBloomLevels = 4 };
|
||||
|
||||
class PPBlurLevel
|
||||
{
|
||||
public:
|
||||
PPViewport Viewport;
|
||||
PPTextureName VTexture;
|
||||
PPTextureName HTexture;
|
||||
};
|
||||
|
||||
class PPBloom : public PPEffectManager
|
||||
{
|
||||
public:
|
||||
void DeclareShaders() override;
|
||||
void UpdateTextures() override;
|
||||
void UpdateSteps() override;
|
||||
|
||||
private:
|
||||
void UpdateBlurSteps();
|
||||
|
||||
PPStep BlurStep(const BlurUniforms &blurUniforms, PPTextureName input, PPTextureName output, PPViewport viewport, bool vertical);
|
||||
|
||||
static float ComputeBlurGaussian(float n, float theta);
|
||||
static void ComputeBlurSamples(int sampleCount, float blurAmount, float *sampleWeights);
|
||||
|
||||
PPBlurLevel levels[NumBloomLevels];
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct LensUniforms
|
||||
{
|
||||
float AspectRatio;
|
||||
float Scale;
|
||||
float Padding0, Padding1;
|
||||
FVector4 LensDistortionCoefficient;
|
||||
FVector4 CubicDistortionValue;
|
||||
|
||||
static std::vector<UniformFieldDesc> Desc()
|
||||
{
|
||||
return
|
||||
{
|
||||
{ "Aspect", UniformType::Float, offsetof(LensUniforms, AspectRatio) },
|
||||
{ "Scale", UniformType::Float, offsetof(LensUniforms, Scale) },
|
||||
{ "Padding0", UniformType::Float, offsetof(LensUniforms, Padding0) },
|
||||
{ "Padding1", UniformType::Float, offsetof(LensUniforms, Padding1) },
|
||||
{ "k", UniformType::Vec4, offsetof(LensUniforms, LensDistortionCoefficient) },
|
||||
{ "kcube", UniformType::Vec4, offsetof(LensUniforms, CubicDistortionValue) }
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
class PPLensDistort : public PPEffectManager
|
||||
{
|
||||
public:
|
||||
void DeclareShaders() override;
|
||||
void UpdateSteps() override;
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct FXAAUniforms
|
||||
{
|
||||
FVector2 ReciprocalResolution;
|
||||
float Padding0, Padding1;
|
||||
|
||||
static std::vector<UniformFieldDesc> Desc()
|
||||
{
|
||||
return
|
||||
{
|
||||
{ "ReciprocalResolution", UniformType::Vec2, offsetof(FXAAUniforms, ReciprocalResolution) },
|
||||
{ "Padding0", UniformType::Float, offsetof(FXAAUniforms, Padding0) },
|
||||
{ "Padding1", UniformType::Float, offsetof(FXAAUniforms, Padding1) }
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
class PPFXAA : public PPEffectManager
|
||||
{
|
||||
public:
|
||||
void DeclareShaders() override;
|
||||
void UpdateSteps() override;
|
||||
|
||||
private:
|
||||
int GetMaxVersion();
|
||||
FString GetDefines();
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct ExposureExtractUniforms
|
||||
{
|
||||
FVector2 Scale;
|
||||
FVector2 Offset;
|
||||
|
||||
static std::vector<UniformFieldDesc> Desc()
|
||||
{
|
||||
return
|
||||
{
|
||||
{ "Scale", UniformType::Vec2, offsetof(ExposureExtractUniforms, Scale) },
|
||||
{ "Offset", UniformType::Vec2, offsetof(ExposureExtractUniforms, Offset) }
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
struct ExposureCombineUniforms
|
||||
{
|
||||
float ExposureBase;
|
||||
float ExposureMin;
|
||||
float ExposureScale;
|
||||
float ExposureSpeed;
|
||||
|
||||
static std::vector<UniformFieldDesc> Desc()
|
||||
{
|
||||
return
|
||||
{
|
||||
{ "ExposureBase", UniformType::Float, offsetof(ExposureCombineUniforms, ExposureBase) },
|
||||
{ "ExposureMin", UniformType::Float, offsetof(ExposureCombineUniforms, ExposureMin) },
|
||||
{ "ExposureScale", UniformType::Float, offsetof(ExposureCombineUniforms, ExposureScale) },
|
||||
{ "ExposureSpeed", UniformType::Float, offsetof(ExposureCombineUniforms, ExposureSpeed) }
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
class PPExposureLevel
|
||||
{
|
||||
public:
|
||||
PPViewport Viewport;
|
||||
PPTextureName Texture;
|
||||
};
|
||||
|
||||
class PPCameraExposure : public PPEffectManager
|
||||
{
|
||||
public:
|
||||
void DeclareShaders() override;
|
||||
void UpdateTextures() override;
|
||||
void UpdateSteps() override;
|
||||
|
||||
private:
|
||||
TArray<PPExposureLevel> ExposureLevels;
|
||||
bool FirstExposureFrame = true;
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct ColormapUniforms
|
||||
{
|
||||
FVector4 MapStart;
|
||||
FVector4 MapRange;
|
||||
|
||||
static std::vector<UniformFieldDesc> Desc()
|
||||
{
|
||||
return
|
||||
{
|
||||
{ "uFixedColormapStart", UniformType::Vec4, offsetof(ColormapUniforms, MapStart) },
|
||||
{ "uFixedColormapRange", UniformType::Vec4, offsetof(ColormapUniforms, MapRange) },
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
class PPColormap : public PPEffectManager
|
||||
{
|
||||
public:
|
||||
void DeclareShaders() override;
|
||||
void UpdateSteps() override;
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class PPTonemap : public PPEffectManager
|
||||
{
|
||||
public:
|
||||
void DeclareShaders() override;
|
||||
void UpdateTextures() override;
|
||||
void UpdateSteps() override;
|
||||
|
||||
enum TonemapMode
|
||||
{
|
||||
None,
|
||||
Uncharted2,
|
||||
HejlDawson,
|
||||
Reinhard,
|
||||
Linear,
|
||||
Palette,
|
||||
NumTonemapModes
|
||||
};
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct LinearDepthUniforms
|
||||
{
|
||||
int SampleIndex;
|
||||
float LinearizeDepthA;
|
||||
float LinearizeDepthB;
|
||||
float InverseDepthRangeA;
|
||||
float InverseDepthRangeB;
|
||||
float Padding0, Padding1, Padding2;
|
||||
FVector2 Scale;
|
||||
FVector2 Offset;
|
||||
|
||||
static std::vector<UniformFieldDesc> Desc()
|
||||
{
|
||||
return
|
||||
{
|
||||
{ "SampleIndex", UniformType::Int, offsetof(LinearDepthUniforms, SampleIndex) },
|
||||
{ "LinearizeDepthA", UniformType::Float, offsetof(LinearDepthUniforms, LinearizeDepthA) },
|
||||
{ "LinearizeDepthB", UniformType::Float, offsetof(LinearDepthUniforms, LinearizeDepthB) },
|
||||
{ "InverseDepthRangeA", UniformType::Float, offsetof(LinearDepthUniforms, InverseDepthRangeA) },
|
||||
{ "InverseDepthRangeB", UniformType::Float, offsetof(LinearDepthUniforms, InverseDepthRangeB) },
|
||||
{ "Padding0", UniformType::Float, offsetof(LinearDepthUniforms, Padding0) },
|
||||
{ "Padding1", UniformType::Float, offsetof(LinearDepthUniforms, Padding1) },
|
||||
{ "Padding2", UniformType::Float, offsetof(LinearDepthUniforms, Padding2) },
|
||||
{ "Scale", UniformType::Vec2, offsetof(LinearDepthUniforms, Scale) },
|
||||
{ "Offset", UniformType::Vec2, offsetof(LinearDepthUniforms, Offset) }
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
struct SSAOUniforms
|
||||
{
|
||||
FVector2 UVToViewA;
|
||||
FVector2 UVToViewB;
|
||||
FVector2 InvFullResolution;
|
||||
float NDotVBias;
|
||||
float NegInvR2;
|
||||
float RadiusToScreen;
|
||||
float AOMultiplier;
|
||||
float AOStrength;
|
||||
int SampleIndex;
|
||||
float Padding0, Padding1;
|
||||
FVector2 Scale;
|
||||
FVector2 Offset;
|
||||
|
||||
static std::vector<UniformFieldDesc> Desc()
|
||||
{
|
||||
return
|
||||
{
|
||||
{ "UVToViewA", UniformType::Vec2, offsetof(SSAOUniforms, UVToViewA) },
|
||||
{ "UVToViewB", UniformType::Vec2, offsetof(SSAOUniforms, UVToViewB) },
|
||||
{ "InvFullResolution", UniformType::Vec2, offsetof(SSAOUniforms, InvFullResolution) },
|
||||
{ "NDotVBias", UniformType::Float, offsetof(SSAOUniforms, NDotVBias) },
|
||||
{ "NegInvR2", UniformType::Float, offsetof(SSAOUniforms, NegInvR2) },
|
||||
{ "RadiusToScreen", UniformType::Float, offsetof(SSAOUniforms, RadiusToScreen) },
|
||||
{ "AOMultiplier", UniformType::Float, offsetof(SSAOUniforms, AOMultiplier) },
|
||||
{ "AOStrength", UniformType::Float, offsetof(SSAOUniforms, AOStrength) },
|
||||
{ "SampleIndex", UniformType::Int, offsetof(SSAOUniforms, SampleIndex) },
|
||||
{ "Padding0", UniformType::Float, offsetof(SSAOUniforms, Padding0) },
|
||||
{ "Padding1", UniformType::Float, offsetof(SSAOUniforms, Padding1) },
|
||||
{ "Scale", UniformType::Vec2, offsetof(SSAOUniforms, Scale) },
|
||||
{ "Offset", UniformType::Vec2, offsetof(SSAOUniforms, Offset) },
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
struct DepthBlurUniforms
|
||||
{
|
||||
float BlurSharpness;
|
||||
float PowExponent;
|
||||
FVector2 InvFullResolution;
|
||||
|
||||
static std::vector<UniformFieldDesc> Desc()
|
||||
{
|
||||
return
|
||||
{
|
||||
{ "BlurSharpness", UniformType::Float, offsetof(DepthBlurUniforms, BlurSharpness) },
|
||||
{ "PowExponent", UniformType::Float, offsetof(DepthBlurUniforms, PowExponent) },
|
||||
{ "InvFullResolution", UniformType::Vec2, offsetof(DepthBlurUniforms, InvFullResolution) }
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
struct AmbientCombineUniforms
|
||||
{
|
||||
int SampleCount;
|
||||
int Padding0, Padding1, Padding2;
|
||||
FVector2 Scale;
|
||||
FVector2 Offset;
|
||||
|
||||
static std::vector<UniformFieldDesc> Desc()
|
||||
{
|
||||
return
|
||||
{
|
||||
{ "SampleCount", UniformType::Int, offsetof(AmbientCombineUniforms, SampleCount) },
|
||||
{ "Padding0", UniformType::Int, offsetof(AmbientCombineUniforms, Padding0) },
|
||||
{ "Padding1", UniformType::Int, offsetof(AmbientCombineUniforms, Padding1) },
|
||||
{ "Padding2", UniformType::Int, offsetof(AmbientCombineUniforms, Padding2) },
|
||||
{ "Scale", UniformType::Vec2, offsetof(AmbientCombineUniforms, Scale) },
|
||||
{ "Offset", UniformType::Vec2, offsetof(AmbientCombineUniforms, Offset) }
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
class PPAmbientOcclusion : public PPEffectManager
|
||||
{
|
||||
public:
|
||||
void DeclareShaders() override;
|
||||
void UpdateTextures() override;
|
||||
void UpdateSteps() override;
|
||||
|
||||
private:
|
||||
enum Quality
|
||||
{
|
||||
Off,
|
||||
LowQuality,
|
||||
MediumQuality,
|
||||
HighQuality,
|
||||
NumQualityModes
|
||||
};
|
||||
|
||||
int AmbientWidth = 0;
|
||||
int AmbientHeight = 0;
|
||||
|
||||
enum { NumAmbientRandomTextures = 3 };
|
||||
PPTextureName AmbientRandomTexture[NumAmbientRandomTextures];
|
||||
};
|
104
src/rendering/hwrenderer/postprocessing/hw_postprocess_cvars.cpp
Normal file
104
src/rendering/hwrenderer/postprocessing/hw_postprocess_cvars.cpp
Normal file
|
@ -0,0 +1,104 @@
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Copyright(C) 2016 Magnus Norddahl
|
||||
// All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with this program. If not, see http://www.gnu.org/licenses/
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
/*
|
||||
** gl_postprocess.cpp
|
||||
** Post processing effects in the render pipeline
|
||||
**
|
||||
*/
|
||||
|
||||
#include "hw_postprocess_cvars.h"
|
||||
#include "v_video.h"
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// CVARs
|
||||
//
|
||||
//==========================================================================
|
||||
CVAR(Bool, gl_bloom, false, CVAR_ARCHIVE);
|
||||
CUSTOM_CVAR(Float, gl_bloom_amount, 1.4f, CVAR_ARCHIVE)
|
||||
{
|
||||
if (self < 0.1f) self = 0.1f;
|
||||
}
|
||||
|
||||
CVAR(Float, gl_exposure_scale, 1.3f, CVAR_ARCHIVE)
|
||||
CVAR(Float, gl_exposure_min, 0.35f, CVAR_ARCHIVE)
|
||||
CVAR(Float, gl_exposure_base, 0.35f, CVAR_ARCHIVE)
|
||||
CVAR(Float, gl_exposure_speed, 0.05f, CVAR_ARCHIVE)
|
||||
|
||||
CUSTOM_CVAR(Int, gl_tonemap, 0, CVAR_ARCHIVE)
|
||||
{
|
||||
if (self < 0 || self > 5)
|
||||
self = 0;
|
||||
}
|
||||
|
||||
CVAR(Bool, gl_lens, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||
|
||||
CVAR(Float, gl_lens_k, -0.12f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||
CVAR(Float, gl_lens_kcube, 0.1f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||
CVAR(Float, gl_lens_chromatic, 1.12f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||
|
||||
CUSTOM_CVAR(Int, gl_fxaa, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||
{
|
||||
if (self < 0 || self >= IFXAAShader::Count)
|
||||
{
|
||||
self = 0;
|
||||
}
|
||||
}
|
||||
|
||||
CUSTOM_CVAR(Int, gl_ssao, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||
{
|
||||
if (self < 0 || self > 3)
|
||||
self = 0;
|
||||
}
|
||||
|
||||
CUSTOM_CVAR(Int, gl_ssao_portals, 1, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||
{
|
||||
if (self < 0)
|
||||
self = 0;
|
||||
}
|
||||
|
||||
CVAR(Float, gl_ssao_strength, 0.7f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||
CVAR(Int, gl_ssao_debug, 0, 0)
|
||||
CVAR(Float, gl_ssao_bias, 0.2f, 0)
|
||||
CVAR(Float, gl_ssao_radius, 80.0f, 0)
|
||||
CUSTOM_CVAR(Float, gl_ssao_blur, 16.0f, 0)
|
||||
{
|
||||
if (self < 0.1f) self = 0.1f;
|
||||
}
|
||||
|
||||
CUSTOM_CVAR(Float, gl_ssao_exponent, 1.8f, 0)
|
||||
{
|
||||
if (self < 0.1f) self = 0.1f;
|
||||
}
|
||||
|
||||
CUSTOM_CVAR(Float, gl_paltonemap_powtable, 2.0f, CVAR_ARCHIVE | CVAR_NOINITCALL)
|
||||
{
|
||||
screen->UpdatePalette();
|
||||
}
|
||||
|
||||
CUSTOM_CVAR(Bool, gl_paltonemap_reverselookup, true, CVAR_ARCHIVE | CVAR_NOINITCALL)
|
||||
{
|
||||
screen->UpdatePalette();
|
||||
}
|
||||
|
||||
CVAR(Float, gl_menu_blur, -1.0f, CVAR_ARCHIVE)
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
#pragma once
|
||||
|
||||
#include "c_cvars.h"
|
||||
|
||||
class IFXAAShader
|
||||
{
|
||||
public:
|
||||
enum Quality
|
||||
{
|
||||
None,
|
||||
Low,
|
||||
Medium,
|
||||
High,
|
||||
Extreme,
|
||||
Count
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// CVARs
|
||||
//
|
||||
//==========================================================================
|
||||
EXTERN_CVAR(Bool, gl_bloom)
|
||||
EXTERN_CVAR(Float, gl_bloom_amount)
|
||||
EXTERN_CVAR(Float, gl_exposure_scale)
|
||||
EXTERN_CVAR(Float, gl_exposure_min)
|
||||
EXTERN_CVAR(Float, gl_exposure_base)
|
||||
EXTERN_CVAR(Float, gl_exposure_speed)
|
||||
EXTERN_CVAR(Int, gl_tonemap)
|
||||
EXTERN_CVAR(Int, gl_bloom_kernel_size)
|
||||
EXTERN_CVAR(Bool, gl_lens)
|
||||
EXTERN_CVAR(Float, gl_lens_k)
|
||||
EXTERN_CVAR(Float, gl_lens_kcube)
|
||||
EXTERN_CVAR(Float, gl_lens_chromatic)
|
||||
EXTERN_CVAR(Int, gl_fxaa)
|
||||
EXTERN_CVAR(Int, gl_ssao)
|
||||
EXTERN_CVAR(Int, gl_ssao_portals)
|
||||
EXTERN_CVAR(Float, gl_ssao_strength)
|
||||
EXTERN_CVAR(Int, gl_ssao_debug)
|
||||
EXTERN_CVAR(Float, gl_ssao_bias)
|
||||
EXTERN_CVAR(Float, gl_ssao_radius)
|
||||
EXTERN_CVAR(Float, gl_ssao_blur)
|
||||
EXTERN_CVAR(Float, gl_ssao_exponent)
|
||||
EXTERN_CVAR(Float, gl_paltonemap_powtable)
|
||||
EXTERN_CVAR(Bool, gl_paltonemap_reverselookup)
|
||||
EXTERN_CVAR(Float, gl_menu_blur)
|
||||
EXTERN_CVAR(Float, vid_brightness)
|
||||
EXTERN_CVAR(Float, vid_contrast)
|
||||
EXTERN_CVAR(Float, vid_saturation)
|
||||
EXTERN_CVAR(Int, gl_satformula)
|
||||
|
171
src/rendering/hwrenderer/postprocessing/hw_postprocessshader.cpp
Normal file
171
src/rendering/hwrenderer/postprocessing/hw_postprocessshader.cpp
Normal file
|
@ -0,0 +1,171 @@
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Copyright(C) 2017 Magnus Norddahl
|
||||
// All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with this program. If not, see http://www.gnu.org/licenses/
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
|
||||
#include "vm.h"
|
||||
#include "d_player.h"
|
||||
#include "hw_postprocessshader.h"
|
||||
|
||||
TArray<PostProcessShader> PostProcessShaders;
|
||||
|
||||
|
||||
static bool IsConsolePlayer(player_t *player)
|
||||
{
|
||||
AActor *activator = player ? player->mo : nullptr;
|
||||
if (activator == nullptr || activator->player == nullptr)
|
||||
return false;
|
||||
return int(activator->player - players) == consoleplayer;
|
||||
}
|
||||
|
||||
static void ShaderSetEnabled(player_t *player, const FString &shaderName, bool value)
|
||||
{
|
||||
if (IsConsolePlayer(player))
|
||||
{
|
||||
for (unsigned int i = 0; i < PostProcessShaders.Size(); i++)
|
||||
{
|
||||
PostProcessShader &shader = PostProcessShaders[i];
|
||||
if (shader.Name == shaderName)
|
||||
shader.Enabled = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION_NATIVE(_Shader, SetEnabled, ShaderSetEnabled)
|
||||
{
|
||||
PARAM_PROLOGUE;
|
||||
PARAM_POINTER(player, player_t);
|
||||
PARAM_STRING(shaderName);
|
||||
PARAM_BOOL(value);
|
||||
ShaderSetEnabled(player, shaderName, value);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ShaderSetUniform1f(player_t *player, const FString &shaderName, const FString &uniformName, double value)
|
||||
{
|
||||
if (IsConsolePlayer(player))
|
||||
{
|
||||
for (unsigned int i = 0; i < PostProcessShaders.Size(); i++)
|
||||
{
|
||||
PostProcessShader &shader = PostProcessShaders[i];
|
||||
if (shader.Name == shaderName)
|
||||
{
|
||||
double *vec4 = shader.Uniforms[uniformName].Values;
|
||||
vec4[0] = value;
|
||||
vec4[1] = 0.0;
|
||||
vec4[2] = 0.0;
|
||||
vec4[3] = 1.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION_NATIVE(_Shader, SetUniform1f, ShaderSetUniform1f)
|
||||
{
|
||||
PARAM_PROLOGUE;
|
||||
PARAM_POINTER(player, player_t);
|
||||
PARAM_STRING(shaderName);
|
||||
PARAM_STRING(uniformName);
|
||||
PARAM_FLOAT(value);
|
||||
ShaderSetUniform1f(player, shaderName, uniformName, value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(_Shader, SetUniform2f)
|
||||
{
|
||||
PARAM_PROLOGUE;
|
||||
PARAM_POINTER(player, player_t);
|
||||
PARAM_STRING(shaderName);
|
||||
PARAM_STRING(uniformName);
|
||||
PARAM_FLOAT(x);
|
||||
PARAM_FLOAT(y);
|
||||
|
||||
if (IsConsolePlayer(player))
|
||||
{
|
||||
for (unsigned int i = 0; i < PostProcessShaders.Size(); i++)
|
||||
{
|
||||
PostProcessShader &shader = PostProcessShaders[i];
|
||||
if (shader.Name == shaderName)
|
||||
{
|
||||
double *vec4 = shader.Uniforms[uniformName].Values;
|
||||
vec4[0] = x;
|
||||
vec4[1] = y;
|
||||
vec4[2] = 0.0;
|
||||
vec4[3] = 1.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(_Shader, SetUniform3f)
|
||||
{
|
||||
PARAM_PROLOGUE;
|
||||
PARAM_POINTER(player, player_t);
|
||||
PARAM_STRING(shaderName);
|
||||
PARAM_STRING(uniformName);
|
||||
PARAM_FLOAT(x);
|
||||
PARAM_FLOAT(y);
|
||||
PARAM_FLOAT(z);
|
||||
|
||||
if (IsConsolePlayer(player))
|
||||
{
|
||||
for (unsigned int i = 0; i < PostProcessShaders.Size(); i++)
|
||||
{
|
||||
PostProcessShader &shader = PostProcessShaders[i];
|
||||
if (shader.Name == shaderName)
|
||||
{
|
||||
double *vec4 = shader.Uniforms[uniformName].Values;
|
||||
vec4[0] = x;
|
||||
vec4[1] = y;
|
||||
vec4[2] = z;
|
||||
vec4[3] = 1.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(_Shader, SetUniform1i)
|
||||
{
|
||||
PARAM_PROLOGUE;
|
||||
PARAM_POINTER(player, player_t);
|
||||
PARAM_STRING(shaderName);
|
||||
PARAM_STRING(uniformName);
|
||||
PARAM_INT(value);
|
||||
|
||||
if (IsConsolePlayer(player))
|
||||
{
|
||||
for (unsigned int i = 0; i < PostProcessShaders.Size(); i++)
|
||||
{
|
||||
PostProcessShader &shader = PostProcessShaders[i];
|
||||
if (shader.Name == shaderName)
|
||||
{
|
||||
double *vec4 = shader.Uniforms[uniformName].Values;
|
||||
vec4[0] = (double)value;
|
||||
vec4[1] = 0.0;
|
||||
vec4[2] = 0.0;
|
||||
vec4[3] = 1.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
#pragma once
|
||||
|
||||
enum class PostProcessUniformType
|
||||
{
|
||||
Undefined,
|
||||
Int,
|
||||
Float,
|
||||
Vec2,
|
||||
Vec3
|
||||
};
|
||||
|
||||
struct PostProcessUniformValue
|
||||
{
|
||||
PostProcessUniformType Type = PostProcessUniformType::Undefined;
|
||||
double Values[4] = { 0.0, 0.0, 0.0, 0.0 };
|
||||
};
|
||||
|
||||
struct PostProcessShader
|
||||
{
|
||||
FString Target;
|
||||
FString ShaderLumpName;
|
||||
int ShaderVersion = 0;
|
||||
|
||||
FString Name;
|
||||
bool Enabled = false;
|
||||
|
||||
TMap<FString, PostProcessUniformValue> Uniforms;
|
||||
TMap<FString, FString> Textures;
|
||||
};
|
||||
|
||||
extern TArray<PostProcessShader> PostProcessShaders;
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Copyright(C) 2016 Christopher Bruns
|
||||
// All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with this program. If not, see http://www.gnu.org/licenses/
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
/*
|
||||
** gl_3dRowshader.cpp
|
||||
** Copy rendered texture to back buffer, possibly with gamma correction
|
||||
** while interleaving rows from two independent viewpoint textures,
|
||||
** representing the left-eye and right-eye views.
|
||||
**
|
||||
*/
|
||||
|
||||
#include "hw_present3dRowshader.h"
|
||||
|
||||
void FPresent3DCheckerShader::Bind(IRenderQueue *q)
|
||||
{
|
||||
if (!mShader)
|
||||
{
|
||||
Init("shaders/glsl/present_checker3d.fp", "shaders/glsl/presentChecker3d");
|
||||
}
|
||||
mShader->Bind(q);
|
||||
}
|
||||
|
||||
void FPresent3DColumnShader::Bind(IRenderQueue *q)
|
||||
{
|
||||
if (!mShader)
|
||||
{
|
||||
Init("shaders/glsl/present_column3d.fp", "shaders/glsl/presentColumn3d");
|
||||
}
|
||||
mShader->Bind(q);
|
||||
}
|
||||
|
||||
void FPresent3DRowShader::Bind(IRenderQueue *q)
|
||||
{
|
||||
if (!mShader)
|
||||
{
|
||||
Init("shaders/glsl/present_row3d.fp", "shaders/glsl/presentRow3d");
|
||||
}
|
||||
mShader->Bind(q);
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Copyright(C) 2015 Christopher Bruns
|
||||
// All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with this program. If not, see http://www.gnu.org/licenses/
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
/*
|
||||
** gl_present3dRowshader.h
|
||||
** Final composition and present shader for row-interleaved stereoscopic 3D mode
|
||||
**
|
||||
*/
|
||||
|
||||
#ifndef GL_PRESENT3DROWSHADER_H_
|
||||
#define GL_PRESENT3DROWSHADER_H_
|
||||
|
||||
#include "hw_shaderprogram.h"
|
||||
#include "hw_presentshader.h"
|
||||
|
||||
class FPresent3DCheckerShader : public FPresentShaderBase
|
||||
{
|
||||
public:
|
||||
void Bind(IRenderQueue *q) override;
|
||||
};
|
||||
|
||||
class FPresent3DColumnShader : public FPresentShaderBase
|
||||
{
|
||||
public:
|
||||
void Bind(IRenderQueue *q) override;
|
||||
};
|
||||
|
||||
class FPresent3DRowShader : public FPresentShaderBase
|
||||
{
|
||||
public:
|
||||
void Bind(IRenderQueue *q) override;
|
||||
};
|
||||
|
||||
// GL_PRESENT3DROWSHADER_H_
|
||||
#endif
|
50
src/rendering/hwrenderer/postprocessing/hw_presentshader.cpp
Normal file
50
src/rendering/hwrenderer/postprocessing/hw_presentshader.cpp
Normal file
|
@ -0,0 +1,50 @@
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Copyright(C) 2016 Magnus Norddahl
|
||||
// All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with this program. If not, see http://www.gnu.org/licenses/
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
/*
|
||||
** gl_presentshader.cpp
|
||||
** Copy rendered texture to back buffer, possibly with gamma correction
|
||||
**
|
||||
*/
|
||||
|
||||
#include "v_video.h"
|
||||
#include "hw_presentshader.h"
|
||||
|
||||
void FPresentShaderBase::Init(const char * vtx_shader_name, const char * program_name)
|
||||
{
|
||||
FString prolog = Uniforms.CreateDeclaration("Uniforms", UniformBlock::Desc());
|
||||
|
||||
mShader.reset(screen->CreateShaderProgram());
|
||||
mShader->Compile(IShaderProgram::Vertex, "shaders/glsl/screenquadscale.vp", prolog, 330);
|
||||
mShader->Compile(IShaderProgram::Fragment, vtx_shader_name, prolog, 330);
|
||||
mShader->Link(program_name);
|
||||
mShader->SetUniformBufferLocation(Uniforms.BindingPoint(), "Uniforms");
|
||||
Uniforms.Init();
|
||||
}
|
||||
|
||||
void FPresentShader::Bind(IRenderQueue *q)
|
||||
{
|
||||
if (!mShader)
|
||||
{
|
||||
Init("shaders/glsl/present.fp", "shaders/glsl/present");
|
||||
}
|
||||
mShader->Bind(q);
|
||||
}
|
54
src/rendering/hwrenderer/postprocessing/hw_presentshader.h
Normal file
54
src/rendering/hwrenderer/postprocessing/hw_presentshader.h
Normal file
|
@ -0,0 +1,54 @@
|
|||
#ifndef __GL_PRESENTSHADER_H
|
||||
#define __GL_PRESENTSHADER_H
|
||||
|
||||
#include "hwrenderer/postprocessing/hw_shaderprogram.h"
|
||||
|
||||
class FPresentShaderBase
|
||||
{
|
||||
public:
|
||||
virtual ~FPresentShaderBase() {}
|
||||
virtual void Bind(IRenderQueue *q) = 0;
|
||||
|
||||
struct UniformBlock
|
||||
{
|
||||
float InvGamma;
|
||||
float Contrast;
|
||||
float Brightness;
|
||||
float Saturation;
|
||||
int GrayFormula;
|
||||
int WindowPositionParity; // top-of-window might not be top-of-screen
|
||||
FVector2 Scale;
|
||||
float ColorScale;
|
||||
float Padding1, Padding2, Padding3;
|
||||
|
||||
static std::vector<UniformFieldDesc> Desc()
|
||||
{
|
||||
return
|
||||
{
|
||||
{ "InvGamma", UniformType::Float, offsetof(UniformBlock, InvGamma) },
|
||||
{ "Contrast", UniformType::Float, offsetof(UniformBlock, Contrast) },
|
||||
{ "Brightness", UniformType::Float, offsetof(UniformBlock, Brightness) },
|
||||
{ "Saturation", UniformType::Float, offsetof(UniformBlock, Saturation) },
|
||||
{ "GrayFormula", UniformType::Int, offsetof(UniformBlock, GrayFormula) },
|
||||
{ "WindowPositionParity", UniformType::Int, offsetof(UniformBlock, WindowPositionParity) },
|
||||
{ "UVScale", UniformType::Vec2, offsetof(UniformBlock, Scale) },
|
||||
{ "ColorScale", UniformType::Float, offsetof(UniformBlock, ColorScale) },
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
ShaderUniforms<UniformBlock, POSTPROCESS_BINDINGPOINT> Uniforms;
|
||||
|
||||
protected:
|
||||
virtual void Init(const char * vtx_shader_name, const char * program_name);
|
||||
std::unique_ptr<IShaderProgram> mShader;
|
||||
};
|
||||
|
||||
class FPresentShader : public FPresentShaderBase
|
||||
{
|
||||
public:
|
||||
void Bind(IRenderQueue *q) override;
|
||||
|
||||
};
|
||||
|
||||
#endif
|
33
src/rendering/hwrenderer/postprocessing/hw_shaderprogram.h
Normal file
33
src/rendering/hwrenderer/postprocessing/hw_shaderprogram.h
Normal file
|
@ -0,0 +1,33 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "hwrenderer/data/shaderuniforms.h"
|
||||
|
||||
class IRenderQueue;
|
||||
|
||||
class IShaderProgram
|
||||
{
|
||||
public:
|
||||
IShaderProgram() {}
|
||||
virtual ~IShaderProgram() {}
|
||||
|
||||
enum ShaderType
|
||||
{
|
||||
Vertex,
|
||||
Fragment,
|
||||
NumShaderTypes
|
||||
};
|
||||
|
||||
virtual void Compile(ShaderType type, const char *lumpName, const char *defines, int maxGlslVersion) = 0;
|
||||
virtual void Compile(ShaderType type, const char *name, const FString &code, const char *defines, int maxGlslVersion) = 0;
|
||||
virtual void Link(const char *name) = 0;
|
||||
virtual void SetUniformBufferLocation(int index, const char *name) = 0;
|
||||
|
||||
virtual void Bind(IRenderQueue *q) = 0; // the parameter here is just a preparation for Vulkan
|
||||
|
||||
private:
|
||||
IShaderProgram(const IShaderProgram &) = delete;
|
||||
IShaderProgram &operator=(const IShaderProgram &) = delete;
|
||||
};
|
|
@ -0,0 +1,40 @@
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Copyright(C) 2016 Magnus Norddahl
|
||||
// All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with this program. If not, see http://www.gnu.org/licenses/
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
|
||||
#include "files.h"
|
||||
#include "hw_shadowmapshader.h"
|
||||
|
||||
void FShadowMapShader::Bind(IRenderQueue *q)
|
||||
{
|
||||
if (!mShader)
|
||||
{
|
||||
FString prolog = Uniforms.CreateDeclaration("Uniforms", UniformBlock::Desc());
|
||||
|
||||
mShader.reset(screen->CreateShaderProgram());
|
||||
mShader->Compile(IShaderProgram::Vertex, "shaders/glsl/screenquad.vp", "", 430);
|
||||
mShader->Compile(IShaderProgram::Fragment, "shaders/glsl/shadowmap.fp", prolog, 430);
|
||||
mShader->Link("shaders/glsl/shadowmap");
|
||||
mShader->SetUniformBufferLocation(Uniforms.BindingPoint(), "Uniforms");
|
||||
Uniforms.Init();
|
||||
}
|
||||
mShader->Bind(q);
|
||||
}
|
34
src/rendering/hwrenderer/postprocessing/hw_shadowmapshader.h
Normal file
34
src/rendering/hwrenderer/postprocessing/hw_shadowmapshader.h
Normal file
|
@ -0,0 +1,34 @@
|
|||
#ifndef __GL_SHADOWMAPSHADER_H
|
||||
#define __GL_SHADOWMAPSHADER_H
|
||||
|
||||
#include "hwrenderer/postprocessing/hw_shaderprogram.h"
|
||||
|
||||
class FShadowMapShader
|
||||
{
|
||||
public:
|
||||
void Bind(IRenderQueue *q);
|
||||
|
||||
struct UniformBlock
|
||||
{
|
||||
float ShadowmapQuality;
|
||||
float Padding0, Padding1, Padding2;
|
||||
|
||||
static std::vector<UniformFieldDesc> Desc()
|
||||
{
|
||||
return
|
||||
{
|
||||
{ "ShadowmapQuality", UniformType::Float, offsetof(UniformBlock, ShadowmapQuality) },
|
||||
{ "Padding0", UniformType::Float, offsetof(UniformBlock, Padding0) },
|
||||
{ "Padding1", UniformType::Float, offsetof(UniformBlock, Padding1) },
|
||||
{ "Padding2", UniformType::Float, offsetof(UniformBlock, Padding2) },
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
ShaderUniforms<UniformBlock, POSTPROCESS_BINDINGPOINT> Uniforms;
|
||||
|
||||
private:
|
||||
std::unique_ptr<IShaderProgram> mShader;
|
||||
};
|
||||
|
||||
#endif
|
829
src/rendering/hwrenderer/scene/hw_bsp.cpp
Normal file
829
src/rendering/hwrenderer/scene/hw_bsp.cpp
Normal file
|
@ -0,0 +1,829 @@
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Copyright(C) 2000-2016 Christoph Oelckers
|
||||
// All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with this program. If not, see http://www.gnu.org/licenses/
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
/*
|
||||
** gl_bsp.cpp
|
||||
** Main rendering loop / BSP traversal / visibility clipping
|
||||
**
|
||||
**/
|
||||
|
||||
#include "p_lnspec.h"
|
||||
#include "p_local.h"
|
||||
#include "a_sharedglobal.h"
|
||||
#include "g_levellocals.h"
|
||||
#include "p_effect.h"
|
||||
#include "po_man.h"
|
||||
#include "m_fixed.h"
|
||||
#include "ctpl.h"
|
||||
#include "hwrenderer/scene/hw_fakeflat.h"
|
||||
#include "hwrenderer/scene/hw_clipper.h"
|
||||
#include "hwrenderer/scene/hw_drawstructs.h"
|
||||
#include "hwrenderer/scene/hw_drawinfo.h"
|
||||
#include "hwrenderer/scene/hw_portal.h"
|
||||
#include "hwrenderer/utility/hw_clock.h"
|
||||
#include "hwrenderer/data/flatvertices.h"
|
||||
#include <immintrin.h>
|
||||
|
||||
CVAR(Bool, gl_multithread, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||
|
||||
thread_local bool isWorkerThread;
|
||||
ctpl::thread_pool renderPool(1);
|
||||
bool inited = false;
|
||||
|
||||
struct RenderJob
|
||||
{
|
||||
enum
|
||||
{
|
||||
FlatJob,
|
||||
WallJob,
|
||||
SpriteJob,
|
||||
ParticleJob,
|
||||
PortalJob,
|
||||
TerminateJob // inserted when all work is done so that the worker can return.
|
||||
};
|
||||
|
||||
int type;
|
||||
subsector_t *sub;
|
||||
seg_t *seg;
|
||||
};
|
||||
|
||||
|
||||
class RenderJobQueue
|
||||
{
|
||||
RenderJob pool[300000]; // Way more than ever needed. The largest ever seen on a single viewpoint is around 40000.
|
||||
std::atomic<int> readindex{};
|
||||
std::atomic<int> writeindex{};
|
||||
public:
|
||||
void AddJob(int type, subsector_t *sub, seg_t *seg = nullptr)
|
||||
{
|
||||
// This does not check for array overflows. The pool should be large enough that it never hits the limit.
|
||||
|
||||
pool[writeindex] = { type, sub, seg };
|
||||
writeindex++; // update index only after the value has been written.
|
||||
}
|
||||
|
||||
RenderJob *GetJob()
|
||||
{
|
||||
if (readindex < writeindex) return &pool[readindex++];
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ReleaseAll()
|
||||
{
|
||||
readindex = 0;
|
||||
writeindex = 0;
|
||||
}
|
||||
};
|
||||
|
||||
static RenderJobQueue jobQueue; // One static queue is sufficient here. This code will never be called recursively.
|
||||
|
||||
void HWDrawInfo::WorkerThread()
|
||||
{
|
||||
sector_t *front, *back;
|
||||
|
||||
WTTotal.Clock();
|
||||
isWorkerThread = true; // for adding asserts in GL API code. The worker thread may never call any GL API.
|
||||
while (true)
|
||||
{
|
||||
auto job = jobQueue.GetJob();
|
||||
if (job == nullptr)
|
||||
{
|
||||
// The queue is empty. But yielding would be too costly here and possibly cause further delays down the line if the thread is halted.
|
||||
// So instead add a few pause instructions and retry immediately.
|
||||
_mm_pause();
|
||||
_mm_pause();
|
||||
_mm_pause();
|
||||
_mm_pause();
|
||||
_mm_pause();
|
||||
_mm_pause();
|
||||
_mm_pause();
|
||||
_mm_pause();
|
||||
_mm_pause();
|
||||
_mm_pause();
|
||||
}
|
||||
// Note that the main thread MUST have prepared the fake sectors that get used below!
|
||||
// This worker thread cannot prepare them itself without costly synchronization.
|
||||
else switch (job->type)
|
||||
{
|
||||
case RenderJob::TerminateJob:
|
||||
WTTotal.Unclock();
|
||||
return;
|
||||
|
||||
case RenderJob::WallJob:
|
||||
{
|
||||
GLWall wall;
|
||||
SetupWall.Clock();
|
||||
wall.sub = job->sub;
|
||||
|
||||
front = hw_FakeFlat(job->sub->sector, in_area, false);
|
||||
auto seg = job->seg;
|
||||
if (seg->backsector)
|
||||
{
|
||||
if (front->sectornum == seg->backsector->sectornum || (seg->sidedef->Flags & WALLF_POLYOBJ))
|
||||
{
|
||||
back = front;
|
||||
}
|
||||
else
|
||||
{
|
||||
back = hw_FakeFlat(seg->backsector, in_area, true);
|
||||
}
|
||||
}
|
||||
else back = nullptr;
|
||||
|
||||
wall.Process(this, job->seg, front, back);
|
||||
rendered_lines++;
|
||||
SetupWall.Unclock();
|
||||
break;
|
||||
}
|
||||
|
||||
case RenderJob::FlatJob:
|
||||
{
|
||||
GLFlat flat;
|
||||
SetupFlat.Clock();
|
||||
flat.section = job->sub->section;
|
||||
front = hw_FakeFlat(job->sub->render_sector, in_area, false);
|
||||
flat.ProcessSector(this, front);
|
||||
SetupFlat.Unclock();
|
||||
break;
|
||||
}
|
||||
|
||||
case RenderJob::SpriteJob:
|
||||
SetupSprite.Clock();
|
||||
front = hw_FakeFlat(job->sub->sector, in_area, false);
|
||||
RenderThings(job->sub, front);
|
||||
SetupSprite.Unclock();
|
||||
break;
|
||||
|
||||
case RenderJob::ParticleJob:
|
||||
SetupSprite.Clock();
|
||||
front = hw_FakeFlat(job->sub->sector, in_area, false);
|
||||
RenderParticles(job->sub, front);
|
||||
SetupSprite.Unclock();
|
||||
break;
|
||||
|
||||
case RenderJob::PortalJob:
|
||||
AddSubsectorToPortal((FSectorPortalGroup *)job->seg, job->sub);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
EXTERN_CVAR(Bool, gl_render_segs)
|
||||
|
||||
CVAR(Bool, gl_render_things, true, 0)
|
||||
CVAR(Bool, gl_render_walls, true, 0)
|
||||
CVAR(Bool, gl_render_flats, true, 0)
|
||||
|
||||
void HWDrawInfo::UnclipSubsector(subsector_t *sub)
|
||||
{
|
||||
int count = sub->numlines;
|
||||
seg_t * seg = sub->firstline;
|
||||
auto &clipper = *mClipper;
|
||||
|
||||
while (count--)
|
||||
{
|
||||
angle_t startAngle = clipper.GetClipAngle(seg->v2);
|
||||
angle_t endAngle = clipper.GetClipAngle(seg->v1);
|
||||
|
||||
// Back side, i.e. backface culling - read: endAngle >= startAngle!
|
||||
if (startAngle-endAngle >= ANGLE_180)
|
||||
{
|
||||
clipper.SafeRemoveClipRange(startAngle, endAngle);
|
||||
clipper.SetBlocked(false);
|
||||
}
|
||||
seg++;
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// R_AddLine
|
||||
// Clips the given segment
|
||||
// and adds any visible pieces to the line list.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void HWDrawInfo::AddLine (seg_t *seg, bool portalclip)
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
if (seg->linedef && seg->linedef->Index() == 38)
|
||||
{
|
||||
int a = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
sector_t * backsector = nullptr;
|
||||
|
||||
if (portalclip)
|
||||
{
|
||||
int clipres = mClipPortal->ClipSeg(seg, Viewpoint.Pos);
|
||||
if (clipres == PClip_InFront) return;
|
||||
}
|
||||
|
||||
auto &clipper = *mClipper;
|
||||
angle_t startAngle = clipper.GetClipAngle(seg->v2);
|
||||
angle_t endAngle = clipper.GetClipAngle(seg->v1);
|
||||
|
||||
// Back side, i.e. backface culling - read: endAngle >= startAngle!
|
||||
if (startAngle-endAngle<ANGLE_180)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (seg->sidedef == nullptr)
|
||||
{
|
||||
if (!(currentsubsector->flags & SSECMF_DRAWN))
|
||||
{
|
||||
if (clipper.SafeCheckRange(startAngle, endAngle))
|
||||
{
|
||||
currentsubsector->flags |= SSECMF_DRAWN;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!clipper.SafeCheckRange(startAngle, endAngle))
|
||||
{
|
||||
return;
|
||||
}
|
||||
currentsubsector->flags |= SSECMF_DRAWN;
|
||||
|
||||
uint8_t ispoly = uint8_t(seg->sidedef->Flags & WALLF_POLYOBJ);
|
||||
|
||||
if (!seg->backsector)
|
||||
{
|
||||
clipper.SafeAddClipRange(startAngle, endAngle);
|
||||
}
|
||||
else if (!ispoly) // Two-sided polyobjects never obstruct the view
|
||||
{
|
||||
if (currentsector->sectornum == seg->backsector->sectornum)
|
||||
{
|
||||
if (!seg->linedef->isVisualPortal())
|
||||
{
|
||||
FTexture * tex = TexMan.GetTexture(seg->sidedef->GetTexture(side_t::mid), true);
|
||||
if (!tex || !tex->isValid())
|
||||
{
|
||||
// nothing to do here!
|
||||
seg->linedef->validcount=validcount;
|
||||
return;
|
||||
}
|
||||
}
|
||||
backsector=currentsector;
|
||||
}
|
||||
else
|
||||
{
|
||||
// clipping checks are only needed when the backsector is not the same as the front sector
|
||||
if (in_area == area_default) in_area = hw_CheckViewArea(seg->v1, seg->v2, seg->frontsector, seg->backsector);
|
||||
|
||||
backsector = hw_FakeFlat(seg->backsector, in_area, true);
|
||||
|
||||
if (hw_CheckClip(seg->sidedef, currentsector, backsector))
|
||||
{
|
||||
clipper.SafeAddClipRange(startAngle, endAngle);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Backsector for polyobj segs is always the containing sector itself
|
||||
backsector = currentsector;
|
||||
}
|
||||
|
||||
seg->linedef->flags |= ML_MAPPED;
|
||||
|
||||
if (ispoly || seg->linedef->validcount!=validcount)
|
||||
{
|
||||
if (!ispoly) seg->linedef->validcount=validcount;
|
||||
|
||||
if (gl_render_walls)
|
||||
{
|
||||
if (multithread)
|
||||
{
|
||||
jobQueue.AddJob(RenderJob::WallJob, seg->Subsector, seg);
|
||||
}
|
||||
else
|
||||
{
|
||||
GLWall wall;
|
||||
SetupWall.Clock();
|
||||
wall.sub = seg->Subsector;
|
||||
wall.Process(this, seg, currentsector, backsector);
|
||||
rendered_lines++;
|
||||
SetupWall.Unclock();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// R_Subsector
|
||||
// Determine floor/ceiling planes.
|
||||
// Add sprites of things in sector.
|
||||
// Draw one or more line segments.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void HWDrawInfo::PolySubsector(subsector_t * sub)
|
||||
{
|
||||
int count = sub->numlines;
|
||||
seg_t * line = sub->firstline;
|
||||
|
||||
while (count--)
|
||||
{
|
||||
if (line->linedef)
|
||||
{
|
||||
AddLine (line, mClipPortal != nullptr);
|
||||
}
|
||||
line++;
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// RenderBSPNode
|
||||
// Renders all subsectors below a given node,
|
||||
// traversing subtree recursively.
|
||||
// Just call with BSP root.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void HWDrawInfo::RenderPolyBSPNode (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 = R_PointOnSide(viewx, viewy, bsp);
|
||||
|
||||
// Recursively divide front space (toward the viewer).
|
||||
RenderPolyBSPNode (bsp->children[side]);
|
||||
|
||||
// Possibly divide back space (away from the viewer).
|
||||
side ^= 1;
|
||||
|
||||
// It is not necessary to use the slower precise version here
|
||||
if (!mClipper->CheckBox(bsp->bbox[side]))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
node = bsp->children[side];
|
||||
}
|
||||
PolySubsector ((subsector_t *)((uint8_t *)node - 1));
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Unlilke the software renderer this function will only draw the walls,
|
||||
// not the flats. Those are handled as a whole by the parent subsector.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void HWDrawInfo::AddPolyobjs(subsector_t *sub)
|
||||
{
|
||||
if (sub->BSP == nullptr || sub->BSP->bDirty)
|
||||
{
|
||||
sub->BuildPolyBSP();
|
||||
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)
|
||||
{
|
||||
PolySubsector(&sub->BSP->Subsectors[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
RenderPolyBSPNode(&sub->BSP->Nodes.Last());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void HWDrawInfo::AddLines(subsector_t * sub, sector_t * sector)
|
||||
{
|
||||
currentsector = sector;
|
||||
currentsubsector = sub;
|
||||
|
||||
ClipWall.Clock();
|
||||
if (sub->polys != nullptr)
|
||||
{
|
||||
AddPolyobjs(sub);
|
||||
}
|
||||
else
|
||||
{
|
||||
int count = sub->numlines;
|
||||
seg_t * seg = sub->firstline;
|
||||
|
||||
while (count--)
|
||||
{
|
||||
if (seg->linedef == nullptr)
|
||||
{
|
||||
if (!(sub->flags & SSECMF_DRAWN)) AddLine (seg, mClipPortal != nullptr);
|
||||
}
|
||||
else if (!(seg->sidedef->Flags & WALLF_POLYOBJ))
|
||||
{
|
||||
AddLine (seg, mClipPortal != nullptr);
|
||||
}
|
||||
seg++;
|
||||
}
|
||||
}
|
||||
ClipWall.Unclock();
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Adds lines that lie directly on the portal boundary.
|
||||
// Only two-sided lines will be handled here, and no polyobjects
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
inline bool PointOnLine(const DVector2 &pos, const line_t *line)
|
||||
{
|
||||
double v = (pos.Y - line->v1->fY()) * line->Delta().X + (line->v1->fX() - pos.X) * line->Delta().Y;
|
||||
return fabs(v) <= EQUAL_EPSILON;
|
||||
}
|
||||
|
||||
void HWDrawInfo::AddSpecialPortalLines(subsector_t * sub, sector_t * sector, line_t *line)
|
||||
{
|
||||
currentsector = sector;
|
||||
currentsubsector = sub;
|
||||
|
||||
ClipWall.Clock();
|
||||
int count = sub->numlines;
|
||||
seg_t * seg = sub->firstline;
|
||||
|
||||
while (count--)
|
||||
{
|
||||
if (seg->linedef != nullptr && seg->PartnerSeg != nullptr)
|
||||
{
|
||||
if (PointOnLine(seg->v1->fPos(), line) && PointOnLine(seg->v2->fPos(), line))
|
||||
AddLine(seg, false);
|
||||
}
|
||||
seg++;
|
||||
}
|
||||
ClipWall.Unclock();
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// R_RenderThings
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void HWDrawInfo::RenderThings(subsector_t * sub, sector_t * sector)
|
||||
{
|
||||
sector_t * sec=sub->sector;
|
||||
// Handle all things in sector.
|
||||
const auto &vp = Viewpoint;
|
||||
for (auto p = sec->touching_renderthings; p != nullptr; p = p->m_snext)
|
||||
{
|
||||
auto thing = p->m_thing;
|
||||
if (thing->validcount == validcount) continue;
|
||||
thing->validcount = validcount;
|
||||
|
||||
FIntCVar *cvar = thing->GetInfo()->distancecheck;
|
||||
if (cvar != nullptr && *cvar >= 0)
|
||||
{
|
||||
double dist = (thing->Pos() - vp.Pos).LengthSquared();
|
||||
double check = (double)**cvar;
|
||||
if (dist >= check * check)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// If this thing is in a map section that's not in view it can't possibly be visible
|
||||
if (CurrentMapSections[thing->subsector->mapsection])
|
||||
{
|
||||
GLSprite sprite;
|
||||
sprite.Process(this, thing, sector, in_area, false);
|
||||
}
|
||||
}
|
||||
|
||||
for (msecnode_t *node = sec->sectorportal_thinglist; node; node = node->m_snext)
|
||||
{
|
||||
AActor *thing = node->m_thing;
|
||||
FIntCVar *cvar = thing->GetInfo()->distancecheck;
|
||||
if (cvar != nullptr && *cvar >= 0)
|
||||
{
|
||||
double dist = (thing->Pos() - vp.Pos).LengthSquared();
|
||||
double check = (double)**cvar;
|
||||
if (dist >= check * check)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
GLSprite sprite;
|
||||
sprite.Process(this, thing, sector, in_area, true);
|
||||
}
|
||||
}
|
||||
|
||||
void HWDrawInfo::RenderParticles(subsector_t *sub, sector_t *front)
|
||||
{
|
||||
SetupSprite.Clock();
|
||||
for (int i = Level->ParticlesInSubsec[sub->Index()]; i != NO_PARTICLE; i = Level->Particles[i].snext)
|
||||
{
|
||||
if (mClipPortal)
|
||||
{
|
||||
int clipres = mClipPortal->ClipPoint(Level->Particles[i].Pos);
|
||||
if (clipres == PClip_InFront) continue;
|
||||
}
|
||||
|
||||
GLSprite sprite;
|
||||
sprite.ProcessParticle(this, &Level->Particles[i], front);
|
||||
}
|
||||
SetupSprite.Unclock();
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// R_Subsector
|
||||
// Determine floor/ceiling planes.
|
||||
// Add sprites of things in sector.
|
||||
// Draw one or more line segments.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void HWDrawInfo::DoSubsector(subsector_t * sub)
|
||||
{
|
||||
sector_t * sector;
|
||||
sector_t * fakesector;
|
||||
|
||||
#ifdef _DEBUG
|
||||
if (sub->sector->sectornum==931)
|
||||
{
|
||||
int a = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
sector=sub->sector;
|
||||
if (!sector) return;
|
||||
|
||||
// If the mapsections differ this subsector can't possibly be visible from the current view point
|
||||
if (!CurrentMapSections[sub->mapsection]) return;
|
||||
if (sub->flags & SSECF_POLYORG) return; // never render polyobject origin subsectors because their vertices no longer are where one may expect.
|
||||
|
||||
if (ss_renderflags[sub->Index()] & SSRF_SEEN)
|
||||
{
|
||||
// This means that we have reached a subsector in a portal that has been marked 'seen'
|
||||
// from the other side of the portal. This means we must clear the clipper for the
|
||||
// range this subsector spans before going on.
|
||||
UnclipSubsector(sub);
|
||||
}
|
||||
if (mClipper->IsBlocked()) return; // if we are inside a stacked sector portal which hasn't unclipped anything yet.
|
||||
|
||||
fakesector=hw_FakeFlat(sector, in_area, false);
|
||||
|
||||
if (mClipPortal)
|
||||
{
|
||||
int clipres = mClipPortal->ClipSubsector(sub);
|
||||
if (clipres == PClip_InFront)
|
||||
{
|
||||
line_t *line = mClipPortal->ClipLine();
|
||||
// The subsector is out of range, but we still have to check lines that lie directly on the boundary and may expose their upper or lower parts.
|
||||
if (line) AddSpecialPortalLines(sub, fakesector, line);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (sector->validcount != validcount)
|
||||
{
|
||||
screen->mVertexData->CheckUpdate(sector);
|
||||
}
|
||||
|
||||
// [RH] Add particles
|
||||
if (gl_render_things && Level->ParticlesInSubsec[sub->Index()] != NO_PARTICLE)
|
||||
{
|
||||
if (multithread)
|
||||
{
|
||||
jobQueue.AddJob(RenderJob::ParticleJob, sub, nullptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetupSprite.Clock();
|
||||
RenderParticles(sub, fakesector);
|
||||
SetupSprite.Unclock();
|
||||
}
|
||||
}
|
||||
|
||||
AddLines(sub, fakesector);
|
||||
|
||||
// BSP is traversed by subsector.
|
||||
// A sector might have been split into several
|
||||
// subsectors during BSP building.
|
||||
// Thus we check whether it was already added.
|
||||
if (sector->validcount != validcount)
|
||||
{
|
||||
// Well, now it will be done.
|
||||
sector->validcount = validcount;
|
||||
sector->MoreFlags |= SECMF_DRAWN;
|
||||
|
||||
if (gl_render_things && (sector->touching_renderthings || sector->sectorportal_thinglist))
|
||||
{
|
||||
if (multithread)
|
||||
{
|
||||
jobQueue.AddJob(RenderJob::SpriteJob, sub, nullptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetupSprite.Clock();
|
||||
RenderThings(sub, fakesector);
|
||||
SetupSprite.Unclock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (gl_render_flats)
|
||||
{
|
||||
// Subsectors with only 2 lines cannot have any area
|
||||
if (sub->numlines>2 || (sub->hacked&1))
|
||||
{
|
||||
// Exclude the case when it tries to render a sector with a heightsec
|
||||
// but undetermined heightsec state. This can only happen if the
|
||||
// subsector is obstructed but not excluded due to a large bounding box.
|
||||
// Due to the way a BSP works such a subsector can never be visible
|
||||
if (!sector->GetHeightSec() || in_area!=area_default)
|
||||
{
|
||||
if (sector != sub->render_sector)
|
||||
{
|
||||
sector = sub->render_sector;
|
||||
// the planes of this subsector are faked to belong to another sector
|
||||
// This means we need the heightsec parts and light info of the render sector, not the actual one.
|
||||
fakesector = hw_FakeFlat(sector, in_area, false);
|
||||
}
|
||||
|
||||
uint8_t &srf = section_renderflags[Level->sections.SectionIndex(sub->section)];
|
||||
if (!(srf & SSRF_PROCESSED))
|
||||
{
|
||||
srf |= SSRF_PROCESSED;
|
||||
|
||||
if (multithread)
|
||||
{
|
||||
jobQueue.AddJob(RenderJob::FlatJob, sub);
|
||||
}
|
||||
else
|
||||
{
|
||||
GLFlat flat;
|
||||
flat.section = sub->section;
|
||||
SetupFlat.Clock();
|
||||
flat.ProcessSector(this, fakesector);
|
||||
SetupFlat.Unclock();
|
||||
}
|
||||
}
|
||||
// mark subsector as processed - but mark for rendering only if it has an actual area.
|
||||
ss_renderflags[sub->Index()] =
|
||||
(sub->numlines > 2) ? SSRF_PROCESSED|SSRF_RENDERALL : SSRF_PROCESSED;
|
||||
if (sub->hacked & 1) AddHackedSubsector(sub);
|
||||
|
||||
// This is for portal coverage.
|
||||
FSectorPortalGroup *portal;
|
||||
|
||||
// AddSubsectorToPortal cannot be called here when using multithreaded processing,
|
||||
// because the wall processing code in the worker can also modify the portal state.
|
||||
// To avoid costly synchronization for every access to the portal list,
|
||||
// the call to AddSubsectorToPortal will be deferred to the worker.
|
||||
// (GetPortalGruop only accesses static sector data so this check can be done here, restricting the new job to the minimum possible extent.)
|
||||
portal = fakesector->GetPortalGroup(sector_t::ceiling);
|
||||
if (portal != nullptr)
|
||||
{
|
||||
if (multithread)
|
||||
{
|
||||
jobQueue.AddJob(RenderJob::PortalJob, sub, (seg_t *)portal);
|
||||
}
|
||||
else
|
||||
{
|
||||
AddSubsectorToPortal(portal, sub);
|
||||
}
|
||||
}
|
||||
|
||||
portal = fakesector->GetPortalGroup(sector_t::floor);
|
||||
if (portal != nullptr)
|
||||
{
|
||||
if (multithread)
|
||||
{
|
||||
jobQueue.AddJob(RenderJob::PortalJob, sub, (seg_t *)portal);
|
||||
}
|
||||
else
|
||||
{
|
||||
AddSubsectorToPortal(portal, sub);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// RenderBSPNode
|
||||
// Renders all subsectors below a given node,
|
||||
// traversing subtree recursively.
|
||||
// Just call with BSP root.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void HWDrawInfo::RenderBSPNode (void *node)
|
||||
{
|
||||
if (Level->nodes.Size() == 0)
|
||||
{
|
||||
DoSubsector (&Level->subsectors[0]);
|
||||
return;
|
||||
}
|
||||
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 = R_PointOnSide(viewx, viewy, bsp);
|
||||
|
||||
// Recursively divide front space (toward the viewer).
|
||||
RenderBSPNode (bsp->children[side]);
|
||||
|
||||
// Possibly divide back space (away from the viewer).
|
||||
side ^= 1;
|
||||
|
||||
// It is not necessary to use the slower precise version here
|
||||
if (!mClipper->CheckBox(bsp->bbox[side]))
|
||||
{
|
||||
if (!(no_renderflags[bsp->Index()] & SSRF_SEEN))
|
||||
return;
|
||||
}
|
||||
|
||||
node = bsp->children[side];
|
||||
}
|
||||
DoSubsector ((subsector_t *)((uint8_t *)node - 1));
|
||||
}
|
||||
|
||||
void HWDrawInfo::RenderBSP(void *node)
|
||||
{
|
||||
Bsp.Clock();
|
||||
|
||||
// Give the DrawInfo the viewpoint in fixed point because that's what the nodes are.
|
||||
viewx = FLOAT2FIXED(Viewpoint.Pos.X);
|
||||
viewy = FLOAT2FIXED(Viewpoint.Pos.Y);
|
||||
|
||||
validcount++; // used for processing sidedefs only once by the renderer.
|
||||
|
||||
multithread = gl_multithread;
|
||||
if (multithread)
|
||||
{
|
||||
jobQueue.ReleaseAll();
|
||||
auto future = renderPool.push([&](int id) {
|
||||
WorkerThread();
|
||||
});
|
||||
RenderBSPNode(node);
|
||||
|
||||
jobQueue.AddJob(RenderJob::TerminateJob, nullptr, nullptr);
|
||||
Bsp.Unclock();
|
||||
MTWait.Clock();
|
||||
future.wait();
|
||||
MTWait.Unclock();
|
||||
}
|
||||
else
|
||||
{
|
||||
RenderBSPNode(node);
|
||||
Bsp.Unclock();
|
||||
}
|
||||
// Process all the sprites on the current portal's back side which touch the portal.
|
||||
if (mCurrentPortal != nullptr) mCurrentPortal->RenderAttached(this);
|
||||
|
||||
|
||||
PreparePlayerSprites(Viewpoint.sector, in_area);
|
||||
}
|
442
src/rendering/hwrenderer/scene/hw_clipper.cpp
Normal file
442
src/rendering/hwrenderer/scene/hw_clipper.cpp
Normal file
|
@ -0,0 +1,442 @@
|
|||
/*
|
||||
*
|
||||
** gl_clipper.cpp
|
||||
**
|
||||
** Handles visibility checks.
|
||||
** Loosely based on the JDoom clipper.
|
||||
**
|
||||
**---------------------------------------------------------------------------
|
||||
** Copyright 2003 Tim Stump
|
||||
** All rights reserved.
|
||||
**
|
||||
** Redistribution and use in source and binary forms, with or without
|
||||
** modification, are permitted provided that the following conditions
|
||||
** are met:
|
||||
**
|
||||
** 1. Redistributions of source code must retain the above copyright
|
||||
** notice, this list of conditions and the following disclaimer.
|
||||
** 2. Redistributions in binary form must reproduce the above copyright
|
||||
** notice, this list of conditions and the following disclaimer in the
|
||||
** documentation and/or other materials provided with the distribution.
|
||||
** 3. The name of the author may not be used to endorse or promote products
|
||||
** derived from this software without specific prior written permission.
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
**---------------------------------------------------------------------------
|
||||
**
|
||||
*/
|
||||
|
||||
#include "hw_clipper.h"
|
||||
#include "g_levellocals.h"
|
||||
|
||||
unsigned Clipper::starttime;
|
||||
|
||||
Clipper::Clipper()
|
||||
{
|
||||
starttime++;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// RemoveRange
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void Clipper::RemoveRange(ClipNode * range)
|
||||
{
|
||||
if (range == cliphead)
|
||||
{
|
||||
cliphead = cliphead->next;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (range->prev) range->prev->next = range->next;
|
||||
if (range->next) range->next->prev = range->prev;
|
||||
}
|
||||
|
||||
Free(range);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// Clear
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void Clipper::Clear()
|
||||
{
|
||||
ClipNode *node = cliphead;
|
||||
ClipNode *temp;
|
||||
|
||||
blocked = false;
|
||||
while (node != NULL)
|
||||
{
|
||||
temp = node;
|
||||
node = node->next;
|
||||
Free(temp);
|
||||
}
|
||||
node = silhouette;
|
||||
|
||||
while (node != NULL)
|
||||
{
|
||||
temp = node;
|
||||
node = node->next;
|
||||
Free(temp);
|
||||
}
|
||||
|
||||
cliphead = NULL;
|
||||
silhouette = NULL;
|
||||
starttime++;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// SetSilhouette
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void Clipper::SetSilhouette()
|
||||
{
|
||||
ClipNode *node = cliphead;
|
||||
ClipNode *last = NULL;
|
||||
|
||||
while (node != NULL)
|
||||
{
|
||||
ClipNode *snode = NewRange(node->start, node->end);
|
||||
if (silhouette == NULL) silhouette = snode;
|
||||
snode->prev = last;
|
||||
if (last != NULL) last->next = snode;
|
||||
last = snode;
|
||||
node = node->next;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// IsRangeVisible
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
bool Clipper::IsRangeVisible(angle_t startAngle, angle_t endAngle)
|
||||
{
|
||||
ClipNode *ci;
|
||||
ci = cliphead;
|
||||
|
||||
if (endAngle==0 && ci && ci->start==0) return false;
|
||||
|
||||
while (ci != NULL && ci->start < endAngle)
|
||||
{
|
||||
if (startAngle >= ci->start && endAngle <= ci->end)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
ci = ci->next;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// AddClipRange
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void Clipper::AddClipRange(angle_t start, angle_t end)
|
||||
{
|
||||
ClipNode *node, *temp, *prevNode;
|
||||
|
||||
if (cliphead)
|
||||
{
|
||||
//check to see if range contains any old ranges
|
||||
node = cliphead;
|
||||
while (node != NULL && node->start < end)
|
||||
{
|
||||
if (node->start >= start && node->end <= end)
|
||||
{
|
||||
temp = node;
|
||||
node = node->next;
|
||||
RemoveRange(temp);
|
||||
}
|
||||
else if (node->start<=start && node->end>=end)
|
||||
{
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
node = node->next;
|
||||
}
|
||||
}
|
||||
|
||||
//check to see if range overlaps a range (or possibly 2)
|
||||
node = cliphead;
|
||||
while (node != NULL && node->start <= end)
|
||||
{
|
||||
if (node->end >= start)
|
||||
{
|
||||
// we found the first overlapping node
|
||||
if (node->start > start)
|
||||
{
|
||||
// the new range overlaps with this node's start point
|
||||
node->start = start;
|
||||
}
|
||||
|
||||
if (node->end < end)
|
||||
{
|
||||
node->end = end;
|
||||
}
|
||||
|
||||
ClipNode *node2 = node->next;
|
||||
while (node2 && node2->start <= node->end)
|
||||
{
|
||||
if (node2->end > node->end) node->end = node2->end;
|
||||
ClipNode *delnode = node2;
|
||||
node2 = node2->next;
|
||||
RemoveRange(delnode);
|
||||
}
|
||||
return;
|
||||
}
|
||||
node = node->next;
|
||||
}
|
||||
|
||||
//just add range
|
||||
node = cliphead;
|
||||
prevNode = NULL;
|
||||
temp = NewRange(start, end);
|
||||
|
||||
while (node != NULL && node->start < end)
|
||||
{
|
||||
prevNode = node;
|
||||
node = node->next;
|
||||
}
|
||||
|
||||
temp->next = node;
|
||||
if (node == NULL)
|
||||
{
|
||||
temp->prev = prevNode;
|
||||
if (prevNode) prevNode->next = temp;
|
||||
if (!cliphead) cliphead = temp;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (node == cliphead)
|
||||
{
|
||||
cliphead->prev = temp;
|
||||
cliphead = temp;
|
||||
}
|
||||
else
|
||||
{
|
||||
temp->prev = prevNode;
|
||||
prevNode->next = temp;
|
||||
node->prev = temp;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
temp = NewRange(start, end);
|
||||
cliphead = temp;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// RemoveClipRange
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void Clipper::RemoveClipRange(angle_t start, angle_t end)
|
||||
{
|
||||
ClipNode *node;
|
||||
|
||||
if (silhouette)
|
||||
{
|
||||
node = silhouette;
|
||||
while (node != NULL && node->end <= start)
|
||||
{
|
||||
node = node->next;
|
||||
}
|
||||
if (node != NULL && node->start <= start)
|
||||
{
|
||||
if (node->end >= end) return;
|
||||
start = node->end;
|
||||
node = node->next;
|
||||
}
|
||||
while (node != NULL && node->start < end)
|
||||
{
|
||||
DoRemoveClipRange(start, node->start);
|
||||
start = node->end;
|
||||
node = node->next;
|
||||
}
|
||||
if (start >= end) return;
|
||||
}
|
||||
DoRemoveClipRange(start, end);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// RemoveClipRange worker function
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void Clipper::DoRemoveClipRange(angle_t start, angle_t end)
|
||||
{
|
||||
ClipNode *node, *temp;
|
||||
|
||||
if (cliphead)
|
||||
{
|
||||
//check to see if range contains any old ranges
|
||||
node = cliphead;
|
||||
while (node != NULL && node->start < end)
|
||||
{
|
||||
if (node->start >= start && node->end <= end)
|
||||
{
|
||||
temp = node;
|
||||
node = node->next;
|
||||
RemoveRange(temp);
|
||||
}
|
||||
else
|
||||
{
|
||||
node = node->next;
|
||||
}
|
||||
}
|
||||
|
||||
//check to see if range overlaps a range (or possibly 2)
|
||||
node = cliphead;
|
||||
while (node != NULL)
|
||||
{
|
||||
if (node->start >= start && node->start <= end)
|
||||
{
|
||||
node->start = end;
|
||||
break;
|
||||
}
|
||||
else if (node->end >= start && node->end <= end)
|
||||
{
|
||||
node->end=start;
|
||||
}
|
||||
else if (node->start < start && node->end > end)
|
||||
{
|
||||
temp = NewRange(end, node->end);
|
||||
node->end=start;
|
||||
temp->next=node->next;
|
||||
temp->prev=node;
|
||||
node->next=temp;
|
||||
if (temp->next) temp->next->prev=temp;
|
||||
break;
|
||||
}
|
||||
node = node->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
//
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
angle_t Clipper::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);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// ! 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 Clipper::PointToPseudoAngle(double x, double y)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// R_CheckBBox
|
||||
// Checks BSP node/subtree bounding box.
|
||||
// Returns true
|
||||
// if some part of the bbox might be visible.
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
static const uint8_t checkcoord[12][4] = // killough -- static const
|
||||
{
|
||||
{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}
|
||||
};
|
||||
|
||||
bool Clipper::CheckBox(const float *bspcoord)
|
||||
{
|
||||
angle_t angle1, angle2;
|
||||
|
||||
int boxpos;
|
||||
const uint8_t* check;
|
||||
|
||||
// Find the corners of the box
|
||||
// that define the edges from current viewpoint.
|
||||
auto &vp = viewpoint;
|
||||
boxpos = (vp->Pos.X <= bspcoord[BOXLEFT] ? 0 : vp->Pos.X < bspcoord[BOXRIGHT ] ? 1 : 2) +
|
||||
(vp->Pos.Y >= bspcoord[BOXTOP ] ? 0 : vp->Pos.Y > bspcoord[BOXBOTTOM] ? 4 : 8);
|
||||
|
||||
if (boxpos == 5) return true;
|
||||
|
||||
check = checkcoord[boxpos];
|
||||
angle1 = PointToPseudoAngle (bspcoord[check[0]], bspcoord[check[1]]);
|
||||
angle2 = PointToPseudoAngle (bspcoord[check[2]], bspcoord[check[3]]);
|
||||
|
||||
return SafeCheckRange(angle2, angle1);
|
||||
}
|
||||
|
162
src/rendering/hwrenderer/scene/hw_clipper.h
Normal file
162
src/rendering/hwrenderer/scene/hw_clipper.h
Normal file
|
@ -0,0 +1,162 @@
|
|||
#ifndef __GL_CLIPPER
|
||||
#define __GL_CLIPPER
|
||||
|
||||
#include "doomtype.h"
|
||||
#include "xs_Float.h"
|
||||
#include "r_utility.h"
|
||||
#include "memarena.h"
|
||||
|
||||
class ClipNode
|
||||
{
|
||||
friend class Clipper;
|
||||
|
||||
ClipNode *prev, *next;
|
||||
angle_t start, end;
|
||||
|
||||
bool operator== (const ClipNode &other)
|
||||
{
|
||||
return other.start == start && other.end == end;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class Clipper
|
||||
{
|
||||
static unsigned starttime;
|
||||
FMemArena nodearena;
|
||||
ClipNode * freelist = nullptr;
|
||||
|
||||
ClipNode * clipnodes = nullptr;
|
||||
ClipNode * cliphead = nullptr;
|
||||
ClipNode * silhouette = nullptr; // will be preserved even when RemoveClipRange is called
|
||||
const FRenderViewpoint *viewpoint = nullptr;
|
||||
bool blocked = false;
|
||||
|
||||
static angle_t AngleToPseudo(angle_t ang);
|
||||
bool IsRangeVisible(angle_t startangle, angle_t endangle);
|
||||
void RemoveRange(ClipNode * cn);
|
||||
void AddClipRange(angle_t startangle, angle_t endangle);
|
||||
void RemoveClipRange(angle_t startangle, angle_t endangle);
|
||||
void DoRemoveClipRange(angle_t start, angle_t end);
|
||||
|
||||
public:
|
||||
|
||||
Clipper();
|
||||
|
||||
void Clear();
|
||||
|
||||
void Free(ClipNode *node)
|
||||
{
|
||||
node->next = freelist;
|
||||
freelist = node;
|
||||
}
|
||||
|
||||
ClipNode * GetNew()
|
||||
{
|
||||
if (freelist)
|
||||
{
|
||||
ClipNode * p = freelist;
|
||||
freelist = p->next;
|
||||
return p;
|
||||
}
|
||||
else return (ClipNode*)nodearena.Alloc(sizeof(ClipNode));
|
||||
}
|
||||
|
||||
ClipNode * NewRange(angle_t start, angle_t end)
|
||||
{
|
||||
ClipNode * c = GetNew();
|
||||
|
||||
c->start = start;
|
||||
c->end = end;
|
||||
c->next = c->prev = NULL;
|
||||
return c;
|
||||
}
|
||||
|
||||
void SetViewpoint(const FRenderViewpoint &vp)
|
||||
{
|
||||
viewpoint = &vp;
|
||||
}
|
||||
|
||||
void SetSilhouette();
|
||||
|
||||
bool SafeCheckRange(angle_t startAngle, angle_t endAngle)
|
||||
{
|
||||
if(startAngle > endAngle)
|
||||
{
|
||||
return (IsRangeVisible(startAngle, ANGLE_MAX) || IsRangeVisible(0, endAngle));
|
||||
}
|
||||
|
||||
return IsRangeVisible(startAngle, endAngle);
|
||||
}
|
||||
|
||||
void SafeAddClipRange(angle_t startangle, angle_t endangle)
|
||||
{
|
||||
if(startangle > endangle)
|
||||
{
|
||||
// The range has to added in two parts.
|
||||
AddClipRange(startangle, ANGLE_MAX);
|
||||
AddClipRange(0, endangle);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Add the range as usual.
|
||||
AddClipRange(startangle, endangle);
|
||||
}
|
||||
}
|
||||
|
||||
void SafeAddClipRange(const vertex_t *v1, const vertex_t *v2)
|
||||
{
|
||||
angle_t a2 = PointToPseudoAngle(v1->p.X, v1->p.Y);
|
||||
angle_t a1 = PointToPseudoAngle(v2->p.X, v2->p.Y);
|
||||
SafeAddClipRange(a1,a2);
|
||||
}
|
||||
|
||||
void SafeAddClipRangeRealAngles(angle_t startangle, angle_t endangle)
|
||||
{
|
||||
SafeAddClipRange(AngleToPseudo(startangle), AngleToPseudo(endangle));
|
||||
}
|
||||
|
||||
|
||||
void SafeRemoveClipRange(angle_t startangle, angle_t endangle)
|
||||
{
|
||||
if(startangle > endangle)
|
||||
{
|
||||
// The range has to added in two parts.
|
||||
RemoveClipRange(startangle, ANGLE_MAX);
|
||||
RemoveClipRange(0, endangle);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Add the range as usual.
|
||||
RemoveClipRange(startangle, endangle);
|
||||
}
|
||||
}
|
||||
|
||||
void SafeRemoveClipRangeRealAngles(angle_t startangle, angle_t endangle)
|
||||
{
|
||||
SafeRemoveClipRange(AngleToPseudo(startangle), AngleToPseudo(endangle));
|
||||
}
|
||||
|
||||
void SetBlocked(bool on)
|
||||
{
|
||||
blocked = on;
|
||||
}
|
||||
|
||||
bool IsBlocked() const
|
||||
{
|
||||
return blocked;
|
||||
}
|
||||
|
||||
angle_t PointToPseudoAngle(double x, double y);
|
||||
|
||||
bool CheckBox(const float *bspcoord);
|
||||
|
||||
// Used to speed up angle calculations during clipping
|
||||
inline angle_t GetClipAngle(vertex_t *v)
|
||||
{
|
||||
return unsigned(v->angletime) == starttime ? v->viewangle : (v->angletime = starttime, v->viewangle = PointToPseudoAngle(v->p.X, v->p.Y));
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif
|
443
src/rendering/hwrenderer/scene/hw_decal.cpp
Normal file
443
src/rendering/hwrenderer/scene/hw_decal.cpp
Normal file
|
@ -0,0 +1,443 @@
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Copyright(C) 2003-2018 Christoph Oelckers
|
||||
// All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with this program. If not, see http://www.gnu.org/licenses/
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
/*
|
||||
** gl_decal.cpp
|
||||
** OpenGL decal processing code
|
||||
**
|
||||
*/
|
||||
|
||||
#include "doomdata.h"
|
||||
#include "a_sharedglobal.h"
|
||||
#include "r_utility.h"
|
||||
#include "g_levellocals.h"
|
||||
#include "hwrenderer/textures/hw_material.h"
|
||||
#include "hwrenderer/utility/hw_cvars.h"
|
||||
#include "hwrenderer/scene/hw_drawstructs.h"
|
||||
#include "hwrenderer/scene/hw_drawinfo.h"
|
||||
#include "hwrenderer/utility/hw_lighting.h"
|
||||
#include "hwrenderer/utility/hw_clock.h"
|
||||
#include "hwrenderer/data/flatvertices.h"
|
||||
#include "hw_renderstate.h"
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void GLDecal::DrawDecal(HWDrawInfo *di, FRenderState &state)
|
||||
{
|
||||
auto tex = gltexture;
|
||||
|
||||
// calculate dynamic light effect.
|
||||
if (di->Level->HasDynamicLights && !di->isFullbrightScene() && gl_light_sprites)
|
||||
{
|
||||
// Note: This should be replaced with proper shader based lighting.
|
||||
double x, y;
|
||||
float out[3];
|
||||
decal->GetXY(decal->Side, x, y);
|
||||
di->GetDynSpriteLight(nullptr, x, y, zcenter, decal->Side->lighthead, decal->Side->sector->PortalGroup, out);
|
||||
state.SetDynLight(out[0], out[1], out[2]);
|
||||
}
|
||||
|
||||
// alpha color only has an effect when using an alpha texture.
|
||||
if (decal->RenderStyle.Flags & (STYLEF_RedIsAlpha | STYLEF_ColorIsFixed))
|
||||
{
|
||||
state.SetObjectColor(decal->AlphaColor | 0xff000000);
|
||||
}
|
||||
|
||||
state.SetTextureMode(decal->RenderStyle);
|
||||
state.SetRenderStyle(decal->RenderStyle);
|
||||
state.SetMaterial(tex, CLAMP_XY, decal->Translation, -1);
|
||||
|
||||
|
||||
// If srcalpha is one it looks better with a higher alpha threshold
|
||||
if (decal->RenderStyle.SrcAlpha == STYLEALPHA_One) state.AlphaFunc(Alpha_GEqual, gl_mask_sprite_threshold);
|
||||
else state.AlphaFunc(Alpha_Greater, 0.f);
|
||||
|
||||
|
||||
di->SetColor(state, lightlevel, rellight, di->isFullbrightScene(), Colormap, alpha);
|
||||
// for additively drawn decals we must temporarily set the fog color to black.
|
||||
PalEntry fc = state.GetFogColor();
|
||||
if (decal->RenderStyle.BlendOp == STYLEOP_Add && decal->RenderStyle.DestAlpha == STYLEALPHA_One)
|
||||
{
|
||||
state.SetFog(0, -1);
|
||||
}
|
||||
|
||||
state.SetNormal(Normal);
|
||||
|
||||
if (lightlist == nullptr)
|
||||
{
|
||||
state.Draw(DT_TriangleFan, vertindex, 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto &lightlist = *this->lightlist;
|
||||
|
||||
for (unsigned k = 0; k < lightlist.Size(); k++)
|
||||
{
|
||||
secplane_t &lowplane = k == lightlist.Size() - 1 ? frontsector->floorplane : lightlist[k + 1].plane;
|
||||
|
||||
float low1 = lowplane.ZatPoint(dv[1].x, dv[1].y);
|
||||
float low2 = lowplane.ZatPoint(dv[2].x, dv[2].y);
|
||||
|
||||
if (low1 < dv[1].z || low2 < dv[2].z)
|
||||
{
|
||||
int thisll = lightlist[k].caster != nullptr ? hw_ClampLight(*lightlist[k].p_lightlevel) : lightlevel;
|
||||
FColormap thiscm;
|
||||
thiscm.FadeColor = Colormap.FadeColor;
|
||||
thiscm.CopyFrom3DLight(&lightlist[k]);
|
||||
di->SetColor(state, thisll, rellight, di->isFullbrightScene(), thiscm, alpha);
|
||||
if (di->Level->flags3 & LEVEL3_NOCOLOREDSPRITELIGHTING) thiscm.Decolorize();
|
||||
di->SetFog(state, thisll, rellight, di->isFullbrightScene(), &thiscm, false);
|
||||
state.SetSplitPlanes(lightlist[k].plane, lowplane);
|
||||
|
||||
state.Draw(DT_TriangleFan, vertindex, 4);
|
||||
}
|
||||
if (low1 <= dv[0].z && low2 <= dv[3].z) break;
|
||||
}
|
||||
}
|
||||
|
||||
rendered_decals++;
|
||||
state.SetTextureMode(TM_NORMAL);
|
||||
state.SetObjectColor(0xffffffff);
|
||||
state.SetFog(fc, -1);
|
||||
state.SetDynLight(0, 0, 0);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
void HWDrawInfo::DrawDecals(FRenderState &state, TArray<GLDecal *> &decals)
|
||||
{
|
||||
side_t *wall = nullptr;
|
||||
state.SetDepthMask(false);
|
||||
state.SetDepthBias(-1, -128);
|
||||
state.SetLightIndex(-1);
|
||||
for (auto gldecal : decals)
|
||||
{
|
||||
if (gldecal->decal->Side != wall)
|
||||
{
|
||||
wall = gldecal->decal->Side;
|
||||
if (gldecal->lightlist != nullptr)
|
||||
{
|
||||
state.EnableSplit(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
state.EnableSplit(false);
|
||||
SetFog(state, gldecal->lightlevel, gldecal->rellight, isFullbrightScene(), &gldecal->Colormap, false);
|
||||
}
|
||||
}
|
||||
gldecal->DrawDecal(this, state);
|
||||
}
|
||||
state.EnableSplit(false);
|
||||
state.ClearDepthBias();
|
||||
state.SetTextureMode(TM_NORMAL);
|
||||
state.SetDepthMask(true);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// This list will never get long, so this code should be ok.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void GLWall::DrawDecalsForMirror(HWDrawInfo *di, FRenderState &state, TArray<GLDecal *> &decals)
|
||||
{
|
||||
state.SetDepthMask(false);
|
||||
state.SetDepthBias(-1, -128);
|
||||
state.SetLightIndex(-1);
|
||||
di->SetFog(state, lightlevel, rellight + getExtraLight(), di->isFullbrightScene(), &Colormap, false);
|
||||
for (auto gldecal : decals)
|
||||
{
|
||||
if (gldecal->decal->Side == seg->sidedef)
|
||||
{
|
||||
gldecal->DrawDecal(di, state);
|
||||
}
|
||||
}
|
||||
state.ClearDepthBias();
|
||||
state.SetTextureMode(TM_NORMAL);
|
||||
state.SetDepthMask(true);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void GLWall::ProcessDecal(HWDrawInfo *di, DBaseDecal *decal, const FVector3 &normal)
|
||||
{
|
||||
line_t * line = seg->linedef;
|
||||
side_t * side = seg->sidedef;
|
||||
int i;
|
||||
float zpos;
|
||||
bool flipx, flipy;
|
||||
FTextureID decalTile;
|
||||
|
||||
|
||||
if (decal->RenderFlags & RF_INVISIBLE) return;
|
||||
if (type == RENDERWALL_FFBLOCK && gltexture->isMasked()) return; // No decals on 3D floors with transparent textures.
|
||||
if (seg == nullptr) return;
|
||||
|
||||
|
||||
decalTile = decal->PicNum;
|
||||
flipx = !!(decal->RenderFlags & RF_XFLIP);
|
||||
flipy = !!(decal->RenderFlags & RF_YFLIP);
|
||||
|
||||
|
||||
FTexture *texture = TexMan.GetTexture(decalTile);
|
||||
if (texture == NULL) return;
|
||||
|
||||
|
||||
// the sectors are only used for their texture origin coordinates
|
||||
// so we don't need the fake sectors for deep water etc.
|
||||
// As this is a completely split wall fragment no further splits are
|
||||
// necessary for the decal.
|
||||
sector_t *frontsector;
|
||||
|
||||
// for 3d-floor segments use the model sector as reference
|
||||
if ((decal->RenderFlags&RF_CLIPMASK) == RF_CLIPMID) frontsector = decal->Sector;
|
||||
else frontsector = seg->frontsector;
|
||||
|
||||
switch (decal->RenderFlags & RF_RELMASK)
|
||||
{
|
||||
default:
|
||||
// No valid decal can have this type. If one is encountered anyway
|
||||
// it is in some way invalid so skip it.
|
||||
return;
|
||||
//zpos = decal->z;
|
||||
//break;
|
||||
|
||||
case RF_RELUPPER:
|
||||
if (type != RENDERWALL_TOP) return;
|
||||
if (line->flags & ML_DONTPEGTOP)
|
||||
{
|
||||
zpos = decal->Z + frontsector->GetPlaneTexZ(sector_t::ceiling);
|
||||
}
|
||||
else
|
||||
{
|
||||
zpos = decal->Z + seg->backsector->GetPlaneTexZ(sector_t::ceiling);
|
||||
}
|
||||
break;
|
||||
case RF_RELLOWER:
|
||||
if (type != RENDERWALL_BOTTOM) return;
|
||||
if (line->flags & ML_DONTPEGBOTTOM)
|
||||
{
|
||||
zpos = decal->Z + frontsector->GetPlaneTexZ(sector_t::ceiling);
|
||||
}
|
||||
else
|
||||
{
|
||||
zpos = decal->Z + seg->backsector->GetPlaneTexZ(sector_t::floor);
|
||||
}
|
||||
break;
|
||||
case RF_RELMID:
|
||||
if (type == RENDERWALL_TOP || type == RENDERWALL_BOTTOM) return;
|
||||
if (line->flags & ML_DONTPEGBOTTOM)
|
||||
{
|
||||
zpos = decal->Z + frontsector->GetPlaneTexZ(sector_t::floor);
|
||||
}
|
||||
else
|
||||
{
|
||||
zpos = decal->Z + frontsector->GetPlaneTexZ(sector_t::ceiling);
|
||||
}
|
||||
}
|
||||
FMaterial *tex = FMaterial::ValidateTexture(texture, false);
|
||||
|
||||
// now clip the decal to the actual polygon
|
||||
|
||||
float decalwidth = tex->TextureWidth() * decal->ScaleX;
|
||||
float decalheight = tex->TextureHeight() * decal->ScaleY;
|
||||
float decallefto = tex->GetLeftOffset() * decal->ScaleX;
|
||||
float decaltopo = tex->GetTopOffset() * decal->ScaleY;
|
||||
|
||||
float leftedge = glseg.fracleft * side->TexelLength;
|
||||
float linelength = glseg.fracright * side->TexelLength - leftedge;
|
||||
|
||||
// texel index of the decal's left edge
|
||||
float decalpixpos = (float)side->TexelLength * decal->LeftDistance - (flipx ? decalwidth - decallefto : decallefto) - leftedge;
|
||||
|
||||
float left, right;
|
||||
float lefttex, righttex;
|
||||
|
||||
// decal is off the left edge
|
||||
if (decalpixpos < 0)
|
||||
{
|
||||
left = 0;
|
||||
lefttex = -decalpixpos;
|
||||
}
|
||||
else
|
||||
{
|
||||
left = decalpixpos;
|
||||
lefttex = 0;
|
||||
}
|
||||
|
||||
// decal is off the right edge
|
||||
if (decalpixpos + decalwidth > linelength)
|
||||
{
|
||||
right = linelength;
|
||||
righttex = right - decalpixpos;
|
||||
}
|
||||
else
|
||||
{
|
||||
right = decalpixpos + decalwidth;
|
||||
righttex = decalwidth;
|
||||
}
|
||||
if (right <= left)
|
||||
return; // nothing to draw
|
||||
|
||||
|
||||
// one texture unit on the wall as vector
|
||||
float vx = (glseg.x2 - glseg.x1) / linelength;
|
||||
float vy = (glseg.y2 - glseg.y1) / linelength;
|
||||
|
||||
DecalVertex dv[4];
|
||||
dv[1].x = dv[0].x = glseg.x1 + vx * left;
|
||||
dv[1].y = dv[0].y = glseg.y1 + vy * left;
|
||||
|
||||
dv[3].x = dv[2].x = glseg.x1 + vx * right;
|
||||
dv[3].y = dv[2].y = glseg.y1 + vy * right;
|
||||
|
||||
zpos += (flipy ? decalheight - decaltopo : decaltopo);
|
||||
|
||||
dv[1].z = dv[2].z = zpos;
|
||||
dv[0].z = dv[3].z = dv[1].z - decalheight;
|
||||
dv[1].v = dv[2].v = tex->GetVT();
|
||||
|
||||
dv[1].u = dv[0].u = tex->GetU(lefttex / decal->ScaleX);
|
||||
dv[3].u = dv[2].u = tex->GetU(righttex / decal->ScaleX);
|
||||
dv[0].v = dv[3].v = tex->GetVB();
|
||||
|
||||
// now clip to the top plane
|
||||
float vzt = (ztop[1] - ztop[0]) / linelength;
|
||||
float topleft = ztop[0] + vzt * left;
|
||||
float topright = ztop[0] + vzt * right;
|
||||
|
||||
// completely below the wall
|
||||
if (topleft < dv[0].z && topright < dv[3].z)
|
||||
return;
|
||||
|
||||
if (topleft < dv[1].z || topright < dv[2].z)
|
||||
{
|
||||
// decal has to be clipped at the top
|
||||
// let texture clamping handle all extreme cases
|
||||
dv[1].v = (dv[1].z - topleft) / (dv[1].z - dv[0].z)*dv[0].v;
|
||||
dv[2].v = (dv[2].z - topright) / (dv[2].z - dv[3].z)*dv[3].v;
|
||||
dv[1].z = topleft;
|
||||
dv[2].z = topright;
|
||||
}
|
||||
|
||||
// now clip to the bottom plane
|
||||
float vzb = (zbottom[1] - zbottom[0]) / linelength;
|
||||
float bottomleft = zbottom[0] + vzb * left;
|
||||
float bottomright = zbottom[0] + vzb * right;
|
||||
|
||||
// completely above the wall
|
||||
if (bottomleft > dv[1].z && bottomright > dv[2].z)
|
||||
return;
|
||||
|
||||
if (bottomleft > dv[0].z || bottomright > dv[3].z)
|
||||
{
|
||||
// decal has to be clipped at the bottom
|
||||
// let texture clamping handle all extreme cases
|
||||
dv[0].v = (dv[1].z - bottomleft) / (dv[1].z - dv[0].z)*(dv[0].v - dv[1].v) + dv[1].v;
|
||||
dv[3].v = (dv[2].z - bottomright) / (dv[2].z - dv[3].z)*(dv[3].v - dv[2].v) + dv[2].v;
|
||||
dv[0].z = bottomleft;
|
||||
dv[3].z = bottomright;
|
||||
}
|
||||
|
||||
|
||||
if (flipx)
|
||||
{
|
||||
float ur = tex->GetUR();
|
||||
for (i = 0; i < 4; i++) dv[i].u = ur - dv[i].u;
|
||||
}
|
||||
if (flipy)
|
||||
{
|
||||
float vb = tex->GetVB();
|
||||
for (i = 0; i < 4; i++) dv[i].v = vb - dv[i].v;
|
||||
}
|
||||
|
||||
GLDecal *gldecal = di->AddDecal(type == RENDERWALL_MIRRORSURFACE);
|
||||
gldecal->gltexture = tex;
|
||||
gldecal->decal = decal;
|
||||
|
||||
if (decal->RenderFlags & RF_FULLBRIGHT)
|
||||
{
|
||||
gldecal->lightlevel = 255;
|
||||
gldecal->rellight = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
gldecal->lightlevel = lightlevel;
|
||||
gldecal->rellight = rellight + getExtraLight();
|
||||
}
|
||||
|
||||
gldecal->Colormap = Colormap;
|
||||
|
||||
if (di->Level->flags3 & LEVEL3_NOCOLOREDSPRITELIGHTING)
|
||||
{
|
||||
gldecal->Colormap.Decolorize();
|
||||
}
|
||||
|
||||
gldecal->alpha = decal->Alpha;
|
||||
gldecal->zcenter = zpos - decalheight * 0.5f;
|
||||
gldecal->frontsector = frontsector;
|
||||
gldecal->Normal = normal;
|
||||
gldecal->lightlist = lightlist;
|
||||
memcpy(gldecal->dv, dv, sizeof(dv));
|
||||
|
||||
auto verts = screen->mVertexData->AllocVertices(4);
|
||||
gldecal->vertindex = verts.second;
|
||||
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
verts.first[i].Set(dv[i].x, dv[i].z, dv[i].y, dv[i].u, dv[i].v);
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void GLWall::ProcessDecals(HWDrawInfo *di)
|
||||
{
|
||||
if (seg->sidedef != nullptr)
|
||||
{
|
||||
DBaseDecal *decal = seg->sidedef->AttachedDecals;
|
||||
if (decal)
|
||||
{
|
||||
auto normal = glseg.Normal(); // calculate the normal only once per wall because it requires a square root.
|
||||
while (decal)
|
||||
{
|
||||
ProcessDecal(di, decal, normal);
|
||||
decal = decal->WallNext;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
679
src/rendering/hwrenderer/scene/hw_drawinfo.cpp
Normal file
679
src/rendering/hwrenderer/scene/hw_drawinfo.cpp
Normal file
|
@ -0,0 +1,679 @@
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Copyright(C) 2000-2018 Christoph Oelckers
|
||||
// All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with this program. If not, see http://www.gnu.org/licenses/
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
/*
|
||||
** gl_drawinfo.cpp
|
||||
** Basic scene draw info management class
|
||||
**
|
||||
*/
|
||||
|
||||
#include "a_sharedglobal.h"
|
||||
#include "r_utility.h"
|
||||
#include "r_sky.h"
|
||||
#include "d_player.h"
|
||||
#include "g_levellocals.h"
|
||||
#include "hw_fakeflat.h"
|
||||
#include "hw_portal.h"
|
||||
#include "hw_renderstate.h"
|
||||
#include "hw_drawinfo.h"
|
||||
#include "po_man.h"
|
||||
#include "r_data/models/models.h"
|
||||
#include "hwrenderer/utility/hw_clock.h"
|
||||
#include "hwrenderer/utility/hw_cvars.h"
|
||||
#include "hwrenderer/data/hw_viewpointbuffer.h"
|
||||
#include "hwrenderer/data/flatvertices.h"
|
||||
#include "hwrenderer/dynlights/hw_lightbuffer.h"
|
||||
#include "hwrenderer/utility/hw_vrmodes.h"
|
||||
#include "hw_clipper.h"
|
||||
|
||||
EXTERN_CVAR(Float, r_visibility)
|
||||
CVAR(Bool, gl_bandedswlight, false, CVAR_ARCHIVE)
|
||||
CVAR(Bool, gl_sort_textures, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||
|
||||
sector_t * hw_FakeFlat(sector_t * sec, sector_t * dest, area_t in_area, bool back);
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
class FDrawInfoList
|
||||
{
|
||||
public:
|
||||
TDeletingArray<HWDrawInfo *> mList;
|
||||
|
||||
HWDrawInfo * GetNew();
|
||||
void Release(HWDrawInfo *);
|
||||
};
|
||||
|
||||
|
||||
FDrawInfoList di_list;
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Try to reuse the lists as often as possible as they contain resources that
|
||||
// are expensive to create and delete.
|
||||
//
|
||||
// Note: If multithreading gets used, this class needs synchronization.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
HWDrawInfo *FDrawInfoList::GetNew()
|
||||
{
|
||||
if (mList.Size() > 0)
|
||||
{
|
||||
HWDrawInfo *di;
|
||||
mList.Pop(di);
|
||||
return di;
|
||||
}
|
||||
return new HWDrawInfo();
|
||||
}
|
||||
|
||||
void FDrawInfoList::Release(HWDrawInfo * di)
|
||||
{
|
||||
di->DrawScene = nullptr;
|
||||
di->ClearBuffers();
|
||||
di->Level = nullptr;
|
||||
mList.Push(di);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Sets up a new drawinfo struct
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
HWDrawInfo *HWDrawInfo::StartDrawInfo(FLevelLocals *lev, HWDrawInfo *parent, FRenderViewpoint &parentvp, HWViewpointUniforms *uniforms)
|
||||
{
|
||||
HWDrawInfo *di = di_list.GetNew();
|
||||
if (parent) di->DrawScene = parent->DrawScene;
|
||||
di->Level = lev;
|
||||
di->StartScene(parentvp, uniforms);
|
||||
return di;
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
static Clipper staticClipper; // Since all scenes are processed sequentially we only need one clipper.
|
||||
static HWDrawInfo * gl_drawinfo; // This is a linked list of all active DrawInfos and needed to free the memory arena after the last one goes out of scope.
|
||||
|
||||
void HWDrawInfo::StartScene(FRenderViewpoint &parentvp, HWViewpointUniforms *uniforms)
|
||||
{
|
||||
staticClipper.Clear();
|
||||
mClipper = &staticClipper;
|
||||
|
||||
Viewpoint = parentvp;
|
||||
lightmode = Level->lightMode;
|
||||
if (uniforms)
|
||||
{
|
||||
VPUniforms = *uniforms;
|
||||
// The clip planes will never be inherited from the parent drawinfo.
|
||||
VPUniforms.mClipLine.X = -1000001.f;
|
||||
VPUniforms.mClipHeight = 0;
|
||||
}
|
||||
else VPUniforms.SetDefaults();
|
||||
mClipper->SetViewpoint(Viewpoint);
|
||||
|
||||
ClearBuffers();
|
||||
|
||||
for (int i = 0; i < GLDL_TYPES; i++) drawlists[i].Reset();
|
||||
hudsprites.Clear();
|
||||
vpIndex = 0;
|
||||
|
||||
// Fullbright information needs to be propagated from the main view.
|
||||
if (outer != nullptr) FullbrightFlags = outer->FullbrightFlags;
|
||||
else FullbrightFlags = 0;
|
||||
|
||||
outer = gl_drawinfo;
|
||||
gl_drawinfo = this;
|
||||
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
HWDrawInfo *HWDrawInfo::EndDrawInfo()
|
||||
{
|
||||
assert(this == gl_drawinfo);
|
||||
for (int i = 0; i < GLDL_TYPES; i++) drawlists[i].Reset();
|
||||
gl_drawinfo = outer;
|
||||
di_list.Release(this);
|
||||
if (gl_drawinfo == nullptr)
|
||||
ResetRenderDataAllocator();
|
||||
return gl_drawinfo;
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void HWDrawInfo::ClearBuffers()
|
||||
{
|
||||
otherFloorPlanes.Clear();
|
||||
otherCeilingPlanes.Clear();
|
||||
floodFloorSegs.Clear();
|
||||
floodCeilingSegs.Clear();
|
||||
|
||||
// clear all the lists that might not have been cleared already
|
||||
MissingUpperTextures.Clear();
|
||||
MissingLowerTextures.Clear();
|
||||
MissingUpperSegs.Clear();
|
||||
MissingLowerSegs.Clear();
|
||||
SubsectorHacks.Clear();
|
||||
//CeilingStacks.Clear();
|
||||
//FloorStacks.Clear();
|
||||
HandledSubsectors.Clear();
|
||||
spriteindex = 0;
|
||||
|
||||
if (Level)
|
||||
{
|
||||
CurrentMapSections.Resize(Level->NumMapSections);
|
||||
CurrentMapSections.Zero();
|
||||
|
||||
section_renderflags.Resize(Level->sections.allSections.Size());
|
||||
ss_renderflags.Resize(Level->subsectors.Size());
|
||||
no_renderflags.Resize(Level->subsectors.Size());
|
||||
|
||||
memset(§ion_renderflags[0], 0, Level->sections.allSections.Size() * sizeof(section_renderflags[0]));
|
||||
memset(&ss_renderflags[0], 0, Level->subsectors.Size() * sizeof(ss_renderflags[0]));
|
||||
memset(&no_renderflags[0], 0, Level->nodes.Size() * sizeof(no_renderflags[0]));
|
||||
}
|
||||
|
||||
Decals[0].Clear();
|
||||
Decals[1].Clear();
|
||||
|
||||
mClipPortal = nullptr;
|
||||
mCurrentPortal = nullptr;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void HWDrawInfo::UpdateCurrentMapSection()
|
||||
{
|
||||
const int mapsection = Level->PointInRenderSubsector(Viewpoint.Pos)->mapsection;
|
||||
CurrentMapSections.Set(mapsection);
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// Sets the area the camera is in
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void HWDrawInfo::SetViewArea()
|
||||
{
|
||||
auto &vp = Viewpoint;
|
||||
// The render_sector is better suited to represent the current position in GL
|
||||
vp.sector = Level->PointInRenderSubsector(vp.Pos)->render_sector;
|
||||
|
||||
// Get the heightsec state from the render sector, not the current one!
|
||||
if (vp.sector->GetHeightSec())
|
||||
{
|
||||
in_area = vp.Pos.Z <= vp.sector->heightsec->floorplane.ZatPoint(vp.Pos) ? area_below :
|
||||
(vp.Pos.Z > vp.sector->heightsec->ceilingplane.ZatPoint(vp.Pos) &&
|
||||
!(vp.sector->heightsec->MoreFlags&SECMF_FAKEFLOORONLY)) ? area_above : area_normal;
|
||||
}
|
||||
else
|
||||
{
|
||||
in_area = Level->HasHeightSecs ? area_default : area_normal; // depends on exposed lower sectors, if map contains heightsecs.
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
//
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
int HWDrawInfo::SetFullbrightFlags(player_t *player)
|
||||
{
|
||||
FullbrightFlags = 0;
|
||||
|
||||
// check for special colormaps
|
||||
player_t * cplayer = player? player->camera->player : nullptr;
|
||||
if (cplayer)
|
||||
{
|
||||
int cm = CM_DEFAULT;
|
||||
if (cplayer->extralight == INT_MIN)
|
||||
{
|
||||
cm = CM_FIRSTSPECIALCOLORMAP + INVERSECOLORMAP;
|
||||
Viewpoint.extralight = 0;
|
||||
FullbrightFlags = Fullbright;
|
||||
// This does never set stealth vision.
|
||||
}
|
||||
else if (cplayer->fixedcolormap != NOFIXEDCOLORMAP)
|
||||
{
|
||||
cm = CM_FIRSTSPECIALCOLORMAP + cplayer->fixedcolormap;
|
||||
FullbrightFlags = Fullbright;
|
||||
if (gl_enhanced_nv_stealth > 2) FullbrightFlags |= StealthVision;
|
||||
}
|
||||
else if (cplayer->fixedlightlevel != -1)
|
||||
{
|
||||
auto torchtype = PClass::FindActor(NAME_PowerTorch);
|
||||
auto litetype = PClass::FindActor(NAME_PowerLightAmp);
|
||||
for (AActor *in = cplayer->mo->Inventory; in; in = in->Inventory)
|
||||
{
|
||||
// Need special handling for light amplifiers
|
||||
if (in->IsKindOf(torchtype))
|
||||
{
|
||||
FullbrightFlags = Fullbright;
|
||||
if (gl_enhanced_nv_stealth > 1) FullbrightFlags |= StealthVision;
|
||||
}
|
||||
else if (in->IsKindOf(litetype))
|
||||
{
|
||||
FullbrightFlags = Fullbright;
|
||||
if (gl_enhanced_nightvision) FullbrightFlags |= Nightvision;
|
||||
if (gl_enhanced_nv_stealth > 0) FullbrightFlags |= StealthVision;
|
||||
}
|
||||
}
|
||||
}
|
||||
return cm;
|
||||
}
|
||||
else
|
||||
{
|
||||
return CM_DEFAULT;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// R_FrustumAngle
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
angle_t HWDrawInfo::FrustumAngle()
|
||||
{
|
||||
float tilt = fabs(Viewpoint.HWAngles.Pitch.Degrees);
|
||||
|
||||
// If the pitch is larger than this you can look all around at a FOV of 90°
|
||||
if (tilt > 46.0f) return 0xffffffff;
|
||||
|
||||
// ok, this is a gross hack that barely works...
|
||||
// but at least it doesn't overestimate too much...
|
||||
double floatangle = 2.0 + (45.0 + ((tilt / 1.9)))*Viewpoint.FieldOfView.Degrees*48.0 / AspectMultiplier(r_viewwindow.WidescreenRatio) / 90.0;
|
||||
angle_t a1 = DAngle(floatangle).BAMs();
|
||||
if (a1 >= ANGLE_180) return 0xffffffff;
|
||||
return a1;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// Setup the modelview matrix
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void HWDrawInfo::SetViewMatrix(const FRotator &angles, float vx, float vy, float vz, bool mirror, bool planemirror)
|
||||
{
|
||||
float mult = mirror ? -1.f : 1.f;
|
||||
float planemult = planemirror ? -Level->info->pixelstretch : Level->info->pixelstretch;
|
||||
|
||||
VPUniforms.mViewMatrix.loadIdentity();
|
||||
VPUniforms.mViewMatrix.rotate(angles.Roll.Degrees, 0.0f, 0.0f, 1.0f);
|
||||
VPUniforms.mViewMatrix.rotate(angles.Pitch.Degrees, 1.0f, 0.0f, 0.0f);
|
||||
VPUniforms.mViewMatrix.rotate(angles.Yaw.Degrees, 0.0f, mult, 0.0f);
|
||||
VPUniforms.mViewMatrix.translate(vx * mult, -vz * planemult, -vy);
|
||||
VPUniforms.mViewMatrix.scale(-mult, planemult, 1);
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// SetupView
|
||||
// Setup the view rotation matrix for the given viewpoint
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
void HWDrawInfo::SetupView(FRenderState &state, float vx, float vy, float vz, bool mirror, bool planemirror)
|
||||
{
|
||||
auto &vp = Viewpoint;
|
||||
vp.SetViewAngle(r_viewwindow);
|
||||
SetViewMatrix(vp.HWAngles, vx, vy, vz, mirror, planemirror);
|
||||
SetCameraPos(vp.Pos);
|
||||
VPUniforms.CalcDependencies();
|
||||
vpIndex = screen->mViewpoints->SetViewpoint(state, &VPUniforms);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
//
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
HWPortal * HWDrawInfo::FindPortal(const void * src)
|
||||
{
|
||||
int i = Portals.Size() - 1;
|
||||
|
||||
while (i >= 0 && Portals[i] && Portals[i]->GetSource() != src) i--;
|
||||
return i >= 0 ? Portals[i] : nullptr;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
//
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void HWViewpointUniforms::SetDefaults()
|
||||
{
|
||||
mProjectionMatrix.loadIdentity();
|
||||
mViewMatrix.loadIdentity();
|
||||
mNormalViewMatrix.loadIdentity();
|
||||
mViewHeight = viewheight;
|
||||
mGlobVis = (float)R_GetGlobVis(r_viewwindow, r_visibility) / 32.f;
|
||||
mPalLightLevels = static_cast<int>(gl_bandedswlight) | (static_cast<int>(gl_fogmode) << 8) | (static_cast<int>(gl_lightmode) << 16);
|
||||
mClipLine.X = -10000000.0f;
|
||||
mShadowmapFilter = gl_shadowmap_filter;
|
||||
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
//
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
GLDecal *HWDrawInfo::AddDecal(bool onmirror)
|
||||
{
|
||||
auto decal = (GLDecal*)RenderDataAllocator.Alloc(sizeof(GLDecal));
|
||||
Decals[onmirror ? 1 : 0].Push(decal);
|
||||
return decal;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// CreateScene
|
||||
//
|
||||
// creates the draw lists for the current scene
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void HWDrawInfo::CreateScene()
|
||||
{
|
||||
const auto &vp = Viewpoint;
|
||||
angle_t a1 = FrustumAngle();
|
||||
mClipper->SafeAddClipRangeRealAngles(vp.Angles.Yaw.BAMs() + a1, vp.Angles.Yaw.BAMs() - a1);
|
||||
|
||||
// reset the portal manager
|
||||
screen->mPortalState->StartFrame();
|
||||
|
||||
ProcessAll.Clock();
|
||||
|
||||
// clip the scene and fill the drawlists
|
||||
screen->mVertexData->Map();
|
||||
screen->mLights->Map();
|
||||
|
||||
RenderBSP(Level->HeadNode());
|
||||
|
||||
// And now the crappy hacks that have to be done to avoid rendering anomalies.
|
||||
// These cannot be multithreaded when the time comes because all these depend
|
||||
// on the global 'validcount' variable.
|
||||
|
||||
HandleMissingTextures(in_area); // Missing upper/lower textures
|
||||
HandleHackedSubsectors(); // open sector hacks for deep water
|
||||
PrepareUnhandledMissingTextures();
|
||||
DispatchRenderHacks();
|
||||
screen->mLights->Unmap();
|
||||
screen->mVertexData->Unmap();
|
||||
|
||||
ProcessAll.Unclock();
|
||||
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// RenderScene
|
||||
//
|
||||
// Draws the current draw lists for the non GLSL renderer
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void HWDrawInfo::RenderScene(FRenderState &state)
|
||||
{
|
||||
const auto &vp = Viewpoint;
|
||||
RenderAll.Clock();
|
||||
|
||||
state.SetDepthMask(true);
|
||||
|
||||
screen->mLights->BindBase();
|
||||
state.EnableFog(true);
|
||||
state.SetRenderStyle(STYLE_Source);
|
||||
|
||||
if (gl_sort_textures)
|
||||
{
|
||||
drawlists[GLDL_PLAINWALLS].SortWalls();
|
||||
drawlists[GLDL_PLAINFLATS].SortFlats();
|
||||
drawlists[GLDL_MASKEDWALLS].SortWalls();
|
||||
drawlists[GLDL_MASKEDFLATS].SortFlats();
|
||||
drawlists[GLDL_MASKEDWALLSOFS].SortWalls();
|
||||
}
|
||||
|
||||
// Part 1: solid geometry. This is set up so that there are no transparent parts
|
||||
state.SetDepthFunc(DF_Less);
|
||||
state.AlphaFunc(Alpha_GEqual, 0.f);
|
||||
state.ClearDepthBias();
|
||||
|
||||
state.EnableTexture(gl_texture);
|
||||
state.EnableBrightmap(true);
|
||||
drawlists[GLDL_PLAINWALLS].DrawWalls(this, state, false);
|
||||
drawlists[GLDL_PLAINFLATS].DrawFlats(this, state, false);
|
||||
|
||||
|
||||
// Part 2: masked geometry. This is set up so that only pixels with alpha>gl_mask_threshold will show
|
||||
state.AlphaFunc(Alpha_GEqual, gl_mask_threshold);
|
||||
drawlists[GLDL_MASKEDWALLS].DrawWalls(this, state, false);
|
||||
drawlists[GLDL_MASKEDFLATS].DrawFlats(this, state, false);
|
||||
|
||||
// Part 3: masked geometry with polygon offset. This list is empty most of the time so only waste time on it when in use.
|
||||
if (drawlists[GLDL_MASKEDWALLSOFS].Size() > 0)
|
||||
{
|
||||
state.SetDepthBias(-1, -128);
|
||||
drawlists[GLDL_MASKEDWALLSOFS].DrawWalls(this, state, false);
|
||||
state.ClearDepthBias();
|
||||
}
|
||||
|
||||
drawlists[GLDL_MODELS].Draw(this, state, false);
|
||||
|
||||
state.SetRenderStyle(STYLE_Translucent);
|
||||
|
||||
// Part 4: Draw decals (not a real pass)
|
||||
state.SetDepthFunc(DF_LEqual);
|
||||
DrawDecals(state, Decals[0]);
|
||||
|
||||
RenderAll.Unclock();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// RenderTranslucent
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void HWDrawInfo::RenderTranslucent(FRenderState &state)
|
||||
{
|
||||
RenderAll.Clock();
|
||||
|
||||
// final pass: translucent stuff
|
||||
state.AlphaFunc(Alpha_GEqual, gl_mask_sprite_threshold);
|
||||
state.SetRenderStyle(STYLE_Translucent);
|
||||
|
||||
state.EnableBrightmap(true);
|
||||
drawlists[GLDL_TRANSLUCENTBORDER].Draw(this, state, true);
|
||||
state.SetDepthMask(false);
|
||||
|
||||
drawlists[GLDL_TRANSLUCENT].DrawSorted(this, state);
|
||||
state.EnableBrightmap(false);
|
||||
|
||||
|
||||
state.AlphaFunc(Alpha_GEqual, 0.5f);
|
||||
state.SetDepthMask(true);
|
||||
|
||||
RenderAll.Unclock();
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// RenderTranslucent
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void HWDrawInfo::RenderPortal(HWPortal *p, FRenderState &state, bool usestencil)
|
||||
{
|
||||
auto gp = static_cast<HWPortal *>(p);
|
||||
gp->SetupStencil(this, state, usestencil);
|
||||
auto new_di = StartDrawInfo(this->Level, this, Viewpoint, &VPUniforms);
|
||||
new_di->mCurrentPortal = gp;
|
||||
state.SetLightIndex(-1);
|
||||
gp->DrawContents(new_di, state);
|
||||
new_di->EndDrawInfo();
|
||||
state.SetVertexBuffer(screen->mVertexData);
|
||||
screen->mViewpoints->Bind(state, vpIndex);
|
||||
gp->RemoveStencil(this, state, usestencil);
|
||||
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// Draws player sprites and color blend
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
|
||||
void HWDrawInfo::EndDrawScene(sector_t * viewsector, FRenderState &state)
|
||||
{
|
||||
state.EnableFog(false);
|
||||
|
||||
// [BB] HUD models need to be rendered here.
|
||||
const bool renderHUDModel = IsHUDModelForPlayerAvailable(players[consoleplayer].camera->player);
|
||||
if (renderHUDModel)
|
||||
{
|
||||
// [BB] The HUD model should be drawn over everything else already drawn.
|
||||
state.Clear(CT_Depth);
|
||||
DrawPlayerSprites(true, state);
|
||||
}
|
||||
|
||||
state.EnableStencil(false);
|
||||
state.SetViewport(screen->mScreenViewport.left, screen->mScreenViewport.top, screen->mScreenViewport.width, screen->mScreenViewport.height);
|
||||
|
||||
// Restore standard rendering state
|
||||
state.SetRenderStyle(STYLE_Translucent);
|
||||
state.ResetColor();
|
||||
state.EnableTexture(true);
|
||||
state.SetScissor(0, 0, -1, -1);
|
||||
}
|
||||
|
||||
void HWDrawInfo::DrawEndScene2D(sector_t * viewsector, FRenderState &state)
|
||||
{
|
||||
const bool renderHUDModel = IsHUDModelForPlayerAvailable(players[consoleplayer].camera->player);
|
||||
auto vrmode = VRMode::GetVRMode(true);
|
||||
|
||||
HWViewpointUniforms vp = VPUniforms;
|
||||
vp.mViewMatrix.loadIdentity();
|
||||
vp.mProjectionMatrix = vrmode->GetHUDSpriteProjection();
|
||||
screen->mViewpoints->SetViewpoint(state, &vp);
|
||||
state.EnableDepthTest(false);
|
||||
state.EnableMultisampling(false);
|
||||
|
||||
DrawPlayerSprites(false, state);
|
||||
|
||||
state.SetNoSoftLightLevel();
|
||||
|
||||
// Restore standard rendering state
|
||||
state.SetRenderStyle(STYLE_Translucent);
|
||||
state.ResetColor();
|
||||
state.EnableTexture(true);
|
||||
state.SetScissor(0, 0, -1, -1);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// sets 3D viewport and initial state
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void HWDrawInfo::Set3DViewport(FRenderState &state)
|
||||
{
|
||||
// Always clear all buffers with scissor test disabled.
|
||||
// This is faster on newer hardware because it allows the GPU to skip
|
||||
// reading from slower memory where the full buffers are stored.
|
||||
state.SetScissor(0, 0, -1, -1);
|
||||
state.Clear(CT_Color | CT_Depth | CT_Stencil);
|
||||
|
||||
const auto &bounds = screen->mSceneViewport;
|
||||
state.SetViewport(bounds.left, bounds.top, bounds.width, bounds.height);
|
||||
state.SetScissor(bounds.left, bounds.top, bounds.width, bounds.height);
|
||||
state.EnableMultisampling(true);
|
||||
state.EnableDepthTest(true);
|
||||
state.EnableStencil(true);
|
||||
state.SetStencil(0, SOP_Keep, SF_AllOn);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// R_RenderView - renders one view - either the screen or a camera texture
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void HWDrawInfo::ProcessScene(bool toscreen, const std::function<void(HWDrawInfo *,int)> &drawScene)
|
||||
{
|
||||
screen->mPortalState->BeginScene();
|
||||
|
||||
int mapsection = Level->PointInRenderSubsector(Viewpoint.Pos)->mapsection;
|
||||
CurrentMapSections.Set(mapsection);
|
||||
DrawScene = drawScene;
|
||||
DrawScene(this, toscreen ? DM_MAINVIEW : DM_OFFSCREEN);
|
||||
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void HWDrawInfo::AddSubsectorToPortal(FSectorPortalGroup *ptg, subsector_t *sub)
|
||||
{
|
||||
auto portal = FindPortal(ptg);
|
||||
if (!portal)
|
||||
{
|
||||
portal = new HWSectorStackPortal(screen->mPortalState, ptg);
|
||||
Portals.Push(portal);
|
||||
}
|
||||
auto ptl = static_cast<HWSectorStackPortal*>(portal);
|
||||
ptl->AddSubsector(sub);
|
||||
}
|
||||
|
332
src/rendering/hwrenderer/scene/hw_drawinfo.h
Normal file
332
src/rendering/hwrenderer/scene/hw_drawinfo.h
Normal file
|
@ -0,0 +1,332 @@
|
|||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
#include "vectors.h"
|
||||
#include "r_defs.h"
|
||||
#include "r_utility.h"
|
||||
#include "hw_viewpointuniforms.h"
|
||||
#include "v_video.h"
|
||||
#include "hw_weapon.h"
|
||||
#include "hw_drawlist.h"
|
||||
|
||||
enum EDrawMode
|
||||
{
|
||||
DM_MAINVIEW,
|
||||
DM_OFFSCREEN,
|
||||
DM_PORTAL,
|
||||
DM_SKYPORTAL
|
||||
};
|
||||
|
||||
struct FSectorPortalGroup;
|
||||
struct FLinePortalSpan;
|
||||
struct FFlatVertex;
|
||||
class GLWall;
|
||||
class GLFlat;
|
||||
class GLSprite;
|
||||
struct GLDecal;
|
||||
class IShadowMap;
|
||||
struct particle_t;
|
||||
struct FDynLightData;
|
||||
struct HUDSprite;
|
||||
class Clipper;
|
||||
class HWPortal;
|
||||
class FFlatVertexBuffer;
|
||||
class IRenderQueue;
|
||||
class HWScenePortalBase;
|
||||
class FRenderState;
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// these are used to link faked planes due to missing textures to a sector
|
||||
//
|
||||
//==========================================================================
|
||||
struct gl_subsectorrendernode
|
||||
{
|
||||
gl_subsectorrendernode * next;
|
||||
subsector_t * sub;
|
||||
int lightindex;
|
||||
int vertexindex;
|
||||
};
|
||||
|
||||
struct gl_floodrendernode
|
||||
{
|
||||
gl_floodrendernode * next;
|
||||
seg_t *seg;
|
||||
int vertexindex;
|
||||
// This will use the light list of the originating sector.
|
||||
};
|
||||
|
||||
enum area_t : int;
|
||||
|
||||
enum SectorRenderFlags
|
||||
{
|
||||
// This is used to merge several subsectors into a single draw item
|
||||
SSRF_RENDERFLOOR = 1,
|
||||
SSRF_RENDERCEILING = 2,
|
||||
SSRF_RENDER3DPLANES = 4,
|
||||
SSRF_RENDERALL = 7,
|
||||
SSRF_PROCESSED = 8,
|
||||
SSRF_SEEN = 16,
|
||||
SSRF_PLANEHACK = 32,
|
||||
SSRF_FLOODHACK = 64
|
||||
};
|
||||
|
||||
enum EPortalClip
|
||||
{
|
||||
PClip_InFront,
|
||||
PClip_Inside,
|
||||
PClip_Behind,
|
||||
};
|
||||
|
||||
enum DrawListType
|
||||
{
|
||||
GLDL_PLAINWALLS,
|
||||
GLDL_PLAINFLATS,
|
||||
GLDL_MASKEDWALLS,
|
||||
GLDL_MASKEDFLATS,
|
||||
GLDL_MASKEDWALLSOFS,
|
||||
GLDL_MODELS,
|
||||
|
||||
GLDL_TRANSLUCENT,
|
||||
GLDL_TRANSLUCENTBORDER,
|
||||
|
||||
GLDL_TYPES,
|
||||
};
|
||||
|
||||
|
||||
struct HWDrawInfo
|
||||
{
|
||||
struct wallseg
|
||||
{
|
||||
float x1, y1, z1, x2, y2, z2;
|
||||
};
|
||||
|
||||
struct MissingTextureInfo
|
||||
{
|
||||
seg_t * seg;
|
||||
subsector_t * sub;
|
||||
float Planez;
|
||||
float Planezfront;
|
||||
};
|
||||
|
||||
struct MissingSegInfo
|
||||
{
|
||||
seg_t * seg;
|
||||
int MTI_Index; // tells us which MissingTextureInfo represents this seg.
|
||||
};
|
||||
|
||||
struct SubsectorHackInfo
|
||||
{
|
||||
subsector_t * sub;
|
||||
uint8_t flags;
|
||||
};
|
||||
|
||||
enum EFullbrightFlags
|
||||
{
|
||||
Fullbright = 1,
|
||||
Nightvision = 2,
|
||||
StealthVision = 4
|
||||
};
|
||||
|
||||
bool isFullbrightScene() const { return !!(FullbrightFlags & Fullbright); }
|
||||
bool isNightvision() const { return !!(FullbrightFlags & Nightvision); }
|
||||
bool isStealthVision() const { return !!(FullbrightFlags & StealthVision); }
|
||||
|
||||
HWDrawList drawlists[GLDL_TYPES];
|
||||
int vpIndex;
|
||||
ELightMode lightmode;
|
||||
|
||||
FLevelLocals *Level;
|
||||
HWDrawInfo * outer = nullptr;
|
||||
int FullbrightFlags;
|
||||
std::atomic<int> spriteindex;
|
||||
HWPortal *mClipPortal;
|
||||
HWPortal *mCurrentPortal;
|
||||
//FRotator mAngles;
|
||||
Clipper *mClipper;
|
||||
FRenderViewpoint Viewpoint;
|
||||
HWViewpointUniforms VPUniforms; // per-viewpoint uniform state
|
||||
TArray<HWPortal *> Portals;
|
||||
TArray<GLDecal *> Decals[2]; // the second slot is for mirrors which get rendered in a separate pass.
|
||||
TArray<HUDSprite> hudsprites; // These may just be stored by value.
|
||||
|
||||
TArray<MissingTextureInfo> MissingUpperTextures;
|
||||
TArray<MissingTextureInfo> MissingLowerTextures;
|
||||
|
||||
TArray<MissingSegInfo> MissingUpperSegs;
|
||||
TArray<MissingSegInfo> MissingLowerSegs;
|
||||
|
||||
TArray<SubsectorHackInfo> SubsectorHacks;
|
||||
|
||||
TMap<int, gl_subsectorrendernode*> otherFloorPlanes;
|
||||
TMap<int, gl_subsectorrendernode*> otherCeilingPlanes;
|
||||
TMap<int, gl_floodrendernode*> floodFloorSegs;
|
||||
TMap<int, gl_floodrendernode*> floodCeilingSegs;
|
||||
|
||||
//TArray<sector_t *> CeilingStacks;
|
||||
//TArray<sector_t *> FloorStacks;
|
||||
|
||||
TArray<subsector_t *> HandledSubsectors;
|
||||
|
||||
TArray<uint8_t> section_renderflags;
|
||||
TArray<uint8_t> ss_renderflags;
|
||||
TArray<uint8_t> no_renderflags;
|
||||
|
||||
// This is needed by the BSP traverser.
|
||||
BitArray CurrentMapSections; // this cannot be a single number, because a group of portals with the same displacement may link different sections.
|
||||
area_t in_area;
|
||||
fixed_t viewx, viewy; // since the nodes are still fixed point, keeping the view position also fixed point for node traversal is faster.
|
||||
bool multithread;
|
||||
|
||||
std::function<void(HWDrawInfo *, int)> DrawScene = nullptr;
|
||||
|
||||
private:
|
||||
// For ProcessLowerMiniseg
|
||||
bool inview;
|
||||
subsector_t * viewsubsector;
|
||||
TArray<seg_t *> lowersegs;
|
||||
|
||||
subsector_t *currentsubsector; // used by the line processing code.
|
||||
sector_t *currentsector;
|
||||
|
||||
void WorkerThread();
|
||||
|
||||
void UnclipSubsector(subsector_t *sub);
|
||||
|
||||
void AddLine(seg_t *seg, bool portalclip);
|
||||
void PolySubsector(subsector_t * sub);
|
||||
void RenderPolyBSPNode(void *node);
|
||||
void AddPolyobjs(subsector_t *sub);
|
||||
void AddLines(subsector_t * sub, sector_t * sector);
|
||||
void AddSpecialPortalLines(subsector_t * sub, sector_t * sector, line_t *line);
|
||||
public:
|
||||
void RenderThings(subsector_t * sub, sector_t * sector);
|
||||
void RenderParticles(subsector_t *sub, sector_t *front);
|
||||
void DoSubsector(subsector_t * sub);
|
||||
int SetupLightsForOtherPlane(subsector_t * sub, FDynLightData &lightdata, const secplane_t *plane);
|
||||
int CreateOtherPlaneVertices(subsector_t *sub, const secplane_t *plane);
|
||||
void DrawPSprite(HUDSprite *huds, FRenderState &state);
|
||||
void SetColor(FRenderState &state, int sectorlightlevel, int rellight, bool fullbright, const FColormap &cm, float alpha, bool weapon = false);
|
||||
void SetFog(FRenderState &state, int lightlevel, int rellight, bool fullbright, const FColormap *cmap, bool isadditive);
|
||||
void SetShaderLight(FRenderState &state, float level, float olight);
|
||||
int CalcLightLevel(int lightlevel, int rellight, bool weapon, int blendfactor);
|
||||
PalEntry CalcLightColor(int light, PalEntry pe, int blendfactor);
|
||||
float GetFogDensity(int lightlevel, PalEntry fogcolor, int sectorfogdensity, int blendfactor);
|
||||
bool CheckFog(sector_t *frontsector, sector_t *backsector);
|
||||
WeaponLighting GetWeaponLighting(sector_t *viewsector, const DVector3 &pos, int cm, area_t in_area, const DVector3 &playerpos);
|
||||
public:
|
||||
|
||||
void SetCameraPos(const DVector3 &pos)
|
||||
{
|
||||
VPUniforms.mCameraPos = { (float)pos.X, (float)pos.Z, (float)pos.Y, 0.f };
|
||||
}
|
||||
|
||||
void SetClipHeight(float h, float d)
|
||||
{
|
||||
VPUniforms.mClipHeight = h;
|
||||
VPUniforms.mClipHeightDirection = d;
|
||||
VPUniforms.mClipLine.X = -1000001.f;
|
||||
}
|
||||
|
||||
void SetClipLine(line_t *line)
|
||||
{
|
||||
VPUniforms.mClipLine = { (float)line->v1->fX(), (float)line->v1->fY(), (float)line->Delta().X, (float)line->Delta().Y };
|
||||
VPUniforms.mClipHeight = 0;
|
||||
}
|
||||
|
||||
HWPortal * FindPortal(const void * src);
|
||||
void RenderBSPNode(void *node);
|
||||
void RenderBSP(void *node);
|
||||
|
||||
static HWDrawInfo *StartDrawInfo(FLevelLocals *lev, HWDrawInfo *parent, FRenderViewpoint &parentvp, HWViewpointUniforms *uniforms);
|
||||
void StartScene(FRenderViewpoint &parentvp, HWViewpointUniforms *uniforms);
|
||||
void ClearBuffers();
|
||||
HWDrawInfo *EndDrawInfo();
|
||||
void SetViewArea();
|
||||
int SetFullbrightFlags(player_t *player);
|
||||
|
||||
void CreateScene();
|
||||
void RenderScene(FRenderState &state);
|
||||
void RenderTranslucent(FRenderState &state);
|
||||
void RenderPortal(HWPortal *p, FRenderState &state, bool usestencil);
|
||||
void EndDrawScene(sector_t * viewsector, FRenderState &state);
|
||||
void DrawEndScene2D(sector_t * viewsector, FRenderState &state);
|
||||
void Set3DViewport(FRenderState &state);
|
||||
void ProcessScene(bool toscreen, const std::function<void(HWDrawInfo *, int)> &drawScene);
|
||||
|
||||
bool DoOneSectorUpper(subsector_t * subsec, float planez, area_t in_area);
|
||||
bool DoOneSectorLower(subsector_t * subsec, float planez, area_t in_area);
|
||||
bool DoFakeBridge(subsector_t * subsec, float planez, area_t in_area);
|
||||
bool DoFakeCeilingBridge(subsector_t * subsec, float planez, area_t in_area);
|
||||
|
||||
bool CheckAnchorFloor(subsector_t * sub);
|
||||
bool CollectSubsectorsFloor(subsector_t * sub, sector_t * anchor);
|
||||
bool CheckAnchorCeiling(subsector_t * sub);
|
||||
bool CollectSubsectorsCeiling(subsector_t * sub, sector_t * anchor);
|
||||
void CollectSectorStacksCeiling(subsector_t * sub, sector_t * anchor, area_t in_area);
|
||||
void CollectSectorStacksFloor(subsector_t * sub, sector_t * anchor, area_t in_area);
|
||||
|
||||
void DispatchRenderHacks();
|
||||
void AddUpperMissingTexture(side_t * side, subsector_t *sub, float backheight);
|
||||
void AddLowerMissingTexture(side_t * side, subsector_t *sub, float backheight);
|
||||
void HandleMissingTextures(area_t in_area);
|
||||
void PrepareUnhandledMissingTextures();
|
||||
void PrepareUpperGap(seg_t * seg);
|
||||
void PrepareLowerGap(seg_t * seg);
|
||||
void CreateFloodPoly(wallseg * ws, FFlatVertex *vertices, float planez, sector_t * sec, bool ceiling);
|
||||
void CreateFloodStencilPoly(wallseg * ws, FFlatVertex *vertices);
|
||||
|
||||
void AddHackedSubsector(subsector_t * sub);
|
||||
void HandleHackedSubsectors();
|
||||
void AddFloorStack(sector_t * sec);
|
||||
void AddCeilingStack(sector_t * sec);
|
||||
void ProcessSectorStacks(area_t in_area);
|
||||
|
||||
void ProcessActorsInPortal(FLinePortalSpan *glport, area_t in_area);
|
||||
|
||||
void AddOtherFloorPlane(int sector, gl_subsectorrendernode * node);
|
||||
void AddOtherCeilingPlane(int sector, gl_subsectorrendernode * node);
|
||||
|
||||
void GetDynSpriteLight(AActor *self, float x, float y, float z, FLightNode *node, int portalgroup, float *out);
|
||||
void GetDynSpriteLight(AActor *thing, particle_t *particle, float *out);
|
||||
|
||||
void PreparePlayerSprites(sector_t * viewsector, area_t in_area);
|
||||
void PrepareTargeterSprites();
|
||||
|
||||
void UpdateCurrentMapSection();
|
||||
void SetViewMatrix(const FRotator &angles, float vx, float vy, float vz, bool mirror, bool planemirror);
|
||||
void SetupView(FRenderState &state, float vx, float vy, float vz, bool mirror, bool planemirror);
|
||||
angle_t FrustumAngle();
|
||||
|
||||
void DrawDecals(FRenderState &state, TArray<GLDecal *> &decals);
|
||||
void DrawPlayerSprites(bool hudModelStep, FRenderState &state);
|
||||
|
||||
void ProcessLowerMinisegs(TArray<seg_t *> &lowersegs);
|
||||
void AddSubsectorToPortal(FSectorPortalGroup *portal, subsector_t *sub);
|
||||
|
||||
void AddWall(GLWall *w);
|
||||
void AddMirrorSurface(GLWall *w);
|
||||
void AddFlat(GLFlat *flat, bool fog);
|
||||
void AddSprite(GLSprite *sprite, bool translucent);
|
||||
|
||||
|
||||
GLDecal *AddDecal(bool onmirror);
|
||||
|
||||
bool isSoftwareLighting() const
|
||||
{
|
||||
return lightmode >= ELightMode::ZDoomSoftware;
|
||||
}
|
||||
|
||||
bool isDarkLightMode() const
|
||||
{
|
||||
return !!((int)lightmode & (int)ELightMode::Doom);
|
||||
}
|
||||
|
||||
void SetFallbackLightMode()
|
||||
{
|
||||
lightmode = ELightMode::Doom;
|
||||
}
|
||||
|
||||
};
|
||||
|
948
src/rendering/hwrenderer/scene/hw_drawlist.cpp
Normal file
948
src/rendering/hwrenderer/scene/hw_drawlist.cpp
Normal file
|
@ -0,0 +1,948 @@
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Copyright(C) 2002-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/
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
/*
|
||||
** hw_drawlist.cpp
|
||||
** The main container type for draw items.
|
||||
**
|
||||
*/
|
||||
|
||||
#include "r_sky.h"
|
||||
#include "r_utility.h"
|
||||
#include "doomstat.h"
|
||||
#include "actor.h"
|
||||
#include "g_levellocals.h"
|
||||
#include "hwrenderer/scene/hw_drawstructs.h"
|
||||
#include "hwrenderer/scene/hw_drawlist.h"
|
||||
#include "hwrenderer/data/flatvertices.h"
|
||||
#include "hwrenderer/utility/hw_clock.h"
|
||||
#include "hw_renderstate.h"
|
||||
#include "hw_drawinfo.h"
|
||||
#include "hw_fakeflat.h"
|
||||
|
||||
FMemArena RenderDataAllocator(1024*1024); // Use large blocks to reduce allocation time.
|
||||
|
||||
void ResetRenderDataAllocator()
|
||||
{
|
||||
RenderDataAllocator.FreeAll();
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
class StaticSortNodeArray : public TDeletingArray<SortNode*>
|
||||
{
|
||||
unsigned usecount;
|
||||
public:
|
||||
unsigned Size() { return usecount; }
|
||||
void Clear() { usecount=0; }
|
||||
void Release(int start) { usecount=start; }
|
||||
SortNode * GetNew();
|
||||
};
|
||||
|
||||
|
||||
SortNode * StaticSortNodeArray::GetNew()
|
||||
{
|
||||
if (usecount==TArray<SortNode*>::Size())
|
||||
{
|
||||
Push(new SortNode);
|
||||
}
|
||||
return operator[](usecount++);
|
||||
}
|
||||
|
||||
|
||||
static StaticSortNodeArray SortNodes;
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
void HWDrawList::Reset()
|
||||
{
|
||||
if (sorted) SortNodes.Release(SortNodeStart);
|
||||
sorted=NULL;
|
||||
walls.Clear();
|
||||
flats.Clear();
|
||||
sprites.Clear();
|
||||
drawitems.Clear();
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
inline void SortNode::UnlinkFromChain()
|
||||
{
|
||||
if (parent) parent->next=next;
|
||||
if (next) next->parent=parent;
|
||||
parent=next=NULL;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
inline void SortNode::Link(SortNode * hook)
|
||||
{
|
||||
if (hook)
|
||||
{
|
||||
parent=hook->parent;
|
||||
hook->parent=this;
|
||||
}
|
||||
next=hook;
|
||||
if (parent) parent->next=this;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
inline void SortNode::AddToEqual(SortNode *child)
|
||||
{
|
||||
child->UnlinkFromChain();
|
||||
child->equal=equal;
|
||||
equal=child;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
inline void SortNode::AddToLeft(SortNode * child)
|
||||
{
|
||||
child->UnlinkFromChain();
|
||||
child->Link(left);
|
||||
left=child;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
inline void SortNode::AddToRight(SortNode * child)
|
||||
{
|
||||
child->UnlinkFromChain();
|
||||
child->Link(right);
|
||||
right=child;
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
void HWDrawList::MakeSortList()
|
||||
{
|
||||
SortNode * p, * n, * c;
|
||||
unsigned i;
|
||||
|
||||
SortNodeStart=SortNodes.Size();
|
||||
p=NULL;
|
||||
n=SortNodes.GetNew();
|
||||
for(i=0;i<drawitems.Size();i++)
|
||||
{
|
||||
n->itemindex=(int)i;
|
||||
n->left=n->equal=n->right=NULL;
|
||||
n->parent=p;
|
||||
p=n;
|
||||
if (i!=drawitems.Size()-1)
|
||||
{
|
||||
c=SortNodes.GetNew();
|
||||
n->next=c;
|
||||
n=c;
|
||||
}
|
||||
else
|
||||
{
|
||||
n->next=NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
SortNode * HWDrawList::FindSortPlane(SortNode * head)
|
||||
{
|
||||
while (head->next && drawitems[head->itemindex].rendertype!=GLDIT_FLAT)
|
||||
head=head->next;
|
||||
if (drawitems[head->itemindex].rendertype==GLDIT_FLAT) return head;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
SortNode * HWDrawList::FindSortWall(SortNode * head)
|
||||
{
|
||||
float farthest = -FLT_MAX;
|
||||
float nearest = FLT_MAX;
|
||||
SortNode * best = NULL;
|
||||
SortNode * node = head;
|
||||
float bestdist = FLT_MAX;
|
||||
|
||||
while (node)
|
||||
{
|
||||
GLDrawItem * it = &drawitems[node->itemindex];
|
||||
if (it->rendertype == GLDIT_WALL)
|
||||
{
|
||||
float d = walls[it->index]->ViewDistance;
|
||||
if (d > farthest) farthest = d;
|
||||
if (d < nearest) nearest = d;
|
||||
}
|
||||
node = node->next;
|
||||
}
|
||||
if (farthest == INT_MIN) return NULL;
|
||||
node = head;
|
||||
farthest = (farthest + nearest) / 2;
|
||||
while (node)
|
||||
{
|
||||
GLDrawItem * it = &drawitems[node->itemindex];
|
||||
if (it->rendertype == GLDIT_WALL)
|
||||
{
|
||||
float di = fabsf(walls[it->index]->ViewDistance - farthest);
|
||||
if (!best || di < bestdist)
|
||||
{
|
||||
best = node;
|
||||
bestdist = di;
|
||||
}
|
||||
}
|
||||
node = node->next;
|
||||
}
|
||||
return best;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Note: sloped planes are a huge problem...
|
||||
//
|
||||
//==========================================================================
|
||||
void HWDrawList::SortPlaneIntoPlane(SortNode * head,SortNode * sort)
|
||||
{
|
||||
GLFlat * fh= flats[drawitems[head->itemindex].index];
|
||||
GLFlat * fs= flats[drawitems[sort->itemindex].index];
|
||||
|
||||
if (fh->z==fs->z)
|
||||
head->AddToEqual(sort);
|
||||
else if ( (fh->z<fs->z && fh->ceiling) || (fh->z>fs->z && !fh->ceiling))
|
||||
head->AddToLeft(sort);
|
||||
else
|
||||
head->AddToRight(sort);
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
void HWDrawList::SortWallIntoPlane(SortNode * head, SortNode * sort)
|
||||
{
|
||||
GLFlat * fh = flats[drawitems[head->itemindex].index];
|
||||
GLWall * ws = walls[drawitems[sort->itemindex].index];
|
||||
|
||||
bool ceiling = fh->z > SortZ;
|
||||
|
||||
if ((ws->ztop[0] > fh->z || ws->ztop[1] > fh->z) && (ws->zbottom[0] < fh->z || ws->zbottom[1] < fh->z))
|
||||
{
|
||||
// We have to split this wall!
|
||||
|
||||
GLWall *w = NewWall();
|
||||
*w = *ws;
|
||||
|
||||
// Splitting is done in the shader with clip planes, if available
|
||||
if (screen->hwcaps & RFL_NO_CLIP_PLANES)
|
||||
{
|
||||
ws->vertcount = 0; // invalidate current vertices.
|
||||
float newtexv = ws->tcs[GLWall::UPLFT].v + ((ws->tcs[GLWall::LOLFT].v - ws->tcs[GLWall::UPLFT].v) / (ws->zbottom[0] - ws->ztop[0])) * (fh->z - ws->ztop[0]);
|
||||
|
||||
// I make the very big assumption here that translucent walls in sloped sectors
|
||||
// and 3D-floors never coexist in the same level - If that were the case this
|
||||
// code would become extremely more complicated.
|
||||
if (!ceiling)
|
||||
{
|
||||
ws->ztop[1] = w->zbottom[1] = ws->ztop[0] = w->zbottom[0] = fh->z;
|
||||
ws->tcs[GLWall::UPRGT].v = w->tcs[GLWall::LORGT].v = ws->tcs[GLWall::UPLFT].v = w->tcs[GLWall::LOLFT].v = newtexv;
|
||||
}
|
||||
else
|
||||
{
|
||||
w->ztop[1] = ws->zbottom[1] = w->ztop[0] = ws->zbottom[0] = fh->z;
|
||||
w->tcs[GLWall::UPLFT].v = ws->tcs[GLWall::LOLFT].v = w->tcs[GLWall::UPRGT].v = ws->tcs[GLWall::LORGT].v = newtexv;
|
||||
}
|
||||
}
|
||||
|
||||
SortNode * sort2 = SortNodes.GetNew();
|
||||
memset(sort2, 0, sizeof(SortNode));
|
||||
sort2->itemindex = drawitems.Size() - 1;
|
||||
|
||||
head->AddToLeft(sort);
|
||||
head->AddToRight(sort2);
|
||||
}
|
||||
else if ((ws->zbottom[0] < fh->z && !ceiling) || (ws->ztop[0] > fh->z && ceiling)) // completely on the left side
|
||||
{
|
||||
head->AddToLeft(sort);
|
||||
}
|
||||
else
|
||||
{
|
||||
head->AddToRight(sort);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
void HWDrawList::SortSpriteIntoPlane(SortNode * head, SortNode * sort)
|
||||
{
|
||||
GLFlat * fh = flats[drawitems[head->itemindex].index];
|
||||
GLSprite * ss = sprites[drawitems[sort->itemindex].index];
|
||||
|
||||
bool ceiling = fh->z > SortZ;
|
||||
|
||||
auto hiz = ss->z1 > ss->z2 ? ss->z1 : ss->z2;
|
||||
auto loz = ss->z1 < ss->z2 ? ss->z1 : ss->z2;
|
||||
|
||||
if ((hiz > fh->z && loz < fh->z) || ss->modelframe)
|
||||
{
|
||||
// We have to split this sprite
|
||||
GLSprite *s = NewSprite();
|
||||
*s = *ss;
|
||||
|
||||
// Splitting is done in the shader with clip planes, if available.
|
||||
// The fallback here only really works for non-y-billboarded sprites.
|
||||
if (screen->hwcaps & RFL_NO_CLIP_PLANES)
|
||||
{
|
||||
float newtexv = ss->vt + ((ss->vb - ss->vt) / (ss->z2 - ss->z1))*(fh->z - ss->z1);
|
||||
|
||||
if (!ceiling)
|
||||
{
|
||||
ss->z1 = s->z2 = fh->z;
|
||||
ss->vt = s->vb = newtexv;
|
||||
}
|
||||
else
|
||||
{
|
||||
s->z1 = ss->z2 = fh->z;
|
||||
s->vt = ss->vb = newtexv;
|
||||
}
|
||||
}
|
||||
|
||||
SortNode * sort2 = SortNodes.GetNew();
|
||||
memset(sort2, 0, sizeof(SortNode));
|
||||
sort2->itemindex = drawitems.Size() - 1;
|
||||
|
||||
head->AddToLeft(sort);
|
||||
head->AddToRight(sort2);
|
||||
}
|
||||
else if ((ss->z2<fh->z && !ceiling) || (ss->z1>fh->z && ceiling)) // completely on the left side
|
||||
{
|
||||
head->AddToLeft(sort);
|
||||
}
|
||||
else
|
||||
{
|
||||
head->AddToRight(sort);
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
#define MIN_EQ (0.0005f)
|
||||
|
||||
// Lines start-end and fdiv must intersect.
|
||||
inline double CalcIntersectionVertex(GLWall *w1, GLWall * w2)
|
||||
{
|
||||
float ax = w1->glseg.x1, ay = w1->glseg.y1;
|
||||
float bx = w1->glseg.x2, by = w1->glseg.y2;
|
||||
float cx = w2->glseg.x1, cy = w2->glseg.y1;
|
||||
float dx = w2->glseg.x2, dy = w2->glseg.y2;
|
||||
return ((ay - cy)*(dx - cx) - (ax - cx)*(dy - cy)) / ((bx - ax)*(dy - cy) - (by - ay)*(dx - cx));
|
||||
}
|
||||
|
||||
void HWDrawList::SortWallIntoWall(HWDrawInfo *di, SortNode * head,SortNode * sort)
|
||||
{
|
||||
GLWall * wh= walls[drawitems[head->itemindex].index];
|
||||
GLWall * ws= walls[drawitems[sort->itemindex].index];
|
||||
float v1=wh->PointOnSide(ws->glseg.x1,ws->glseg.y1);
|
||||
float v2=wh->PointOnSide(ws->glseg.x2,ws->glseg.y2);
|
||||
|
||||
if (fabs(v1)<MIN_EQ && fabs(v2)<MIN_EQ)
|
||||
{
|
||||
if (ws->type==RENDERWALL_FOGBOUNDARY && wh->type!=RENDERWALL_FOGBOUNDARY)
|
||||
{
|
||||
head->AddToRight(sort);
|
||||
}
|
||||
else if (ws->type!=RENDERWALL_FOGBOUNDARY && wh->type==RENDERWALL_FOGBOUNDARY)
|
||||
{
|
||||
head->AddToLeft(sort);
|
||||
}
|
||||
else
|
||||
{
|
||||
head->AddToEqual(sort);
|
||||
}
|
||||
}
|
||||
else if (v1<MIN_EQ && v2<MIN_EQ)
|
||||
{
|
||||
head->AddToLeft(sort);
|
||||
}
|
||||
else if (v1>-MIN_EQ && v2>-MIN_EQ)
|
||||
{
|
||||
head->AddToRight(sort);
|
||||
}
|
||||
else
|
||||
{
|
||||
double r = CalcIntersectionVertex(ws, wh);
|
||||
|
||||
float ix=(float)(ws->glseg.x1+r*(ws->glseg.x2-ws->glseg.x1));
|
||||
float iy=(float)(ws->glseg.y1+r*(ws->glseg.y2-ws->glseg.y1));
|
||||
float iu=(float)(ws->tcs[GLWall::UPLFT].u + r * (ws->tcs[GLWall::UPRGT].u - ws->tcs[GLWall::UPLFT].u));
|
||||
float izt=(float)(ws->ztop[0]+r*(ws->ztop[1]-ws->ztop[0]));
|
||||
float izb=(float)(ws->zbottom[0]+r*(ws->zbottom[1]-ws->zbottom[0]));
|
||||
|
||||
ws->vertcount = 0; // invalidate current vertices.
|
||||
GLWall *w= NewWall();
|
||||
*w = *ws;
|
||||
|
||||
w->glseg.x1=ws->glseg.x2=ix;
|
||||
w->glseg.y1=ws->glseg.y2=iy;
|
||||
w->glseg.fracleft = ws->glseg.fracright = ws->glseg.fracleft + r*(ws->glseg.fracright - ws->glseg.fracleft);
|
||||
w->ztop[0]=ws->ztop[1]=izt;
|
||||
w->zbottom[0]=ws->zbottom[1]=izb;
|
||||
w->tcs[GLWall::LOLFT].u = w->tcs[GLWall::UPLFT].u = ws->tcs[GLWall::LORGT].u = ws->tcs[GLWall::UPRGT].u = iu;
|
||||
ws->MakeVertices(di, false);
|
||||
w->MakeVertices(di, false);
|
||||
|
||||
SortNode * sort2=SortNodes.GetNew();
|
||||
memset(sort2,0,sizeof(SortNode));
|
||||
sort2->itemindex=drawitems.Size()-1;
|
||||
|
||||
if (v1>0)
|
||||
{
|
||||
head->AddToLeft(sort2);
|
||||
head->AddToRight(sort);
|
||||
}
|
||||
else
|
||||
{
|
||||
head->AddToLeft(sort);
|
||||
head->AddToRight(sort2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
EXTERN_CVAR(Int, gl_billboard_mode)
|
||||
EXTERN_CVAR(Bool, gl_billboard_faces_camera)
|
||||
EXTERN_CVAR(Bool, gl_billboard_particles)
|
||||
|
||||
inline double CalcIntersectionVertex(GLSprite *s, GLWall * w2)
|
||||
{
|
||||
float ax = s->x1, ay = s->y1;
|
||||
float bx = s->x2, by = s->y2;
|
||||
float cx = w2->glseg.x1, cy = w2->glseg.y1;
|
||||
float dx = w2->glseg.x2, dy = w2->glseg.y2;
|
||||
return ((ay - cy)*(dx - cx) - (ax - cx)*(dy - cy)) / ((bx - ax)*(dy - cy) - (by - ay)*(dx - cx));
|
||||
}
|
||||
|
||||
void HWDrawList::SortSpriteIntoWall(HWDrawInfo *di, SortNode * head,SortNode * sort)
|
||||
{
|
||||
GLWall *wh= walls[drawitems[head->itemindex].index];
|
||||
GLSprite * ss= sprites[drawitems[sort->itemindex].index];
|
||||
|
||||
float v1 = wh->PointOnSide(ss->x1, ss->y1);
|
||||
float v2 = wh->PointOnSide(ss->x2, ss->y2);
|
||||
|
||||
if (fabs(v1)<MIN_EQ && fabs(v2)<MIN_EQ)
|
||||
{
|
||||
if (wh->type==RENDERWALL_FOGBOUNDARY)
|
||||
{
|
||||
head->AddToLeft(sort);
|
||||
}
|
||||
else
|
||||
{
|
||||
head->AddToEqual(sort);
|
||||
}
|
||||
}
|
||||
else if (v1<MIN_EQ && v2<MIN_EQ)
|
||||
{
|
||||
head->AddToLeft(sort);
|
||||
}
|
||||
else if (v1>-MIN_EQ && v2>-MIN_EQ)
|
||||
{
|
||||
head->AddToRight(sort);
|
||||
}
|
||||
else
|
||||
{
|
||||
const bool drawWithXYBillboard = ((ss->particle && gl_billboard_particles) || (!(ss->actor && ss->actor->renderflags & RF_FORCEYBILLBOARD)
|
||||
&& (gl_billboard_mode == 1 || (ss->actor && ss->actor->renderflags & RF_FORCEXYBILLBOARD))));
|
||||
|
||||
const bool drawBillboardFacingCamera = gl_billboard_faces_camera;
|
||||
// [Nash] has +ROLLSPRITE
|
||||
const bool rotated = (ss->actor != nullptr && ss->actor->renderflags & (RF_ROLLSPRITE | RF_WALLSPRITE | RF_FLATSPRITE));
|
||||
|
||||
// cannot sort them at the moment. This requires more complex splitting.
|
||||
if (drawWithXYBillboard || drawBillboardFacingCamera || rotated)
|
||||
{
|
||||
float v1 = wh->PointOnSide(ss->x, ss->y);
|
||||
if (v1 < 0)
|
||||
{
|
||||
head->AddToLeft(sort);
|
||||
}
|
||||
else
|
||||
{
|
||||
head->AddToRight(sort);
|
||||
}
|
||||
return;
|
||||
}
|
||||
double r=CalcIntersectionVertex(ss, wh);
|
||||
|
||||
float ix=(float)(ss->x1 + r * (ss->x2-ss->x1));
|
||||
float iy=(float)(ss->y1 + r * (ss->y2-ss->y1));
|
||||
float iu=(float)(ss->ul + r * (ss->ur-ss->ul));
|
||||
|
||||
GLSprite *s = NewSprite();
|
||||
*s = *ss;
|
||||
|
||||
s->x1=ss->x2=ix;
|
||||
s->y1=ss->y2=iy;
|
||||
s->ul=ss->ur=iu;
|
||||
|
||||
SortNode * sort2=SortNodes.GetNew();
|
||||
memset(sort2,0,sizeof(SortNode));
|
||||
sort2->itemindex=drawitems.Size()-1;
|
||||
|
||||
if (v1>0)
|
||||
{
|
||||
head->AddToLeft(sort2);
|
||||
head->AddToRight(sort);
|
||||
}
|
||||
else
|
||||
{
|
||||
head->AddToLeft(sort);
|
||||
head->AddToRight(sort2);
|
||||
}
|
||||
if (screen->BuffersArePersistent())
|
||||
{
|
||||
s->vertexindex = ss->vertexindex = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
s->CreateVertices(di);
|
||||
ss->CreateVertices(di);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
inline int HWDrawList::CompareSprites(SortNode * a,SortNode * b)
|
||||
{
|
||||
GLSprite * s1= sprites[drawitems[a->itemindex].index];
|
||||
GLSprite * s2= sprites[drawitems[b->itemindex].index];
|
||||
|
||||
int res = s1->depth - s2->depth;
|
||||
|
||||
if (res != 0) return -res;
|
||||
else return reverseSort? s2->index-s1->index : s1->index-s2->index;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
SortNode * HWDrawList::SortSpriteList(SortNode * head)
|
||||
{
|
||||
SortNode * n;
|
||||
int count;
|
||||
unsigned i;
|
||||
|
||||
static TArray<SortNode*> sortspritelist;
|
||||
|
||||
SortNode * parent=head->parent;
|
||||
|
||||
sortspritelist.Clear();
|
||||
for(count=0,n=head;n;n=n->next) sortspritelist.Push(n);
|
||||
std::stable_sort(sortspritelist.begin(), sortspritelist.end(), [=](SortNode *a, SortNode *b)
|
||||
{
|
||||
return CompareSprites(a, b) < 0;
|
||||
});
|
||||
|
||||
for(i=0;i<sortspritelist.Size();i++)
|
||||
{
|
||||
sortspritelist[i]->next=NULL;
|
||||
if (parent) parent->equal=sortspritelist[i];
|
||||
parent=sortspritelist[i];
|
||||
}
|
||||
return sortspritelist[0];
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
SortNode * HWDrawList::DoSort(HWDrawInfo *di, SortNode * head)
|
||||
{
|
||||
SortNode * node, * sn, * next;
|
||||
|
||||
sn=FindSortPlane(head);
|
||||
if (sn)
|
||||
{
|
||||
if (sn==head) head=head->next;
|
||||
sn->UnlinkFromChain();
|
||||
node=head;
|
||||
head=sn;
|
||||
while (node)
|
||||
{
|
||||
next=node->next;
|
||||
switch(drawitems[node->itemindex].rendertype)
|
||||
{
|
||||
case GLDIT_FLAT:
|
||||
SortPlaneIntoPlane(head,node);
|
||||
break;
|
||||
|
||||
case GLDIT_WALL:
|
||||
SortWallIntoPlane(head,node);
|
||||
break;
|
||||
|
||||
case GLDIT_SPRITE:
|
||||
SortSpriteIntoPlane(head,node);
|
||||
break;
|
||||
}
|
||||
node=next;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sn=FindSortWall(head);
|
||||
if (sn)
|
||||
{
|
||||
if (sn==head) head=head->next;
|
||||
sn->UnlinkFromChain();
|
||||
node=head;
|
||||
head=sn;
|
||||
while (node)
|
||||
{
|
||||
next=node->next;
|
||||
switch(drawitems[node->itemindex].rendertype)
|
||||
{
|
||||
case GLDIT_WALL:
|
||||
SortWallIntoWall(di, head,node);
|
||||
break;
|
||||
|
||||
case GLDIT_SPRITE:
|
||||
SortSpriteIntoWall(di, head, node);
|
||||
break;
|
||||
|
||||
case GLDIT_FLAT: break;
|
||||
}
|
||||
node=next;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return SortSpriteList(head);
|
||||
}
|
||||
}
|
||||
if (head->left) head->left=DoSort(di, head->left);
|
||||
if (head->right) head->right=DoSort(di, head->right);
|
||||
return sn;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
void HWDrawList::Sort(HWDrawInfo *di)
|
||||
{
|
||||
reverseSort = !!(di->Level->i_compatflags & COMPATF_SPRITESORT);
|
||||
SortZ = di->Viewpoint.Pos.Z;
|
||||
MakeSortList();
|
||||
sorted = DoSort(di, SortNodes[SortNodeStart]);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Sorting the drawitems first by texture and then by light level
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void HWDrawList::SortWalls()
|
||||
{
|
||||
if (drawitems.Size() > 1)
|
||||
{
|
||||
std::sort(drawitems.begin(), drawitems.end(), [=](const GLDrawItem &a, const GLDrawItem &b) -> bool
|
||||
{
|
||||
GLWall * w1 = walls[a.index];
|
||||
GLWall * w2 = walls[b.index];
|
||||
|
||||
if (w1->gltexture != w2->gltexture) return w1->gltexture < w2->gltexture;
|
||||
return (w1->flags & 3) < (w2->flags & 3);
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void HWDrawList::SortFlats()
|
||||
{
|
||||
if (drawitems.Size() > 1)
|
||||
{
|
||||
std::sort(drawitems.begin(), drawitems.end(), [=](const GLDrawItem &a, const GLDrawItem &b)
|
||||
{
|
||||
GLFlat * w1 = flats[a.index];
|
||||
GLFlat* w2 = flats[b.index];
|
||||
return w1->gltexture < w2->gltexture;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
GLWall *HWDrawList::NewWall()
|
||||
{
|
||||
auto wall = (GLWall*)RenderDataAllocator.Alloc(sizeof(GLWall));
|
||||
drawitems.Push(GLDrawItem(GLDIT_WALL, walls.Push(wall)));
|
||||
return wall;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
GLFlat *HWDrawList::NewFlat()
|
||||
{
|
||||
auto flat = (GLFlat*)RenderDataAllocator.Alloc(sizeof(GLFlat));
|
||||
drawitems.Push(GLDrawItem(GLDIT_FLAT,flats.Push(flat)));
|
||||
return flat;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
GLSprite *HWDrawList::NewSprite()
|
||||
{
|
||||
auto sprite = (GLSprite*)RenderDataAllocator.Alloc(sizeof(GLSprite));
|
||||
drawitems.Push(GLDrawItem(GLDIT_SPRITE, sprites.Push(sprite)));
|
||||
return sprite;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
void HWDrawList::DoDraw(HWDrawInfo *di, FRenderState &state, bool translucent, int i)
|
||||
{
|
||||
switch(drawitems[i].rendertype)
|
||||
{
|
||||
case GLDIT_FLAT:
|
||||
{
|
||||
GLFlat * f= flats[drawitems[i].index];
|
||||
RenderFlat.Clock();
|
||||
f->DrawFlat(di, state, translucent);
|
||||
RenderFlat.Unclock();
|
||||
}
|
||||
break;
|
||||
|
||||
case GLDIT_WALL:
|
||||
{
|
||||
GLWall * w= walls[drawitems[i].index];
|
||||
RenderWall.Clock();
|
||||
w->DrawWall(di, state, translucent);
|
||||
RenderWall.Unclock();
|
||||
}
|
||||
break;
|
||||
|
||||
case GLDIT_SPRITE:
|
||||
{
|
||||
GLSprite * s= sprites[drawitems[i].index];
|
||||
RenderSprite.Clock();
|
||||
s->DrawSprite(di, state, translucent);
|
||||
RenderSprite.Unclock();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
void HWDrawList::Draw(HWDrawInfo *di, FRenderState &state, bool translucent)
|
||||
{
|
||||
for (unsigned i = 0; i < drawitems.Size(); i++)
|
||||
{
|
||||
DoDraw(di, state, translucent, i);
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
void HWDrawList::DrawWalls(HWDrawInfo *di, FRenderState &state, bool translucent)
|
||||
{
|
||||
RenderWall.Clock();
|
||||
for (auto &item : drawitems)
|
||||
{
|
||||
walls[item.index]->DrawWall(di, state, translucent);
|
||||
}
|
||||
RenderWall.Unclock();
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
void HWDrawList::DrawFlats(HWDrawInfo *di, FRenderState &state, bool translucent)
|
||||
{
|
||||
RenderFlat.Clock();
|
||||
for (unsigned i = 0; i<drawitems.Size(); i++)
|
||||
{
|
||||
flats[drawitems[i].index]->DrawFlat(di, state, translucent);
|
||||
}
|
||||
RenderFlat.Unclock();
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
void HWDrawList::DrawSorted(HWDrawInfo *di, FRenderState &state, SortNode * head)
|
||||
{
|
||||
float clipsplit[2];
|
||||
int relation = 0;
|
||||
float z = 0.f;
|
||||
|
||||
state.GetClipSplit(clipsplit);
|
||||
|
||||
if (drawitems[head->itemindex].rendertype == GLDIT_FLAT)
|
||||
{
|
||||
z = flats[drawitems[head->itemindex].index]->z;
|
||||
relation = z > di->Viewpoint.Pos.Z ? 1 : -1;
|
||||
}
|
||||
|
||||
|
||||
// left is further away, i.e. for stuff above viewz its z coordinate higher, for stuff below viewz its z coordinate is lower
|
||||
if (head->left)
|
||||
{
|
||||
if (relation == -1)
|
||||
{
|
||||
state.SetClipSplit(clipsplit[0], z); // render below: set flat as top clip plane
|
||||
}
|
||||
else if (relation == 1)
|
||||
{
|
||||
state.SetClipSplit(z, clipsplit[1]); // render above: set flat as bottom clip plane
|
||||
}
|
||||
DrawSorted(di, state, head->left);
|
||||
state.SetClipSplit(clipsplit);
|
||||
}
|
||||
DoDraw(di, state, true, head->itemindex);
|
||||
if (head->equal)
|
||||
{
|
||||
SortNode * ehead = head->equal;
|
||||
while (ehead)
|
||||
{
|
||||
DoDraw(di, state, true, ehead->itemindex);
|
||||
ehead = ehead->equal;
|
||||
}
|
||||
}
|
||||
// right is closer, i.e. for stuff above viewz its z coordinate is lower, for stuff below viewz its z coordinate is higher
|
||||
if (head->right)
|
||||
{
|
||||
if (relation == 1)
|
||||
{
|
||||
state.SetClipSplit(clipsplit[0], z); // render below: set flat as top clip plane
|
||||
}
|
||||
else if (relation == -1)
|
||||
{
|
||||
state.SetClipSplit(z, clipsplit[1]); // render above: set flat as bottom clip plane
|
||||
}
|
||||
DrawSorted(di, state, head->right);
|
||||
state.SetClipSplit(clipsplit);
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
void HWDrawList::DrawSorted(HWDrawInfo *di, FRenderState &state)
|
||||
{
|
||||
if (drawitems.Size() == 0) return;
|
||||
|
||||
if (!sorted)
|
||||
{
|
||||
screen->mVertexData->Map();
|
||||
Sort(di);
|
||||
screen->mVertexData->Unmap();
|
||||
}
|
||||
state.ClearClipSplit();
|
||||
state.EnableClipDistance(1, true);
|
||||
state.EnableClipDistance(2, true);
|
||||
DrawSorted(di, state, sorted);
|
||||
state.EnableClipDistance(1, false);
|
||||
state.EnableClipDistance(2, false);
|
||||
state.ClearClipSplit();
|
||||
}
|
||||
|
122
src/rendering/hwrenderer/scene/hw_drawlist.h
Normal file
122
src/rendering/hwrenderer/scene/hw_drawlist.h
Normal file
|
@ -0,0 +1,122 @@
|
|||
#pragma once
|
||||
|
||||
#include "memarena.h"
|
||||
|
||||
extern FMemArena RenderDataAllocator;
|
||||
void ResetRenderDataAllocator();
|
||||
struct HWDrawInfo;
|
||||
class GLWall;
|
||||
class GLFlat;
|
||||
class GLSprite;
|
||||
class FRenderState;
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Intermediate struct to link one draw item into a draw list
|
||||
//
|
||||
// unfortunately this struct must not contain pointers because
|
||||
// the arrays may be reallocated!
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
enum GLDrawItemType
|
||||
{
|
||||
GLDIT_WALL,
|
||||
GLDIT_FLAT,
|
||||
GLDIT_SPRITE,
|
||||
};
|
||||
|
||||
struct GLDrawItem
|
||||
{
|
||||
GLDrawItemType rendertype;
|
||||
int index;
|
||||
|
||||
GLDrawItem(GLDrawItemType _rendertype,int _index) : rendertype(_rendertype),index(_index) {}
|
||||
};
|
||||
|
||||
struct SortNode
|
||||
{
|
||||
int itemindex;
|
||||
SortNode * parent;
|
||||
SortNode * next; // unsorted successor
|
||||
SortNode * left; // left side of this node
|
||||
SortNode * equal; // equal to this node
|
||||
SortNode * right; // right side of this node
|
||||
|
||||
|
||||
void UnlinkFromChain();
|
||||
void Link(SortNode * hook);
|
||||
void AddToEqual(SortNode * newnode);
|
||||
void AddToLeft (SortNode * newnode);
|
||||
void AddToRight(SortNode * newnode);
|
||||
};
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// One draw list. This contains all info for one type of rendering data
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
struct HWDrawList
|
||||
{
|
||||
//private:
|
||||
TArray<GLWall*> walls;
|
||||
TArray<GLFlat*> flats;
|
||||
TArray<GLSprite*> sprites;
|
||||
TArray<GLDrawItem> drawitems;
|
||||
int SortNodeStart;
|
||||
float SortZ;
|
||||
SortNode * sorted;
|
||||
bool reverseSort;
|
||||
|
||||
public:
|
||||
HWDrawList()
|
||||
{
|
||||
next=NULL;
|
||||
SortNodeStart=-1;
|
||||
sorted=NULL;
|
||||
}
|
||||
|
||||
~HWDrawList()
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
unsigned int Size()
|
||||
{
|
||||
return drawitems.Size();
|
||||
}
|
||||
|
||||
GLWall *NewWall();
|
||||
GLFlat *NewFlat();
|
||||
GLSprite *NewSprite();
|
||||
void Reset();
|
||||
void SortWalls();
|
||||
void SortFlats();
|
||||
|
||||
|
||||
void MakeSortList();
|
||||
SortNode * FindSortPlane(SortNode * head);
|
||||
SortNode * FindSortWall(SortNode * head);
|
||||
void SortPlaneIntoPlane(SortNode * head,SortNode * sort);
|
||||
void SortWallIntoPlane(SortNode * head,SortNode * sort);
|
||||
void SortSpriteIntoPlane(SortNode * head,SortNode * sort);
|
||||
void SortWallIntoWall(HWDrawInfo *di, SortNode * head,SortNode * sort);
|
||||
void SortSpriteIntoWall(HWDrawInfo *di, SortNode * head,SortNode * sort);
|
||||
int CompareSprites(SortNode * a,SortNode * b);
|
||||
SortNode * SortSpriteList(SortNode * head);
|
||||
SortNode * DoSort(HWDrawInfo *di, SortNode * head);
|
||||
void Sort(HWDrawInfo *di);
|
||||
|
||||
void DoDraw(HWDrawInfo *di, FRenderState &state, bool translucent, int i);
|
||||
void Draw(HWDrawInfo *di, FRenderState &state, bool translucent);
|
||||
void DrawWalls(HWDrawInfo *di, FRenderState &state, bool translucent);
|
||||
void DrawFlats(HWDrawInfo *di, FRenderState &state, bool translucent);
|
||||
|
||||
void DrawSorted(HWDrawInfo *di, FRenderState &state, SortNode * head);
|
||||
void DrawSorted(HWDrawInfo *di, FRenderState &state);
|
||||
|
||||
HWDrawList * next;
|
||||
} ;
|
||||
|
||||
|
150
src/rendering/hwrenderer/scene/hw_drawlistadd.cpp
Normal file
150
src/rendering/hwrenderer/scene/hw_drawlistadd.cpp
Normal file
|
@ -0,0 +1,150 @@
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Copyright(C) 2000-2016 Christoph Oelckers
|
||||
// All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with this program. If not, see http://www.gnu.org/licenses/
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
|
||||
#include "hwrenderer/dynlights/hw_dynlightdata.h"
|
||||
#include "hwrenderer/utility/hw_cvars.h"
|
||||
#include "hwrenderer/dynlights/hw_lightbuffer.h"
|
||||
#include "hwrenderer/scene/hw_drawstructs.h"
|
||||
#include "hwrenderer/scene/hw_drawinfo.h"
|
||||
#include "hwrenderer/textures/hw_material.h"
|
||||
|
||||
EXTERN_CVAR(Bool, gl_seamless)
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void HWDrawInfo::AddWall(GLWall *wall)
|
||||
{
|
||||
if (wall->flags & GLWall::GLWF_TRANSLUCENT)
|
||||
{
|
||||
auto newwall = drawlists[GLDL_TRANSLUCENT].NewWall();
|
||||
*newwall = *wall;
|
||||
}
|
||||
else
|
||||
{
|
||||
bool masked = GLWall::passflag[wall->type] == 1 ? false : (wall->gltexture && wall->gltexture->isMasked());
|
||||
int list;
|
||||
|
||||
if ((wall->flags & GLWall::GLWF_SKYHACK && wall->type == RENDERWALL_M2S))
|
||||
{
|
||||
list = GLDL_MASKEDWALLSOFS;
|
||||
}
|
||||
else
|
||||
{
|
||||
list = masked ? GLDL_MASKEDWALLS : GLDL_PLAINWALLS;
|
||||
}
|
||||
auto newwall = drawlists[list].NewWall();
|
||||
*newwall = *wall;
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void HWDrawInfo::AddMirrorSurface(GLWall *w)
|
||||
{
|
||||
w->type = RENDERWALL_MIRRORSURFACE;
|
||||
auto newwall = drawlists[GLDL_TRANSLUCENTBORDER].NewWall();
|
||||
*newwall = *w;
|
||||
|
||||
// Invalidate vertices to allow setting of texture coordinates
|
||||
newwall->vertcount = 0;
|
||||
|
||||
FVector3 v = newwall->glseg.Normal();
|
||||
auto tcs = newwall->tcs;
|
||||
tcs[GLWall::LOLFT].u = tcs[GLWall::LORGT].u = tcs[GLWall::UPLFT].u = tcs[GLWall::UPRGT].u = v.X;
|
||||
tcs[GLWall::LOLFT].v = tcs[GLWall::LORGT].v = tcs[GLWall::UPLFT].v = tcs[GLWall::UPRGT].v = v.Z;
|
||||
newwall->MakeVertices(this, false);
|
||||
newwall->ProcessDecals(this);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// FDrawInfo::AddFlat
|
||||
//
|
||||
// Checks texture, lighting and translucency settings and puts this
|
||||
// plane in the appropriate render list.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void HWDrawInfo::AddFlat(GLFlat *flat, bool fog)
|
||||
{
|
||||
int list;
|
||||
|
||||
if (flat->renderstyle != STYLE_Translucent || flat->alpha < 1.f - FLT_EPSILON || fog || flat->gltexture == nullptr)
|
||||
{
|
||||
// translucent 3D floors go into the regular translucent list, translucent portals go into the translucent border list.
|
||||
list = (flat->renderflags&SSRF_RENDER3DPLANES) ? GLDL_TRANSLUCENT : GLDL_TRANSLUCENTBORDER;
|
||||
}
|
||||
else if (flat->gltexture->tex->GetTranslucency())
|
||||
{
|
||||
if (flat->stack)
|
||||
{
|
||||
list = GLDL_TRANSLUCENTBORDER;
|
||||
}
|
||||
else if ((flat->renderflags&SSRF_RENDER3DPLANES) && !flat->plane.plane.isSlope())
|
||||
{
|
||||
list = GLDL_TRANSLUCENT;
|
||||
}
|
||||
else
|
||||
{
|
||||
list = GLDL_PLAINFLATS;
|
||||
}
|
||||
}
|
||||
else //if (flat->hacktype != SSRF_FLOODHACK) // The flood hack may later need different treatment but with the current setup can go into the existing render list.
|
||||
{
|
||||
bool masked = flat->gltexture->isMasked() && ((flat->renderflags&SSRF_RENDER3DPLANES) || flat->stack);
|
||||
list = masked ? GLDL_MASKEDFLATS : GLDL_PLAINFLATS;
|
||||
}
|
||||
auto newflat = drawlists[list].NewFlat();
|
||||
*newflat = *flat;
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
void HWDrawInfo::AddSprite(GLSprite *sprite, bool translucent)
|
||||
{
|
||||
int list;
|
||||
// [BB] Allow models to be drawn in the GLDL_TRANSLUCENT pass.
|
||||
if (translucent || sprite->actor == nullptr || (!sprite->modelframe && (sprite->actor->renderflags & RF_SPRITETYPEMASK) != RF_WALLSPRITE))
|
||||
{
|
||||
list = GLDL_TRANSLUCENT;
|
||||
}
|
||||
else
|
||||
{
|
||||
list = GLDL_MODELS;
|
||||
}
|
||||
|
||||
auto newsprt = drawlists[list].NewSprite();
|
||||
*newsprt = *sprite;
|
||||
}
|
||||
|
431
src/rendering/hwrenderer/scene/hw_drawstructs.h
Normal file
431
src/rendering/hwrenderer/scene/hw_drawstructs.h
Normal file
|
@ -0,0 +1,431 @@
|
|||
#pragma once
|
||||
//==========================================================================
|
||||
//
|
||||
// One wall segment in the draw list
|
||||
//
|
||||
//==========================================================================
|
||||
#include "r_defs.h"
|
||||
#include "r_data/renderstyle.h"
|
||||
#include "textures/textures.h"
|
||||
#include "r_data/colormaps.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable:4244)
|
||||
#endif
|
||||
|
||||
struct GLHorizonInfo;
|
||||
struct GLSkyInfo;
|
||||
struct F3DFloor;
|
||||
class FMaterial;
|
||||
struct FTexCoordInfo;
|
||||
struct FSectorPortalGroup;
|
||||
struct FFlatVertex;
|
||||
struct FLinePortalSpan;
|
||||
struct FDynLightData;
|
||||
class VSMatrix;
|
||||
struct FSpriteModelFrame;
|
||||
struct particle_t;
|
||||
class FRenderState;
|
||||
struct GLDecal;
|
||||
struct FSection;
|
||||
enum area_t : int;
|
||||
|
||||
enum HWRenderStyle
|
||||
{
|
||||
STYLEHW_Normal, // default
|
||||
STYLEHW_Solid, // drawn solid (needs special treatment for sprites)
|
||||
STYLEHW_NoAlphaTest, // disable alpha test
|
||||
};
|
||||
|
||||
enum WallTypes
|
||||
{
|
||||
RENDERWALL_NONE,
|
||||
RENDERWALL_TOP,
|
||||
RENDERWALL_M1S,
|
||||
RENDERWALL_M2S,
|
||||
RENDERWALL_BOTTOM,
|
||||
RENDERWALL_FOGBOUNDARY,
|
||||
RENDERWALL_MIRRORSURFACE,
|
||||
RENDERWALL_M2SNF,
|
||||
RENDERWALL_COLOR,
|
||||
RENDERWALL_FFBLOCK,
|
||||
// Insert new types at the end!
|
||||
};
|
||||
|
||||
enum PortalTypes
|
||||
{
|
||||
PORTALTYPE_SKY,
|
||||
PORTALTYPE_HORIZON,
|
||||
PORTALTYPE_SKYBOX,
|
||||
PORTALTYPE_SECTORSTACK,
|
||||
PORTALTYPE_PLANEMIRROR,
|
||||
PORTALTYPE_MIRROR,
|
||||
PORTALTYPE_LINETOLINE,
|
||||
};
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// One sector plane, still in fixed point
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
struct GLSectorPlane
|
||||
{
|
||||
FTextureID texture;
|
||||
secplane_t plane;
|
||||
float Texheight;
|
||||
float Angle;
|
||||
FVector2 Offs;
|
||||
FVector2 Scale;
|
||||
|
||||
void GetFromSector(sector_t * sec, int ceiling)
|
||||
{
|
||||
Offs.X = (float)sec->GetXOffset(ceiling);
|
||||
Offs.Y = (float)sec->GetYOffset(ceiling);
|
||||
Scale.X = (float)sec->GetXScale(ceiling);
|
||||
Scale.Y = (float)sec->GetYScale(ceiling);
|
||||
Angle = (float)sec->GetAngle(ceiling).Degrees;
|
||||
texture = sec->GetTexture(ceiling);
|
||||
plane = sec->GetSecPlane(ceiling);
|
||||
Texheight = (float)((ceiling == sector_t::ceiling)? plane.fD() : -plane.fD());
|
||||
}
|
||||
};
|
||||
|
||||
struct GLSeg
|
||||
{
|
||||
float x1,x2;
|
||||
float y1,y2;
|
||||
float fracleft, fracright; // fractional offset of the 2 vertices on the linedef
|
||||
|
||||
FVector3 Normal() const
|
||||
{
|
||||
// we do not use the vector math inlines here because they are not optimized for speed but accuracy in the playsim and this is called quite frequently.
|
||||
float x = y2 - y1;
|
||||
float y = x1 - x2;
|
||||
float ilength = 1.f / sqrtf(x*x + y*y);
|
||||
return FVector3(x * ilength, 0, y * ilength);
|
||||
}
|
||||
};
|
||||
|
||||
struct texcoord
|
||||
{
|
||||
float u,v;
|
||||
};
|
||||
|
||||
struct HWDrawInfo;
|
||||
|
||||
class GLWall
|
||||
{
|
||||
public:
|
||||
static const char passflag[];
|
||||
|
||||
enum
|
||||
{
|
||||
GLWF_CLAMPX=1,
|
||||
GLWF_CLAMPY=2,
|
||||
GLWF_SKYHACK=4,
|
||||
GLWF_GLOW=8, // illuminated by glowing flats
|
||||
GLWF_NOSPLITUPPER=16,
|
||||
GLWF_NOSPLITLOWER=32,
|
||||
GLWF_NOSPLIT=64,
|
||||
GLWF_TRANSLUCENT = 128
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
RWF_BLANK = 0,
|
||||
RWF_TEXTURED = 1, // actually not being used anymore because with buffers it's even less efficient not writing the texture coordinates - but leave it here
|
||||
RWF_NOSPLIT = 4,
|
||||
RWF_NORENDER = 8,
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
LOLFT,
|
||||
UPLFT,
|
||||
UPRGT,
|
||||
LORGT,
|
||||
};
|
||||
|
||||
friend struct HWDrawList;
|
||||
friend class HWPortal;
|
||||
|
||||
vertex_t * vertexes[2]; // required for polygon splitting
|
||||
FMaterial *gltexture;
|
||||
TArray<lightlist_t> *lightlist;
|
||||
|
||||
GLSeg glseg;
|
||||
float ztop[2],zbottom[2];
|
||||
texcoord tcs[4];
|
||||
float alpha;
|
||||
|
||||
FColormap Colormap;
|
||||
ERenderStyle RenderStyle;
|
||||
|
||||
float ViewDistance;
|
||||
|
||||
int lightlevel;
|
||||
uint8_t type;
|
||||
uint8_t flags;
|
||||
short rellight;
|
||||
|
||||
float topglowcolor[4];
|
||||
float bottomglowcolor[4];
|
||||
|
||||
int dynlightindex;
|
||||
|
||||
union
|
||||
{
|
||||
// it's either one of them but never more!
|
||||
FSectorPortal *secportal; // sector portal (formerly skybox)
|
||||
GLSkyInfo * sky; // for normal sky
|
||||
GLHorizonInfo * horizon; // for horizon information
|
||||
FSectorPortalGroup * portal; // stacked sector portals
|
||||
secplane_t * planemirror; // for plane mirrors
|
||||
FLinePortalSpan *lineportal; // line-to-line portals
|
||||
};
|
||||
|
||||
|
||||
|
||||
// these are not the same as ytop and ybottom!!!
|
||||
float zceil[2];
|
||||
float zfloor[2];
|
||||
|
||||
unsigned int vertindex;
|
||||
unsigned int vertcount;
|
||||
|
||||
public:
|
||||
seg_t * seg; // this gives the easiest access to all other structs involved
|
||||
subsector_t * sub; // For polyobjects
|
||||
sector_t *frontsector, *backsector;
|
||||
//private:
|
||||
|
||||
void PutWall(HWDrawInfo *di, bool translucent);
|
||||
void PutPortal(HWDrawInfo *di, int ptype, int plane);
|
||||
void CheckTexturePosition(FTexCoordInfo *tci);
|
||||
|
||||
void Put3DWall(HWDrawInfo *di, lightlist_t * lightlist, bool translucent);
|
||||
bool SplitWallComplex(HWDrawInfo *di, sector_t * frontsector, bool translucent, float& maplightbottomleft, float& maplightbottomright);
|
||||
void SplitWall(HWDrawInfo *di, sector_t * frontsector, bool translucent);
|
||||
|
||||
void SetupLights(HWDrawInfo *di, FDynLightData &lightdata);
|
||||
|
||||
void MakeVertices(HWDrawInfo *di, bool nosplit);
|
||||
|
||||
void SkyPlane(HWDrawInfo *di, sector_t *sector, int plane, bool allowmirror);
|
||||
void SkyLine(HWDrawInfo *di, sector_t *sec, line_t *line);
|
||||
void SkyNormal(HWDrawInfo *di, sector_t * fs,vertex_t * v1,vertex_t * v2);
|
||||
void SkyTop(HWDrawInfo *di, seg_t * seg,sector_t * fs,sector_t * bs,vertex_t * v1,vertex_t * v2);
|
||||
void SkyBottom(HWDrawInfo *di, seg_t * seg,sector_t * fs,sector_t * bs,vertex_t * v1,vertex_t * v2);
|
||||
|
||||
bool DoHorizon(HWDrawInfo *di, seg_t * seg,sector_t * fs, vertex_t * v1,vertex_t * v2);
|
||||
|
||||
bool SetWallCoordinates(seg_t * seg, FTexCoordInfo *tci, float ceilingrefheight,
|
||||
float topleft, float topright, float bottomleft, float bottomright, float t_ofs);
|
||||
|
||||
void DoTexture(HWDrawInfo *di, int type,seg_t * seg,int peg,
|
||||
float ceilingrefheight, float floorrefheight,
|
||||
float CeilingHeightstart,float CeilingHeightend,
|
||||
float FloorHeightstart,float FloorHeightend,
|
||||
float v_offset);
|
||||
|
||||
void DoMidTexture(HWDrawInfo *di, seg_t * seg, bool drawfogboundary,
|
||||
sector_t * front, sector_t * back,
|
||||
sector_t * realfront, sector_t * realback,
|
||||
float fch1, float fch2, float ffh1, float ffh2,
|
||||
float bch1, float bch2, float bfh1, float bfh2);
|
||||
|
||||
void GetPlanePos(F3DFloor::planeref * planeref, float & left, float & right);
|
||||
|
||||
void BuildFFBlock(HWDrawInfo *di, seg_t * seg, F3DFloor * rover,
|
||||
float ff_topleft, float ff_topright,
|
||||
float ff_bottomleft, float ff_bottomright);
|
||||
void InverseFloors(HWDrawInfo *di, seg_t * seg, sector_t * frontsector,
|
||||
float topleft, float topright,
|
||||
float bottomleft, float bottomright);
|
||||
void ClipFFloors(HWDrawInfo *di, seg_t * seg, F3DFloor * ffloor, sector_t * frontsector,
|
||||
float topleft, float topright,
|
||||
float bottomleft, float bottomright);
|
||||
void DoFFloorBlocks(HWDrawInfo *di, seg_t * seg, sector_t * frontsector, sector_t * backsector,
|
||||
float fch1, float fch2, float ffh1, float ffh2,
|
||||
float bch1, float bch2, float bfh1, float bfh2);
|
||||
|
||||
void ProcessDecal(HWDrawInfo *di, DBaseDecal *decal, const FVector3 &normal);
|
||||
void ProcessDecals(HWDrawInfo *di);
|
||||
|
||||
int CreateVertices(FFlatVertex *&ptr, bool nosplit);
|
||||
void SplitLeftEdge (FFlatVertex *&ptr);
|
||||
void SplitRightEdge(FFlatVertex *&ptr);
|
||||
void SplitUpperEdge(FFlatVertex *&ptr);
|
||||
void SplitLowerEdge(FFlatVertex *&ptr);
|
||||
|
||||
void CountLeftEdge (unsigned &ptr);
|
||||
void CountRightEdge(unsigned &ptr);
|
||||
|
||||
int CountVertices();
|
||||
|
||||
void RenderWall(HWDrawInfo *di, FRenderState &state, int textured);
|
||||
void RenderFogBoundary(HWDrawInfo *di, FRenderState &state);
|
||||
void RenderMirrorSurface(HWDrawInfo *di, FRenderState &state);
|
||||
void RenderTexturedWall(HWDrawInfo *di, FRenderState &state, int rflags);
|
||||
void RenderTranslucentWall(HWDrawInfo *di, FRenderState &state);
|
||||
void DrawDecalsForMirror(HWDrawInfo *di, FRenderState &state, TArray<GLDecal *> &decals);
|
||||
|
||||
public:
|
||||
void Process(HWDrawInfo *di, seg_t *seg, sector_t *frontsector, sector_t *backsector);
|
||||
void ProcessLowerMiniseg(HWDrawInfo *di, seg_t *seg, sector_t *frontsector, sector_t *backsector);
|
||||
|
||||
float PointOnSide(float x,float y)
|
||||
{
|
||||
return -((y-glseg.y1)*(glseg.x2-glseg.x1)-(x-glseg.x1)*(glseg.y2-glseg.y1));
|
||||
}
|
||||
|
||||
void DrawWall(HWDrawInfo *di, FRenderState &state, bool translucent);
|
||||
|
||||
};
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// One flat plane in the draw list
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
class GLFlat
|
||||
{
|
||||
public:
|
||||
sector_t * sector;
|
||||
FSection *section;
|
||||
float z; // the z position of the flat (only valid for non-sloped planes)
|
||||
FMaterial *gltexture;
|
||||
|
||||
FColormap Colormap; // light and fog
|
||||
PalEntry FlatColor;
|
||||
PalEntry AddColor;
|
||||
ERenderStyle renderstyle;
|
||||
|
||||
float alpha;
|
||||
GLSectorPlane plane;
|
||||
int lightlevel;
|
||||
bool stack;
|
||||
bool ceiling;
|
||||
uint8_t renderflags;
|
||||
uint8_t hacktype;
|
||||
int iboindex;
|
||||
//int vboheight;
|
||||
|
||||
int dynlightindex;
|
||||
|
||||
void CreateSkyboxVertices(FFlatVertex *buffer);
|
||||
void SetupLights(HWDrawInfo *di, FLightNode *head, FDynLightData &lightdata, int portalgroup);
|
||||
|
||||
void PutFlat(HWDrawInfo *di, bool fog = false);
|
||||
void Process(HWDrawInfo *di, sector_t * model, int whichplane, bool notexture);
|
||||
void SetFrom3DFloor(F3DFloor *rover, bool top, bool underside);
|
||||
void ProcessSector(HWDrawInfo *di, sector_t * frontsector, int which = 7 /*SSRF_RENDERALL*/); // cannot use constant due to circular dependencies.
|
||||
|
||||
void DrawSubsectors(HWDrawInfo *di, FRenderState &state);
|
||||
void DrawFlat(HWDrawInfo *di, FRenderState &state, bool translucent);
|
||||
|
||||
void DrawOtherPlanes(HWDrawInfo *di, FRenderState &state);
|
||||
void DrawFloodPlanes(HWDrawInfo *di, FRenderState &state);
|
||||
};
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// One sprite in the draw list
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
|
||||
class GLSprite
|
||||
{
|
||||
public:
|
||||
int lightlevel;
|
||||
uint8_t foglevel;
|
||||
uint8_t hw_styleflags;
|
||||
bool fullbright;
|
||||
bool polyoffset;
|
||||
PalEntry ThingColor; // thing's own color
|
||||
FColormap Colormap;
|
||||
FSpriteModelFrame * modelframe;
|
||||
FRenderStyle RenderStyle;
|
||||
int OverrideShader;
|
||||
|
||||
int translation;
|
||||
int index;
|
||||
int depth;
|
||||
int vertexindex;
|
||||
|
||||
float topclip;
|
||||
float bottomclip;
|
||||
|
||||
float x,y,z; // needed for sorting!
|
||||
|
||||
float ul,ur;
|
||||
float vt,vb;
|
||||
float x1,y1,z1;
|
||||
float x2,y2,z2;
|
||||
|
||||
FMaterial *gltexture;
|
||||
float trans;
|
||||
AActor * actor;
|
||||
particle_t * particle;
|
||||
TArray<lightlist_t> *lightlist;
|
||||
DRotator Angles;
|
||||
|
||||
int dynlightindex;
|
||||
|
||||
void SplitSprite(HWDrawInfo *di, sector_t * frontsector, bool translucent);
|
||||
void PerformSpriteClipAdjustment(AActor *thing, const DVector2 &thingpos, float spriteheight);
|
||||
bool CalculateVertices(HWDrawInfo *di, FVector3 *v, DVector3 *vp);
|
||||
|
||||
public:
|
||||
|
||||
void CreateVertices(HWDrawInfo *di);
|
||||
void PutSprite(HWDrawInfo *di, bool translucent);
|
||||
void Process(HWDrawInfo *di, AActor* thing,sector_t * sector, area_t in_area, int thruportal = false);
|
||||
void ProcessParticle (HWDrawInfo *di, particle_t *particle, sector_t *sector);//, int shade, int fakeside)
|
||||
|
||||
void DrawSprite(HWDrawInfo *di, FRenderState &state, bool translucent);
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
struct DecalVertex
|
||||
{
|
||||
float x, y, z;
|
||||
float u, v;
|
||||
};
|
||||
|
||||
struct GLDecal
|
||||
{
|
||||
FMaterial *gltexture;
|
||||
TArray<lightlist_t> *lightlist;
|
||||
DBaseDecal *decal;
|
||||
DecalVertex dv[4];
|
||||
float zcenter;
|
||||
unsigned int vertindex;
|
||||
|
||||
FRenderStyle renderstyle;
|
||||
int lightlevel;
|
||||
int rellight;
|
||||
float alpha;
|
||||
FColormap Colormap;
|
||||
sector_t *frontsector;
|
||||
FVector3 Normal;
|
||||
|
||||
void DrawDecal(HWDrawInfo *di, FRenderState &state);
|
||||
|
||||
};
|
||||
|
||||
|
||||
inline float Dist2(float x1,float y1,float x2,float y2)
|
||||
{
|
||||
return sqrtf((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
|
||||
}
|
||||
|
||||
bool hw_SetPlaneTextureRotation(const GLSectorPlane * secplane, FMaterial * gltexture, VSMatrix &mat);
|
||||
void hw_GetDynModelLight(AActor *self, FDynLightData &modellightdata);
|
||||
|
||||
extern const float LARGE_VALUE;
|
423
src/rendering/hwrenderer/scene/hw_fakeflat.cpp
Normal file
423
src/rendering/hwrenderer/scene/hw_fakeflat.cpp
Normal file
|
@ -0,0 +1,423 @@
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Copyright(C) 2001-2016 Christoph Oelckers
|
||||
// All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with this program. If not, see http://www.gnu.org/licenses/
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
/*
|
||||
** gl_fakeflat.cpp
|
||||
** Fake flat functions to render stacked sectors
|
||||
**
|
||||
*/
|
||||
|
||||
#include "p_lnspec.h"
|
||||
#include "p_local.h"
|
||||
#include "g_levellocals.h"
|
||||
#include "a_sharedglobal.h"
|
||||
#include "d_player.h"
|
||||
#include "r_sky.h"
|
||||
#include "hw_fakeflat.h"
|
||||
#include "hw_drawinfo.h"
|
||||
#include "hwrenderer/utility/hw_cvars.h"
|
||||
#include "r_utility.h"
|
||||
|
||||
static sector_t **fakesectorbuffer;
|
||||
|
||||
extern thread_local bool isWorkerThread;
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Check whether the player can look beyond this line
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
bool hw_CheckClip(side_t * sidedef, sector_t * frontsector, sector_t * backsector)
|
||||
{
|
||||
line_t *linedef = sidedef->linedef;
|
||||
double bs_floorheight1;
|
||||
double bs_floorheight2;
|
||||
double bs_ceilingheight1;
|
||||
double bs_ceilingheight2;
|
||||
double fs_floorheight1;
|
||||
double fs_floorheight2;
|
||||
double fs_ceilingheight1;
|
||||
double fs_ceilingheight2;
|
||||
|
||||
// Mirrors and horizons always block the view
|
||||
//if (linedef->special==Line_Mirror || linedef->special==Line_Horizon) return true;
|
||||
|
||||
// Lines with portals must never block.
|
||||
// Portals which require the sky flat are excluded here, because for them the special sky semantics apply.
|
||||
if (!(frontsector->GetPortal(sector_t::ceiling)->mFlags & PORTSF_SKYFLATONLY) ||
|
||||
!(frontsector->GetPortal(sector_t::floor)->mFlags & PORTSF_SKYFLATONLY) ||
|
||||
!(backsector->GetPortal(sector_t::ceiling)->mFlags & PORTSF_SKYFLATONLY) ||
|
||||
!(backsector->GetPortal(sector_t::floor)->mFlags & PORTSF_SKYFLATONLY))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// on large levels this distinction can save some time
|
||||
// That's a lot of avoided multiplications if there's a lot to see!
|
||||
|
||||
if (frontsector->ceilingplane.isSlope())
|
||||
{
|
||||
fs_ceilingheight1 = frontsector->ceilingplane.ZatPoint(linedef->v1);
|
||||
fs_ceilingheight2 = frontsector->ceilingplane.ZatPoint(linedef->v2);
|
||||
}
|
||||
else
|
||||
{
|
||||
fs_ceilingheight2 = fs_ceilingheight1 = frontsector->ceilingplane.fD();
|
||||
}
|
||||
|
||||
if (frontsector->floorplane.isSlope())
|
||||
{
|
||||
fs_floorheight1 = frontsector->floorplane.ZatPoint(linedef->v1);
|
||||
fs_floorheight2 = frontsector->floorplane.ZatPoint(linedef->v2);
|
||||
}
|
||||
else
|
||||
{
|
||||
fs_floorheight2 = fs_floorheight1 = -frontsector->floorplane.fD();
|
||||
}
|
||||
|
||||
if (backsector->ceilingplane.isSlope())
|
||||
{
|
||||
bs_ceilingheight1 = backsector->ceilingplane.ZatPoint(linedef->v1);
|
||||
bs_ceilingheight2 = backsector->ceilingplane.ZatPoint(linedef->v2);
|
||||
}
|
||||
else
|
||||
{
|
||||
bs_ceilingheight2 = bs_ceilingheight1 = backsector->ceilingplane.fD();
|
||||
}
|
||||
|
||||
if (backsector->floorplane.isSlope())
|
||||
{
|
||||
bs_floorheight1 = backsector->floorplane.ZatPoint(linedef->v1);
|
||||
bs_floorheight2 = backsector->floorplane.ZatPoint(linedef->v2);
|
||||
}
|
||||
else
|
||||
{
|
||||
bs_floorheight2 = bs_floorheight1 = -backsector->floorplane.fD();
|
||||
}
|
||||
|
||||
// now check for closed sectors!
|
||||
if (bs_ceilingheight1 <= fs_floorheight1 && bs_ceilingheight2 <= fs_floorheight2)
|
||||
{
|
||||
FTexture * tex = TexMan.GetTexture(sidedef->GetTexture(side_t::top), true);
|
||||
if (!tex || !tex->isValid()) return false;
|
||||
if (backsector->GetTexture(sector_t::ceiling) == skyflatnum &&
|
||||
frontsector->GetTexture(sector_t::ceiling) == skyflatnum) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (fs_ceilingheight1 <= bs_floorheight1 && fs_ceilingheight2 <= bs_floorheight2)
|
||||
{
|
||||
FTexture * tex = TexMan.GetTexture(sidedef->GetTexture(side_t::bottom), true);
|
||||
if (!tex || !tex->isValid()) return false;
|
||||
|
||||
// properly render skies (consider door "open" if both floors are sky):
|
||||
if (backsector->GetTexture(sector_t::ceiling) == skyflatnum &&
|
||||
frontsector->GetTexture(sector_t::ceiling) == skyflatnum) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (bs_ceilingheight1 <= bs_floorheight1 && bs_ceilingheight2 <= bs_floorheight2)
|
||||
{
|
||||
// preserve a kind of transparent door/lift special effect:
|
||||
if (bs_ceilingheight1 < fs_ceilingheight1 || bs_ceilingheight2 < fs_ceilingheight2)
|
||||
{
|
||||
FTexture * tex = TexMan.GetTexture(sidedef->GetTexture(side_t::top), true);
|
||||
if (!tex || !tex->isValid()) return false;
|
||||
}
|
||||
if (bs_floorheight1 > fs_floorheight1 || bs_floorheight2 > fs_floorheight2)
|
||||
{
|
||||
FTexture * tex = TexMan.GetTexture(sidedef->GetTexture(side_t::bottom), true);
|
||||
if (!tex || !tex->isValid()) return false;
|
||||
}
|
||||
if (backsector->GetTexture(sector_t::ceiling) == skyflatnum &&
|
||||
frontsector->GetTexture(sector_t::ceiling) == skyflatnum) return false;
|
||||
if (backsector->GetTexture(sector_t::floor) == skyflatnum && frontsector->GetTexture(sector_t::floor)
|
||||
== skyflatnum) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// check for levels with exposed lower areas. May only be called if actual
|
||||
// state is not known yet!
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
area_t hw_CheckViewArea(vertex_t *v1, vertex_t *v2, sector_t *frontsector, sector_t *backsector)
|
||||
{
|
||||
if (backsector->GetHeightSec() && !frontsector->GetHeightSec())
|
||||
{
|
||||
sector_t * s = backsector->heightsec;
|
||||
|
||||
double cz1 = frontsector->ceilingplane.ZatPoint(v1);
|
||||
double cz2 = frontsector->ceilingplane.ZatPoint(v2);
|
||||
double fz1 = s->floorplane.ZatPoint(v1);
|
||||
double fz2 = s->floorplane.ZatPoint(v2);
|
||||
|
||||
// allow some tolerance in case slopes are involved
|
||||
if (cz1 <= fz1 + 1. / 100 && cz2 <= fz2 + 1. / 100)
|
||||
return area_below;
|
||||
else
|
||||
return area_normal;
|
||||
}
|
||||
return area_default;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
static FMemArena FakeSectorAllocator(20 * sizeof(sector_t));
|
||||
|
||||
static sector_t *allocateSector(sector_t *sec)
|
||||
{
|
||||
if (fakesectorbuffer == nullptr)
|
||||
{
|
||||
unsigned numsectors = sec->Level->sectors.Size();
|
||||
fakesectorbuffer = (sector_t**)FakeSectorAllocator.Alloc(numsectors * sizeof(sector_t*));
|
||||
memset(fakesectorbuffer, 0, numsectors * sizeof(sector_t*));
|
||||
}
|
||||
auto sectornum = sec->sectornum;
|
||||
fakesectorbuffer[sectornum] = (sector_t*)FakeSectorAllocator.Alloc(sizeof(sector_t));
|
||||
return fakesectorbuffer[sectornum];
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// This is mostly like R_FakeFlat but with a few alterations necessitated
|
||||
// by hardware rendering
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
sector_t * hw_FakeFlat(sector_t * sec, area_t in_area, bool back, sector_t *localcopy)
|
||||
{
|
||||
if (!sec->GetHeightSec() || sec->heightsec==sec)
|
||||
{
|
||||
// check for backsectors with the ceiling lower than the floor. These will create
|
||||
// visual glitches because upper amd lower textures overlap.
|
||||
if (back && (sec->MoreFlags & SECMF_OVERLAPPING))
|
||||
{
|
||||
if (fakesectorbuffer && fakesectorbuffer[sec->sectornum]) return fakesectorbuffer[sec->sectornum];
|
||||
auto dest = localcopy? localcopy : allocateSector(sec);
|
||||
*dest = *sec;
|
||||
dest->ceilingplane = sec->floorplane;
|
||||
dest->ceilingplane.FlipVert();
|
||||
dest->planes[sector_t::ceiling].TexZ = dest->planes[sector_t::floor].TexZ;
|
||||
dest->ClearPortal(sector_t::ceiling);
|
||||
dest->ClearPortal(sector_t::floor);
|
||||
return dest;
|
||||
}
|
||||
return sec;
|
||||
}
|
||||
|
||||
#ifdef _DEBUG
|
||||
if (sec->sectornum==560)
|
||||
{
|
||||
int a = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (fakesectorbuffer && fakesectorbuffer[sec->sectornum])
|
||||
{
|
||||
return fakesectorbuffer[sec->sectornum];
|
||||
}
|
||||
assert(!(isWorkerThread && localcopy == nullptr));
|
||||
|
||||
if (in_area==area_above)
|
||||
{
|
||||
if (sec->heightsec->MoreFlags&SECMF_FAKEFLOORONLY /*|| sec->GetTexture(sector_t::ceiling)==skyflatnum*/) in_area=area_normal;
|
||||
}
|
||||
|
||||
int diffTex = (sec->heightsec->MoreFlags & SECMF_CLIPFAKEPLANES);
|
||||
sector_t * s = sec->heightsec;
|
||||
|
||||
auto dest = localcopy ? localcopy : allocateSector(sec);
|
||||
*dest = *sec;
|
||||
|
||||
// Replace floor and ceiling height with control sector's heights.
|
||||
if (diffTex)
|
||||
{
|
||||
if (s->floorplane.CopyPlaneIfValid (&dest->floorplane, &sec->ceilingplane))
|
||||
{
|
||||
dest->SetTexture(sector_t::floor, s->GetTexture(sector_t::floor), false);
|
||||
dest->SetPlaneTexZQuick(sector_t::floor, s->GetPlaneTexZ(sector_t::floor));
|
||||
dest->iboindex[sector_t::floor] = sec->iboindex[sector_t::vbo_fakefloor];
|
||||
dest->vboheight[sector_t::floor] = s->vboheight[sector_t::floor];
|
||||
}
|
||||
else if (s->MoreFlags & SECMF_FAKEFLOORONLY)
|
||||
{
|
||||
if (in_area==area_below)
|
||||
{
|
||||
dest->CopyColors(s);
|
||||
if (!(s->MoreFlags & SECMF_NOFAKELIGHT))
|
||||
{
|
||||
dest->lightlevel = s->lightlevel;
|
||||
dest->SetPlaneLight(sector_t::floor, s->GetPlaneLight(sector_t::floor));
|
||||
dest->SetPlaneLight(sector_t::ceiling, s->GetPlaneLight(sector_t::ceiling));
|
||||
dest->ChangeFlags(sector_t::floor, -1, s->GetFlags(sector_t::floor));
|
||||
dest->ChangeFlags(sector_t::ceiling, -1, s->GetFlags(sector_t::ceiling));
|
||||
}
|
||||
return dest;
|
||||
}
|
||||
return sec;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
dest->SetPlaneTexZQuick(sector_t::floor, s->GetPlaneTexZ(sector_t::floor));
|
||||
dest->floorplane = s->floorplane;
|
||||
|
||||
dest->iboindex[sector_t::floor] = sec->iboindex[sector_t::vbo_fakefloor];
|
||||
dest->vboheight[sector_t::floor] = s->vboheight[sector_t::floor];
|
||||
}
|
||||
|
||||
if (!(s->MoreFlags&SECMF_FAKEFLOORONLY))
|
||||
{
|
||||
if (diffTex)
|
||||
{
|
||||
if (s->ceilingplane.CopyPlaneIfValid (&dest->ceilingplane, &sec->floorplane))
|
||||
{
|
||||
dest->SetTexture(sector_t::ceiling, s->GetTexture(sector_t::ceiling), false);
|
||||
dest->SetPlaneTexZQuick(sector_t::ceiling, s->GetPlaneTexZ(sector_t::ceiling));
|
||||
dest->iboindex[sector_t::ceiling] = sec->iboindex[sector_t::vbo_fakeceiling];
|
||||
dest->vboheight[sector_t::ceiling] = s->vboheight[sector_t::ceiling];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
dest->ceilingplane = s->ceilingplane;
|
||||
dest->SetPlaneTexZQuick(sector_t::ceiling, s->GetPlaneTexZ(sector_t::ceiling));
|
||||
dest->iboindex[sector_t::ceiling] = sec->iboindex[sector_t::vbo_fakeceiling];
|
||||
dest->vboheight[sector_t::ceiling] = s->vboheight[sector_t::ceiling];
|
||||
}
|
||||
}
|
||||
|
||||
if (in_area==area_below)
|
||||
{
|
||||
dest->CopyColors(s);
|
||||
dest->SetPlaneTexZQuick(sector_t::floor, sec->GetPlaneTexZ(sector_t::floor));
|
||||
dest->SetPlaneTexZQuick(sector_t::ceiling, s->GetPlaneTexZ(sector_t::floor));
|
||||
dest->floorplane=sec->floorplane;
|
||||
dest->ceilingplane=s->floorplane;
|
||||
dest->ceilingplane.FlipVert();
|
||||
|
||||
dest->iboindex[sector_t::floor] = sec->iboindex[sector_t::floor];
|
||||
dest->vboheight[sector_t::floor] = sec->vboheight[sector_t::floor];
|
||||
|
||||
dest->iboindex[sector_t::ceiling] = sec->iboindex[sector_t::vbo_fakefloor];
|
||||
dest->vboheight[sector_t::ceiling] = s->vboheight[sector_t::floor];
|
||||
|
||||
dest->ClearPortal(sector_t::ceiling);
|
||||
|
||||
if (!(s->MoreFlags & SECMF_NOFAKELIGHT))
|
||||
{
|
||||
dest->lightlevel = s->lightlevel;
|
||||
}
|
||||
|
||||
//if (!back)
|
||||
{
|
||||
dest->SetTexture(sector_t::floor, diffTex ? sec->GetTexture(sector_t::floor) : s->GetTexture(sector_t::floor), false);
|
||||
dest->planes[sector_t::floor].xform = s->planes[sector_t::floor].xform;
|
||||
|
||||
//dest->ceilingplane = s->floorplane;
|
||||
|
||||
if (s->GetTexture(sector_t::ceiling) == skyflatnum)
|
||||
{
|
||||
dest->SetTexture(sector_t::ceiling, dest->GetTexture(sector_t::floor), false);
|
||||
//dest->floorplane = dest->ceilingplane;
|
||||
//dest->floorplane.FlipVert ();
|
||||
//dest->floorplane.ChangeHeight (+1);
|
||||
dest->planes[sector_t::ceiling].xform = dest->planes[sector_t::floor].xform;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
dest->SetTexture(sector_t::ceiling, diffTex ? s->GetTexture(sector_t::floor) : s->GetTexture(sector_t::ceiling), false);
|
||||
dest->planes[sector_t::ceiling].xform = s->planes[sector_t::ceiling].xform;
|
||||
}
|
||||
|
||||
if (!(s->MoreFlags & SECMF_NOFAKELIGHT))
|
||||
{
|
||||
dest->SetPlaneLight(sector_t::floor, s->GetPlaneLight(sector_t::floor));
|
||||
dest->SetPlaneLight(sector_t::ceiling, s->GetPlaneLight(sector_t::ceiling));
|
||||
dest->ChangeFlags(sector_t::floor, -1, s->GetFlags(sector_t::floor));
|
||||
dest->ChangeFlags(sector_t::ceiling, -1, s->GetFlags(sector_t::ceiling));
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (in_area == area_above)
|
||||
{
|
||||
dest->CopyColors(s);
|
||||
dest->SetPlaneTexZQuick(sector_t::ceiling, sec->GetPlaneTexZ(sector_t::ceiling));
|
||||
dest->SetPlaneTexZQuick(sector_t::floor, s->GetPlaneTexZ(sector_t::ceiling));
|
||||
dest->ceilingplane = sec->ceilingplane;
|
||||
dest->floorplane = s->ceilingplane;
|
||||
dest->floorplane.FlipVert();
|
||||
|
||||
dest->iboindex[sector_t::floor] = sec->iboindex[sector_t::vbo_fakeceiling];
|
||||
dest->vboheight[sector_t::floor] = s->vboheight[sector_t::ceiling];
|
||||
|
||||
dest->iboindex[sector_t::ceiling] = sec->iboindex[sector_t::ceiling];
|
||||
dest->vboheight[sector_t::ceiling] = sec->vboheight[sector_t::ceiling];
|
||||
|
||||
dest->ClearPortal(sector_t::floor);
|
||||
|
||||
if (!(s->MoreFlags & SECMF_NOFAKELIGHT))
|
||||
{
|
||||
dest->lightlevel = s->lightlevel;
|
||||
}
|
||||
|
||||
//if (!back)
|
||||
{
|
||||
dest->SetTexture(sector_t::ceiling, diffTex ? sec->GetTexture(sector_t::ceiling) : s->GetTexture(sector_t::ceiling), false);
|
||||
dest->SetTexture(sector_t::floor, s->GetTexture(sector_t::ceiling), false);
|
||||
dest->planes[sector_t::ceiling].xform = dest->planes[sector_t::floor].xform = s->planes[sector_t::ceiling].xform;
|
||||
|
||||
if (s->GetTexture(sector_t::floor) != skyflatnum)
|
||||
{
|
||||
dest->SetTexture(sector_t::floor, s->GetTexture(sector_t::floor), false);
|
||||
dest->planes[sector_t::floor].xform = s->planes[sector_t::floor].xform;
|
||||
}
|
||||
|
||||
if (!(s->MoreFlags & SECMF_NOFAKELIGHT))
|
||||
{
|
||||
dest->lightlevel = s->lightlevel;
|
||||
dest->SetPlaneLight(sector_t::floor, s->GetPlaneLight(sector_t::floor));
|
||||
dest->SetPlaneLight(sector_t::ceiling, s->GetPlaneLight(sector_t::ceiling));
|
||||
dest->ChangeFlags(sector_t::floor, -1, s->GetFlags(sector_t::floor));
|
||||
dest->ChangeFlags(sector_t::ceiling, -1, s->GetFlags(sector_t::ceiling));
|
||||
}
|
||||
}
|
||||
}
|
||||
return dest;
|
||||
}
|
||||
|
||||
|
||||
void hw_ClearFakeFlat()
|
||||
{
|
||||
FakeSectorAllocator.FreeAll();
|
||||
fakesectorbuffer = nullptr;
|
||||
}
|
17
src/rendering/hwrenderer/scene/hw_fakeflat.h
Normal file
17
src/rendering/hwrenderer/scene/hw_fakeflat.h
Normal file
|
@ -0,0 +1,17 @@
|
|||
#pragma once
|
||||
|
||||
enum area_t : int
|
||||
{
|
||||
area_normal,
|
||||
area_below,
|
||||
area_above,
|
||||
area_default
|
||||
};
|
||||
|
||||
|
||||
// Global functions.
|
||||
bool hw_CheckClip(side_t * sidedef, sector_t * frontsector, sector_t * backsector);
|
||||
void hw_ClearFakeFlat();
|
||||
sector_t * hw_FakeFlat(sector_t * sec, area_t in_area, bool back, sector_t *localcopy = nullptr);
|
||||
area_t hw_CheckViewArea(vertex_t *v1, vertex_t *v2, sector_t *frontsector, sector_t *backsector);
|
||||
|
718
src/rendering/hwrenderer/scene/hw_flats.cpp
Normal file
718
src/rendering/hwrenderer/scene/hw_flats.cpp
Normal file
|
@ -0,0 +1,718 @@
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Copyright(C) 2000-2016 Christoph Oelckers
|
||||
// All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with this program. If not, see http://www.gnu.org/licenses/
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
/*
|
||||
** gl_flat.cpp
|
||||
** Flat processing
|
||||
**
|
||||
*/
|
||||
|
||||
#include "a_sharedglobal.h"
|
||||
#include "r_defs.h"
|
||||
#include "r_sky.h"
|
||||
#include "r_utility.h"
|
||||
#include "doomstat.h"
|
||||
#include "d_player.h"
|
||||
#include "g_levellocals.h"
|
||||
#include "actorinlines.h"
|
||||
#include "p_lnspec.h"
|
||||
#include "r_data/matrix.h"
|
||||
#include "hwrenderer/dynlights/hw_dynlightdata.h"
|
||||
#include "hwrenderer/utility/hw_cvars.h"
|
||||
#include "hwrenderer/utility/hw_clock.h"
|
||||
#include "hwrenderer/utility/hw_lighting.h"
|
||||
#include "hwrenderer/textures/hw_material.h"
|
||||
#include "hwrenderer/scene/hw_drawinfo.h"
|
||||
#include "hwrenderer/data/flatvertices.h"
|
||||
#include "hwrenderer/dynlights/hw_lightbuffer.h"
|
||||
#include "hw_drawstructs.h"
|
||||
#include "hw_renderstate.h"
|
||||
|
||||
#ifdef _DEBUG
|
||||
CVAR(Int, gl_breaksec, -1, 0)
|
||||
#endif
|
||||
//==========================================================================
|
||||
//
|
||||
// Sets the texture matrix according to the plane's texture positioning
|
||||
// information
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
bool hw_SetPlaneTextureRotation(const GLSectorPlane * secplane, FMaterial * gltexture, VSMatrix &dest)
|
||||
{
|
||||
// only manipulate the texture matrix if needed.
|
||||
if (!secplane->Offs.isZero() ||
|
||||
secplane->Scale.X != 1. || secplane->Scale.Y != 1 ||
|
||||
secplane->Angle != 0 ||
|
||||
gltexture->TextureWidth() != 64 ||
|
||||
gltexture->TextureHeight() != 64)
|
||||
{
|
||||
float uoffs = secplane->Offs.X / gltexture->TextureWidth();
|
||||
float voffs = secplane->Offs.Y / gltexture->TextureHeight();
|
||||
|
||||
float xscale1 = secplane->Scale.X;
|
||||
float yscale1 = secplane->Scale.Y;
|
||||
if (gltexture->hasCanvas())
|
||||
{
|
||||
yscale1 = 0 - yscale1;
|
||||
}
|
||||
float angle = -secplane->Angle;
|
||||
|
||||
float xscale2 = 64.f / gltexture->TextureWidth();
|
||||
float yscale2 = 64.f / gltexture->TextureHeight();
|
||||
|
||||
dest.loadIdentity();
|
||||
dest.scale(xscale1, yscale1, 1.0f);
|
||||
dest.translate(uoffs, voffs, 0.0f);
|
||||
dest.scale(xscale2, yscale2, 1.0f);
|
||||
dest.rotate(angle, 0.0f, 0.0f, 1.0f);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// special handling for skyboxes which need texture clamping.
|
||||
// This will find the bounding rectangle of the sector and just
|
||||
// draw one single polygon filling that rectangle with a clamped
|
||||
// texture.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void GLFlat::CreateSkyboxVertices(FFlatVertex *vert)
|
||||
{
|
||||
float minx = FLT_MAX, miny = FLT_MAX;
|
||||
float maxx = -FLT_MAX, maxy = -FLT_MAX;
|
||||
|
||||
for (auto ln : sector->Lines)
|
||||
{
|
||||
float x = ln->v1->fX();
|
||||
float y = ln->v1->fY();
|
||||
if (x < minx) minx = x;
|
||||
if (y < miny) miny = y;
|
||||
if (x > maxx) maxx = x;
|
||||
if (y > maxy) maxy = y;
|
||||
x = ln->v2->fX();
|
||||
y = ln->v2->fY();
|
||||
if (x < minx) minx = x;
|
||||
if (y < miny) miny = y;
|
||||
if (x > maxx) maxx = x;
|
||||
if (y > maxy) maxy = y;
|
||||
}
|
||||
|
||||
static float uvals[] = { 0, 0, 1, 1 };
|
||||
static float vvals[] = { 1, 0, 0, 1 };
|
||||
int rot = -xs_FloorToInt(plane.Angle / 90.f);
|
||||
|
||||
vert[0].Set(minx, z, miny, uvals[rot & 3], vvals[rot & 3]);
|
||||
vert[1].Set(minx, z, maxy, uvals[(rot + 1) & 3], vvals[(rot + 1) & 3]);
|
||||
vert[2].Set(maxx, z, maxy, uvals[(rot + 2) & 3], vvals[(rot + 2) & 3]);
|
||||
vert[3].Set(maxx, z, miny, uvals[(rot + 3) & 3], vvals[(rot + 3) & 3]);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void GLFlat::SetupLights(HWDrawInfo *di, FLightNode * node, FDynLightData &lightdata, int portalgroup)
|
||||
{
|
||||
Plane p;
|
||||
|
||||
lightdata.Clear();
|
||||
if (renderstyle == STYLE_Add && !di->Level->lightadditivesurfaces)
|
||||
{
|
||||
dynlightindex = -1;
|
||||
return; // no lights on additively blended surfaces.
|
||||
}
|
||||
while (node)
|
||||
{
|
||||
FDynamicLight * light = node->lightsource;
|
||||
|
||||
if (!light->IsActive())
|
||||
{
|
||||
node = node->nextLight;
|
||||
continue;
|
||||
}
|
||||
iter_dlightf++;
|
||||
|
||||
// we must do the side check here because gl_GetLight needs the correct plane orientation
|
||||
// which we don't have for Legacy-style 3D-floors
|
||||
double planeh = plane.plane.ZatPoint(light->Pos);
|
||||
if ((planeh<light->Z() && ceiling) || (planeh>light->Z() && !ceiling))
|
||||
{
|
||||
node = node->nextLight;
|
||||
continue;
|
||||
}
|
||||
|
||||
p.Set(plane.plane.Normal(), plane.plane.fD());
|
||||
draw_dlightf += lightdata.GetLight(portalgroup, p, light, false);
|
||||
node = node->nextLight;
|
||||
}
|
||||
|
||||
dynlightindex = screen->mLights->UploadLights(lightdata);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void GLFlat::DrawSubsectors(HWDrawInfo *di, FRenderState &state)
|
||||
{
|
||||
if (di->Level->HasDynamicLights && screen->BuffersArePersistent() && !di->isFullbrightScene())
|
||||
{
|
||||
SetupLights(di, section->lighthead, lightdata, sector->PortalGroup);
|
||||
}
|
||||
state.SetLightIndex(dynlightindex);
|
||||
|
||||
|
||||
state.DrawIndexed(DT_Triangles, iboindex + section->vertexindex, section->vertexcount);
|
||||
flatvertices += section->vertexcount;
|
||||
flatprimitives++;
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Drawer for render hacks
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void GLFlat::DrawOtherPlanes(HWDrawInfo *di, FRenderState &state)
|
||||
{
|
||||
state.SetMaterial(gltexture, CLAMP_NONE, 0, -1);
|
||||
|
||||
// Draw the subsectors assigned to it due to missing textures
|
||||
auto pNode = (renderflags&SSRF_RENDERFLOOR) ?
|
||||
di->otherFloorPlanes.CheckKey(sector->sectornum) : di->otherCeilingPlanes.CheckKey(sector->sectornum);
|
||||
|
||||
if (!pNode) return;
|
||||
auto node = *pNode;
|
||||
|
||||
while (node)
|
||||
{
|
||||
state.SetLightIndex(node->lightindex);
|
||||
auto num = node->sub->numlines;
|
||||
flatvertices += num;
|
||||
flatprimitives++;
|
||||
state.Draw(DT_TriangleFan,node->vertexindex, num);
|
||||
node = node->next;
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Drawer for render hacks
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void GLFlat::DrawFloodPlanes(HWDrawInfo *di, FRenderState &state)
|
||||
{
|
||||
// Flood gaps with the back side's ceiling/floor texture
|
||||
// This requires a stencil because the projected plane interferes with
|
||||
// the depth buffer
|
||||
|
||||
state.SetMaterial(gltexture, CLAMP_NONE, 0, -1);
|
||||
|
||||
// Draw the subsectors assigned to it due to missing textures
|
||||
auto pNode = (renderflags&SSRF_RENDERFLOOR) ?
|
||||
di->floodFloorSegs.CheckKey(sector->sectornum) : di->floodCeilingSegs.CheckKey(sector->sectornum);
|
||||
if (!pNode) return;
|
||||
|
||||
auto fnode = *pNode;
|
||||
|
||||
state.SetLightIndex(-1);
|
||||
while (fnode)
|
||||
{
|
||||
flatvertices += 12;
|
||||
flatprimitives += 3;
|
||||
|
||||
// Push bleeding floor/ceiling textures back a little in the z-buffer
|
||||
// so they don't interfere with overlapping mid textures.
|
||||
state.SetDepthBias(1, 128);
|
||||
|
||||
// Create stencil
|
||||
state.SetEffect(EFF_STENCIL);
|
||||
state.EnableTexture(false);
|
||||
state.SetStencil(0, SOP_Increment, SF_ColorMaskOff);
|
||||
state.Draw(DT_TriangleFan, fnode->vertexindex, 4);
|
||||
|
||||
// Draw projected plane into stencil
|
||||
state.EnableTexture(true);
|
||||
state.SetEffect(EFF_NONE);
|
||||
state.SetStencil(1, SOP_Keep, SF_DepthMaskOff);
|
||||
state.EnableDepthTest(false);
|
||||
state.Draw(DT_TriangleFan, fnode->vertexindex + 4, 4);
|
||||
|
||||
// clear stencil
|
||||
state.SetEffect(EFF_STENCIL);
|
||||
state.EnableTexture(false);
|
||||
state.SetStencil(1, SOP_Decrement, SF_ColorMaskOff | SF_DepthMaskOff);
|
||||
state.Draw(DT_TriangleFan, fnode->vertexindex, 4);
|
||||
|
||||
// restore old stencil op.
|
||||
state.EnableTexture(true);
|
||||
state.EnableDepthTest(true);
|
||||
state.SetEffect(EFF_NONE);
|
||||
state.SetDepthBias(0, 0);
|
||||
state.SetStencil(0, SOP_Keep, SF_AllOn);
|
||||
|
||||
fnode = fnode->next;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
void GLFlat::DrawFlat(HWDrawInfo *di, FRenderState &state, bool translucent)
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
if (sector->sectornum == gl_breaksec)
|
||||
{
|
||||
int a = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
int rel = getExtraLight();
|
||||
|
||||
state.SetNormal(plane.plane.Normal().X, plane.plane.Normal().Z, plane.plane.Normal().Y);
|
||||
|
||||
di->SetColor(state, lightlevel, rel, di->isFullbrightScene(), Colormap, alpha);
|
||||
di->SetFog(state, lightlevel, rel, di->isFullbrightScene(), &Colormap, false);
|
||||
state.SetObjectColor(FlatColor | 0xff000000);
|
||||
state.SetAddColor(AddColor | 0xff000000);
|
||||
|
||||
if (hacktype & SSRF_PLANEHACK)
|
||||
{
|
||||
DrawOtherPlanes(di, state);
|
||||
}
|
||||
else if (hacktype & SSRF_FLOODHACK)
|
||||
{
|
||||
DrawFloodPlanes(di, state);
|
||||
}
|
||||
else if (!translucent)
|
||||
{
|
||||
if (sector->special != GLSector_Skybox)
|
||||
{
|
||||
state.SetMaterial(gltexture, CLAMP_NONE, 0, -1);
|
||||
state.SetPlaneTextureRotation(&plane, gltexture);
|
||||
DrawSubsectors(di, state);
|
||||
state.EnableTextureMatrix(false);
|
||||
}
|
||||
else if (!hacktype)
|
||||
{
|
||||
state.SetMaterial(gltexture, CLAMP_XY, 0, -1);
|
||||
state.SetLightIndex(dynlightindex);
|
||||
state.Draw(DT_TriangleFan,iboindex, 4);
|
||||
flatvertices += 4;
|
||||
flatprimitives++;
|
||||
}
|
||||
state.SetObjectColor(0xffffffff);
|
||||
}
|
||||
else
|
||||
{
|
||||
state.SetRenderStyle(renderstyle);
|
||||
if (!gltexture)
|
||||
{
|
||||
state.AlphaFunc(Alpha_GEqual, 0.f);
|
||||
state.EnableTexture(false);
|
||||
DrawSubsectors(di, state);
|
||||
state.EnableTexture(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!gltexture->tex->GetTranslucency()) state.AlphaFunc(Alpha_GEqual, gl_mask_threshold);
|
||||
else state.AlphaFunc(Alpha_GEqual, 0.f);
|
||||
state.SetMaterial(gltexture, CLAMP_NONE, 0, -1);
|
||||
state.SetPlaneTextureRotation(&plane, gltexture);
|
||||
DrawSubsectors(di, state);
|
||||
state.EnableTextureMatrix(false);
|
||||
}
|
||||
state.SetRenderStyle(DefaultRenderStyle());
|
||||
state.SetObjectColor(0xffffffff);
|
||||
}
|
||||
state.SetAddColor(0);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// GLFlat::PutFlat
|
||||
//
|
||||
// submit to the renderer
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
inline void GLFlat::PutFlat(HWDrawInfo *di, bool fog)
|
||||
{
|
||||
if (di->isFullbrightScene())
|
||||
{
|
||||
Colormap.Clear();
|
||||
}
|
||||
else if (!screen->BuffersArePersistent())
|
||||
{
|
||||
if (di->Level->HasDynamicLights && gltexture != nullptr && !di->isFullbrightScene() && !(hacktype & (SSRF_PLANEHACK|SSRF_FLOODHACK)) )
|
||||
{
|
||||
SetupLights(di, section->lighthead, lightdata, sector->PortalGroup);
|
||||
}
|
||||
}
|
||||
di->AddFlat(this, fog);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// This draws one flat
|
||||
// The whichplane boolean indicates if the flat is a floor(false) or a ceiling(true)
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void GLFlat::Process(HWDrawInfo *di, sector_t * model, int whichplane, bool fog)
|
||||
{
|
||||
plane.GetFromSector(model, whichplane);
|
||||
if (whichplane != int(ceiling))
|
||||
{
|
||||
// Flip the normal if the source plane has a different orientation than what we are about to render.
|
||||
plane.plane.FlipVert();
|
||||
}
|
||||
|
||||
if (!fog)
|
||||
{
|
||||
gltexture=FMaterial::ValidateTexture(plane.texture, false, true);
|
||||
if (!gltexture) return;
|
||||
if (gltexture->tex->isFullbright())
|
||||
{
|
||||
Colormap.MakeWhite();
|
||||
lightlevel=255;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
gltexture = NULL;
|
||||
lightlevel = abs(lightlevel);
|
||||
}
|
||||
|
||||
z = plane.plane.ZatPoint(0.f, 0.f);
|
||||
if (sector->special == GLSector_Skybox)
|
||||
{
|
||||
auto vert = screen->mVertexData->AllocVertices(4);
|
||||
CreateSkyboxVertices(vert.first);
|
||||
iboindex = vert.second;
|
||||
}
|
||||
|
||||
// For hacks this won't go into a render list.
|
||||
PutFlat(di, fog);
|
||||
rendered_flats++;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Sets 3D floor info. Common code for all 4 cases
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void GLFlat::SetFrom3DFloor(F3DFloor *rover, bool top, bool underside)
|
||||
{
|
||||
F3DFloor::planeref & plane = top? rover->top : rover->bottom;
|
||||
|
||||
// FF_FOG requires an inverted logic where to get the light from
|
||||
lightlist_t *light = P_GetPlaneLight(sector, plane.plane, underside);
|
||||
lightlevel = hw_ClampLight(*light->p_lightlevel);
|
||||
|
||||
if (rover->flags & FF_FOG)
|
||||
{
|
||||
Colormap.LightColor = light->extra_colormap.FadeColor;
|
||||
FlatColor = 0xffffffff;
|
||||
AddColor = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
Colormap.CopyFrom3DLight(light);
|
||||
FlatColor = *plane.flatcolor;
|
||||
// AddColor = sector->SpecialColors[sector_t::add];
|
||||
}
|
||||
|
||||
|
||||
alpha = rover->alpha/255.0f;
|
||||
renderstyle = rover->flags&FF_ADDITIVETRANS? STYLE_Add : STYLE_Translucent;
|
||||
iboindex = plane.vindex;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Process a sector's flats for rendering
|
||||
// This function is only called once per sector.
|
||||
// Subsequent subsectors are just quickly added to the ss_renderflags array
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void GLFlat::ProcessSector(HWDrawInfo *di, sector_t * frontsector, int which)
|
||||
{
|
||||
lightlist_t * light;
|
||||
FSectorPortal *port;
|
||||
|
||||
#ifdef _DEBUG
|
||||
if (frontsector->sectornum == gl_breaksec)
|
||||
{
|
||||
int a = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Get the real sector for this one.
|
||||
sector = &di->Level->sectors[frontsector->sectornum];
|
||||
extsector_t::xfloor &x = sector->e->XFloor;
|
||||
dynlightindex = -1;
|
||||
hacktype = (which & (SSRF_PLANEHACK|SSRF_FLOODHACK));
|
||||
|
||||
uint8_t sink;
|
||||
uint8_t &srf = hacktype? sink : di->section_renderflags[di->Level->sections.SectionIndex(section)];
|
||||
const auto &vp = di->Viewpoint;
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
// do floors
|
||||
//
|
||||
//
|
||||
//
|
||||
if ((which & SSRF_RENDERFLOOR) && frontsector->floorplane.ZatPoint(vp.Pos) <= vp.Pos.Z)
|
||||
{
|
||||
// process the original floor first.
|
||||
|
||||
srf |= SSRF_RENDERFLOOR;
|
||||
|
||||
lightlevel = hw_ClampLight(frontsector->GetFloorLight());
|
||||
Colormap = frontsector->Colormap;
|
||||
FlatColor = frontsector->SpecialColors[sector_t::floor];
|
||||
AddColor = frontsector->AdditiveColors[sector_t::floor];
|
||||
port = frontsector->ValidatePortal(sector_t::floor);
|
||||
if ((stack = (port != NULL)))
|
||||
{
|
||||
/* to be redone in a less invasive manner
|
||||
if (port->mType == PORTS_STACKEDSECTORTHING)
|
||||
{
|
||||
di->AddFloorStack(sector); // stacked sector things require visplane merging.
|
||||
}
|
||||
*/
|
||||
alpha = frontsector->GetAlpha(sector_t::floor);
|
||||
}
|
||||
else
|
||||
{
|
||||
alpha = 1.0f - frontsector->GetReflect(sector_t::floor);
|
||||
}
|
||||
|
||||
if (alpha != 0.f && frontsector->GetTexture(sector_t::floor) != skyflatnum)
|
||||
{
|
||||
iboindex = frontsector->iboindex[sector_t::floor];
|
||||
|
||||
ceiling = false;
|
||||
renderflags = SSRF_RENDERFLOOR;
|
||||
|
||||
if (x.ffloors.Size())
|
||||
{
|
||||
light = P_GetPlaneLight(sector, &frontsector->floorplane, false);
|
||||
if ((!(sector->GetFlags(sector_t::floor)&PLANEF_ABSLIGHTING) || light->lightsource == NULL)
|
||||
&& (light->p_lightlevel != &frontsector->lightlevel))
|
||||
{
|
||||
lightlevel = hw_ClampLight(*light->p_lightlevel);
|
||||
}
|
||||
|
||||
Colormap.CopyFrom3DLight(light);
|
||||
}
|
||||
renderstyle = STYLE_Translucent;
|
||||
Process(di, frontsector, sector_t::floor, false);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
// do ceilings
|
||||
//
|
||||
//
|
||||
//
|
||||
if ((which & SSRF_RENDERCEILING) && frontsector->ceilingplane.ZatPoint(vp.Pos) >= vp.Pos.Z)
|
||||
{
|
||||
// process the original ceiling first.
|
||||
|
||||
srf |= SSRF_RENDERCEILING;
|
||||
|
||||
lightlevel = hw_ClampLight(frontsector->GetCeilingLight());
|
||||
Colormap = frontsector->Colormap;
|
||||
FlatColor = frontsector->SpecialColors[sector_t::ceiling];
|
||||
AddColor = frontsector->AdditiveColors[sector_t::ceiling];
|
||||
port = frontsector->ValidatePortal(sector_t::ceiling);
|
||||
if ((stack = (port != NULL)))
|
||||
{
|
||||
/* as above for floors
|
||||
if (port->mType == PORTS_STACKEDSECTORTHING)
|
||||
{
|
||||
di->AddCeilingStack(sector);
|
||||
}
|
||||
*/
|
||||
alpha = frontsector->GetAlpha(sector_t::ceiling);
|
||||
}
|
||||
else
|
||||
{
|
||||
alpha = 1.0f - frontsector->GetReflect(sector_t::ceiling);
|
||||
}
|
||||
|
||||
if (alpha != 0.f && frontsector->GetTexture(sector_t::ceiling) != skyflatnum)
|
||||
{
|
||||
iboindex = frontsector->iboindex[sector_t::ceiling];
|
||||
ceiling = true;
|
||||
renderflags = SSRF_RENDERCEILING;
|
||||
|
||||
if (x.ffloors.Size())
|
||||
{
|
||||
light = P_GetPlaneLight(sector, §or->ceilingplane, true);
|
||||
|
||||
if ((!(sector->GetFlags(sector_t::ceiling)&PLANEF_ABSLIGHTING))
|
||||
&& (light->p_lightlevel != &frontsector->lightlevel))
|
||||
{
|
||||
lightlevel = hw_ClampLight(*light->p_lightlevel);
|
||||
}
|
||||
Colormap.CopyFrom3DLight(light);
|
||||
}
|
||||
renderstyle = STYLE_Translucent;
|
||||
Process(di, frontsector, sector_t::ceiling, false);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
// do 3D floors
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
stack = false;
|
||||
if ((which & SSRF_RENDER3DPLANES) && x.ffloors.Size())
|
||||
{
|
||||
renderflags = SSRF_RENDER3DPLANES;
|
||||
srf |= SSRF_RENDER3DPLANES;
|
||||
// 3d-floors must not overlap!
|
||||
double lastceilingheight = sector->CenterCeiling(); // render only in the range of the
|
||||
double lastfloorheight = sector->CenterFloor(); // current sector part (if applicable)
|
||||
F3DFloor * rover;
|
||||
int k;
|
||||
|
||||
// floors are ordered now top to bottom so scanning the list for the best match
|
||||
// is no longer necessary.
|
||||
|
||||
ceiling = true;
|
||||
Colormap = frontsector->Colormap;
|
||||
for (k = 0; k < (int)x.ffloors.Size(); k++)
|
||||
{
|
||||
rover = x.ffloors[k];
|
||||
|
||||
if ((rover->flags&(FF_EXISTS | FF_RENDERPLANES | FF_THISINSIDE)) == (FF_EXISTS | FF_RENDERPLANES))
|
||||
{
|
||||
if (rover->flags&FF_FOG && di->isFullbrightScene()) continue;
|
||||
if (!rover->top.copied && rover->flags&(FF_INVERTPLANES | FF_BOTHPLANES))
|
||||
{
|
||||
double ff_top = rover->top.plane->ZatPoint(sector->centerspot);
|
||||
if (ff_top < lastceilingheight)
|
||||
{
|
||||
if (vp.Pos.Z <= rover->top.plane->ZatPoint(vp.Pos))
|
||||
{
|
||||
SetFrom3DFloor(rover, true, !!(rover->flags&FF_FOG));
|
||||
Colormap.FadeColor = frontsector->Colormap.FadeColor;
|
||||
Process(di, rover->top.model, rover->top.isceiling, !!(rover->flags&FF_FOG));
|
||||
}
|
||||
lastceilingheight = ff_top;
|
||||
}
|
||||
}
|
||||
if (!rover->bottom.copied && !(rover->flags&FF_INVERTPLANES))
|
||||
{
|
||||
double ff_bottom = rover->bottom.plane->ZatPoint(sector->centerspot);
|
||||
if (ff_bottom < lastceilingheight)
|
||||
{
|
||||
if (vp.Pos.Z <= rover->bottom.plane->ZatPoint(vp.Pos))
|
||||
{
|
||||
SetFrom3DFloor(rover, false, !(rover->flags&FF_FOG));
|
||||
Colormap.FadeColor = frontsector->Colormap.FadeColor;
|
||||
Process(di, rover->bottom.model, rover->bottom.isceiling, !!(rover->flags&FF_FOG));
|
||||
}
|
||||
lastceilingheight = ff_bottom;
|
||||
if (rover->alpha < 255) lastceilingheight += EQUAL_EPSILON;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ceiling = false;
|
||||
for (k = x.ffloors.Size() - 1; k >= 0; k--)
|
||||
{
|
||||
rover = x.ffloors[k];
|
||||
|
||||
if ((rover->flags&(FF_EXISTS | FF_RENDERPLANES | FF_THISINSIDE)) == (FF_EXISTS | FF_RENDERPLANES))
|
||||
{
|
||||
if (rover->flags&FF_FOG && di->isFullbrightScene()) continue;
|
||||
if (!rover->bottom.copied && rover->flags&(FF_INVERTPLANES | FF_BOTHPLANES))
|
||||
{
|
||||
double ff_bottom = rover->bottom.plane->ZatPoint(sector->centerspot);
|
||||
if (ff_bottom > lastfloorheight || (rover->flags&FF_FIX))
|
||||
{
|
||||
if (vp.Pos.Z >= rover->bottom.plane->ZatPoint(vp.Pos))
|
||||
{
|
||||
SetFrom3DFloor(rover, false, !(rover->flags&FF_FOG));
|
||||
Colormap.FadeColor = frontsector->Colormap.FadeColor;
|
||||
|
||||
if (rover->flags&FF_FIX)
|
||||
{
|
||||
lightlevel = hw_ClampLight(rover->model->lightlevel);
|
||||
Colormap = rover->GetColormap();
|
||||
}
|
||||
|
||||
Process(di, rover->bottom.model, rover->bottom.isceiling, !!(rover->flags&FF_FOG));
|
||||
}
|
||||
lastfloorheight = ff_bottom;
|
||||
}
|
||||
}
|
||||
if (!rover->top.copied && !(rover->flags&FF_INVERTPLANES))
|
||||
{
|
||||
double ff_top = rover->top.plane->ZatPoint(sector->centerspot);
|
||||
if (ff_top > lastfloorheight)
|
||||
{
|
||||
if (vp.Pos.Z >= rover->top.plane->ZatPoint(vp.Pos))
|
||||
{
|
||||
SetFrom3DFloor(rover, true, !!(rover->flags&FF_FOG));
|
||||
Colormap.FadeColor = frontsector->Colormap.FadeColor;
|
||||
Process(di, rover->top.model, rover->top.isceiling, !!(rover->flags&FF_FOG));
|
||||
}
|
||||
lastfloorheight = ff_top;
|
||||
if (rover->alpha < 255) lastfloorheight -= EQUAL_EPSILON;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
1059
src/rendering/hwrenderer/scene/hw_portal.cpp
Normal file
1059
src/rendering/hwrenderer/scene/hw_portal.cpp
Normal file
File diff suppressed because it is too large
Load diff
381
src/rendering/hwrenderer/scene/hw_portal.h
Normal file
381
src/rendering/hwrenderer/scene/hw_portal.h
Normal file
|
@ -0,0 +1,381 @@
|
|||
#pragma once
|
||||
|
||||
#include "tarray.h"
|
||||
#include "r_utility.h"
|
||||
#include "actor.h"
|
||||
#include "hwrenderer/scene/hw_drawinfo.h"
|
||||
#include "hwrenderer/scene/hw_drawstructs.h"
|
||||
#include "hw_renderstate.h"
|
||||
#include "hwrenderer/textures/hw_material.h"
|
||||
|
||||
|
||||
struct GLSkyInfo
|
||||
{
|
||||
float x_offset[2];
|
||||
float y_offset; // doubleskies don't have a y-offset
|
||||
FMaterial * texture[2];
|
||||
FTextureID skytexno1;
|
||||
bool mirrored;
|
||||
bool doublesky;
|
||||
bool sky2;
|
||||
PalEntry fadecolor;
|
||||
|
||||
bool operator==(const GLSkyInfo & inf)
|
||||
{
|
||||
return !memcmp(this, &inf, sizeof(*this));
|
||||
}
|
||||
bool operator!=(const GLSkyInfo & inf)
|
||||
{
|
||||
return !!memcmp(this, &inf, sizeof(*this));
|
||||
}
|
||||
void init(HWDrawInfo *di, int sky1, PalEntry fadecolor);
|
||||
};
|
||||
|
||||
struct GLHorizonInfo
|
||||
{
|
||||
GLSectorPlane plane;
|
||||
int lightlevel;
|
||||
FColormap colormap;
|
||||
PalEntry specialcolor;
|
||||
};
|
||||
|
||||
struct FPortalSceneState;
|
||||
|
||||
class HWPortal
|
||||
{
|
||||
friend struct FPortalSceneState;
|
||||
|
||||
enum
|
||||
{
|
||||
STP_Stencil,
|
||||
STP_DepthClear,
|
||||
STP_DepthRestore,
|
||||
STP_AllInOne
|
||||
};
|
||||
|
||||
ActorRenderFlags savedvisibility;
|
||||
TArray<unsigned int> mPrimIndices;
|
||||
unsigned int mTopCap = ~0u, mBottomCap = ~0u;
|
||||
|
||||
void DrawPortalStencil(FRenderState &state, int pass);
|
||||
|
||||
public:
|
||||
FPortalSceneState * mState;
|
||||
TArray<GLWall> lines;
|
||||
BoundingRect boundingBox;
|
||||
int planesused = 0;
|
||||
|
||||
HWPortal(FPortalSceneState *s, bool local = false) : mState(s), boundingBox(false)
|
||||
{
|
||||
}
|
||||
virtual ~HWPortal() {}
|
||||
virtual int ClipSeg(seg_t *seg, const DVector3 &viewpos) { return PClip_Inside; }
|
||||
virtual int ClipSubsector(subsector_t *sub) { return PClip_Inside; }
|
||||
virtual int ClipPoint(const DVector2 &pos) { return PClip_Inside; }
|
||||
virtual line_t *ClipLine() { return nullptr; }
|
||||
virtual void * GetSource() const = 0; // GetSource MUST be implemented!
|
||||
virtual const char *GetName() = 0;
|
||||
virtual bool IsSky() { return false; }
|
||||
virtual bool NeedCap() { return true; }
|
||||
virtual bool NeedDepthBuffer() { return true; }
|
||||
virtual void DrawContents(HWDrawInfo *di, FRenderState &state) = 0;
|
||||
virtual void RenderAttached(HWDrawInfo *di) {}
|
||||
void SetupStencil(HWDrawInfo *di, FRenderState &state, bool usestencil);
|
||||
void RemoveStencil(HWDrawInfo *di, FRenderState &state, bool usestencil);
|
||||
|
||||
void AddLine(GLWall * l)
|
||||
{
|
||||
lines.Push(*l);
|
||||
boundingBox.addVertex(l->glseg.x1, l->glseg.y1);
|
||||
boundingBox.addVertex(l->glseg.x2, l->glseg.y2);
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
struct FPortalSceneState
|
||||
{
|
||||
int MirrorFlag = 0;
|
||||
int PlaneMirrorFlag = 0;
|
||||
int renderdepth = 0;
|
||||
|
||||
int PlaneMirrorMode = 0;
|
||||
bool inskybox = 0;
|
||||
|
||||
UniqueList<GLSkyInfo> UniqueSkies;
|
||||
UniqueList<GLHorizonInfo> UniqueHorizons;
|
||||
UniqueList<secplane_t> UniquePlaneMirrors;
|
||||
|
||||
int skyboxrecursion = 0;
|
||||
|
||||
void BeginScene()
|
||||
{
|
||||
UniqueSkies.Clear();
|
||||
UniqueHorizons.Clear();
|
||||
UniquePlaneMirrors.Clear();
|
||||
}
|
||||
|
||||
bool isMirrored() const
|
||||
{
|
||||
return !!((MirrorFlag ^ PlaneMirrorFlag) & 1);
|
||||
}
|
||||
|
||||
void StartFrame();
|
||||
bool RenderFirstSkyPortal(int recursion, HWDrawInfo *outer_di, FRenderState &state);
|
||||
void EndFrame(HWDrawInfo *outer_di, FRenderState &state);
|
||||
void RenderPortal(HWPortal *p, FRenderState &state, bool usestencil, HWDrawInfo *outer_di);
|
||||
};
|
||||
|
||||
|
||||
class HWScenePortalBase : public HWPortal
|
||||
{
|
||||
protected:
|
||||
HWScenePortalBase(FPortalSceneState *state) : HWPortal(state, false)
|
||||
{
|
||||
|
||||
}
|
||||
public:
|
||||
void ClearClipper(HWDrawInfo *di, Clipper *clipper);
|
||||
virtual bool NeedDepthBuffer() { return true; }
|
||||
virtual void DrawContents(HWDrawInfo *di, FRenderState &state)
|
||||
{
|
||||
if (Setup(di, state, di->mClipper))
|
||||
{
|
||||
di->DrawScene(di, DM_PORTAL);
|
||||
Shutdown(di, state);
|
||||
}
|
||||
else state.ClearScreen();
|
||||
}
|
||||
virtual bool Setup(HWDrawInfo *di, FRenderState &rstate, Clipper *clipper) = 0;
|
||||
virtual void Shutdown(HWDrawInfo *di, FRenderState &rstate) {}
|
||||
};
|
||||
|
||||
struct HWLinePortal : public HWScenePortalBase
|
||||
{
|
||||
// this must be the same as at the start of line_t, so that we can pass in this structure directly to P_ClipLineToPortal.
|
||||
vertex_t *v1, *v2; // vertices, from v1 to v2
|
||||
DVector2 delta; // precalculated v2 - v1 for side checking
|
||||
|
||||
angle_t angv1, angv2; // for quick comparisons with a line or subsector
|
||||
|
||||
HWLinePortal(FPortalSceneState *state, line_t *line) : HWScenePortalBase(state)
|
||||
{
|
||||
v1 = line->v1;
|
||||
v2 = line->v2;
|
||||
CalcDelta();
|
||||
}
|
||||
|
||||
HWLinePortal(FPortalSceneState *state, FLinePortalSpan *line) : HWScenePortalBase(state)
|
||||
{
|
||||
if (line->lines[0]->mType != PORTT_LINKED || line->v1 == nullptr)
|
||||
{
|
||||
// For non-linked portals we must check the actual linedef.
|
||||
line_t *lline = line->lines[0]->mDestination;
|
||||
v1 = lline->v1;
|
||||
v2 = lline->v2;
|
||||
}
|
||||
else
|
||||
{
|
||||
// For linked portals we can check the merged span.
|
||||
v1 = line->v1;
|
||||
v2 = line->v2;
|
||||
}
|
||||
CalcDelta();
|
||||
}
|
||||
|
||||
void CalcDelta()
|
||||
{
|
||||
delta = v2->fPos() - v1->fPos();
|
||||
}
|
||||
|
||||
line_t *line()
|
||||
{
|
||||
vertex_t **pv = &v1;
|
||||
return reinterpret_cast<line_t*>(pv);
|
||||
}
|
||||
|
||||
int ClipSeg(seg_t *seg, const DVector3 &viewpos) override;
|
||||
int ClipSubsector(subsector_t *sub) override;
|
||||
int ClipPoint(const DVector2 &pos);
|
||||
bool NeedCap() override { return false; }
|
||||
};
|
||||
|
||||
struct HWMirrorPortal : public HWLinePortal
|
||||
{
|
||||
// mirror portals always consist of single linedefs!
|
||||
line_t * linedef;
|
||||
|
||||
protected:
|
||||
bool Setup(HWDrawInfo *di, FRenderState &rstate, Clipper *clipper) override;
|
||||
void Shutdown(HWDrawInfo *di, FRenderState &rstate) override;
|
||||
void * GetSource() const override { return linedef; }
|
||||
const char *GetName() override;
|
||||
|
||||
public:
|
||||
|
||||
HWMirrorPortal(FPortalSceneState *state, line_t * line)
|
||||
: HWLinePortal(state, line)
|
||||
{
|
||||
linedef = line;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct HWLineToLinePortal : public HWLinePortal
|
||||
{
|
||||
FLinePortalSpan *glport;
|
||||
protected:
|
||||
bool Setup(HWDrawInfo *di, FRenderState &rstate, Clipper *clipper) override;
|
||||
virtual void * GetSource() const override { return glport; }
|
||||
virtual const char *GetName() override;
|
||||
virtual line_t *ClipLine() override { return line(); }
|
||||
virtual void RenderAttached(HWDrawInfo *di) override;
|
||||
|
||||
public:
|
||||
|
||||
HWLineToLinePortal(FPortalSceneState *state, FLinePortalSpan *ll)
|
||||
: HWLinePortal(state, ll)
|
||||
{
|
||||
glport = ll;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct HWSkyboxPortal : public HWScenePortalBase
|
||||
{
|
||||
bool oldclamp;
|
||||
int old_pm;
|
||||
FSectorPortal * portal;
|
||||
|
||||
protected:
|
||||
bool Setup(HWDrawInfo *di, FRenderState &rstate, Clipper *clipper) override;
|
||||
void Shutdown(HWDrawInfo *di, FRenderState &rstate) override;
|
||||
virtual void * GetSource() const { return portal; }
|
||||
virtual bool IsSky() { return true; }
|
||||
virtual const char *GetName();
|
||||
|
||||
public:
|
||||
|
||||
|
||||
HWSkyboxPortal(FPortalSceneState *state, FSectorPortal * pt) : HWScenePortalBase(state)
|
||||
{
|
||||
portal = pt;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
struct HWSectorStackPortal : public HWScenePortalBase
|
||||
{
|
||||
TArray<subsector_t *> subsectors;
|
||||
protected:
|
||||
bool Setup(HWDrawInfo *di, FRenderState &rstate, Clipper *clipper) override;
|
||||
void Shutdown(HWDrawInfo *di, FRenderState &rstate) override;
|
||||
virtual void * GetSource() const { return origin; }
|
||||
virtual bool IsSky() { return true; } // although this isn't a real sky it can be handled as one.
|
||||
virtual const char *GetName();
|
||||
FSectorPortalGroup *origin;
|
||||
|
||||
public:
|
||||
|
||||
HWSectorStackPortal(FPortalSceneState *state, FSectorPortalGroup *pt) : HWScenePortalBase(state)
|
||||
{
|
||||
origin = pt;
|
||||
}
|
||||
void SetupCoverage(HWDrawInfo *di);
|
||||
void AddSubsector(subsector_t *sub)
|
||||
{
|
||||
subsectors.Push(sub);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
struct HWPlaneMirrorPortal : public HWScenePortalBase
|
||||
{
|
||||
int old_pm;
|
||||
protected:
|
||||
bool Setup(HWDrawInfo *di, FRenderState &rstate, Clipper *clipper) override;
|
||||
void Shutdown(HWDrawInfo *di, FRenderState &rstate) override;
|
||||
virtual void * GetSource() const { return origin; }
|
||||
virtual const char *GetName();
|
||||
secplane_t * origin;
|
||||
|
||||
public:
|
||||
|
||||
HWPlaneMirrorPortal(FPortalSceneState *state, secplane_t * pt) : HWScenePortalBase(state)
|
||||
{
|
||||
origin = pt;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
struct HWHorizonPortal : public HWPortal
|
||||
{
|
||||
GLHorizonInfo * origin;
|
||||
unsigned int voffset;
|
||||
unsigned int vcount;
|
||||
friend struct HWEEHorizonPortal;
|
||||
|
||||
protected:
|
||||
virtual void DrawContents(HWDrawInfo *di, FRenderState &state);
|
||||
virtual void * GetSource() const { return origin; }
|
||||
virtual bool NeedDepthBuffer() { return false; }
|
||||
virtual bool NeedCap() { return false; }
|
||||
virtual const char *GetName();
|
||||
|
||||
public:
|
||||
|
||||
HWHorizonPortal(FPortalSceneState *state, GLHorizonInfo * pt, FRenderViewpoint &vp, bool local = false);
|
||||
};
|
||||
|
||||
struct HWEEHorizonPortal : public HWPortal
|
||||
{
|
||||
FSectorPortal * portal;
|
||||
|
||||
protected:
|
||||
virtual void DrawContents(HWDrawInfo *di, FRenderState &state);
|
||||
virtual void * GetSource() const { return portal; }
|
||||
virtual bool NeedDepthBuffer() { return false; }
|
||||
virtual bool NeedCap() { return false; }
|
||||
virtual const char *GetName();
|
||||
|
||||
public:
|
||||
|
||||
HWEEHorizonPortal(FPortalSceneState *state, FSectorPortal *pt) : HWPortal(state)
|
||||
{
|
||||
portal = pt;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
struct HWSkyPortal : public HWPortal
|
||||
{
|
||||
GLSkyInfo * origin;
|
||||
FSkyVertexBuffer *vertexBuffer;
|
||||
friend struct HWEEHorizonPortal;
|
||||
|
||||
void RenderRow(HWDrawInfo *di, FRenderState &state, EDrawType prim, int row, bool apply = true);
|
||||
void RenderBox(HWDrawInfo *di, FRenderState &state, FTextureID texno, FMaterial * gltex, float x_offset, bool sky2);
|
||||
void RenderDome(HWDrawInfo *di, FRenderState &state, FMaterial * tex, float x_offset, float y_offset, bool mirror, int mode);
|
||||
|
||||
protected:
|
||||
virtual void DrawContents(HWDrawInfo *di, FRenderState &state);
|
||||
virtual void * GetSource() const { return origin; }
|
||||
virtual bool IsSky() { return true; }
|
||||
virtual bool NeedDepthBuffer() { return false; }
|
||||
virtual const char *GetName();
|
||||
|
||||
public:
|
||||
|
||||
|
||||
HWSkyPortal(FSkyVertexBuffer *vertexbuffer, FPortalSceneState *state, GLSkyInfo * pt, bool local = false)
|
||||
: HWPortal(state, local)
|
||||
{
|
||||
origin = pt;
|
||||
vertexBuffer = vertexbuffer;
|
||||
}
|
||||
|
||||
};
|
1390
src/rendering/hwrenderer/scene/hw_renderhacks.cpp
Normal file
1390
src/rendering/hwrenderer/scene/hw_renderhacks.cpp
Normal file
File diff suppressed because it is too large
Load diff
158
src/rendering/hwrenderer/scene/hw_renderstate.cpp
Normal file
158
src/rendering/hwrenderer/scene/hw_renderstate.cpp
Normal file
|
@ -0,0 +1,158 @@
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Copyright(C) 2000-2018 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/
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
/*
|
||||
** hw_renderstate.cpp
|
||||
** hardware independent part of render state.
|
||||
**
|
||||
*/
|
||||
|
||||
#include "hw_renderstate.h"
|
||||
#include "hw_drawstructs.h"
|
||||
#include "hw_portal.h"
|
||||
#include "hwrenderer/utility/hw_lighting.h"
|
||||
#include "hwrenderer/utility/hw_cvars.h"
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// set current light color
|
||||
//
|
||||
//==========================================================================
|
||||
void HWDrawInfo::SetColor(FRenderState &state, int sectorlightlevel, int rellight, bool fullbright, const FColormap &cm, float alpha, bool weapon)
|
||||
{
|
||||
if (fullbright)
|
||||
{
|
||||
state.SetColorAlpha(0xffffff, alpha, 0);
|
||||
if (isSoftwareLighting()) state.SetSoftLightLevel(255);
|
||||
else state.SetNoSoftLightLevel();
|
||||
}
|
||||
else
|
||||
{
|
||||
int hwlightlevel = CalcLightLevel(sectorlightlevel, rellight, weapon, cm.BlendFactor);
|
||||
PalEntry pe = CalcLightColor(hwlightlevel, cm.LightColor, cm.BlendFactor);
|
||||
state.SetColorAlpha(pe, alpha, cm.Desaturation);
|
||||
if (isSoftwareLighting()) state.SetSoftLightLevel(hw_ClampLight(sectorlightlevel + rellight), cm.BlendFactor);
|
||||
else state.SetNoSoftLightLevel();
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Lighting stuff
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void HWDrawInfo::SetShaderLight(FRenderState &state, float level, float olight)
|
||||
{
|
||||
const float MAXDIST = 256.f;
|
||||
const float THRESHOLD = 96.f;
|
||||
const float FACTOR = 0.75f;
|
||||
|
||||
if (level > 0)
|
||||
{
|
||||
float lightdist, lightfactor;
|
||||
|
||||
if (olight < THRESHOLD)
|
||||
{
|
||||
lightdist = (MAXDIST / 2) + (olight * MAXDIST / THRESHOLD / 2);
|
||||
olight = THRESHOLD;
|
||||
}
|
||||
else lightdist = MAXDIST;
|
||||
|
||||
lightfactor = 1.f + ((olight / level) - 1.f) * FACTOR;
|
||||
if (lightfactor == 1.f) lightdist = 0.f; // save some code in the shader
|
||||
state.SetLightParms(lightfactor, lightdist);
|
||||
}
|
||||
else
|
||||
{
|
||||
state.SetLightParms(1.f, 0.f);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Sets the fog for the current polygon
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void HWDrawInfo::SetFog(FRenderState &state, int lightlevel, int rellight, bool fullbright, const FColormap *cmap, bool isadditive)
|
||||
{
|
||||
PalEntry fogcolor;
|
||||
float fogdensity;
|
||||
|
||||
if (Level->flags&LEVEL_HASFADETABLE)
|
||||
{
|
||||
fogdensity = 70;
|
||||
fogcolor = 0x808080;
|
||||
}
|
||||
else if (cmap != NULL && !fullbright)
|
||||
{
|
||||
fogcolor = cmap->FadeColor;
|
||||
fogdensity = GetFogDensity(lightlevel, fogcolor, cmap->FogDensity, cmap->BlendFactor);
|
||||
fogcolor.a = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
fogcolor = 0;
|
||||
fogdensity = 0;
|
||||
}
|
||||
|
||||
// Make fog a little denser when inside a skybox
|
||||
if (screen->mPortalState->inskybox) fogdensity += fogdensity / 2;
|
||||
|
||||
|
||||
// no fog in enhanced vision modes!
|
||||
if (fogdensity == 0 || gl_fogmode == 0)
|
||||
{
|
||||
state.EnableFog(false);
|
||||
state.SetFog(0, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((lightmode == ELightMode::Doom || (isSoftwareLighting() && cmap->BlendFactor > 0)) && fogcolor == 0)
|
||||
{
|
||||
float light = (float)CalcLightLevel(lightlevel, rellight, false, cmap->BlendFactor);
|
||||
SetShaderLight(state, light, lightlevel);
|
||||
}
|
||||
else
|
||||
{
|
||||
state.SetLightParms(1.f, 0.f);
|
||||
}
|
||||
|
||||
// For additive rendering using the regular fog color here would mean applying it twice
|
||||
// so always use black
|
||||
if (isadditive)
|
||||
{
|
||||
fogcolor = 0;
|
||||
}
|
||||
|
||||
state.EnableFog(true);
|
||||
state.SetFog(fogcolor, fogdensity);
|
||||
|
||||
// Korshun: fullbright fog like in software renderer.
|
||||
if (isSoftwareLighting() && cmap->BlendFactor == 0 && Level->brightfog && fogdensity != 0 && fogcolor != 0)
|
||||
{
|
||||
state.SetSoftLightLevel(255);
|
||||
}
|
||||
}
|
||||
}
|
536
src/rendering/hwrenderer/scene/hw_renderstate.h
Normal file
536
src/rendering/hwrenderer/scene/hw_renderstate.h
Normal file
|
@ -0,0 +1,536 @@
|
|||
#pragma once
|
||||
|
||||
#include "v_palette.h"
|
||||
#include "vectors.h"
|
||||
#include "g_levellocals.h"
|
||||
#include "hw_drawstructs.h"
|
||||
#include "hw_drawlist.h"
|
||||
#include "r_data/matrix.h"
|
||||
#include "hwrenderer/textures/hw_material.h"
|
||||
|
||||
struct FColormap;
|
||||
class IVertexBuffer;
|
||||
class IIndexBuffer;
|
||||
|
||||
enum EClearTarget
|
||||
{
|
||||
CT_Depth = 1,
|
||||
CT_Stencil = 2,
|
||||
CT_Color = 4
|
||||
};
|
||||
|
||||
enum ERenderEffect
|
||||
{
|
||||
EFF_NONE = -1,
|
||||
EFF_FOGBOUNDARY,
|
||||
EFF_SPHEREMAP,
|
||||
EFF_BURN,
|
||||
EFF_STENCIL,
|
||||
|
||||
MAX_EFFECTS
|
||||
};
|
||||
|
||||
enum EAlphaFunc
|
||||
{
|
||||
Alpha_GEqual = 0,
|
||||
Alpha_Greater = 1
|
||||
};
|
||||
|
||||
enum EDrawType
|
||||
{
|
||||
DT_Points = 0,
|
||||
DT_Lines = 1,
|
||||
DT_Triangles = 2,
|
||||
DT_TriangleFan = 3,
|
||||
DT_TriangleStrip = 4
|
||||
};
|
||||
|
||||
enum EDepthFunc
|
||||
{
|
||||
DF_Less,
|
||||
DF_LEqual,
|
||||
DF_Always
|
||||
};
|
||||
|
||||
enum EStencilFlags
|
||||
{
|
||||
SF_AllOn = 0,
|
||||
SF_ColorMaskOff = 1,
|
||||
SF_DepthMaskOff = 2,
|
||||
};
|
||||
|
||||
enum EStencilOp
|
||||
{
|
||||
SOP_Keep = 0,
|
||||
SOP_Increment = 1,
|
||||
SOP_Decrement = 2
|
||||
};
|
||||
|
||||
enum ECull
|
||||
{
|
||||
Cull_None,
|
||||
Cull_CCW,
|
||||
Cull_CW
|
||||
};
|
||||
|
||||
|
||||
|
||||
struct FStateVec4
|
||||
{
|
||||
float vec[4];
|
||||
|
||||
void Set(float r, float g, float b, float a)
|
||||
{
|
||||
vec[0] = r;
|
||||
vec[1] = g;
|
||||
vec[2] = b;
|
||||
vec[3] = a;
|
||||
}
|
||||
};
|
||||
|
||||
struct FMaterialState
|
||||
{
|
||||
FMaterial *mMaterial;
|
||||
int mClampMode;
|
||||
int mTranslation;
|
||||
int mOverrideShader;
|
||||
bool mChanged;
|
||||
|
||||
void Reset()
|
||||
{
|
||||
mMaterial = nullptr;
|
||||
mTranslation = 0;
|
||||
mClampMode = CLAMP_NONE;
|
||||
mOverrideShader = -1;
|
||||
mChanged = false;
|
||||
}
|
||||
};
|
||||
|
||||
struct FDepthBiasState
|
||||
{
|
||||
float mFactor;
|
||||
float mUnits;
|
||||
bool mChanged;
|
||||
|
||||
void Reset()
|
||||
{
|
||||
mFactor = 0;
|
||||
mUnits = 0;
|
||||
mChanged = false;
|
||||
}
|
||||
};
|
||||
|
||||
class FRenderState
|
||||
{
|
||||
protected:
|
||||
uint8_t mFogEnabled;
|
||||
uint8_t mTextureEnabled:1;
|
||||
uint8_t mGlowEnabled : 1;
|
||||
uint8_t mGradientEnabled : 1;
|
||||
uint8_t mBrightmapEnabled : 1;
|
||||
uint8_t mModelMatrixEnabled : 1;
|
||||
uint8_t mTextureMatrixEnabled : 1;
|
||||
uint8_t mSplitEnabled : 1;
|
||||
|
||||
int mLightIndex;
|
||||
int mSpecialEffect;
|
||||
int mTextureMode;
|
||||
int mDesaturation;
|
||||
int mSoftLight;
|
||||
float mLightParms[4];
|
||||
|
||||
float mAlphaThreshold;
|
||||
float mClipSplit[2];
|
||||
float mInterpolationFactor;
|
||||
|
||||
FStateVec4 mNormal;
|
||||
FStateVec4 mColor;
|
||||
FStateVec4 mGlowTop, mGlowBottom;
|
||||
FStateVec4 mGlowTopPlane, mGlowBottomPlane;
|
||||
FStateVec4 mGradientTopPlane, mGradientBottomPlane;
|
||||
FStateVec4 mSplitTopPlane, mSplitBottomPlane;
|
||||
PalEntry mAddColor;
|
||||
PalEntry mFogColor;
|
||||
PalEntry mObjectColor;
|
||||
PalEntry mObjectColor2;
|
||||
FStateVec4 mDynColor;
|
||||
FRenderStyle mRenderStyle;
|
||||
|
||||
FMaterialState mMaterial;
|
||||
FDepthBiasState mBias;
|
||||
|
||||
IVertexBuffer *mVertexBuffer;
|
||||
int mVertexOffsets[2]; // one per binding point
|
||||
IIndexBuffer *mIndexBuffer;
|
||||
|
||||
|
||||
public:
|
||||
VSMatrix mModelMatrix;
|
||||
VSMatrix mTextureMatrix;
|
||||
|
||||
public:
|
||||
|
||||
void Reset()
|
||||
{
|
||||
mTextureEnabled = true;
|
||||
mGradientEnabled = mBrightmapEnabled = mFogEnabled = mGlowEnabled = false;
|
||||
mFogColor.d = -1;
|
||||
mTextureMode = -1;
|
||||
mDesaturation = 0;
|
||||
mAlphaThreshold = 0.5f;
|
||||
mModelMatrixEnabled = false;
|
||||
mTextureMatrixEnabled = false;
|
||||
mSplitEnabled = false;
|
||||
mAddColor = 0;
|
||||
mObjectColor = 0xffffffff;
|
||||
mObjectColor2 = 0;
|
||||
mSoftLight = 0;
|
||||
mLightParms[0] = mLightParms[1] = mLightParms[2] = 0.0f;
|
||||
mLightParms[3] = -1.f;
|
||||
mSpecialEffect = EFF_NONE;
|
||||
mLightIndex = -1;
|
||||
mInterpolationFactor = 0;
|
||||
mRenderStyle = DefaultRenderStyle();
|
||||
mMaterial.Reset();
|
||||
mBias.Reset();
|
||||
|
||||
mVertexBuffer = nullptr;
|
||||
mVertexOffsets[0] = mVertexOffsets[1] = 0;
|
||||
mIndexBuffer = nullptr;
|
||||
|
||||
mColor.Set(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
mGlowTop.Set(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
mGlowBottom.Set(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
mGlowTopPlane.Set(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
mGlowBottomPlane.Set(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
mGradientTopPlane.Set(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
mGradientBottomPlane.Set(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
mSplitTopPlane.Set(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
mSplitBottomPlane.Set(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
mDynColor.Set(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
|
||||
mModelMatrix.loadIdentity();
|
||||
mTextureMatrix.loadIdentity();
|
||||
ClearClipSplit();
|
||||
}
|
||||
|
||||
void SetNormal(FVector3 norm)
|
||||
{
|
||||
mNormal.Set(norm.X, norm.Y, norm.Z, 0.f);
|
||||
}
|
||||
|
||||
void SetNormal(float x, float y, float z)
|
||||
{
|
||||
mNormal.Set(x, y, z, 0.f);
|
||||
}
|
||||
|
||||
void SetColor(float r, float g, float b, float a = 1.f, int desat = 0)
|
||||
{
|
||||
mColor.Set(r, g, b, a);
|
||||
mDesaturation = desat;
|
||||
}
|
||||
|
||||
void SetColor(PalEntry pe, int desat = 0)
|
||||
{
|
||||
mColor.Set(pe.r / 255.f, pe.g / 255.f, pe.b / 255.f, pe.a / 255.f);
|
||||
mDesaturation = desat;
|
||||
}
|
||||
|
||||
void SetColorAlpha(PalEntry pe, float alpha = 1.f, int desat = 0)
|
||||
{
|
||||
mColor.Set(pe.r / 255.f, pe.g / 255.f, pe.b / 255.f, alpha);
|
||||
mDesaturation = desat;
|
||||
}
|
||||
|
||||
void ResetColor()
|
||||
{
|
||||
mColor.Set(1, 1, 1, 1);
|
||||
mDesaturation = 0;
|
||||
}
|
||||
|
||||
void SetTextureMode(int mode)
|
||||
{
|
||||
mTextureMode = mode;
|
||||
}
|
||||
|
||||
void SetTextureMode(FRenderStyle style)
|
||||
{
|
||||
if (style.Flags & STYLEF_RedIsAlpha)
|
||||
{
|
||||
mTextureMode = TM_ALPHATEXTURE;
|
||||
}
|
||||
else if (style.Flags & STYLEF_ColorIsFixed)
|
||||
{
|
||||
mTextureMode = TM_STENCIL;
|
||||
}
|
||||
else if (style.Flags & STYLEF_InvertSource)
|
||||
{
|
||||
mTextureMode = TM_INVERSE;
|
||||
}
|
||||
}
|
||||
|
||||
int GetTextureMode()
|
||||
{
|
||||
return mTextureMode;
|
||||
}
|
||||
|
||||
void EnableTexture(bool on)
|
||||
{
|
||||
mTextureEnabled = on;
|
||||
}
|
||||
|
||||
void EnableFog(uint8_t on)
|
||||
{
|
||||
mFogEnabled = on;
|
||||
}
|
||||
|
||||
void SetEffect(int eff)
|
||||
{
|
||||
mSpecialEffect = eff;
|
||||
}
|
||||
|
||||
void EnableGlow(bool on)
|
||||
{
|
||||
mGlowEnabled = on;
|
||||
}
|
||||
|
||||
void EnableGradient(bool on)
|
||||
{
|
||||
mGradientEnabled = on;
|
||||
}
|
||||
|
||||
void EnableBrightmap(bool on)
|
||||
{
|
||||
mBrightmapEnabled = on;
|
||||
}
|
||||
|
||||
void EnableSplit(bool on)
|
||||
{
|
||||
mSplitEnabled = on;
|
||||
}
|
||||
|
||||
void EnableModelMatrix(bool on)
|
||||
{
|
||||
mModelMatrixEnabled = on;
|
||||
}
|
||||
|
||||
void EnableTextureMatrix(bool on)
|
||||
{
|
||||
mTextureMatrixEnabled = on;
|
||||
}
|
||||
|
||||
void SetGlowParams(float *t, float *b)
|
||||
{
|
||||
mGlowTop.Set(t[0], t[1], t[2], t[3]);
|
||||
mGlowBottom.Set(b[0], b[1], b[2], b[3]);
|
||||
}
|
||||
|
||||
void SetSoftLightLevel(int llevel, int blendfactor = 0)
|
||||
{
|
||||
if (blendfactor == 0) mLightParms[3] = llevel / 255.f;
|
||||
else mLightParms[3] = -1.f;
|
||||
}
|
||||
|
||||
void SetNoSoftLightLevel()
|
||||
{
|
||||
mLightParms[3] = -1.f;
|
||||
}
|
||||
|
||||
void SetGlowPlanes(const secplane_t &top, const secplane_t &bottom)
|
||||
{
|
||||
auto &tn = top.Normal();
|
||||
auto &bn = bottom.Normal();
|
||||
mGlowTopPlane.Set((float)tn.X, (float)tn.Y, (float)top.negiC, (float)top.fD());
|
||||
mGlowBottomPlane.Set((float)bn.X, (float)bn.Y, (float)bottom.negiC, (float)bottom.fD());
|
||||
}
|
||||
|
||||
void SetGradientPlanes(const secplane_t &top, const secplane_t &bottom)
|
||||
{
|
||||
auto &tn = top.Normal();
|
||||
auto &bn = bottom.Normal();
|
||||
mGradientTopPlane.Set((float)tn.X, (float)tn.Y, (float)top.negiC, (float)top.fD());
|
||||
mGradientBottomPlane.Set((float)bn.X, (float)bn.Y, (float)bottom.negiC, (float)bottom.fD());
|
||||
}
|
||||
|
||||
void SetSplitPlanes(const secplane_t &top, const secplane_t &bottom)
|
||||
{
|
||||
auto &tn = top.Normal();
|
||||
auto &bn = bottom.Normal();
|
||||
mSplitTopPlane.Set((float)tn.X, (float)tn.Y, (float)top.negiC, (float)top.fD());
|
||||
mSplitBottomPlane.Set((float)bn.X, (float)bn.Y, (float)bottom.negiC, (float)bottom.fD());
|
||||
}
|
||||
|
||||
void SetDynLight(float r, float g, float b)
|
||||
{
|
||||
mDynColor.Set(r, g, b, 0);
|
||||
}
|
||||
|
||||
void SetObjectColor(PalEntry pe)
|
||||
{
|
||||
mObjectColor = pe;
|
||||
}
|
||||
|
||||
void SetObjectColor2(PalEntry pe)
|
||||
{
|
||||
mObjectColor2 = pe;
|
||||
}
|
||||
|
||||
void SetAddColor(PalEntry pe)
|
||||
{
|
||||
mAddColor = pe;
|
||||
}
|
||||
|
||||
void SetFog(PalEntry c, float d)
|
||||
{
|
||||
const float LOG2E = 1.442692f; // = 1/log(2)
|
||||
mFogColor = c;
|
||||
if (d >= 0.0f) mLightParms[2] = d * (-LOG2E / 64000.f);
|
||||
}
|
||||
|
||||
void SetLightParms(float f, float d)
|
||||
{
|
||||
mLightParms[1] = f;
|
||||
mLightParms[0] = d;
|
||||
}
|
||||
|
||||
PalEntry GetFogColor() const
|
||||
{
|
||||
return mFogColor;
|
||||
}
|
||||
|
||||
void AlphaFunc(int func, float thresh)
|
||||
{
|
||||
if (func == Alpha_Greater) mAlphaThreshold = thresh;
|
||||
else mAlphaThreshold = thresh - 0.001f;
|
||||
}
|
||||
|
||||
void SetPlaneTextureRotation(GLSectorPlane *plane, FMaterial *texture)
|
||||
{
|
||||
if (hw_SetPlaneTextureRotation(plane, texture, mTextureMatrix))
|
||||
{
|
||||
EnableTextureMatrix(true);
|
||||
}
|
||||
}
|
||||
|
||||
void SetLightIndex(int index)
|
||||
{
|
||||
mLightIndex = index;
|
||||
}
|
||||
|
||||
void SetRenderStyle(FRenderStyle rs)
|
||||
{
|
||||
mRenderStyle = rs;
|
||||
}
|
||||
|
||||
void SetRenderStyle(ERenderStyle rs)
|
||||
{
|
||||
mRenderStyle = rs;
|
||||
}
|
||||
|
||||
void SetDepthBias(float a, float b)
|
||||
{
|
||||
mBias.mFactor = a;
|
||||
mBias.mUnits = b;
|
||||
mBias.mChanged = true;
|
||||
}
|
||||
|
||||
void ClearDepthBias()
|
||||
{
|
||||
mBias.mFactor = 0;
|
||||
mBias.mUnits = 0;
|
||||
mBias.mChanged = true;
|
||||
}
|
||||
|
||||
void SetMaterial(FMaterial *mat, int clampmode, int translation, int overrideshader)
|
||||
{
|
||||
mMaterial.mMaterial = mat;
|
||||
mMaterial.mClampMode = clampmode;
|
||||
mMaterial.mTranslation = translation;
|
||||
mMaterial.mOverrideShader = overrideshader;
|
||||
mMaterial.mChanged = true;
|
||||
}
|
||||
|
||||
void SetClipSplit(float bottom, float top)
|
||||
{
|
||||
mClipSplit[0] = bottom;
|
||||
mClipSplit[1] = top;
|
||||
}
|
||||
|
||||
void SetClipSplit(float *vals)
|
||||
{
|
||||
memcpy(mClipSplit, vals, 2 * sizeof(float));
|
||||
}
|
||||
|
||||
void GetClipSplit(float *out)
|
||||
{
|
||||
memcpy(out, mClipSplit, 2 * sizeof(float));
|
||||
}
|
||||
|
||||
void ClearClipSplit()
|
||||
{
|
||||
mClipSplit[0] = -1000000.f;
|
||||
mClipSplit[1] = 1000000.f;
|
||||
}
|
||||
|
||||
void SetVertexBuffer(IVertexBuffer *vb, int offset0, int offset1)
|
||||
{
|
||||
assert(vb);
|
||||
mVertexBuffer = vb;
|
||||
mVertexOffsets[0] = offset0;
|
||||
mVertexOffsets[1] = offset1;
|
||||
}
|
||||
|
||||
void SetIndexBuffer(IIndexBuffer *ib)
|
||||
{
|
||||
mIndexBuffer = ib;
|
||||
}
|
||||
|
||||
template <class T> void SetVertexBuffer(T *buffer)
|
||||
{
|
||||
auto ptrs = buffer->GetBufferObjects();
|
||||
SetVertexBuffer(ptrs.first, 0, 0);
|
||||
SetIndexBuffer(ptrs.second);
|
||||
}
|
||||
|
||||
void SetInterpolationFactor(float fac)
|
||||
{
|
||||
mInterpolationFactor = fac;
|
||||
}
|
||||
|
||||
float GetInterpolationFactor()
|
||||
{
|
||||
return mInterpolationFactor;
|
||||
}
|
||||
|
||||
// API-dependent render interface
|
||||
|
||||
// Draw commands
|
||||
virtual void ClearScreen() = 0;
|
||||
virtual void Draw(int dt, int index, int count, bool apply = true) = 0;
|
||||
virtual void DrawIndexed(int dt, int index, int count, bool apply = true) = 0;
|
||||
|
||||
// Immediate render state change commands. These only change infrequently and should not clutter the render state.
|
||||
virtual bool SetDepthClamp(bool on) = 0; // Deactivated only by skyboxes.
|
||||
virtual void SetDepthMask(bool on) = 0; // Used by decals and indirectly by portal setup.
|
||||
virtual void SetDepthFunc(int func) = 0; // Used by models, portals and mirror surfaces.
|
||||
virtual void SetDepthRange(float min, float max) = 0; // Used by portal setup.
|
||||
virtual void SetColorMask(bool r, bool g, bool b, bool a) = 0; // Used by portals.
|
||||
virtual void EnableDrawBufferAttachments(bool on) = 0; // Used by fog boundary drawer.
|
||||
virtual void SetStencil(int offs, int op, int flags=-1) = 0; // Used by portal setup and render hacks.
|
||||
virtual void SetCulling(int mode) = 0; // Used by model drawer only.
|
||||
virtual void EnableClipDistance(int num, bool state) = 0; // Use by sprite sorter for vertical splits.
|
||||
virtual void Clear(int targets) = 0; // not used during normal rendering
|
||||
virtual void EnableStencil(bool on) = 0; // always on for 3D, always off for 2D
|
||||
virtual void SetScissor(int x, int y, int w, int h) = 0; // constant for 3D, changes for 2D
|
||||
virtual void SetViewport(int x, int y, int w, int h) = 0; // constant for all 3D and all 2D
|
||||
virtual void EnableDepthTest(bool on) = 0; // used by 2D, portals and render hacks.
|
||||
virtual void EnableMultisampling(bool on) = 0; // only active for 2D
|
||||
virtual void EnableLineSmooth(bool on) = 0; // constant setting for each 2D drawer operation
|
||||
|
||||
void SetColorMask(bool on)
|
||||
{
|
||||
SetColorMask(on, on, on, on);
|
||||
}
|
||||
|
||||
};
|
||||
|
393
src/rendering/hwrenderer/scene/hw_sky.cpp
Normal file
393
src/rendering/hwrenderer/scene/hw_sky.cpp
Normal file
|
@ -0,0 +1,393 @@
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Copyright(C) 2002-2016 Christoph Oelckers
|
||||
// All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with this program. If not, see http://www.gnu.org/licenses/
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
|
||||
#include "a_sharedglobal.h"
|
||||
#include "r_sky.h"
|
||||
#include "r_state.h"
|
||||
#include "r_utility.h"
|
||||
#include "doomdata.h"
|
||||
#include "g_levellocals.h"
|
||||
#include "p_lnspec.h"
|
||||
#include "hwrenderer/scene/hw_drawinfo.h"
|
||||
#include "hwrenderer/scene/hw_drawstructs.h"
|
||||
#include "hwrenderer/scene/hw_portal.h"
|
||||
#include "hwrenderer/utility/hw_lighting.h"
|
||||
#include "hwrenderer/textures/hw_material.h"
|
||||
|
||||
CVAR(Bool,gl_noskyboxes, false, 0)
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Set up the skyinfo struct
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void GLSkyInfo::init(HWDrawInfo *di, int sky1, PalEntry FadeColor)
|
||||
{
|
||||
memset(this, 0, sizeof(*this));
|
||||
if ((sky1 & PL_SKYFLAT) && (sky1 & (PL_SKYFLAT - 1)))
|
||||
{
|
||||
const line_t *l = &di->Level->lines[(sky1&(PL_SKYFLAT - 1)) - 1];
|
||||
const side_t *s = l->sidedef[0];
|
||||
int pos;
|
||||
|
||||
if (di->Level->flags & LEVEL_SWAPSKIES && s->GetTexture(side_t::bottom).isValid())
|
||||
{
|
||||
pos = side_t::bottom;
|
||||
}
|
||||
else
|
||||
{
|
||||
pos = side_t::top;
|
||||
}
|
||||
|
||||
FTextureID texno = s->GetTexture(pos);
|
||||
texture[0] = FMaterial::ValidateTexture(texno, false, true);
|
||||
if (!texture[0] || !texture[0]->tex->isValid()) goto normalsky;
|
||||
skytexno1 = texno;
|
||||
x_offset[0] = s->GetTextureXOffset(pos) * (360.f/65536.f);
|
||||
y_offset = s->GetTextureYOffset(pos);
|
||||
mirrored = !l->args[2];
|
||||
}
|
||||
else
|
||||
{
|
||||
normalsky:
|
||||
if (di->Level->flags&LEVEL_DOUBLESKY)
|
||||
{
|
||||
texture[1] = FMaterial::ValidateTexture(di->Level->skytexture1, false, true);
|
||||
x_offset[1] = di->Level->hw_sky1pos;
|
||||
doublesky = true;
|
||||
}
|
||||
|
||||
if ((di->Level->flags&LEVEL_SWAPSKIES || (sky1 == PL_SKYFLAT) || (di->Level->flags&LEVEL_DOUBLESKY)) &&
|
||||
di->Level->skytexture2 != di->Level->skytexture1) // If both skies are equal use the scroll offset of the first!
|
||||
{
|
||||
texture[0] = FMaterial::ValidateTexture(di->Level->skytexture2, false, true);
|
||||
skytexno1 = di->Level->skytexture2;
|
||||
sky2 = true;
|
||||
x_offset[0] = di->Level->hw_sky2pos;
|
||||
}
|
||||
else if (!doublesky)
|
||||
{
|
||||
texture[0] = FMaterial::ValidateTexture(di->Level->skytexture1, false, true);
|
||||
skytexno1 = di->Level->skytexture1;
|
||||
x_offset[0] = di->Level->hw_sky1pos;
|
||||
}
|
||||
}
|
||||
if (di->Level->skyfog > 0)
|
||||
{
|
||||
fadecolor = FadeColor;
|
||||
fadecolor.a = 0;
|
||||
}
|
||||
else fadecolor = 0;
|
||||
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Calculate sky texture for ceiling or floor
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void GLWall::SkyPlane(HWDrawInfo *di, sector_t *sector, int plane, bool allowreflect)
|
||||
{
|
||||
int ptype = -1;
|
||||
|
||||
FSectorPortal *sportal = sector->ValidatePortal(plane);
|
||||
if (sportal != nullptr && sportal->mFlags & PORTSF_INSKYBOX) sportal = nullptr; // no recursions, delete it here to simplify the following code
|
||||
|
||||
// Either a regular sky or a skybox with skyboxes disabled
|
||||
if ((sportal == nullptr && sector->GetTexture(plane) == skyflatnum) || (gl_noskyboxes && sportal != nullptr && sportal->mType == PORTS_SKYVIEWPOINT))
|
||||
{
|
||||
GLSkyInfo skyinfo;
|
||||
skyinfo.init(di, sector->sky, Colormap.FadeColor);
|
||||
ptype = PORTALTYPE_SKY;
|
||||
sky = &skyinfo;
|
||||
PutPortal(di, ptype, plane);
|
||||
}
|
||||
else if (sportal != nullptr)
|
||||
{
|
||||
switch (sportal->mType)
|
||||
{
|
||||
case PORTS_STACKEDSECTORTHING:
|
||||
case PORTS_PORTAL:
|
||||
case PORTS_LINKEDPORTAL:
|
||||
{
|
||||
auto glport = sector->GetPortalGroup(plane);
|
||||
if (glport != NULL)
|
||||
{
|
||||
if (sector->PortalBlocksView(plane)) return;
|
||||
|
||||
if (screen->instack[1 - plane]) return;
|
||||
ptype = PORTALTYPE_SECTORSTACK;
|
||||
portal = glport;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case PORTS_SKYVIEWPOINT:
|
||||
case PORTS_HORIZON:
|
||||
case PORTS_PLANE:
|
||||
ptype = PORTALTYPE_SKYBOX;
|
||||
secportal = sportal;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (allowreflect && sector->GetReflect(plane) > 0)
|
||||
{
|
||||
auto vpz = di->Viewpoint.Pos.Z;
|
||||
if ((plane == sector_t::ceiling && vpz > sector->ceilingplane.fD()) ||
|
||||
(plane == sector_t::floor && vpz < -sector->floorplane.fD())) return;
|
||||
ptype = PORTALTYPE_PLANEMIRROR;
|
||||
planemirror = plane == sector_t::ceiling ? §or->ceilingplane : §or->floorplane;
|
||||
}
|
||||
if (ptype != -1)
|
||||
{
|
||||
PutPortal(di, ptype, plane);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Calculate sky texture for a line
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void GLWall::SkyLine(HWDrawInfo *di, sector_t *fs, line_t *line)
|
||||
{
|
||||
FSectorPortal *secport = line->GetTransferredPortal();
|
||||
GLSkyInfo skyinfo;
|
||||
int ptype;
|
||||
|
||||
// JUSTHIT is used as an indicator that a skybox is in use.
|
||||
// This is to avoid recursion
|
||||
|
||||
if (!gl_noskyboxes && secport && (secport->mSkybox == nullptr || !(secport->mFlags & PORTSF_INSKYBOX)))
|
||||
{
|
||||
ptype = PORTALTYPE_SKYBOX;
|
||||
secportal = secport;
|
||||
}
|
||||
else
|
||||
{
|
||||
skyinfo.init(di, fs->sky, Colormap.FadeColor);
|
||||
ptype = PORTALTYPE_SKY;
|
||||
sky = &skyinfo;
|
||||
}
|
||||
ztop[0] = zceil[0];
|
||||
ztop[1] = zceil[1];
|
||||
zbottom[0] = zfloor[0];
|
||||
zbottom[1] = zfloor[1];
|
||||
PutPortal(di, ptype, -1);
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Skies on one sided walls
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void GLWall::SkyNormal(HWDrawInfo *di, sector_t * fs,vertex_t * v1,vertex_t * v2)
|
||||
{
|
||||
ztop[0]=ztop[1]=32768.0f;
|
||||
zbottom[0]=zceil[0];
|
||||
zbottom[1]=zceil[1];
|
||||
SkyPlane(di, fs, sector_t::ceiling, true);
|
||||
|
||||
ztop[0]=zfloor[0];
|
||||
ztop[1]=zfloor[1];
|
||||
zbottom[0]=zbottom[1]=-32768.0f;
|
||||
SkyPlane(di, fs, sector_t::floor, true);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Upper Skies on two sided walls
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void GLWall::SkyTop(HWDrawInfo *di, seg_t * seg,sector_t * fs,sector_t * bs,vertex_t * v1,vertex_t * v2)
|
||||
{
|
||||
if (fs->GetTexture(sector_t::ceiling)==skyflatnum)
|
||||
{
|
||||
if (bs->special == GLSector_NoSkyDraw) return;
|
||||
if (bs->GetTexture(sector_t::ceiling)==skyflatnum)
|
||||
{
|
||||
// if the back sector is closed the sky must be drawn!
|
||||
if (bs->ceilingplane.ZatPoint(v1) > bs->floorplane.ZatPoint(v1) ||
|
||||
bs->ceilingplane.ZatPoint(v2) > bs->floorplane.ZatPoint(v2) || bs->transdoor)
|
||||
return;
|
||||
|
||||
// one more check for some ugly transparent door hacks
|
||||
if (!bs->floorplane.isSlope() && !fs->floorplane.isSlope())
|
||||
{
|
||||
if (bs->GetPlaneTexZ(sector_t::floor)==fs->GetPlaneTexZ(sector_t::floor)+1.)
|
||||
{
|
||||
FTexture * tex = TexMan.GetTexture(seg->sidedef->GetTexture(side_t::bottom), true);
|
||||
if (!tex || !tex->isValid()) return;
|
||||
|
||||
// very, very, very ugly special case (See Icarus MAP14)
|
||||
// It is VERY important that this is only done for a floor height difference of 1
|
||||
// or it will cause glitches elsewhere.
|
||||
tex = TexMan.GetTexture(seg->sidedef->GetTexture(side_t::mid), true);
|
||||
if (tex != NULL && !(seg->linedef->flags & ML_DONTPEGTOP) &&
|
||||
seg->sidedef->GetTextureYOffset(side_t::mid) > 0)
|
||||
{
|
||||
ztop[0]=ztop[1]=32768.0f;
|
||||
zbottom[0]=zbottom[1]=
|
||||
bs->ceilingplane.ZatPoint(v2) + seg->sidedef->GetTextureYOffset(side_t::mid);
|
||||
SkyPlane(di, fs, sector_t::ceiling, false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ztop[0]=ztop[1]=32768.0f;
|
||||
|
||||
FTexture * tex = TexMan.GetTexture(seg->sidedef->GetTexture(side_t::top), true);
|
||||
if (bs->GetTexture(sector_t::ceiling) != skyflatnum)
|
||||
|
||||
{
|
||||
zbottom[0]=zceil[0];
|
||||
zbottom[1]=zceil[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
zbottom[0] = bs->ceilingplane.ZatPoint(v1);
|
||||
zbottom[1] = bs->ceilingplane.ZatPoint(v2);
|
||||
flags|=GLWF_SKYHACK; // mid textures on such lines need special treatment!
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
float frontreflect = fs->GetReflect(sector_t::ceiling);
|
||||
if (frontreflect > 0)
|
||||
{
|
||||
float backreflect = bs->GetReflect(sector_t::ceiling);
|
||||
if (backreflect > 0 && bs->ceilingplane.fD() == fs->ceilingplane.fD() && !bs->isClosed())
|
||||
{
|
||||
// Don't add intra-portal line to the portal.
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int type = fs->GetPortalType(sector_t::ceiling);
|
||||
if (type == PORTS_STACKEDSECTORTHING || type == PORTS_PORTAL || type == PORTS_LINKEDPORTAL)
|
||||
{
|
||||
auto pfront = fs->GetPortalGroup(sector_t::ceiling);
|
||||
auto pback = bs->GetPortalGroup(sector_t::ceiling);
|
||||
if (pfront == NULL || fs->PortalBlocksView(sector_t::ceiling)) return;
|
||||
if (pfront == pback && !bs->PortalBlocksView(sector_t::ceiling)) return;
|
||||
}
|
||||
}
|
||||
|
||||
// stacked sectors
|
||||
ztop[0] = ztop[1] = 32768.0f;
|
||||
zbottom[0] = fs->ceilingplane.ZatPoint(v1);
|
||||
zbottom[1] = fs->ceilingplane.ZatPoint(v2);
|
||||
|
||||
}
|
||||
|
||||
SkyPlane(di, fs, sector_t::ceiling, true);
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Lower Skies on two sided walls
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void GLWall::SkyBottom(HWDrawInfo *di, seg_t * seg,sector_t * fs,sector_t * bs,vertex_t * v1,vertex_t * v2)
|
||||
{
|
||||
if (fs->GetTexture(sector_t::floor)==skyflatnum)
|
||||
{
|
||||
if (bs->special == GLSector_NoSkyDraw) return;
|
||||
FTexture * tex = TexMan.GetTexture(seg->sidedef->GetTexture(side_t::bottom), true);
|
||||
|
||||
// For lower skies the normal logic only applies to walls with no lower texture.
|
||||
if (!tex->isValid())
|
||||
{
|
||||
if (bs->GetTexture(sector_t::floor)==skyflatnum)
|
||||
{
|
||||
// if the back sector is closed the sky must be drawn!
|
||||
if (bs->ceilingplane.ZatPoint(v1) > bs->floorplane.ZatPoint(v1) ||
|
||||
bs->ceilingplane.ZatPoint(v2) > bs->floorplane.ZatPoint(v2))
|
||||
return;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
// Special hack for Vrack2b
|
||||
if (bs->floorplane.ZatPoint(di->Viewpoint.Pos) > di->Viewpoint.Pos.Z) return;
|
||||
}
|
||||
}
|
||||
zbottom[0]=zbottom[1]=-32768.0f;
|
||||
|
||||
if ((tex && tex->isValid()) || bs->GetTexture(sector_t::floor) != skyflatnum)
|
||||
{
|
||||
ztop[0] = zfloor[0];
|
||||
ztop[1] = zfloor[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
ztop[0] = bs->floorplane.ZatPoint(v1);
|
||||
ztop[1] = bs->floorplane.ZatPoint(v2);
|
||||
flags |= GLWF_SKYHACK; // mid textures on such lines need special treatment!
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
float frontreflect = fs->GetReflect(sector_t::floor);
|
||||
if (frontreflect > 0)
|
||||
{
|
||||
float backreflect = bs->GetReflect(sector_t::floor);
|
||||
if (backreflect > 0 && bs->floorplane.fD() == fs->floorplane.fD() && !bs->isClosed())
|
||||
{
|
||||
// Don't add intra-portal line to the portal.
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int type = fs->GetPortalType(sector_t::floor);
|
||||
if (type == PORTS_STACKEDSECTORTHING || type == PORTS_PORTAL || type == PORTS_LINKEDPORTAL)
|
||||
{
|
||||
auto pfront = fs->GetPortalGroup(sector_t::floor);
|
||||
auto pback = bs->GetPortalGroup(sector_t::floor);
|
||||
if (pfront == NULL || fs->PortalBlocksView(sector_t::floor)) return;
|
||||
if (pfront == pback && !bs->PortalBlocksView(sector_t::floor)) return;
|
||||
}
|
||||
}
|
||||
|
||||
// stacked sectors
|
||||
zbottom[0]=zbottom[1]=-32768.0f;
|
||||
ztop[0] = fs->floorplane.ZatPoint(v1);
|
||||
ztop[1] = fs->floorplane.ZatPoint(v2);
|
||||
}
|
||||
|
||||
SkyPlane(di, fs, sector_t::floor, true);
|
||||
}
|
||||
|
327
src/rendering/hwrenderer/scene/hw_skydome.cpp
Normal file
327
src/rendering/hwrenderer/scene/hw_skydome.cpp
Normal file
|
@ -0,0 +1,327 @@
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Copyright(C) 2003-2018 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/
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
/*
|
||||
**
|
||||
** Draws the sky. Loosely based on the JDoom sky and the ZDoomGL 0.66.2 sky.
|
||||
**
|
||||
** for FSkyVertexBuffer::SkyVertex only:
|
||||
**---------------------------------------------------------------------------
|
||||
** Copyright 2003 Tim Stump
|
||||
** All rights reserved.
|
||||
**
|
||||
** Redistribution and use in source and binary forms, with or without
|
||||
** modification, are permitted provided that the following conditions
|
||||
** are met:
|
||||
**
|
||||
** 1. Redistributions of source code must retain the above copyright
|
||||
** notice, this list of conditions and the following disclaimer.
|
||||
** 2. Redistributions in binary form must reproduce the above copyright
|
||||
** notice, this list of conditions and the following disclaimer in the
|
||||
** documentation and/or other materials provided with the distribution.
|
||||
** 3. The name of the author may not be used to endorse or promote products
|
||||
** derived from this software without specific prior written permission.
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
**---------------------------------------------------------------------------
|
||||
**
|
||||
*/
|
||||
#include "doomtype.h"
|
||||
#include "g_level.h"
|
||||
#include "w_wad.h"
|
||||
#include "r_state.h"
|
||||
#include "r_utility.h"
|
||||
#include "g_levellocals.h"
|
||||
#include "r_sky.h"
|
||||
#include "cmdlib.h"
|
||||
|
||||
#include "textures/skyboxtexture.h"
|
||||
#include "hwrenderer/textures/hw_material.h"
|
||||
#include "hw_skydome.h"
|
||||
#include "hw_renderstate.h"
|
||||
#include "hw_drawinfo.h"
|
||||
#include "hwrenderer/data/buffers.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// Shamelessly lifted from Doomsday (written by Jaakko Keränen)
|
||||
// also shamelessly lifted from ZDoomGL! ;)
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
EXTERN_CVAR(Float, skyoffset)
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
//
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
FSkyVertexBuffer::FSkyVertexBuffer()
|
||||
{
|
||||
CreateDome();
|
||||
mVertexBuffer = screen->CreateVertexBuffer();
|
||||
|
||||
static const FVertexBufferAttribute format[] = {
|
||||
{ 0, VATTR_VERTEX, VFmt_Float3, (int)myoffsetof(FSkyVertex, x) },
|
||||
{ 0, VATTR_TEXCOORD, VFmt_Float2, (int)myoffsetof(FSkyVertex, u) },
|
||||
{ 0, VATTR_COLOR, VFmt_Byte4, (int)myoffsetof(FSkyVertex, color) }
|
||||
};
|
||||
mVertexBuffer->SetFormat(1, 3, sizeof(FSkyVertex), format);
|
||||
mVertexBuffer->SetData(mVertices.Size() * sizeof(FSkyVertex), &mVertices[0], true);
|
||||
}
|
||||
|
||||
FSkyVertexBuffer::~FSkyVertexBuffer()
|
||||
{
|
||||
delete mVertexBuffer;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
//
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void FSkyVertexBuffer::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;
|
||||
|
||||
FSkyVertex vert;
|
||||
|
||||
vert.color = r == 0 ? 0xffffff : 0xffffffff;
|
||||
|
||||
// And the texture coordinates.
|
||||
if (!zflip) // Flipped Y is for the lower hemisphere.
|
||||
{
|
||||
vert.u = (-c / (float)mColumns);
|
||||
vert.v = (r / (float)mRows);
|
||||
}
|
||||
else
|
||||
{
|
||||
vert.u = (-c / (float)mColumns);
|
||||
vert.v = 1.0f + ((mRows - r) / (float)mRows);
|
||||
}
|
||||
|
||||
if (r != 4) z += 300;
|
||||
// And finally the vertex.
|
||||
vert.x = -pos.X; // Doom mirrors the sky vertically!
|
||||
vert.y = z - 1.f;
|
||||
vert.z = pos.Y;
|
||||
|
||||
mVertices.Push(vert);
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
//
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void FSkyVertexBuffer::CreateSkyHemisphere(int hemi)
|
||||
{
|
||||
int r, c;
|
||||
bool zflip = !!(hemi & SKYHEMI_LOWER);
|
||||
|
||||
mPrimStart.Push(mVertices.Size());
|
||||
|
||||
for (c = 0; c < mColumns; c++)
|
||||
{
|
||||
SkyVertex(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 + zflip, c, zflip);
|
||||
SkyVertex(r + 1 - zflip, c, zflip);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
//
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void FSkyVertexBuffer::CreateDome()
|
||||
{
|
||||
// the first thing we put into the buffer is the fog layer object which is just 4 triangles around the viewpoint.
|
||||
|
||||
mVertices.Reserve(12);
|
||||
mVertices[0].Set(1.0f, 1.0f, -1.0f);
|
||||
mVertices[1].Set(1.0f, -1.0f, -1.0f);
|
||||
mVertices[2].Set(-1.0f, 0.0f, -1.0f);
|
||||
|
||||
mVertices[3].Set(1.0f, 1.0f, -1.0f);
|
||||
mVertices[4].Set(1.0f, -1.0f, -1.0f);
|
||||
mVertices[5].Set(0.0f, 0.0f, 1.0f);
|
||||
|
||||
mVertices[6].Set(-1.0f, 0.0f, -1.0f);
|
||||
mVertices[7].Set(1.0f, 1.0f, -1.0f);
|
||||
mVertices[8].Set(0.0f, 0.0f, 1.0f);
|
||||
|
||||
mVertices[9].Set(1.0f, -1.0f, -1.0f);
|
||||
mVertices[10].Set(-1.0f, 0.0f, -1.0f);
|
||||
mVertices[11].Set(0.0f, 0.0f, 1.0f);
|
||||
|
||||
mColumns = 128;
|
||||
mRows = 4;
|
||||
CreateSkyHemisphere(SKYHEMI_UPPER);
|
||||
CreateSkyHemisphere(SKYHEMI_LOWER);
|
||||
mPrimStart.Push(mVertices.Size());
|
||||
|
||||
mSideStart = mVertices.Size();
|
||||
mFaceStart[0] = mSideStart + 10;
|
||||
mFaceStart[1] = mFaceStart[0] + 4;
|
||||
mFaceStart[2] = mFaceStart[1] + 4;
|
||||
mFaceStart[3] = mFaceStart[2] + 4;
|
||||
mFaceStart[4] = mFaceStart[3] + 4;
|
||||
mFaceStart[5] = mFaceStart[4] + 4;
|
||||
mFaceStart[6] = mFaceStart[5] + 4;
|
||||
mVertices.Reserve(10 + 7*4);
|
||||
FSkyVertex *ptr = &mVertices[mSideStart];
|
||||
|
||||
// all sides
|
||||
ptr[0].SetXYZ(128.f, 128.f, -128.f, 0, 0);
|
||||
ptr[1].SetXYZ(128.f, -128.f, -128.f, 0, 1);
|
||||
ptr[2].SetXYZ(-128.f, 128.f, -128.f, 0.25f, 0);
|
||||
ptr[3].SetXYZ(-128.f, -128.f, -128.f, 0.25f, 1);
|
||||
ptr[4].SetXYZ(-128.f, 128.f, 128.f, 0.5f, 0);
|
||||
ptr[5].SetXYZ(-128.f, -128.f, 128.f, 0.5f, 1);
|
||||
ptr[6].SetXYZ(128.f, 128.f, 128.f, 0.75f, 0);
|
||||
ptr[7].SetXYZ(128.f, -128.f, 128.f, 0.75f, 1);
|
||||
ptr[8].SetXYZ(128.f, 128.f, -128.f, 1, 0);
|
||||
ptr[9].SetXYZ(128.f, -128.f, -128.f, 1, 1);
|
||||
|
||||
// north face
|
||||
ptr[10].SetXYZ(128.f, 128.f, -128.f, 0, 0);
|
||||
ptr[11].SetXYZ(-128.f, 128.f, -128.f, 1, 0);
|
||||
ptr[12].SetXYZ(128.f, -128.f, -128.f, 0, 1);
|
||||
ptr[13].SetXYZ(-128.f, -128.f, -128.f, 1, 1);
|
||||
|
||||
// east face
|
||||
ptr[14].SetXYZ(-128.f, 128.f, -128.f, 0, 0);
|
||||
ptr[15].SetXYZ(-128.f, 128.f, 128.f, 1, 0);
|
||||
ptr[16].SetXYZ(-128.f, -128.f, -128.f, 0, 1);
|
||||
ptr[17].SetXYZ(-128.f, -128.f, 128.f, 1, 1);
|
||||
|
||||
// south face
|
||||
ptr[18].SetXYZ(-128.f, 128.f, 128.f, 0, 0);
|
||||
ptr[19].SetXYZ(128.f, 128.f, 128.f, 1, 0);
|
||||
ptr[20].SetXYZ(-128.f, -128.f, 128.f, 0, 1);
|
||||
ptr[21].SetXYZ(128.f, -128.f, 128.f, 1, 1);
|
||||
|
||||
// west face
|
||||
ptr[22].SetXYZ(128.f, 128.f, 128.f, 0, 0);
|
||||
ptr[23].SetXYZ(128.f, 128.f, -128.f, 1, 0);
|
||||
ptr[24].SetXYZ(128.f, -128.f, 128.f, 0, 1);
|
||||
ptr[25].SetXYZ(128.f, -128.f, -128.f, 1, 1);
|
||||
|
||||
// bottom face
|
||||
ptr[26].SetXYZ(128.f, -128.f, -128.f, 0, 0);
|
||||
ptr[27].SetXYZ(-128.f, -128.f, -128.f, 1, 0);
|
||||
ptr[28].SetXYZ(128.f, -128.f, 128.f, 0, 1);
|
||||
ptr[29].SetXYZ(-128.f, -128.f, 128.f, 1, 1);
|
||||
|
||||
// top face
|
||||
ptr[30].SetXYZ(128.f, 128.f, -128.f, 0, 0);
|
||||
ptr[31].SetXYZ(-128.f, 128.f, -128.f, 1, 0);
|
||||
ptr[32].SetXYZ(128.f, 128.f, 128.f, 0, 1);
|
||||
ptr[33].SetXYZ(-128.f, 128.f, 128.f, 1, 1);
|
||||
|
||||
// top face flipped
|
||||
ptr[34].SetXYZ(128.f, 128.f, -128.f, 0, 1);
|
||||
ptr[35].SetXYZ(-128.f, 128.f, -128.f, 1, 1);
|
||||
ptr[36].SetXYZ(128.f, 128.f, 128.f, 0, 0);
|
||||
ptr[37].SetXYZ(-128.f, 128.f, 128.f, 1, 0);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
//
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void FSkyVertexBuffer::SetupMatrices(HWDrawInfo *di, FMaterial *tex, float x_offset, float y_offset, bool mirror, int mode, VSMatrix &modelMatrix, VSMatrix &textureMatrix)
|
||||
{
|
||||
int texw = tex->TextureWidth();
|
||||
int texh = tex->TextureHeight();
|
||||
|
||||
modelMatrix.loadIdentity();
|
||||
modelMatrix.rotate(-180.0f + x_offset, 0.f, 1.f, 0.f);
|
||||
|
||||
float xscale = texw < 1024.f ? floor(1024.f / float(texw)) : 1.f;
|
||||
float yscale = 1.f;
|
||||
if (texh <= 128 && (di->Level->flags & LEVEL_FORCETILEDSKY))
|
||||
{
|
||||
modelMatrix.translate(0.f, (-40 + tex->tex->GetSkyOffset() + skyoffset)*skyoffsetfactor, 0.f);
|
||||
modelMatrix.scale(1.f, 1.2f * 1.17f, 1.f);
|
||||
yscale = 240.f / texh;
|
||||
}
|
||||
else if (texh < 128)
|
||||
{
|
||||
// smaller sky textures must be tiled. We restrict it to 128 sky pixels, though
|
||||
modelMatrix.translate(0.f, -1250.f, 0.f);
|
||||
modelMatrix.scale(1.f, 128 / 230.f, 1.f);
|
||||
yscale = float(128 / texh); // intentionally left as integer.
|
||||
}
|
||||
else if (texh < 200)
|
||||
{
|
||||
modelMatrix.translate(0.f, -1250.f, 0.f);
|
||||
modelMatrix.scale(1.f, texh / 230.f, 1.f);
|
||||
}
|
||||
else if (texh <= 240)
|
||||
{
|
||||
modelMatrix.translate(0.f, (200 - texh + tex->tex->GetSkyOffset() + skyoffset)*skyoffsetfactor, 0.f);
|
||||
modelMatrix.scale(1.f, 1.f + ((texh - 200.f) / 200.f) * 1.17f, 1.f);
|
||||
}
|
||||
else
|
||||
{
|
||||
modelMatrix.translate(0.f, (-40 + tex->tex->GetSkyOffset() + skyoffset)*skyoffsetfactor, 0.f);
|
||||
modelMatrix.scale(1.f, 1.2f * 1.17f, 1.f);
|
||||
yscale = 240.f / texh;
|
||||
}
|
||||
textureMatrix.loadIdentity();
|
||||
textureMatrix.scale(mirror ? -xscale : xscale, yscale, 1.f);
|
||||
textureMatrix.translate(1.f, y_offset / texh, 1.f);
|
||||
}
|
84
src/rendering/hwrenderer/scene/hw_skydome.h
Normal file
84
src/rendering/hwrenderer/scene/hw_skydome.h
Normal file
|
@ -0,0 +1,84 @@
|
|||
#pragma once
|
||||
|
||||
#include "v_palette.h"
|
||||
#include "r_data/matrix.h"
|
||||
|
||||
class FMaterial;
|
||||
class FRenderState;
|
||||
class IVertexBuffer;
|
||||
struct HWSkyPortal;
|
||||
struct HWDrawInfo;
|
||||
|
||||
struct FSkyVertex
|
||||
{
|
||||
float x, y, z, u, v;
|
||||
PalEntry color;
|
||||
|
||||
void Set(float xx, float zz, float yy, float uu=0, float vv=0, PalEntry col=0xffffffff)
|
||||
{
|
||||
x = xx;
|
||||
z = zz;
|
||||
y = yy;
|
||||
u = uu;
|
||||
v = vv;
|
||||
color = col;
|
||||
}
|
||||
|
||||
void SetXYZ(float xx, float yy, float zz, float uu = 0, float vv = 0, PalEntry col = 0xffffffff)
|
||||
{
|
||||
x = xx;
|
||||
y = yy;
|
||||
z = zz;
|
||||
u = uu;
|
||||
v = vv;
|
||||
color = col;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
class FSkyVertexBuffer
|
||||
{
|
||||
friend struct HWSkyPortal;
|
||||
public:
|
||||
static const int SKYHEMI_UPPER = 1;
|
||||
static const int SKYHEMI_LOWER = 2;
|
||||
|
||||
enum
|
||||
{
|
||||
SKYMODE_MAINLAYER = 0,
|
||||
SKYMODE_SECONDLAYER = 1,
|
||||
SKYMODE_FOGLAYER = 2
|
||||
};
|
||||
|
||||
IVertexBuffer *mVertexBuffer;
|
||||
|
||||
TArray<FSkyVertex> mVertices;
|
||||
TArray<unsigned int> mPrimStart;
|
||||
|
||||
int mRows, mColumns;
|
||||
|
||||
// indices for sky cubemap faces
|
||||
int mFaceStart[7];
|
||||
int mSideStart;
|
||||
|
||||
void SkyVertex(int r, int c, bool yflip);
|
||||
void CreateSkyHemisphere(int hemi);
|
||||
void CreateDome();
|
||||
|
||||
public:
|
||||
|
||||
FSkyVertexBuffer();
|
||||
~FSkyVertexBuffer();
|
||||
void SetupMatrices(HWDrawInfo *di, FMaterial *tex, float x_offset, float y_offset, bool mirror, int mode, VSMatrix &modelmatrix, VSMatrix &textureMatrix);
|
||||
std::pair<IVertexBuffer *, IIndexBuffer *> GetBufferObjects() const
|
||||
{
|
||||
return std::make_pair(mVertexBuffer, nullptr);
|
||||
}
|
||||
|
||||
int FaceStart(int i)
|
||||
{
|
||||
if (i >= 0 && i < 7) return mFaceStart[i];
|
||||
else return mSideStart;
|
||||
}
|
||||
|
||||
};
|
222
src/rendering/hwrenderer/scene/hw_skyportal.cpp
Normal file
222
src/rendering/hwrenderer/scene/hw_skyportal.cpp
Normal file
|
@ -0,0 +1,222 @@
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// 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/
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
|
||||
#include "doomtype.h"
|
||||
#include "g_level.h"
|
||||
#include "w_wad.h"
|
||||
#include "r_state.h"
|
||||
#include "r_utility.h"
|
||||
#include "g_levellocals.h"
|
||||
#include "hwrenderer/scene/hw_skydome.h"
|
||||
#include "hwrenderer/scene/hw_portal.h"
|
||||
#include "hwrenderer/scene/hw_renderstate.h"
|
||||
#include "textures/skyboxtexture.h"
|
||||
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
//
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void HWSkyPortal::RenderRow(HWDrawInfo *di, FRenderState &state, EDrawType prim, int row, bool apply)
|
||||
{
|
||||
state.Draw(prim, vertexBuffer->mPrimStart[row], vertexBuffer->mPrimStart[row + 1] - vertexBuffer->mPrimStart[row]);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
//
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void HWSkyPortal::RenderDome(HWDrawInfo *di, FRenderState &state, FMaterial * tex, float x_offset, float y_offset, bool mirror, int mode)
|
||||
{
|
||||
if (tex)
|
||||
{
|
||||
state.SetMaterial(tex, CLAMP_NONE, 0, -1);
|
||||
state.EnableModelMatrix(true);
|
||||
state.EnableTextureMatrix(true);
|
||||
|
||||
vertexBuffer->SetupMatrices(di, tex, x_offset, y_offset, mirror, mode, state.mModelMatrix, state.mTextureMatrix);
|
||||
}
|
||||
|
||||
int rc = vertexBuffer->mRows + 1;
|
||||
|
||||
// The caps only get drawn for the main layer but not for the overlay.
|
||||
if (mode == FSkyVertexBuffer::SKYMODE_MAINLAYER && tex != NULL)
|
||||
{
|
||||
PalEntry pe = tex->tex->GetSkyCapColor(false);
|
||||
state.SetObjectColor(pe);
|
||||
state.EnableTexture(false);
|
||||
RenderRow(di, state, DT_TriangleFan, 0);
|
||||
|
||||
pe = tex->tex->GetSkyCapColor(true);
|
||||
state.SetObjectColor(pe);
|
||||
RenderRow(di, state, DT_TriangleFan, rc);
|
||||
state.EnableTexture(true);
|
||||
}
|
||||
state.SetObjectColor(0xffffffff);
|
||||
for (int i = 1; i <= vertexBuffer->mRows; i++)
|
||||
{
|
||||
RenderRow(di, state, DT_TriangleStrip, i, i == 1);
|
||||
RenderRow(di, state, DT_TriangleStrip, rc + i, false);
|
||||
}
|
||||
|
||||
state.EnableTextureMatrix(false);
|
||||
state.EnableModelMatrix(false);
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
//
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void HWSkyPortal::RenderBox(HWDrawInfo *di, FRenderState &state, FTextureID texno, FMaterial * gltex, float x_offset, bool sky2)
|
||||
{
|
||||
FSkyBox * sb = static_cast<FSkyBox*>(gltex->tex);
|
||||
int faces;
|
||||
FMaterial * tex;
|
||||
|
||||
state.EnableModelMatrix(true);
|
||||
state.mModelMatrix.loadIdentity();
|
||||
|
||||
if (!sky2)
|
||||
state.mModelMatrix.rotate(-180.0f+x_offset, di->Level->info->skyrotatevector.X, di->Level->info->skyrotatevector.Z, di->Level->info->skyrotatevector.Y);
|
||||
else
|
||||
state.mModelMatrix.rotate(-180.0f+x_offset, di->Level->info->skyrotatevector2.X, di->Level->info->skyrotatevector2.Z, di->Level->info->skyrotatevector2.Y);
|
||||
|
||||
if (sb->faces[5])
|
||||
{
|
||||
faces=4;
|
||||
|
||||
// north
|
||||
tex = FMaterial::ValidateTexture(sb->faces[0], false);
|
||||
state.SetMaterial(tex, CLAMP_XY, 0, -1);
|
||||
state.Draw(DT_TriangleStrip, vertexBuffer->FaceStart(0), 4);
|
||||
|
||||
// east
|
||||
tex = FMaterial::ValidateTexture(sb->faces[1], false);
|
||||
state.SetMaterial(tex, CLAMP_XY, 0, -1);
|
||||
state.Draw(DT_TriangleStrip, vertexBuffer->FaceStart(1), 4);
|
||||
|
||||
// south
|
||||
tex = FMaterial::ValidateTexture(sb->faces[2], false);
|
||||
state.SetMaterial(tex, CLAMP_XY, 0, -1);
|
||||
state.Draw(DT_TriangleStrip, vertexBuffer->FaceStart(2), 4);
|
||||
|
||||
// west
|
||||
tex = FMaterial::ValidateTexture(sb->faces[3], false);
|
||||
state.SetMaterial(tex, CLAMP_XY, 0, -1);
|
||||
state.Draw(DT_TriangleStrip, vertexBuffer->FaceStart(3), 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
faces=1;
|
||||
tex = FMaterial::ValidateTexture(sb->faces[0], false);
|
||||
state.SetMaterial(tex, CLAMP_XY, 0, -1);
|
||||
state.Draw(DT_TriangleStrip, vertexBuffer->FaceStart(-1), 10);
|
||||
}
|
||||
|
||||
// top
|
||||
tex = FMaterial::ValidateTexture(sb->faces[faces], false);
|
||||
state.SetMaterial(tex, CLAMP_XY, 0, -1);
|
||||
state.Draw(DT_TriangleStrip, vertexBuffer->FaceStart(sb->fliptop ? 6 : 5), 4);
|
||||
|
||||
// bottom
|
||||
tex = FMaterial::ValidateTexture(sb->faces[faces+1], false);
|
||||
state.SetMaterial(tex, CLAMP_XY, 0, -1);
|
||||
state.Draw(DT_TriangleStrip, vertexBuffer->FaceStart(4), 4);
|
||||
|
||||
state.EnableModelMatrix(false);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
//
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
void HWSkyPortal::DrawContents(HWDrawInfo *di, FRenderState &state)
|
||||
{
|
||||
bool drawBoth = false;
|
||||
auto &vp = di->Viewpoint;
|
||||
|
||||
// We have no use for Doom lighting special handling here, so disable it for this function.
|
||||
auto oldlightmode = di->lightmode;
|
||||
if (di->isSoftwareLighting())
|
||||
{
|
||||
di->SetFallbackLightMode();
|
||||
state.SetNoSoftLightLevel();
|
||||
}
|
||||
|
||||
|
||||
state.ResetColor();
|
||||
state.EnableFog(false);
|
||||
state.AlphaFunc(Alpha_GEqual, 0.f);
|
||||
state.SetRenderStyle(STYLE_Translucent);
|
||||
bool oldClamp = state.SetDepthClamp(true);
|
||||
|
||||
di->SetupView(state, 0, 0, 0, !!(mState->MirrorFlag & 1), !!(mState->PlaneMirrorFlag & 1));
|
||||
|
||||
state.SetVertexBuffer(vertexBuffer);
|
||||
if (origin->texture[0] && origin->texture[0]->tex->isSkybox())
|
||||
{
|
||||
RenderBox(di, state, origin->skytexno1, origin->texture[0], origin->x_offset[0], origin->sky2);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (origin->texture[0]==origin->texture[1] && origin->doublesky) origin->doublesky=false;
|
||||
|
||||
if (origin->texture[0])
|
||||
{
|
||||
state.SetTextureMode(TM_OPAQUE);
|
||||
RenderDome(di, state, origin->texture[0], origin->x_offset[0], origin->y_offset, origin->mirrored, FSkyVertexBuffer::SKYMODE_MAINLAYER);
|
||||
state.SetTextureMode(TM_NORMAL);
|
||||
}
|
||||
|
||||
state.AlphaFunc(Alpha_Greater, 0.f);
|
||||
|
||||
if (origin->doublesky && origin->texture[1])
|
||||
{
|
||||
RenderDome(di, state, origin->texture[1], origin->x_offset[1], origin->y_offset, false, FSkyVertexBuffer::SKYMODE_SECONDLAYER);
|
||||
}
|
||||
|
||||
if (di->Level->skyfog>0 && !di->isFullbrightScene() && (origin->fadecolor & 0xffffff) != 0)
|
||||
{
|
||||
PalEntry FadeColor = origin->fadecolor;
|
||||
FadeColor.a = clamp<int>(di->Level->skyfog, 0, 255);
|
||||
|
||||
state.EnableTexture(false);
|
||||
state.SetObjectColor(FadeColor);
|
||||
state.Draw(DT_Triangles, 0, 12);
|
||||
state.EnableTexture(true);
|
||||
state.SetObjectColor(0xffffffff);
|
||||
}
|
||||
}
|
||||
di->lightmode = oldlightmode;
|
||||
state.SetDepthClamp(oldClamp);
|
||||
}
|
||||
|
||||
const char *HWSkyPortal::GetName() { return "Sky"; }
|
193
src/rendering/hwrenderer/scene/hw_spritelight.cpp
Normal file
193
src/rendering/hwrenderer/scene/hw_spritelight.cpp
Normal file
|
@ -0,0 +1,193 @@
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Copyright(C) 2002-2016 Christoph Oelckers
|
||||
// All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with this program. If not, see http://www.gnu.org/licenses/
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
/*
|
||||
** gl_light.cpp
|
||||
** Light level / fog management / dynamic lights
|
||||
**
|
||||
*/
|
||||
|
||||
#include "c_dispatch.h"
|
||||
#include "p_local.h"
|
||||
#include "p_effect.h"
|
||||
#include "g_level.h"
|
||||
#include "g_levellocals.h"
|
||||
#include "actorinlines.h"
|
||||
#include "hwrenderer/dynlights/hw_dynlightdata.h"
|
||||
#include "hwrenderer/dynlights/hw_shadowmap.h"
|
||||
#include "hwrenderer/scene/hw_drawinfo.h"
|
||||
#include "r_data/models/models.h"
|
||||
|
||||
template<class T>
|
||||
T smoothstep(const T edge0, const T edge1, const T x)
|
||||
{
|
||||
auto t = clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0);
|
||||
return t * t * (3.0 - 2.0 * t);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Sets a single light value from all dynamic lights affecting the specified location
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void HWDrawInfo::GetDynSpriteLight(AActor *self, float x, float y, float z, FLightNode *node, int portalgroup, float *out)
|
||||
{
|
||||
FDynamicLight *light;
|
||||
float frac, lr, lg, lb;
|
||||
float radius;
|
||||
|
||||
out[0] = out[1] = out[2] = 0.f;
|
||||
// Go through both light lists
|
||||
while (node)
|
||||
{
|
||||
light=node->lightsource;
|
||||
if (light->ShouldLightActor(self))
|
||||
{
|
||||
float dist;
|
||||
FVector3 L;
|
||||
|
||||
// This is a performance critical section of code where we cannot afford to let the compiler decide whether to inline the function or not.
|
||||
// This will do the calculations explicitly rather than calling one of AActor's utility functions.
|
||||
if (Level->Displacements.size > 0)
|
||||
{
|
||||
int fromgroup = light->Sector->PortalGroup;
|
||||
int togroup = portalgroup;
|
||||
if (fromgroup == togroup || fromgroup == 0 || togroup == 0) goto direct;
|
||||
|
||||
DVector2 offset = Level->Displacements.getOffset(fromgroup, togroup);
|
||||
L = FVector3(x - (float)(light->X() + offset.X), y - (float)(light->Y() + offset.Y), z - (float)light->Z());
|
||||
}
|
||||
else
|
||||
{
|
||||
direct:
|
||||
L = FVector3(x - (float)light->X(), y - (float)light->Y(), z - (float)light->Z());
|
||||
}
|
||||
|
||||
dist = (float)L.LengthSquared();
|
||||
radius = light->GetRadius();
|
||||
|
||||
if (dist < radius * radius)
|
||||
{
|
||||
dist = sqrtf(dist); // only calculate the square root if we really need it.
|
||||
|
||||
frac = 1.0f - (dist / radius);
|
||||
|
||||
if (light->IsSpot())
|
||||
{
|
||||
L *= -1.0f / dist;
|
||||
DAngle negPitch = -*light->pPitch;
|
||||
DAngle Angle = light->target->Angles.Yaw;
|
||||
double xyLen = negPitch.Cos();
|
||||
double spotDirX = -Angle.Cos() * xyLen;
|
||||
double spotDirY = -Angle.Sin() * xyLen;
|
||||
double spotDirZ = -negPitch.Sin();
|
||||
double cosDir = L.X * spotDirX + L.Y * spotDirY + L.Z * spotDirZ;
|
||||
frac *= (float)smoothstep(light->pSpotOuterAngle->Cos(), light->pSpotInnerAngle->Cos(), cosDir);
|
||||
}
|
||||
|
||||
if (frac > 0 && (!light->shadowmapped || screen->mShadowMap.ShadowTest(light, { x, y, z })))
|
||||
{
|
||||
lr = light->GetRed() / 255.0f;
|
||||
lg = light->GetGreen() / 255.0f;
|
||||
lb = light->GetBlue() / 255.0f;
|
||||
if (light->IsSubtractive())
|
||||
{
|
||||
float bright = (float)FVector3(lr, lg, lb).Length();
|
||||
FVector3 lightColor(lr, lg, lb);
|
||||
lr = (bright - lr) * -1;
|
||||
lg = (bright - lg) * -1;
|
||||
lb = (bright - lb) * -1;
|
||||
}
|
||||
|
||||
out[0] += lr * frac;
|
||||
out[1] += lg * frac;
|
||||
out[2] += lb * frac;
|
||||
}
|
||||
}
|
||||
}
|
||||
node = node->nextLight;
|
||||
}
|
||||
}
|
||||
|
||||
void HWDrawInfo::GetDynSpriteLight(AActor *thing, particle_t *particle, float *out)
|
||||
{
|
||||
if (thing != NULL)
|
||||
{
|
||||
GetDynSpriteLight(thing, (float)thing->X(), (float)thing->Y(), (float)thing->Center(), thing->section->lighthead, thing->Sector->PortalGroup, out);
|
||||
}
|
||||
else if (particle != NULL)
|
||||
{
|
||||
GetDynSpriteLight(NULL, (float)particle->Pos.X, (float)particle->Pos.Y, (float)particle->Pos.Z, particle->subsector->section->lighthead, particle->subsector->sector->PortalGroup, out);
|
||||
}
|
||||
}
|
||||
|
||||
// static so that we build up a reserve (memory allocations stop)
|
||||
// For multithread processing each worker thread needs its own copy, though.
|
||||
static thread_local TArray<FDynamicLight*> addedLightsArray;
|
||||
|
||||
void hw_GetDynModelLight(AActor *self, FDynLightData &modellightdata)
|
||||
{
|
||||
modellightdata.Clear();
|
||||
|
||||
if (self)
|
||||
{
|
||||
auto &addedLights = addedLightsArray; // avoid going through the thread local storage for each use.
|
||||
|
||||
addedLights.Clear();
|
||||
|
||||
float x = (float)self->X();
|
||||
float y = (float)self->Y();
|
||||
float z = (float)self->Center();
|
||||
float radiusSquared = (float)(self->renderradius * self->renderradius);
|
||||
dl_validcount++;
|
||||
|
||||
BSPWalkCircle(self->Level, x, y, radiusSquared, [&](subsector_t *subsector) // Iterate through all subsectors potentially touched by actor
|
||||
{
|
||||
auto section = subsector->section;
|
||||
if (section->validcount == dl_validcount) return; // already done from a previous subsector.
|
||||
FLightNode * node = section->lighthead;
|
||||
while (node) // check all lights touching a subsector
|
||||
{
|
||||
FDynamicLight *light = node->lightsource;
|
||||
if (light->ShouldLightActor(self))
|
||||
{
|
||||
int group = subsector->sector->PortalGroup;
|
||||
DVector3 pos = light->PosRelative(group);
|
||||
float radius = (float)(light->GetRadius() + self->renderradius);
|
||||
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
|
||||
{
|
||||
modellightdata.AddLightToList(group, light, true);
|
||||
addedLights.Push(light);
|
||||
}
|
||||
}
|
||||
}
|
||||
node = node->nextLight;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
1301
src/rendering/hwrenderer/scene/hw_sprites.cpp
Normal file
1301
src/rendering/hwrenderer/scene/hw_sprites.cpp
Normal file
File diff suppressed because it is too large
Load diff
31
src/rendering/hwrenderer/scene/hw_viewpointuniforms.h
Normal file
31
src/rendering/hwrenderer/scene/hw_viewpointuniforms.h
Normal file
|
@ -0,0 +1,31 @@
|
|||
#pragma once
|
||||
|
||||
#include "r_data/matrix.h"
|
||||
#include "r_utility.h"
|
||||
|
||||
struct HWViewpointUniforms
|
||||
{
|
||||
VSMatrix mProjectionMatrix;
|
||||
VSMatrix mViewMatrix;
|
||||
VSMatrix mNormalViewMatrix;
|
||||
FVector4 mCameraPos;
|
||||
FVector4 mClipLine;
|
||||
|
||||
float mGlobVis = 1.f;
|
||||
int mPalLightLevels = 0;
|
||||
int mViewHeight = 0;
|
||||
float mClipHeight = 0.f;
|
||||
float mClipHeightDirection = 0.f;
|
||||
int mShadowmapFilter = 1;
|
||||
|
||||
void CalcDependencies()
|
||||
{
|
||||
mNormalViewMatrix.computeNormalMatrix(mViewMatrix);
|
||||
}
|
||||
|
||||
void SetDefaults();
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
2209
src/rendering/hwrenderer/scene/hw_walls.cpp
Normal file
2209
src/rendering/hwrenderer/scene/hw_walls.cpp
Normal file
File diff suppressed because it is too large
Load diff
275
src/rendering/hwrenderer/scene/hw_walls_vertex.cpp
Normal file
275
src/rendering/hwrenderer/scene/hw_walls_vertex.cpp
Normal file
|
@ -0,0 +1,275 @@
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Copyright(C) 2006-2016 Christoph Oelckers
|
||||
// All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with this program. If not, see http://www.gnu.org/licenses/
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
|
||||
|
||||
#include "r_defs.h"
|
||||
#include "hwrenderer/data/flatvertices.h"
|
||||
#include "hwrenderer/scene/hw_drawinfo.h"
|
||||
#include "hwrenderer/scene/hw_drawstructs.h"
|
||||
|
||||
EXTERN_CVAR(Bool, gl_seamless)
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Split upper edge of wall
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void GLWall::SplitUpperEdge(FFlatVertex *&ptr)
|
||||
{
|
||||
side_t *sidedef = seg->sidedef;
|
||||
float polyw = glseg.fracright - glseg.fracleft;
|
||||
float facu = (tcs[UPRGT].u - tcs[UPLFT].u) / polyw;
|
||||
float facv = (tcs[UPRGT].v - tcs[UPLFT].v) / polyw;
|
||||
float fact = (ztop[1] - ztop[0]) / polyw;
|
||||
float facc = (zceil[1] - zceil[0]) / polyw;
|
||||
float facf = (zfloor[1] - zfloor[0]) / polyw;
|
||||
|
||||
for (int i = 0; i < sidedef->numsegs - 1; i++)
|
||||
{
|
||||
seg_t *cseg = sidedef->segs[i];
|
||||
float sidefrac = cseg->sidefrac;
|
||||
if (sidefrac <= glseg.fracleft) continue;
|
||||
if (sidefrac >= glseg.fracright) return;
|
||||
|
||||
float fracfac = sidefrac - glseg.fracleft;
|
||||
|
||||
ptr->x = cseg->v2->fX();
|
||||
ptr->y = cseg->v2->fY();
|
||||
ptr->z = ztop[0] + fact * fracfac;
|
||||
ptr->u = tcs[UPLFT].u + facu * fracfac;
|
||||
ptr->v = tcs[UPLFT].v + facv * fracfac;
|
||||
ptr++;
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Split upper edge of wall
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void GLWall::SplitLowerEdge(FFlatVertex *&ptr)
|
||||
{
|
||||
side_t *sidedef = seg->sidedef;
|
||||
float polyw = glseg.fracright - glseg.fracleft;
|
||||
float facu = (tcs[LORGT].u - tcs[LOLFT].u) / polyw;
|
||||
float facv = (tcs[LORGT].v - tcs[LOLFT].v) / polyw;
|
||||
float facb = (zbottom[1] - zbottom[0]) / polyw;
|
||||
float facc = (zceil[1] - zceil[0]) / polyw;
|
||||
float facf = (zfloor[1] - zfloor[0]) / polyw;
|
||||
|
||||
for (int i = sidedef->numsegs - 2; i >= 0; i--)
|
||||
{
|
||||
seg_t *cseg = sidedef->segs[i];
|
||||
float sidefrac = cseg->sidefrac;
|
||||
if (sidefrac >= glseg.fracright) continue;
|
||||
if (sidefrac <= glseg.fracleft) return;
|
||||
|
||||
float fracfac = sidefrac - glseg.fracleft;
|
||||
|
||||
ptr->x = cseg->v2->fX();
|
||||
ptr->y = cseg->v2->fY();
|
||||
ptr->z = zbottom[0] + facb * fracfac;
|
||||
ptr->u = tcs[LOLFT].u + facu * fracfac;
|
||||
ptr->v = tcs[LOLFT].v + facv * fracfac;
|
||||
ptr++;
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Split left edge of wall
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void GLWall::SplitLeftEdge(FFlatVertex *&ptr)
|
||||
{
|
||||
if (vertexes[0] == NULL) return;
|
||||
|
||||
vertex_t * vi = vertexes[0];
|
||||
|
||||
if (vi->numheights)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
float polyh1 = ztop[0] - zbottom[0];
|
||||
float factv1 = polyh1 ? (tcs[UPLFT].v - tcs[LOLFT].v) / polyh1 : 0;
|
||||
float factu1 = polyh1 ? (tcs[UPLFT].u - tcs[LOLFT].u) / polyh1 : 0;
|
||||
|
||||
while (i<vi->numheights && vi->heightlist[i] <= zbottom[0]) i++;
|
||||
while (i<vi->numheights && vi->heightlist[i] < ztop[0])
|
||||
{
|
||||
ptr->x = glseg.x1;
|
||||
ptr->y = glseg.y1;
|
||||
ptr->z = vi->heightlist[i];
|
||||
ptr->u = factu1*(vi->heightlist[i] - ztop[0]) + tcs[UPLFT].u;
|
||||
ptr->v = factv1*(vi->heightlist[i] - ztop[0]) + tcs[UPLFT].v;
|
||||
ptr++;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Split right edge of wall
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void GLWall::SplitRightEdge(FFlatVertex *&ptr)
|
||||
{
|
||||
if (vertexes[1] == NULL) return;
|
||||
|
||||
vertex_t * vi = vertexes[1];
|
||||
|
||||
if (vi->numheights)
|
||||
{
|
||||
int i = vi->numheights - 1;
|
||||
|
||||
float polyh2 = ztop[1] - zbottom[1];
|
||||
float factv2 = polyh2 ? (tcs[UPRGT].v - tcs[LORGT].v) / polyh2 : 0;
|
||||
float factu2 = polyh2 ? (tcs[UPRGT].u - tcs[LORGT].u) / polyh2 : 0;
|
||||
|
||||
while (i>0 && vi->heightlist[i] >= ztop[1]) i--;
|
||||
while (i>0 && vi->heightlist[i] > zbottom[1])
|
||||
{
|
||||
ptr->x = glseg.x2;
|
||||
ptr->y = glseg.y2;
|
||||
ptr->z = vi->heightlist[i];
|
||||
ptr->u = factu2*(vi->heightlist[i] - ztop[1]) + tcs[UPRGT].u;
|
||||
ptr->v = factv2*(vi->heightlist[i] - ztop[1]) + tcs[UPRGT].v;
|
||||
ptr++;
|
||||
i--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Create vertices for one wall
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
int GLWall::CreateVertices(FFlatVertex *&ptr, bool split)
|
||||
{
|
||||
auto oo = ptr;
|
||||
ptr->Set(glseg.x1, zbottom[0], glseg.y1, tcs[LOLFT].u, tcs[LOLFT].v);
|
||||
ptr++;
|
||||
if (split && glseg.fracleft == 0) SplitLeftEdge(ptr);
|
||||
ptr->Set(glseg.x1, ztop[0], glseg.y1, tcs[UPLFT].u, tcs[UPLFT].v);
|
||||
ptr++;
|
||||
if (split && !(flags & GLWF_NOSPLITUPPER && seg->sidedef->numsegs > 1)) SplitUpperEdge(ptr);
|
||||
ptr->Set(glseg.x2, ztop[1], glseg.y2, tcs[UPRGT].u, tcs[UPRGT].v);
|
||||
ptr++;
|
||||
if (split && glseg.fracright == 1) SplitRightEdge(ptr);
|
||||
ptr->Set(glseg.x2, zbottom[1], glseg.y2, tcs[LORGT].u, tcs[LORGT].v);
|
||||
ptr++;
|
||||
if (split && !(flags & GLWF_NOSPLITLOWER) && seg->sidedef->numsegs > 1) SplitLowerEdge(ptr);
|
||||
return int(ptr - oo);
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Split left edge of wall
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void GLWall::CountLeftEdge(unsigned &ptr)
|
||||
{
|
||||
if (vertexes[0] == NULL) return;
|
||||
|
||||
vertex_t * vi = vertexes[0];
|
||||
|
||||
if (vi->numheights)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
while (i<vi->numheights && vi->heightlist[i] <= zbottom[0]) i++;
|
||||
while (i<vi->numheights && vi->heightlist[i] < ztop[0])
|
||||
{
|
||||
ptr++;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Split right edge of wall
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void GLWall::CountRightEdge(unsigned &ptr)
|
||||
{
|
||||
if (vertexes[1] == NULL) return;
|
||||
|
||||
vertex_t * vi = vertexes[1];
|
||||
|
||||
if (vi->numheights)
|
||||
{
|
||||
int i = vi->numheights - 1;
|
||||
|
||||
while (i>0 && vi->heightlist[i] >= ztop[1]) i--;
|
||||
while (i>0 && vi->heightlist[i] > zbottom[1])
|
||||
{
|
||||
ptr++;
|
||||
i--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
int GLWall::CountVertices()
|
||||
{
|
||||
unsigned ptr = 4;
|
||||
if (glseg.fracleft == 0) CountLeftEdge(ptr);
|
||||
if (glseg.fracright == 1) CountRightEdge(ptr);
|
||||
// This may allocate a few vertices too many in case of a split linedef but this is a rare case that isn't worth the required overhead for a precise calculation.
|
||||
if (!(flags & GLWF_NOSPLITUPPER)) ptr += seg->sidedef->numsegs - 1;
|
||||
if (!(flags & GLWF_NOSPLITLOWER)) ptr += seg->sidedef->numsegs - 1;
|
||||
return (int)ptr;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// build the vertices for this wall
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void GLWall::MakeVertices(HWDrawInfo *di, bool nosplit)
|
||||
{
|
||||
if (vertcount == 0)
|
||||
{
|
||||
bool split = (gl_seamless && !nosplit && seg->sidedef != nullptr && !(seg->sidedef->Flags & WALLF_POLYOBJ) && !(flags & GLWF_NOSPLIT));
|
||||
auto ret = screen->mVertexData->AllocVertices(split ? CountVertices() : 4);
|
||||
vertindex = ret.second;
|
||||
vertcount = CreateVertices(ret.first, split);
|
||||
}
|
||||
}
|
||||
|
608
src/rendering/hwrenderer/scene/hw_weapon.cpp
Normal file
608
src/rendering/hwrenderer/scene/hw_weapon.cpp
Normal file
|
@ -0,0 +1,608 @@
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Copyright(C) 2000-2016 Christoph Oelckers
|
||||
// All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with this program. If not, see http://www.gnu.org/licenses/
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
/*
|
||||
** hw_weapon.cpp
|
||||
** Weapon sprite utilities
|
||||
**
|
||||
*/
|
||||
|
||||
#include "sbar.h"
|
||||
#include "r_utility.h"
|
||||
#include "v_video.h"
|
||||
#include "doomstat.h"
|
||||
#include "d_player.h"
|
||||
#include "g_levellocals.h"
|
||||
#include "r_data/models/models.h"
|
||||
#include "hw_weapon.h"
|
||||
#include "hw_fakeflat.h"
|
||||
|
||||
#include "hwrenderer/models/hw_models.h"
|
||||
#include "hwrenderer/dynlights/hw_dynlightdata.h"
|
||||
#include "hwrenderer/textures/hw_material.h"
|
||||
#include "hwrenderer/utility/hw_lighting.h"
|
||||
#include "hwrenderer/utility/hw_cvars.h"
|
||||
#include "hwrenderer/scene/hw_drawinfo.h"
|
||||
#include "hwrenderer/scene/hw_drawstructs.h"
|
||||
#include "hwrenderer/data/flatvertices.h"
|
||||
#include "hwrenderer/dynlights/hw_lightbuffer.h"
|
||||
#include "hw_renderstate.h"
|
||||
|
||||
EXTERN_CVAR(Float, transsouls)
|
||||
EXTERN_CVAR(Int, gl_fuzztype)
|
||||
EXTERN_CVAR(Bool, r_drawplayersprites)
|
||||
EXTERN_CVAR(Bool, r_deathcamera)
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// R_DrawPSprite
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void HWDrawInfo::DrawPSprite(HUDSprite *huds, FRenderState &state)
|
||||
{
|
||||
if (huds->RenderStyle.BlendOp == STYLEOP_Shadow)
|
||||
{
|
||||
state.SetColor(0.2f, 0.2f, 0.2f, 0.33f, huds->cm.Desaturation);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetColor(state, huds->lightlevel, 0, isFullbrightScene(), huds->cm, huds->alpha, true);
|
||||
}
|
||||
state.SetLightIndex(-1);
|
||||
state.SetRenderStyle(huds->RenderStyle);
|
||||
state.SetTextureMode(huds->RenderStyle);
|
||||
state.SetObjectColor(huds->ObjectColor);
|
||||
if (huds->owner->Sector)
|
||||
{
|
||||
state.SetAddColor(huds->owner->Sector->AdditiveColors[sector_t::sprites] | 0xff000000);
|
||||
}
|
||||
else
|
||||
{
|
||||
state.SetAddColor(0);
|
||||
}
|
||||
state.SetDynLight(huds->dynrgb[0], huds->dynrgb[1], huds->dynrgb[2]);
|
||||
state.EnableBrightmap(!(huds->RenderStyle.Flags & STYLEF_ColorIsFixed));
|
||||
|
||||
if (huds->mframe)
|
||||
{
|
||||
state.AlphaFunc(Alpha_GEqual, 0);
|
||||
|
||||
FGLModelRenderer renderer(this, state, huds->lightindex);
|
||||
renderer.RenderHUDModel(huds->weapon, huds->mx, huds->my);
|
||||
state.SetVertexBuffer(screen->mVertexData);
|
||||
}
|
||||
else
|
||||
{
|
||||
float thresh = (huds->tex->tex->GetTranslucency() || huds->OverrideShader != -1) ? 0.f : gl_mask_sprite_threshold;
|
||||
state.AlphaFunc(Alpha_GEqual, thresh);
|
||||
state.SetMaterial(huds->tex, CLAMP_XY_NOMIP, 0, huds->OverrideShader);
|
||||
state.Draw(DT_TriangleStrip, huds->mx, 4);
|
||||
}
|
||||
|
||||
state.SetTextureMode(TM_NORMAL);
|
||||
state.AlphaFunc(Alpha_GEqual, gl_mask_sprite_threshold);
|
||||
state.SetObjectColor(0xffffffff);
|
||||
state.SetAddColor(0);
|
||||
state.SetDynLight(0, 0, 0);
|
||||
state.EnableBrightmap(false);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// R_DrawPlayerSprites
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void HWDrawInfo::DrawPlayerSprites(bool hudModelStep, FRenderState &state)
|
||||
{
|
||||
auto oldlightmode = lightmode;
|
||||
if (!hudModelStep && isSoftwareLighting()) SetFallbackLightMode(); // Software lighting cannot handle 2D content.
|
||||
for (auto &hudsprite : hudsprites)
|
||||
{
|
||||
if ((!!hudsprite.mframe) == hudModelStep)
|
||||
DrawPSprite(&hudsprite, state);
|
||||
}
|
||||
lightmode = oldlightmode;
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
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.GetTexture(lump, true);
|
||||
if (tex) disablefullbright = tex->isFullbrightDisabled();
|
||||
}
|
||||
return psp->GetState()->GetFullbright() && !disablefullbright;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Weapon position
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
static WeaponPosition GetWeaponPosition(player_t *player, double ticFrac)
|
||||
{
|
||||
WeaponPosition w;
|
||||
P_BobWeapon(player, &w.bobx, &w.boby, ticFrac);
|
||||
|
||||
// Interpolate the main weapon layer once so as to be able to add it to other layers.
|
||||
if ((w.weapon = player->FindPSprite(PSP_WEAPON)) != nullptr)
|
||||
{
|
||||
if (w.weapon->firstTic)
|
||||
{
|
||||
w.wx = (float)w.weapon->x;
|
||||
w.wy = (float)w.weapon->y;
|
||||
}
|
||||
else
|
||||
{
|
||||
w.wx = (float)(w.weapon->oldx + (w.weapon->x - w.weapon->oldx) * ticFrac);
|
||||
w.wy = (float)(w.weapon->oldy + (w.weapon->y - w.weapon->oldy) * ticFrac);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
w.wx = 0;
|
||||
w.wy = 0;
|
||||
}
|
||||
return w;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Bobbing
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
static FVector2 BobWeapon(WeaponPosition &weap, DPSprite *psp, double ticFrac)
|
||||
{
|
||||
if (psp->firstTic)
|
||||
{ // Can't interpolate the first tic.
|
||||
psp->firstTic = false;
|
||||
psp->oldx = psp->x;
|
||||
psp->oldy = psp->y;
|
||||
}
|
||||
|
||||
float sx = float(psp->oldx + (psp->x - psp->oldx) * ticFrac);
|
||||
float sy = float(psp->oldy + (psp->y - psp->oldy) * ticFrac);
|
||||
|
||||
if (psp->Flags & PSPF_ADDBOB)
|
||||
{
|
||||
sx += (psp->Flags & PSPF_MIRROR) ? -weap.bobx : weap.bobx;
|
||||
sy += weap.boby;
|
||||
}
|
||||
|
||||
if (psp->Flags & PSPF_ADDWEAPON && psp->GetID() != PSP_WEAPON)
|
||||
{
|
||||
sx += weap.wx;
|
||||
sy += weap.wy;
|
||||
}
|
||||
return { sx, sy };
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Lighting
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
WeaponLighting HWDrawInfo::GetWeaponLighting(sector_t *viewsector, const DVector3 &pos, int cm, area_t in_area, const DVector3 &playerpos)
|
||||
{
|
||||
WeaponLighting l;
|
||||
|
||||
if (cm)
|
||||
{
|
||||
l.lightlevel = 255;
|
||||
l.cm.Clear();
|
||||
l.isbelow = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto fakesec = hw_FakeFlat(viewsector, in_area, false);
|
||||
|
||||
// calculate light level for weapon sprites
|
||||
l.lightlevel = hw_ClampLight(fakesec->lightlevel);
|
||||
|
||||
// calculate colormap for weapon sprites
|
||||
if (viewsector->e->XFloor.ffloors.Size() && !(Level->flags3 & LEVEL3_NOCOLOREDSPRITELIGHTING))
|
||||
{
|
||||
TArray<lightlist_t> & lightlist = viewsector->e->XFloor.lightlist;
|
||||
for (unsigned i = 0; i<lightlist.Size(); i++)
|
||||
{
|
||||
double lightbottom;
|
||||
|
||||
if (i<lightlist.Size() - 1)
|
||||
{
|
||||
lightbottom = lightlist[i + 1].plane.ZatPoint(pos);
|
||||
}
|
||||
else
|
||||
{
|
||||
lightbottom = viewsector->floorplane.ZatPoint(pos);
|
||||
}
|
||||
|
||||
if (lightbottom < pos.Z)
|
||||
{
|
||||
l.cm = lightlist[i].extra_colormap;
|
||||
l.lightlevel = hw_ClampLight(*lightlist[i].p_lightlevel);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
l.cm = fakesec->Colormap;
|
||||
if (Level->flags3 & LEVEL3_NOCOLOREDSPRITELIGHTING) l.cm.ClearColor();
|
||||
}
|
||||
|
||||
l.lightlevel = CalcLightLevel(l.lightlevel, getExtraLight(), true, 0);
|
||||
|
||||
if (isSoftwareLighting() || l.lightlevel < 92)
|
||||
{
|
||||
// Korshun: the way based on max possible light level for sector like in software renderer.
|
||||
double min_L = 36.0 / 31.0 - ((l.lightlevel / 255.0) * (63.0 / 31.0)); // Lightlevel in range 0-63
|
||||
if (min_L < 0)
|
||||
min_L = 0;
|
||||
else if (min_L > 1.0)
|
||||
min_L = 1.0;
|
||||
|
||||
l.lightlevel = int((1.0 - min_L) * 255);
|
||||
}
|
||||
else
|
||||
{
|
||||
l.lightlevel = (2 * l.lightlevel + 255) / 3;
|
||||
}
|
||||
l.lightlevel = viewsector->CheckSpriteGlow(l.lightlevel, playerpos);
|
||||
l.isbelow = fakesec != viewsector && in_area == area_below;
|
||||
}
|
||||
|
||||
// Korshun: fullbright fog in opengl, render weapon sprites fullbright (but don't cancel out the light color!)
|
||||
if (Level->brightfog && ((Level->flags&LEVEL_HASFADETABLE) || l.cm.FadeColor != 0))
|
||||
{
|
||||
l.lightlevel = 255;
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void HUDSprite::SetBright(bool isbelow)
|
||||
{
|
||||
if (!isbelow)
|
||||
{
|
||||
cm.MakeWhite();
|
||||
}
|
||||
else
|
||||
{
|
||||
// under water areas keep most of their color for fullbright objects
|
||||
cm.LightColor.r = (3 * cm.LightColor.r + 0xff) / 4;
|
||||
cm.LightColor.g = (3 * cm.LightColor.g + 0xff) / 4;
|
||||
cm.LightColor.b = (3 * cm.LightColor.b + 0xff) / 4;
|
||||
}
|
||||
lightlevel = 255;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Render Style
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
bool HUDSprite::GetWeaponRenderStyle(DPSprite *psp, AActor *playermo, sector_t *viewsector, WeaponLighting &lighting)
|
||||
{
|
||||
auto rs = psp->GetRenderStyle(playermo->RenderStyle, playermo->Alpha);
|
||||
|
||||
visstyle_t vis;
|
||||
|
||||
vis.RenderStyle = STYLE_Count;
|
||||
vis.Alpha = rs.second;
|
||||
vis.Invert = false;
|
||||
playermo->AlterWeaponSprite(&vis);
|
||||
|
||||
alpha = (psp->Flags & PSPF_FORCEALPHA) ? 0.f : vis.Alpha;
|
||||
|
||||
if (vis.RenderStyle != STYLE_Count && !(psp->Flags & PSPF_FORCESTYLE))
|
||||
{
|
||||
RenderStyle = vis.RenderStyle;
|
||||
}
|
||||
else
|
||||
{
|
||||
RenderStyle = rs.first;
|
||||
}
|
||||
if (RenderStyle.BlendOp == STYLEOP_None) return false;
|
||||
|
||||
if (vis.Invert)
|
||||
{
|
||||
// this only happens for Strife's inverted weapon sprite
|
||||
RenderStyle.Flags |= STYLEF_InvertSource;
|
||||
}
|
||||
|
||||
// Set the render parameters
|
||||
|
||||
OverrideShader = -1;
|
||||
if (RenderStyle.BlendOp == STYLEOP_Fuzz)
|
||||
{
|
||||
if (gl_fuzztype != 0)
|
||||
{
|
||||
// Todo: implement shader selection here
|
||||
RenderStyle = LegacyRenderStyles[STYLE_Translucent];
|
||||
OverrideShader = SHADER_NoTexture + gl_fuzztype;
|
||||
alpha = 0.99f; // trans may not be 1 here
|
||||
}
|
||||
else
|
||||
{
|
||||
RenderStyle.BlendOp = STYLEOP_Shadow;
|
||||
}
|
||||
}
|
||||
|
||||
if (RenderStyle.Flags & STYLEF_TransSoulsAlpha)
|
||||
{
|
||||
alpha = transsouls;
|
||||
}
|
||||
else if (RenderStyle.Flags & STYLEF_Alpha1)
|
||||
{
|
||||
alpha = 1.f;
|
||||
}
|
||||
else if (alpha == 0.f)
|
||||
{
|
||||
alpha = vis.Alpha;
|
||||
}
|
||||
if (!RenderStyle.IsVisible(alpha)) return false; // if it isn't visible skip the rest.
|
||||
|
||||
PalEntry ThingColor = (playermo->RenderStyle.Flags & STYLEF_ColorIsFixed) ? playermo->fillcolor : 0xffffff;
|
||||
ThingColor.a = 255;
|
||||
|
||||
const bool bright = isBright(psp);
|
||||
ObjectColor = bright ? ThingColor : ThingColor.Modulate(viewsector->SpecialColors[sector_t::sprites]);
|
||||
|
||||
lightlevel = lighting.lightlevel;
|
||||
cm = lighting.cm;
|
||||
if (bright) SetBright(lighting.isbelow);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Coordinates
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
bool HUDSprite::GetWeaponRect(HWDrawInfo *di, DPSprite *psp, float sx, float sy, player_t *player)
|
||||
{
|
||||
float tx;
|
||||
float scale;
|
||||
float scalex;
|
||||
float ftexturemid;
|
||||
|
||||
// decide which patch to use
|
||||
bool mirror;
|
||||
FTextureID lump = sprites[psp->GetSprite()].GetSpriteFrame(psp->GetFrame(), 0, 0., &mirror);
|
||||
if (!lump.isValid()) return false;
|
||||
|
||||
FMaterial * tex = FMaterial::ValidateTexture(lump, true, false);
|
||||
if (!tex) return false;
|
||||
|
||||
float vw = (float)viewwidth;
|
||||
float vh = (float)viewheight;
|
||||
|
||||
FloatRect r;
|
||||
tex->GetSpriteRect(&r);
|
||||
|
||||
// calculate edges of the shape
|
||||
scalex = (320.0f / (240.0f * r_viewwindow.WidescreenRatio)) * vw / 320;
|
||||
|
||||
float x1, y1, x2, y2, u1, v1, u2, v2;
|
||||
|
||||
tx = (psp->Flags & PSPF_MIRROR) ? ((160 - r.width) - (sx + r.left)) : (sx - (160 - r.left));
|
||||
x1 = tx * scalex + vw / 2;
|
||||
if (x1 > vw) return false; // off the right side
|
||||
x1 += viewwindowx;
|
||||
|
||||
|
||||
tx += r.width;
|
||||
x2 = tx * scalex + vw / 2;
|
||||
if (x2 < 0) return false; // off the left side
|
||||
x2 += viewwindowx;
|
||||
|
||||
// killough 12/98: fix psprite positioning problem
|
||||
ftexturemid = 100.f - sy - r.top - psp->GetYAdjust(screenblocks >= 11);
|
||||
|
||||
scale = (SCREENHEIGHT*vw) / (SCREENWIDTH * 200.0f);
|
||||
y1 = viewwindowy + vh / 2 - (ftexturemid * scale);
|
||||
y2 = y1 + (r.height * scale) + 1;
|
||||
|
||||
|
||||
if (!(mirror) != !(psp->Flags & (PSPF_FLIP)))
|
||||
{
|
||||
u2 = tex->GetSpriteUL();
|
||||
v1 = tex->GetSpriteVT();
|
||||
u1 = tex->GetSpriteUR();
|
||||
v2 = tex->GetSpriteVB();
|
||||
}
|
||||
else
|
||||
{
|
||||
u1 = tex->GetSpriteUL();
|
||||
v1 = tex->GetSpriteVT();
|
||||
u2 = tex->GetSpriteUR();
|
||||
v2 = tex->GetSpriteVB();
|
||||
}
|
||||
|
||||
auto verts = screen->mVertexData->AllocVertices(4);
|
||||
mx = verts.second;
|
||||
|
||||
verts.first[0].Set(x1, y1, 0, u1, v1);
|
||||
verts.first[1].Set(x1, y2, 0, u1, v2);
|
||||
verts.first[2].Set(x2, y1, 0, u2, v1);
|
||||
verts.first[3].Set(x2, y2, 0, u2, v2);
|
||||
|
||||
this->tex = tex;
|
||||
return true;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// R_DrawPlayerSprites
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void HWDrawInfo::PreparePlayerSprites(sector_t * viewsector, area_t in_area)
|
||||
{
|
||||
|
||||
bool brightflash = false;
|
||||
AActor * playermo = players[consoleplayer].camera;
|
||||
player_t * player = playermo->player;
|
||||
|
||||
const auto &vp = Viewpoint;
|
||||
|
||||
AActor *camera = vp.camera;
|
||||
|
||||
// this is the same as the software renderer
|
||||
if (!player ||
|
||||
!r_drawplayersprites ||
|
||||
!camera->player ||
|
||||
(player->cheats & CF_CHASECAM) ||
|
||||
(r_deathcamera && camera->health <= 0))
|
||||
return;
|
||||
|
||||
const bool hudModelStep = IsHUDModelForPlayerAvailable(camera->player);
|
||||
WeaponPosition weap = GetWeaponPosition(camera->player, vp.TicFrac);
|
||||
WeaponLighting light = GetWeaponLighting(viewsector, vp.Pos, isFullbrightScene(), in_area, camera->Pos());
|
||||
|
||||
// hack alert! Rather than changing everything in the underlying lighting code let's just temporarily change
|
||||
// light mode here to draw the weapon sprite.
|
||||
auto oldlightmode = lightmode;
|
||||
if (isSoftwareLighting()) SetFallbackLightMode();
|
||||
|
||||
for (DPSprite *psp = player->psprites; psp != nullptr && psp->GetID() < PSP_TARGETCENTER; psp = psp->GetNext())
|
||||
{
|
||||
if (!psp->GetState()) continue;
|
||||
FSpriteModelFrame *smf = playermo->player->ReadyWeapon ? FindModelFrame(playermo->player->ReadyWeapon->GetClass(), psp->GetState()->sprite, psp->GetState()->GetFrame(), false) : nullptr;
|
||||
// This is an 'either-or' proposition. This maybe needs some work to allow overlays with weapon models but as originally implemented this just won't work.
|
||||
if (smf && !hudModelStep) continue;
|
||||
if (!smf && hudModelStep) continue;
|
||||
|
||||
HUDSprite hudsprite;
|
||||
hudsprite.owner = playermo;
|
||||
hudsprite.mframe = smf;
|
||||
hudsprite.weapon = psp;
|
||||
|
||||
if (!hudsprite.GetWeaponRenderStyle(psp, camera, viewsector, light)) continue;
|
||||
|
||||
FVector2 spos = BobWeapon(weap, psp, vp.TicFrac);
|
||||
|
||||
hudsprite.dynrgb[0] = hudsprite.dynrgb[1] = hudsprite.dynrgb[2] = 0;
|
||||
hudsprite.lightindex = -1;
|
||||
// set the lighting parameters
|
||||
if (hudsprite.RenderStyle.BlendOp != STYLEOP_Shadow && Level->HasDynamicLights && !isFullbrightScene() && gl_light_sprites)
|
||||
{
|
||||
if (!hudModelStep)
|
||||
{
|
||||
GetDynSpriteLight(playermo, nullptr, hudsprite.dynrgb);
|
||||
}
|
||||
else
|
||||
{
|
||||
hw_GetDynModelLight(playermo, lightdata);
|
||||
hudsprite.lightindex = screen->mLights->UploadLights(lightdata);
|
||||
}
|
||||
}
|
||||
|
||||
// [BB] In the HUD model step we just render the model and break out.
|
||||
if (hudModelStep)
|
||||
{
|
||||
hudsprite.mx = spos.X;
|
||||
hudsprite.my = spos.Y;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!hudsprite.GetWeaponRect(this, psp, spos.X, spos.Y, player)) continue;
|
||||
}
|
||||
hudsprites.Push(hudsprite);
|
||||
}
|
||||
lightmode = oldlightmode;
|
||||
PrepareTargeterSprites();
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// R_DrawPlayerSprites
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void HWDrawInfo::PrepareTargeterSprites()
|
||||
{
|
||||
AActor * playermo = players[consoleplayer].camera;
|
||||
player_t * player = playermo->player;
|
||||
AActor *camera = Viewpoint.camera;
|
||||
|
||||
// this is the same as above
|
||||
if (!player ||
|
||||
!r_drawplayersprites ||
|
||||
!camera->player ||
|
||||
(player->cheats & CF_CHASECAM) ||
|
||||
(r_deathcamera && camera->health <= 0))
|
||||
return;
|
||||
|
||||
HUDSprite hudsprite;
|
||||
|
||||
hudsprite.owner = playermo;
|
||||
hudsprite.mframe = nullptr;
|
||||
hudsprite.cm.Clear();
|
||||
hudsprite.lightlevel = 255;
|
||||
hudsprite.ObjectColor = 0xffffffff;
|
||||
hudsprite.alpha = 1;
|
||||
hudsprite.RenderStyle = DefaultRenderStyle();
|
||||
hudsprite.OverrideShader = -1;
|
||||
hudsprite.dynrgb[0] = hudsprite.dynrgb[1] = hudsprite.dynrgb[2] = 0;
|
||||
|
||||
// The Targeter's sprites are always drawn normally.
|
||||
for (DPSprite *psp = player->FindPSprite(PSP_TARGETCENTER); psp != nullptr; psp = psp->GetNext())
|
||||
{
|
||||
if (psp->GetState() != nullptr && (psp->GetID() != PSP_TARGETCENTER || CrosshairImage == nullptr))
|
||||
{
|
||||
hudsprite.weapon = psp;
|
||||
if (hudsprite.GetWeaponRect(this, psp, psp->x, psp->y, player))
|
||||
{
|
||||
hudsprites.Push(hudsprite);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
52
src/rendering/hwrenderer/scene/hw_weapon.h
Normal file
52
src/rendering/hwrenderer/scene/hw_weapon.h
Normal file
|
@ -0,0 +1,52 @@
|
|||
#pragma once
|
||||
|
||||
#include "vectors.h"
|
||||
|
||||
class DPSprite;
|
||||
class player_t;
|
||||
class AActor;
|
||||
enum area_t : int;
|
||||
struct FSpriteModelFrame;
|
||||
struct HWDrawInfo;
|
||||
class FMaterial;
|
||||
|
||||
|
||||
struct WeaponPosition
|
||||
{
|
||||
float wx, wy;
|
||||
float bobx, boby;
|
||||
DPSprite *weapon;
|
||||
};
|
||||
|
||||
struct WeaponLighting
|
||||
{
|
||||
FColormap cm;
|
||||
int lightlevel;
|
||||
bool isbelow;
|
||||
};
|
||||
|
||||
struct HUDSprite
|
||||
{
|
||||
AActor *owner;
|
||||
DPSprite *weapon;
|
||||
FMaterial *tex;
|
||||
FSpriteModelFrame *mframe;
|
||||
|
||||
FColormap cm;
|
||||
int lightlevel;
|
||||
PalEntry ObjectColor;
|
||||
|
||||
FRenderStyle RenderStyle;
|
||||
float alpha;
|
||||
int OverrideShader;
|
||||
|
||||
float mx, my;
|
||||
float dynrgb[3];
|
||||
|
||||
int lightindex;
|
||||
|
||||
void SetBright(bool isbelow);
|
||||
bool GetWeaponRenderStyle(DPSprite *psp, AActor *playermo, sector_t *viewsector, WeaponLighting &light);
|
||||
bool GetWeaponRect(HWDrawInfo *di, DPSprite *psp, float sx, float sy, player_t *player);
|
||||
|
||||
};
|
30
src/rendering/hwrenderer/textures/hw_ihwtexture.h
Normal file
30
src/rendering/hwrenderer/textures/hw_ihwtexture.h
Normal file
|
@ -0,0 +1,30 @@
|
|||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include "tarray.h"
|
||||
|
||||
typedef TMap<int, bool> SpriteHits;
|
||||
class FTexture;
|
||||
|
||||
class IHardwareTexture
|
||||
{
|
||||
public:
|
||||
enum
|
||||
{
|
||||
MAX_TEXTURES = 16
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
|
||||
public:
|
||||
IHardwareTexture() {}
|
||||
virtual ~IHardwareTexture() {}
|
||||
|
||||
virtual void AllocateBuffer(int w, int h, int texelsize) = 0;
|
||||
virtual uint8_t *MapBuffer() = 0;
|
||||
virtual unsigned int CreateTexture(unsigned char * buffer, int w, int h, int texunit, bool mipmap, int translation, const char *name) = 0;
|
||||
|
||||
void Resize(int swidth, int sheight, int width, int height, unsigned char *src_data, unsigned char *dst_data);
|
||||
};
|
||||
|
496
src/rendering/hwrenderer/textures/hw_material.cpp
Normal file
496
src/rendering/hwrenderer/textures/hw_material.cpp
Normal file
|
@ -0,0 +1,496 @@
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Copyright(C) 2004-2016 Christoph Oelckers
|
||||
// All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with this program. If not, see http://www.gnu.org/licenses/
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
|
||||
#include "w_wad.h"
|
||||
#include "m_png.h"
|
||||
#include "sbar.h"
|
||||
#include "stats.h"
|
||||
#include "r_utility.h"
|
||||
#include "c_dispatch.h"
|
||||
#include "hw_ihwtexture.h"
|
||||
#include "hw_material.h"
|
||||
|
||||
EXTERN_CVAR(Bool, gl_texture_usehires)
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// Quick'n dirty image rescaling.
|
||||
//
|
||||
// This will only be used when the source texture is larger than
|
||||
// what the hardware can manage (extremely rare in Doom)
|
||||
//
|
||||
// Code taken from wxWidgets
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
struct BoxPrecalc
|
||||
{
|
||||
int boxStart;
|
||||
int boxEnd;
|
||||
};
|
||||
|
||||
static void ResampleBoxPrecalc(TArray<BoxPrecalc>& boxes, int oldDim)
|
||||
{
|
||||
int newDim = boxes.Size();
|
||||
const double scale_factor_1 = double(oldDim) / newDim;
|
||||
const int scale_factor_2 = (int)(scale_factor_1 / 2);
|
||||
|
||||
for (int dst = 0; dst < newDim; ++dst)
|
||||
{
|
||||
// Source pixel in the Y direction
|
||||
const int src_p = int(dst * scale_factor_1);
|
||||
|
||||
BoxPrecalc& precalc = boxes[dst];
|
||||
precalc.boxStart = clamp<int>(int(src_p - scale_factor_1 / 2.0 + 1), 0, oldDim - 1);
|
||||
precalc.boxEnd = clamp<int>(MAX<int>(precalc.boxStart + 1, int(src_p + scale_factor_2)), 0, oldDim - 1);
|
||||
}
|
||||
}
|
||||
|
||||
void IHardwareTexture::Resize(int swidth, int sheight, int width, int height, unsigned char *src_data, unsigned char *dst_data)
|
||||
{
|
||||
|
||||
// This function implements a simple pre-blur/box averaging method for
|
||||
// downsampling that gives reasonably smooth results To scale the image
|
||||
// down we will need to gather a grid of pixels of the size of the scale
|
||||
// factor in each direction and then do an averaging of the pixels.
|
||||
|
||||
TArray<BoxPrecalc> vPrecalcs(height, true);
|
||||
TArray<BoxPrecalc> hPrecalcs(width, true);
|
||||
|
||||
ResampleBoxPrecalc(vPrecalcs, sheight);
|
||||
ResampleBoxPrecalc(hPrecalcs, swidth);
|
||||
|
||||
int averaged_pixels, averaged_alpha, src_pixel_index;
|
||||
double sum_r, sum_g, sum_b, sum_a;
|
||||
|
||||
for (int y = 0; y < height; y++) // Destination image - Y direction
|
||||
{
|
||||
// Source pixel in the Y direction
|
||||
const BoxPrecalc& vPrecalc = vPrecalcs[y];
|
||||
|
||||
for (int x = 0; x < width; x++) // Destination image - X direction
|
||||
{
|
||||
// Source pixel in the X direction
|
||||
const BoxPrecalc& hPrecalc = hPrecalcs[x];
|
||||
|
||||
// Box of pixels to average
|
||||
averaged_pixels = 0;
|
||||
averaged_alpha = 0;
|
||||
sum_r = sum_g = sum_b = sum_a = 0.0;
|
||||
|
||||
for (int j = vPrecalc.boxStart; j <= vPrecalc.boxEnd; ++j)
|
||||
{
|
||||
for (int i = hPrecalc.boxStart; i <= hPrecalc.boxEnd; ++i)
|
||||
{
|
||||
// Calculate the actual index in our source pixels
|
||||
src_pixel_index = j * swidth + i;
|
||||
|
||||
int a = src_data[src_pixel_index * 4 + 3];
|
||||
if (a > 0) // do not use color from fully transparent pixels
|
||||
{
|
||||
sum_r += src_data[src_pixel_index * 4 + 0];
|
||||
sum_g += src_data[src_pixel_index * 4 + 1];
|
||||
sum_b += src_data[src_pixel_index * 4 + 2];
|
||||
sum_a += a;
|
||||
averaged_pixels++;
|
||||
}
|
||||
averaged_alpha++;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate the average from the sum and number of averaged pixels
|
||||
dst_data[0] = (unsigned char)xs_CRoundToInt(sum_r / averaged_pixels);
|
||||
dst_data[1] = (unsigned char)xs_CRoundToInt(sum_g / averaged_pixels);
|
||||
dst_data[2] = (unsigned char)xs_CRoundToInt(sum_b / averaged_pixels);
|
||||
dst_data[3] = (unsigned char)xs_CRoundToInt(sum_a / averaged_alpha);
|
||||
dst_data += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// Constructor
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
FMaterial::FMaterial(FTexture * tx, bool expanded)
|
||||
{
|
||||
mShaderIndex = SHADER_Default;
|
||||
sourcetex = tex = tx;
|
||||
|
||||
if (tx->UseType == ETextureType::SWCanvas && static_cast<FWrapperTexture*>(tx)->GetColorFormat() == 0)
|
||||
{
|
||||
mShaderIndex = SHADER_Paletted;
|
||||
}
|
||||
else if (tx->isWarped())
|
||||
{
|
||||
mShaderIndex = tx->isWarped(); // This picks SHADER_Warp1 or SHADER_Warp2
|
||||
}
|
||||
else if (tx->isHardwareCanvas())
|
||||
{
|
||||
if (tx->shaderindex >= FIRST_USER_SHADER)
|
||||
{
|
||||
mShaderIndex = tx->shaderindex;
|
||||
}
|
||||
// no brightmap for cameratexture
|
||||
}
|
||||
else
|
||||
{
|
||||
if (tx->Normal && tx->Specular)
|
||||
{
|
||||
for (auto &texture : { tx->Normal, tx->Specular })
|
||||
{
|
||||
mTextureLayers.Push(texture);
|
||||
}
|
||||
mShaderIndex = SHADER_Specular;
|
||||
}
|
||||
else if (tx->Normal && tx->Metallic && tx->Roughness && tx->AmbientOcclusion)
|
||||
{
|
||||
for (auto &texture : { tx->Normal, tx->Metallic, tx->Roughness, tx->AmbientOcclusion })
|
||||
{
|
||||
mTextureLayers.Push(texture);
|
||||
}
|
||||
mShaderIndex = SHADER_PBR;
|
||||
}
|
||||
|
||||
tx->CreateDefaultBrightmap();
|
||||
if (tx->Brightmap)
|
||||
{
|
||||
mTextureLayers.Push(tx->Brightmap);
|
||||
if (mShaderIndex == SHADER_Specular)
|
||||
mShaderIndex = SHADER_SpecularBrightmap;
|
||||
else if (mShaderIndex == SHADER_PBR)
|
||||
mShaderIndex = SHADER_PBRBrightmap;
|
||||
else
|
||||
mShaderIndex = SHADER_Brightmap;
|
||||
}
|
||||
|
||||
if (tx->shaderindex >= FIRST_USER_SHADER)
|
||||
{
|
||||
const UserShaderDesc &usershader = usershaders[tx->shaderindex - FIRST_USER_SHADER];
|
||||
if (usershader.shaderType == mShaderIndex) // Only apply user shader if it matches the expected material
|
||||
{
|
||||
for (auto &texture : tx->CustomShaderTextures)
|
||||
{
|
||||
if (texture == nullptr) continue;
|
||||
mTextureLayers.Push(texture);
|
||||
}
|
||||
mShaderIndex = tx->shaderindex;
|
||||
}
|
||||
}
|
||||
}
|
||||
mWidth = tx->GetWidth();
|
||||
mHeight = tx->GetHeight();
|
||||
mLeftOffset = tx->GetLeftOffset(0); // These only get used by decals and decals should not use renderer-specific offsets.
|
||||
mTopOffset = tx->GetTopOffset(0);
|
||||
mRenderWidth = tx->GetScaledWidth();
|
||||
mRenderHeight = tx->GetScaledHeight();
|
||||
mSpriteU[0] = mSpriteV[0] = 0.f;
|
||||
mSpriteU[1] = mSpriteV[1] = 1.f;
|
||||
|
||||
mExpanded = expanded;
|
||||
if (expanded)
|
||||
{
|
||||
int oldwidth = mWidth;
|
||||
int oldheight = mHeight;
|
||||
|
||||
mTrimResult = TrimBorders(trim); // get the trim size before adding the empty frame
|
||||
mWidth += 2;
|
||||
mHeight += 2;
|
||||
mRenderWidth = mRenderWidth * mWidth / oldwidth;
|
||||
mRenderHeight = mRenderHeight * mHeight / oldheight;
|
||||
|
||||
}
|
||||
SetSpriteRect();
|
||||
|
||||
mTextureLayers.ShrinkToFit();
|
||||
tx->Material[expanded] = this;
|
||||
if (tx->isHardwareCanvas()) tx->bTranslucent = 0;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// Destructor
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
FMaterial::~FMaterial()
|
||||
{
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// Set the sprite rectangle
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
void FMaterial::SetSpriteRect()
|
||||
{
|
||||
auto leftOffset = tex->GetLeftOffsetHW();
|
||||
auto topOffset = tex->GetTopOffsetHW();
|
||||
|
||||
float fxScale = (float)tex->Scale.X;
|
||||
float fyScale = (float)tex->Scale.Y;
|
||||
|
||||
// mSpriteRect is for positioning the sprite in the scene.
|
||||
mSpriteRect.left = -leftOffset / fxScale;
|
||||
mSpriteRect.top = -topOffset / fyScale;
|
||||
mSpriteRect.width = mWidth / fxScale;
|
||||
mSpriteRect.height = mHeight / fyScale;
|
||||
|
||||
if (mExpanded)
|
||||
{
|
||||
// a little adjustment to make sprites look better with texture filtering:
|
||||
// create a 1 pixel wide empty frame around them.
|
||||
|
||||
int oldwidth = mWidth - 2;
|
||||
int oldheight = mHeight - 2;
|
||||
|
||||
leftOffset += 1;
|
||||
topOffset += 1;
|
||||
|
||||
// Reposition the sprite with the frame considered
|
||||
mSpriteRect.left = -leftOffset / fxScale;
|
||||
mSpriteRect.top = -topOffset / fyScale;
|
||||
mSpriteRect.width = mWidth / fxScale;
|
||||
mSpriteRect.height = mHeight / fyScale;
|
||||
|
||||
if (mTrimResult)
|
||||
{
|
||||
mSpriteRect.left += trim[0] / fxScale;
|
||||
mSpriteRect.top += trim[1] / fyScale;
|
||||
|
||||
mSpriteRect.width -= (oldwidth - trim[2]) / fxScale;
|
||||
mSpriteRect.height -= (oldheight - trim[3]) / fyScale;
|
||||
|
||||
mSpriteU[0] = trim[0] / (float)mWidth;
|
||||
mSpriteV[0] = trim[1] / (float)mHeight;
|
||||
mSpriteU[1] -= (oldwidth - trim[0] - trim[2]) / (float)mWidth;
|
||||
mSpriteV[1] -= (oldheight - trim[1] - trim[3]) / (float)mHeight;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// Finds empty space around the texture.
|
||||
// Used for sprites that got placed into a huge empty frame.
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
bool FMaterial::TrimBorders(uint16_t *rect)
|
||||
{
|
||||
|
||||
auto texbuffer = sourcetex->CreateTexBuffer(0);
|
||||
int w = texbuffer.mWidth;
|
||||
int h = texbuffer.mHeight;
|
||||
auto Buffer = texbuffer.mBuffer;
|
||||
|
||||
if (texbuffer.mBuffer == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (w != mWidth || h != mHeight)
|
||||
{
|
||||
// external Hires replacements cannot be trimmed.
|
||||
return false;
|
||||
}
|
||||
|
||||
int size = w*h;
|
||||
if (size == 1)
|
||||
{
|
||||
// nothing to be done here.
|
||||
rect[0] = 0;
|
||||
rect[1] = 0;
|
||||
rect[2] = 1;
|
||||
rect[3] = 1;
|
||||
return true;
|
||||
}
|
||||
int first, last;
|
||||
|
||||
for(first = 0; first < size; first++)
|
||||
{
|
||||
if (Buffer[first*4+3] != 0) break;
|
||||
}
|
||||
if (first >= size)
|
||||
{
|
||||
// completely empty
|
||||
rect[0] = 0;
|
||||
rect[1] = 0;
|
||||
rect[2] = 1;
|
||||
rect[3] = 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
for(last = size-1; last >= first; last--)
|
||||
{
|
||||
if (Buffer[last*4+3] != 0) break;
|
||||
}
|
||||
|
||||
rect[1] = first / w;
|
||||
rect[3] = 1 + last/w - rect[1];
|
||||
|
||||
rect[0] = 0;
|
||||
rect[2] = w;
|
||||
|
||||
unsigned char *bufferoff = Buffer + (rect[1] * w * 4);
|
||||
h = rect[3];
|
||||
|
||||
for(int x = 0; x < w; x++)
|
||||
{
|
||||
for(int y = 0; y < h; y++)
|
||||
{
|
||||
if (bufferoff[(x+y*w)*4+3] != 0) goto outl;
|
||||
}
|
||||
rect[0]++;
|
||||
}
|
||||
outl:
|
||||
rect[2] -= rect[0];
|
||||
|
||||
for(int x = w-1; rect[2] > 1; x--)
|
||||
{
|
||||
for(int y = 0; y < h; y++)
|
||||
{
|
||||
if (bufferoff[(x+y*w)*4+3] != 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
rect[2]--;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
IHardwareTexture *FMaterial::GetLayer(int i, int translation, FTexture **pLayer)
|
||||
{
|
||||
FTexture *layer = i == 0 ? tex : mTextureLayers[i - 1];
|
||||
if (pLayer) *pLayer = layer;
|
||||
|
||||
if (layer && layer->UseType!=ETextureType::Null)
|
||||
{
|
||||
IHardwareTexture *hwtex = layer->SystemTextures.GetHardwareTexture(translation, mExpanded);
|
||||
if (hwtex == nullptr)
|
||||
{
|
||||
hwtex = screen->CreateHardwareTexture();
|
||||
layer->SystemTextures.AddHardwareTexture(translation, mExpanded, hwtex);
|
||||
}
|
||||
return hwtex;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//===========================================================================
|
||||
void FMaterial::Precache()
|
||||
{
|
||||
screen->PrecacheMaterial(this, 0);
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//===========================================================================
|
||||
void FMaterial::PrecacheList(SpriteHits &translations)
|
||||
{
|
||||
tex->SystemTextures.CleanUnused(translations, mExpanded);
|
||||
SpriteHits::Iterator it(translations);
|
||||
SpriteHits::Pair *pair;
|
||||
while(it.NextPair(pair)) screen->PrecacheMaterial(this, pair->Key);
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
int FMaterial::GetAreas(FloatRect **pAreas) const
|
||||
{
|
||||
if (mShaderIndex == SHADER_Default) // texture splitting can only be done if there's no attached effects
|
||||
{
|
||||
*pAreas = sourcetex->areas;
|
||||
return sourcetex->areacount;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Gets a texture from the texture manager and checks its validity for
|
||||
// GL rendering.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FMaterial * FMaterial::ValidateTexture(FTexture * tex, bool expand, bool create)
|
||||
{
|
||||
again:
|
||||
if (tex && tex->isValid())
|
||||
{
|
||||
if (tex->bNoExpand) expand = false;
|
||||
|
||||
FMaterial *hwtex = tex->Material[expand];
|
||||
if (hwtex == NULL && create)
|
||||
{
|
||||
if (expand)
|
||||
{
|
||||
if (tex->isWarped() || tex->isHardwareCanvas() || tex->shaderindex >= FIRST_USER_SHADER || (tex->shaderindex >= SHADER_Specular && tex->shaderindex <= SHADER_PBRBrightmap))
|
||||
{
|
||||
tex->bNoExpand = true;
|
||||
goto again;
|
||||
}
|
||||
if (tex->Brightmap != NULL &&
|
||||
(tex->GetWidth() != tex->Brightmap->GetWidth() ||
|
||||
tex->GetHeight() != tex->Brightmap->GetHeight())
|
||||
)
|
||||
{
|
||||
// do not expand if the brightmap's size differs.
|
||||
tex->bNoExpand = true;
|
||||
goto again;
|
||||
}
|
||||
}
|
||||
hwtex = new FMaterial(tex, expand);
|
||||
}
|
||||
return hwtex;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
FMaterial * FMaterial::ValidateTexture(FTextureID no, bool expand, bool translate, bool create)
|
||||
{
|
||||
return ValidateTexture(TexMan.GetTexture(no, translate), expand, create);
|
||||
}
|
143
src/rendering/hwrenderer/textures/hw_material.h
Normal file
143
src/rendering/hwrenderer/textures/hw_material.h
Normal file
|
@ -0,0 +1,143 @@
|
|||
|
||||
#ifndef __GL_MATERIAL_H
|
||||
#define __GL_MATERIAL_H
|
||||
|
||||
#include "m_fixed.h"
|
||||
#include "textures/textures.h"
|
||||
|
||||
#include "r_defs.h"
|
||||
|
||||
EXTERN_CVAR(Bool, gl_precache)
|
||||
|
||||
struct FRemapTable;
|
||||
class FTextureShader;
|
||||
class IHardwareTexture;
|
||||
|
||||
enum
|
||||
{
|
||||
CLAMP_NONE = 0,
|
||||
CLAMP_X = 1,
|
||||
CLAMP_Y = 2,
|
||||
CLAMP_XY = 3,
|
||||
CLAMP_XY_NOMIP = 4,
|
||||
CLAMP_NOFILTER = 5,
|
||||
CLAMP_CAMTEX = 6,
|
||||
};
|
||||
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// this is the material class for OpenGL.
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
class FMaterial
|
||||
{
|
||||
TArray<FTexture*> mTextureLayers;
|
||||
int mShaderIndex;
|
||||
|
||||
short mLeftOffset;
|
||||
short mTopOffset;
|
||||
short mWidth;
|
||||
short mHeight;
|
||||
short mRenderWidth;
|
||||
short mRenderHeight;
|
||||
bool mExpanded;
|
||||
bool mTrimResult;
|
||||
uint16_t trim[4];
|
||||
|
||||
float mSpriteU[2], mSpriteV[2];
|
||||
FloatRect mSpriteRect;
|
||||
|
||||
bool TrimBorders(uint16_t *rect);
|
||||
|
||||
public:
|
||||
FTexture *tex;
|
||||
FTexture *sourcetex; // in case of redirection this is different from tex.
|
||||
|
||||
FMaterial(FTexture *tex, bool forceexpand);
|
||||
~FMaterial();
|
||||
void SetSpriteRect();
|
||||
void Precache();
|
||||
void PrecacheList(SpriteHits &translations);
|
||||
int GetShaderIndex() const { return mShaderIndex; }
|
||||
void AddTextureLayer(FTexture *tex)
|
||||
{
|
||||
ValidateTexture(tex, false);
|
||||
mTextureLayers.Push(tex);
|
||||
}
|
||||
bool isMasked() const
|
||||
{
|
||||
return sourcetex->bMasked;
|
||||
}
|
||||
bool isExpanded() const
|
||||
{
|
||||
return mExpanded;
|
||||
}
|
||||
|
||||
int GetLayers() const
|
||||
{
|
||||
return mTextureLayers.Size() + 1;
|
||||
}
|
||||
|
||||
bool hasCanvas()
|
||||
{
|
||||
return tex->isHardwareCanvas();
|
||||
}
|
||||
|
||||
IHardwareTexture *GetLayer(int i, int translation, FTexture **pLayer = nullptr);
|
||||
|
||||
// Patch drawing utilities
|
||||
|
||||
void GetSpriteRect(FloatRect * r) const
|
||||
{
|
||||
*r = mSpriteRect;
|
||||
}
|
||||
|
||||
// This is scaled size in integer units as needed by walls and flats
|
||||
int TextureHeight() const { return mRenderHeight; }
|
||||
int TextureWidth() const { return mRenderWidth; }
|
||||
|
||||
int GetAreas(FloatRect **pAreas) const;
|
||||
|
||||
int GetWidth() const
|
||||
{
|
||||
return mWidth;
|
||||
}
|
||||
|
||||
int GetHeight() const
|
||||
{
|
||||
return mHeight;
|
||||
}
|
||||
|
||||
int GetLeftOffset() const
|
||||
{
|
||||
return mLeftOffset;
|
||||
}
|
||||
|
||||
int GetTopOffset() const
|
||||
{
|
||||
return mTopOffset;
|
||||
}
|
||||
|
||||
// Get right/bottom UV coordinates for patch drawing
|
||||
float GetUL() const { return 0; }
|
||||
float GetVT() const { return 0; }
|
||||
float GetUR() const { return 1; }
|
||||
float GetVB() const { return 1; }
|
||||
float GetU(float upix) const { return upix/(float)mWidth; }
|
||||
float GetV(float vpix) const { return vpix/(float)mHeight; }
|
||||
|
||||
float GetSpriteUL() const { return mSpriteU[0]; }
|
||||
float GetSpriteVT() const { return mSpriteV[0]; }
|
||||
float GetSpriteUR() const { return mSpriteU[1]; }
|
||||
float GetSpriteVB() const { return mSpriteV[1]; }
|
||||
|
||||
|
||||
static FMaterial *ValidateTexture(FTexture * tex, bool expand, bool create = true);
|
||||
static FMaterial *ValidateTexture(FTextureID no, bool expand, bool trans, bool create = true);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
246
src/rendering/hwrenderer/textures/hw_precache.cpp
Normal file
246
src/rendering/hwrenderer/textures/hw_precache.cpp
Normal file
|
@ -0,0 +1,246 @@
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Copyright(C) 2004-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/
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
/*
|
||||
** Texture precaching
|
||||
**
|
||||
*/
|
||||
|
||||
#include "c_cvars.h"
|
||||
#include "w_wad.h"
|
||||
#include "r_data/r_translate.h"
|
||||
#include "c_dispatch.h"
|
||||
#include "r_state.h"
|
||||
#include "actor.h"
|
||||
#include "r_data/models/models.h"
|
||||
#include "textures/skyboxtexture.h"
|
||||
#include "hwrenderer/textures/hw_material.h"
|
||||
#include "image.h"
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// DFrameBuffer :: PrecacheTexture
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
static void PrecacheTexture(FTexture *tex, int cache)
|
||||
{
|
||||
if (cache & (FTextureManager::HIT_Wall | FTextureManager::HIT_Flat | FTextureManager::HIT_Sky))
|
||||
{
|
||||
FMaterial * gltex = FMaterial::ValidateTexture(tex, false);
|
||||
if (gltex) gltex->Precache();
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// DFrameBuffer :: PrecacheSprite
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
static void PrecacheSprite(FTexture *tex, SpriteHits &hits)
|
||||
{
|
||||
FMaterial * gltex = FMaterial::ValidateTexture(tex, true);
|
||||
if (gltex) gltex->PrecacheList(hits);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// DFrameBuffer :: Precache
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void hw_PrecacheTexture(uint8_t *texhitlist, TMap<PClassActor*, bool> &actorhitlist)
|
||||
{
|
||||
SpriteHits *spritelist = new SpriteHits[sprites.Size()];
|
||||
SpriteHits **spritehitlist = new SpriteHits*[TexMan.NumTextures()];
|
||||
TMap<PClassActor*, bool>::Iterator it(actorhitlist);
|
||||
TMap<PClassActor*, bool>::Pair *pair;
|
||||
uint8_t *modellist = new uint8_t[Models.Size()];
|
||||
memset(modellist, 0, Models.Size());
|
||||
memset(spritehitlist, 0, sizeof(SpriteHits**) * TexMan.NumTextures());
|
||||
|
||||
// this isn't done by the main code so it needs to be done here first:
|
||||
// check skybox textures and mark the separate faces as used
|
||||
for (int i = 0; i<TexMan.NumTextures(); i++)
|
||||
{
|
||||
// HIT_Wall must be checked for MBF-style sky transfers.
|
||||
if (texhitlist[i] & (FTextureManager::HIT_Sky | FTextureManager::HIT_Wall))
|
||||
{
|
||||
FTexture *tex = TexMan.ByIndex(i);
|
||||
if (tex->isSkybox())
|
||||
{
|
||||
FSkyBox *sb = static_cast<FSkyBox*>(tex);
|
||||
for (int i = 0; i<6; i++)
|
||||
{
|
||||
if (sb->faces[i])
|
||||
{
|
||||
int index = sb->faces[i]->GetID().GetIndex();
|
||||
texhitlist[index] |= FTextureManager::HIT_Flat;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check all used actors.
|
||||
// 1. mark all sprites associated with its states
|
||||
// 2. mark all model data and skins associated with its states
|
||||
while (it.NextPair(pair))
|
||||
{
|
||||
PClassActor *cls = pair->Key;
|
||||
auto remap = TranslationToTable(GetDefaultByType(cls)->Translation);
|
||||
int gltrans = remap == nullptr ? 0 : remap->GetUniqueIndex();
|
||||
|
||||
for (unsigned i = 0; i < cls->GetStateCount(); i++)
|
||||
{
|
||||
auto &state = cls->GetStates()[i];
|
||||
spritelist[state.sprite].Insert(gltrans, true);
|
||||
FSpriteModelFrame * smf = FindModelFrame(cls, state.sprite, state.Frame, false);
|
||||
if (smf != NULL)
|
||||
{
|
||||
for (int i = 0; i < MAX_MODELS_PER_FRAME; i++)
|
||||
{
|
||||
if (smf->skinIDs[i].isValid())
|
||||
{
|
||||
texhitlist[smf->skinIDs[i].GetIndex()] |= FTextureManager::HIT_Flat;
|
||||
}
|
||||
else if (smf->modelIDs[i] != -1)
|
||||
{
|
||||
Models[smf->modelIDs[i]]->PushSpriteMDLFrame(smf, i);
|
||||
Models[smf->modelIDs[i]]->AddSkins(texhitlist);
|
||||
}
|
||||
if (smf->modelIDs[i] != -1)
|
||||
{
|
||||
modellist[smf->modelIDs[i]] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// mark all sprite textures belonging to the marked sprites.
|
||||
for (int i = (int)(sprites.Size() - 1); i >= 0; i--)
|
||||
{
|
||||
if (spritelist[i].CountUsed())
|
||||
{
|
||||
int j, k;
|
||||
for (j = 0; j < sprites[i].numframes; j++)
|
||||
{
|
||||
const spriteframe_t *frame = &SpriteFrames[sprites[i].spriteframes + j];
|
||||
|
||||
for (k = 0; k < 16; k++)
|
||||
{
|
||||
FTextureID pic = frame->Texture[k];
|
||||
if (pic.isValid())
|
||||
{
|
||||
spritehitlist[pic.GetIndex()] = &spritelist[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// delete everything unused before creating any new resources to avoid memory usage peaks.
|
||||
|
||||
// delete unused models
|
||||
for (unsigned i = 0; i < Models.Size(); i++)
|
||||
{
|
||||
if (!modellist[i]) Models[i]->DestroyVertexBuffer();
|
||||
}
|
||||
|
||||
// delete unused textures
|
||||
int cnt = TexMan.NumTextures();
|
||||
for (int i = cnt - 1; i >= 0; i--)
|
||||
{
|
||||
FTexture *tex = TexMan.ByIndex(i);
|
||||
if (tex != nullptr)
|
||||
{
|
||||
if (!texhitlist[i])
|
||||
{
|
||||
tex->SystemTextures.Clean(true, false);
|
||||
}
|
||||
if (spritehitlist[i] == nullptr || (*spritehitlist[i]).CountUsed() == 0)
|
||||
{
|
||||
tex->SystemTextures.Clean(false, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (gl_precache)
|
||||
{
|
||||
FImageSource::BeginPrecaching();
|
||||
|
||||
// cache all used textures
|
||||
for (int i = cnt - 1; i >= 0; i--)
|
||||
{
|
||||
FTexture *tex = TexMan.ByIndex(i);
|
||||
if (tex != nullptr && tex->GetImage() != nullptr)
|
||||
{
|
||||
if (texhitlist[i] & (FTextureManager::HIT_Wall | FTextureManager::HIT_Flat | FTextureManager::HIT_Sky))
|
||||
{
|
||||
if (tex->GetImage() && tex->SystemTextures.GetHardwareTexture(0, false) == nullptr)
|
||||
{
|
||||
FImageSource::RegisterForPrecache(tex->GetImage());
|
||||
}
|
||||
}
|
||||
|
||||
// Only register untranslated sprites. Translated ones are very unlikely to require data that can be reused.
|
||||
if (spritehitlist[i] != nullptr && (*spritehitlist[i]).CheckKey(0))
|
||||
{
|
||||
FImageSource::RegisterForPrecache(tex->GetImage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// cache all used textures
|
||||
for (int i = cnt - 1; i >= 0; i--)
|
||||
{
|
||||
FTexture *tex = TexMan.ByIndex(i);
|
||||
if (tex != nullptr)
|
||||
{
|
||||
PrecacheTexture(tex, texhitlist[i]);
|
||||
if (spritehitlist[i] != nullptr && (*spritehitlist[i]).CountUsed() > 0)
|
||||
{
|
||||
PrecacheSprite(tex, *spritehitlist[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
FImageSource::EndPrecaching();
|
||||
|
||||
// cache all used models
|
||||
FModelRenderer *renderer = screen->CreateModelRenderer(-1);
|
||||
for (unsigned i = 0; i < Models.Size(); i++)
|
||||
{
|
||||
if (modellist[i])
|
||||
Models[i]->BuildVertexBuffer(renderer);
|
||||
}
|
||||
delete renderer;
|
||||
}
|
||||
|
||||
delete[] spritehitlist;
|
||||
delete[] spritelist;
|
||||
delete[] modellist;
|
||||
}
|
||||
|
130
src/rendering/hwrenderer/textures/hw_texcontainer.h
Normal file
130
src/rendering/hwrenderer/textures/hw_texcontainer.h
Normal file
|
@ -0,0 +1,130 @@
|
|||
#pragma once
|
||||
|
||||
#include "tarray.h"
|
||||
#include "hwrenderer/textures/hw_ihwtexture.h"
|
||||
|
||||
struct FTextureBuffer;
|
||||
class IHardwareTexture;
|
||||
|
||||
class FHardwareTextureContainer
|
||||
{
|
||||
public:
|
||||
enum
|
||||
{
|
||||
MAX_TEXTURES = 16
|
||||
};
|
||||
|
||||
private:
|
||||
struct TranslatedTexture
|
||||
{
|
||||
IHardwareTexture *hwTexture = nullptr;
|
||||
int translation = 0;
|
||||
|
||||
void Delete()
|
||||
{
|
||||
if (hwTexture) delete hwTexture;
|
||||
hwTexture = nullptr;
|
||||
}
|
||||
|
||||
~TranslatedTexture()
|
||||
{
|
||||
Delete();
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
TranslatedTexture hwDefTex[2];
|
||||
TArray<TranslatedTexture> hwTex_Translated;
|
||||
|
||||
TranslatedTexture * GetTexID(int translation, bool expanded)
|
||||
{
|
||||
translation = TranslationToIndex(translation);
|
||||
if (translation == 0)
|
||||
{
|
||||
return &hwDefTex[expanded];
|
||||
}
|
||||
|
||||
if (expanded) translation = -translation;
|
||||
// normally there aren't more than very few different
|
||||
// translations here so this isn't performance critical.
|
||||
unsigned index = hwTex_Translated.FindEx([=](auto &element)
|
||||
{
|
||||
return element.translation == translation;
|
||||
});
|
||||
if (index < hwTex_Translated.Size())
|
||||
{
|
||||
return &hwTex_Translated[index];
|
||||
}
|
||||
|
||||
int add = hwTex_Translated.Reserve(1);
|
||||
auto item = &hwTex_Translated[add];
|
||||
item->translation = translation;
|
||||
return item;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
void Clean(bool cleannormal, bool cleanexpanded)
|
||||
{
|
||||
if (cleannormal) hwDefTex[0].Delete();
|
||||
if (cleanexpanded) hwDefTex[1].Delete();
|
||||
for (int i = hwTex_Translated.Size() - 1; i >= 0; i--)
|
||||
{
|
||||
if (cleannormal && hwTex_Translated[i].translation > 0) hwTex_Translated.Delete(i);
|
||||
else if (cleanexpanded && hwTex_Translated[i].translation < 0) hwTex_Translated.Delete(i);
|
||||
}
|
||||
}
|
||||
|
||||
IHardwareTexture * GetHardwareTexture(int translation, bool expanded)
|
||||
{
|
||||
auto tt = GetTexID(translation, expanded);
|
||||
return tt->hwTexture;
|
||||
}
|
||||
|
||||
void AddHardwareTexture(int translation, bool expanded, IHardwareTexture *tex)
|
||||
{
|
||||
auto tt = GetTexID(translation, expanded);
|
||||
tt->Delete();
|
||||
tt->hwTexture =tex;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// Deletes all allocated resources and considers translations
|
||||
// This will only be called for sprites
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
void CleanUnused(SpriteHits &usedtranslations, bool expanded)
|
||||
{
|
||||
if (usedtranslations.CheckKey(0) == nullptr)
|
||||
{
|
||||
hwDefTex[expanded].Delete();
|
||||
}
|
||||
int fac = expanded ? -1 : 1;
|
||||
for (int i = hwTex_Translated.Size()-1; i>= 0; i--)
|
||||
{
|
||||
if (usedtranslations.CheckKey(hwTex_Translated[i].translation * fac) == nullptr)
|
||||
{
|
||||
hwTex_Translated.Delete(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int TranslationToIndex(int translation)
|
||||
{
|
||||
if (translation <= 0)
|
||||
{
|
||||
return -translation;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto remap = TranslationToTable(translation);
|
||||
return remap == nullptr ? 0 : remap->GetUniqueIndex();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
216
src/rendering/hwrenderer/utility/hw_clock.cpp
Normal file
216
src/rendering/hwrenderer/utility/hw_clock.cpp
Normal file
|
@ -0,0 +1,216 @@
|
|||
/*
|
||||
**
|
||||
** Hardware render profiling info
|
||||
**
|
||||
**---------------------------------------------------------------------------
|
||||
** Copyright 2007-2018 Christoph Oelckers
|
||||
** All rights reserved.
|
||||
**
|
||||
** Redistribution and use in source and binary forms, with or without
|
||||
** modification, are permitted provided that the following conditions
|
||||
** are met:
|
||||
**
|
||||
** 1. Redistributions of source code must retain the above copyright
|
||||
** notice, this list of conditions and the following disclaimer.
|
||||
** 2. Redistributions in binary form must reproduce the above copyright
|
||||
** notice, this list of conditions and the following disclaimer in the
|
||||
** documentation and/or other materials provided with the distribution.
|
||||
** 3. The name of the author may not be used to endorse or promote products
|
||||
** derived from this software without specific prior written permission.
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
**---------------------------------------------------------------------------
|
||||
**
|
||||
*/
|
||||
|
||||
|
||||
#include "g_level.h"
|
||||
#include "c_console.h"
|
||||
#include "c_dispatch.h"
|
||||
#include "r_utility.h"
|
||||
#include "v_video.h"
|
||||
#include "g_levellocals.h"
|
||||
#include "hw_clock.h"
|
||||
#include "i_time.h"
|
||||
|
||||
glcycle_t RenderWall,SetupWall,ClipWall;
|
||||
glcycle_t RenderFlat,SetupFlat;
|
||||
glcycle_t RenderSprite,SetupSprite;
|
||||
glcycle_t All, Finish, PortalAll, Bsp;
|
||||
glcycle_t ProcessAll, PostProcess;
|
||||
glcycle_t RenderAll;
|
||||
glcycle_t Dirty;
|
||||
glcycle_t drawcalls;
|
||||
glcycle_t twoD, Flush3D;
|
||||
glcycle_t MTWait, WTTotal;
|
||||
int vertexcount, flatvertices, flatprimitives;
|
||||
|
||||
int rendered_lines,rendered_flats,rendered_sprites,render_vertexsplit,render_texsplit,rendered_decals, rendered_portals;
|
||||
int iter_dlightf, iter_dlight, draw_dlight, draw_dlightf;
|
||||
|
||||
void ResetProfilingData()
|
||||
{
|
||||
All.Reset();
|
||||
All.Clock();
|
||||
Bsp.Reset();
|
||||
PortalAll.Reset();
|
||||
RenderAll.Reset();
|
||||
ProcessAll.Reset();
|
||||
PostProcess.Reset();
|
||||
RenderWall.Reset();
|
||||
SetupWall.Reset();
|
||||
ClipWall.Reset();
|
||||
RenderFlat.Reset();
|
||||
SetupFlat.Reset();
|
||||
RenderSprite.Reset();
|
||||
SetupSprite.Reset();
|
||||
drawcalls.Reset();
|
||||
MTWait.Reset();
|
||||
WTTotal.Reset();
|
||||
|
||||
flatvertices=flatprimitives=vertexcount=0;
|
||||
render_texsplit=render_vertexsplit=rendered_lines=rendered_flats=rendered_sprites=rendered_decals=rendered_portals = 0;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// Rendering statistics
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
static void AppendRenderTimes(FString &str)
|
||||
{
|
||||
double setupwall = SetupWall.TimeMS();
|
||||
double clipwall = ClipWall.TimeMS();
|
||||
double bsp = Bsp.TimeMS() - ClipWall.TimeMS();
|
||||
|
||||
str.AppendFormat("BSP = %2.3f, Clip=%2.3f\n"
|
||||
"W: Render=%2.3f, Setup=%2.3f\n"
|
||||
"F: Render=%2.3f, Setup=%2.3f\n"
|
||||
"S: Render=%2.3f, Setup=%2.3f\n"
|
||||
"2D: %2.3f Finish3D: %2.3f\n"
|
||||
"Main thread total=%2.3f, Main thread waiting=%2.3f Worker thread total=%2.3f, Worker thread waiting=%2.3f\n"
|
||||
"All=%2.3f, Render=%2.3f, Setup=%2.3f, Portal=%2.3f, Drawcalls=%2.3f, Postprocess=%2.3f, Finish=%2.3f\n",
|
||||
bsp, clipwall,
|
||||
RenderWall.TimeMS(), setupwall,
|
||||
RenderFlat.TimeMS(), SetupFlat.TimeMS(),
|
||||
RenderSprite.TimeMS(), SetupSprite.TimeMS(),
|
||||
twoD.TimeMS(), Flush3D.TimeMS() - twoD.TimeMS(),
|
||||
MTWait.TimeMS() + Bsp.TimeMS(), MTWait.TimeMS(), WTTotal.TimeMS(), WTTotal.TimeMS() - setupwall - SetupFlat.TimeMS() - SetupSprite.TimeMS(),
|
||||
All.TimeMS() + Finish.TimeMS(), RenderAll.TimeMS(), ProcessAll.TimeMS(), PortalAll.TimeMS(), drawcalls.TimeMS(), PostProcess.TimeMS(), Finish.TimeMS());
|
||||
}
|
||||
|
||||
static void AppendRenderStats(FString &out)
|
||||
{
|
||||
out.AppendFormat("Walls: %d (%d splits, %d t-splits, %d vertices)\n"
|
||||
"Flats: %d (%d primitives, %d vertices)\n"
|
||||
"Sprites: %d, Decals=%d, Portals: %d\n",
|
||||
rendered_lines, render_vertexsplit, render_texsplit, vertexcount, rendered_flats, flatprimitives, flatvertices, rendered_sprites,rendered_decals, rendered_portals );
|
||||
}
|
||||
|
||||
static void AppendLightStats(FString &out)
|
||||
{
|
||||
out.AppendFormat("DLight - Walls: %d processed, %d rendered - Flats: %d processed, %d rendered\n",
|
||||
iter_dlight, draw_dlight, iter_dlightf, draw_dlightf );
|
||||
}
|
||||
|
||||
ADD_STAT(rendertimes)
|
||||
{
|
||||
static FString buff;
|
||||
static int64_t lasttime=0;
|
||||
int64_t t=I_msTime();
|
||||
if (t-lasttime>1000)
|
||||
{
|
||||
buff.Truncate(0);
|
||||
AppendRenderTimes(buff);
|
||||
lasttime=t;
|
||||
}
|
||||
return buff;
|
||||
}
|
||||
|
||||
ADD_STAT(renderstats)
|
||||
{
|
||||
FString out;
|
||||
AppendRenderStats(out);
|
||||
return out;
|
||||
}
|
||||
|
||||
ADD_STAT(lightstats)
|
||||
{
|
||||
FString out;
|
||||
AppendLightStats(out);
|
||||
return out;
|
||||
}
|
||||
|
||||
static int printstats;
|
||||
static bool switchfps;
|
||||
static uint64_t waitstart;
|
||||
EXTERN_CVAR(Bool, vid_fps)
|
||||
|
||||
void CheckBench()
|
||||
{
|
||||
if (printstats && ConsoleState == c_up)
|
||||
{
|
||||
// if we started the FPS counter ourselves or ran from the console
|
||||
// we need to wait for it to stabilize before using it.
|
||||
if (waitstart > 0 && I_msTime() - waitstart < 5000) return;
|
||||
|
||||
FString compose;
|
||||
|
||||
auto &vp = r_viewpoint;
|
||||
auto Level = vp.ViewLevel;
|
||||
compose.Format("Map %s: \"%s\",\nx = %1.4f, y = %1.4f, z = %1.4f, angle = %1.4f, pitch = %1.4f\n",
|
||||
Level->MapName.GetChars(), Level->LevelName.GetChars(), vp.Pos.X, vp.Pos.Y, vp.Pos.Z, vp.Angles.Yaw.Degrees, vp.Angles.Pitch.Degrees);
|
||||
|
||||
AppendRenderStats(compose);
|
||||
AppendRenderTimes(compose);
|
||||
AppendLightStats(compose);
|
||||
//AppendMissingTextureStats(compose);
|
||||
compose.AppendFormat("%llu fps\n\n", (unsigned long long)screen->GetLastFPS());
|
||||
|
||||
FILE *f = fopen("benchmarks.txt", "at");
|
||||
if (f != NULL)
|
||||
{
|
||||
fputs(compose.GetChars(), f);
|
||||
fclose(f);
|
||||
}
|
||||
Printf("Benchmark info saved\n");
|
||||
if (switchfps) vid_fps = false;
|
||||
printstats = false;
|
||||
}
|
||||
}
|
||||
|
||||
CCMD(bench)
|
||||
{
|
||||
printstats = true;
|
||||
if (vid_fps == 0)
|
||||
{
|
||||
vid_fps = 1;
|
||||
waitstart = I_msTime();
|
||||
switchfps = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ConsoleState == c_up) waitstart = I_msTime();
|
||||
switchfps = false;
|
||||
}
|
||||
C_HideConsole ();
|
||||
}
|
||||
|
||||
bool glcycle_t::active = false;
|
||||
|
||||
void checkBenchActive()
|
||||
{
|
||||
FStat *stat = FStat::FindStat("rendertimes");
|
||||
glcycle_t::active = ((stat != NULL && stat->isActive()) || printstats);
|
||||
}
|
||||
|
29
src/rendering/hwrenderer/utility/hw_clock.h
Normal file
29
src/rendering/hwrenderer/utility/hw_clock.h
Normal file
|
@ -0,0 +1,29 @@
|
|||
#ifndef __GL_CLOCK_H
|
||||
#define __GL_CLOCK_H
|
||||
|
||||
#include "stats.h"
|
||||
#include "x86.h"
|
||||
#include "m_fixed.h"
|
||||
|
||||
extern glcycle_t RenderWall,SetupWall,ClipWall;
|
||||
extern glcycle_t RenderFlat,SetupFlat;
|
||||
extern glcycle_t RenderSprite,SetupSprite;
|
||||
extern glcycle_t All, Finish, PortalAll, Bsp;
|
||||
extern glcycle_t ProcessAll, PostProcess;
|
||||
extern glcycle_t RenderAll;
|
||||
extern glcycle_t Dirty;
|
||||
extern glcycle_t drawcalls, twoD, Flush3D;
|
||||
extern glcycle_t MTWait, WTTotal;
|
||||
|
||||
extern int iter_dlightf, iter_dlight, draw_dlight, draw_dlightf;
|
||||
extern int rendered_lines,rendered_flats,rendered_sprites,rendered_decals,render_vertexsplit,render_texsplit;
|
||||
extern int rendered_portals;
|
||||
|
||||
extern int vertexcount, flatvertices, flatprimitives;
|
||||
|
||||
void ResetProfilingData();
|
||||
void CheckBench();
|
||||
void checkBenchActive();
|
||||
|
||||
|
||||
#endif
|
118
src/rendering/hwrenderer/utility/hw_cvars.cpp
Normal file
118
src/rendering/hwrenderer/utility/hw_cvars.cpp
Normal file
|
@ -0,0 +1,118 @@
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Copyright(C) 2005-2016 Christoph Oelckers
|
||||
// All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with this program. If not, see http://www.gnu.org/licenses/
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
|
||||
|
||||
#include "c_cvars.h"
|
||||
#include "c_dispatch.h"
|
||||
#include "v_video.h"
|
||||
#include "hw_cvars.h"
|
||||
#include "hwrenderer/textures/hw_material.h"
|
||||
#include "menu/menu.h"
|
||||
|
||||
|
||||
|
||||
// OpenGL stuff moved here
|
||||
// GL related CVARs
|
||||
CVAR(Bool, gl_portals, true, 0)
|
||||
CVAR(Bool, gl_noquery, false, 0)
|
||||
CVAR(Bool,gl_mirrors,true,0) // This is for debugging only!
|
||||
CVAR(Bool,gl_mirror_envmap, true, CVAR_GLOBALCONFIG|CVAR_ARCHIVE)
|
||||
CVAR(Bool, gl_seamless, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
|
||||
|
||||
CUSTOM_CVAR(Int, r_mirror_recursions,4,CVAR_GLOBALCONFIG|CVAR_ARCHIVE)
|
||||
{
|
||||
if (self<0) self=0;
|
||||
if (self>10) self=10;
|
||||
}
|
||||
bool gl_plane_reflection_i; // This is needed in a header that cannot include the CVAR stuff...
|
||||
CUSTOM_CVAR(Bool, gl_plane_reflection, true, CVAR_GLOBALCONFIG|CVAR_ARCHIVE)
|
||||
{
|
||||
gl_plane_reflection_i = self;
|
||||
}
|
||||
|
||||
CUSTOM_CVAR(Bool, gl_render_precise, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
|
||||
{
|
||||
gl_seamless=self;
|
||||
}
|
||||
|
||||
CVAR (Float, vid_brightness, 0.f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
|
||||
CVAR (Float, vid_contrast, 1.f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
|
||||
CVAR (Float, vid_saturation, 1.f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
|
||||
CVAR(Int, gl_satformula, 1, CVAR_ARCHIVE|CVAR_GLOBALCONFIG);
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Texture CVARs
|
||||
//
|
||||
//==========================================================================
|
||||
CUSTOM_CVAR(Float,gl_texture_filter_anisotropic,8.0f,CVAR_ARCHIVE|CVAR_GLOBALCONFIG|CVAR_NOINITCALL)
|
||||
{
|
||||
screen->TextureFilterChanged();
|
||||
}
|
||||
|
||||
CCMD(gl_flush)
|
||||
{
|
||||
TexMan.FlushAll();
|
||||
}
|
||||
|
||||
CUSTOM_CVAR(Int, gl_texture_filter, 4, CVAR_ARCHIVE|CVAR_GLOBALCONFIG|CVAR_NOINITCALL)
|
||||
{
|
||||
if (self < 0 || self > 6) self=4;
|
||||
screen->TextureFilterChanged();
|
||||
}
|
||||
|
||||
CUSTOM_CVAR(Bool, gl_texture_usehires, true, CVAR_ARCHIVE|CVAR_NOINITCALL)
|
||||
{
|
||||
TexMan.FlushAll();
|
||||
}
|
||||
|
||||
CVAR(Bool, gl_precache, false, CVAR_ARCHIVE)
|
||||
|
||||
CVAR(Bool, gl_trimsprites, true, CVAR_ARCHIVE);
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Sprite CVARs
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
CVAR(Bool, gl_usecolorblending, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
|
||||
CVAR(Bool, gl_spritebrightfog, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG);
|
||||
CVAR(Bool, gl_sprite_blend, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG);
|
||||
CVAR(Int, gl_spriteclip, 1, CVAR_ARCHIVE)
|
||||
CVAR(Float, gl_sclipthreshold, 10.0, CVAR_ARCHIVE)
|
||||
CVAR(Float, gl_sclipfactor, 1.8f, CVAR_ARCHIVE)
|
||||
CVAR(Int, gl_particles_style, 2, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) // 0 = square, 1 = round, 2 = smooth
|
||||
CVAR(Int, gl_billboard_mode, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||
CVAR(Bool, gl_billboard_faces_camera, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||
CVAR(Bool, gl_billboard_particles, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||
CVAR(Int, gl_enhanced_nv_stealth, 3, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||
CUSTOM_CVAR(Int, gl_fuzztype, 0, CVAR_ARCHIVE)
|
||||
{
|
||||
if (self < 0 || self > 8) self = 0;
|
||||
}
|
||||
|
||||
CUSTOM_CVAR(Int, gl_shadowmap_filter, 1, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||
{
|
||||
if (self < 0 || self > 8) self = 1;
|
||||
}
|
70
src/rendering/hwrenderer/utility/hw_cvars.h
Normal file
70
src/rendering/hwrenderer/utility/hw_cvars.h
Normal file
|
@ -0,0 +1,70 @@
|
|||
#pragma once
|
||||
|
||||
|
||||
#include "r_defs.h"
|
||||
#include "c_cvars.h"
|
||||
|
||||
EXTERN_CVAR(Bool,gl_enhanced_nightvision)
|
||||
EXTERN_CVAR(Int, screenblocks);
|
||||
EXTERN_CVAR(Bool, gl_texture)
|
||||
EXTERN_CVAR(Int, gl_texture_filter)
|
||||
EXTERN_CVAR(Float, gl_texture_filter_anisotropic)
|
||||
EXTERN_CVAR(Int, gl_texture_format)
|
||||
EXTERN_CVAR(Bool, gl_texture_usehires)
|
||||
EXTERN_CVAR(Bool, gl_usefb)
|
||||
|
||||
EXTERN_CVAR(Int, gl_weaponlight)
|
||||
|
||||
EXTERN_CVAR (Bool, gl_light_sprites);
|
||||
EXTERN_CVAR (Bool, gl_light_particles);
|
||||
EXTERN_CVAR (Bool, gl_light_shadowmap);
|
||||
EXTERN_CVAR (Int, gl_shadowmap_quality);
|
||||
|
||||
EXTERN_CVAR(Int, gl_fogmode)
|
||||
EXTERN_CVAR(Int, gl_lightmode)
|
||||
EXTERN_CVAR(Bool,gl_mirror_envmap)
|
||||
|
||||
EXTERN_CVAR(Bool,gl_mirrors)
|
||||
EXTERN_CVAR(Bool,gl_mirror_envmap)
|
||||
EXTERN_CVAR(Bool, gl_seamless)
|
||||
|
||||
EXTERN_CVAR(Float, gl_mask_threshold)
|
||||
EXTERN_CVAR(Float, gl_mask_sprite_threshold)
|
||||
|
||||
EXTERN_CVAR(Int, gl_multisample)
|
||||
|
||||
EXTERN_CVAR(Bool, gl_bloom)
|
||||
EXTERN_CVAR(Float, gl_bloom_amount)
|
||||
EXTERN_CVAR(Int, gl_bloom_kernel_size)
|
||||
EXTERN_CVAR(Int, gl_tonemap)
|
||||
EXTERN_CVAR(Float, gl_exposure)
|
||||
EXTERN_CVAR(Bool, gl_lens)
|
||||
EXTERN_CVAR(Float, gl_lens_k)
|
||||
EXTERN_CVAR(Float, gl_lens_kcube)
|
||||
EXTERN_CVAR(Float, gl_lens_chromatic)
|
||||
EXTERN_CVAR(Int, gl_ssao)
|
||||
EXTERN_CVAR(Int, gl_ssao_portals)
|
||||
EXTERN_CVAR(Float, gl_ssao_strength)
|
||||
EXTERN_CVAR(Int, gl_ssao_debug)
|
||||
EXTERN_CVAR(Float, gl_ssao_bias)
|
||||
EXTERN_CVAR(Float, gl_ssao_radius)
|
||||
EXTERN_CVAR(Float, gl_ssao_blur_amount)
|
||||
|
||||
EXTERN_CVAR(Int, gl_debug_level)
|
||||
EXTERN_CVAR(Bool, gl_debug_breakpoint)
|
||||
|
||||
|
||||
EXTERN_CVAR(Bool, gl_usecolorblending)
|
||||
EXTERN_CVAR(Bool, gl_spritebrightfog)
|
||||
EXTERN_CVAR(Bool, gl_sprite_blend)
|
||||
EXTERN_CVAR(Int, gl_spriteclip)
|
||||
EXTERN_CVAR(Float, gl_sclipthreshold)
|
||||
EXTERN_CVAR(Float, gl_sclipfactor)
|
||||
EXTERN_CVAR(Int, gl_particles_style)
|
||||
EXTERN_CVAR(Int, gl_billboard_mode)
|
||||
EXTERN_CVAR(Bool, gl_billboard_faces_camera)
|
||||
EXTERN_CVAR(Bool, gl_billboard_particles)
|
||||
EXTERN_CVAR(Int, gl_enhanced_nv_stealth)
|
||||
EXTERN_CVAR(Int, gl_fuzztype)
|
||||
|
||||
EXTERN_CVAR(Int, gl_shadowmap_filter)
|
221
src/rendering/hwrenderer/utility/hw_draw2d.cpp
Normal file
221
src/rendering/hwrenderer/utility/hw_draw2d.cpp
Normal file
|
@ -0,0 +1,221 @@
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Copyright(C) 2018 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/
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
/*
|
||||
** 2d drawer
|
||||
** Renderer interface
|
||||
**
|
||||
*/
|
||||
|
||||
#include "doomstat.h"
|
||||
#include "v_video.h"
|
||||
#include "cmdlib.h"
|
||||
#include "r_defs.h"
|
||||
#include "hwrenderer/data/buffers.h"
|
||||
#include "hwrenderer/data/flatvertices.h"
|
||||
#include "hwrenderer/data/hw_viewpointbuffer.h"
|
||||
#include "hwrenderer/utility/hw_clock.h"
|
||||
#include "hwrenderer/utility/hw_cvars.h"
|
||||
#include "hwrenderer/scene/hw_renderstate.h"
|
||||
#include "r_videoscale.h"
|
||||
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// Vertex buffer for 2D drawer
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
class F2DVertexBuffer
|
||||
{
|
||||
IVertexBuffer *mVertexBuffer;
|
||||
IIndexBuffer *mIndexBuffer;
|
||||
|
||||
|
||||
public:
|
||||
|
||||
F2DVertexBuffer()
|
||||
{
|
||||
mVertexBuffer = screen->CreateVertexBuffer();
|
||||
mIndexBuffer = screen->CreateIndexBuffer();
|
||||
|
||||
static const FVertexBufferAttribute format[] = {
|
||||
{ 0, VATTR_VERTEX, VFmt_Float3, (int)myoffsetof(F2DDrawer::TwoDVertex, x) },
|
||||
{ 0, VATTR_TEXCOORD, VFmt_Float2, (int)myoffsetof(F2DDrawer::TwoDVertex, u) },
|
||||
{ 0, VATTR_COLOR, VFmt_Byte4, (int)myoffsetof(F2DDrawer::TwoDVertex, color0) }
|
||||
};
|
||||
mVertexBuffer->SetFormat(1, 3, sizeof(F2DDrawer::TwoDVertex), format);
|
||||
}
|
||||
~F2DVertexBuffer()
|
||||
{
|
||||
delete mIndexBuffer;
|
||||
delete mVertexBuffer;
|
||||
}
|
||||
|
||||
void UploadData(F2DDrawer::TwoDVertex *vertices, int vertcount, int *indices, int indexcount)
|
||||
{
|
||||
mVertexBuffer->SetData(vertcount * sizeof(*vertices), vertices, false);
|
||||
mIndexBuffer->SetData(indexcount * sizeof(unsigned int), indices, false);
|
||||
}
|
||||
|
||||
std::pair<IVertexBuffer *, IIndexBuffer *> GetBufferObjects() const
|
||||
{
|
||||
return std::make_pair(mVertexBuffer, mIndexBuffer);
|
||||
}
|
||||
};
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// Draws the 2D stuff. This is the version for OpenGL 3 and later.
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
CVAR(Bool, gl_aalines, false, CVAR_ARCHIVE)
|
||||
|
||||
void Draw2D(F2DDrawer *drawer, FRenderState &state)
|
||||
{
|
||||
twoD.Clock();
|
||||
|
||||
const auto &mScreenViewport = screen->mScreenViewport;
|
||||
state.SetViewport(mScreenViewport.left, mScreenViewport.top, mScreenViewport.width, mScreenViewport.height);
|
||||
screen->mViewpoints->Set2D(state, screen->GetWidth(), screen->GetHeight());
|
||||
|
||||
state.EnableDepthTest(false);
|
||||
state.EnableMultisampling(false);
|
||||
state.EnableLineSmooth(gl_aalines);
|
||||
|
||||
auto &vertices = drawer->mVertices;
|
||||
auto &indices = drawer->mIndices;
|
||||
auto &commands = drawer->mData;
|
||||
|
||||
if (commands.Size() == 0)
|
||||
{
|
||||
twoD.Unclock();
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto &v : vertices)
|
||||
{
|
||||
// Change from BGRA to RGBA
|
||||
std::swap(v.color0.r, v.color0.b);
|
||||
}
|
||||
F2DVertexBuffer vb;
|
||||
vb.UploadData(&vertices[0], vertices.Size(), &indices[0], indices.Size());
|
||||
state.SetVertexBuffer(&vb);
|
||||
state.EnableFog(false);
|
||||
|
||||
for(auto &cmd : commands)
|
||||
{
|
||||
|
||||
int gltrans = -1;
|
||||
state.SetRenderStyle(cmd.mRenderStyle);
|
||||
state.EnableBrightmap(!(cmd.mRenderStyle.Flags & STYLEF_ColorIsFixed));
|
||||
state.EnableFog(2); // Special 2D mode 'fog'.
|
||||
|
||||
state.SetTextureMode(cmd.mDrawMode);
|
||||
|
||||
int sciX, sciY, sciW, sciH;
|
||||
if (cmd.mFlags & F2DDrawer::DTF_Scissor)
|
||||
{
|
||||
// scissor test doesn't use the current viewport for the coordinates, so use real screen coordinates
|
||||
// Note that the origin here is the lower left corner!
|
||||
sciX = screen->ScreenToWindowX(cmd.mScissor[0]);
|
||||
sciY = screen->ScreenToWindowY(cmd.mScissor[3]);
|
||||
sciW = screen->ScreenToWindowX(cmd.mScissor[2]) - sciX;
|
||||
sciH = screen->ScreenToWindowY(cmd.mScissor[1]) - sciY;
|
||||
}
|
||||
else
|
||||
{
|
||||
sciX = sciY = sciW = sciH = -1;
|
||||
}
|
||||
state.SetScissor(sciX, sciY, sciW, sciH);
|
||||
|
||||
if (cmd.mSpecialColormap[0].a != 0)
|
||||
{
|
||||
state.SetTextureMode(TM_FIXEDCOLORMAP);
|
||||
state.SetObjectColor(cmd.mSpecialColormap[0]);
|
||||
state.SetObjectColor2(cmd.mSpecialColormap[1]);
|
||||
}
|
||||
state.SetFog(cmd.mColor1, 0);
|
||||
state.SetColor(1, 1, 1, 1, cmd.mDesaturate);
|
||||
|
||||
state.AlphaFunc(Alpha_GEqual, 0.f);
|
||||
|
||||
if (cmd.mTexture != nullptr)
|
||||
{
|
||||
auto mat = FMaterial::ValidateTexture(cmd.mTexture, false);
|
||||
if (mat == nullptr) continue;
|
||||
|
||||
if (gltrans == -1 && cmd.mTranslation != nullptr) gltrans = cmd.mTranslation->GetUniqueIndex();
|
||||
state.SetMaterial(mat, cmd.mFlags & F2DDrawer::DTF_Wrap ? CLAMP_NONE : CLAMP_XY_NOMIP, -gltrans, -1);
|
||||
state.EnableTexture(true);
|
||||
|
||||
// Canvas textures are stored upside down
|
||||
if (cmd.mTexture->isHardwareCanvas())
|
||||
{
|
||||
state.mTextureMatrix.loadIdentity();
|
||||
state.mTextureMatrix.scale(1.f, -1.f, 1.f);
|
||||
state.mTextureMatrix.translate(0.f, 1.f, 0.0f);
|
||||
state.EnableTextureMatrix(true);
|
||||
}
|
||||
if (cmd.mFlags & F2DDrawer::DTF_Burn)
|
||||
{
|
||||
state.SetEffect(EFF_BURN);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
state.EnableTexture(false);
|
||||
}
|
||||
|
||||
switch (cmd.mType)
|
||||
{
|
||||
case F2DDrawer::DrawTypeTriangles:
|
||||
state.DrawIndexed(DT_Triangles, cmd.mIndexIndex, cmd.mIndexCount);
|
||||
break;
|
||||
|
||||
case F2DDrawer::DrawTypeLines:
|
||||
state.Draw(DT_Lines, cmd.mVertIndex, cmd.mVertCount);
|
||||
break;
|
||||
|
||||
case F2DDrawer::DrawTypePoints:
|
||||
state.Draw(DT_Points, cmd.mVertIndex, cmd.mVertCount);
|
||||
break;
|
||||
|
||||
}
|
||||
state.SetObjectColor(0xffffffff);
|
||||
state.SetObjectColor2(0);
|
||||
state.SetAddColor(0);
|
||||
state.EnableTextureMatrix(false);
|
||||
state.SetEffect(EFF_NONE);
|
||||
|
||||
}
|
||||
state.SetScissor(-1, -1, -1, -1);
|
||||
|
||||
state.SetRenderStyle(STYLE_Translucent);
|
||||
state.SetVertexBuffer(screen->mVertexData);
|
||||
state.EnableTexture(true);
|
||||
state.EnableBrightmap(true);
|
||||
state.SetTextureMode(TM_NORMAL);
|
||||
state.EnableFog(false);
|
||||
state.ResetColor();
|
||||
twoD.Unclock();
|
||||
}
|
289
src/rendering/hwrenderer/utility/hw_lighting.cpp
Normal file
289
src/rendering/hwrenderer/utility/hw_lighting.cpp
Normal file
|
@ -0,0 +1,289 @@
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Copyright(C) 2002-2018 Christoph Oelckers
|
||||
// All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with this program. If not, see http://www.gnu.org/licenses/
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
/*
|
||||
** gl_light.cpp
|
||||
** Light level / fog management / dynamic lights
|
||||
**
|
||||
**/
|
||||
|
||||
#include "c_cvars.h"
|
||||
#include "r_sky.h"
|
||||
#include "g_levellocals.h"
|
||||
#include "hw_lighting.h"
|
||||
#include "hwrenderer/scene/hw_drawinfo.h"
|
||||
|
||||
// externally settable lighting properties
|
||||
static float distfogtable[2][256]; // light to fog conversion table for black fog
|
||||
|
||||
CVAR(Int, gl_weaponlight, 8, CVAR_ARCHIVE);
|
||||
CVAR(Bool, gl_enhanced_nightvision, true, CVAR_ARCHIVE|CVAR_NOINITCALL)
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Sets up the fog tables
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
CUSTOM_CVAR(Int, gl_distfog, 70, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||
{
|
||||
for (int i = 0; i < 256; i++)
|
||||
{
|
||||
|
||||
if (i < 164)
|
||||
{
|
||||
distfogtable[0][i] = (float)((gl_distfog >> 1) + (gl_distfog)*(164 - i) / 164);
|
||||
}
|
||||
else if (i < 230)
|
||||
{
|
||||
distfogtable[0][i] = (float)((gl_distfog >> 1) - (gl_distfog >> 1)*(i - 164) / (230 - 164));
|
||||
}
|
||||
else distfogtable[0][i] = 0;
|
||||
|
||||
if (i < 128)
|
||||
{
|
||||
distfogtable[1][i] = 6.f + (float)((gl_distfog >> 1) + (gl_distfog)*(128 - i) / 48);
|
||||
}
|
||||
else if (i < 216)
|
||||
{
|
||||
distfogtable[1][i] = (216.f - i) / ((216.f - 128.f)) * gl_distfog / 10;
|
||||
}
|
||||
else distfogtable[1][i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
CUSTOM_CVAR(Int,gl_fogmode,1,CVAR_ARCHIVE|CVAR_NOINITCALL)
|
||||
{
|
||||
if (self>2) self=2;
|
||||
if (self<0) self=0;
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Get current light level
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
int HWDrawInfo::CalcLightLevel(int lightlevel, int rellight, bool weapon, int blendfactor)
|
||||
{
|
||||
int light;
|
||||
|
||||
if (lightlevel <= 0) return 0;
|
||||
|
||||
bool darklightmode = (isDarkLightMode()) || (isSoftwareLighting() && blendfactor > 0);
|
||||
|
||||
if (darklightmode && lightlevel < 192 && !weapon)
|
||||
{
|
||||
if (lightlevel > 100)
|
||||
{
|
||||
light = xs_CRoundToInt(192.f - (192 - lightlevel)* 1.87f);
|
||||
if (light + rellight < 20)
|
||||
{
|
||||
light = 20 + (light + rellight - 20) / 5;
|
||||
}
|
||||
else
|
||||
{
|
||||
light += rellight;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
light = (lightlevel + rellight) / 5;
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
light=lightlevel+rellight;
|
||||
}
|
||||
|
||||
// Fake contrast should never turn a positive value into 0.
|
||||
return clamp(light, 1, 255);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Get current light color
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
PalEntry HWDrawInfo::CalcLightColor(int light, PalEntry pe, int blendfactor)
|
||||
{
|
||||
int r,g,b;
|
||||
|
||||
if (blendfactor == 0)
|
||||
{
|
||||
if (isSoftwareLighting())
|
||||
{
|
||||
return pe;
|
||||
}
|
||||
|
||||
r = pe.r * light / 255;
|
||||
g = pe.g * light / 255;
|
||||
b = pe.b * light / 255;
|
||||
}
|
||||
else
|
||||
{
|
||||
// This is what Legacy does with colored light in 3D volumes. No, it doesn't really make sense...
|
||||
// It also doesn't translate well to software style lighting.
|
||||
int mixlight = light * (255 - blendfactor);
|
||||
|
||||
r = (mixlight + pe.r * blendfactor) / 255;
|
||||
g = (mixlight + pe.g * blendfactor) / 255;
|
||||
b = (mixlight + pe.b * blendfactor) / 255;
|
||||
}
|
||||
return PalEntry(255, uint8_t(r), uint8_t(g), uint8_t(b));
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// calculates the current fog density
|
||||
//
|
||||
// Rules for fog:
|
||||
//
|
||||
// 1. If bit 4 of gl_lightmode is set always use the level's fog density.
|
||||
// This is what Legacy's GL render does.
|
||||
// 2. black fog means no fog and always uses the distfogtable based on the level's fog density setting
|
||||
// 3. If outside fog is defined and the current fog color is the same as the outside fog
|
||||
// the engine always uses the outside fog density to make the fog uniform across the Level->
|
||||
// If the outside fog's density is undefined it uses the level's fog density and if that is
|
||||
// not defined it uses a default of 70.
|
||||
// 4. If a global fog density is specified it is being used for all fog on the level
|
||||
// 5. If none of the above apply fog density is based on the light level as for the software renderer.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
float HWDrawInfo::GetFogDensity(int lightlevel, PalEntry fogcolor, int sectorfogdensity, int blendfactor)
|
||||
{
|
||||
float density;
|
||||
|
||||
auto oldlightmode = lightmode;
|
||||
if (isSoftwareLighting() && blendfactor > 0) lightmode = ELightMode::Doom; // The blendfactor feature does not work with software-style lighting.
|
||||
|
||||
if (lightmode == ELightMode::DoomLegacy)
|
||||
{
|
||||
// uses approximations of Legacy's default settings.
|
||||
density = Level->fogdensity ? (float)Level->fogdensity : 18;
|
||||
}
|
||||
else if (sectorfogdensity != 0)
|
||||
{
|
||||
// case 1: Sector has an explicit fog density set.
|
||||
density = (float)sectorfogdensity;
|
||||
}
|
||||
else if ((fogcolor.d & 0xffffff) == 0)
|
||||
{
|
||||
// case 2: black fog
|
||||
if ((!isSoftwareLighting() || blendfactor > 0) && !(Level->flags3 & LEVEL3_NOLIGHTFADE))
|
||||
{
|
||||
density = distfogtable[lightmode != ELightMode::LinearStandard][hw_ClampLight(lightlevel)];
|
||||
}
|
||||
else
|
||||
{
|
||||
density = 0;
|
||||
}
|
||||
}
|
||||
else if (Level->outsidefogdensity != 0 && APART(Level->info->outsidefog) != 0xff && (fogcolor.d & 0xffffff) == (Level->info->outsidefog & 0xffffff))
|
||||
{
|
||||
// case 3. outsidefogdensity has already been set as needed
|
||||
density = (float)Level->outsidefogdensity;
|
||||
}
|
||||
else if (Level->fogdensity != 0)
|
||||
{
|
||||
// case 4: level has fog density set
|
||||
density = (float)Level->fogdensity;
|
||||
}
|
||||
else if (lightlevel < 248)
|
||||
{
|
||||
// case 5: use light level
|
||||
density = (float)clamp<int>(255 - lightlevel, 30, 255);
|
||||
}
|
||||
else
|
||||
{
|
||||
density = 0.f;
|
||||
}
|
||||
return density;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Check if the current linedef is a candidate for a fog boundary
|
||||
//
|
||||
// Requirements for a fog boundary:
|
||||
// - front sector has no fog
|
||||
// - back sector has fog
|
||||
// - at least one of both does not have a sky ceiling.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
bool HWDrawInfo::CheckFog(sector_t *frontsector, sector_t *backsector)
|
||||
{
|
||||
if (frontsector == backsector) return false; // there can't be a boundary if both sides are in the same sector.
|
||||
|
||||
// Check for fog boundaries. This needs a few more checks for the sectors
|
||||
|
||||
PalEntry fogcolor = frontsector->Colormap.FadeColor;
|
||||
|
||||
if ((fogcolor.d & 0xffffff) == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (fogcolor.a != 0)
|
||||
{
|
||||
}
|
||||
else if (Level->outsidefogdensity != 0 && APART(Level->info->outsidefog) != 0xff && (fogcolor.d & 0xffffff) == (Level->info->outsidefog & 0xffffff))
|
||||
{
|
||||
}
|
||||
else if (Level->fogdensity!=0 || lightmode == ELightMode::DoomLegacy)
|
||||
{
|
||||
// case 3: level has fog density set
|
||||
}
|
||||
else
|
||||
{
|
||||
// case 4: use light level
|
||||
if (frontsector->lightlevel >= 248) return false;
|
||||
}
|
||||
|
||||
fogcolor = backsector->Colormap.FadeColor;
|
||||
|
||||
if ((fogcolor.d & 0xffffff) == 0)
|
||||
{
|
||||
}
|
||||
else if (Level->outsidefogdensity != 0 && APART(Level->info->outsidefog) != 0xff && (fogcolor.d & 0xffffff) == (Level->info->outsidefog & 0xffffff))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (Level->fogdensity!=0 || lightmode == ELightMode::DoomLegacy)
|
||||
{
|
||||
// case 3: level has fog density set
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// case 4: use light level
|
||||
if (backsector->lightlevel < 248) return false;
|
||||
}
|
||||
|
||||
// in all other cases this might create more problems than it solves.
|
||||
return ((frontsector->GetTexture(sector_t::ceiling)!=skyflatnum ||
|
||||
backsector->GetTexture(sector_t::ceiling)!=skyflatnum));
|
||||
}
|
||||
|
21
src/rendering/hwrenderer/utility/hw_lighting.h
Normal file
21
src/rendering/hwrenderer/utility/hw_lighting.h
Normal file
|
@ -0,0 +1,21 @@
|
|||
#pragma once
|
||||
|
||||
#include "c_cvars.h"
|
||||
#include "v_palette.h"
|
||||
#include "templates.h"
|
||||
#include "r_utility.h"
|
||||
|
||||
struct Colormap;
|
||||
|
||||
inline int hw_ClampLight(int lightlevel)
|
||||
{
|
||||
return clamp(lightlevel, 0, 255);
|
||||
}
|
||||
|
||||
EXTERN_CVAR(Int, gl_weaponlight);
|
||||
|
||||
inline int getExtraLight()
|
||||
{
|
||||
return r_viewpoint.extralight * gl_weaponlight;
|
||||
}
|
||||
|
187
src/rendering/hwrenderer/utility/hw_shaderpatcher.cpp
Normal file
187
src/rendering/hwrenderer/utility/hw_shaderpatcher.cpp
Normal file
|
@ -0,0 +1,187 @@
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Copyright(C) 2004-2018 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/
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
/*
|
||||
** hw_shaderpatcher.cpp
|
||||
**
|
||||
** Modifies shader source to account for different syntax versions or engine changes.
|
||||
**
|
||||
*/
|
||||
|
||||
|
||||
#include "hw_shaderpatcher.h"
|
||||
|
||||
|
||||
static bool IsGlslWhitespace(char c)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case ' ':
|
||||
case '\r':
|
||||
case '\n':
|
||||
case '\t':
|
||||
case '\f':
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static FString NextGlslToken(const char *chars, long len, long &pos)
|
||||
{
|
||||
// Eat whitespace
|
||||
long tokenStart = pos;
|
||||
while (tokenStart != len && IsGlslWhitespace(chars[tokenStart]))
|
||||
tokenStart++;
|
||||
|
||||
// Find token end
|
||||
long tokenEnd = tokenStart;
|
||||
while (tokenEnd != len && !IsGlslWhitespace(chars[tokenEnd]) && chars[tokenEnd] != ';')
|
||||
tokenEnd++;
|
||||
|
||||
pos = tokenEnd;
|
||||
return FString(chars + tokenStart, tokenEnd - tokenStart);
|
||||
}
|
||||
|
||||
static bool isShaderType(const char *name)
|
||||
{
|
||||
return !strcmp(name, "sampler1D") || !strcmp(name, "sampler2D") || !strcmp(name, "sampler3D") || !strcmp(name, "samplerCube") || !strcmp(name, "sampler2DMS");
|
||||
}
|
||||
|
||||
FString RemoveLegacyUserUniforms(FString code)
|
||||
{
|
||||
// User shaders must declare their uniforms via the GLDEFS file.
|
||||
// The following code searches for legacy uniform declarations in the shader itself and replaces them with whitespace.
|
||||
|
||||
long len = (long)code.Len();
|
||||
char *chars = code.LockBuffer();
|
||||
|
||||
long startIndex = 0;
|
||||
while (true)
|
||||
{
|
||||
long matchIndex = code.IndexOf("uniform", startIndex);
|
||||
if (matchIndex == -1)
|
||||
break;
|
||||
|
||||
bool isLegacyUniformName = false;
|
||||
|
||||
bool isKeywordStart = matchIndex == 0 || IsGlslWhitespace(chars[matchIndex - 1]);
|
||||
bool isKeywordEnd = matchIndex + 7 == len || IsGlslWhitespace(chars[matchIndex + 7]);
|
||||
if (isKeywordStart && isKeywordEnd)
|
||||
{
|
||||
long pos = matchIndex + 7;
|
||||
FString type = NextGlslToken(chars, len, pos);
|
||||
FString identifier = NextGlslToken(chars, len, pos);
|
||||
|
||||
isLegacyUniformName = type.Compare("float") == 0 && identifier.Compare("timer") == 0;
|
||||
}
|
||||
|
||||
if (isLegacyUniformName)
|
||||
{
|
||||
long statementEndIndex = code.IndexOf(';', matchIndex + 7);
|
||||
if (statementEndIndex == -1)
|
||||
statementEndIndex = len;
|
||||
for (long i = matchIndex; i <= statementEndIndex; i++)
|
||||
{
|
||||
if (!IsGlslWhitespace(chars[i]))
|
||||
chars[i] = ' ';
|
||||
}
|
||||
startIndex = statementEndIndex;
|
||||
}
|
||||
else
|
||||
{
|
||||
startIndex = matchIndex + 7;
|
||||
}
|
||||
}
|
||||
|
||||
code.UnlockBuffer();
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
FString RemoveSamplerBindings(FString code, TArray<std::pair<FString, int>> &samplerstobind)
|
||||
{
|
||||
long len = (long)code.Len();
|
||||
char *chars = code.LockBuffer();
|
||||
|
||||
long startIndex = 0;
|
||||
long startpos, endpos;
|
||||
while (true)
|
||||
{
|
||||
long matchIndex = code.IndexOf("layout(binding", startIndex);
|
||||
if (matchIndex == -1)
|
||||
break;
|
||||
|
||||
bool isSamplerUniformName = false;
|
||||
|
||||
bool isKeywordStart = matchIndex == 0 || IsGlslWhitespace(chars[matchIndex - 1]);
|
||||
bool isKeywordEnd = matchIndex + 14 == len || IsGlslWhitespace(chars[matchIndex + 14]) || chars[matchIndex + 14] == '=';
|
||||
if (isKeywordStart && isKeywordEnd)
|
||||
{
|
||||
long pos = matchIndex + 14;
|
||||
startpos = matchIndex;
|
||||
while (IsGlslWhitespace(chars[pos])) pos++;
|
||||
if (chars[pos] == '=')
|
||||
{
|
||||
char *p;
|
||||
pos++;
|
||||
auto val = strtol(&chars[pos], &p, 0);
|
||||
if (p != &chars[pos])
|
||||
{
|
||||
pos = long(p - chars);
|
||||
while (IsGlslWhitespace(chars[pos])) pos++;
|
||||
if (chars[pos] == ')')
|
||||
{
|
||||
endpos = ++pos;
|
||||
FString uniform = NextGlslToken(chars, len, pos);
|
||||
FString type = NextGlslToken(chars, len, pos);
|
||||
FString identifier = NextGlslToken(chars, len, pos);
|
||||
|
||||
isSamplerUniformName = uniform.Compare("uniform") == 0 && isShaderType(type);
|
||||
if (isSamplerUniformName)
|
||||
{
|
||||
samplerstobind.Push(std::make_pair(identifier, val));
|
||||
for (auto pos = startpos; pos < endpos; pos++)
|
||||
{
|
||||
if (!IsGlslWhitespace(chars[pos]))
|
||||
chars[pos] = ' ';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isSamplerUniformName)
|
||||
{
|
||||
startIndex = endpos;
|
||||
}
|
||||
else
|
||||
{
|
||||
startIndex = matchIndex + 7;
|
||||
}
|
||||
}
|
||||
|
||||
code.UnlockBuffer();
|
||||
|
||||
return code;
|
||||
}
|
||||
|
9
src/rendering/hwrenderer/utility/hw_shaderpatcher.h
Normal file
9
src/rendering/hwrenderer/utility/hw_shaderpatcher.h
Normal file
|
@ -0,0 +1,9 @@
|
|||
#pragma once
|
||||
|
||||
#include "tarray.h"
|
||||
#include "zstring.h"
|
||||
#include "utility"
|
||||
|
||||
FString RemoveLegacyUserUniforms(FString code);
|
||||
FString RemoveSamplerBindings(FString code, TArray<std::pair<FString, int>> &samplerstobind); // For GL 3.3 compatibility which cannot declare sampler bindings in the sampler source.
|
||||
|
176
src/rendering/hwrenderer/utility/hw_vrmodes.cpp
Normal file
176
src/rendering/hwrenderer/utility/hw_vrmodes.cpp
Normal file
|
@ -0,0 +1,176 @@
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Copyright(C) 2015 Christopher Bruns
|
||||
// All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with this program. If not, see http://www.gnu.org/licenses/
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
/*
|
||||
** gl_stereo_leftright.cpp
|
||||
** Offsets for left and right eye views
|
||||
**
|
||||
*/
|
||||
|
||||
#include "vectors.h" // RAD2DEG
|
||||
#include "doomtype.h" // M_PI
|
||||
#include "hwrenderer/utility/hw_cvars.h"
|
||||
#include "hw_vrmodes.h"
|
||||
#include "v_video.h"
|
||||
|
||||
// Set up 3D-specific console variables:
|
||||
CVAR(Int, vr_mode, 0, CVAR_GLOBALCONFIG)
|
||||
|
||||
// switch left and right eye views
|
||||
CVAR(Bool, vr_swap_eyes, false, CVAR_GLOBALCONFIG)
|
||||
|
||||
// intraocular distance in meters
|
||||
CVAR(Float, vr_ipd, 0.062f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) // METERS
|
||||
|
||||
// distance between viewer and the display screen
|
||||
CVAR(Float, vr_screendist, 0.80f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) // METERS
|
||||
|
||||
// default conversion between (vertical) DOOM units and meters
|
||||
CVAR(Float, vr_hunits_per_meter, 41.0f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) // METERS
|
||||
|
||||
|
||||
#define isqrt2 0.7071067812f
|
||||
static VRMode vrmi_mono = { 1, 1.f, 1.f, 1.f,{ { 0.f, 1.f },{ 0.f, 0.f } } };
|
||||
static VRMode vrmi_stereo = { 2, 1.f, 1.f, 1.f,{ { -.5f, 1.f },{ .5f, 1.f } } };
|
||||
static VRMode vrmi_sbsfull = { 2, .5f, 1.f, 2.f,{ { -.5f, .5f },{ .5f, .5f } } };
|
||||
static VRMode vrmi_sbssquished = { 2, .5f, 1.f, 1.f,{ { -.5f, 1.f },{ .5f, 1.f } } };
|
||||
static VRMode vrmi_lefteye = { 1, 1.f, 1.f, 1.f, { { -.5f, 1.f },{ 0.f, 0.f } } };
|
||||
static VRMode vrmi_righteye = { 1, 1.f, 1.f, 1.f,{ { .5f, 1.f },{ 0.f, 0.f } } };
|
||||
static VRMode vrmi_topbottom = { 2, 1.f, .5f, 1.f,{ { -.5f, 1.f },{ .5f, 1.f } } };
|
||||
static VRMode vrmi_checker = { 2, isqrt2, isqrt2, 1.f,{ { -.5f, 1.f },{ .5f, 1.f } } };
|
||||
|
||||
const VRMode *VRMode::GetVRMode(bool toscreen)
|
||||
{
|
||||
switch (toscreen && vid_rendermode == 4 ? vr_mode : 0)
|
||||
{
|
||||
default:
|
||||
case VR_MONO:
|
||||
return &vrmi_mono;
|
||||
|
||||
case VR_GREENMAGENTA:
|
||||
case VR_REDCYAN:
|
||||
case VR_QUADSTEREO:
|
||||
case VR_AMBERBLUE:
|
||||
return &vrmi_stereo;
|
||||
|
||||
case VR_SIDEBYSIDESQUISHED:
|
||||
case VR_COLUMNINTERLEAVED:
|
||||
return &vrmi_sbssquished;
|
||||
|
||||
case VR_SIDEBYSIDEFULL:
|
||||
return &vrmi_sbsfull;
|
||||
|
||||
case VR_TOPBOTTOM:
|
||||
case VR_ROWINTERLEAVED:
|
||||
return &vrmi_topbottom;
|
||||
|
||||
case VR_LEFTEYEVIEW:
|
||||
return &vrmi_lefteye;
|
||||
|
||||
case VR_RIGHTEYEVIEW:
|
||||
return &vrmi_righteye;
|
||||
|
||||
case VR_CHECKERINTERLEAVED:
|
||||
return &vrmi_checker;
|
||||
}
|
||||
}
|
||||
|
||||
void VRMode::AdjustViewport(DFrameBuffer *screen) const
|
||||
{
|
||||
screen->mSceneViewport.height = (int)(screen->mSceneViewport.height * mVerticalViewportScale);
|
||||
screen->mSceneViewport.top = (int)(screen->mSceneViewport.top * mVerticalViewportScale);
|
||||
screen->mSceneViewport.width = (int)(screen->mSceneViewport.width * mHorizontalViewportScale);
|
||||
screen->mSceneViewport.left = (int)(screen->mSceneViewport.left * mHorizontalViewportScale);
|
||||
|
||||
screen->mScreenViewport.height = (int)(screen->mScreenViewport.height * mVerticalViewportScale);
|
||||
screen->mScreenViewport.top = (int)(screen->mScreenViewport.top * mVerticalViewportScale);
|
||||
screen->mScreenViewport.width = (int)(screen->mScreenViewport.width * mHorizontalViewportScale);
|
||||
screen->mScreenViewport.left = (int)(screen->mScreenViewport.left * mHorizontalViewportScale);
|
||||
}
|
||||
|
||||
VSMatrix VRMode::GetHUDSpriteProjection() const
|
||||
{
|
||||
VSMatrix mat;
|
||||
int w = screen->GetWidth();
|
||||
int h = screen->GetHeight();
|
||||
float scaled_w = w / mWeaponProjectionScale;
|
||||
float left_ofs = (w - scaled_w) / 2.f;
|
||||
mat.ortho(left_ofs, left_ofs + scaled_w, (float)h, 0, -1.0f, 1.0f);
|
||||
return mat;
|
||||
}
|
||||
|
||||
float VREyeInfo::getShift() const
|
||||
{
|
||||
auto res = mShiftFactor * vr_ipd;
|
||||
return vr_swap_eyes ? -res : res;
|
||||
}
|
||||
|
||||
VSMatrix VREyeInfo::GetProjection(float fov, float aspectRatio, float fovRatio) const
|
||||
{
|
||||
VSMatrix result;
|
||||
|
||||
if (mShiftFactor == 0)
|
||||
{
|
||||
float fovy = (float)(2 * RAD2DEG(atan(tan(DEG2RAD(fov) / 2) / fovRatio)));
|
||||
result.perspective(fovy, aspectRatio, screen->GetZNear(), screen->GetZFar());
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
double zNear = screen->GetZNear();
|
||||
double zFar = screen->GetZFar();
|
||||
|
||||
// For stereo 3D, use asymmetric frustum shift in projection matrix
|
||||
// Q: shouldn't shift vary with roll angle, at least for desktop display?
|
||||
// A: No. (lab) roll is not measured on desktop display (yet)
|
||||
double frustumShift = zNear * getShift() / vr_screendist; // meters cancel, leaving doom units
|
||||
// double frustumShift = 0; // Turning off shift for debugging
|
||||
double fH = zNear * tan(DEG2RAD(fov) / 2) / fovRatio;
|
||||
double fW = fH * aspectRatio * mScaleFactor;
|
||||
double left = -fW - frustumShift;
|
||||
double right = fW - frustumShift;
|
||||
double bottom = -fH;
|
||||
double top = fH;
|
||||
|
||||
VSMatrix result(1);
|
||||
result.frustum((float)left, (float)right, (float)bottom, (float)top, (float)zNear, (float)zFar);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* virtual */
|
||||
DVector3 VREyeInfo::GetViewShift(float yaw) const
|
||||
{
|
||||
if (mShiftFactor == 0)
|
||||
{
|
||||
// pass-through for Mono view
|
||||
return { 0,0,0 };
|
||||
}
|
||||
else
|
||||
{
|
||||
double dx = -cos(DEG2RAD(yaw)) * vr_hunits_per_meter * getShift();
|
||||
double dy = sin(DEG2RAD(yaw)) * vr_hunits_per_meter * getShift();
|
||||
return { dx, dy, 0 };
|
||||
}
|
||||
}
|
||||
|
47
src/rendering/hwrenderer/utility/hw_vrmodes.h
Normal file
47
src/rendering/hwrenderer/utility/hw_vrmodes.h
Normal file
|
@ -0,0 +1,47 @@
|
|||
#pragma once
|
||||
|
||||
#include "r_data/matrix.h"
|
||||
|
||||
class DFrameBuffer;
|
||||
|
||||
enum
|
||||
{
|
||||
VR_MONO = 0,
|
||||
VR_GREENMAGENTA = 1,
|
||||
VR_REDCYAN = 2,
|
||||
VR_SIDEBYSIDEFULL = 3,
|
||||
VR_SIDEBYSIDESQUISHED = 4,
|
||||
VR_LEFTEYEVIEW = 5,
|
||||
VR_RIGHTEYEVIEW = 6,
|
||||
VR_QUADSTEREO = 7,
|
||||
VR_AMBERBLUE = 9,
|
||||
VR_TOPBOTTOM = 11,
|
||||
VR_ROWINTERLEAVED = 12,
|
||||
VR_COLUMNINTERLEAVED = 13,
|
||||
VR_CHECKERINTERLEAVED = 14
|
||||
};
|
||||
|
||||
struct VREyeInfo
|
||||
{
|
||||
float mShiftFactor;
|
||||
float mScaleFactor;
|
||||
|
||||
VSMatrix GetProjection(float fov, float aspectRatio, float fovRatio) const;
|
||||
DVector3 GetViewShift(float yaw) const;
|
||||
private:
|
||||
float getShift() const;
|
||||
|
||||
};
|
||||
|
||||
struct VRMode
|
||||
{
|
||||
int mEyeCount;
|
||||
float mHorizontalViewportScale;
|
||||
float mVerticalViewportScale;
|
||||
float mWeaponProjectionScale;
|
||||
VREyeInfo mEyes[2];
|
||||
|
||||
static const VRMode *GetVRMode(bool toscreen = true);
|
||||
void AdjustViewport(DFrameBuffer *fb) const;
|
||||
VSMatrix GetHUDSpriteProjection() const;
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue