mirror of
https://github.com/flyinghead/flycast.git
synced 2026-11-09 17:11:59 -10:00
Vulkan without VK_EXT_provoking_vertex, DX9 and DX11 take flat-shaded colors from the first vertex, while on Dreamcast it's the last. For the depth-sorted triangle list, setFirstProvokingVertex() copied each triangle's last vertex colors onto its first vertex. But sortTriangles() builds these triangles from strips, so neighbors share vertices: tri0=(P0,P1,P2), tri1=(P2,P1,P3). P2 is tri0's source and tri1's destination, and the list is processed in depth order, so one triangle's flat color can overwrite another's before it's read. In strips of three or more triangles, consecutive triangles even share their first vertex. Flat-shaded translucent polygons then pick up a neighbor's color, e.g. the player shadow decals in Tony Hawk's Pro Skater 2 and Mat Hoffman's Pro BMX turn uniformly dark. Each sorted triangle has its own three slots in idx, so rotate them (a,b,c) -> (c,a,b) instead. The winding is unchanged and the provoking vertex comes first, without touching shared vertices. Strips keep the color copy, which is safe there. Adds SorterTest, which fails on the old code and passes with this change. Co-authored-by: Claude Opus 5.5 <[email protected]> [ci skip]
87 lines
2.9 KiB
C++
87 lines
2.9 KiB
C++
/*
|
|
This file is part of reicast.
|
|
|
|
reicast 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 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
reicast 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 reicast. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
#include "sorter.h"
|
|
|
|
#include <cstring>
|
|
|
|
// Vulkan and DirectX use the color values of the first vertex for flat shaded triangles.
|
|
// On Dreamcast the last vertex is the provoking one, so for strips we copy its colors onto the
|
|
// first vertex, and for the depth-sorted triangle list we rotate each triangle's indices instead
|
|
// (the sorted list shares vertices between triangles, so copying would corrupt neighbors).
|
|
void setFirstProvokingVertex(rend_context& rendContext)
|
|
{
|
|
auto setProvokingVertex = [&rendContext](const std::vector<PolyParam>& list) {
|
|
for (const PolyParam& pp : list)
|
|
{
|
|
if (pp.pcw.Gouraud)
|
|
continue;
|
|
for (u32 i = 0; i + 2 < pp.count; i++)
|
|
{
|
|
u32 idx = rendContext.idx[pp.first + i];
|
|
if (idx == (u32)-1)
|
|
// primitive restart
|
|
continue;
|
|
Vertex& vertex = rendContext.verts[idx];
|
|
idx = rendContext.idx[pp.first + i + 1];
|
|
if (idx == (u32)-1) {
|
|
i++;
|
|
// primitive restart
|
|
continue;
|
|
}
|
|
idx = rendContext.idx[pp.first + i + 2];
|
|
if (idx == (u32)-1) {
|
|
i += 2;
|
|
// primitive restart
|
|
continue;
|
|
}
|
|
Vertex& lastVertex = rendContext.verts[idx];
|
|
memcpy(vertex.col, lastVertex.col, sizeof(vertex.col));
|
|
memcpy(vertex.spc, lastVertex.spc, sizeof(vertex.spc));
|
|
memcpy(vertex.col1, lastVertex.col1, sizeof(vertex.col1));
|
|
memcpy(vertex.spc1, lastVertex.spc1, sizeof(vertex.spc1));
|
|
}
|
|
}
|
|
};
|
|
setProvokingVertex(rendContext.global_param_op);
|
|
setProvokingVertex(rendContext.global_param_pt);
|
|
if (rendContext.sortedTriangles.empty())
|
|
{
|
|
setProvokingVertex(rendContext.global_param_tr);
|
|
}
|
|
else
|
|
{
|
|
// Sorted triangles share vertices (tri0=(P0,P1,P2), tri1=(P2,P1,P3)), and
|
|
// they're processed in depth order, so copying colors can overwrite a
|
|
// source before it's read. Each triangle owns its 3 index slots though:
|
|
// rotate (a,b,c) -> (c,a,b), which keeps the winding and puts the
|
|
// provoking vertex first.
|
|
for (const SortedTriangle& tri : rendContext.sortedTriangles)
|
|
{
|
|
if (rendContext.global_param_tr[tri.polyIndex].pcw.Gouraud)
|
|
continue;
|
|
for (u32 i = 0; i + 2 < tri.count; i += 3)
|
|
{
|
|
u32 *idx = &rendContext.idx[tri.first + i];
|
|
const u32 last = idx[2];
|
|
idx[2] = idx[1];
|
|
idx[1] = idx[0];
|
|
idx[0] = last;
|
|
}
|
|
}
|
|
}
|
|
}
|