diff --git a/desmume/src/OGLRender.cpp b/desmume/src/OGLRender.cpp index cbd5e8114..3c7c1b858 100755 --- a/desmume/src/OGLRender.cpp +++ b/desmume/src/OGLRender.cpp @@ -1487,6 +1487,186 @@ OpenGLTexture* OpenGLRenderer::GetLoadedTextureFromPolygon(const POLY &thePoly, return theTexture; } +template +size_t OpenGLRenderer::DrawPolygonsForIndexRange(const POLYLIST *polyList, const INDEXLIST *indexList, size_t indexOffset, size_t firstIndex, size_t lastIndex) +{ + OGLRenderRef &OGLRef = *this->ref; + + if (lastIndex > (polyList->count - 1)) + { + lastIndex = polyList->count - 1; + } + + if (firstIndex > lastIndex) + { + return 0; + } + + // Map GFX3D_QUADS and GFX3D_QUAD_STRIP to GL_TRIANGLES since we will convert them. + // + // Also map GFX3D_TRIANGLE_STRIP to GL_TRIANGLES. This is okay since this is actually + // how the POLY struct stores triangle strip vertices, which is in sets of 3 vertices + // each. This redefinition is necessary since uploading more than 3 indices at a time + // will cause glDrawElements() to draw the triangle strip incorrectly. + static const GLenum oglPrimitiveType[] = { GL_TRIANGLES, GL_TRIANGLES, GL_TRIANGLES, GL_TRIANGLES, + GL_LINE_LOOP, GL_LINE_LOOP, GL_LINE_STRIP, GL_LINE_STRIP }; + + static const GLsizei indexIncrementLUT[] = {3, 6, 3, 6, 3, 4, 3, 4}; + + const POLY &firstPoly = polyList->list[indexList->list[firstIndex]]; + u32 lastPolyAttr = firstPoly.polyAttr; + u32 lastTexParams = firstPoly.texParam; + u32 lastTexPalette = firstPoly.texPalette; + u32 lastViewport = firstPoly.viewport; + + this->SetupPolygon(firstPoly, (DRAWMODE != OGLPolyDrawMode_ZeroAlphaPass)); + this->SetupTexture(firstPoly, firstIndex); + this->SetupViewport(lastViewport); + + GLsizei vertIndexCount = 0; + GLushort *indexBufferPtr = OGLRef.vertIndexBuffer + indexOffset; + + // Enumerate through all polygons and render + size_t i = firstIndex; + for (; i <= lastIndex; i++) + { + const POLY &thePoly = polyList->list[indexList->list[i]]; + + // Set up the polygon if it changed + if (lastPolyAttr != thePoly.polyAttr) + { + lastPolyAttr = thePoly.polyAttr; + this->SetupPolygon(thePoly, (DRAWMODE != OGLPolyDrawMode_ZeroAlphaPass)); + } + + // Set up the texture if it changed + if (lastTexParams != thePoly.texParam || lastTexPalette != thePoly.texPalette) + { + lastTexParams = thePoly.texParam; + lastTexPalette = thePoly.texPalette; + this->SetupTexture(thePoly, i); + } + + // Set up the viewport if it changed + if (lastViewport != thePoly.viewport) + { + lastViewport = thePoly.viewport; + this->SetupViewport(thePoly.viewport); + } + + // In wireframe mode, redefine all primitives as GL_LINE_LOOP rather than + // setting the polygon mode to GL_LINE though glPolygonMode(). Not only is + // drawing more accurate this way, but it also allows GFX3D_QUADS and + // GFX3D_QUAD_STRIP primitives to properly draw as wireframe without the + // extra diagonal line. + const GLenum polyPrimitive = (!thePoly.isWireframe()) ? oglPrimitiveType[thePoly.vtxFormat] : GL_LINE_LOOP; + + // Increment the vertex count + vertIndexCount += indexIncrementLUT[thePoly.vtxFormat]; + + // Look ahead to the next polygon to see if we can simply buffer the indices + // instead of uploading them now. We can buffer if all polygon states remain + // the same and we're not drawing a line loop or line strip. + if (i+1 <= lastIndex) + { + const POLY *nextPoly = &polyList->list[indexList->list[i+1]]; + + if (lastPolyAttr == nextPoly->polyAttr && + lastTexParams == nextPoly->texParam && + lastTexPalette == nextPoly->texPalette && + lastViewport == nextPoly->viewport && + polyPrimitive == oglPrimitiveType[nextPoly->vtxFormat] && + polyPrimitive != GL_LINE_LOOP && + polyPrimitive != GL_LINE_STRIP && + oglPrimitiveType[nextPoly->vtxFormat] != GL_LINE_LOOP && + oglPrimitiveType[nextPoly->vtxFormat] != GL_LINE_STRIP) + { + continue; + } + } + + // Render the polygons + this->SetPolygonIndex(i); + + if (thePoly.getAttributePolygonMode() == POLYGON_MODE_SHADOW) + { + if (DRAWMODE != OGLPolyDrawMode_ZeroAlphaPass) + { + this->DrawShadowPolygon(polyPrimitive, vertIndexCount, indexBufferPtr, thePoly.getAttributeEnableAlphaDepthWrite(), thePoly.isTranslucent(), thePoly.getAttributePolygonID()); + } + } + else if ( (thePoly.getTexParamTexFormat() == TEXMODE_A3I5) || (thePoly.getTexParamTexFormat() == TEXMODE_A5I3) ) + { + if (DRAWMODE == OGLPolyDrawMode_ZeroAlphaPass) + { + this->DrawAlphaTexturePolygon(polyPrimitive, vertIndexCount, indexBufferPtr, thePoly.getAttributeEnableAlphaDepthWrite(), thePoly.isTranslucent(), thePoly.isWireframe() || thePoly.isOpaque()); + } + else + { + this->DrawAlphaTexturePolygon(polyPrimitive, vertIndexCount, indexBufferPtr, thePoly.getAttributeEnableAlphaDepthWrite(), thePoly.isTranslucent(), thePoly.isWireframe() || thePoly.isOpaque()); + } + } + else + { + glDrawElements(polyPrimitive, vertIndexCount, GL_UNSIGNED_SHORT, indexBufferPtr); + } + + indexBufferPtr += vertIndexCount; + indexOffset += vertIndexCount; + vertIndexCount = 0; + } + + return indexOffset; +} + +template +Render3DError OpenGLRenderer::DrawAlphaTexturePolygon(const GLenum polyPrimitive, const GLsizei vertIndexCount, const GLushort *indexBufferPtr, const bool enableAlphaDepthWrite, const bool isTranslucent, const bool canHaveOpaqueFragments) +{ + if (this->isShaderSupported) + { + const OGLRenderRef &OGLRef = *this->ref; + + if (isTranslucent) + { + // Draw the translucent fragments. + glDrawElements(polyPrimitive, vertIndexCount, GL_UNSIGNED_SHORT, indexBufferPtr); + + // Draw the opaque fragments if they might exist. + if (canHaveOpaqueFragments) + { + if (WILLUPDATESTENCILBUFFER) + { + glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); + glDepthMask(GL_TRUE); + } + + glUniform1i(OGLRef.uniformTexDrawOpaque, GL_TRUE); + glDrawElements(polyPrimitive, vertIndexCount, GL_UNSIGNED_SHORT, indexBufferPtr); + glUniform1i(OGLRef.uniformTexDrawOpaque, GL_FALSE); + + if (WILLUPDATESTENCILBUFFER) + { + glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + glDepthMask((enableAlphaDepthWrite) ? GL_TRUE : GL_FALSE); + } + } + } + else + { + // Draw the polygon as completely opaque. + glUniform1i(OGLRef.uniformTexDrawOpaque, GL_TRUE); + glDrawElements(polyPrimitive, vertIndexCount, GL_UNSIGNED_SHORT, indexBufferPtr); + glUniform1i(OGLRef.uniformTexDrawOpaque, GL_FALSE); + } + } + else + { + glDrawElements(polyPrimitive, vertIndexCount, GL_UNSIGNED_SHORT, indexBufferPtr); + } + + return OGLERROR_NOERR; +} + OpenGLRenderer_1_2::~OpenGLRenderer_1_2() { glFinish(); @@ -3037,154 +3217,6 @@ Render3DError OpenGLRenderer_1_2::DisableVertexAttributes() return OGLERROR_NOERR; } -template -size_t OpenGLRenderer_1_2::DrawPolygonsForIndexRange(const POLYLIST *polyList, const INDEXLIST *indexList, size_t indexOffset, size_t firstIndex, size_t lastIndex) -{ - OGLRenderRef &OGLRef = *this->ref; - - if (lastIndex > (polyList->count - 1)) - { - lastIndex = polyList->count - 1; - } - - if (firstIndex > lastIndex) - { - return 0; - } - - // Map GFX3D_QUADS and GFX3D_QUAD_STRIP to GL_TRIANGLES since we will convert them. - // - // Also map GFX3D_TRIANGLE_STRIP to GL_TRIANGLES. This is okay since this is actually - // how the POLY struct stores triangle strip vertices, which is in sets of 3 vertices - // each. This redefinition is necessary since uploading more than 3 indices at a time - // will cause glDrawElements() to draw the triangle strip incorrectly. - static const GLenum oglPrimitiveType[] = { GL_TRIANGLES, GL_TRIANGLES, GL_TRIANGLES, GL_TRIANGLES, - GL_LINE_LOOP, GL_LINE_LOOP, GL_LINE_STRIP, GL_LINE_STRIP }; - - static const GLsizei indexIncrementLUT[] = {3, 6, 3, 6, 3, 4, 3, 4}; - - const POLY &firstPoly = polyList->list[indexList->list[firstIndex]]; - u32 lastPolyAttr = firstPoly.polyAttr; - u32 lastTexParams = firstPoly.texParam; - u32 lastTexPalette = firstPoly.texPalette; - u32 lastViewport = firstPoly.viewport; - - if (DRAWMODE == OGLPolyDrawMode_ZeroAlphaPass) - { - this->SetupPolygon(firstPoly); - } - else - { - this->SetupPolygon(firstPoly); - } - - this->SetupTexture(firstPoly, firstIndex); - this->SetupViewport(lastViewport); - - GLsizei vertIndexCount = 0; - GLushort *indexBufferPtr = OGLRef.vertIndexBuffer + indexOffset; - - // Enumerate through all polygons and render - size_t i = firstIndex; - for (; i <= lastIndex; i++) - { - const POLY &thePoly = polyList->list[indexList->list[i]]; - - // Set up the polygon if it changed - if (lastPolyAttr != thePoly.polyAttr) - { - lastPolyAttr = thePoly.polyAttr; - - if (DRAWMODE == OGLPolyDrawMode_ZeroAlphaPass) - { - this->SetupPolygon(thePoly); - } - else - { - this->SetupPolygon(thePoly); - } - } - - // Set up the texture if it changed - if (lastTexParams != thePoly.texParam || lastTexPalette != thePoly.texPalette) - { - lastTexParams = thePoly.texParam; - lastTexPalette = thePoly.texPalette; - this->SetupTexture(thePoly, i); - } - - // Set up the viewport if it changed - if (lastViewport != thePoly.viewport) - { - lastViewport = thePoly.viewport; - this->SetupViewport(thePoly.viewport); - } - - // In wireframe mode, redefine all primitives as GL_LINE_LOOP rather than - // setting the polygon mode to GL_LINE though glPolygonMode(). Not only is - // drawing more accurate this way, but it also allows GFX3D_QUADS and - // GFX3D_QUAD_STRIP primitives to properly draw as wireframe without the - // extra diagonal line. - const GLenum polyPrimitive = (!thePoly.isWireframe()) ? oglPrimitiveType[thePoly.vtxFormat] : GL_LINE_LOOP; - - // Increment the vertex count - vertIndexCount += indexIncrementLUT[thePoly.vtxFormat]; - - // Look ahead to the next polygon to see if we can simply buffer the indices - // instead of uploading them now. We can buffer if all polygon states remain - // the same and we're not drawing a line loop or line strip. - if (i+1 <= lastIndex) - { - const POLY *nextPoly = &polyList->list[indexList->list[i+1]]; - - if (lastPolyAttr == nextPoly->polyAttr && - lastTexParams == nextPoly->texParam && - lastTexPalette == nextPoly->texPalette && - lastViewport == nextPoly->viewport && - polyPrimitive == oglPrimitiveType[nextPoly->vtxFormat] && - polyPrimitive != GL_LINE_LOOP && - polyPrimitive != GL_LINE_STRIP && - oglPrimitiveType[nextPoly->vtxFormat] != GL_LINE_LOOP && - oglPrimitiveType[nextPoly->vtxFormat] != GL_LINE_STRIP) - { - continue; - } - } - - // Render the polygons - this->SetPolygonIndex(i); - - if (thePoly.getAttributePolygonMode() == POLYGON_MODE_SHADOW) - { - if (DRAWMODE != OGLPolyDrawMode_ZeroAlphaPass) - { - this->DrawShadowPolygon(polyPrimitive, vertIndexCount, indexBufferPtr, thePoly.getAttributeEnableAlphaDepthWrite(), thePoly.isTranslucent(), thePoly.getAttributePolygonID()); - } - } - else if ( (thePoly.getTexParamTexFormat() == TEXMODE_A3I5) || (thePoly.getTexParamTexFormat() == TEXMODE_A5I3) ) - { - if (DRAWMODE == OGLPolyDrawMode_ZeroAlphaPass) - { - this->DrawAlphaTexturePolygon(polyPrimitive, vertIndexCount, indexBufferPtr, thePoly.getAttributeEnableAlphaDepthWrite(), thePoly.isTranslucent(), thePoly.isWireframe() || thePoly.isOpaque()); - } - else - { - this->DrawAlphaTexturePolygon(polyPrimitive, vertIndexCount, indexBufferPtr, thePoly.getAttributeEnableAlphaDepthWrite(), thePoly.isTranslucent(), thePoly.isWireframe() || thePoly.isOpaque()); - } - } - else - { - glDrawElements(polyPrimitive, vertIndexCount, GL_UNSIGNED_SHORT, indexBufferPtr); - } - - indexBufferPtr += vertIndexCount; - indexOffset += vertIndexCount; - vertIndexCount = 0; - } - - return indexOffset; -} - Render3DError OpenGLRenderer_1_2::DownsampleFBO() { OGLRenderRef &OGLRef = *this->ref; @@ -3859,8 +3891,7 @@ void OpenGLRenderer_1_2::SetPolygonIndex(const size_t index) this->_currentPolyIndex = index; } -template -Render3DError OpenGLRenderer_1_2::SetupPolygon(const POLY &thePoly) +Render3DError OpenGLRenderer_1_2::SetupPolygon(const POLY &thePoly, bool willChangeStencilBuffer) { const PolygonAttributes attr = thePoly.getAttributes(); @@ -3882,7 +3913,7 @@ Render3DError OpenGLRenderer_1_2::SetupPolygon(const POLY &thePoly) glCullFace(cullingMode); } - if (WILLCHANGESTENCILBUFFER) + if (willChangeStencilBuffer) { // Handle drawing states for the polygon if (attr.polygonMode == POLYGON_MODE_SHADOW) @@ -4028,54 +4059,6 @@ Render3DError OpenGLRenderer_1_2::SetupViewport(const u32 viewportValue) return OGLERROR_NOERR; } -template -Render3DError OpenGLRenderer_1_2::DrawAlphaTexturePolygon(const GLenum polyPrimitive, const GLsizei vertIndexCount, const GLushort *indexBufferPtr, const bool enableAlphaDepthWrite, const bool isTranslucent, const bool canHaveOpaqueFragments) -{ - if (this->isShaderSupported) - { - const OGLRenderRef &OGLRef = *this->ref; - - if (isTranslucent) - { - // Draw the translucent fragments. - glDrawElements(polyPrimitive, vertIndexCount, GL_UNSIGNED_SHORT, indexBufferPtr); - - // Draw the opaque fragments if they might exist. - if (canHaveOpaqueFragments) - { - if (WILLUPDATESTENCILBUFFER) - { - glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); - glDepthMask(GL_TRUE); - } - - glUniform1i(OGLRef.uniformTexDrawOpaque, GL_TRUE); - glDrawElements(polyPrimitive, vertIndexCount, GL_UNSIGNED_SHORT, indexBufferPtr); - glUniform1i(OGLRef.uniformTexDrawOpaque, GL_FALSE); - - if (WILLUPDATESTENCILBUFFER) - { - glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); - glDepthMask((enableAlphaDepthWrite) ? GL_TRUE : GL_FALSE); - } - } - } - else - { - // Draw the polygon as completely opaque. - glUniform1i(OGLRef.uniformTexDrawOpaque, GL_TRUE); - glDrawElements(polyPrimitive, vertIndexCount, GL_UNSIGNED_SHORT, indexBufferPtr); - glUniform1i(OGLRef.uniformTexDrawOpaque, GL_FALSE); - } - } - else - { - glDrawElements(polyPrimitive, vertIndexCount, GL_UNSIGNED_SHORT, indexBufferPtr); - } - - return OGLERROR_NOERR; -} - Render3DError OpenGLRenderer_1_2::DrawShadowPolygon(const GLenum polyPrimitive, const GLsizei vertIndexCount, const GLushort *indexBufferPtr, const bool enableAlphaDepthWrite, const bool isTranslucent, const u8 opaquePolyID) { // Shadow polygons are actually drawn over the course of multiple passes. diff --git a/desmume/src/OGLRender.h b/desmume/src/OGLRender.h index e7633a3b0..7cf37c7d0 100755 --- a/desmume/src/OGLRender.h +++ b/desmume/src/OGLRender.h @@ -631,6 +631,8 @@ protected: Render3DError FlushFramebuffer(const FragmentColor *__restrict srcFramebuffer, FragmentColor *__restrict dstFramebufferMain, u16 *__restrict dstFramebuffer16); OpenGLTexture* GetLoadedTextureFromPolygon(const POLY &thePoly, bool enableTexturing); + template size_t DrawPolygonsForIndexRange(const POLYLIST *polyList, const INDEXLIST *indexList, size_t indexOffset, size_t firstIndex, size_t lastIndex); + template Render3DError DrawAlphaTexturePolygon(const GLenum polyPrimitive, const GLsizei vertIndexCount, const GLushort *indexBufferPtr, const bool enableAlphaDepthWrite, const bool isTranslucent, const bool canHaveOpaqueFragments); // OpenGL-specific methods virtual Render3DError CreateVBOs() = 0; @@ -681,6 +683,7 @@ protected: virtual Render3DError DrawShadowPolygon(const GLenum polyPrimitive, const GLsizei vertIndexCount, const GLushort *indexBufferPtr, const bool enableAlphaDepthWrite, const bool isTranslucent, const u8 opaquePolyID) = 0; virtual void SetPolygonIndex(const size_t index) = 0; + virtual Render3DError SetupPolygon(const POLY &thePoly, bool willChangeStencilBuffer) = 0; public: OpenGLRenderer(); @@ -745,7 +748,6 @@ protected: virtual void GetExtensionSet(std::set *oglExtensionSet); virtual Render3DError EnableVertexAttributes(); virtual Render3DError DisableVertexAttributes(); - template size_t DrawPolygonsForIndexRange(const POLYLIST *polyList, const INDEXLIST *indexList, size_t indexOffset, size_t firstIndex, size_t lastIndex); virtual Render3DError DownsampleFBO(); virtual Render3DError ReadBackPixels(); @@ -760,11 +762,10 @@ protected: virtual Render3DError ClearUsingValues(const FragmentColor &clearColor6665, const FragmentAttributes &clearAttributes) const; virtual void SetPolygonIndex(const size_t index); - template Render3DError SetupPolygon(const POLY &thePoly); + virtual Render3DError SetupPolygon(const POLY &thePoly, bool willChangeStencilBuffer); virtual Render3DError SetupTexture(const POLY &thePoly, size_t polyRenderIndex); virtual Render3DError SetupViewport(const u32 viewportValue); - template Render3DError DrawAlphaTexturePolygon(const GLenum polyPrimitive, const GLsizei vertIndexCount, const GLushort *indexBufferPtr, const bool enableAlphaDepthWrite, const bool isTranslucent, const bool canHaveOpaqueFragments); virtual Render3DError DrawShadowPolygon(const GLenum polyPrimitive, const GLsizei vertIndexCount, const GLushort *indexBufferPtr, const bool enableAlphaDepthWrite, const bool isTranslucent, const u8 opaquePolyID); public: diff --git a/desmume/src/OGLRender_3_2.cpp b/desmume/src/OGLRender_3_2.cpp index 01cd643b1..942058cac 100644 --- a/desmume/src/OGLRender_3_2.cpp +++ b/desmume/src/OGLRender_3_2.cpp @@ -1642,8 +1642,7 @@ void OpenGLRenderer_3_2::SetPolygonIndex(const size_t index) glUniform1i(this->ref->uniformPolyStateIndex, index); } -template -Render3DError OpenGLRenderer_3_2::SetupPolygon(const POLY &thePoly) +Render3DError OpenGLRenderer_3_2::SetupPolygon(const POLY &thePoly, bool willChangeStencilBuffer) { const PolygonAttributes attr = thePoly.getAttributes(); @@ -1665,7 +1664,7 @@ Render3DError OpenGLRenderer_3_2::SetupPolygon(const POLY &thePoly) glCullFace(cullingMode); } - if (WILLCHANGESTENCILBUFFER) + if (willChangeStencilBuffer) { // Handle drawing states for the polygon if (attr.polygonMode == POLYGON_MODE_SHADOW) diff --git a/desmume/src/OGLRender_3_2.h b/desmume/src/OGLRender_3_2.h index 898caa51f..128fff2d0 100644 --- a/desmume/src/OGLRender_3_2.h +++ b/desmume/src/OGLRender_3_2.h @@ -99,7 +99,7 @@ protected: virtual Render3DError ClearUsingValues(const FragmentColor &clearColor6665, const FragmentAttributes &clearAttributes) const; virtual void SetPolygonIndex(const size_t index); - template Render3DError SetupPolygon(const POLY &thePoly); + virtual Render3DError SetupPolygon(const POLY &thePoly, bool willChangeStencilBuffer); virtual Render3DError SetupTexture(const POLY &thePoly, size_t polyRenderIndex); virtual Render3DError SetFramebufferSize(size_t w, size_t h);