/* 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 . */ #include "sorter.h" #include "hw/pvr/Renderer_if.h" #include #include #include struct IndexTrig { u32 id[3]; u16 pid; f32 z; }; static float minZ(const Vertex *v, const u32 *mod) { return std::min(std::min(v[mod[0]].z, v[mod[1]].z), v[mod[2]].z); } static bool operator<(const IndexTrig& left, const IndexTrig& right) { return left.z < right.z; } static bool operator<(const PolyParam& left, const PolyParam& right) { return left.zvZ < right.zvZ; } static float getProjectedZ(const Vertex *v, const float *mat) { // -1 / z return -1 / (mat[2] * v->x + mat[1 * 4 + 2] * v->y + mat[2 * 4 + 2] * v->z + mat[3 * 4 + 2]); } void SortPParams(int first, int count) { if (pvrrc.verts.used() == 0 || count <= 1) return; Vertex* vtx_base=pvrrc.verts.head(); u32* idx_base = pvrrc.idx.head(); PolyParam* pp = &pvrrc.global_param_tr.head()[first]; PolyParam* pp_end = pp + count; while(pp!=pp_end) { if (pp->count<2) { pp->zvZ=0; } else { u32* idx = idx_base + pp->first; Vertex* vtx=vtx_base+idx[0]; Vertex* vtx_end=vtx_base + idx[pp->count-1]+1; if (pp->isNaomi2()) { glm::mat4 mvMat = pp->mvMatrix != nullptr ? glm::make_mat4(pp->mvMatrix) : glm::mat4(1); glm::vec3 min{ 1e38f, 1e38f, 1e38f }; glm::vec3 max{ -1e38f, -1e38f, -1e38f }; while (vtx != vtx_end) { glm::vec3 pos{ vtx->x, vtx->y, vtx->z }; min = glm::min(min, pos); max = glm::max(max, pos); vtx++; } glm::vec4 center((min + max) / 2.f, 1); glm::vec4 extents(max - glm::vec3(center), 0); // transform center = mvMat * center; glm::vec3 extentX = mvMat * glm::vec4(extents.x, 0, 0, 0); glm::vec3 extentY = mvMat * glm::vec4(0, extents.y, 0, 0); glm::vec3 extentZ = mvMat * glm::vec4(0, 0, extents.z, 0); // new AA extents glm::vec3 newExtent = glm::abs(extentX) + glm::abs(extentY) + glm::abs(extentZ); min = glm::vec3(center) - newExtent; max = glm::vec3(center) + newExtent; // project pp->zvZ = -1 / std::min(min.z, max.z); } else { u32 zv=0xFFFFFFFF; while(vtx!=vtx_end) { zv = std::min(zv, (u32&)vtx->z); vtx++; } pp->zvZ=(f32&)zv; } } pp++; } std::stable_sort(pvrrc.global_param_tr.head() + first, pvrrc.global_param_tr.head() + first + count); } const static Vertex *vtx_sort_base; static void fill_id(u32 *d, const Vertex *v0, const Vertex *v1, const Vertex *v2, const Vertex *vb) { d[0] = (u32)(v0 - vb); d[1] = (u32)(v1 - vb); d[2] = (u32)(v2 - vb); } void GenSorted(int first, int count, std::vector& pidx_sort, std::vector& vidx_sort) { u32 tess_gen=0; pidx_sort.clear(); if (pvrrc.verts.used() == 0 || count == 0) return; const Vertex * const vtx_base = pvrrc.verts.head(); const u32 * const idx_base = pvrrc.idx.head(); const PolyParam * const pp_base = &pvrrc.global_param_tr.head()[first]; const PolyParam *pp = pp_base; const PolyParam * const pp_end = pp + count; while (pp->count == 0 && pp < pp_end) pp++; if (pp == pp_end) return; vtx_sort_base=vtx_base; static u32 vtx_cnt; int vtx_count = pvrrc.verts.used() - idx_base[pp->first]; if ((u32)vtx_count > vtx_cnt) vtx_cnt = vtx_count; #if PRINT_SORT_STATS printf("TVTX: %d || %d\n",vtx_cnt,vtx_count); #endif if (vtx_count<=0) return; //make lists of all triangles, with their pid and vid static std::vector lst; lst.resize(vtx_count*4); int pfsti=0; while (pp != pp_end) { u32 ppid = (u32)(pp - pp_base); if (pp->count > 2) { const u32 *idx = idx_base + pp->first; u32 flip = 0; float z0 = 0, z1 = 0; if (pp->isNaomi2()) { z0 = getProjectedZ(vtx_base + idx[0], pp->mvMatrix); z1 = getProjectedZ(vtx_base + idx[1], pp->mvMatrix); } for (u32 i = 0; i < pp->count - 2; i++) { const Vertex *v0, *v1; if (flip) { v0 = vtx_base + idx[i + 1]; v1 = vtx_base + idx[i]; } else { v0 = vtx_base + idx[i]; v1 = vtx_base + idx[i + 1]; } const Vertex *v2 = vtx_base + idx[i + 2]; fill_id(lst[pfsti].id, v0, v1, v2, vtx_base); lst[pfsti].pid = ppid; if (pp->isNaomi2()) { float z2 = getProjectedZ(v2, pp->mvMatrix); lst[pfsti].z = std::min(z0, std::min(z1, z2)); z0 = z1; z1 = z2; } else { lst[pfsti].z = minZ(vtx_base, lst[pfsti].id); } pfsti++; flip ^= 1; } } pp++; } u32 aused=pfsti; lst.resize(aused); //sort them std::stable_sort(lst.begin(),lst.end()); //Merge pids/draw cmds if two different pids are actually equal for (u32 k = 1; k < aused; k++) if (lst[k].pid != lst[k - 1].pid) { const PolyParam& curPoly = pp_base[lst[k].pid]; const PolyParam& prevPoly = pp_base[lst[k - 1].pid]; if (curPoly.equivalentIgnoreCullingDirection(prevPoly) && (curPoly.isp.CullMode < 2 || curPoly.isp.CullMode == prevPoly.isp.CullMode)) lst[k].pid = lst[k - 1].pid; } //re-assemble them into drawing commands vidx_sort.resize(aused*3); int idx=-1; for (u32 i=0; i& list) { u32 *idx_base = rendContext.idx.head(); Vertex *vtx_base = rendContext.verts.head(); for (const PolyParam& pp : list) { if (pp.pcw.Gouraud) continue; for (u32 i = 0; i + 2 < pp.count; i++) { Vertex& vertex = vtx_base[idx_base[pp.first + i]]; Vertex& lastVertex = vtx_base[idx_base[pp.first + i + 2]]; 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); setProvokingVertex(rendContext.global_param_tr); }