mirror of
https://git.suyu.dev/suyu/suyu.git
synced 2024-11-09 00:12:45 +01:00
Pica/Rasterizer: Implement backface culling.
This commit is contained in:
parent
3b78af904e
commit
0f49424022
2 changed files with 36 additions and 10 deletions
|
@ -50,7 +50,19 @@ struct Regs {
|
||||||
|
|
||||||
u32 trigger_irq;
|
u32 trigger_irq;
|
||||||
|
|
||||||
INSERT_PADDING_WORDS(0x30);
|
INSERT_PADDING_WORDS(0x2f);
|
||||||
|
|
||||||
|
enum class CullMode : u32 {
|
||||||
|
// Select which polygons are considered to be "frontfacing".
|
||||||
|
KeepAll = 0,
|
||||||
|
KeepClockWise = 1,
|
||||||
|
KeepCounterClockWise = 2,
|
||||||
|
// TODO: What does the third value imply?
|
||||||
|
};
|
||||||
|
|
||||||
|
union {
|
||||||
|
BitField<0, 2, CullMode> cull_mode;
|
||||||
|
};
|
||||||
|
|
||||||
BitField<0, 24, u32> viewport_size_x;
|
BitField<0, 24, u32> viewport_size_x;
|
||||||
|
|
||||||
|
@ -659,6 +671,7 @@ struct Regs {
|
||||||
} while(false)
|
} while(false)
|
||||||
|
|
||||||
ADD_FIELD(trigger_irq);
|
ADD_FIELD(trigger_irq);
|
||||||
|
ADD_FIELD(cull_mode);
|
||||||
ADD_FIELD(viewport_size_x);
|
ADD_FIELD(viewport_size_x);
|
||||||
ADD_FIELD(viewport_size_y);
|
ADD_FIELD(viewport_size_y);
|
||||||
ADD_FIELD(viewport_depth_range);
|
ADD_FIELD(viewport_depth_range);
|
||||||
|
@ -730,6 +743,7 @@ private:
|
||||||
#define ASSERT_REG_POSITION(field_name, position) static_assert(offsetof(Regs, field_name) == position * 4, "Field "#field_name" has invalid position")
|
#define ASSERT_REG_POSITION(field_name, position) static_assert(offsetof(Regs, field_name) == position * 4, "Field "#field_name" has invalid position")
|
||||||
|
|
||||||
ASSERT_REG_POSITION(trigger_irq, 0x10);
|
ASSERT_REG_POSITION(trigger_irq, 0x10);
|
||||||
|
ASSERT_REG_POSITION(cull_mode, 0x40);
|
||||||
ASSERT_REG_POSITION(viewport_size_x, 0x41);
|
ASSERT_REG_POSITION(viewport_size_x, 0x41);
|
||||||
ASSERT_REG_POSITION(viewport_size_y, 0x43);
|
ASSERT_REG_POSITION(viewport_size_y, 0x43);
|
||||||
ASSERT_REG_POSITION(viewport_depth_range, 0x4d);
|
ASSERT_REG_POSITION(viewport_depth_range, 0x4d);
|
||||||
|
|
|
@ -82,10 +82,31 @@ void ProcessTriangle(const VertexShader::OutputVertex& v0,
|
||||||
auto ScreenToRasterizerCoordinates = [FloatToFix](const Math::Vec3<float24> vec) {
|
auto ScreenToRasterizerCoordinates = [FloatToFix](const Math::Vec3<float24> vec) {
|
||||||
return Math::Vec3<Fix12P4>{FloatToFix(vec.x), FloatToFix(vec.y), FloatToFix(vec.z)};
|
return Math::Vec3<Fix12P4>{FloatToFix(vec.x), FloatToFix(vec.y), FloatToFix(vec.z)};
|
||||||
};
|
};
|
||||||
|
static auto orient2d = [](const Math::Vec2<Fix12P4>& vtx1,
|
||||||
|
const Math::Vec2<Fix12P4>& vtx2,
|
||||||
|
const Math::Vec2<Fix12P4>& vtx3) {
|
||||||
|
const auto vec1 = Math::MakeVec(vtx2 - vtx1, 0);
|
||||||
|
const auto vec2 = Math::MakeVec(vtx3 - vtx1, 0);
|
||||||
|
// TODO: There is a very small chance this will overflow for sizeof(int) == 4
|
||||||
|
return Math::Cross(vec1, vec2).z;
|
||||||
|
};
|
||||||
|
|
||||||
Math::Vec3<Fix12P4> vtxpos[3]{ ScreenToRasterizerCoordinates(v0.screenpos),
|
Math::Vec3<Fix12P4> vtxpos[3]{ ScreenToRasterizerCoordinates(v0.screenpos),
|
||||||
ScreenToRasterizerCoordinates(v1.screenpos),
|
ScreenToRasterizerCoordinates(v1.screenpos),
|
||||||
ScreenToRasterizerCoordinates(v2.screenpos) };
|
ScreenToRasterizerCoordinates(v2.screenpos) };
|
||||||
|
|
||||||
|
if (registers.cull_mode == Regs::CullMode::KeepClockWise) {
|
||||||
|
// Reverse vertex order and use the CCW code path.
|
||||||
|
std::swap(vtxpos[1], vtxpos[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (registers.cull_mode != Regs::CullMode::KeepAll) {
|
||||||
|
// Cull away triangles which are wound clockwise.
|
||||||
|
// TODO: A check for degenerate triangles ("== 0") should be considered for CullMode::KeepAll
|
||||||
|
if (orient2d(vtxpos[0].xy(), vtxpos[1].xy(), vtxpos[2].xy()) <= 0)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Proper scissor rect test!
|
// TODO: Proper scissor rect test!
|
||||||
u16 min_x = std::min({vtxpos[0].x, vtxpos[1].x, vtxpos[2].x});
|
u16 min_x = std::min({vtxpos[0].x, vtxpos[1].x, vtxpos[2].x});
|
||||||
u16 min_y = std::min({vtxpos[0].y, vtxpos[1].y, vtxpos[2].y});
|
u16 min_y = std::min({vtxpos[0].y, vtxpos[1].y, vtxpos[2].y});
|
||||||
|
@ -128,15 +149,6 @@ void ProcessTriangle(const VertexShader::OutputVertex& v0,
|
||||||
for (u16 x = min_x; x < max_x; x += 0x10) {
|
for (u16 x = min_x; x < max_x; x += 0x10) {
|
||||||
|
|
||||||
// Calculate the barycentric coordinates w0, w1 and w2
|
// Calculate the barycentric coordinates w0, w1 and w2
|
||||||
auto orient2d = [](const Math::Vec2<Fix12P4>& vtx1,
|
|
||||||
const Math::Vec2<Fix12P4>& vtx2,
|
|
||||||
const Math::Vec2<Fix12P4>& vtx3) {
|
|
||||||
const auto vec1 = Math::MakeVec(vtx2 - vtx1, 0);
|
|
||||||
const auto vec2 = Math::MakeVec(vtx3 - vtx1, 0);
|
|
||||||
// TODO: There is a very small chance this will overflow for sizeof(int) == 4
|
|
||||||
return Math::Cross(vec1, vec2).z;
|
|
||||||
};
|
|
||||||
|
|
||||||
int w0 = bias0 + orient2d(vtxpos[1].xy(), vtxpos[2].xy(), {x, y});
|
int w0 = bias0 + orient2d(vtxpos[1].xy(), vtxpos[2].xy(), {x, y});
|
||||||
int w1 = bias1 + orient2d(vtxpos[2].xy(), vtxpos[0].xy(), {x, y});
|
int w1 = bias1 + orient2d(vtxpos[2].xy(), vtxpos[0].xy(), {x, y});
|
||||||
int w2 = bias2 + orient2d(vtxpos[0].xy(), vtxpos[1].xy(), {x, y});
|
int w2 = bias2 + orient2d(vtxpos[0].xy(), vtxpos[1].xy(), {x, y});
|
||||||
|
|
Loading…
Reference in a new issue