mirror of
https://github.com/DrBeef/Raze.git
synced 2024-12-13 14:10:48 +00:00
303 lines
8.2 KiB
C++
303 lines
8.2 KiB
C++
/*
|
|
** Polygon Doom software renderer
|
|
** Copyright (c) 2016 Magnus Norddahl
|
|
**
|
|
** This software is provided 'as-is', without any express or implied
|
|
** warranty. In no event will the authors be held liable for any damages
|
|
** arising from the use of this software.
|
|
**
|
|
** Permission is granted to anyone to use this software for any purpose,
|
|
** including commercial applications, and to alter it and redistribute it
|
|
** freely, subject to the following restrictions:
|
|
**
|
|
** 1. The origin of this software must not be misrepresented; you must not
|
|
** claim that you wrote the original software. If you use this software
|
|
** in a product, an acknowledgment in the product documentation would be
|
|
** appreciated but is not required.
|
|
** 2. Altered source versions must be plainly marked as such, and must not be
|
|
** misrepresented as being the original software.
|
|
** 3. This notice may not be removed or altered from any source distribution.
|
|
**
|
|
*/
|
|
|
|
#include <stddef.h>
|
|
#include "templates.h"
|
|
|
|
#include "filesystem.h"
|
|
#include "v_video.h"
|
|
#include "poly_triangle.h"
|
|
#include "screen_triangle.h"
|
|
#include "screen_blend.h"
|
|
#include "screen_scanline_setup.h"
|
|
#include "screen_shader.h"
|
|
#include <cmath>
|
|
|
|
static void WriteDepth(int y, int x0, int x1, PolyTriangleThreadData* thread)
|
|
{
|
|
float* line = thread->depthstencil->DepthValues() + (size_t)thread->depthstencil->Width() * y;
|
|
|
|
if (thread->DepthRangeScale != 0.0f)
|
|
{
|
|
float* w = thread->scanline.W;
|
|
for (int x = x0; x < x1; x++)
|
|
{
|
|
line[x] = w[x];
|
|
}
|
|
}
|
|
else // portal fills always uses DepthRangeStart = 1 and DepthRangeScale = 0
|
|
{
|
|
for (int x = x0; x < x1; x++)
|
|
{
|
|
line[x] = 65536.0f;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void WriteStencil(int y, int x0, int x1, PolyTriangleThreadData* thread)
|
|
{
|
|
uint8_t* line = thread->depthstencil->StencilValues() + (size_t)thread->depthstencil->Width() * y;
|
|
uint8_t value = thread->StencilWriteValue;
|
|
for (int x = x0; x < x1; x++)
|
|
{
|
|
line[x] = value;
|
|
}
|
|
}
|
|
|
|
static void RunFragmentShader(int y, int x0, int x1, const TriDrawTriangleArgs* args, PolyTriangleThreadData* thread)
|
|
{
|
|
WriteVaryings(y, x0, x1, args, thread);
|
|
thread->FragmentShader(x0, x1, thread);
|
|
}
|
|
|
|
static void DrawSpan(int y, int x0, int x1, const TriDrawTriangleArgs* args, PolyTriangleThreadData* thread)
|
|
{
|
|
if (thread->WriteColor || thread->AlphaTest)
|
|
RunFragmentShader(y, x0, x1, args, thread);
|
|
|
|
if (!thread->AlphaTest)
|
|
{
|
|
if (thread->WriteColor)
|
|
thread->WriteColorFunc(y, x0, x1, thread);
|
|
if (thread->WriteDepth)
|
|
WriteDepth(y, x0, x1, thread);
|
|
if (thread->WriteStencil)
|
|
WriteStencil(y, x0, x1, thread);
|
|
}
|
|
else
|
|
{
|
|
uint8_t* discard = thread->scanline.discard;
|
|
while (x0 < x1)
|
|
{
|
|
int xstart = x0;
|
|
while (!discard[x0] && x0 < x1) x0++;
|
|
|
|
if (xstart < x0)
|
|
{
|
|
if (thread->WriteColor)
|
|
thread->WriteColorFunc(y, xstart, x0, thread);
|
|
if (thread->WriteDepth)
|
|
WriteDepth(y, xstart, x0, thread);
|
|
if (thread->WriteStencil)
|
|
WriteStencil(y, xstart, x0, thread);
|
|
}
|
|
|
|
while (discard[x0] && x0 < x1) x0++;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void NoTestSpan(int y, int x0, int x1, const TriDrawTriangleArgs* args, PolyTriangleThreadData* thread)
|
|
{
|
|
WriteW(y, x0, x1, args, thread);
|
|
DrawSpan(y, x0, x1, args, thread);
|
|
}
|
|
|
|
static void DepthTestSpan(int y, int x0, int x1, const TriDrawTriangleArgs* args, PolyTriangleThreadData* thread)
|
|
{
|
|
WriteW(y, x0, x1, args, thread);
|
|
|
|
float* zbufferLine = thread->depthstencil->DepthValues() + (size_t)thread->depthstencil->Width() * y;
|
|
float* w = thread->scanline.W;
|
|
float depthbias = thread->depthbias;
|
|
|
|
int x = x0;
|
|
int xend = x1;
|
|
while (x < xend)
|
|
{
|
|
int xstart = x;
|
|
|
|
while (zbufferLine[x] >= w[x] + depthbias && x < xend)
|
|
x++;
|
|
|
|
if (x > xstart)
|
|
{
|
|
DrawSpan(y, xstart, x, args, thread);
|
|
}
|
|
|
|
while (zbufferLine[x] < w[x] + depthbias && x < xend)
|
|
x++;
|
|
}
|
|
}
|
|
|
|
static void DepthStencilTestSpan(int y, int x0, int x1, const TriDrawTriangleArgs* args, PolyTriangleThreadData* thread)
|
|
{
|
|
uint8_t* stencilLine = thread->depthstencil->StencilValues() + (size_t)thread->depthstencil->Width() * y;
|
|
uint8_t stencilTestValue = thread->StencilTestValue;
|
|
|
|
int x = x0;
|
|
int xend = x1;
|
|
while (x < xend)
|
|
{
|
|
int xstart = x;
|
|
while (stencilLine[x] == stencilTestValue && x < xend)
|
|
x++;
|
|
|
|
if (x > xstart)
|
|
{
|
|
DepthTestSpan(y, xstart, x, args, thread);
|
|
}
|
|
|
|
while (stencilLine[x] != stencilTestValue && x < xend)
|
|
x++;
|
|
}
|
|
}
|
|
|
|
static void StencilTestSpan(int y, int x0, int x1, const TriDrawTriangleArgs* args, PolyTriangleThreadData* thread)
|
|
{
|
|
uint8_t* stencilLine = thread->depthstencil->StencilValues() + (size_t)thread->depthstencil->Width() * y;
|
|
uint8_t stencilTestValue = thread->StencilTestValue;
|
|
|
|
int x = x0;
|
|
int xend = x1;
|
|
while (x < xend)
|
|
{
|
|
int xstart = x;
|
|
while (stencilLine[x] == stencilTestValue && x < xend)
|
|
x++;
|
|
|
|
if (x > xstart)
|
|
{
|
|
WriteW(y, x0, x1, args, thread);
|
|
DrawSpan(y, xstart, x, args, thread);
|
|
}
|
|
|
|
while (stencilLine[x] != stencilTestValue && x < xend)
|
|
x++;
|
|
}
|
|
}
|
|
|
|
static void SortVertices(const TriDrawTriangleArgs* args, ScreenTriVertex** sortedVertices)
|
|
{
|
|
sortedVertices[0] = args->v1;
|
|
sortedVertices[1] = args->v2;
|
|
sortedVertices[2] = args->v3;
|
|
|
|
if (sortedVertices[1]->y < sortedVertices[0]->y)
|
|
std::swap(sortedVertices[0], sortedVertices[1]);
|
|
if (sortedVertices[2]->y < sortedVertices[0]->y)
|
|
std::swap(sortedVertices[0], sortedVertices[2]);
|
|
if (sortedVertices[2]->y < sortedVertices[1]->y)
|
|
std::swap(sortedVertices[1], sortedVertices[2]);
|
|
}
|
|
|
|
void ScreenTriangle::Draw(const TriDrawTriangleArgs* args, PolyTriangleThreadData* thread)
|
|
{
|
|
// Sort vertices by Y position
|
|
ScreenTriVertex* sortedVertices[3];
|
|
SortVertices(args, sortedVertices);
|
|
|
|
int clipleft = thread->clip.left;
|
|
int cliptop = MAX(thread->clip.top, thread->numa_start_y);
|
|
int clipright = thread->clip.right;
|
|
int clipbottom = MIN(thread->clip.bottom, thread->numa_end_y);
|
|
|
|
int topY = (int)(sortedVertices[0]->y + 0.5f);
|
|
int midY = (int)(sortedVertices[1]->y + 0.5f);
|
|
int bottomY = (int)(sortedVertices[2]->y + 0.5f);
|
|
|
|
topY = MAX(topY, cliptop);
|
|
midY = MIN(midY, clipbottom);
|
|
bottomY = MIN(bottomY, clipbottom);
|
|
|
|
if (topY >= bottomY)
|
|
return;
|
|
|
|
SelectFragmentShader(thread);
|
|
SelectWriteColorFunc(thread);
|
|
|
|
void(*testfunc)(int y, int x0, int x1, const TriDrawTriangleArgs * args, PolyTriangleThreadData * thread);
|
|
|
|
int opt = 0;
|
|
if (thread->DepthTest) opt |= SWTRI_DepthTest;
|
|
if (thread->StencilTest) opt |= SWTRI_StencilTest;
|
|
testfunc = ScreenTriangle::TestSpanOpts[opt];
|
|
|
|
topY += thread->skipped_by_thread(topY);
|
|
int num_cores = thread->num_cores;
|
|
|
|
// Find start/end X positions for each line covered by the triangle:
|
|
|
|
int y = topY;
|
|
|
|
float longDX = sortedVertices[2]->x - sortedVertices[0]->x;
|
|
float longDY = sortedVertices[2]->y - sortedVertices[0]->y;
|
|
float longStep = longDX / longDY;
|
|
float longPos = sortedVertices[0]->x + longStep * (y + 0.5f - sortedVertices[0]->y) + 0.5f;
|
|
longStep *= num_cores;
|
|
|
|
if (y < midY)
|
|
{
|
|
float shortDX = sortedVertices[1]->x - sortedVertices[0]->x;
|
|
float shortDY = sortedVertices[1]->y - sortedVertices[0]->y;
|
|
float shortStep = shortDX / shortDY;
|
|
float shortPos = sortedVertices[0]->x + shortStep * (y + 0.5f - sortedVertices[0]->y) + 0.5f;
|
|
shortStep *= num_cores;
|
|
|
|
while (y < midY)
|
|
{
|
|
int x0 = (int)shortPos;
|
|
int x1 = (int)longPos;
|
|
if (x1 < x0) std::swap(x0, x1);
|
|
x0 = clamp(x0, clipleft, clipright);
|
|
x1 = clamp(x1, clipleft, clipright);
|
|
|
|
testfunc(y, x0, x1, args, thread);
|
|
|
|
shortPos += shortStep;
|
|
longPos += longStep;
|
|
y += num_cores;
|
|
}
|
|
}
|
|
|
|
if (y < bottomY)
|
|
{
|
|
float shortDX = sortedVertices[2]->x - sortedVertices[1]->x;
|
|
float shortDY = sortedVertices[2]->y - sortedVertices[1]->y;
|
|
float shortStep = shortDX / shortDY;
|
|
float shortPos = sortedVertices[1]->x + shortStep * (y + 0.5f - sortedVertices[1]->y) + 0.5f;
|
|
shortStep *= num_cores;
|
|
|
|
while (y < bottomY)
|
|
{
|
|
int x0 = (int)shortPos;
|
|
int x1 = (int)longPos;
|
|
if (x1 < x0) std::swap(x0, x1);
|
|
x0 = clamp(x0, clipleft, clipright);
|
|
x1 = clamp(x1, clipleft, clipright);
|
|
|
|
testfunc(y, x0, x1, args, thread);
|
|
|
|
shortPos += shortStep;
|
|
longPos += longStep;
|
|
y += num_cores;
|
|
}
|
|
}
|
|
}
|
|
|
|
void(*ScreenTriangle::TestSpanOpts[])(int y, int x0, int x1, const TriDrawTriangleArgs* args, PolyTriangleThreadData* thread) =
|
|
{
|
|
&NoTestSpan,
|
|
&DepthTestSpan,
|
|
&StencilTestSpan,
|
|
&DepthStencilTestSpan
|
|
};
|