diff --git a/GLideN64/src/Config.cpp b/GLideN64/src/Config.cpp index e3e19e2..6bcb520 100644 --- a/GLideN64/src/Config.cpp +++ b/GLideN64/src/Config.cpp @@ -46,6 +46,7 @@ void Config::resetToDefaults() generalEmulation.rdramImageDitheringMode = BufferDitheringMode::bdmBlueNoise; generalEmulation.enableHWLighting = 0; generalEmulation.enableCoverage = 0; + generalEmulation.enableClipping = 1; generalEmulation.enableCustomSettings = 1; generalEmulation.enableShadersStorage = 1; generalEmulation.enableLegacyBlending = 0; diff --git a/GLideN64/src/Config.h b/GLideN64/src/Config.h index 0d63735..c6b62ec 100644 --- a/GLideN64/src/Config.h +++ b/GLideN64/src/Config.h @@ -63,6 +63,7 @@ struct Config u32 enableLOD; u32 enableHWLighting; u32 enableCoverage; + u32 enableClipping; u32 enableCustomSettings; u32 enableShadersStorage; u32 enableLegacyBlending; diff --git a/GLideN64/src/DepthBufferRender/ClipPolygon.cpp b/GLideN64/src/DepthBufferRender/ClipPolygon.cpp index 312710b..b45a3cc 100644 --- a/GLideN64/src/DepthBufferRender/ClipPolygon.cpp +++ b/GLideN64/src/DepthBufferRender/ClipPolygon.cpp @@ -24,34 +24,11 @@ //**************************************************************** -#include +#include +#include "gSP.h" +#include "Combiner.h" #include "ClipPolygon.h" -float LeftClip = 0.0f; -float RightClip = 320.0f; -float TopClip = 240.0f; -float BotClip = 0.0f; - -inline int cliptestx(vertexclip * v) -{ - int bits = 0; - if(v->y < LeftClip) - bits |= LEFT; - if(v->y > RightClip) - bits |= RIGHT; - return bits; -} - -inline int cliptesty(vertexclip * v) -{ - int bits = 0; - if(v->y < BotClip) - bits |= BOT; - if(v->y > TopClip) - bits |= TOP; - return bits; -} - /* * vbp is a pointer to a vertex array. The first 3 vertices in that * array is our source vertices. The rest of the array will be used @@ -61,46 +38,75 @@ inline int cliptesty(vertexclip * v) * * function returns the number of vertices in the resulting polygon */ - -int ClipPolygon(vertexclip *** final, vertexclip * vbp, int numVertices) +unsigned int clipInScreenSpace(vertexclip *** final, vertexclip * vbp, unsigned int numVertices) { - LeftClip = gSP.viewport.x; - RightClip = LeftClip + gSP.viewport.width; - BotClip = gSP.viewport.y; - TopClip = BotClip + gSP.viewport.height; - int max, n, dsti; + const float LeftClip = gSP.viewport.x; + const float RightClip = LeftClip + gSP.viewport.width; + const float BotClip = gSP.viewport.y; + const float TopClip = BotClip + gSP.viewport.height; + graphics::CombinerProgram * pCurrCmb = currentCombiner(); + const bool clipColor = pCurrCmb->usesShade(); + const bool clipTexCoords = pCurrCmb->usesTexture(); + + auto cliptestx = [LeftClip, RightClip](vertexclip * v) + { + int bits = 0; + if (v->y < LeftClip) + bits |= LEFT; + if (v->y > RightClip) + bits |= RIGHT; + return bits; + }; + + auto cliptesty = [BotClip, TopClip](vertexclip * v) + { + int bits = 0; + if (v->y < BotClip) + bits |= BOT; + if (v->y > TopClip) + bits |= TOP; + return bits; + }; + + auto clipVertex = [clipColor, clipTexCoords](vertexclip const* src1, vertexclip const* src2, vertexclip * dst, float a) + { + float ima = 1.0f - a; +#define doClip(V) dst->V = src1->V*ima + src2->V*a + doClip(x); + doClip(y); + doClip(z); +#undef doClip + }; + static vertexclip * vp1[12], * vp2[12]; // vertex ptr buffers vertexclip ** src = vp1; vertexclip ** dst = vp2; vertexclip ** tmp; - for (n = 0; n < numVertices; n++) + for (unsigned int n = 0; n < numVertices; n++) vp1[n] = vbp + n; - vp1[n] = vbp + 0; + vp1[numVertices] = vbp + 0; vbp += numVertices; // Next free vertex - dsti = 0; - max = numVertices; + unsigned int dsti = 0; + unsigned int max = numVertices; // right clip - - for (n = 0; n < max; n++) { + for (unsigned int n = 0; n < max; n++) { vertexclip * src1 = src[n]; // current vertex vertexclip * src2 = src[n + 1]; // next vertex - if ((src1->visible & RIGHT) == VISIBLE) { - dst[dsti++] = src1; // add visible vertex to list - if ((src2->visible & RIGHT) == VISIBLE) + if ((src1->clip & RIGHT) == VISIBLE) { + dst[dsti++] = src1; // add clip vertex to list + if ((src2->clip & RIGHT) == VISIBLE) continue; - } else if ((src2->visible & RIGHT) != VISIBLE) + } else if ((src2->clip & RIGHT) != VISIBLE) continue; float a = (RightClip - src1->x) / (src2->x - src1->x); - float ima = 1.0f - a; dst[dsti] = vbp++; // create new vertex - dst[dsti]->y = src1->y*ima + src2->y*a; + clipVertex(src1, src2, dst[dsti], a); dst[dsti]->x = RightClip; - dst[dsti]->z = src1->z*ima + src2->z*a; - dst[dsti]->visible = cliptesty(dst[dsti]); + dst[dsti]->clip = cliptesty(dst[dsti]); dsti++; } dst[dsti] = dst[0]; @@ -109,23 +115,20 @@ int ClipPolygon(vertexclip *** final, vertexclip * vbp, int numVertices) dsti = 0; // left clip - - for(n = 0; n < max; n++) { + for(unsigned int n = 0; n < max; n++) { vertexclip * src1 = src[n]; // current vertex vertexclip * src2 = src[n+1]; // next vertex - if((src1->visible & LEFT) == VISIBLE) { - dst[dsti++] = src1; // add visible vertex to list - if((src2->visible & LEFT) == VISIBLE) + if((src1->clip & LEFT) == VISIBLE) { + dst[dsti++] = src1; // add clip vertex to list + if((src2->clip & LEFT) == VISIBLE) continue; - } else if((src2->visible & LEFT) != VISIBLE) + } else if((src2->clip & LEFT) != VISIBLE) continue; float a = (LeftClip - src1->x) / (src2->x - src1->x); - float ima = 1.0f - a; dst[dsti] = vbp++; // create new vertex - dst[dsti]->y = src1->y*ima + src2->y*a; + clipVertex(src1, src2, dst[dsti], a); dst[dsti]->x = LeftClip; - dst[dsti]->z = src1->z*ima + src2->z*a; - dst[dsti]->visible = cliptesty(dst[dsti]); + dst[dsti]->clip = cliptesty(dst[dsti]); dsti++; } dst[dsti] = dst[0]; @@ -134,23 +137,20 @@ int ClipPolygon(vertexclip *** final, vertexclip * vbp, int numVertices) dsti = 0; // top clip - - for(n = 0; n < max; n++) { + for(unsigned int n = 0; n < max; n++) { vertexclip * src1 = src[n]; // current vertex vertexclip * src2 = src[n+1]; // next vertex - if((src1->visible & TOP) == VISIBLE) { - dst[dsti++] = src1; // add visible vertex to list - if((src2->visible & TOP) == VISIBLE) + if((src1->clip & TOP) == VISIBLE) { + dst[dsti++] = src1; // add clip vertex to list + if((src2->clip & TOP) == VISIBLE) continue; - } else if((src2->visible & TOP) != VISIBLE) + } else if((src2->clip & TOP) != VISIBLE) continue; float a = (TopClip - src1->y) / (src2->y - src1->y); - float ima = 1.0f - a; dst[dsti] = vbp++; // create new vertex - dst[dsti]->x = src1->x*ima + src2->x*a; + clipVertex(src1, src2, dst[dsti], a); dst[dsti]->y = TopClip; - dst[dsti]->z = src1->z*ima + src2->z*a; - dst[dsti]->visible = cliptestx(dst[dsti]); + dst[dsti]->clip = cliptestx(dst[dsti]); dsti++; } dst[dsti] = dst[0]; @@ -159,22 +159,19 @@ int ClipPolygon(vertexclip *** final, vertexclip * vbp, int numVertices) dsti = 0; // bot clip - - for(n = 0; n < max; n++) { + for(unsigned int n = 0; n < max; n++) { vertexclip * src1 = src[n]; // current vertex vertexclip * src2 = src[n+1]; // next vertex - if((src1->visible & BOT) == VISIBLE) { - dst[dsti++] = src1; // add visible vertex to list - if((src2->visible & BOT) == VISIBLE) + if((src1->clip & BOT) == VISIBLE) { + dst[dsti++] = src1; // add clip vertex to list + if((src2->clip & BOT) == VISIBLE) continue; - } else if((src2->visible & BOT) != VISIBLE) + } else if((src2->clip & BOT) != VISIBLE) continue; float a = (BotClip - src1->y) / (src2->y - src1->y); - float ima = 1.0f - a; dst[dsti] = vbp++; // create new vertex - dst[dsti]->x = src1->x*ima + src2->x*a; + clipVertex(src1, src2, dst[dsti], a); dst[dsti]->y = BotClip; - dst[dsti]->z = src1->z*ima + src2->z*a; dsti++; } @@ -182,3 +179,326 @@ int ClipPolygon(vertexclip *** final, vertexclip * vbp, int numVertices) return dsti; } + +unsigned int clipW(const SPVertex ** _vsrc, SPVertex * _vdst) +{ + graphics::CombinerProgram * pCurrCmb = currentCombiner(); + const bool clipColor = pCurrCmb->usesShade(); + const bool clipTexCoords = pCurrCmb->usesTexture(); + + auto copyVertex = [](SPVertex & _dst, const SPVertex * _src) + { + _dst.x = _src->x; + _dst.y = _src->y; + _dst.z = _src->z; + _dst.w = _src->w; + _dst.modify = _src->modify; + }; + + unsigned int dsti = 0; + for (int n = 0; n < 3; ++n) { + const SPVertex * src1 = _vsrc[n]; // current vertex + const SPVertex * src2 = _vsrc[n + 1]; // next vertex + if (src1->w >= 0.01f) { + copyVertex(_vdst[dsti++], src1); // add visible vertex to list + if (src2->w >= 0.01f) + continue; + } else if (src2->w < 0.01f) + continue; + float a = (-src1->w) / (src2->w - src1->w); + float ima = 1.0f - a; + // create new vertex + _vdst[dsti].x = src1->x*ima + src2->x*a; + _vdst[dsti].y = src1->y*ima + src2->y*a; + _vdst[dsti].z = src1->z*ima + src2->z*a; + _vdst[dsti].w = 0.01f; + _vdst[dsti].modify = 0; + dsti++; + } + return dsti; +} + +// -------------------------------------------------------------------------------------------------------------------- +// +// clipping using homogeneous coordinates +// http://www.3dcpptutorials.sk/ +// +// -------------------------------------------------------------------------------------------------------------------- +// +// c equals the x or y or z coordinate of a fragment +// +// -------------------------------------------------------------------------------------------------------------------- +// +// a fragment of an edge is in front of or lies on a negative clip plane: - w <= c +// +// a fragment of an edge is behind a negative clip plane: - w > c +// +// to find the intersection of an edge with a negative clip plane we need to find t for which: - w = c +// +// -------------------------------------------------------------------------------------------------------------------- +// +// w = w1 + (w2 - w1) * t +// c = c1 + (c2 - c1) * t +// +// - w = c +// +// - (w1 + (w2 - w1) * t) = c1 + (c2 - c1) * t +// - w1 - (w2 - w1) * t = c1 + (c2 - c1) * t +// - (w2 - w1) * t = c1 + w1 + (c2 - c1) * t +// - (c2 - c1) * t - (w2 - w1) * t = c1 + w1 +// (c2 - c1) * t + (w2 - w1) * t = - c1 - w1 +// (c2 - c1 + w2 - w1) * t = - c1 - w1 +// t = (- c1 - w1) / (c2 - c1 + w2 - w1) +// t = (c1 + w1) / (- c2 + c1 - w2 + w1) +// t = (c1 + w1) / (c1 - c2 + w1 - w2) +// +// -------------------------------------------------------------------------------------------------------------------- +// +// a fragment of an edge is in front of or lies on a positive clip plane: c <= w +// +// a fragment of an edge is behind a positive clip plane: c > w +// +// to find the intersection of an edge with a positive clip plane we need to find t for which: c = w +// +// -------------------------------------------------------------------------------------------------------------------- +// +// c = c1 + (c2 - c1) * t +// w = w1 + (w2 - w1) * t +// +// c = w +// +// c1 + (c2 - c1) * t = w1 + (w2 - w1) * t +// (c2 - c1) * t = w1 - c1 + (w2 - w1) * t +// (c2 - c1) * t - (w2 - w1) * t = w1 - c1 +// ((c2 - c1) - (w2 - w1)) * t = w1 - c1 +// (c2 - c1 - w2 + w1) * t = w1 - c1 +// t = (w1 - c1) / (c2 - c1 - w2 + w1) +// t = (- w1 + c1) / (- c2 + c1 + w2 - w1) +// t = (c1 - w1) / (c1 - c2 - w1 + w2) +// +// -------------------------------------------------------------------------------------------------------------------- +void clipInHomogeneousSpace(SPVertex * _pVertices, std::vector & _vResult, int _clipPlane) +{ + if (_clipPlane == 5 && GBI.isNoN()) { + // Skip near plane clipping + clipInHomogeneousSpace(_pVertices, _vResult, _clipPlane + 1); + return; + } + + graphics::CombinerProgram * pCurrCmb = currentCombiner(); + const bool usesColor = pCurrCmb->usesShade(); + const bool usesTexture = pCurrCmb->usesTexture(); + const float wScale = static_cast(gSP.clipRatio); + + auto clipVertex = [usesColor, usesTexture](SPVertex const* src, SPVertex * dst, float t) + { +#define doClip(V) dst->V += (src->V - dst->V) * t + doClip(x); + doClip(y); + doClip(z); + doClip(w); + doClip(bc0); + doClip(bc1); + if (usesColor) + { + doClip(r); + doClip(g); + doClip(b); + doClip(a); + doClip(flat_r); + doClip(flat_g); + doClip(flat_b); + doClip(flat_a); + } + if (usesTexture) + { + doClip(s); + doClip(t); + } + dst->modify = src->modify & (MODIFY_ST | MODIFY_RGBA); + dst->HWLight = src->HWLight; +#undef doClip + }; + + auto copyVertex = [usesColor, usesTexture](SPVertex const* src, SPVertex * dst) + { +#define doCopy(V) dst->V = src->V + doCopy(x); + doCopy(y); + doCopy(z); + doCopy(w); + doCopy(bc0); + doCopy(bc1); + doCopy(a); + doCopy(flat_a); + if (usesColor) + { + doCopy(r); + doCopy(g); + doCopy(b); + doCopy(flat_r); + doCopy(flat_g); + doCopy(flat_b); + } + if (usesTexture) + { + doCopy(s); + doCopy(t); + } + doCopy(HWLight); + dst->modify = src->modify & (MODIFY_ST | MODIFY_RGBA); +#undef doCopy + }; + + + if (_clipPlane >= 1 && _clipPlane <= 6) { + unsigned int fifocp, fbcp, fbcpc = 0; // fragment in front of clip plane, fragment behind clip plane, fragments behind clip plane count + + // visibility testing + + SPVertex *pVertex = _pVertices; + + for (unsigned int i = 0; i < 3; i++) { + bool visible; + + switch (_clipPlane) { + case 1: + visible = -pVertex->w * wScale <= pVertex->x; + break; + case 2: + visible = pVertex->x <= pVertex->w * wScale; + break; + case 3: + visible = -pVertex->w * wScale <= pVertex->y; + break; + case 4: + visible = pVertex->y <= pVertex->w * wScale; + break; + case 5: + visible = -pVertex->w * wScale <= pVertex->z; + break; + case 6: + visible = pVertex->z <= pVertex->w * wScale; + break; + } + + if (visible) { + fifocp = i; + } else { + fbcp = i; + fbcpc++; + } + + pVertex++; + } + + // clipping + + if (fbcpc == 3) { + return; + } else if (fbcpc == 2) { + SPVertex *Vertex1 = _pVertices + (fifocp + 1) % 3; + SPVertex *Vertex2 = _pVertices + (fifocp + 2) % 3; + SPVertex *Vertex3 = _pVertices + fifocp; + + float t1, t2; + + switch (_clipPlane) { + case 1: + t1 = (Vertex1->x + Vertex1->w * wScale) / (Vertex1->x - Vertex3->x + Vertex1->w * wScale - Vertex3->w * wScale); + t2 = (Vertex2->x + Vertex2->w * wScale) / (Vertex2->x - Vertex3->x + Vertex2->w * wScale - Vertex3->w * wScale); + break; + + case 2: + t1 = (Vertex1->x - Vertex1->w * wScale) / (Vertex1->x - Vertex3->x - Vertex1->w * wScale + Vertex3->w * wScale); + t2 = (Vertex2->x - Vertex2->w * wScale) / (Vertex2->x - Vertex3->x - Vertex2->w * wScale + Vertex3->w * wScale); + break; + + case 3: + t1 = (Vertex1->y + Vertex1->w * wScale) / (Vertex1->y - Vertex3->y + Vertex1->w * wScale - Vertex3->w * wScale); + t2 = (Vertex2->y + Vertex2->w * wScale) / (Vertex2->y - Vertex3->y + Vertex2->w * wScale - Vertex3->w * wScale); + break; + + case 4: + t1 = (Vertex1->y - Vertex1->w * wScale) / (Vertex1->y - Vertex3->y - Vertex1->w * wScale + Vertex3->w * wScale); + t2 = (Vertex2->y - Vertex2->w * wScale) / (Vertex2->y - Vertex3->y - Vertex2->w * wScale + Vertex3->w * wScale); + break; + + case 5: + t1 = (Vertex1->z + Vertex1->w * wScale) / (Vertex1->z - Vertex3->z + Vertex1->w * wScale - Vertex3->w * wScale); + t2 = (Vertex2->z + Vertex2->w * wScale) / (Vertex2->z - Vertex3->z + Vertex2->w * wScale - Vertex3->w * wScale); + break; + + case 6: + t1 = (Vertex1->z - Vertex1->w * wScale) / (Vertex1->z - Vertex3->z - Vertex1->w * wScale + Vertex3->w * wScale); + t2 = (Vertex2->z - Vertex2->w * wScale) / (Vertex2->z - Vertex3->z - Vertex2->w * wScale + Vertex3->w * wScale); + break; + } + + clipVertex(Vertex3, Vertex1, t1); + clipVertex(Vertex3, Vertex2, t2); + + clipInHomogeneousSpace(_pVertices, _vResult, _clipPlane + 1); + } else if (fbcpc == 1) { + unsigned int if1 = (fbcp + 1) % 3, if2 = (fbcp + 2) % 3; + + SPVertex *Vertex1 = _pVertices + if1; + SPVertex *Vertex2 = _pVertices + if2; + SPVertex *Vertex3 = _pVertices + fbcp; + + SPVertex NewVertices[3]; + + SPVertex *NewVertex1 = NewVertices + if1; + SPVertex *NewVertex2 = NewVertices + if2; + SPVertex *NewVertex3 = NewVertices + fbcp; + + float t1, t2; + + switch (_clipPlane) { + case 1: + t1 = (Vertex3->x + Vertex3->w * wScale) / (Vertex3->x - Vertex1->x + Vertex3->w * wScale - Vertex1->w * wScale); + t2 = (Vertex3->x + Vertex3->w * wScale) / (Vertex3->x - Vertex2->x + Vertex3->w * wScale - Vertex2->w * wScale); + break; + + case 2: + t1 = (Vertex3->x - Vertex3->w * wScale) / (Vertex3->x - Vertex1->x - Vertex3->w * wScale + Vertex1->w * wScale); + t2 = (Vertex3->x - Vertex3->w * wScale) / (Vertex3->x - Vertex2->x - Vertex3->w * wScale + Vertex2->w * wScale); + break; + + case 3: + t1 = (Vertex3->y + Vertex3->w * wScale) / (Vertex3->y - Vertex1->y + Vertex3->w * wScale - Vertex1->w * wScale); + t2 = (Vertex3->y + Vertex3->w * wScale) / (Vertex3->y - Vertex2->y + Vertex3->w * wScale - Vertex2->w * wScale); + break; + + case 4: + t1 = (Vertex3->y - Vertex3->w * wScale) / (Vertex3->y - Vertex1->y - Vertex3->w * wScale + Vertex1->w * wScale); + t2 = (Vertex3->y - Vertex3->w * wScale) / (Vertex3->y - Vertex2->y - Vertex3->w * wScale + Vertex2->w * wScale); + break; + + case 5: + t1 = (Vertex3->z + Vertex3->w * wScale) / (Vertex3->z - Vertex1->z + Vertex3->w * wScale - Vertex1->w * wScale); + t2 = (Vertex3->z + Vertex3->w * wScale) / (Vertex3->z - Vertex2->z + Vertex3->w * wScale - Vertex2->w * wScale); + break; + + case 6: + t1 = (Vertex3->z - Vertex3->w * wScale) / (Vertex3->z - Vertex1->z - Vertex3->w * wScale + Vertex1->w * wScale); + t2 = (Vertex3->z - Vertex3->w * wScale) / (Vertex3->z - Vertex2->z - Vertex3->w * wScale + Vertex2->w * wScale); + break; + } + + copyVertex(Vertex3, NewVertex3); + copyVertex(Vertex2, NewVertex2); + clipVertex(Vertex1, Vertex3, t1); + copyVertex(Vertex3, NewVertex1); + clipVertex(NewVertex2, NewVertex3, t2); + + clipInHomogeneousSpace(_pVertices, _vResult, _clipPlane + 1); + clipInHomogeneousSpace(NewVertices, _vResult, _clipPlane + 1); + } else if (fbcpc == 0) { + clipInHomogeneousSpace(_pVertices, _vResult, _clipPlane + 1); + } + } else if (_clipPlane == 7) { + for (int i = 0; i < 3; ++i) + _vResult.push_back(_pVertices[i]); + } +} diff --git a/GLideN64/src/DepthBufferRender/ClipPolygon.h b/GLideN64/src/DepthBufferRender/ClipPolygon.h index fe88473..dd5cb18 100644 --- a/GLideN64/src/DepthBufferRender/ClipPolygon.h +++ b/GLideN64/src/DepthBufferRender/ClipPolygon.h @@ -7,10 +7,12 @@ #define TOP 4 #define BOT 8 +struct SPVertex; + struct vertexclip { - float x,y,z; - int visible; + float x, y, z; + int clip; }; /* @@ -22,6 +24,10 @@ struct vertexclip * * function returns the number of vertices in the resulting polygon */ -int ClipPolygon(vertexclip *** final, vertexclip * vbp, int numVertices); +unsigned int clipInScreenSpace(vertexclip *** final, vertexclip * vbp, unsigned int numVertices); + +unsigned int clipW(const SPVertex ** _vsrc, SPVertex * _vdst); + +void clipInHomogeneousSpace(SPVertex * pFragments, std::vector & _vResult, int _clipPlane = 1); #endif // CLIP_POLYGON_H diff --git a/GLideN64/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramBuilder.cpp b/GLideN64/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramBuilder.cpp index 75cde79..c3e15fe 100644 --- a/GLideN64/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramBuilder.cpp +++ b/GLideN64/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramBuilder.cpp @@ -256,7 +256,7 @@ public: ss << "# define IN in" << std::endl << "# define OUT out" << std::endl; if (_glinfo.noPerspective) { ss << "#extension GL_NV_shader_noperspective_interpolation : enable" << std::endl - << "noperspective OUT highp float vZCoord;" << std::endl << "uniform lowp int uClampMode;" << std::endl; + << "noperspective OUT highp float vZCoord;" << std::endl; } m_part = ss.str(); } @@ -322,7 +322,6 @@ public: "{ \n" " gl_Position = aPosition; \n" " vShadeColor = aColor; \n" - " vShadeColorNoperspective = aColor; \n" " vec2 texCoord = aTexCoord; \n" " texCoord *= uTexScale; \n" " if (uTexturePersp == 0 && aModify[2] == 0.0) texCoord *= 0.5;\n" @@ -354,6 +353,7 @@ public: " vShadeColor.rgb = vec3(fp); \n" " } \n" " vBaryCoords = vec4(aBaryCoords, 1.0 - aBaryCoords.x - aBaryCoords.y, 0.5); \n" + " vShadeColorNoperspective = vShadeColor; \n" ; } }; @@ -388,7 +388,6 @@ public: "{ \n" " gl_Position = aPosition; \n" " vShadeColor = aColor; \n" - " vShadeColorNoperspective = aColor; \n" " vNumLights = aNumLights; \n" " if (aModify != vec4(0.0)) { \n" " if ((aModify[0]) != 0.0) { \n" @@ -414,6 +413,7 @@ public: " vShadeColor.rgb = vec3(fp); \n" " } \n" " vBaryCoords = vec4(aBaryCoords, 1.0 - aBaryCoords.x - aBaryCoords.y, 0.5); \n" + " vShadeColorNoperspective = vShadeColor; \n" ; } }; @@ -489,12 +489,17 @@ public: m_part = " gl_ClipDistance[0] = gl_Position.w - gl_Position.z; \n" ; - } else if (config.generalEmulation.enableFragmentDepthWrite != 0 && _glinfo.noPerspective) { - m_part = - " vZCoord = gl_Position.z / gl_Position.w; \n" - " if (uClampMode > 0) \n" - " gl_Position.z = 0.0; \n" - ; + } else if (config.generalEmulation.enableClipping != 0) { + + if (config.generalEmulation.enableFragmentDepthWrite != 0 && _glinfo.noPerspective) { + m_part = " vZCoord = gl_Position.z / gl_Position.w; \n"; + m_part += " gl_Position.z = 0.0; \n"; + } else { + // Move the near plane towards the camera. + // It helps to avoid issues with near-plane clipping in games, which do not use it. + // Z must be scaled back in fragment shader. + m_part = " gl_Position.z /= 8.0; \n"; + } } m_part += " gl_Position.zw *= vec2(uClipRatio); \n" @@ -1171,15 +1176,15 @@ public: m_part = "highp float writeDepth();\n"; ; - if (_glinfo.isGLESX && _glinfo.noPerspective) { - m_part = - "noperspective IN highp float vZCoord; \n" - "uniform lowp float uPolygonOffset; \n" - "uniform lowp int uClampMode; \n" - + m_part - ; - } } + if (_glinfo.isGLESX && _glinfo.noPerspective) { + m_part = + "noperspective IN highp float vZCoord; \n" + "uniform lowp float uPolygonOffset; \n" + + m_part + ; + } + } }; @@ -1507,8 +1512,16 @@ public: " lowp vec4 vec_color; \n" " lowp float alpha1; \n" " lowp vec3 color1, input_color; \n" - " lowp vec4 shadeColor = uScreenSpaceTriangle == 0 ? vShadeColor : vShadeColorNoperspective; \n" ; + if (config.generalEmulation.enableClipping != 0) + m_part += + " lowp vec4 shadeColor = vShadeColorNoperspective; \n" + ; + else + m_part += + " lowp vec4 shadeColor = uScreenSpaceTriangle == 0 ? vShadeColor : vShadeColorNoperspective; \n" + ; + m_part += "#define WRAP(x, low, high) mod((x)-(low), (high)-(low)) + (low) \n"; // Return wrapped value of x in interval [low, high) // m_part += "#define WRAP(x, low, high) (x) - ((high)-(low)) * floor(((x)-(low))/((high)-(low))) \n"; // Perhaps more compatible? // m_part += "#define WRAP(x, low, high) (x) + ((high)-(low)) * (1.0-step(low,x)) - ((high)-(low)) * step(high,x) \n"; // Step based version. Only wraps correctly if input is in the range [low-(high-low), high + (high-low)). Similar to old code. @@ -1534,8 +1547,16 @@ public: " lowp vec4 vec_color, combined_color; \n" " lowp float alpha1, alpha2; \n" " lowp vec3 color1, color2, input_color; \n" - " lowp vec4 shadeColor = uScreenSpaceTriangle == 0 ? vShadeColor : vShadeColorNoperspective; \n" ; + if (config.generalEmulation.enableClipping != 0) + m_part += + " lowp vec4 shadeColor = vShadeColorNoperspective; \n" + ; + else + m_part += + " lowp vec4 shadeColor = uScreenSpaceTriangle == 0 ? vShadeColor : vShadeColorNoperspective; \n" + ; + m_part += "#define WRAP(x, low, high) mod((x)-(low), (high)-(low)) + (low) \n"; // Return wrapped value of x in interval [low, high) // m_part += "#define WRAP(x, low, high) (x) - ((high)-(low)) * floor(((x)-(low))/((high)-(low))) \n"; // Perhaps more compatible? // m_part += "#define WRAP(x, low, high) (x) + (2.0) * (1.0-step(low,x)) - (2.0) * step(high,x) \n"; // Step based version. Only wraps correctly if input is in the range [low-(high-low), high + (high-low)). Similar to old code. @@ -1909,12 +1930,18 @@ public: "highp float writeDepth() \n" "{ \n" ; - if (_glinfo.isGLESX && _glinfo.noPerspective) { - m_part += - " if (uClampMode == 1 && (vZCoord > 1.0)) discard; \n" - " highp float FragDepth = (uDepthSource != 0) ? uPrimDepth : \n" - " clamp((vZCoord - uPolygonOffset) * uDepthScale.s + uDepthScale.t, 0.0, 1.0); \n" - ; + if (_glinfo.isGLESX && config.generalEmulation.enableClipping != 0) { + if (_glinfo.noPerspective) { + m_part += + " highp float FragDepth = (uDepthSource != 0) ? uPrimDepth : \n" + " clamp((vZCoord - uPolygonOffset) * uDepthScale.s + uDepthScale.t, 0.0, 1.0); \n" + ; + } else { + m_part += + " highp float FragDepth = (uDepthSource != 0) ? uPrimDepth : \n" + " clamp(8.0 * (gl_FragCoord.z * 2.0 - 1.0) * uDepthScale.s + uDepthScale.t, 0.0, 1.0); \n" + ; + } } else { m_part += " highp float FragDepth = (uDepthSource != 0) ? uPrimDepth : \n" @@ -1922,23 +1949,33 @@ public: ; } m_part += - " highp int iZ = FragDepth > 0.999 ? 262143 : int(floor(FragDepth * 262143.0)); \n" + " highp int iZ = FragDepth > 0.999 ? 262143 : int(floor(FragDepth * 262143.0)); \n" " mediump int y0 = clamp(iZ/512, 0, 511); \n" " mediump int x0 = iZ - 512*y0; \n" - " highp uint iN64z = texelFetch(uZlutImage,ivec2(x0,y0), 0).r; \n" - " return clamp(float(iN64z)/65532.0, 0.0, 1.0); \n" + " highp uint iN64z = texelFetch(uZlutImage,ivec2(x0,y0), 0).r; \n" + " return clamp(float(iN64z)/65532.0, 0.0, 1.0); \n" "} \n" ; } else { - if (_glinfo.isGLESX && _glinfo.noPerspective) { - m_part = - "highp float writeDepth() \n" - "{ \n" - " if (uClampMode == 1 && (vZCoord > 1.0)) discard; \n" - " if (uDepthSource != 0) return uPrimDepth; \n" - " return clamp((vZCoord - uPolygonOffset) * uDepthScale.s + uDepthScale.t, 0.0, 1.0); \n" - "} \n" - ; + if (_glinfo.isGLESX && config.generalEmulation.enableClipping != 0) { + if (_glinfo.noPerspective) { + m_part = + "highp float writeDepth() \n" + "{ \n" + " if (uDepthSource != 0) return uPrimDepth; \n" + " return clamp((vZCoord - uPolygonOffset) * uDepthScale.s + uDepthScale.t, 0.0, 1.0); \n" + "} \n" + ; + } else { + m_part = + "highp float writeDepth() \n" + "{ \n" + " if (uDepthSource != 0) return uPrimDepth; \n" + " return clamp(8.0 * (gl_FragCoord.z * 2.0 - 1.0) * uDepthScale.s + uDepthScale.t, 0.0, 1.0); \n" + "} \n" + ; + } + } else { m_part = "highp float writeDepth() \n" @@ -1946,7 +1983,7 @@ public: " if (uDepthSource != 0) return uPrimDepth; \n" " return clamp((gl_FragCoord.z * 2.0 - 1.0) * uDepthScale.s + uDepthScale.t, 0.0, 1.0); \n" "} \n" - ; + ; } } } diff --git a/GLideN64/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramUniformFactory.cpp b/GLideN64/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramUniformFactory.cpp index 66173c0..9088fcd 100644 --- a/GLideN64/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramUniformFactory.cpp +++ b/GLideN64/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramUniformFactory.cpp @@ -709,35 +709,6 @@ private: iUniform uRenderTarget; }; -class UClampMode : public UniformGroup -{ -public: - UClampMode(GLuint _program) { - LocateUniform(uClampMode); - } - - void update(bool _force) override - { - int clampMode = -1; - switch (gfxContext.getClampMode()) - { - case graphics::ClampMode::ClippingEnabled: - clampMode = 0; - break; - case graphics::ClampMode::NoNearPlaneClipping: - clampMode = 1; - break; - case graphics::ClampMode::NoClipping: - clampMode = 2; - break; - } - uClampMode.set(clampMode, _force); - } - -private: - iUniform uClampMode; -}; - class UClipRatio : public UniformGroup { public: @@ -771,7 +742,6 @@ private: fUniform uPolygonOffset; }; - class UScreenCoordsScale : public UniformGroup { public: @@ -1162,7 +1132,6 @@ void CombinerProgramUniformFactory::buildUniforms(GLuint _program, _uniforms.emplace_back(new URenderTarget(_program)); if (m_glInfo.isGLESX && m_glInfo.noPerspective) { - _uniforms.emplace_back(new UClampMode(_program)); _uniforms.emplace_back(new UPolygonOffset(_program)); } diff --git a/GLideN64/src/Graphics/OpenGLContext/GLSL/glsl_ShaderStorage.cpp b/GLideN64/src/Graphics/OpenGLContext/GLSL/glsl_ShaderStorage.cpp index 06f4ff9..0a3304f 100644 --- a/GLideN64/src/Graphics/OpenGLContext/GLSL/glsl_ShaderStorage.cpp +++ b/GLideN64/src/Graphics/OpenGLContext/GLSL/glsl_ShaderStorage.cpp @@ -202,12 +202,10 @@ bool ShaderStorage::saveShadersStorage(const graphics::Combiners & _combiners) c static CombinerProgramImpl * _readCombinerProgramFromStream(std::istream & _is, + CombinerKey& _cmbKey, CombinerProgramUniformFactory & _uniformFactory, opengl::CachedUseProgram * _useProgram) { - CombinerKey cmbKey; - cmbKey.read(_is); - int inputs; _is.read((char*)&inputs, sizeof(inputs)); CombinerInputs cmbInputs(inputs); @@ -220,15 +218,17 @@ CombinerProgramImpl * _readCombinerProgramFromStream(std::istream & _is, _is.read(binary.data(), binaryLength); GLuint program = glCreateProgram(); - const bool isRect = cmbKey.isRectKey(); + const bool isRect = _cmbKey.isRectKey(); glsl::Utils::locateAttributes(program, isRect, cmbInputs.usesTexture()); glProgramBinary(program, binaryFormat, binary.data(), binaryLength); - assert(glsl::Utils::checkProgramLinkStatus(program)); + if (!glsl::Utils::checkProgramLinkStatus(program, true)) { + return nullptr; + } UniformGroups uniforms; - _uniformFactory.buildUniforms(program, cmbInputs, cmbKey, uniforms); + _uniformFactory.buildUniforms(program, cmbInputs, _cmbKey, uniforms); - return new CombinerProgramImpl(cmbKey, program, _useProgram, cmbInputs, std::move(uniforms)); + return new CombinerProgramImpl(_cmbKey, program, _useProgram, cmbInputs, std::move(uniforms)); } bool ShaderStorage::_loadFromCombinerKeys(graphics::Combiners & _combiners) @@ -344,9 +344,21 @@ bool ShaderStorage::loadShadersStorage(graphics::Combiners & _combiners) f32 progress = 0.0f; f32 percents = percent; for (u32 i = 0; i < len; ++i) { - CombinerProgramImpl * pCombiner = _readCombinerProgramFromStream(fin, uniformFactory, m_useProgram); - pCombiner->update(true); - _combiners[pCombiner->getKey()] = pCombiner; + CombinerKey cmbKey; + cmbKey.read(fin); + + CombinerProgramImpl * pCombiner = _readCombinerProgramFromStream(fin, cmbKey, uniformFactory, m_useProgram); + + if (pCombiner != nullptr) { + pCombiner->update(true); + _combiners[pCombiner->getKey()] = pCombiner; + } else { + LOG(LOG_ERROR, "Shader is not a valid binary compiling from key instead"); + graphics::CombinerProgram *pCombinerFromKey = Combiner_Compile(cmbKey); + pCombinerFromKey->update(true); + _combiners[cmbKey] = pCombinerFromKey; + } + progress += step; if (progress > percents) { displayLoadProgress(L"LOAD COMBINER SHADERS %.1f%%", f32(i + 1) * 100.f / f32(len) ); diff --git a/GLideN64/src/Graphics/OpenGLContext/GLSL/glsl_ShaderStorage.h b/GLideN64/src/Graphics/OpenGLContext/GLSL/glsl_ShaderStorage.h index 4ef8fa3..786e6aa 100644 --- a/GLideN64/src/Graphics/OpenGLContext/GLSL/glsl_ShaderStorage.h +++ b/GLideN64/src/Graphics/OpenGLContext/GLSL/glsl_ShaderStorage.h @@ -20,7 +20,7 @@ namespace glsl { bool _saveCombinerKeys(const graphics::Combiners & _combiners) const; bool _loadFromCombinerKeys(graphics::Combiners & _combiners); - const u32 m_formatVersion = 0x30U; + const u32 m_formatVersion = 0x31U; const u32 m_keysFormatVersion = 0x05; const opengl::GLInfo & m_glinfo; opengl::CachedUseProgram * m_useProgram; diff --git a/GLideN64/src/Graphics/OpenGLContext/GLSL/glsl_Utils.cpp b/GLideN64/src/Graphics/OpenGLContext/GLSL/glsl_Utils.cpp index 125e898..6cca54e 100644 --- a/GLideN64/src/Graphics/OpenGLContext/GLSL/glsl_Utils.cpp +++ b/GLideN64/src/Graphics/OpenGLContext/GLSL/glsl_Utils.cpp @@ -52,9 +52,9 @@ bool Utils::checkShaderCompileStatus(GLuint obj) return true; } -bool Utils::checkProgramLinkStatus(GLuint obj) +static +bool _checkProgramLinkStatus(GLuint obj) { -#ifdef GL_DEBUG GLint status; glGetProgramiv(obj, GL_LINK_STATUS, &status); if (status == GL_FALSE) { @@ -64,10 +64,20 @@ bool Utils::checkProgramLinkStatus(GLuint obj) LOG(LOG_ERROR, "shader_link error: %s", shader_log); return false; } -#endif return true; } +bool Utils::checkProgramLinkStatus(GLuint obj, bool _force) +{ +#ifdef GL_DEBUG + return _checkProgramLinkStatus(obj); +#else + if (_force) + return _checkProgramLinkStatus(obj); + return true; +#endif +} + void Utils::logErrorShader(GLenum _shaderType, const std::string & _strShader) { LOG(LOG_ERROR, "Error in %s shader", _shaderType == GL_VERTEX_SHADER ? "vertex" : "fragment"); diff --git a/GLideN64/src/Graphics/OpenGLContext/GLSL/glsl_Utils.h b/GLideN64/src/Graphics/OpenGLContext/GLSL/glsl_Utils.h index 6d8239d..c74d112 100644 --- a/GLideN64/src/Graphics/OpenGLContext/GLSL/glsl_Utils.h +++ b/GLideN64/src/Graphics/OpenGLContext/GLSL/glsl_Utils.h @@ -8,7 +8,7 @@ namespace glsl { struct Utils { static void locateAttributes(GLuint _program, bool _rect, bool _textures); static bool checkShaderCompileStatus(GLuint obj); - static bool checkProgramLinkStatus(GLuint obj); + static bool checkProgramLinkStatus(GLuint obj, bool _force = false); static void logErrorShader(GLenum _shaderType, const std::string & _strShader); static GLuint createRectShaderProgram(const char * _strVertex, const char * _strFragment); diff --git a/GLideN64/src/Graphics/OpenGLContext/opengl_ColorBufferReaderWithEGLImage.cpp b/GLideN64/src/Graphics/OpenGLContext/opengl_ColorBufferReaderWithEGLImage.cpp index c248ed6..1bb66a4 100644 --- a/GLideN64/src/Graphics/OpenGLContext/opengl_ColorBufferReaderWithEGLImage.cpp +++ b/GLideN64/src/Graphics/OpenGLContext/opengl_ColorBufferReaderWithEGLImage.cpp @@ -58,7 +58,7 @@ const u8 * ColorBufferReaderWithEGLImage::_readPixels(const ReadColorBufferParam _stride = m_pTexture->width; } else { gpuData = m_pixelData.data(); - glReadPixels(_params.x0, _params.y0, _params.width, _params.height, format, type, gpuData); + glReadPixels(_params.x0, _params.y0, m_pTexture->width, _params.height, format, type, gpuData); _heightOffset = 0; _stride = m_pTexture->width; } diff --git a/GLideN64/src/Graphics/OpenGLContext/opengl_Parameters.cpp b/GLideN64/src/Graphics/OpenGLContext/opengl_Parameters.cpp index ea8e449..68ad2ce 100644 --- a/GLideN64/src/Graphics/OpenGLContext/opengl_Parameters.cpp +++ b/GLideN64/src/Graphics/OpenGLContext/opengl_Parameters.cpp @@ -126,6 +126,7 @@ namespace graphics { namespace drawmode { DrawModeParam TRIANGLES(GL_TRIANGLES); DrawModeParam TRIANGLE_STRIP(GL_TRIANGLE_STRIP); + DrawModeParam TRIANGLE_FAN(GL_TRIANGLE_FAN); DrawModeParam LINES(GL_LINES); } diff --git a/GLideN64/src/Graphics/Parameters.h b/GLideN64/src/Graphics/Parameters.h index 86f92a4..f23ac95 100644 --- a/GLideN64/src/Graphics/Parameters.h +++ b/GLideN64/src/Graphics/Parameters.h @@ -126,6 +126,7 @@ namespace graphics { namespace drawmode { extern DrawModeParam TRIANGLES; extern DrawModeParam TRIANGLE_STRIP; + extern DrawModeParam TRIANGLE_FAN; extern DrawModeParam LINES; } diff --git a/GLideN64/src/GraphicsDrawer.cpp b/GLideN64/src/GraphicsDrawer.cpp index bd8aba6..a9606e8 100644 --- a/GLideN64/src/GraphicsDrawer.cpp +++ b/GLideN64/src/GraphicsDrawer.cpp @@ -813,7 +813,6 @@ void GraphicsDrawer::drawTriangles() } _prepareDrawTriangle(DrawingState::Triangle); - Context::DrawTriangleParameters triParams; triParams.mode = drawmode::TRIANGLES; triParams.flatColors = m_bFlatColors; @@ -823,11 +822,16 @@ void GraphicsDrawer::drawTriangles() triParams.vertices = triangles.vertices.data(); triParams.elements = triangles.elements.data(); triParams.combiner = currentCombiner(); - gfxContext.drawTriangles(triParams); g_debugger.addTriangles(triParams); if (config.frameBufferEmulation.enable != 0) { - const f32 maxY = renderTriangles(triangles.vertices.data(), triangles.elements.data(), triangles.num); + f32 maxY; + if (config.generalEmulation.enableClipping != 0) { + maxY = renderAndDrawTriangles(triangles.vertices.data(), triangles.elements.data(), triangles.num, m_bFlatColors); + } else { + gfxContext.drawTriangles(triParams); + maxY = renderTriangles(triangles.vertices.data(), triangles.elements.data(), triangles.num); + } frameBufferList().setBufferChanged(maxY); if (config.frameBufferEmulation.copyDepthToRDRAM == Config::cdSoftwareRender && gDP.otherMode.depthUpdate != 0) { @@ -835,6 +839,8 @@ void GraphicsDrawer::drawTriangles() if (pCurrentDepthBuffer != nullptr) pCurrentDepthBuffer->setDirty(); } + } else { + gfxContext.drawTriangles(triParams); } triangles.num = 0; @@ -880,7 +886,7 @@ void GraphicsDrawer::drawScreenSpaceTriangle(u32 _numVtx, graphics::DrawModePara m_dmaVerticesNum = 0; if (config.frameBufferEmulation.enable != 0) { - const f32 maxY = renderTriangles(m_dmaVertices.data(), nullptr, _numVtx); + const f32 maxY = renderScreenSpaceTriangles(m_dmaVertices.data(), _numVtx); frameBufferList().setBufferChanged(maxY); if (config.frameBufferEmulation.copyDepthToRDRAM == Config::cdSoftwareRender && gDP.otherMode.depthUpdate != 0) { @@ -905,12 +911,18 @@ void GraphicsDrawer::drawDMATriangles(u32 _numVtx) triParams.verticesCount = _numVtx; triParams.vertices = m_dmaVertices.data(); triParams.combiner = currentCombiner(); - gfxContext.drawTriangles(triParams); g_debugger.addTriangles(triParams); m_dmaVerticesNum = 0; if (config.frameBufferEmulation.enable != 0) { - const f32 maxY = renderTriangles(m_dmaVertices.data(), nullptr, _numVtx); + f32 maxY; + if (config.generalEmulation.enableClipping != 0) { + maxY = renderAndDrawTriangles(m_dmaVertices.data(), nullptr, _numVtx, m_bFlatColors); + } + else { + gfxContext.drawTriangles(triParams); + maxY = renderTriangles(m_dmaVertices.data(), nullptr, _numVtx); + } frameBufferList().setBufferChanged(maxY); if (config.frameBufferEmulation.copyDepthToRDRAM == Config::cdSoftwareRender && gDP.otherMode.depthUpdate != 0) { @@ -918,6 +930,8 @@ void GraphicsDrawer::drawDMATriangles(u32 _numVtx) if (pCurrentDepthBuffer != nullptr) pCurrentDepthBuffer->setDirty(); } + } else { + gfxContext.drawTriangles(triParams); } } diff --git a/GLideN64/src/SoftwareRender.cpp b/GLideN64/src/SoftwareRender.cpp index 6717776..776677a 100644 --- a/GLideN64/src/SoftwareRender.cpp +++ b/GLideN64/src/SoftwareRender.cpp @@ -1,5 +1,6 @@ #include #include +#include #include "DepthBufferRender/ClipPolygon.h" #include "DepthBufferRender/DepthBufferRender.h" #include "gSP.h" @@ -8,25 +9,70 @@ #include "DepthBuffer.h" #include "Config.h" +static +bool calcScreenCoordinates(const SPVertex** _vsrc, vertexclip * _vclip, u32 _numVertex, bool & _clockwise) +{ + for (u32 i = 0; i < _numVertex; ++i) { + const SPVertex& v = *_vsrc[i]; + + if ((v.modify & MODIFY_XY) == 0) { + _vclip[i].x = gSP.viewport.vtrans[0] + (v.x / v.w) * gSP.viewport.vscale[0]; + _vclip[i].y = gSP.viewport.vtrans[1] + (v.y / v.w) * -gSP.viewport.vscale[1]; + } else { + _vclip[i].x = v.x; + _vclip[i].y = v.y; + } + + if ((v.modify & MODIFY_Z) == 0) { + _vclip[i].z = (gSP.viewport.vtrans[2] + (v.z / v.w) * gSP.viewport.vscale[2]) * 32767.0f; + } else { + _vclip[i].z = v.z * 32767.0f; + } + } + + // Check culling + const float x1 = _vclip[0].x - _vclip[1].x; + const float y1 = _vclip[0].y - _vclip[1].y; + const float x2 = _vclip[2].x - _vclip[1].x; + const float y2 = _vclip[2].y - _vclip[1].y; + + _clockwise = (x1*y2 - y1 * x2) >= 0.0f; + + const u32 cullMode = (gSP.geometryMode & G_CULL_BOTH); + if (cullMode == G_CULL_BOTH && GBI.isCullBoth()) { + // Cull front and back + return false; + } else if (cullMode == G_CULL_FRONT) { + if (_clockwise) //clockwise, negative + return false; + } else if (cullMode == G_CULL_BACK) { + if (!_clockwise) //counter-clockwise, positive + return false; + } + + return true; +} + inline void clipTest(vertexclip & _vtx) { - _vtx.visible = 0; + _vtx.clip = 0; if (_vtx.x > gSP.viewport.width) - _vtx.visible |= RIGHT; + _vtx.clip |= RIGHT; if (_vtx.x < 0) - _vtx.visible |= LEFT; + _vtx.clip |= LEFT; if (_vtx.y > gSP.viewport.height) - _vtx.visible |= TOP; + _vtx.clip |= TOP; if (_vtx.y < 0) - _vtx.visible |= BOT; + _vtx.clip |= BOT; + if (_vtx.clip != 0) + int t = 0; } -static -bool calcScreenCoordinates(SPVertex * _vsrc, vertexclip * _vclip, u32 _numVertex, bool & _clockwise) +bool calcScreenCoordinates(const SPVertex * _vsrc, vertexclip * _vclip, u32 _numVertex, bool _wClipped, bool & _clockwise) { for (u32 i = 0; i < _numVertex; ++i) { - SPVertex & v = _vsrc[i]; + const SPVertex & v = _vsrc[i]; if ((v.modify & MODIFY_XY) == 0) { _vclip[i].x = gSP.viewport.vtrans[0] + (v.x / v.w) * gSP.viewport.vscale[0]; @@ -45,7 +91,7 @@ bool calcScreenCoordinates(SPVertex * _vsrc, vertexclip * _vclip, u32 _numVertex clipTest(_vclip[i]); } - if (_numVertex > 3) // Don't cull w-clipped vertices + if (_wClipped) // Don't cull w-clipped vertices return true; // Check culling @@ -54,13 +100,13 @@ bool calcScreenCoordinates(SPVertex * _vsrc, vertexclip * _vclip, u32 _numVertex const float x2 = _vclip[2].x - _vclip[1].x; const float y2 = _vclip[2].y - _vclip[1].y; - _clockwise = (x1*y2 - y1*x2) >= 0.0f; - if (RSP.LLE) - return true; + _clockwise = (x1*y2 - y1 * x2) >= 0.0f; const u32 cullMode = (gSP.geometryMode & G_CULL_BOTH); - - if (cullMode == G_CULL_FRONT) { + if (cullMode == G_CULL_BOTH && GBI.isCullBoth()) { + // Cull front and back + return false; + } else if (cullMode == G_CULL_FRONT) { if (_clockwise) //clockwise, negative return false; } else if (cullMode == G_CULL_BACK) { @@ -71,6 +117,19 @@ bool calcScreenCoordinates(SPVertex * _vsrc, vertexclip * _vclip, u32 _numVertex return true; } +static +bool isClockwise(const SPVertex ** _vsrc) +{ + // Check culling + const float x1 = _vsrc[0]->x - _vsrc[1]->x; + const float y1 = _vsrc[0]->y - _vsrc[1]->y; + const float x2 = _vsrc[2]->x - _vsrc[1]->x; + const float y2 = _vsrc[2]->y - _vsrc[1]->y; + + return (x1*y2 - y1 * x2) >= 0.0f; +} + + inline int floatToFixed16(double _v) { @@ -78,7 +137,7 @@ int floatToFixed16(double _v) } static -int calcDzDx(vertexclip * _v) +int calcDzDx(const vertexclip* _v) { double X0 = _v[0].x; double Y0 = _v[0].y; @@ -101,6 +160,30 @@ int calcDzDx(vertexclip * _v) return 0; } +static +int calcDzDx(const SPVertex ** _v) +{ + double X0 = _v[0]->x; + double Y0 = _v[0]->y; + double X1 = _v[1]->x; + double Y1 = _v[1]->y; + double X2 = _v[2]->x; + double Y2 = _v[2]->y; + double diffy_02 = Y0 - Y2; + double diffy_12 = Y1 - Y2; + double diffx_02 = X0 - X2; + double diffx_12 = X1 - X2; + + double denom = (diffx_02 * diffy_12 - diffx_12 * diffy_02); + if (denom*denom > 0.0) { + double diffz_02 = _v[0]->z - _v[2]->z; + double diffz_12 = _v[1]->z - _v[2]->z; + double fdzdx = (diffz_02 * diffy_12 - diffz_12 * diffy_02) / denom; + return floatToFixed16(fdzdx); + } + return 0; +} + static int calcDzDx2(const SPVertex ** _vsrc) { @@ -131,40 +214,44 @@ int calcDzDx2(const SPVertex ** _vsrc) return 0; } -inline -void copyVertex(SPVertex & _dst, const SPVertex * _src) +f32 renderScreenSpaceTriangles(const SPVertex *_pVertices, u32 _numElements) { - _dst.x = _src->x; - _dst.y = _src->y; - _dst.z = _src->z; - _dst.w = _src->w; - _dst.modify = _src->modify; -} + vertexi vdraw[3]; + const SPVertex * vsrc[3]; + f32 maxY = 0.0f; + const bool needResterise = (depthBufferList().getCurrent() != nullptr && + config.frameBufferEmulation.copyDepthToRDRAM == Config::cdSoftwareRender && + gDP.otherMode.depthUpdate != 0); -static -u32 clipW(const SPVertex ** _vsrc, SPVertex * _vdst) -{ - u32 dsti = 0; - for (int n = 0; n < 3; ++n) { - const SPVertex * src1 = _vsrc[n]; // current vertex - const SPVertex * src2 = _vsrc[n + 1]; // next vertex - if (src1->w >= 0.01f) { - copyVertex(_vdst[dsti++], src1); // add visible vertex to list - if (src2->w >= 0.01f) - continue; - } else if (src2->w < 0.01f) - continue; - float a = (-src1->w) / (src2->w - src1->w); - float ima = 1.0f - a; - // create new vertex - _vdst[dsti].x = src1->x*ima + src2->x*a; - _vdst[dsti].y = src1->y*ima + src2->y*a; - _vdst[dsti].z = src1->z*ima + src2->z*a; - _vdst[dsti].w = 0.01f; - _vdst[dsti].modify = 0; - dsti++; + for (u32 i = 0; i < _numElements; i += 3) { + for (u32 j = 0; j < 3; ++j) { + vsrc[j] = &_pVertices[i + j]; + } + + if (isClockwise(vsrc)) { + for (int k = 0; k < 3; ++k) { + maxY = std::max(maxY, vsrc[k]->y); + vdraw[k].x = floatToFixed16(vsrc[k]->x); + vdraw[k].y = floatToFixed16(vsrc[k]->y); + vdraw[k].z = floatToFixed16(vsrc[k]->z); + } + } else { + for (int k = 0; k < 3; ++k) { + const u32 idx = 3 - k - 1; + maxY = std::max(maxY, vsrc[idx]->y); + vdraw[k].x = floatToFixed16(vsrc[idx]->x); + vdraw[k].y = floatToFixed16(vsrc[idx]->y); + vdraw[k].z = floatToFixed16(vsrc[idx]->z); + } + } + + //Current depth buffer can be null if we are loading from a save state + if (needResterise) { + const int dzdx = calcDzDx(vsrc); + Rasterize(vdraw, 3, dzdx); + } } - return dsti; + return maxY; } f32 renderTriangles(const SPVertex * _pVertices, const u16 * _pElements, u32 _numElements) @@ -174,6 +261,11 @@ f32 renderTriangles(const SPVertex * _pVertices, const u16 * _pElements, u32 _nu const SPVertex * vsrc[4]; SPVertex vdata[6]; f32 maxY = 0.0f; + //Current depth buffer can be null if we are loading from a save state + const bool needResterise = (depthBufferList().getCurrent() != nullptr && + config.frameBufferEmulation.copyDepthToRDRAM == Config::cdSoftwareRender && + gDP.otherMode.depthUpdate != 0); + for (u32 i = 0; i < _numElements; i += 3) { u32 orbits = 0; if (_pElements != nullptr) { @@ -191,12 +283,11 @@ f32 renderTriangles(const SPVertex * _pVertices, const u16 * _pElements, u32 _nu u32 numVertex = clipW(vsrc, vdata); + const bool wClipped = (orbits & CLIP_W) != 0; bool clockwise = true; - if (!calcScreenCoordinates(vdata, vclip, numVertex, clockwise)) + if (!calcScreenCoordinates(vdata, vclip, numVertex, wClipped, clockwise)) continue; - const int dzdx = ((orbits & CLIP_W) == 0) ? calcDzDx(vclip) : calcDzDx2(vsrc); - if (orbits == 0) { assert(numVertex == 3); if (clockwise) { @@ -217,7 +308,7 @@ f32 renderTriangles(const SPVertex * _pVertices, const u16 * _pElements, u32 _nu } } else { vertexclip ** vtx; - numVertex = ClipPolygon(&vtx, vclip, numVertex); + numVertex = clipInScreenSpace(&vtx, vclip, numVertex); if (numVertex < 3) continue; @@ -239,11 +330,108 @@ f32 renderTriangles(const SPVertex * _pVertices, const u16 * _pElements, u32 _nu } } - //Current depth buffer can be null if we are loading from a save state - if (depthBufferList().getCurrent() != nullptr && - config.frameBufferEmulation.copyDepthToRDRAM == Config::cdSoftwareRender && - gDP.otherMode.depthUpdate != 0) + if (needResterise) { + const int dzdx = !wClipped ? calcDzDx(vclip) : calcDzDx2(vsrc); Rasterize(vdraw, numVertex, dzdx); + } } return maxY; } + +f32 renderAndDrawTriangles(const SPVertex *_pVertices, const u16 *_pElements, u32 _numElements, bool _flatColors) +{ + f32 maxY = 0.0f; + std::vector vResult; + vResult.reserve(_numElements * 3); + + //Current depth buffer can be null if we are loading from a save state + const bool needResterise = (depthBufferList().getCurrent() != nullptr && + config.frameBufferEmulation.copyDepthToRDRAM == Config::cdSoftwareRender && + gDP.otherMode.depthUpdate != 0); + + auto rasterise = [&maxY](const vertexclip* pVtxClip, bool clockwise) + { + int dzdx = calcDzDx(pVtxClip); + vertexi vdraw[3]; + for (u32 k = 0; k < 3; ++k) { + for (u32 k = 0; k < 3; ++k) { + const u32 idx = clockwise ? k : 3 - k - 1; + maxY = std::max(maxY, pVtxClip[idx].y); + vdraw[k].x = floatToFixed16(pVtxClip[idx].x); + vdraw[k].y = floatToFixed16(pVtxClip[idx].y); + vdraw[k].z = floatToFixed16(pVtxClip[idx].z); + } + } + Rasterize(vdraw, 3, dzdx); + }; + + for (u32 i = 0; i < _numElements; i += 3) { + u32 orbits = 0; + u32 modify = 0; + const SPVertex* vsrc[3]; + for (u32 j = 0; j < 3; ++j) { + vsrc[j] = _pElements != nullptr ? &_pVertices[_pElements[i + j]] : &_pVertices[i + j]; + orbits |= vsrc[j]->clip; + modify |= vsrc[j]->modify & (MODIFY_XY | MODIFY_Z); + } + + if (orbits == 0) { + // No clipping is necessary. + vertexclip vclip[3]; + bool clockwise = true; + if (!calcScreenCoordinates(vsrc, vclip, 3, clockwise)) + // Culled + continue; + + // Copy vertices to result + for (u32 k = 0; k < 3; ++k) { + vResult.push_back(*vsrc[k]); + SPVertex& v = vResult.back(); + v.bc0 = (k % 3 == 0) ? 1.0f : 0.0f; + v.bc1 = (k % 3 == 1) ? 1.0f : 0.0f; + } + + if (needResterise) + rasterise(vclip, clockwise); + } else { + assert(modify == 0); + // No screen space coordinates, so use clipping in homogeneous space. + SPVertex vCopy[3]; + for (u32 k = 0; k < 3; ++k) { + SPVertex& v = vCopy[k]; + v = *vsrc[k]; + v.bc0 = (k % 3 == 0) ? 1.0f : 0.0f; + v.bc1 = (k % 3 == 1) ? 1.0f : 0.0f; + } + auto prevNumVtx = vResult.size(); + clipInHomogeneousSpace(vCopy, vResult); + const u32 numVertex = vResult.size() - prevNumVtx; + if (!needResterise || numVertex == 0) + continue; + + std::vector vclip(numVertex); + const bool wClipped = (orbits & CLIP_W) != 0; + bool clockwise = true; + if (!calcScreenCoordinates(vResult.data() + prevNumVtx, vclip.data(), numVertex, wClipped, clockwise)) { + vResult.resize(prevNumVtx); + continue; + } + + for (u32 l = 0; l < numVertex; l += 3) + rasterise(vclip.data() + l, clockwise); + } + } + + if (!vResult.empty()) { + vResult[0].HWLight = _pVertices[0].HWLight; + graphics::Context::DrawTriangleParameters triParams; + triParams.mode = graphics::drawmode::TRIANGLES; + triParams.flatColors = _flatColors; + triParams.combiner = currentCombiner(); + triParams.verticesCount = static_cast(vResult.size()); + triParams.vertices = vResult.data(); + gfxContext.drawTriangles(triParams); + } + + return maxY; +} diff --git a/GLideN64/src/SoftwareRender.h b/GLideN64/src/SoftwareRender.h index aaad968..518e98c 100644 --- a/GLideN64/src/SoftwareRender.h +++ b/GLideN64/src/SoftwareRender.h @@ -2,7 +2,22 @@ #define SOFTWARE_RENDER_H #include "gSP.h" +#include "Graphics/Context.h" -f32 renderTriangles(const SPVertex * _pVertices, const u16 * _pElements, u32 _numElements); +/* Software render triangles to N64 depth buffer +* Coordinates of vertices must be in screen space. +* No coordinate clipping applied. +*/ +f32 renderScreenSpaceTriangles(const SPVertex *_pVertices, u32 _numElements); + +/* Software render triangles to N64 depth buffer +* Coordinates of vertices can be in screen space or in homogeneous space +*/ +f32 renderTriangles(const SPVertex *_pVertices, const u16 *_pElements, u32 _numElements); + +/* Software render triangles to N64 depth buffer and draw them with GPU +* Software clipping is used before rendering and drawing. +*/ +f32 renderAndDrawTriangles(const SPVertex *_pVertices, const u16 *_pElements, u32 _numElements, bool _flatColors); #endif // SOFTWARE_RENDER_H diff --git a/GLideN64/src/gDP.cpp b/GLideN64/src/gDP.cpp index 7d29833..8ee1d84 100644 --- a/GLideN64/src/gDP.cpp +++ b/GLideN64/src/gDP.cpp @@ -850,8 +850,9 @@ void gDPTextureRectangle(f32 ulx, f32 uly, f32 lrx, f32 lry, s32 tile, s16 s, s1 dsdx /= 4.0f; lrx += 1.0f; lry += 1.0f; + } else if (lry - uly < 1.0f) { + lry = ceil(lry); } - lry = ceil(lry); gDPTile *textureTileOrg[2]; textureTileOrg[0] = gSP.textureTile[0]; diff --git a/GLideN64/src/gSP.cpp b/GLideN64/src/gSP.cpp index f2a202e..e9cf9d3 100644 --- a/GLideN64/src/gSP.cpp +++ b/GLideN64/src/gSP.cpp @@ -2077,8 +2077,8 @@ void gSPSprite2DBase(u32 _base) f32 uls = pSprite->imageX; f32 ult = pSprite->imageY; - f32 lrs = uls + pSprite->imageW - 1; - f32 lrt = ult + pSprite->imageH - 1; + f32 lrs = uls + pSprite->imageW; + f32 lrt = ult + pSprite->imageH; // Hack for WCW Nitro. if ((config.generalEmulation.hacks & hack_WCWNitro) != 0) { diff --git a/GLideN64/src/mupenplus/Config_mupenplus.cpp b/GLideN64/src/mupenplus/Config_mupenplus.cpp index 0227922..40f222f 100644 --- a/GLideN64/src/mupenplus/Config_mupenplus.cpp +++ b/GLideN64/src/mupenplus/Config_mupenplus.cpp @@ -151,6 +151,8 @@ bool Config_SetDefault() assert(res == M64ERR_SUCCESS); res = ConfigSetDefaultBool(g_configVideoGliden64, "EnableCoverage", config.generalEmulation.enableCoverage, "Enable pixel coverage calculation. Used for better blending emulation and wire-frame mode. Needs fast GPU."); assert(res == M64ERR_SUCCESS); + res = ConfigSetDefaultBool(g_configVideoGliden64, "EnableClipping", config.generalEmulation.enableClipping, "Enable software vertices clipping. Brings various benefits."); + assert(res == M64ERR_SUCCESS); res = ConfigSetDefaultBool(g_configVideoGliden64, "EnableShadersStorage", config.generalEmulation.enableShadersStorage, "Use persistent storage for compiled shaders."); assert(res == M64ERR_SUCCESS); res = ConfigSetDefaultBool(g_configVideoGliden64, "EnableLegacyBlending", config.generalEmulation.enableLegacyBlending, "Do not use shaders to emulate N64 blending modes. Works faster on slow GPU. Can cause glitches."); @@ -364,6 +366,8 @@ void Config_LoadCustomConfig() if (result == M64ERR_SUCCESS) config.generalEmulation.enableHWLighting = atoi(value); result = ConfigExternalGetParameter(fileHandle, sectionName, "generalEmulation\\enableCoverage", value, sizeof(value)); if (result == M64ERR_SUCCESS) config.generalEmulation.enableCoverage = atoi(value); + result = ConfigExternalGetParameter(fileHandle, sectionName, "generalEmulation\\enableClipping", value, sizeof(value)); + if (result == M64ERR_SUCCESS) config.generalEmulation.enableClipping = atoi(value); result = ConfigExternalGetParameter(fileHandle, sectionName, "generalEmulation\\enableShadersStorage", value, sizeof(value)); if (result == M64ERR_SUCCESS) config.generalEmulation.enableShadersStorage = atoi(value); result = ConfigExternalGetParameter(fileHandle, sectionName, "generalEmulation\\enableLegacyBlending", value, sizeof(value)); @@ -488,6 +492,7 @@ void Config_LoadConfig() config.generalEmulation.enableLOD = ConfigGetParamBool(g_configVideoGliden64, "EnableLOD"); config.generalEmulation.enableHWLighting = ConfigGetParamBool(g_configVideoGliden64, "EnableHWLighting"); config.generalEmulation.enableCoverage = ConfigGetParamBool(g_configVideoGliden64, "EnableCoverage"); + config.generalEmulation.enableClipping = ConfigGetParamBool(g_configVideoGliden64, "enableClipping"); config.generalEmulation.enableShadersStorage = ConfigGetParamBool(g_configVideoGliden64, "EnableShadersStorage"); config.generalEmulation.enableLegacyBlending = ConfigGetParamBool(g_configVideoGliden64, "EnableLegacyBlending"); config.generalEmulation.enableHybridFilter = ConfigGetParamBool(g_configVideoGliden64, "EnableHybridFilter");