From e1969c470ba1aa0fa0b2bdfdc4aa12e6d9e80d7c Mon Sep 17 00:00:00 2001 From: rogerman Date: Sun, 19 Feb 2023 20:23:44 -0800 Subject: [PATCH] GFX3D: Vertex post-processing has been moved from the 3D renderers to GFX3D. - Specifically, viewport transformation, face calculation, and face culling are now handled in GFX3D, and are now standard behaviors for all 3D renderers. This reorganization makes more sense since the 3D renderers are primarily responsible for rasterization and framebuffer post-processing, rather than for processing geometry. - As a positive side-effect, the OpenGL renderer gains a small performance improvement as well as better accuracy in face culling. --- desmume/src/OGLRender.cpp | 102 +++++++-------------------- desmume/src/OGLRender.h | 1 - desmume/src/OGLRender_3_2.cpp | 39 +--------- desmume/src/gfx3d.cpp | 109 +++++++++++++++++++++++++++- desmume/src/gfx3d.h | 1 + desmume/src/rasterize.cpp | 129 +--------------------------------- desmume/src/rasterize.h | 5 -- 7 files changed, 138 insertions(+), 248 deletions(-) diff --git a/desmume/src/OGLRender.cpp b/desmume/src/OGLRender.cpp index 2c4d21cf0..694ff77c0 100755 --- a/desmume/src/OGLRender.cpp +++ b/desmume/src/OGLRender.cpp @@ -1774,13 +1774,14 @@ size_t OpenGLRenderer::DrawPolygonsForIndexRange(const POLY *rawPolyList, const }; // Set up the initial polygon - const POLY &initialPoly = rawPolyList[clippedPolyList[firstIndex].index]; - TEXIMAGE_PARAM lastTexParams = initialPoly.texParam; - u32 lastTexPalette = initialPoly.texPalette; - GFX3D_Viewport lastViewport = initialPoly.viewport; + const CPoly &initialClippedPoly = clippedPolyList[firstIndex]; + const POLY &initialRawPoly = rawPolyList[initialClippedPoly.index]; + TEXIMAGE_PARAM lastTexParams = initialRawPoly.texParam; + u32 lastTexPalette = initialRawPoly.texPalette; + GFX3D_Viewport lastViewport = initialRawPoly.viewport; - this->SetupTexture(initialPoly, firstIndex); - this->SetupViewport(initialPoly.viewport); + this->SetupTexture(initialRawPoly, firstIndex); + this->SetupViewport(initialRawPoly.viewport); // Enumerate through all polygons and render GLsizei vertIndexCount = 0; @@ -1788,7 +1789,8 @@ size_t OpenGLRenderer::DrawPolygonsForIndexRange(const POLY *rawPolyList, const for (size_t i = firstIndex; i <= lastIndex; i++) { - const POLY &rawPoly = rawPolyList[clippedPolyList[i].index]; + const CPoly &clippedPoly = clippedPolyList[i]; + const POLY &rawPoly = rawPolyList[clippedPoly.index]; // Set up the polygon if it changed if (lastPolyAttr.value != rawPoly.attribute.value) @@ -1828,18 +1830,19 @@ size_t OpenGLRenderer::DrawPolygonsForIndexRange(const POLY *rawPolyList, const // the same and we're not drawing a line loop or line strip. if (i+1 <= lastIndex) { - const POLY &nextPoly = rawPolyList[clippedPolyList[i+1].index]; + const CPoly &nextClippedPoly = clippedPolyList[i+1]; + const POLY &nextRawPoly = rawPolyList[nextClippedPoly.index]; - if (lastPolyAttr.value == nextPoly.attribute.value && - lastTexParams.value == nextPoly.texParam.value && - lastTexPalette == nextPoly.texPalette && - lastViewport.value == nextPoly.viewport.value && - polyPrimitive == oglPrimitiveType[nextPoly.vtxFormat] && + if (lastPolyAttr.value == nextRawPoly.attribute.value && + lastTexParams.value == nextRawPoly.texParam.value && + lastTexPalette == nextRawPoly.texPalette && + lastViewport.value == nextRawPoly.viewport.value && + polyPrimitive == oglPrimitiveType[nextRawPoly.vtxFormat] && polyPrimitive != GL_LINE_LOOP && polyPrimitive != GL_LINE_STRIP && - oglPrimitiveType[nextPoly.vtxFormat] != GL_LINE_LOOP && - oglPrimitiveType[nextPoly.vtxFormat] != GL_LINE_STRIP && - this->_isPolyFrontFacing[i] == this->_isPolyFrontFacing[i+1]) + oglPrimitiveType[nextRawPoly.vtxFormat] != GL_LINE_LOOP && + oglPrimitiveType[nextRawPoly.vtxFormat] != GL_LINE_STRIP && + clippedPoly.isPolyBackFacing == nextClippedPoly.isPolyBackFacing) { continue; } @@ -1869,7 +1872,7 @@ size_t OpenGLRenderer::DrawPolygonsForIndexRange(const POLY *rawPolyList, const rawPoly.attribute.TranslucentDepthWrite_Enable, GFX3D_IsPolyWireframe(rawPoly) || GFX3D_IsPolyOpaque(rawPoly), rawPoly.attribute.PolygonID, - this->_isPolyFrontFacing[i]); + !clippedPoly.isPolyBackFacing); } else { @@ -1879,7 +1882,7 @@ size_t OpenGLRenderer::DrawPolygonsForIndexRange(const POLY *rawPolyList, const rawPoly.attribute.DepthEqualTest_Enable, rawPoly.attribute.TranslucentDepthWrite_Enable, rawPoly.attribute.PolygonID, - this->_isPolyFrontFacing[i]); + !clippedPoly.isPolyBackFacing); } indexBufferPtr += vertIndexCount; @@ -3912,7 +3915,6 @@ Render3DError OpenGLRenderer_1_2::ZeroDstAlphaPass(const POLY *rawPolyList, cons glDisable(GL_BLEND); glEnable(GL_STENCIL_TEST); glDisable(GL_DEPTH_TEST); - glDisable(GL_CULL_FACE); glStencilFunc(GL_ALWAYS, 0x40, 0x40); glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); @@ -4097,7 +4099,6 @@ Render3DError OpenGLRenderer_1_2::ReadBackPixels() glDisable(GL_DEPTH_TEST); glDisable(GL_STENCIL_TEST); glDisable(GL_BLEND); - glDisable(GL_CULL_FACE); glBindBuffer(GL_ARRAY_BUFFER, OGLRef.vboPostprocessVtxID); @@ -4199,15 +4200,9 @@ Render3DError OpenGLRenderer_1_2::BeginRender(const GFX3D_State &renderState, co for (size_t i = 0, vertIndexCount = 0; i < this->_clippedPolyCount; i++) { - const POLY &rawPoly = this->_rawPolyList[this->_clippedPolyList[i].index]; - + const CPoly &cPoly = this->_clippedPolyList[i]; + const POLY &rawPoly = this->_rawPolyList[cPoly.index]; const size_t polyType = rawPoly.type; - const VERT vert[4] = { - renderGList.rawVertList[rawPoly.vertIndexes[0]], - renderGList.rawVertList[rawPoly.vertIndexes[1]], - renderGList.rawVertList[rawPoly.vertIndexes[2]], - renderGList.rawVertList[rawPoly.vertIndexes[3]] - }; if (this->isShaderSupported) { @@ -4269,19 +4264,7 @@ Render3DError OpenGLRenderer_1_2::BeginRender(const GFX3D_State &renderState, co } } - // Get this polygon's facing. - const size_t n = polyType - 1; - float facing = (vert[0].y + vert[n].y) * (vert[0].x - vert[n].x) + - (vert[1].y + vert[0].y) * (vert[1].x - vert[0].x) + - (vert[2].y + vert[1].y) * (vert[2].x - vert[1].x); - - for (size_t j = 2; j < n; j++) - { - facing += (vert[j+1].y + vert[j].y) * (vert[j+1].x - vert[j].x); - } - renderNeedsToonTable = (renderNeedsToonTable || (rawPoly.attribute.Mode == POLYGON_MODE_TOONHIGHLIGHT)) && this->isShaderSupported; - this->_isPolyFrontFacing[i] = (facing < 0); // Get the texture that is to be attached to this polygon. this->_textureList[i] = this->GetLoadedTextureFromPolygon(rawPoly, this->_enableTextureSampling); @@ -4394,6 +4377,7 @@ Render3DError OpenGLRenderer_1_2::RenderGeometry() { if (this->_clippedPolyCount > 0) { + glDisable(GL_CULL_FACE); // Polygons should already be culled before we get here. glEnable(GL_DEPTH_TEST); glEnable(GL_STENCIL_TEST); @@ -4488,7 +4472,6 @@ Render3DError OpenGLRenderer_1_2::PostprocessFramebuffer() // Set up the postprocessing states glViewport(0, 0, (GLsizei)this->_framebufferWidth, (GLsizei)this->_framebufferHeight); glDisable(GL_DEPTH_TEST); - glDisable(GL_CULL_FACE); glBindBuffer(GL_ARRAY_BUFFER, OGLRef.vboPostprocessVtxID); @@ -4803,20 +4786,6 @@ Render3DError OpenGLRenderer_1_2::SetupPolygon(const POLY &thePoly, bool treatAs // Set up depth test mode glDepthFunc((thePoly.attribute.DepthEqualTest_Enable) ? GL_EQUAL : GL_LESS); - // Set up culling mode - static const GLenum oglCullingMode[4] = {GL_FRONT_AND_BACK, GL_FRONT, GL_BACK, 0}; - GLenum cullingMode = oglCullingMode[thePoly.attribute.SurfaceCullingMode]; - - if (cullingMode == 0) - { - glDisable(GL_CULL_FACE); - } - else - { - glEnable(GL_CULL_FACE); - glCullFace(cullingMode); - } - if (willChangeStencilBuffer) { // Handle drawing states for the polygon @@ -5162,7 +5131,6 @@ Render3DError OpenGLRenderer_1_2::Reset() OGLRef.vtxPtrColor = (this->isShaderSupported) ? (GLvoid *)offsetof(VERT, color) : OGLRef.color4fBuffer; memset(&this->_pendingRenderStates, 0, sizeof(this->_pendingRenderStates)); - memset(this->_isPolyFrontFacing, 0, sizeof(this->_isPolyFrontFacing)); texCache.Reset(); @@ -5472,15 +5440,9 @@ Render3DError OpenGLRenderer_2_0::BeginRender(const GFX3D_State &renderState, co for (size_t i = 0, vertIndexCount = 0; i < this->_clippedPolyCount; i++) { - const POLY &rawPoly = this->_rawPolyList[this->_clippedPolyList[i].index]; - + const CPoly &cPoly = this->_clippedPolyList[i]; + const POLY &rawPoly = this->_rawPolyList[cPoly.index]; const size_t polyType = rawPoly.type; - const VERT vert[4] = { - renderGList.rawVertList[rawPoly.vertIndexes[0]], - renderGList.rawVertList[rawPoly.vertIndexes[1]], - renderGList.rawVertList[rawPoly.vertIndexes[2]], - renderGList.rawVertList[rawPoly.vertIndexes[3]] - }; for (size_t j = 0; j < polyType; j++) { @@ -5504,19 +5466,7 @@ Render3DError OpenGLRenderer_2_0::BeginRender(const GFX3D_State &renderState, co } } - // Get this polygon's facing. - const size_t n = polyType - 1; - float facing = (vert[0].y + vert[n].y) * (vert[0].x - vert[n].x) + - (vert[1].y + vert[0].y) * (vert[1].x - vert[0].x) + - (vert[2].y + vert[1].y) * (vert[2].x - vert[1].x); - - for (size_t j = 2; j < n; j++) - { - facing += (vert[j+1].y + vert[j].y) * (vert[j+1].x - vert[j].x); - } - renderNeedsToonTable = renderNeedsToonTable || (rawPoly.attribute.Mode == POLYGON_MODE_TOONHIGHLIGHT); - this->_isPolyFrontFacing[i] = (facing < 0); // Get the texture that is to be attached to this polygon. this->_textureList[i] = this->GetLoadedTextureFromPolygon(rawPoly, this->_enableTextureSampling); diff --git a/desmume/src/OGLRender.h b/desmume/src/OGLRender.h index 4aa64a3e5..6a2079787 100755 --- a/desmume/src/OGLRender.h +++ b/desmume/src/OGLRender.h @@ -736,7 +736,6 @@ protected: bool _enableMultisampledRendering; int _selectedMultisampleSize; - bool _isPolyFrontFacing[CLIPPED_POLYLIST_SIZE]; size_t _clearImageIndex; Render3DError FlushFramebuffer(const FragmentColor *__restrict srcFramebuffer, FragmentColor *__restrict dstFramebufferMain, u16 *__restrict dstFramebuffer16); diff --git a/desmume/src/OGLRender_3_2.cpp b/desmume/src/OGLRender_3_2.cpp index 49f0c7cc7..f67746a7b 100644 --- a/desmume/src/OGLRender_3_2.cpp +++ b/desmume/src/OGLRender_3_2.cpp @@ -1788,7 +1788,6 @@ Render3DError OpenGLRenderer_3_2::ZeroDstAlphaPass(const POLY *rawPolyList, cons glDisable(GL_BLEND); glEnable(GL_STENCIL_TEST); glDisable(GL_DEPTH_TEST); - glDisable(GL_CULL_FACE); glStencilFunc(GL_ALWAYS, 0x40, 0x40); glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); @@ -1926,7 +1925,6 @@ Render3DError OpenGLRenderer_3_2::ReadBackPixels() glDisable(GL_DEPTH_TEST); glDisable(GL_STENCIL_TEST); glDisable(GL_BLEND); - glDisable(GL_CULL_FACE); glBindBuffer(GL_ARRAY_BUFFER, OGLRef.vboPostprocessVtxID); glBindVertexArray(OGLRef.vaoPostprocessStatesID); @@ -2023,15 +2021,9 @@ Render3DError OpenGLRenderer_3_2::BeginRender(const GFX3D_State &renderState, co for (size_t i = 0, vertIndexCount = 0; i < this->_clippedPolyCount; i++) { - const POLY &rawPoly = this->_rawPolyList[this->_clippedPolyList[i].index]; - + const CPoly &cPoly = this->_clippedPolyList[i]; + const POLY &rawPoly = this->_rawPolyList[cPoly.index]; const size_t polyType = rawPoly.type; - const VERT vert[4] = { - renderGList.rawVertList[rawPoly.vertIndexes[0]], - renderGList.rawVertList[rawPoly.vertIndexes[1]], - renderGList.rawVertList[rawPoly.vertIndexes[2]], - renderGList.rawVertList[rawPoly.vertIndexes[3]] - }; for (size_t j = 0; j < polyType; j++) { @@ -2055,19 +2047,7 @@ Render3DError OpenGLRenderer_3_2::BeginRender(const GFX3D_State &renderState, co } } - // Get the polygon's facing. - const size_t n = polyType - 1; - float facing = (vert[0].y + vert[n].y) * (vert[0].x - vert[n].x) + - (vert[1].y + vert[0].y) * (vert[1].x - vert[0].x) + - (vert[2].y + vert[1].y) * (vert[2].x - vert[1].x); - - for (size_t j = 2; j < n; j++) - { - facing += (vert[j+1].y + vert[j].y) * (vert[j+1].x - vert[j].x); - } - renderNeedsToonTable = renderNeedsToonTable || (rawPoly.attribute.Mode == POLYGON_MODE_TOONHIGHLIGHT); - this->_isPolyFrontFacing[i] = (facing < 0); // Get the texture that is to be attached to this polygon. this->_textureList[i] = this->GetLoadedTextureFromPolygon(rawPoly, this->_enableTextureSampling); @@ -2206,7 +2186,6 @@ Render3DError OpenGLRenderer_3_2::PostprocessFramebuffer() // Set up the postprocessing states glViewport(0, 0, (GLsizei)this->_framebufferWidth, (GLsizei)this->_framebufferHeight); glDisable(GL_DEPTH_TEST); - glDisable(GL_CULL_FACE); glBindBuffer(GL_ARRAY_BUFFER, OGLRef.vboPostprocessVtxID); glBindVertexArray(OGLRef.vaoPostprocessStatesID); @@ -2430,20 +2409,6 @@ Render3DError OpenGLRenderer_3_2::SetupPolygon(const POLY &thePoly, bool treatAs glDepthFunc((thePoly.attribute.DepthEqualTest_Enable) ? GL_EQUAL : GL_LESS); glUniform1f(OGLRef.uniformPolyDepthOffset[this->_geometryProgramFlags.value], 0.0f); - // Set up culling mode - static const GLenum oglCullingMode[4] = {GL_FRONT_AND_BACK, GL_FRONT, GL_BACK, 0}; - GLenum cullingMode = oglCullingMode[thePoly.attribute.SurfaceCullingMode]; - - if (cullingMode == 0) - { - glDisable(GL_CULL_FACE); - } - else - { - glEnable(GL_CULL_FACE); - glCullFace(cullingMode); - } - if (willChangeStencilBuffer) { // Handle drawing states for the polygon diff --git a/desmume/src/gfx3d.cpp b/desmume/src/gfx3d.cpp index 9d81a2013..ff752eca2 100644 --- a/desmume/src/gfx3d.cpp +++ b/desmume/src/gfx3d.cpp @@ -3257,12 +3257,20 @@ static bool gfx3d_ysort_compare(const u16 idx1, const u16 idx2) return (idx1 < idx2); } +static FORCEINLINE s32 iround(const float f) +{ + return (s32)f; //lol +} + template size_t gfx3d_PerformClipping(const GFX3D_GeometryList &gList, CPoly *outCPolyUnsortedList) { size_t clipCount = 0; PolygonType cpType = POLYGON_TYPE_UNDEFINED; + const float wScalar = (float)CurrentRenderer->GetFramebufferWidth() / (float)GPU_FRAMEBUFFER_NATIVE_WIDTH; + const float hScalar = (float)CurrentRenderer->GetFramebufferHeight() / (float)GPU_FRAMEBUFFER_NATIVE_HEIGHT; + for (size_t polyIndex = 0; polyIndex < gList.rawPolyCount; polyIndex++) { const POLY &rawPoly = gList.rawPolyList[polyIndex]; @@ -3274,11 +3282,106 @@ size_t gfx3d_PerformClipping(const GFX3D_GeometryList &gList, CPoly *outCPolyUns (rawPoly.type == POLYGON_TYPE_QUAD) ? &gList.rawVertList[rawPoly.vertIndexes[3]] : NULL }; - cpType = GFX3D_GenerateClippedPoly(polyIndex, rawPoly.type, rawVerts, outCPolyUnsortedList[clipCount]); - if (cpType != POLYGON_TYPE_UNDEFINED) + CPoly &cPoly = outCPolyUnsortedList[clipCount]; + + cpType = GFX3D_GenerateClippedPoly(polyIndex, rawPoly.type, rawVerts, cPoly); + if (cpType == POLYGON_TYPE_UNDEFINED) { - clipCount++; + continue; } + + for (size_t j = 0; j < (size_t)cPoly.type; j++) + { + VERT &vtx = cPoly.clipVerts[j]; + + // TODO: Possible divide by zero with the w-coordinate. + // Is the vertex being read correctly? Is 0 a valid value for w? + // If both of these questions answer to yes, then how does the NDS handle a NaN? + // For now, simply prevent w from being zero. + // + // Test case: Dance scenes in Princess Debut can generate undefined vertices + // when the -ffast-math option (relaxed IEEE754 compliance) is used. + const float vtxW = (vtx.coord[3] != 0.0f) ? vtx.coord[3] : 0.00000001f; + + //homogeneous divide + vtx.coord[0] = (vtx.coord[0]+vtxW) / (2*vtxW); + vtx.coord[1] = (vtx.coord[1]+vtxW) / (2*vtxW); + vtx.coord[2] = (vtx.coord[2]+vtxW) / (2*vtxW); + + //CONSIDER: do we need to guarantee that these are in bounds? perhaps not. + //vtx.coord[0] = max( 0.0f, min(1.0f, vtx.coord[0]) ); + //vtx.coord[1] = max( 0.0f, min(1.0f, vtx.coord[1]) ); + //vtx.coord[2] = max( 0.0f, min(1.0f, vtx.coord[2]) ); + + //viewport transformation + const GFX3D_Viewport theViewport = rawPoly.viewport; + vtx.coord[0] *= (float)theViewport.width; + vtx.coord[0] += (float)theViewport.x; + vtx.coord[0] *= wScalar; + + vtx.coord[1] *= (float)theViewport.height; + vtx.coord[1] += (float)theViewport.y; + vtx.coord[1] = 192 - vtx.coord[1]; + vtx.coord[1] *= hScalar; + + //here is a hack which needs to be removed. + //at some point our shape engine needs these to be converted to "fixed point" + //which is currently just a float + vtx.coord[0] = (float)iround(16.0f * vtx.coord[0]); + vtx.coord[1] = (float)iround(16.0f * vtx.coord[1]); + + if (CLIPPERMODE != ClipperMode_DetermineClipOnly) + { + vtx.texcoord[0] /= vtxW; + vtx.texcoord[1] /= vtxW; + + //perspective-correct the colors + vtx.fcolor[0] /= vtxW; + vtx.fcolor[1] /= vtxW; + vtx.fcolor[2] /= vtxW; + } + } + + // Perform face culling. + + //an older approach + //(not good enough for quads and other shapes) + //float ab[2], ac[2]; Vector2Copy(ab, verts[1].coord); Vector2Copy(ac, verts[2].coord); Vector2Subtract(ab, verts[0].coord); + //Vector2Subtract(ac, verts[0].coord); float cross = Vector2Cross(ab, ac); polyAttr.backfacing = (cross>0); + + //a better approach + // we have to support somewhat non-convex polygons (see NSMB world map 1st screen). + // this version should handle those cases better. + + const VERT *vtx = cPoly.clipVerts; + const size_t n = cPoly.type - 1; + float facing = (vtx[0].y + vtx[n].y) * (vtx[0].x - vtx[n].x) + + (vtx[1].y + vtx[0].y) * (vtx[1].x - vtx[0].x) + + (vtx[2].y + vtx[1].y) * (vtx[2].x - vtx[1].x); + + for (size_t j = 2; j < n; j++) + { + facing += (vtx[j+1].y + vtx[j].y) * (vtx[j+1].x - vtx[j].x); + } + + cPoly.isPolyBackFacing = (facing < 0); + + static const bool visibleFunction[2][4] = { + //always false, backfacing, !backfacing, always true + { false, false, true, true }, + { false, true, false, true } + }; + + const u8 cullingMode = rawPoly.attribute.SurfaceCullingMode; + const bool isPolyVisible = visibleFunction[(cPoly.isPolyBackFacing) ? 1 : 0][cullingMode]; + if (!isPolyVisible) + { + // If the polygon is to be culled, then don't count it. + continue; + } + + // Incrementing the count will keep the polygon in the list. + clipCount++; } return clipCount; diff --git a/desmume/src/gfx3d.h b/desmume/src/gfx3d.h index 468d23dab..619d027bf 100644 --- a/desmume/src/gfx3d.h +++ b/desmume/src/gfx3d.h @@ -748,6 +748,7 @@ struct CPoly { u16 index; // The index number of this polygon in the full polygon list. PolygonType type; //otherwise known as "count" of verts + bool isPolyBackFacing; VERT clipVerts[MAX_CLIPPED_VERTS]; NDSVertex clipVtxFixed[MAX_CLIPPED_VERTS]; }; diff --git a/desmume/src/rasterize.cpp b/desmume/src/rasterize.cpp index 5d076c2f1..fc3a34f63 100644 --- a/desmume/src/rasterize.cpp +++ b/desmume/src/rasterize.cpp @@ -543,7 +543,7 @@ FORCEINLINE void RasterizerUnit::_pixel(const POLYGON_ATTR polyAttr, c depthFail = true; } } - else if ( (ISFRONTFACING && (dstAttributePolyFacing == PolyFacing_Back)) && (dstColor.a == 0x1F)) + else if ( (ISFRONTFACING && (dstAttributePolyFacing == PolyFacing_Back)) && (dstColor.a == 0x1F) ) { // The LEQUAL test is used in the special case where an incoming front-facing polygon's pixel // is to be drawn on top of a back-facing polygon's opaque pixel. @@ -1289,8 +1289,7 @@ FORCEINLINE void RasterizerUnit::Render() for (size_t i = 0; i < clippedPolyCount; i++) { if (!RENDERER) _debug_thisPoly = (i == this->_softRender->_debug_drawClippedUserPoly); - if (!this->_softRender->isPolyVisible[i]) continue; - + const CPoly &clippedPoly = this->_softRender->GetClippedPolyByIndex(i); const POLY &rawPoly = rawPolyList[clippedPoly.index]; const size_t vertCount = (size_t)clippedPoly.type; @@ -1311,7 +1310,7 @@ FORCEINLINE void RasterizerUnit::Render() for (size_t j = vertCount; j < MAX_CLIPPED_VERTS; j++) this->_verts[j] = NULL; - if (!this->_softRender->isPolyBackFacing[i]) + if (!clippedPoly.isPolyBackFacing) { if (polyAttr.Mode == POLYGON_MODE_SHADOW) { @@ -1382,14 +1381,6 @@ void* SoftRasterizer_RunRasterizerUnit(void *arg) return 0; } -static void* SoftRasterizer_RunProcessAllVertices(void *arg) -{ - SoftRasterizerRenderer *softRender = (SoftRasterizerRenderer *)arg; - softRender->ProcessAllVertices(); - - return NULL; -} - static void* SoftRasterizer_RunGetAndLoadAllTextures(void *arg) { SoftRasterizerRenderer *softRender = (SoftRasterizerRenderer *)arg; @@ -1875,112 +1866,6 @@ ClipperMode SoftRasterizerRenderer::GetPreferredPolygonClippingMode() const return (this->_enableHighPrecisionColorInterpolation) ? ClipperMode_FullColorInterpolate : ClipperMode_Full; } -void SoftRasterizerRenderer::_TransformVertices() -{ - const POLY *rawPolyList = this->_rawPolyList; - const float wScalar = (float)this->_framebufferWidth / (float)GPU_FRAMEBUFFER_NATIVE_WIDTH; - const float hScalar = (float)this->_framebufferHeight / (float)GPU_FRAMEBUFFER_NATIVE_HEIGHT; - - //viewport transforms - for (size_t i = 0; i < this->_clippedPolyCount; i++) - { - CPoly &cPoly = this->_clippedPolyList[i]; - for (size_t j = 0; j < (size_t)cPoly.type; j++) - { - VERT &vert = cPoly.clipVerts[j]; - - // TODO: Possible divide by zero with the w-coordinate. - // Is the vertex being read correctly? Is 0 a valid value for w? - // If both of these questions answer to yes, then how does the NDS handle a NaN? - // For now, simply prevent w from being zero. - // - // Test case: Dance scenes in Princess Debut can generate undefined vertices - // when the -ffast-math option (relaxed IEEE754 compliance) is used. - const float vertw = (vert.coord[3] != 0.0f) ? vert.coord[3] : 0.00000001f; - - //homogeneous divide - vert.coord[0] = (vert.coord[0]+vertw) / (2*vertw); - vert.coord[1] = (vert.coord[1]+vertw) / (2*vertw); - vert.coord[2] = (vert.coord[2]+vertw) / (2*vertw); - vert.texcoord[0] /= vertw; - vert.texcoord[1] /= vertw; - - //CONSIDER: do we need to guarantee that these are in bounds? perhaps not. - //vert.coord[0] = max(0.0f,min(1.0f,vert.coord[0])); - //vert.coord[1] = max(0.0f,min(1.0f,vert.coord[1])); - //vert.coord[2] = max(0.0f,min(1.0f,vert.coord[2])); - - //perspective-correct the colors - vert.fcolor[0] /= vertw; - vert.fcolor[1] /= vertw; - vert.fcolor[2] /= vertw; - - //viewport transformation - const GFX3D_Viewport theViewport = rawPolyList[cPoly.index].viewport; - vert.coord[0] *= theViewport.width; - vert.coord[0] += theViewport.x; - vert.coord[0] *= wScalar; - - vert.coord[1] *= theViewport.height; - vert.coord[1] += theViewport.y; - vert.coord[1] = 192 - vert.coord[1]; - vert.coord[1] *= hScalar; - - //here is a hack which needs to be removed. - //at some point our shape engine needs these to be converted to "fixed point" - //which is currently just a float - vert.coord[0] = (float)iround(16.0f * vert.coord[0]); - vert.coord[1] = (float)iround(16.0f * vert.coord[1]); - } - } -} - -void SoftRasterizerRenderer::_GetPolygonStates() -{ - static const bool visibleFunction[2][4] = { - //always false, backfacing, !backfacing, always true - { false, false, true, true }, - { false, true, false, true } - }; - - const POLY *rawPolyList = this->_rawPolyList; - - for (size_t i = 0; i < this->_clippedPolyCount; i++) - { - const CPoly &clippedPoly = this->_clippedPolyList[i]; - const POLY &rawPoly = rawPolyList[clippedPoly.index]; - const PolygonType polyType = clippedPoly.type; - const VERT *vert = &clippedPoly.clipVerts[0]; - const u8 cullingMode = rawPoly.attribute.SurfaceCullingMode; - - //HACK: backface culling - //this should be moved to gfx3d, but first we need to redo the way the lists are built - //because it is too convoluted right now. - //(must we throw out verts if a poly gets backface culled? if not, then it might be easier) - - //an older approach - //(not good enough for quads and other shapes) - //float ab[2], ac[2]; Vector2Copy(ab, verts[1].coord); Vector2Copy(ac, verts[2].coord); Vector2Subtract(ab, verts[0].coord); - //Vector2Subtract(ac, verts[0].coord); float cross = Vector2Cross(ab, ac); polyAttr.backfacing = (cross>0); - - //a better approach - // we have to support somewhat non-convex polygons (see NSMB world map 1st screen). - // this version should handle those cases better. - const size_t n = polyType - 1; - float facing = (vert[0].y + vert[n].y) * (vert[0].x - vert[n].x) + - (vert[1].y + vert[0].y) * (vert[1].x - vert[0].x) + - (vert[2].y + vert[1].y) * (vert[2].x - vert[1].x); - - for (size_t j = 2; j < n; j++) - { - facing += (vert[j+1].y + vert[j].y) * (vert[j+1].x - vert[j].x); - } - - this->isPolyBackFacing[i] = (facing < 0); - this->isPolyVisible[i] = visibleFunction[this->isPolyBackFacing[i]][cullingMode]; - } -} - void SoftRasterizerRenderer::GetAndLoadAllTextures() { const POLY *rawPolyList = this->_rawPolyList; @@ -1998,12 +1883,6 @@ void SoftRasterizerRenderer::GetAndLoadAllTextures() } } -void SoftRasterizerRenderer::ProcessAllVertices() -{ - this->_TransformVertices(); - this->_GetPolygonStates(); -} - Render3DError SoftRasterizerRenderer::ApplyRenderingSettings(const GFX3D_State &renderState) { this->_enableHighPrecisionColorInterpolation = CommonSettings.GFX3D_HighResolutionInterpolateColor; @@ -2033,12 +1912,10 @@ Render3DError SoftRasterizerRenderer::BeginRender(const GFX3D_State &renderState if (doMultithreadedStateSetup) { this->_task[0].execute(&SoftRasterizer_RunGetAndLoadAllTextures, this); - this->_task[1].execute(&SoftRasterizer_RunProcessAllVertices, this); } else { this->GetAndLoadAllTextures(); - this->ProcessAllVertices(); } // Convert the toon table colors diff --git a/desmume/src/rasterize.h b/desmume/src/rasterize.h index 6f9c2685e..74f94275f 100644 --- a/desmume/src/rasterize.h +++ b/desmume/src/rasterize.h @@ -171,8 +171,6 @@ protected: // SoftRasterizer-specific methods void _UpdateEdgeMarkColorTable(const u16 *edgeMarkColorTable); void _UpdateFogTable(const u8 *fogDensityTable); - void _TransformVertices(); - void _GetPolygonStates(); // Base rendering methods virtual Render3DError BeginRender(const GFX3D_State &renderState, const GFX3D_GeometryList &renderGList); @@ -186,8 +184,6 @@ public: int _debug_drawClippedUserPoly; CACHE_ALIGN FragmentColor toonColor32LUT[32]; FragmentAttributesBuffer *_framebufferAttributes; - bool isPolyVisible[CLIPPED_POLYLIST_SIZE]; - bool isPolyBackFacing[CLIPPED_POLYLIST_SIZE]; GFX3D_State *currentRenderState; bool _enableFragmentSamplingHack; @@ -198,7 +194,6 @@ public: virtual ClipperMode GetPreferredPolygonClippingMode() const; void GetAndLoadAllTextures(); - void ProcessAllVertices(); Render3DError RenderEdgeMarkingAndFog(const SoftRasterizerPostProcessParams ¶m); SoftRasterizerTexture* GetLoadedTextureFromPolygon(const POLY &thePoly, bool enableTexturing);