SoftGPU: Correct clipping for flat shading.

It needs to use the provoking color, regardless of culling or clipping.

Fixes Blade Dancer lighting (see #4140.)
This commit is contained in:
Unknown W. Brackets 2018-11-22 17:48:55 -08:00
parent 1c19bce514
commit 11a8857a7e
3 changed files with 40 additions and 26 deletions

View file

@ -176,10 +176,10 @@ void ProcessRect(const VertexData& v0, const VertexData& v1)
} }
// Four triangles to do backfaces as well. Two of them will get backface culled. // Four triangles to do backfaces as well. Two of them will get backface culled.
ProcessTriangle(*topleft, *topright, *bottomright); ProcessTriangle(*topleft, *topright, *bottomright, buf[3]);
ProcessTriangle(*bottomright, *topright, *topleft); ProcessTriangle(*bottomright, *topright, *topleft, buf[3]);
ProcessTriangle(*bottomright, *bottomleft, *topleft); ProcessTriangle(*bottomright, *bottomleft, *topleft, buf[3]);
ProcessTriangle(*topleft, *bottomleft, *bottomright); ProcessTriangle(*topleft, *bottomleft, *bottomright, buf[3]);
} else { } else {
// through mode handling // through mode handling
VertexData buf[4]; VertexData buf[4];
@ -271,10 +271,17 @@ void ProcessLine(VertexData& v0, VertexData& v1)
Rasterizer::DrawLine(data[0], data[1]); Rasterizer::DrawLine(data[0], data[1]);
} }
void ProcessTriangle(VertexData& v0, VertexData& v1, VertexData& v2) void ProcessTriangle(VertexData& v0, VertexData& v1, VertexData& v2, const VertexData &provoking) {
{
if (gstate.isModeThrough()) { if (gstate.isModeThrough()) {
// In case of cull reordering, make sure the right color is on the final vertex.
if (gstate.getShadeMode() == GE_SHADE_FLAT) {
VertexData corrected2 = v2;
corrected2.color0 = provoking.color0;
corrected2.color1 = provoking.color1;
Rasterizer::DrawTriangle(v0, v1, corrected2);
} else {
Rasterizer::DrawTriangle(v0, v1, v2); Rasterizer::DrawTriangle(v0, v1, v2);
}
return; return;
} }
@ -339,14 +346,19 @@ void ProcessTriangle(VertexData& v0, VertexData& v1, VertexData& v2)
return; return;
} }
for (int i = 0; i+3 <= numIndices; i+=3) for (int i = 0; i + 3 <= numIndices; i += 3) {
{ if (indices[i] != SKIP_FLAG) {
if(indices[i] != SKIP_FLAG)
{
VertexData data[3] = { *Vertices[indices[i]], *Vertices[indices[i+1]], *Vertices[indices[i+2]] }; VertexData data[3] = { *Vertices[indices[i]], *Vertices[indices[i+1]], *Vertices[indices[i+2]] };
data[0].screenpos = TransformUnit::ClipToScreen(data[0].clippos); data[0].screenpos = TransformUnit::ClipToScreen(data[0].clippos);
data[1].screenpos = TransformUnit::ClipToScreen(data[1].clippos); data[1].screenpos = TransformUnit::ClipToScreen(data[1].clippos);
data[2].screenpos = TransformUnit::ClipToScreen(data[2].clippos); data[2].screenpos = TransformUnit::ClipToScreen(data[2].clippos);
if (gstate.getShadeMode() == GE_SHADE_FLAT) {
// So that the order of clipping doesn't matter...
data[2].color0 = provoking.color0;
data[2].color1 = provoking.color1;
}
Rasterizer::DrawTriangle(data[0], data[1], data[2]); Rasterizer::DrawTriangle(data[0], data[1], data[2]);
} }
} }

View file

@ -23,7 +23,7 @@ namespace Clipper {
void ProcessPoint(VertexData& v0); void ProcessPoint(VertexData& v0);
void ProcessLine(VertexData& v0, VertexData& v1); void ProcessLine(VertexData& v0, VertexData& v1);
void ProcessTriangle(VertexData& v0, VertexData& v1, VertexData& v2); void ProcessTriangle(VertexData& v0, VertexData& v1, VertexData& v2, const VertexData &provoking);
void ProcessRect(const VertexData& v0, const VertexData& v1); void ProcessRect(const VertexData& v0, const VertexData& v1);
} }

View file

@ -342,12 +342,12 @@ void TransformUnit::SubmitPrimitive(void* vertices, void* indices, GEPrimitiveTy
case GE_PRIM_TRIANGLES: case GE_PRIM_TRIANGLES:
{ {
if (!gstate.isCullEnabled() || gstate.isModeClear()) { if (!gstate.isCullEnabled() || gstate.isModeClear()) {
Clipper::ProcessTriangle(data[0], data[1], data[2]); Clipper::ProcessTriangle(data[0], data[1], data[2], data[2]);
Clipper::ProcessTriangle(data[2], data[1], data[0]); Clipper::ProcessTriangle(data[2], data[1], data[0], data[2]);
} else if (!gstate.getCullMode()) { } else if (!gstate.getCullMode()) {
Clipper::ProcessTriangle(data[2], data[1], data[0]); Clipper::ProcessTriangle(data[2], data[1], data[0], data[2]);
} else { } else {
Clipper::ProcessTriangle(data[0], data[1], data[2]); Clipper::ProcessTriangle(data[0], data[1], data[2], data[2]);
} }
break; break;
} }
@ -413,7 +413,8 @@ void TransformUnit::SubmitPrimitive(void* vertices, void* indices, GEPrimitiveTy
vreader.Goto(vtx); vreader.Goto(vtx);
} }
data[(data_index++) % 3] = ReadVertex(vreader); int provoking_index = (data_index++) % 3;
data[provoking_index] = ReadVertex(vreader);
if (outside_range_flag) { if (outside_range_flag) {
// Drop all primitives containing the current vertex // Drop all primitives containing the current vertex
skip_count = 2; skip_count = 2;
@ -427,14 +428,14 @@ void TransformUnit::SubmitPrimitive(void* vertices, void* indices, GEPrimitiveTy
} }
if (!gstate.isCullEnabled() || gstate.isModeClear()) { if (!gstate.isCullEnabled() || gstate.isModeClear()) {
Clipper::ProcessTriangle(data[0], data[1], data[2]); Clipper::ProcessTriangle(data[0], data[1], data[2], data[provoking_index]);
Clipper::ProcessTriangle(data[2], data[1], data[0]); Clipper::ProcessTriangle(data[2], data[1], data[0], data[provoking_index]);
} else if ((!gstate.getCullMode()) ^ ((data_index - 1) % 2)) { } else if ((!gstate.getCullMode()) ^ ((data_index - 1) % 2)) {
// We need to reverse the vertex order for each second primitive, // We need to reverse the vertex order for each second primitive,
// but we additionally need to do that for every primitive if CCW cullmode is used. // but we additionally need to do that for every primitive if CCW cullmode is used.
Clipper::ProcessTriangle(data[2], data[1], data[0]); Clipper::ProcessTriangle(data[2], data[1], data[0], data[provoking_index]);
} else { } else {
Clipper::ProcessTriangle(data[0], data[1], data[2]); Clipper::ProcessTriangle(data[0], data[1], data[2], data[provoking_index]);
} }
} }
break; break;
@ -466,7 +467,8 @@ void TransformUnit::SubmitPrimitive(void* vertices, void* indices, GEPrimitiveTy
vreader.Goto(vtx); vreader.Goto(vtx);
} }
data[2 - ((data_index++) % 2)] = ReadVertex(vreader); int provoking_index = 2 - ((data_index++) % 2);
data[provoking_index] = ReadVertex(vreader);
if (outside_range_flag) { if (outside_range_flag) {
// Drop all primitives containing the current vertex // Drop all primitives containing the current vertex
skip_count = 2; skip_count = 2;
@ -480,14 +482,14 @@ void TransformUnit::SubmitPrimitive(void* vertices, void* indices, GEPrimitiveTy
} }
if (!gstate.isCullEnabled() || gstate.isModeClear()) { if (!gstate.isCullEnabled() || gstate.isModeClear()) {
Clipper::ProcessTriangle(data[0], data[1], data[2]); Clipper::ProcessTriangle(data[0], data[1], data[2], data[provoking_index]);
Clipper::ProcessTriangle(data[2], data[1], data[0]); Clipper::ProcessTriangle(data[2], data[1], data[0], data[provoking_index]);
} else if ((!gstate.getCullMode()) ^ ((data_index - 1) % 2)) { } else if ((!gstate.getCullMode()) ^ ((data_index - 1) % 2)) {
// We need to reverse the vertex order for each second primitive, // We need to reverse the vertex order for each second primitive,
// but we additionally need to do that for every primitive if CCW cullmode is used. // but we additionally need to do that for every primitive if CCW cullmode is used.
Clipper::ProcessTriangle(data[2], data[1], data[0]); Clipper::ProcessTriangle(data[2], data[1], data[0], data[provoking_index]);
} else { } else {
Clipper::ProcessTriangle(data[0], data[1], data[2]); Clipper::ProcessTriangle(data[0], data[1], data[2], data[provoking_index]);
} }
} }
break; break;