2017-02-10 08:50:26 -07:00
|
|
|
/*
|
2022-01-08 18:15:50 -07:00
|
|
|
Copyright 2016-2022 melonDS team
|
2017-02-10 08:50:26 -07:00
|
|
|
|
|
|
|
This file is part of melonDS.
|
|
|
|
|
|
|
|
melonDS is free software: you can redistribute it and/or modify it under
|
|
|
|
the terms of the GNU General Public License as published by the Free
|
|
|
|
Software Foundation, either version 3 of the License, or (at your option)
|
|
|
|
any later version.
|
|
|
|
|
|
|
|
melonDS 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 General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License along
|
|
|
|
with melonDS. If not, see http://www.gnu.org/licenses/.
|
|
|
|
*/
|
|
|
|
|
2021-02-09 15:38:51 -07:00
|
|
|
#include "GPU3D_Soft.h"
|
|
|
|
|
2021-08-20 17:54:09 -06:00
|
|
|
#include <algorithm>
|
2017-02-10 08:50:26 -07:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include "NDS.h"
|
2017-03-01 16:49:44 -07:00
|
|
|
#include "GPU.h"
|
2017-02-10 08:50:26 -07:00
|
|
|
|
|
|
|
|
|
|
|
namespace GPU3D
|
|
|
|
{
|
2017-05-23 15:38:28 -06:00
|
|
|
|
|
|
|
void RenderThreadFunc();
|
|
|
|
|
2017-02-10 08:50:26 -07:00
|
|
|
|
2021-02-09 15:38:51 -07:00
|
|
|
void SoftRenderer::StopRenderThread()
|
2017-06-04 07:55:23 -06:00
|
|
|
{
|
2021-03-11 08:54:27 -07:00
|
|
|
if (RenderThreadRunning.load(std::memory_order_relaxed))
|
2017-06-04 07:55:23 -06:00
|
|
|
{
|
|
|
|
RenderThreadRunning = false;
|
|
|
|
Platform::Semaphore_Post(Sema_RenderStart);
|
2021-02-11 08:00:36 -07:00
|
|
|
Platform::Thread_Wait(RenderThread);
|
|
|
|
Platform::Thread_Free(RenderThread);
|
2017-06-04 07:55:23 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-09 15:38:51 -07:00
|
|
|
void SoftRenderer::SetupRenderThread()
|
2017-06-04 07:55:23 -06:00
|
|
|
{
|
2020-05-28 07:53:32 -06:00
|
|
|
if (Threaded)
|
2017-06-04 07:55:23 -06:00
|
|
|
{
|
2021-03-11 08:54:27 -07:00
|
|
|
if (!RenderThreadRunning.load(std::memory_order_relaxed))
|
2017-06-04 07:55:23 -06:00
|
|
|
{
|
|
|
|
RenderThreadRunning = true;
|
2021-02-11 11:11:18 -07:00
|
|
|
RenderThread = Platform::Thread_Create(std::bind(&SoftRenderer::RenderThreadFunc, this));
|
2017-06-04 07:55:23 -06:00
|
|
|
}
|
|
|
|
|
2021-01-26 08:42:27 -07:00
|
|
|
// otherwise more than one frame can be queued up at once
|
|
|
|
Platform::Semaphore_Reset(Sema_RenderStart);
|
|
|
|
|
2017-06-04 07:55:23 -06:00
|
|
|
if (RenderThreadRendering)
|
|
|
|
Platform::Semaphore_Wait(Sema_RenderDone);
|
|
|
|
|
2021-01-26 08:42:27 -07:00
|
|
|
Platform::Semaphore_Reset(Sema_RenderDone);
|
2017-06-04 07:55:23 -06:00
|
|
|
Platform::Semaphore_Reset(Sema_RenderStart);
|
|
|
|
Platform::Semaphore_Reset(Sema_ScanlineCount);
|
|
|
|
|
|
|
|
Platform::Semaphore_Post(Sema_RenderStart);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
StopRenderThread();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-02-09 15:38:51 -07:00
|
|
|
SoftRenderer::SoftRenderer()
|
|
|
|
: Renderer3D(false)
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
bool SoftRenderer::Init()
|
2017-02-10 08:50:26 -07:00
|
|
|
{
|
2017-05-23 15:38:28 -06:00
|
|
|
Sema_RenderStart = Platform::Semaphore_Create();
|
|
|
|
Sema_RenderDone = Platform::Semaphore_Create();
|
|
|
|
Sema_ScanlineCount = Platform::Semaphore_Create();
|
|
|
|
|
2020-05-28 07:53:32 -06:00
|
|
|
Threaded = false;
|
2017-05-23 15:38:28 -06:00
|
|
|
RenderThreadRunning = false;
|
2017-05-25 17:22:11 -06:00
|
|
|
RenderThreadRendering = false;
|
2017-05-23 15:38:28 -06:00
|
|
|
|
2017-02-10 19:54:08 -07:00
|
|
|
return true;
|
2017-02-10 08:50:26 -07:00
|
|
|
}
|
|
|
|
|
2021-02-09 15:38:51 -07:00
|
|
|
void SoftRenderer::DeInit()
|
2017-02-10 08:50:26 -07:00
|
|
|
{
|
2017-06-04 07:55:23 -06:00
|
|
|
StopRenderThread();
|
2017-05-25 17:22:11 -06:00
|
|
|
|
|
|
|
Platform::Semaphore_Free(Sema_RenderStart);
|
|
|
|
Platform::Semaphore_Free(Sema_RenderDone);
|
|
|
|
Platform::Semaphore_Free(Sema_ScanlineCount);
|
2017-02-10 08:50:26 -07:00
|
|
|
}
|
|
|
|
|
2021-02-09 15:38:51 -07:00
|
|
|
void SoftRenderer::Reset()
|
2017-02-10 08:50:26 -07:00
|
|
|
{
|
2019-03-31 18:51:31 -06:00
|
|
|
memset(ColorBuffer, 0, BufferSize * 2 * 4);
|
|
|
|
memset(DepthBuffer, 0, BufferSize * 2 * 4);
|
|
|
|
memset(AttrBuffer, 0, BufferSize * 2 * 4);
|
2017-05-23 15:38:28 -06:00
|
|
|
|
2017-05-25 20:00:15 -06:00
|
|
|
PrevIsShadowMask = false;
|
|
|
|
|
2017-06-04 07:55:23 -06:00
|
|
|
SetupRenderThread();
|
2017-02-10 08:50:26 -07:00
|
|
|
}
|
|
|
|
|
2021-02-09 15:38:51 -07:00
|
|
|
void SoftRenderer::SetRenderSettings(GPU::RenderSettings& settings)
|
2020-05-28 07:53:32 -06:00
|
|
|
{
|
|
|
|
Threaded = settings.Soft_Threaded;
|
|
|
|
SetupRenderThread();
|
|
|
|
}
|
|
|
|
|
2021-02-09 15:38:51 -07:00
|
|
|
void SoftRenderer::TextureLookup(u32 texparam, u32 texpal, s16 s, s16 t, u16* color, u8* alpha)
|
2017-02-13 19:29:02 -07:00
|
|
|
{
|
2017-03-01 16:49:44 -07:00
|
|
|
u32 vramaddr = (texparam & 0xFFFF) << 3;
|
|
|
|
|
2017-05-21 12:14:03 -06:00
|
|
|
s32 width = 8 << ((texparam >> 20) & 0x7);
|
|
|
|
s32 height = 8 << ((texparam >> 23) & 0x7);
|
2017-03-01 16:49:44 -07:00
|
|
|
|
|
|
|
s >>= 4;
|
|
|
|
t >>= 4;
|
|
|
|
|
2017-03-06 10:25:20 -07:00
|
|
|
// texture wrapping
|
|
|
|
// TODO: optimize this somehow
|
2017-04-21 14:40:15 -06:00
|
|
|
// testing shows that it's hardly worth optimizing, actually
|
2017-03-06 10:25:20 -07:00
|
|
|
|
|
|
|
if (texparam & (1<<16))
|
|
|
|
{
|
|
|
|
if (texparam & (1<<18))
|
|
|
|
{
|
|
|
|
if (s & width) s = (width-1) - (s & (width-1));
|
|
|
|
else s = (s & (width-1));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
s &= width-1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (s < 0) s = 0;
|
|
|
|
else if (s >= width) s = width-1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (texparam & (1<<17))
|
|
|
|
{
|
|
|
|
if (texparam & (1<<19))
|
|
|
|
{
|
|
|
|
if (t & height) t = (height-1) - (t & (height-1));
|
|
|
|
else t = (t & (height-1));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
t &= height-1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (t < 0) t = 0;
|
|
|
|
else if (t >= height) t = height-1;
|
|
|
|
}
|
2017-03-01 16:49:44 -07:00
|
|
|
|
2017-03-07 14:05:38 -07:00
|
|
|
u8 alpha0;
|
|
|
|
if (texparam & (1<<29)) alpha0 = 0;
|
|
|
|
else alpha0 = 31;
|
|
|
|
|
2017-03-01 16:49:44 -07:00
|
|
|
switch ((texparam >> 26) & 0x7)
|
|
|
|
{
|
2017-03-07 14:05:38 -07:00
|
|
|
case 1: // A3I5
|
2017-03-01 16:49:44 -07:00
|
|
|
{
|
2017-03-07 14:05:38 -07:00
|
|
|
vramaddr += ((t * width) + s);
|
2020-11-30 08:58:52 -07:00
|
|
|
u8 pixel = ReadVRAM_Texture<u8>(vramaddr);
|
2017-03-01 16:49:44 -07:00
|
|
|
|
|
|
|
texpal <<= 4;
|
2020-11-30 08:58:52 -07:00
|
|
|
*color = ReadVRAM_TexPal<u16>(texpal + ((pixel&0x1F)<<1));
|
2017-03-07 14:05:38 -07:00
|
|
|
*alpha = ((pixel >> 3) & 0x1C) + (pixel >> 6);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 2: // 4-color
|
|
|
|
{
|
|
|
|
vramaddr += (((t * width) + s) >> 2);
|
2020-11-30 08:58:52 -07:00
|
|
|
u8 pixel = ReadVRAM_Texture<u8>(vramaddr);
|
2017-03-07 14:05:38 -07:00
|
|
|
pixel >>= ((s & 0x3) << 1);
|
|
|
|
pixel &= 0x3;
|
2017-03-01 16:49:44 -07:00
|
|
|
|
2017-03-07 14:05:38 -07:00
|
|
|
texpal <<= 3;
|
2020-11-30 08:58:52 -07:00
|
|
|
*color = ReadVRAM_TexPal<u16>(texpal + (pixel<<1));
|
2017-03-07 14:05:38 -07:00
|
|
|
*alpha = (pixel==0) ? alpha0 : 31;
|
2017-03-01 16:49:44 -07:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2017-03-07 14:05:38 -07:00
|
|
|
case 3: // 16-color
|
2017-03-01 16:49:44 -07:00
|
|
|
{
|
2017-03-07 14:05:38 -07:00
|
|
|
vramaddr += (((t * width) + s) >> 1);
|
2020-11-30 08:58:52 -07:00
|
|
|
u8 pixel = ReadVRAM_Texture<u8>(vramaddr);
|
2017-03-07 14:05:38 -07:00
|
|
|
if (s & 0x1) pixel >>= 4;
|
|
|
|
else pixel &= 0xF;
|
2017-03-01 16:49:44 -07:00
|
|
|
|
|
|
|
texpal <<= 4;
|
2020-11-30 08:58:52 -07:00
|
|
|
*color = ReadVRAM_TexPal<u16>(texpal + (pixel<<1));
|
2017-03-07 14:05:38 -07:00
|
|
|
*alpha = (pixel==0) ? alpha0 : 31;
|
2017-03-01 17:53:08 -07:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2017-03-07 14:05:38 -07:00
|
|
|
case 4: // 256-color
|
2017-03-06 10:25:20 -07:00
|
|
|
{
|
2017-03-07 14:05:38 -07:00
|
|
|
vramaddr += ((t * width) + s);
|
2020-11-30 08:58:52 -07:00
|
|
|
u8 pixel = ReadVRAM_Texture<u8>(vramaddr);
|
2017-03-06 10:25:20 -07:00
|
|
|
|
2017-03-07 14:05:38 -07:00
|
|
|
texpal <<= 4;
|
2020-11-30 08:58:52 -07:00
|
|
|
*color = ReadVRAM_TexPal<u16>(texpal + (pixel<<1));
|
2017-03-07 14:05:38 -07:00
|
|
|
*alpha = (pixel==0) ? alpha0 : 31;
|
2017-03-06 10:25:20 -07:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2017-03-01 17:53:08 -07:00
|
|
|
case 5: // compressed
|
|
|
|
{
|
|
|
|
vramaddr += ((t & 0x3FC) * (width>>2)) + (s & 0x3FC);
|
|
|
|
vramaddr += (t & 0x3);
|
|
|
|
|
|
|
|
u32 slot1addr = 0x20000 + ((vramaddr & 0x1FFFC) >> 1);
|
|
|
|
if (vramaddr >= 0x40000)
|
|
|
|
slot1addr += 0x10000;
|
|
|
|
|
2020-11-30 08:58:52 -07:00
|
|
|
u8 val = ReadVRAM_Texture<u8>(vramaddr);
|
2017-03-01 17:53:08 -07:00
|
|
|
val >>= (2 * (s & 0x3));
|
|
|
|
|
2020-11-30 08:58:52 -07:00
|
|
|
u16 palinfo = ReadVRAM_Texture<u16>(slot1addr);
|
2017-03-01 17:53:08 -07:00
|
|
|
u32 paloffset = (palinfo & 0x3FFF) << 2;
|
|
|
|
texpal <<= 4;
|
|
|
|
|
|
|
|
switch (val & 0x3)
|
|
|
|
{
|
|
|
|
case 0:
|
2020-11-30 08:58:52 -07:00
|
|
|
*color = ReadVRAM_TexPal<u16>(texpal + paloffset);
|
2017-03-07 14:05:38 -07:00
|
|
|
*alpha = 31;
|
2017-03-01 17:53:08 -07:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 1:
|
2020-11-30 08:58:52 -07:00
|
|
|
*color = ReadVRAM_TexPal<u16>(texpal + paloffset + 2);
|
2017-03-07 14:05:38 -07:00
|
|
|
*alpha = 31;
|
2017-03-01 17:53:08 -07:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 2:
|
|
|
|
if ((palinfo >> 14) == 1)
|
|
|
|
{
|
2020-11-30 08:58:52 -07:00
|
|
|
u16 color0 = ReadVRAM_TexPal<u16>(texpal + paloffset);
|
|
|
|
u16 color1 = ReadVRAM_TexPal<u16>(texpal + paloffset + 2);
|
2017-03-01 17:53:08 -07:00
|
|
|
|
|
|
|
u32 r0 = color0 & 0x001F;
|
|
|
|
u32 g0 = color0 & 0x03E0;
|
|
|
|
u32 b0 = color0 & 0x7C00;
|
|
|
|
u32 r1 = color1 & 0x001F;
|
|
|
|
u32 g1 = color1 & 0x03E0;
|
|
|
|
u32 b1 = color1 & 0x7C00;
|
|
|
|
|
|
|
|
u32 r = (r0 + r1) >> 1;
|
|
|
|
u32 g = ((g0 + g1) >> 1) & 0x03E0;
|
|
|
|
u32 b = ((b0 + b1) >> 1) & 0x7C00;
|
|
|
|
|
2017-03-07 14:05:38 -07:00
|
|
|
*color = r | g | b;
|
2017-03-01 17:53:08 -07:00
|
|
|
}
|
|
|
|
else if ((palinfo >> 14) == 3)
|
|
|
|
{
|
2020-11-30 08:58:52 -07:00
|
|
|
u16 color0 = ReadVRAM_TexPal<u16>(texpal + paloffset);
|
|
|
|
u16 color1 = ReadVRAM_TexPal<u16>(texpal + paloffset + 2);
|
2017-03-01 17:53:08 -07:00
|
|
|
|
|
|
|
u32 r0 = color0 & 0x001F;
|
|
|
|
u32 g0 = color0 & 0x03E0;
|
|
|
|
u32 b0 = color0 & 0x7C00;
|
|
|
|
u32 r1 = color1 & 0x001F;
|
|
|
|
u32 g1 = color1 & 0x03E0;
|
|
|
|
u32 b1 = color1 & 0x7C00;
|
|
|
|
|
|
|
|
u32 r = (r0*5 + r1*3) >> 3;
|
|
|
|
u32 g = ((g0*5 + g1*3) >> 3) & 0x03E0;
|
|
|
|
u32 b = ((b0*5 + b1*3) >> 3) & 0x7C00;
|
|
|
|
|
2017-03-07 14:05:38 -07:00
|
|
|
*color = r | g | b;
|
2017-03-01 17:53:08 -07:00
|
|
|
}
|
|
|
|
else
|
2020-11-30 08:58:52 -07:00
|
|
|
*color = ReadVRAM_TexPal<u16>(texpal + paloffset + 4);
|
2017-03-07 14:05:38 -07:00
|
|
|
*alpha = 31;
|
2017-03-01 17:53:08 -07:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 3:
|
|
|
|
if ((palinfo >> 14) == 2)
|
2017-03-07 14:05:38 -07:00
|
|
|
{
|
2020-11-30 08:58:52 -07:00
|
|
|
*color = ReadVRAM_TexPal<u16>(texpal + paloffset + 6);
|
2017-03-07 14:05:38 -07:00
|
|
|
*alpha = 31;
|
|
|
|
}
|
2017-03-01 17:53:08 -07:00
|
|
|
else if ((palinfo >> 14) == 3)
|
|
|
|
{
|
2020-11-30 08:58:52 -07:00
|
|
|
u16 color0 = ReadVRAM_TexPal<u16>(texpal + paloffset);
|
|
|
|
u16 color1 = ReadVRAM_TexPal<u16>(texpal + paloffset + 2);
|
2017-03-01 17:53:08 -07:00
|
|
|
|
|
|
|
u32 r0 = color0 & 0x001F;
|
|
|
|
u32 g0 = color0 & 0x03E0;
|
|
|
|
u32 b0 = color0 & 0x7C00;
|
|
|
|
u32 r1 = color1 & 0x001F;
|
|
|
|
u32 g1 = color1 & 0x03E0;
|
|
|
|
u32 b1 = color1 & 0x7C00;
|
|
|
|
|
|
|
|
u32 r = (r0*3 + r1*5) >> 3;
|
|
|
|
u32 g = ((g0*3 + g1*5) >> 3) & 0x03E0;
|
|
|
|
u32 b = ((b0*3 + b1*5) >> 3) & 0x7C00;
|
|
|
|
|
2017-03-07 14:05:38 -07:00
|
|
|
*color = r | g | b;
|
|
|
|
*alpha = 31;
|
2017-03-01 17:53:08 -07:00
|
|
|
}
|
|
|
|
else
|
2017-03-07 14:05:38 -07:00
|
|
|
{
|
|
|
|
*color = 0;
|
|
|
|
*alpha = 0;
|
|
|
|
}
|
2017-03-01 17:53:08 -07:00
|
|
|
break;
|
|
|
|
}
|
2017-03-07 14:05:38 -07:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 6: // A5I3
|
|
|
|
{
|
|
|
|
vramaddr += ((t * width) + s);
|
2020-11-30 08:58:52 -07:00
|
|
|
u8 pixel = ReadVRAM_Texture<u8>(vramaddr);
|
2017-03-01 17:53:08 -07:00
|
|
|
|
2017-03-07 14:05:38 -07:00
|
|
|
texpal <<= 4;
|
2020-11-30 08:58:52 -07:00
|
|
|
*color = ReadVRAM_TexPal<u16>(texpal + ((pixel&0x7)<<1));
|
2017-03-07 14:05:38 -07:00
|
|
|
*alpha = (pixel >> 3);
|
2017-03-01 16:49:44 -07:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2017-03-07 14:05:38 -07:00
|
|
|
case 7: // direct color
|
|
|
|
{
|
|
|
|
vramaddr += (((t * width) + s) << 1);
|
2020-11-30 08:58:52 -07:00
|
|
|
*color = ReadVRAM_Texture<u16>(vramaddr);
|
2017-03-07 14:05:38 -07:00
|
|
|
*alpha = (*color & 0x8000) ? 31 : 0;
|
|
|
|
}
|
2017-03-01 16:49:44 -07:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-06 10:38:13 -06:00
|
|
|
// depth test is 'less or equal' instead of 'less than' under the following conditions:
|
|
|
|
// * when drawing a front-facing pixel over an opaque back-facing pixel
|
|
|
|
// * when drawing wireframe edges, under certain conditions (TODO)
|
2018-12-13 14:46:12 -07:00
|
|
|
//
|
|
|
|
// range is different based on depth-buffering mode
|
|
|
|
// Z-buffering: +-0x200
|
|
|
|
// W-buffering: +-0xFF
|
|
|
|
|
|
|
|
bool DepthTest_Equal_Z(s32 dstz, s32 z, u32 dstattr)
|
|
|
|
{
|
|
|
|
s32 diff = dstz - z;
|
|
|
|
if ((u32)(diff + 0x200) <= 0x400)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
2017-07-06 10:38:13 -06:00
|
|
|
|
2018-12-13 14:46:12 -07:00
|
|
|
bool DepthTest_Equal_W(s32 dstz, s32 z, u32 dstattr)
|
2017-07-06 10:38:13 -06:00
|
|
|
{
|
|
|
|
s32 diff = dstz - z;
|
2018-12-13 14:46:12 -07:00
|
|
|
if ((u32)(diff + 0xFF) <= 0x1FE)
|
2017-07-06 10:38:13 -06:00
|
|
|
return true;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool DepthTest_LessThan(s32 dstz, s32 z, u32 dstattr)
|
2017-03-01 16:49:44 -07:00
|
|
|
{
|
2017-07-06 10:38:13 -06:00
|
|
|
if (z < dstz)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool DepthTest_LessThan_FrontFacing(s32 dstz, s32 z, u32 dstattr)
|
|
|
|
{
|
2017-07-15 07:24:25 -06:00
|
|
|
if ((dstattr & 0x00400010) == 0x00000010) // opaque, back facing
|
2017-02-13 19:29:02 -07:00
|
|
|
{
|
2017-07-06 10:38:13 -06:00
|
|
|
if (z <= dstz)
|
2017-03-12 17:45:26 -06:00
|
|
|
return true;
|
2017-02-13 19:29:02 -07:00
|
|
|
}
|
|
|
|
else
|
2017-07-06 10:38:13 -06:00
|
|
|
{
|
|
|
|
if (z < dstz)
|
|
|
|
return true;
|
|
|
|
}
|
2017-02-13 19:29:02 -07:00
|
|
|
|
2017-03-12 17:45:26 -06:00
|
|
|
return false;
|
|
|
|
}
|
2017-02-13 19:29:02 -07:00
|
|
|
|
2017-05-31 16:45:47 -06:00
|
|
|
u32 AlphaBlend(u32 srccolor, u32 dstcolor, u32 alpha)
|
|
|
|
{
|
|
|
|
u32 dstalpha = dstcolor >> 24;
|
|
|
|
|
|
|
|
if (dstalpha == 0)
|
|
|
|
return srccolor;
|
|
|
|
|
|
|
|
u32 srcR = srccolor & 0x3F;
|
|
|
|
u32 srcG = (srccolor >> 8) & 0x3F;
|
|
|
|
u32 srcB = (srccolor >> 16) & 0x3F;
|
|
|
|
|
|
|
|
if (RenderDispCnt & (1<<3))
|
|
|
|
{
|
|
|
|
u32 dstR = dstcolor & 0x3F;
|
|
|
|
u32 dstG = (dstcolor >> 8) & 0x3F;
|
|
|
|
u32 dstB = (dstcolor >> 16) & 0x3F;
|
|
|
|
|
|
|
|
alpha++;
|
|
|
|
srcR = ((srcR * alpha) + (dstR * (32-alpha))) >> 5;
|
|
|
|
srcG = ((srcG * alpha) + (dstG * (32-alpha))) >> 5;
|
|
|
|
srcB = ((srcB * alpha) + (dstB * (32-alpha))) >> 5;
|
|
|
|
alpha--;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (alpha > dstalpha)
|
|
|
|
dstalpha = alpha;
|
|
|
|
|
|
|
|
return srcR | (srcG << 8) | (srcB << 16) | (dstalpha << 24);
|
|
|
|
}
|
|
|
|
|
2021-02-09 15:38:51 -07:00
|
|
|
u32 SoftRenderer::RenderPixel(Polygon* polygon, u8 vr, u8 vg, u8 vb, s16 s, s16 t)
|
2017-03-12 17:45:26 -06:00
|
|
|
{
|
2017-03-07 14:05:38 -07:00
|
|
|
u8 r, g, b, a;
|
2017-03-01 16:49:44 -07:00
|
|
|
|
2017-04-12 12:25:54 -06:00
|
|
|
u32 blendmode = (polygon->Attr >> 4) & 0x3;
|
2017-03-14 17:10:32 -06:00
|
|
|
u32 polyalpha = (polygon->Attr >> 16) & 0x1F;
|
|
|
|
bool wireframe = (polyalpha == 0);
|
2017-03-01 16:49:44 -07:00
|
|
|
|
2017-04-12 12:25:54 -06:00
|
|
|
if (blendmode == 2)
|
|
|
|
{
|
2017-06-28 01:19:38 -06:00
|
|
|
if (RenderDispCnt & (1<<1))
|
|
|
|
{
|
|
|
|
// highlight mode: color is calculated normally
|
|
|
|
// except all vertex color components are set
|
|
|
|
// to the red component
|
|
|
|
// the toon color is added to the final color
|
2017-04-12 12:25:54 -06:00
|
|
|
|
2017-06-28 01:19:38 -06:00
|
|
|
vg = vr;
|
|
|
|
vb = vr;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// toon mode: vertex color is replaced by toon color
|
|
|
|
|
|
|
|
u16 tooncolor = RenderToonTable[vr >> 1];
|
|
|
|
|
|
|
|
vr = (tooncolor << 1) & 0x3E; if (vr) vr++;
|
|
|
|
vg = (tooncolor >> 4) & 0x3E; if (vg) vg++;
|
|
|
|
vb = (tooncolor >> 9) & 0x3E; if (vb) vb++;
|
|
|
|
}
|
2017-04-12 12:25:54 -06:00
|
|
|
}
|
|
|
|
|
2017-05-09 14:22:52 -06:00
|
|
|
if ((RenderDispCnt & (1<<0)) && (((polygon->TexParam >> 26) & 0x7) != 0))
|
2017-03-14 17:10:32 -06:00
|
|
|
{
|
2017-03-01 16:49:44 -07:00
|
|
|
u8 tr, tg, tb;
|
2017-03-07 14:05:38 -07:00
|
|
|
|
|
|
|
u16 tcolor; u8 talpha;
|
|
|
|
TextureLookup(polygon->TexParam, polygon->TexPalette, s, t, &tcolor, &talpha);
|
|
|
|
|
|
|
|
tr = (tcolor << 1) & 0x3E; if (tr) tr++;
|
|
|
|
tg = (tcolor >> 4) & 0x3E; if (tg) tg++;
|
|
|
|
tb = (tcolor >> 9) & 0x3E; if (tb) tb++;
|
2017-03-01 16:49:44 -07:00
|
|
|
|
2017-04-21 14:40:15 -06:00
|
|
|
if (blendmode & 0x1)
|
2017-04-12 12:25:54 -06:00
|
|
|
{
|
|
|
|
// decal
|
|
|
|
|
|
|
|
if (talpha == 0)
|
|
|
|
{
|
|
|
|
r = vr;
|
|
|
|
g = vg;
|
|
|
|
b = vb;
|
|
|
|
}
|
|
|
|
else if (talpha == 31)
|
|
|
|
{
|
|
|
|
r = tr;
|
|
|
|
g = tg;
|
|
|
|
b = tb;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
r = ((tr * talpha) + (vr * (31-talpha))) >> 5;
|
|
|
|
g = ((tg * talpha) + (vg * (31-talpha))) >> 5;
|
|
|
|
b = ((tb * talpha) + (vb * (31-talpha))) >> 5;
|
|
|
|
}
|
|
|
|
a = polyalpha;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// modulate
|
|
|
|
|
|
|
|
r = ((tr+1) * (vr+1) - 1) >> 6;
|
|
|
|
g = ((tg+1) * (vg+1) - 1) >> 6;
|
|
|
|
b = ((tb+1) * (vb+1) - 1) >> 6;
|
|
|
|
a = ((talpha+1) * (polyalpha+1) - 1) >> 5;
|
|
|
|
}
|
2017-03-01 16:49:44 -07:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
r = vr;
|
|
|
|
g = vg;
|
|
|
|
b = vb;
|
2017-03-14 17:10:32 -06:00
|
|
|
a = polyalpha;
|
2017-03-01 16:49:44 -07:00
|
|
|
}
|
|
|
|
|
2017-05-09 14:22:52 -06:00
|
|
|
if ((blendmode == 2) && (RenderDispCnt & (1<<1)))
|
2017-04-12 12:25:54 -06:00
|
|
|
{
|
2017-06-28 01:19:38 -06:00
|
|
|
u16 tooncolor = RenderToonTable[vr >> 1];
|
|
|
|
|
|
|
|
vr = (tooncolor << 1) & 0x3E; if (vr) vr++;
|
|
|
|
vg = (tooncolor >> 4) & 0x3E; if (vg) vg++;
|
|
|
|
vb = (tooncolor >> 9) & 0x3E; if (vb) vb++;
|
|
|
|
|
2017-04-12 12:25:54 -06:00
|
|
|
r += vr;
|
|
|
|
g += vg;
|
|
|
|
b += vb;
|
|
|
|
|
|
|
|
if (r > 63) r = 63;
|
|
|
|
if (g > 63) g = 63;
|
|
|
|
if (b > 63) b = 63;
|
2017-04-13 11:53:09 -06:00
|
|
|
}
|
2017-04-12 12:25:54 -06:00
|
|
|
|
|
|
|
// checkme: can wireframe polygons use texture alpha?
|
2017-03-14 17:10:32 -06:00
|
|
|
if (wireframe) a = 31;
|
2017-03-06 15:57:04 -07:00
|
|
|
|
2017-03-12 17:45:26 -06:00
|
|
|
return r | (g << 8) | (b << 16) | (a << 24);
|
2017-02-13 19:29:02 -07:00
|
|
|
}
|
|
|
|
|
2021-02-09 15:38:51 -07:00
|
|
|
void SoftRenderer::PlotTranslucentPixel(u32 pixeladdr, u32 color, u32 z, u32 polyattr, u32 shadow)
|
2017-08-28 14:19:49 -06:00
|
|
|
{
|
|
|
|
u32 dstattr = AttrBuffer[pixeladdr];
|
|
|
|
u32 attr = (polyattr & 0xE0F0) | ((polyattr >> 8) & 0xFF0000) | (1<<22) | (dstattr & 0xFF001F0F);
|
|
|
|
|
|
|
|
if (shadow)
|
|
|
|
{
|
|
|
|
// for shadows, opaque pixels are also checked
|
|
|
|
if (dstattr & (1<<22))
|
|
|
|
{
|
|
|
|
if ((dstattr & 0x007F0000) == (attr & 0x007F0000))
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if ((dstattr & 0x3F000000) == (polyattr & 0x3F000000))
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// skip if translucent polygon IDs are equal
|
|
|
|
if ((dstattr & 0x007F0000) == (attr & 0x007F0000))
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// fog flag
|
|
|
|
if (!(dstattr & (1<<15)))
|
|
|
|
attr &= ~(1<<15);
|
|
|
|
|
|
|
|
color = AlphaBlend(color, ColorBuffer[pixeladdr], color>>24);
|
|
|
|
|
|
|
|
if (z != -1)
|
|
|
|
DepthBuffer[pixeladdr] = z;
|
|
|
|
|
|
|
|
ColorBuffer[pixeladdr] = color;
|
|
|
|
AttrBuffer[pixeladdr] = attr;
|
|
|
|
}
|
|
|
|
|
2021-02-09 15:38:51 -07:00
|
|
|
void SoftRenderer::SetupPolygonLeftEdge(SoftRenderer::RendererPolygon* rp, s32 y)
|
2017-05-21 12:14:03 -06:00
|
|
|
{
|
|
|
|
Polygon* polygon = rp->PolyData;
|
|
|
|
|
|
|
|
while (y >= polygon->Vertices[rp->NextVL]->FinalPosition[1] && rp->CurVL != polygon->VBottom)
|
|
|
|
{
|
|
|
|
rp->CurVL = rp->NextVL;
|
|
|
|
|
|
|
|
if (polygon->FacingView)
|
|
|
|
{
|
|
|
|
rp->NextVL = rp->CurVL + 1;
|
|
|
|
if (rp->NextVL >= polygon->NumVertices)
|
|
|
|
rp->NextVL = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
rp->NextVL = rp->CurVL - 1;
|
|
|
|
if ((s32)rp->NextVL < 0)
|
|
|
|
rp->NextVL = polygon->NumVertices - 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
rp->XL = rp->SlopeL.Setup(polygon->Vertices[rp->CurVL]->FinalPosition[0], polygon->Vertices[rp->NextVL]->FinalPosition[0],
|
|
|
|
polygon->Vertices[rp->CurVL]->FinalPosition[1], polygon->Vertices[rp->NextVL]->FinalPosition[1],
|
2017-06-28 13:19:44 -06:00
|
|
|
polygon->FinalW[rp->CurVL], polygon->FinalW[rp->NextVL], y);
|
2017-05-21 12:14:03 -06:00
|
|
|
}
|
|
|
|
|
2021-02-09 15:38:51 -07:00
|
|
|
void SoftRenderer::SetupPolygonRightEdge(SoftRenderer::RendererPolygon* rp, s32 y)
|
2017-05-21 12:14:03 -06:00
|
|
|
{
|
|
|
|
Polygon* polygon = rp->PolyData;
|
|
|
|
|
|
|
|
while (y >= polygon->Vertices[rp->NextVR]->FinalPosition[1] && rp->CurVR != polygon->VBottom)
|
|
|
|
{
|
|
|
|
rp->CurVR = rp->NextVR;
|
|
|
|
|
|
|
|
if (polygon->FacingView)
|
|
|
|
{
|
|
|
|
rp->NextVR = rp->CurVR - 1;
|
|
|
|
if ((s32)rp->NextVR < 0)
|
|
|
|
rp->NextVR = polygon->NumVertices - 1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
rp->NextVR = rp->CurVR + 1;
|
|
|
|
if (rp->NextVR >= polygon->NumVertices)
|
|
|
|
rp->NextVR = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
rp->XR = rp->SlopeR.Setup(polygon->Vertices[rp->CurVR]->FinalPosition[0], polygon->Vertices[rp->NextVR]->FinalPosition[0],
|
|
|
|
polygon->Vertices[rp->CurVR]->FinalPosition[1], polygon->Vertices[rp->NextVR]->FinalPosition[1],
|
2017-06-28 13:19:44 -06:00
|
|
|
polygon->FinalW[rp->CurVR], polygon->FinalW[rp->NextVR], y);
|
2017-05-21 12:14:03 -06:00
|
|
|
}
|
|
|
|
|
2021-02-09 15:38:51 -07:00
|
|
|
void SoftRenderer::SetupPolygon(SoftRenderer::RendererPolygon* rp, Polygon* polygon)
|
2017-05-21 12:14:03 -06:00
|
|
|
{
|
|
|
|
u32 nverts = polygon->NumVertices;
|
|
|
|
|
|
|
|
u32 vtop = polygon->VTop, vbot = polygon->VBottom;
|
|
|
|
s32 ytop = polygon->YTop, ybot = polygon->YBottom;
|
|
|
|
|
|
|
|
rp->PolyData = polygon;
|
|
|
|
|
|
|
|
rp->CurVL = vtop;
|
|
|
|
rp->CurVR = vtop;
|
|
|
|
|
|
|
|
if (polygon->FacingView)
|
|
|
|
{
|
|
|
|
rp->NextVL = rp->CurVL + 1;
|
|
|
|
if (rp->NextVL >= nverts) rp->NextVL = 0;
|
|
|
|
rp->NextVR = rp->CurVR - 1;
|
|
|
|
if ((s32)rp->NextVR < 0) rp->NextVR = nverts - 1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
rp->NextVL = rp->CurVL - 1;
|
|
|
|
if ((s32)rp->NextVL < 0) rp->NextVL = nverts - 1;
|
|
|
|
rp->NextVR = rp->CurVR + 1;
|
|
|
|
if (rp->NextVR >= nverts) rp->NextVR = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ybot == ytop)
|
|
|
|
{
|
|
|
|
vtop = 0; vbot = 0;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
i = 1;
|
|
|
|
if (polygon->Vertices[i]->FinalPosition[0] < polygon->Vertices[vtop]->FinalPosition[0]) vtop = i;
|
|
|
|
if (polygon->Vertices[i]->FinalPosition[0] > polygon->Vertices[vbot]->FinalPosition[0]) vbot = i;
|
|
|
|
|
|
|
|
i = nverts - 1;
|
|
|
|
if (polygon->Vertices[i]->FinalPosition[0] < polygon->Vertices[vtop]->FinalPosition[0]) vtop = i;
|
|
|
|
if (polygon->Vertices[i]->FinalPosition[0] > polygon->Vertices[vbot]->FinalPosition[0]) vbot = i;
|
|
|
|
|
|
|
|
rp->CurVL = vtop; rp->NextVL = vtop;
|
|
|
|
rp->CurVR = vbot; rp->NextVR = vbot;
|
|
|
|
|
2017-05-28 10:36:42 -06:00
|
|
|
rp->XL = rp->SlopeL.SetupDummy(polygon->Vertices[rp->CurVL]->FinalPosition[0]);
|
|
|
|
rp->XR = rp->SlopeR.SetupDummy(polygon->Vertices[rp->CurVR]->FinalPosition[0]);
|
2017-05-21 12:14:03 -06:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
SetupPolygonLeftEdge(rp, ytop);
|
|
|
|
SetupPolygonRightEdge(rp, ytop);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-09 15:38:51 -07:00
|
|
|
void SoftRenderer::RenderShadowMaskScanline(RendererPolygon* rp, s32 y)
|
2017-05-21 12:14:03 -06:00
|
|
|
{
|
|
|
|
Polygon* polygon = rp->PolyData;
|
|
|
|
|
2017-07-06 10:38:13 -06:00
|
|
|
u32 polyattr = (polygon->Attr & 0x3F008000);
|
2017-07-15 07:24:25 -06:00
|
|
|
if (!polygon->FacingView) polyattr |= (1<<4);
|
2017-07-06 10:38:13 -06:00
|
|
|
|
2017-05-21 12:14:03 -06:00
|
|
|
u32 polyalpha = (polygon->Attr >> 16) & 0x1F;
|
|
|
|
bool wireframe = (polyalpha == 0);
|
|
|
|
|
2017-07-06 10:38:13 -06:00
|
|
|
bool (*fnDepthTest)(s32 dstz, s32 z, u32 dstattr);
|
2017-05-21 12:14:03 -06:00
|
|
|
if (polygon->Attr & (1<<14))
|
2018-12-13 14:46:12 -07:00
|
|
|
fnDepthTest = polygon->WBuffer ? DepthTest_Equal_W : DepthTest_Equal_Z;
|
2017-07-06 10:38:13 -06:00
|
|
|
else if (polygon->FacingView)
|
|
|
|
fnDepthTest = DepthTest_LessThan_FrontFacing;
|
2017-05-21 12:14:03 -06:00
|
|
|
else
|
2017-07-06 10:38:13 -06:00
|
|
|
fnDepthTest = DepthTest_LessThan;
|
2017-05-21 12:14:03 -06:00
|
|
|
|
2017-08-28 12:27:56 -06:00
|
|
|
if (!PrevIsShadowMask)
|
2017-05-22 14:29:21 -06:00
|
|
|
memset(&StencilBuffer[256 * (y&0x1)], 0, 256);
|
|
|
|
|
2017-08-28 12:27:56 -06:00
|
|
|
PrevIsShadowMask = true;
|
|
|
|
|
|
|
|
if (polygon->YTop != polygon->YBottom)
|
|
|
|
{
|
|
|
|
if (y >= polygon->Vertices[rp->NextVL]->FinalPosition[1] && rp->CurVL != polygon->VBottom)
|
|
|
|
{
|
|
|
|
SetupPolygonLeftEdge(rp, y);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (y >= polygon->Vertices[rp->NextVR]->FinalPosition[1] && rp->CurVR != polygon->VBottom)
|
|
|
|
{
|
|
|
|
SetupPolygonRightEdge(rp, y);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Vertex *vlcur, *vlnext, *vrcur, *vrnext;
|
|
|
|
s32 xstart, xend;
|
|
|
|
bool l_filledge, r_filledge;
|
|
|
|
s32 l_edgelen, r_edgelen;
|
|
|
|
s32 l_edgecov, r_edgecov;
|
|
|
|
Interpolator<1>* interp_start;
|
|
|
|
Interpolator<1>* interp_end;
|
|
|
|
|
|
|
|
xstart = rp->XL;
|
|
|
|
xend = rp->XR;
|
|
|
|
|
|
|
|
// CHECKME: edge fill rules for opaque shadow mask polygons
|
|
|
|
|
|
|
|
if ((polyalpha < 31) || (RenderDispCnt & (3<<4)))
|
|
|
|
{
|
|
|
|
l_filledge = true;
|
|
|
|
r_filledge = true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
l_filledge = (rp->SlopeL.Negative || !rp->SlopeL.XMajor);
|
|
|
|
r_filledge = (!rp->SlopeR.Negative && rp->SlopeR.XMajor) || (rp->SlopeR.Increment==0);
|
|
|
|
}
|
|
|
|
|
|
|
|
s32 wl = rp->SlopeL.Interp.Interpolate(polygon->FinalW[rp->CurVL], polygon->FinalW[rp->NextVL]);
|
|
|
|
s32 wr = rp->SlopeR.Interp.Interpolate(polygon->FinalW[rp->CurVR], polygon->FinalW[rp->NextVR]);
|
|
|
|
|
|
|
|
s32 zl = rp->SlopeL.Interp.InterpolateZ(polygon->FinalZ[rp->CurVL], polygon->FinalZ[rp->NextVL], polygon->WBuffer);
|
|
|
|
s32 zr = rp->SlopeR.Interp.InterpolateZ(polygon->FinalZ[rp->CurVR], polygon->FinalZ[rp->NextVR], polygon->WBuffer);
|
|
|
|
|
|
|
|
// if the left and right edges are swapped, render backwards.
|
|
|
|
if (xstart > xend)
|
|
|
|
{
|
|
|
|
vlcur = polygon->Vertices[rp->CurVR];
|
|
|
|
vlnext = polygon->Vertices[rp->NextVR];
|
|
|
|
vrcur = polygon->Vertices[rp->CurVL];
|
|
|
|
vrnext = polygon->Vertices[rp->NextVL];
|
|
|
|
|
|
|
|
interp_start = &rp->SlopeR.Interp;
|
|
|
|
interp_end = &rp->SlopeL.Interp;
|
|
|
|
|
|
|
|
rp->SlopeR.EdgeParams_YMajor(&l_edgelen, &l_edgecov);
|
|
|
|
rp->SlopeL.EdgeParams_YMajor(&r_edgelen, &r_edgecov);
|
|
|
|
|
2021-08-20 17:54:09 -06:00
|
|
|
std::swap(xstart, xend);
|
|
|
|
std::swap(wl, wr);
|
|
|
|
std::swap(zl, zr);
|
|
|
|
std::swap(l_filledge, r_filledge);
|
2017-08-28 12:27:56 -06:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
vlcur = polygon->Vertices[rp->CurVL];
|
|
|
|
vlnext = polygon->Vertices[rp->NextVL];
|
|
|
|
vrcur = polygon->Vertices[rp->CurVR];
|
|
|
|
vrnext = polygon->Vertices[rp->NextVR];
|
|
|
|
|
|
|
|
interp_start = &rp->SlopeL.Interp;
|
|
|
|
interp_end = &rp->SlopeR.Interp;
|
|
|
|
|
|
|
|
rp->SlopeL.EdgeParams(&l_edgelen, &l_edgecov);
|
|
|
|
rp->SlopeR.EdgeParams(&r_edgelen, &r_edgecov);
|
|
|
|
}
|
|
|
|
|
|
|
|
// color/texcoord attributes aren't needed for shadow masks
|
|
|
|
// all the pixels are guaranteed to have the same alpha
|
|
|
|
// even if a texture is used (decal blending is used for shadows)
|
|
|
|
// similarly, we can perform alpha test early (checkme)
|
|
|
|
|
|
|
|
if (wireframe) polyalpha = 31;
|
|
|
|
if (polyalpha <= RenderAlphaRef) return;
|
|
|
|
|
|
|
|
// in wireframe mode, there are special rules for equal Z (TODO)
|
|
|
|
|
|
|
|
int yedge = 0;
|
|
|
|
if (y == polygon->YTop) yedge = 0x4;
|
|
|
|
else if (y == polygon->YBottom-1) yedge = 0x8;
|
|
|
|
int edge;
|
|
|
|
|
|
|
|
s32 x = xstart;
|
|
|
|
Interpolator<0> interpX(xstart, xend+1, wl, wr);
|
|
|
|
|
|
|
|
if (x < 0) x = 0;
|
|
|
|
s32 xlimit;
|
|
|
|
|
|
|
|
// for shadow masks: set stencil bits where the depth test fails.
|
|
|
|
// draw nothing.
|
|
|
|
|
|
|
|
// part 1: left edge
|
|
|
|
edge = yedge | 0x1;
|
2018-11-04 15:21:58 -07:00
|
|
|
xlimit = xstart+l_edgelen;
|
|
|
|
if (xlimit > xend+1) xlimit = xend+1;
|
|
|
|
if (xlimit > 256) xlimit = 256;
|
2017-08-28 12:27:56 -06:00
|
|
|
|
|
|
|
for (; x < xlimit; x++)
|
|
|
|
{
|
|
|
|
u32 pixeladdr = FirstPixelOffset + (y*ScanlineWidth) + x;
|
|
|
|
|
|
|
|
interpX.SetX(x);
|
|
|
|
|
|
|
|
s32 z = interpX.InterpolateZ(zl, zr, polygon->WBuffer);
|
|
|
|
u32 dstattr = AttrBuffer[pixeladdr];
|
|
|
|
|
|
|
|
// checkme
|
|
|
|
if (!l_filledge)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (!fnDepthTest(DepthBuffer[pixeladdr], z, dstattr))
|
2021-05-07 16:12:48 -06:00
|
|
|
StencilBuffer[256*(y&0x1) + x] = 1;
|
2018-10-21 17:36:04 -06:00
|
|
|
|
|
|
|
if (dstattr & 0x3)
|
|
|
|
{
|
|
|
|
pixeladdr += BufferSize;
|
|
|
|
if (!fnDepthTest(DepthBuffer[pixeladdr], z, AttrBuffer[pixeladdr]))
|
|
|
|
StencilBuffer[256*(y&0x1) + x] |= 0x2;
|
|
|
|
}
|
2017-08-28 12:27:56 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// part 2: polygon inside
|
|
|
|
edge = yedge;
|
2018-11-04 15:21:58 -07:00
|
|
|
xlimit = xend-r_edgelen+1;
|
|
|
|
if (xlimit > xend+1) xlimit = xend+1;
|
|
|
|
if (xlimit > 256) xlimit = 256;
|
2017-08-28 12:27:56 -06:00
|
|
|
if (wireframe && !edge) x = xlimit;
|
|
|
|
else for (; x < xlimit; x++)
|
|
|
|
{
|
|
|
|
u32 pixeladdr = FirstPixelOffset + (y*ScanlineWidth) + x;
|
|
|
|
|
|
|
|
interpX.SetX(x);
|
|
|
|
|
|
|
|
s32 z = interpX.InterpolateZ(zl, zr, polygon->WBuffer);
|
|
|
|
u32 dstattr = AttrBuffer[pixeladdr];
|
|
|
|
|
|
|
|
if (!fnDepthTest(DepthBuffer[pixeladdr], z, dstattr))
|
|
|
|
StencilBuffer[256*(y&0x1) + x] = 1;
|
2018-10-21 17:36:04 -06:00
|
|
|
|
|
|
|
if (dstattr & 0x3)
|
|
|
|
{
|
|
|
|
pixeladdr += BufferSize;
|
|
|
|
if (!fnDepthTest(DepthBuffer[pixeladdr], z, AttrBuffer[pixeladdr]))
|
|
|
|
StencilBuffer[256*(y&0x1) + x] |= 0x2;
|
|
|
|
}
|
2017-08-28 12:27:56 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// part 3: right edge
|
|
|
|
edge = yedge | 0x2;
|
2018-11-04 15:21:58 -07:00
|
|
|
xlimit = xend+1;
|
|
|
|
if (xlimit > 256) xlimit = 256;
|
2017-08-28 12:27:56 -06:00
|
|
|
|
|
|
|
for (; x < xlimit; x++)
|
|
|
|
{
|
|
|
|
u32 pixeladdr = FirstPixelOffset + (y*ScanlineWidth) + x;
|
|
|
|
|
|
|
|
interpX.SetX(x);
|
|
|
|
|
|
|
|
s32 z = interpX.InterpolateZ(zl, zr, polygon->WBuffer);
|
|
|
|
u32 dstattr = AttrBuffer[pixeladdr];
|
|
|
|
|
|
|
|
// checkme
|
|
|
|
if (!r_filledge)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (!fnDepthTest(DepthBuffer[pixeladdr], z, dstattr))
|
|
|
|
StencilBuffer[256*(y&0x1) + x] = 1;
|
2018-10-21 17:36:04 -06:00
|
|
|
|
|
|
|
if (dstattr & 0x3)
|
|
|
|
{
|
|
|
|
pixeladdr += BufferSize;
|
|
|
|
if (!fnDepthTest(DepthBuffer[pixeladdr], z, AttrBuffer[pixeladdr]))
|
|
|
|
StencilBuffer[256*(y&0x1) + x] |= 0x2;
|
|
|
|
}
|
2017-08-28 12:27:56 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
rp->XL = rp->SlopeL.Step();
|
|
|
|
rp->XR = rp->SlopeR.Step();
|
|
|
|
}
|
|
|
|
|
2021-02-09 15:38:51 -07:00
|
|
|
void SoftRenderer::RenderPolygonScanline(RendererPolygon* rp, s32 y)
|
2017-08-28 12:27:56 -06:00
|
|
|
{
|
|
|
|
Polygon* polygon = rp->PolyData;
|
|
|
|
|
|
|
|
u32 polyattr = (polygon->Attr & 0x3F008000);
|
|
|
|
if (!polygon->FacingView) polyattr |= (1<<4);
|
|
|
|
|
|
|
|
u32 polyalpha = (polygon->Attr >> 16) & 0x1F;
|
|
|
|
bool wireframe = (polyalpha == 0);
|
|
|
|
|
|
|
|
bool (*fnDepthTest)(s32 dstz, s32 z, u32 dstattr);
|
|
|
|
if (polygon->Attr & (1<<14))
|
2018-12-13 14:46:12 -07:00
|
|
|
fnDepthTest = polygon->WBuffer ? DepthTest_Equal_W : DepthTest_Equal_Z;
|
2017-08-28 12:27:56 -06:00
|
|
|
else if (polygon->FacingView)
|
|
|
|
fnDepthTest = DepthTest_LessThan_FrontFacing;
|
|
|
|
else
|
|
|
|
fnDepthTest = DepthTest_LessThan;
|
|
|
|
|
|
|
|
PrevIsShadowMask = false;
|
2017-05-25 20:00:15 -06:00
|
|
|
|
2017-05-21 12:14:03 -06:00
|
|
|
if (polygon->YTop != polygon->YBottom)
|
|
|
|
{
|
|
|
|
if (y >= polygon->Vertices[rp->NextVL]->FinalPosition[1] && rp->CurVL != polygon->VBottom)
|
|
|
|
{
|
|
|
|
SetupPolygonLeftEdge(rp, y);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (y >= polygon->Vertices[rp->NextVR]->FinalPosition[1] && rp->CurVR != polygon->VBottom)
|
|
|
|
{
|
|
|
|
SetupPolygonRightEdge(rp, y);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Vertex *vlcur, *vlnext, *vrcur, *vrnext;
|
|
|
|
s32 xstart, xend;
|
2017-05-28 10:36:42 -06:00
|
|
|
bool l_filledge, r_filledge;
|
|
|
|
s32 l_edgelen, r_edgelen;
|
|
|
|
s32 l_edgecov, r_edgecov;
|
2017-08-17 15:34:37 -06:00
|
|
|
Interpolator<1>* interp_start;
|
|
|
|
Interpolator<1>* interp_end;
|
2017-05-21 12:14:03 -06:00
|
|
|
|
|
|
|
xstart = rp->XL;
|
|
|
|
xend = rp->XR;
|
|
|
|
|
2017-05-28 10:36:42 -06:00
|
|
|
// edge fill rules for opaque pixels:
|
|
|
|
// * right edge is filled if slope > 1
|
|
|
|
// * left edge is filled if slope <= 1
|
|
|
|
// * edges with slope = 0 are always filled
|
2017-06-04 07:55:23 -06:00
|
|
|
// right vertical edges are pushed 1px to the left
|
|
|
|
// edges are always filled if antialiasing/edgemarking are enabled or if the pixels are translucent
|
2017-05-28 10:36:42 -06:00
|
|
|
|
2019-06-10 19:10:32 -06:00
|
|
|
if (wireframe || (RenderDispCnt & ((1<<4)|(1<<5))))
|
2017-06-03 14:33:14 -06:00
|
|
|
{
|
|
|
|
l_filledge = true;
|
|
|
|
r_filledge = true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
l_filledge = (rp->SlopeL.Negative || !rp->SlopeL.XMajor);
|
|
|
|
r_filledge = (!rp->SlopeR.Negative && rp->SlopeR.XMajor) || (rp->SlopeR.Increment==0);
|
|
|
|
}
|
2017-05-28 10:36:42 -06:00
|
|
|
|
2017-05-21 12:14:03 -06:00
|
|
|
s32 wl = rp->SlopeL.Interp.Interpolate(polygon->FinalW[rp->CurVL], polygon->FinalW[rp->NextVL]);
|
|
|
|
s32 wr = rp->SlopeR.Interp.Interpolate(polygon->FinalW[rp->CurVR], polygon->FinalW[rp->NextVR]);
|
|
|
|
|
|
|
|
s32 zl = rp->SlopeL.Interp.InterpolateZ(polygon->FinalZ[rp->CurVL], polygon->FinalZ[rp->NextVL], polygon->WBuffer);
|
|
|
|
s32 zr = rp->SlopeR.Interp.InterpolateZ(polygon->FinalZ[rp->CurVR], polygon->FinalZ[rp->NextVR], polygon->WBuffer);
|
|
|
|
|
|
|
|
// if the left and right edges are swapped, render backwards.
|
2017-05-28 10:36:42 -06:00
|
|
|
// on hardware, swapped edges seem to break edge length calculation,
|
|
|
|
// causing X-major edges to be rendered wrong when
|
|
|
|
// wireframe/edgemarking/antialiasing are used
|
|
|
|
// it also causes bad antialiasing, but not sure what's going on (TODO)
|
|
|
|
// most probable explanation is that such slopes are considered to be Y-major
|
|
|
|
|
2017-05-21 12:14:03 -06:00
|
|
|
if (xstart > xend)
|
|
|
|
{
|
|
|
|
vlcur = polygon->Vertices[rp->CurVR];
|
|
|
|
vlnext = polygon->Vertices[rp->NextVR];
|
|
|
|
vrcur = polygon->Vertices[rp->CurVL];
|
|
|
|
vrnext = polygon->Vertices[rp->NextVL];
|
|
|
|
|
2017-05-28 10:36:42 -06:00
|
|
|
interp_start = &rp->SlopeR.Interp;
|
|
|
|
interp_end = &rp->SlopeL.Interp;
|
|
|
|
|
|
|
|
rp->SlopeR.EdgeParams_YMajor(&l_edgelen, &l_edgecov);
|
|
|
|
rp->SlopeL.EdgeParams_YMajor(&r_edgelen, &r_edgecov);
|
2017-05-21 12:14:03 -06:00
|
|
|
|
2021-08-20 17:54:09 -06:00
|
|
|
std::swap(xstart, xend);
|
|
|
|
std::swap(wl, wr);
|
|
|
|
std::swap(zl, zr);
|
|
|
|
std::swap(l_filledge, r_filledge);
|
2017-05-21 12:14:03 -06:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
vlcur = polygon->Vertices[rp->CurVL];
|
|
|
|
vlnext = polygon->Vertices[rp->NextVL];
|
|
|
|
vrcur = polygon->Vertices[rp->CurVR];
|
|
|
|
vrnext = polygon->Vertices[rp->NextVR];
|
|
|
|
|
2017-05-28 10:36:42 -06:00
|
|
|
interp_start = &rp->SlopeL.Interp;
|
|
|
|
interp_end = &rp->SlopeR.Interp;
|
|
|
|
|
|
|
|
rp->SlopeL.EdgeParams(&l_edgelen, &l_edgecov);
|
|
|
|
rp->SlopeR.EdgeParams(&r_edgelen, &r_edgecov);
|
2017-05-21 12:14:03 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// interpolate attributes along Y
|
|
|
|
|
2017-05-28 10:36:42 -06:00
|
|
|
s32 rl = interp_start->Interpolate(vlcur->FinalColor[0], vlnext->FinalColor[0]);
|
|
|
|
s32 gl = interp_start->Interpolate(vlcur->FinalColor[1], vlnext->FinalColor[1]);
|
|
|
|
s32 bl = interp_start->Interpolate(vlcur->FinalColor[2], vlnext->FinalColor[2]);
|
2017-05-21 12:14:03 -06:00
|
|
|
|
2017-05-28 10:36:42 -06:00
|
|
|
s32 sl = interp_start->Interpolate(vlcur->TexCoords[0], vlnext->TexCoords[0]);
|
|
|
|
s32 tl = interp_start->Interpolate(vlcur->TexCoords[1], vlnext->TexCoords[1]);
|
2017-05-21 12:14:03 -06:00
|
|
|
|
2017-05-28 10:36:42 -06:00
|
|
|
s32 rr = interp_end->Interpolate(vrcur->FinalColor[0], vrnext->FinalColor[0]);
|
|
|
|
s32 gr = interp_end->Interpolate(vrcur->FinalColor[1], vrnext->FinalColor[1]);
|
|
|
|
s32 br = interp_end->Interpolate(vrcur->FinalColor[2], vrnext->FinalColor[2]);
|
2017-05-21 12:14:03 -06:00
|
|
|
|
2017-05-28 10:36:42 -06:00
|
|
|
s32 sr = interp_end->Interpolate(vrcur->TexCoords[0], vrnext->TexCoords[0]);
|
|
|
|
s32 tr = interp_end->Interpolate(vrcur->TexCoords[1], vrnext->TexCoords[1]);
|
2017-05-21 12:14:03 -06:00
|
|
|
|
|
|
|
// in wireframe mode, there are special rules for equal Z (TODO)
|
|
|
|
|
|
|
|
int yedge = 0;
|
|
|
|
if (y == polygon->YTop) yedge = 0x4;
|
|
|
|
else if (y == polygon->YBottom-1) yedge = 0x8;
|
2017-05-31 17:02:39 -06:00
|
|
|
int edge;
|
2017-05-21 12:14:03 -06:00
|
|
|
|
2017-05-31 17:02:39 -06:00
|
|
|
s32 x = xstart;
|
2017-08-17 15:34:37 -06:00
|
|
|
Interpolator<0> interpX(xstart, xend+1, wl, wr);
|
2017-05-31 17:02:39 -06:00
|
|
|
|
2017-06-01 06:59:41 -06:00
|
|
|
if (x < 0) x = 0;
|
2017-07-04 11:11:43 -06:00
|
|
|
s32 xlimit;
|
2017-06-01 06:59:41 -06:00
|
|
|
|
2017-08-28 10:37:07 -06:00
|
|
|
s32 xcov = 0;
|
|
|
|
|
2017-05-31 17:02:39 -06:00
|
|
|
// part 1: left edge
|
|
|
|
edge = yedge | 0x1;
|
2018-11-04 15:21:58 -07:00
|
|
|
xlimit = xstart+l_edgelen;
|
|
|
|
if (xlimit > xend+1) xlimit = xend+1;
|
|
|
|
if (xlimit > 256) xlimit = 256;
|
2017-08-28 10:37:07 -06:00
|
|
|
if (l_edgecov & (1<<31))
|
|
|
|
{
|
|
|
|
xcov = (l_edgecov >> 12) & 0x3FF;
|
|
|
|
if (xcov == 0x3FF) xcov = 0;
|
|
|
|
}
|
|
|
|
|
2019-06-10 19:10:32 -06:00
|
|
|
if (!l_filledge) x = std::min(xlimit, xend-r_edgelen+1);
|
|
|
|
else
|
2017-07-04 11:11:43 -06:00
|
|
|
for (; x < xlimit; x++)
|
2017-05-21 12:14:03 -06:00
|
|
|
{
|
2017-06-03 14:10:32 -06:00
|
|
|
u32 pixeladdr = FirstPixelOffset + (y*ScanlineWidth) + x;
|
2018-10-21 17:36:04 -06:00
|
|
|
u32 dstattr = AttrBuffer[pixeladdr];
|
2017-05-21 12:14:03 -06:00
|
|
|
|
2017-05-31 17:02:39 -06:00
|
|
|
// check stencil buffer for shadows
|
|
|
|
if (polygon->IsShadow)
|
2017-05-21 12:14:03 -06:00
|
|
|
{
|
2018-10-21 17:36:04 -06:00
|
|
|
u8 stencil = StencilBuffer[256*(y&0x1) + x];
|
|
|
|
if (!stencil)
|
2017-05-31 17:02:39 -06:00
|
|
|
continue;
|
2018-10-21 17:36:04 -06:00
|
|
|
if (!(stencil & 0x1))
|
|
|
|
pixeladdr += BufferSize;
|
|
|
|
if (!(stencil & 0x2))
|
|
|
|
dstattr &= ~0x3; // quick way to prevent drawing the shadow under antialiased edges
|
2017-05-31 17:02:39 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
interpX.SetX(x);
|
|
|
|
|
|
|
|
s32 z = interpX.InterpolateZ(zl, zr, polygon->WBuffer);
|
|
|
|
|
|
|
|
// if depth test against the topmost pixel fails, test
|
|
|
|
// against the pixel underneath
|
2017-07-06 10:38:13 -06:00
|
|
|
if (!fnDepthTest(DepthBuffer[pixeladdr], z, dstattr))
|
2017-05-31 17:02:39 -06:00
|
|
|
{
|
2021-05-24 11:41:24 -06:00
|
|
|
if (!(dstattr & 0x3) || pixeladdr >= BufferSize) continue;
|
2017-06-01 07:16:44 -06:00
|
|
|
|
2017-06-03 14:10:32 -06:00
|
|
|
pixeladdr += BufferSize;
|
2017-07-15 07:24:25 -06:00
|
|
|
dstattr = AttrBuffer[pixeladdr];
|
|
|
|
if (!fnDepthTest(DepthBuffer[pixeladdr], z, dstattr))
|
2017-05-31 17:02:39 -06:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
u32 vr = interpX.Interpolate(rl, rr);
|
|
|
|
u32 vg = interpX.Interpolate(gl, gr);
|
|
|
|
u32 vb = interpX.Interpolate(bl, br);
|
|
|
|
|
|
|
|
s16 s = interpX.Interpolate(sl, sr);
|
|
|
|
s16 t = interpX.Interpolate(tl, tr);
|
|
|
|
|
|
|
|
u32 color = RenderPixel(polygon, vr>>3, vg>>3, vb>>3, s, t);
|
|
|
|
u8 alpha = color >> 24;
|
|
|
|
|
|
|
|
// alpha test
|
|
|
|
if (alpha <= RenderAlphaRef) continue;
|
|
|
|
|
|
|
|
if (alpha == 31)
|
|
|
|
{
|
2017-08-28 14:19:49 -06:00
|
|
|
u32 attr = polyattr | edge;
|
2017-06-03 13:46:14 -06:00
|
|
|
|
2017-05-31 17:02:39 -06:00
|
|
|
if (RenderDispCnt & (1<<4))
|
|
|
|
{
|
|
|
|
// anti-aliasing: all edges are rendered
|
|
|
|
|
2017-06-03 14:33:14 -06:00
|
|
|
// calculate coverage
|
2017-08-28 10:37:07 -06:00
|
|
|
s32 cov = l_edgecov;
|
|
|
|
if (cov & (1<<31))
|
2017-05-31 17:02:39 -06:00
|
|
|
{
|
2017-08-28 10:37:07 -06:00
|
|
|
cov = xcov >> 5;
|
|
|
|
if (cov > 31) cov = 31;
|
|
|
|
xcov += (l_edgecov & 0x3FF);
|
|
|
|
}
|
2017-06-03 14:33:14 -06:00
|
|
|
attr |= (cov << 8);
|
|
|
|
|
|
|
|
// push old pixel down if needed
|
|
|
|
if (pixeladdr < BufferSize)
|
|
|
|
{
|
|
|
|
ColorBuffer[pixeladdr+BufferSize] = ColorBuffer[pixeladdr];
|
|
|
|
DepthBuffer[pixeladdr+BufferSize] = DepthBuffer[pixeladdr];
|
|
|
|
AttrBuffer[pixeladdr+BufferSize] = AttrBuffer[pixeladdr];
|
2017-05-31 17:02:39 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
DepthBuffer[pixeladdr] = z;
|
2017-08-28 14:19:49 -06:00
|
|
|
ColorBuffer[pixeladdr] = color;
|
|
|
|
AttrBuffer[pixeladdr] = attr;
|
2017-05-31 17:02:39 -06:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-08-28 14:19:49 -06:00
|
|
|
if (!(polygon->Attr & (1<<11))) z = -1;
|
|
|
|
PlotTranslucentPixel(pixeladdr, color, z, polyattr, polygon->IsShadow);
|
2017-05-31 17:02:39 -06:00
|
|
|
|
2017-08-28 14:19:49 -06:00
|
|
|
// blend with bottom pixel too, if needed
|
|
|
|
if ((dstattr & 0x3) && (pixeladdr < BufferSize))
|
|
|
|
PlotTranslucentPixel(pixeladdr+BufferSize, color, z, polyattr, polygon->IsShadow);
|
2017-05-31 17:02:39 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// part 2: polygon inside
|
|
|
|
edge = yedge;
|
2018-11-04 15:21:58 -07:00
|
|
|
xlimit = xend-r_edgelen+1;
|
|
|
|
if (xlimit > xend+1) xlimit = xend+1;
|
|
|
|
if (xlimit > 256) xlimit = 256;
|
2019-06-10 19:10:32 -06:00
|
|
|
|
2017-07-04 11:11:43 -06:00
|
|
|
if (wireframe && !edge) x = xlimit;
|
2019-06-10 19:10:32 -06:00
|
|
|
else
|
|
|
|
for (; x < xlimit; x++)
|
2017-05-31 17:02:39 -06:00
|
|
|
{
|
2017-06-03 14:10:32 -06:00
|
|
|
u32 pixeladdr = FirstPixelOffset + (y*ScanlineWidth) + x;
|
2018-10-21 17:36:04 -06:00
|
|
|
u32 dstattr = AttrBuffer[pixeladdr];
|
2017-05-31 17:02:39 -06:00
|
|
|
|
|
|
|
// check stencil buffer for shadows
|
|
|
|
if (polygon->IsShadow)
|
|
|
|
{
|
2018-10-21 17:36:04 -06:00
|
|
|
u8 stencil = StencilBuffer[256*(y&0x1) + x];
|
|
|
|
if (!stencil)
|
2017-05-31 17:02:39 -06:00
|
|
|
continue;
|
2018-10-21 17:36:04 -06:00
|
|
|
if (!(stencil & 0x1))
|
|
|
|
pixeladdr += BufferSize;
|
|
|
|
if (!(stencil & 0x2))
|
|
|
|
dstattr &= ~0x3; // quick way to prevent drawing the shadow under antialiased edges
|
2017-05-31 17:02:39 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
interpX.SetX(x);
|
|
|
|
|
|
|
|
s32 z = interpX.InterpolateZ(zl, zr, polygon->WBuffer);
|
|
|
|
|
|
|
|
// if depth test against the topmost pixel fails, test
|
|
|
|
// against the pixel underneath
|
2017-07-06 10:38:13 -06:00
|
|
|
if (!fnDepthTest(DepthBuffer[pixeladdr], z, dstattr))
|
2017-05-31 17:02:39 -06:00
|
|
|
{
|
2021-05-24 11:41:24 -06:00
|
|
|
if (!(dstattr & 0x3) || pixeladdr >= BufferSize) continue;
|
2017-06-01 07:16:44 -06:00
|
|
|
|
2017-06-03 14:10:32 -06:00
|
|
|
pixeladdr += BufferSize;
|
2017-07-15 07:24:25 -06:00
|
|
|
dstattr = AttrBuffer[pixeladdr];
|
|
|
|
if (!fnDepthTest(DepthBuffer[pixeladdr], z, dstattr))
|
2017-05-31 17:02:39 -06:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
u32 vr = interpX.Interpolate(rl, rr);
|
|
|
|
u32 vg = interpX.Interpolate(gl, gr);
|
|
|
|
u32 vb = interpX.Interpolate(bl, br);
|
|
|
|
|
|
|
|
s16 s = interpX.Interpolate(sl, sr);
|
|
|
|
s16 t = interpX.Interpolate(tl, tr);
|
|
|
|
|
|
|
|
u32 color = RenderPixel(polygon, vr>>3, vg>>3, vb>>3, s, t);
|
|
|
|
u8 alpha = color >> 24;
|
|
|
|
|
|
|
|
// alpha test
|
|
|
|
if (alpha <= RenderAlphaRef) continue;
|
|
|
|
|
|
|
|
if (alpha == 31)
|
|
|
|
{
|
2017-08-28 14:19:49 -06:00
|
|
|
u32 attr = polyattr | edge;
|
2017-05-31 17:02:39 -06:00
|
|
|
DepthBuffer[pixeladdr] = z;
|
2017-08-28 14:19:49 -06:00
|
|
|
ColorBuffer[pixeladdr] = color;
|
|
|
|
AttrBuffer[pixeladdr] = attr;
|
2017-05-31 17:02:39 -06:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-08-28 14:19:49 -06:00
|
|
|
if (!(polygon->Attr & (1<<11))) z = -1;
|
|
|
|
PlotTranslucentPixel(pixeladdr, color, z, polyattr, polygon->IsShadow);
|
2017-07-15 07:24:25 -06:00
|
|
|
|
2017-08-28 14:19:49 -06:00
|
|
|
// blend with bottom pixel too, if needed
|
|
|
|
if ((dstattr & 0x3) && (pixeladdr < BufferSize))
|
|
|
|
PlotTranslucentPixel(pixeladdr+BufferSize, color, z, polyattr, polygon->IsShadow);
|
2017-05-31 17:02:39 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// part 3: right edge
|
|
|
|
edge = yedge | 0x2;
|
2018-11-04 15:21:58 -07:00
|
|
|
xlimit = xend+1;
|
|
|
|
if (xlimit > 256) xlimit = 256;
|
2017-08-28 10:37:07 -06:00
|
|
|
if (r_edgecov & (1<<31))
|
|
|
|
{
|
|
|
|
xcov = (r_edgecov >> 12) & 0x3FF;
|
|
|
|
if (xcov == 0x3FF) xcov = 0;
|
|
|
|
}
|
|
|
|
|
2019-06-10 19:10:32 -06:00
|
|
|
if (r_filledge)
|
2017-07-04 11:11:43 -06:00
|
|
|
for (; x < xlimit; x++)
|
2017-05-31 17:02:39 -06:00
|
|
|
{
|
2017-06-03 14:10:32 -06:00
|
|
|
u32 pixeladdr = FirstPixelOffset + (y*ScanlineWidth) + x;
|
2018-10-21 17:36:04 -06:00
|
|
|
u32 dstattr = AttrBuffer[pixeladdr];
|
2017-05-21 12:14:03 -06:00
|
|
|
|
|
|
|
// check stencil buffer for shadows
|
|
|
|
if (polygon->IsShadow)
|
|
|
|
{
|
2018-10-21 17:36:04 -06:00
|
|
|
u8 stencil = StencilBuffer[256*(y&0x1) + x];
|
|
|
|
if (!stencil)
|
2017-05-21 12:14:03 -06:00
|
|
|
continue;
|
2018-10-21 17:36:04 -06:00
|
|
|
if (!(stencil & 0x1))
|
|
|
|
pixeladdr += BufferSize;
|
|
|
|
if (!(stencil & 0x2))
|
|
|
|
dstattr &= ~0x3; // quick way to prevent drawing the shadow under antialiased edges
|
2017-05-21 12:14:03 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
interpX.SetX(x);
|
|
|
|
|
|
|
|
s32 z = interpX.InterpolateZ(zl, zr, polygon->WBuffer);
|
|
|
|
|
2017-05-28 11:18:16 -06:00
|
|
|
// if depth test against the topmost pixel fails, test
|
|
|
|
// against the pixel underneath
|
2017-07-06 10:38:13 -06:00
|
|
|
if (!fnDepthTest(DepthBuffer[pixeladdr], z, dstattr))
|
2017-05-28 11:18:16 -06:00
|
|
|
{
|
2021-05-24 11:41:24 -06:00
|
|
|
if (!(dstattr & 0x3) || pixeladdr >= BufferSize) continue;
|
2017-06-01 07:16:44 -06:00
|
|
|
|
2017-06-03 14:10:32 -06:00
|
|
|
pixeladdr += BufferSize;
|
2017-07-15 07:24:25 -06:00
|
|
|
dstattr = AttrBuffer[pixeladdr];
|
|
|
|
if (!fnDepthTest(DepthBuffer[pixeladdr], z, dstattr))
|
2017-05-28 11:18:16 -06:00
|
|
|
continue;
|
|
|
|
}
|
2017-05-21 12:14:03 -06:00
|
|
|
|
|
|
|
u32 vr = interpX.Interpolate(rl, rr);
|
|
|
|
u32 vg = interpX.Interpolate(gl, gr);
|
|
|
|
u32 vb = interpX.Interpolate(bl, br);
|
|
|
|
|
|
|
|
s16 s = interpX.Interpolate(sl, sr);
|
|
|
|
s16 t = interpX.Interpolate(tl, tr);
|
|
|
|
|
|
|
|
u32 color = RenderPixel(polygon, vr>>3, vg>>3, vb>>3, s, t);
|
|
|
|
u8 alpha = color >> 24;
|
|
|
|
|
|
|
|
// alpha test
|
|
|
|
if (alpha <= RenderAlphaRef) continue;
|
|
|
|
|
|
|
|
if (alpha == 31)
|
|
|
|
{
|
2017-08-28 14:19:49 -06:00
|
|
|
u32 attr = polyattr | edge;
|
2017-06-03 13:46:14 -06:00
|
|
|
|
2017-05-31 16:45:47 -06:00
|
|
|
if (RenderDispCnt & (1<<4))
|
2017-05-21 12:14:03 -06:00
|
|
|
{
|
2017-05-31 16:45:47 -06:00
|
|
|
// anti-aliasing: all edges are rendered
|
2017-05-21 12:14:03 -06:00
|
|
|
|
2017-06-03 14:33:14 -06:00
|
|
|
// calculate coverage
|
2017-08-28 10:37:07 -06:00
|
|
|
s32 cov = r_edgecov;
|
|
|
|
if (cov & (1<<31))
|
2017-05-31 16:45:47 -06:00
|
|
|
{
|
2017-08-28 10:37:07 -06:00
|
|
|
cov = 0x1F - (xcov >> 5);
|
|
|
|
if (cov < 0) cov = 0;
|
|
|
|
xcov += (r_edgecov & 0x3FF);
|
|
|
|
}
|
2017-06-03 14:33:14 -06:00
|
|
|
attr |= (cov << 8);
|
|
|
|
|
|
|
|
// push old pixel down if needed
|
|
|
|
if (pixeladdr < BufferSize)
|
2017-05-31 16:45:47 -06:00
|
|
|
{
|
2017-06-03 14:33:14 -06:00
|
|
|
ColorBuffer[pixeladdr+BufferSize] = ColorBuffer[pixeladdr];
|
|
|
|
DepthBuffer[pixeladdr+BufferSize] = DepthBuffer[pixeladdr];
|
|
|
|
AttrBuffer[pixeladdr+BufferSize] = AttrBuffer[pixeladdr];
|
2017-05-31 16:45:47 -06:00
|
|
|
}
|
2017-05-28 11:18:16 -06:00
|
|
|
}
|
|
|
|
|
2017-05-21 12:14:03 -06:00
|
|
|
DepthBuffer[pixeladdr] = z;
|
2017-08-28 14:19:49 -06:00
|
|
|
ColorBuffer[pixeladdr] = color;
|
|
|
|
AttrBuffer[pixeladdr] = attr;
|
2017-05-21 12:14:03 -06:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-08-28 14:19:49 -06:00
|
|
|
if (!(polygon->Attr & (1<<11))) z = -1;
|
|
|
|
PlotTranslucentPixel(pixeladdr, color, z, polyattr, polygon->IsShadow);
|
2017-05-21 12:14:03 -06:00
|
|
|
|
2017-08-28 14:19:49 -06:00
|
|
|
// blend with bottom pixel too, if needed
|
|
|
|
if ((dstattr & 0x3) && (pixeladdr < BufferSize))
|
|
|
|
PlotTranslucentPixel(pixeladdr+BufferSize, color, z, polyattr, polygon->IsShadow);
|
2017-05-21 12:14:03 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
rp->XL = rp->SlopeL.Step();
|
|
|
|
rp->XR = rp->SlopeR.Step();
|
|
|
|
}
|
|
|
|
|
2021-02-09 15:38:51 -07:00
|
|
|
void SoftRenderer::RenderScanline(s32 y, int npolys)
|
2017-05-22 14:22:26 -06:00
|
|
|
{
|
|
|
|
for (int i = 0; i < npolys; i++)
|
|
|
|
{
|
|
|
|
RendererPolygon* rp = &PolygonList[i];
|
|
|
|
Polygon* polygon = rp->PolyData;
|
|
|
|
|
|
|
|
if (y >= polygon->YTop && (y < polygon->YBottom || (y == polygon->YTop && polygon->YBottom == polygon->YTop)))
|
2017-08-28 12:27:56 -06:00
|
|
|
{
|
|
|
|
if (polygon->IsShadowMask)
|
|
|
|
RenderShadowMaskScanline(rp, y);
|
|
|
|
else
|
|
|
|
RenderPolygonScanline(rp, y);
|
|
|
|
}
|
2017-05-22 14:22:26 -06:00
|
|
|
}
|
2017-05-28 11:18:16 -06:00
|
|
|
}
|
2017-05-26 07:14:22 -06:00
|
|
|
|
2021-02-09 15:38:51 -07:00
|
|
|
u32 SoftRenderer::CalculateFogDensity(u32 pixeladdr)
|
2017-08-28 10:37:07 -06:00
|
|
|
{
|
|
|
|
u32 z = DepthBuffer[pixeladdr];
|
|
|
|
u32 densityid, densityfrac;
|
|
|
|
|
|
|
|
if (z < RenderFogOffset)
|
|
|
|
{
|
|
|
|
densityid = 0;
|
|
|
|
densityfrac = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// technically: Z difference is shifted right by two, then shifted left by fog shift
|
|
|
|
// then bit 0-16 are the fractional part and bit 17-31 are the density index
|
|
|
|
// on hardware, the final value can overflow the 32-bit range with a shift big enough,
|
|
|
|
// causing fog to 'wrap around' and accidentally apply to larger Z ranges
|
|
|
|
|
|
|
|
z -= RenderFogOffset;
|
|
|
|
z = (z >> 2) << RenderFogShift;
|
|
|
|
|
|
|
|
densityid = z >> 17;
|
|
|
|
if (densityid >= 32)
|
|
|
|
{
|
|
|
|
densityid = 32;
|
|
|
|
densityfrac = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
densityfrac = z & 0x1FFFF;
|
|
|
|
}
|
|
|
|
|
|
|
|
// checkme (may be too precise?)
|
|
|
|
u32 density =
|
|
|
|
((RenderFogDensityTable[densityid] * (0x20000-densityfrac)) +
|
|
|
|
(RenderFogDensityTable[densityid+1] * densityfrac)) >> 17;
|
|
|
|
if (density >= 127) density = 128;
|
|
|
|
|
|
|
|
return density;
|
|
|
|
}
|
|
|
|
|
2021-02-09 15:38:51 -07:00
|
|
|
void SoftRenderer::ScanlineFinalPass(s32 y)
|
2017-05-28 11:18:16 -06:00
|
|
|
{
|
2017-06-03 14:33:14 -06:00
|
|
|
// to consider:
|
|
|
|
// clearing all polygon fog flags if the master flag isn't set?
|
|
|
|
// merging all final pass loops into one?
|
|
|
|
|
|
|
|
if (RenderDispCnt & (1<<5))
|
|
|
|
{
|
|
|
|
// edge marking
|
2017-07-21 02:19:56 -06:00
|
|
|
// only applied to topmost pixels
|
2017-06-04 07:55:23 -06:00
|
|
|
|
2017-06-03 14:33:14 -06:00
|
|
|
for (int x = 0; x < 256; x++)
|
|
|
|
{
|
|
|
|
u32 pixeladdr = FirstPixelOffset + (y*ScanlineWidth) + x;
|
|
|
|
|
|
|
|
u32 attr = AttrBuffer[pixeladdr];
|
|
|
|
if (!(attr & 0xF)) continue;
|
|
|
|
|
2017-07-15 07:24:25 -06:00
|
|
|
u32 polyid = attr >> 24; // opaque polygon IDs are used for edgemarking
|
2017-06-03 14:33:14 -06:00
|
|
|
u32 z = DepthBuffer[pixeladdr];
|
|
|
|
|
|
|
|
if (((polyid != (AttrBuffer[pixeladdr-1] >> 24)) && (z < DepthBuffer[pixeladdr-1])) ||
|
|
|
|
((polyid != (AttrBuffer[pixeladdr+1] >> 24)) && (z < DepthBuffer[pixeladdr+1])) ||
|
|
|
|
((polyid != (AttrBuffer[pixeladdr-ScanlineWidth] >> 24)) && (z < DepthBuffer[pixeladdr-ScanlineWidth])) ||
|
|
|
|
((polyid != (AttrBuffer[pixeladdr+ScanlineWidth] >> 24)) && (z < DepthBuffer[pixeladdr+ScanlineWidth])))
|
|
|
|
{
|
|
|
|
u16 edgecolor = RenderEdgeTable[polyid >> 3];
|
|
|
|
u32 edgeR = (edgecolor << 1) & 0x3E; if (edgeR) edgeR++;
|
|
|
|
u32 edgeG = (edgecolor >> 4) & 0x3E; if (edgeG) edgeG++;
|
|
|
|
u32 edgeB = (edgecolor >> 9) & 0x3E; if (edgeB) edgeB++;
|
|
|
|
|
|
|
|
ColorBuffer[pixeladdr] = edgeR | (edgeG << 8) | (edgeB << 16) | (ColorBuffer[pixeladdr] & 0xFF000000);
|
|
|
|
|
|
|
|
// break antialiasing coverage (checkme)
|
|
|
|
AttrBuffer[pixeladdr] = (AttrBuffer[pixeladdr] & 0xFFFFE0FF) | 0x00001000;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-26 07:14:22 -06:00
|
|
|
if (RenderDispCnt & (1<<7))
|
|
|
|
{
|
|
|
|
// fog
|
|
|
|
|
|
|
|
// hardware testing shows that the fog step is 0x80000>>SHIFT
|
|
|
|
// basically, the depth values used in GBAtek need to be
|
|
|
|
// multiplied by 0x200 to match Z-buffer values
|
|
|
|
|
2017-05-28 11:18:16 -06:00
|
|
|
// fog is applied to the topmost two pixels, which is required for
|
2017-08-28 10:37:07 -06:00
|
|
|
// proper antialiasing
|
|
|
|
|
|
|
|
// TODO: check the 'fog alpha glitch with small Z' GBAtek talks about
|
2017-05-28 11:18:16 -06:00
|
|
|
|
2017-05-26 07:14:22 -06:00
|
|
|
bool fogcolor = !(RenderDispCnt & (1<<6));
|
|
|
|
|
|
|
|
u32 fogR = (RenderFogColor << 1) & 0x3E; if (fogR) fogR++;
|
|
|
|
u32 fogG = (RenderFogColor >> 4) & 0x3E; if (fogG) fogG++;
|
|
|
|
u32 fogB = (RenderFogColor >> 9) & 0x3E; if (fogB) fogB++;
|
|
|
|
u32 fogA = (RenderFogColor >> 16) & 0x1F;
|
|
|
|
|
2017-08-28 10:37:07 -06:00
|
|
|
for (int x = 0; x < 256; x++)
|
2017-05-26 07:14:22 -06:00
|
|
|
{
|
2017-08-28 10:37:07 -06:00
|
|
|
u32 pixeladdr = FirstPixelOffset + (y*ScanlineWidth) + x;
|
|
|
|
u32 density, srccolor, srcR, srcG, srcB, srcA;
|
|
|
|
|
|
|
|
u32 attr = AttrBuffer[pixeladdr];
|
|
|
|
if (!(attr & (1<<15))) continue;
|
|
|
|
|
|
|
|
density = CalculateFogDensity(pixeladdr);
|
|
|
|
|
|
|
|
srccolor = ColorBuffer[pixeladdr];
|
|
|
|
srcR = srccolor & 0x3F;
|
|
|
|
srcG = (srccolor >> 8) & 0x3F;
|
|
|
|
srcB = (srccolor >> 16) & 0x3F;
|
|
|
|
srcA = (srccolor >> 24) & 0x1F;
|
|
|
|
|
|
|
|
if (fogcolor)
|
2017-05-28 11:18:16 -06:00
|
|
|
{
|
2017-08-28 10:37:07 -06:00
|
|
|
srcR = ((fogR * density) + (srcR * (128-density))) >> 7;
|
|
|
|
srcG = ((fogG * density) + (srcG * (128-density))) >> 7;
|
|
|
|
srcB = ((fogB * density) + (srcB * (128-density))) >> 7;
|
|
|
|
}
|
2017-05-26 07:14:22 -06:00
|
|
|
|
2017-08-28 10:37:07 -06:00
|
|
|
srcA = ((fogA * density) + (srcA * (128-density))) >> 7;
|
2017-05-26 07:14:22 -06:00
|
|
|
|
2017-08-28 10:37:07 -06:00
|
|
|
ColorBuffer[pixeladdr] = srcR | (srcG << 8) | (srcB << 16) | (srcA << 24);
|
|
|
|
|
|
|
|
// fog for lower pixel
|
|
|
|
// TODO: make this code nicer, but avoid using a loop
|
2017-05-26 07:14:22 -06:00
|
|
|
|
2017-08-28 10:37:07 -06:00
|
|
|
if (!(attr & 0x3)) continue;
|
|
|
|
pixeladdr += BufferSize;
|
2017-05-26 07:14:22 -06:00
|
|
|
|
2017-08-28 10:37:07 -06:00
|
|
|
attr = AttrBuffer[pixeladdr];
|
|
|
|
if (!(attr & (1<<15))) continue;
|
2017-05-28 11:18:16 -06:00
|
|
|
|
2017-08-28 10:37:07 -06:00
|
|
|
density = CalculateFogDensity(pixeladdr);
|
2017-05-26 07:14:22 -06:00
|
|
|
|
2017-08-28 10:37:07 -06:00
|
|
|
srccolor = ColorBuffer[pixeladdr];
|
|
|
|
srcR = srccolor & 0x3F;
|
|
|
|
srcG = (srccolor >> 8) & 0x3F;
|
|
|
|
srcB = (srccolor >> 16) & 0x3F;
|
|
|
|
srcA = (srccolor >> 24) & 0x1F;
|
2017-05-28 11:18:16 -06:00
|
|
|
|
2017-08-28 10:37:07 -06:00
|
|
|
if (fogcolor)
|
|
|
|
{
|
|
|
|
srcR = ((fogR * density) + (srcR * (128-density))) >> 7;
|
|
|
|
srcG = ((fogG * density) + (srcG * (128-density))) >> 7;
|
|
|
|
srcB = ((fogB * density) + (srcB * (128-density))) >> 7;
|
2017-05-28 11:18:16 -06:00
|
|
|
}
|
2017-08-28 10:37:07 -06:00
|
|
|
|
|
|
|
srcA = ((fogA * density) + (srcA * (128-density))) >> 7;
|
|
|
|
|
|
|
|
ColorBuffer[pixeladdr] = srcR | (srcG << 8) | (srcB << 16) | (srcA << 24);
|
2017-05-26 07:14:22 -06:00
|
|
|
}
|
|
|
|
}
|
2017-05-31 16:45:47 -06:00
|
|
|
|
|
|
|
if (RenderDispCnt & (1<<4))
|
|
|
|
{
|
|
|
|
// anti-aliasing
|
|
|
|
|
2017-10-01 16:55:44 -06:00
|
|
|
// edges were flagged and their coverages calculated during rendering
|
|
|
|
// this is where such edge pixels are blended with the pixels underneath
|
2017-08-28 10:37:07 -06:00
|
|
|
|
2017-05-31 16:45:47 -06:00
|
|
|
for (int x = 0; x < 256; x++)
|
|
|
|
{
|
2017-08-28 10:37:07 -06:00
|
|
|
u32 pixeladdr = FirstPixelOffset + (y*ScanlineWidth) + x;
|
2017-05-31 16:45:47 -06:00
|
|
|
|
|
|
|
u32 attr = AttrBuffer[pixeladdr];
|
2017-08-28 14:19:49 -06:00
|
|
|
if (!(attr & 0x3)) continue;
|
2017-05-31 16:45:47 -06:00
|
|
|
|
|
|
|
u32 coverage = (attr >> 8) & 0x1F;
|
|
|
|
if (coverage == 0x1F) continue;
|
|
|
|
|
|
|
|
if (coverage == 0)
|
|
|
|
{
|
2017-08-28 10:37:07 -06:00
|
|
|
ColorBuffer[pixeladdr] = ColorBuffer[pixeladdr+BufferSize];
|
2017-05-31 16:45:47 -06:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
u32 topcolor = ColorBuffer[pixeladdr];
|
|
|
|
u32 topR = topcolor & 0x3F;
|
|
|
|
u32 topG = (topcolor >> 8) & 0x3F;
|
|
|
|
u32 topB = (topcolor >> 16) & 0x3F;
|
|
|
|
u32 topA = (topcolor >> 24) & 0x1F;
|
|
|
|
|
2017-08-28 10:37:07 -06:00
|
|
|
u32 botcolor = ColorBuffer[pixeladdr+BufferSize];
|
2017-05-31 16:45:47 -06:00
|
|
|
u32 botR = botcolor & 0x3F;
|
|
|
|
u32 botG = (botcolor >> 8) & 0x3F;
|
|
|
|
u32 botB = (botcolor >> 16) & 0x3F;
|
|
|
|
u32 botA = (botcolor >> 24) & 0x1F;
|
2017-08-28 10:37:07 -06:00
|
|
|
|
2017-05-31 16:45:47 -06:00
|
|
|
coverage++;
|
|
|
|
|
|
|
|
// only blend color if the bottom pixel isn't fully transparent
|
|
|
|
if (botA > 0)
|
|
|
|
{
|
|
|
|
topR = ((topR * coverage) + (botR * (32-coverage))) >> 5;
|
|
|
|
topG = ((topG * coverage) + (botG * (32-coverage))) >> 5;
|
|
|
|
topB = ((topB * coverage) + (botB * (32-coverage))) >> 5;
|
|
|
|
}
|
|
|
|
|
|
|
|
// alpha is always blended
|
|
|
|
topA = ((topA * coverage) + (botA * (32-coverage))) >> 5;
|
|
|
|
|
|
|
|
ColorBuffer[pixeladdr] = topR | (topG << 8) | (topB << 16) | (topA << 24);
|
|
|
|
}
|
|
|
|
}
|
2017-05-22 14:22:26 -06:00
|
|
|
}
|
|
|
|
|
2021-02-09 15:38:51 -07:00
|
|
|
void SoftRenderer::ClearBuffers()
|
2017-02-10 08:50:26 -07:00
|
|
|
{
|
2017-05-28 11:18:16 -06:00
|
|
|
u32 clearz = ((RenderClearAttr2 & 0x7FFF) * 0x200) + 0x1FF;
|
2017-07-15 07:24:25 -06:00
|
|
|
u32 polyid = RenderClearAttr1 & 0x3F000000; // this sets the opaque polygonID
|
2017-03-06 17:13:00 -07:00
|
|
|
|
2017-05-28 11:18:16 -06:00
|
|
|
// fill screen borders for edge marking
|
|
|
|
|
2017-06-03 14:10:32 -06:00
|
|
|
for (int x = 0; x < ScanlineWidth; x++)
|
2017-05-28 11:18:16 -06:00
|
|
|
{
|
|
|
|
ColorBuffer[x] = 0;
|
|
|
|
DepthBuffer[x] = clearz;
|
|
|
|
AttrBuffer[x] = polyid;
|
|
|
|
}
|
|
|
|
|
2017-06-03 14:10:32 -06:00
|
|
|
for (int x = ScanlineWidth; x < ScanlineWidth*193; x+=ScanlineWidth)
|
2017-05-28 11:18:16 -06:00
|
|
|
{
|
|
|
|
ColorBuffer[x] = 0;
|
|
|
|
DepthBuffer[x] = clearz;
|
|
|
|
AttrBuffer[x] = polyid;
|
|
|
|
ColorBuffer[x+257] = 0;
|
|
|
|
DepthBuffer[x+257] = clearz;
|
|
|
|
AttrBuffer[x+257] = polyid;
|
|
|
|
}
|
|
|
|
|
2017-06-03 14:10:32 -06:00
|
|
|
for (int x = ScanlineWidth*193; x < ScanlineWidth*194; x++)
|
2017-05-28 11:18:16 -06:00
|
|
|
{
|
|
|
|
ColorBuffer[x] = 0;
|
|
|
|
DepthBuffer[x] = clearz;
|
|
|
|
AttrBuffer[x] = polyid;
|
|
|
|
}
|
|
|
|
|
|
|
|
// clear the screen
|
|
|
|
|
2017-05-09 14:22:52 -06:00
|
|
|
if (RenderDispCnt & (1<<14))
|
2017-03-06 17:13:00 -07:00
|
|
|
{
|
2017-04-10 12:24:41 -06:00
|
|
|
u8 xoff = (RenderClearAttr2 >> 16) & 0xFF;
|
|
|
|
u8 yoff = (RenderClearAttr2 >> 24) & 0xFF;
|
2017-03-06 17:13:00 -07:00
|
|
|
|
2017-06-03 14:10:32 -06:00
|
|
|
for (int y = 0; y < ScanlineWidth*192; y+=ScanlineWidth)
|
2017-03-06 17:13:00 -07:00
|
|
|
{
|
|
|
|
for (int x = 0; x < 256; x++)
|
|
|
|
{
|
2020-11-30 08:58:52 -07:00
|
|
|
u16 val2 = ReadVRAM_Texture<u16>(0x40000 + (yoff << 9) + (xoff << 1));
|
|
|
|
u16 val3 = ReadVRAM_Texture<u16>(0x60000 + (yoff << 9) + (xoff << 1));
|
2017-03-06 17:13:00 -07:00
|
|
|
|
|
|
|
// TODO: confirm color conversion
|
|
|
|
u32 r = (val2 << 1) & 0x3E; if (r) r++;
|
|
|
|
u32 g = (val2 >> 4) & 0x3E; if (g) g++;
|
|
|
|
u32 b = (val2 >> 9) & 0x3E; if (b) b++;
|
|
|
|
u32 a = (val2 & 0x8000) ? 0x1F000000 : 0;
|
|
|
|
u32 color = r | (g << 8) | (b << 16) | a;
|
|
|
|
|
|
|
|
u32 z = ((val3 & 0x7FFF) * 0x200) + 0x1FF;
|
|
|
|
|
2017-06-03 14:10:32 -06:00
|
|
|
u32 pixeladdr = FirstPixelOffset + y + x;
|
2017-05-28 11:18:16 -06:00
|
|
|
ColorBuffer[pixeladdr] = color;
|
|
|
|
DepthBuffer[pixeladdr] = z;
|
|
|
|
AttrBuffer[pixeladdr] = polyid | (val3 & 0x8000);
|
2017-03-06 17:13:00 -07:00
|
|
|
|
|
|
|
xoff++;
|
|
|
|
}
|
|
|
|
|
|
|
|
yoff++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
2017-02-10 19:54:08 -07:00
|
|
|
{
|
2017-03-06 17:13:00 -07:00
|
|
|
// TODO: confirm color conversion
|
2017-04-10 12:24:41 -06:00
|
|
|
u32 r = (RenderClearAttr1 << 1) & 0x3E; if (r) r++;
|
|
|
|
u32 g = (RenderClearAttr1 >> 4) & 0x3E; if (g) g++;
|
|
|
|
u32 b = (RenderClearAttr1 >> 9) & 0x3E; if (b) b++;
|
|
|
|
u32 a = (RenderClearAttr1 >> 16) & 0x1F;
|
2017-03-06 17:13:00 -07:00
|
|
|
u32 color = r | (g << 8) | (b << 16) | (a << 24);
|
|
|
|
|
2021-02-09 15:38:51 -07:00
|
|
|
polyid |= (RenderClearAttr1 & 0x8000);
|
2017-03-12 17:45:26 -06:00
|
|
|
|
2017-06-03 14:10:32 -06:00
|
|
|
for (int y = 0; y < ScanlineWidth*192; y+=ScanlineWidth)
|
2017-03-06 17:13:00 -07:00
|
|
|
{
|
2017-05-28 11:18:16 -06:00
|
|
|
for (int x = 0; x < 256; x++)
|
|
|
|
{
|
2017-06-03 14:10:32 -06:00
|
|
|
u32 pixeladdr = FirstPixelOffset + y + x;
|
2017-05-28 11:18:16 -06:00
|
|
|
ColorBuffer[pixeladdr] = color;
|
|
|
|
DepthBuffer[pixeladdr] = clearz;
|
|
|
|
AttrBuffer[pixeladdr] = polyid;
|
|
|
|
}
|
2017-03-06 17:13:00 -07:00
|
|
|
}
|
2017-02-10 19:54:08 -07:00
|
|
|
}
|
2017-05-23 15:38:28 -06:00
|
|
|
}
|
2017-05-25 17:22:11 -06:00
|
|
|
|
2021-02-09 15:38:51 -07:00
|
|
|
void SoftRenderer::RenderPolygons(bool threaded, Polygon** polygons, int npolys)
|
2017-05-23 15:38:28 -06:00
|
|
|
{
|
2017-05-22 14:22:26 -06:00
|
|
|
int j = 0;
|
2017-05-21 12:14:03 -06:00
|
|
|
for (int i = 0; i < npolys; i++)
|
|
|
|
{
|
2018-12-19 17:31:31 -07:00
|
|
|
if (polygons[i]->Degenerate) continue;
|
2017-07-05 10:11:00 -06:00
|
|
|
SetupPolygon(&PolygonList[j++], polygons[i]);
|
2017-05-22 14:22:26 -06:00
|
|
|
}
|
|
|
|
|
2017-07-04 11:11:43 -06:00
|
|
|
RenderScanline(0, j);
|
2017-05-28 11:18:16 -06:00
|
|
|
|
|
|
|
for (s32 y = 1; y < 192; y++)
|
2017-03-15 08:53:36 -06:00
|
|
|
{
|
2017-07-04 11:11:43 -06:00
|
|
|
RenderScanline(y, j);
|
2017-05-28 11:18:16 -06:00
|
|
|
ScanlineFinalPass(y-1);
|
2017-05-23 15:38:28 -06:00
|
|
|
|
|
|
|
if (threaded)
|
2017-05-25 17:22:11 -06:00
|
|
|
Platform::Semaphore_Post(Sema_ScanlineCount);
|
2017-03-15 08:53:36 -06:00
|
|
|
}
|
2017-05-28 11:18:16 -06:00
|
|
|
|
|
|
|
ScanlineFinalPass(191);
|
|
|
|
|
|
|
|
if (threaded)
|
|
|
|
Platform::Semaphore_Post(Sema_ScanlineCount);
|
2017-05-23 15:38:28 -06:00
|
|
|
}
|
2017-03-15 08:53:36 -06:00
|
|
|
|
2021-02-09 15:38:51 -07:00
|
|
|
void SoftRenderer::VCount144()
|
2017-05-23 15:38:28 -06:00
|
|
|
{
|
2021-08-04 06:35:54 -06:00
|
|
|
if (RenderThreadRunning.load(std::memory_order_relaxed) && !GPU3D::AbortFrame)
|
2017-06-04 07:55:23 -06:00
|
|
|
Platform::Semaphore_Wait(Sema_RenderDone);
|
2017-05-23 15:38:28 -06:00
|
|
|
}
|
|
|
|
|
2021-02-09 15:38:51 -07:00
|
|
|
void SoftRenderer::RenderFrame()
|
2017-05-23 15:38:28 -06:00
|
|
|
{
|
2020-11-30 08:58:52 -07:00
|
|
|
auto textureDirty = GPU::VRAMDirty_Texture.DeriveState(GPU::VRAMMap_Texture);
|
|
|
|
auto texPalDirty = GPU::VRAMDirty_TexPal.DeriveState(GPU::VRAMMap_TexPal);
|
|
|
|
|
|
|
|
bool textureChanged = GPU::MakeVRAMFlat_TextureCoherent(textureDirty);
|
|
|
|
bool texPalChanged = GPU::MakeVRAMFlat_TexPalCoherent(texPalDirty);
|
|
|
|
|
|
|
|
FrameIdentical = !(textureChanged || texPalChanged) && RenderFrameIdentical;
|
|
|
|
|
2021-03-11 08:54:27 -07:00
|
|
|
if (RenderThreadRunning.load(std::memory_order_relaxed))
|
2017-06-04 07:55:23 -06:00
|
|
|
{
|
|
|
|
Platform::Semaphore_Post(Sema_RenderStart);
|
|
|
|
}
|
2020-11-30 08:58:52 -07:00
|
|
|
else if (!FrameIdentical)
|
2017-06-04 07:55:23 -06:00
|
|
|
{
|
|
|
|
ClearBuffers();
|
2017-07-05 10:11:00 -06:00
|
|
|
RenderPolygons(false, &RenderPolygonRAM[0], RenderNumPolygons);
|
2017-06-04 07:55:23 -06:00
|
|
|
}
|
2017-05-23 15:38:28 -06:00
|
|
|
}
|
|
|
|
|
2021-02-09 15:38:51 -07:00
|
|
|
void SoftRenderer::RestartFrame()
|
|
|
|
{
|
|
|
|
SetupRenderThread();
|
|
|
|
}
|
|
|
|
|
|
|
|
void SoftRenderer::RenderThreadFunc()
|
2017-05-23 15:38:28 -06:00
|
|
|
{
|
|
|
|
for (;;)
|
2017-05-22 14:22:26 -06:00
|
|
|
{
|
2017-05-23 15:38:28 -06:00
|
|
|
Platform::Semaphore_Wait(Sema_RenderStart);
|
|
|
|
if (!RenderThreadRunning) return;
|
|
|
|
|
2017-05-25 17:22:11 -06:00
|
|
|
RenderThreadRendering = true;
|
2020-11-30 08:58:52 -07:00
|
|
|
if (FrameIdentical)
|
|
|
|
{
|
|
|
|
Platform::Semaphore_Post(Sema_ScanlineCount, 192);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ClearBuffers();
|
|
|
|
RenderPolygons(true, &RenderPolygonRAM[0], RenderNumPolygons);
|
|
|
|
}
|
2017-05-23 15:38:28 -06:00
|
|
|
|
|
|
|
Platform::Semaphore_Post(Sema_RenderDone);
|
2017-05-25 17:22:11 -06:00
|
|
|
RenderThreadRendering = false;
|
2017-02-10 19:54:08 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-09 15:38:51 -07:00
|
|
|
u32* SoftRenderer::GetLine(int line)
|
2017-05-23 15:38:28 -06:00
|
|
|
{
|
2021-03-11 08:54:27 -07:00
|
|
|
if (RenderThreadRunning.load(std::memory_order_relaxed))
|
2017-07-21 02:19:56 -06:00
|
|
|
{
|
|
|
|
if (line < 192)
|
|
|
|
Platform::Semaphore_Wait(Sema_ScanlineCount);
|
|
|
|
}
|
2017-05-23 15:38:28 -06:00
|
|
|
|
2017-06-03 14:10:32 -06:00
|
|
|
return &ColorBuffer[(line * ScanlineWidth) + FirstPixelOffset];
|
2017-02-10 08:50:26 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|