OpenGL Renderer: Add true support for the depth-equals test by properly emulating the tolerance value. This fixes many longstanding bugs related to missing polygons, Z-fighting glitches, and other misc. graphical glitches in many games.
This commit is contained in:
parent
2a1aaf727e
commit
47a71941ac
|
@ -323,6 +323,7 @@ static const char *GeometryFragShader_100 = {"\
|
||||||
uniform bool texSingleBitAlpha;\n\
|
uniform bool texSingleBitAlpha;\n\
|
||||||
\n\
|
\n\
|
||||||
uniform bool polyDrawShadow;\n\
|
uniform bool polyDrawShadow;\n\
|
||||||
|
uniform int polyDepthOffsetMode;\n\
|
||||||
\n\
|
\n\
|
||||||
void main()\n\
|
void main()\n\
|
||||||
{\n\
|
{\n\
|
||||||
|
@ -331,8 +332,9 @@ static const char *GeometryFragShader_100 = {"\
|
||||||
vec4 newFogAttributes = vec4(0.0, 0.0, 0.0, 0.0);\n\
|
vec4 newFogAttributes = vec4(0.0, 0.0, 0.0, 0.0);\n\
|
||||||
\n\
|
\n\
|
||||||
float vertW = (vtxPosition.w == 0.0) ? 0.00000001 : vtxPosition.w;\n\
|
float vertW = (vtxPosition.w == 0.0) ? 0.00000001 : vtxPosition.w;\n\
|
||||||
|
float depthOffset = (polyDepthOffsetMode == 0) ? 0.0 : ((polyDepthOffsetMode == 1) ? -512.0 : 512.0);\n\
|
||||||
// hack: when using z-depth, drop some LSBs so that the overworld map in Dragon Quest IV shows up correctly\n\
|
// hack: when using z-depth, drop some LSBs so that the overworld map in Dragon Quest IV shows up correctly\n\
|
||||||
float newFragDepthValue = (stateUseWDepth) ? floor(vtxPosition.w * 4096.0) / 16777215.0 : (floor(clamp(((vtxPosition.z/vertW) * 0.5 + 0.5), 0.0, 1.0) * 32767.0) * 512.0) / 16777215.0;\n\
|
float newFragDepthValue = (stateUseWDepth) ? clamp( ( floor(vtxPosition.w * 4096.0) + depthOffset ) / 16777215.0, 0.0, 1.0 ) : clamp( ( (floor(clamp(((vtxPosition.z/vertW) * 0.5 + 0.5), 0.0, 1.0) * 32767.0) * 512.0) + depthOffset ) / 16777215.0, 0.0, 1.0 );\n\
|
||||||
\n\
|
\n\
|
||||||
if ((polyMode != 3) || polyDrawShadow)\n\
|
if ((polyMode != 3) || polyDrawShadow)\n\
|
||||||
{\n\
|
{\n\
|
||||||
|
@ -1625,23 +1627,16 @@ size_t OpenGLRenderer::DrawPolygonsForIndexRange(const POLYLIST *polyList, const
|
||||||
{
|
{
|
||||||
if (DRAWMODE != OGLPolyDrawMode_ZeroAlphaPass)
|
if (DRAWMODE != OGLPolyDrawMode_ZeroAlphaPass)
|
||||||
{
|
{
|
||||||
this->DrawShadowPolygon(polyPrimitive, vertIndexCount, indexBufferPtr, thePoly.attribute.TranslucentDepthWrite_Enable, thePoly.isTranslucent(), thePoly.attribute.PolygonID);
|
this->DrawShadowPolygon(polyPrimitive, vertIndexCount, indexBufferPtr, thePoly.attribute.DepthEqualTest_Enable, thePoly.attribute.TranslucentDepthWrite_Enable, (DRAWMODE == OGLPolyDrawMode_DrawTranslucentPolys), thePoly.attribute.PolygonID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if ( (thePoly.texParam.PackedFormat == TEXMODE_A3I5) || (thePoly.texParam.PackedFormat == TEXMODE_A5I3) )
|
else if ( (thePoly.texParam.PackedFormat == TEXMODE_A3I5) || (thePoly.texParam.PackedFormat == TEXMODE_A5I3) )
|
||||||
{
|
{
|
||||||
if (DRAWMODE == OGLPolyDrawMode_ZeroAlphaPass)
|
this->DrawAlphaTexturePolygon<DRAWMODE>(polyPrimitive, vertIndexCount, indexBufferPtr, thePoly.attribute.DepthEqualTest_Enable, thePoly.attribute.TranslucentDepthWrite_Enable, thePoly.isWireframe() || thePoly.isOpaque(), thePoly.attribute.PolygonID);
|
||||||
{
|
|
||||||
this->DrawAlphaTexturePolygon<false>(polyPrimitive, vertIndexCount, indexBufferPtr, thePoly.attribute.TranslucentDepthWrite_Enable, thePoly.isTranslucent(), thePoly.isWireframe() || thePoly.isOpaque());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
this->DrawAlphaTexturePolygon<true>(polyPrimitive, vertIndexCount, indexBufferPtr, thePoly.attribute.TranslucentDepthWrite_Enable, thePoly.isTranslucent(), thePoly.isWireframe() || thePoly.isOpaque());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
glDrawElements(polyPrimitive, vertIndexCount, GL_UNSIGNED_SHORT, indexBufferPtr);
|
this->DrawOtherPolygon<DRAWMODE>(polyPrimitive, vertIndexCount, indexBufferPtr, thePoly.attribute.DepthEqualTest_Enable, thePoly.attribute.TranslucentDepthWrite_Enable, thePoly.attribute.PolygonID);
|
||||||
}
|
}
|
||||||
|
|
||||||
indexBufferPtr += vertIndexCount;
|
indexBufferPtr += vertIndexCount;
|
||||||
|
@ -1652,44 +1647,145 @@ size_t OpenGLRenderer::DrawPolygonsForIndexRange(const POLYLIST *polyList, const
|
||||||
return indexOffset;
|
return indexOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <bool WILLUPDATESTENCILBUFFER>
|
template <OGLPolyDrawMode DRAWMODE>
|
||||||
Render3DError OpenGLRenderer::DrawAlphaTexturePolygon(const GLenum polyPrimitive, const GLsizei vertIndexCount, const GLushort *indexBufferPtr, const bool enableAlphaDepthWrite, const bool isTranslucent, const bool canHaveOpaqueFragments)
|
Render3DError OpenGLRenderer::DrawAlphaTexturePolygon(const GLenum polyPrimitive, const GLsizei vertIndexCount, const GLushort *indexBufferPtr, const bool performDepthEqualTest, const bool enableAlphaDepthWrite, const bool canHaveOpaqueFragments, const u8 opaquePolyID)
|
||||||
{
|
{
|
||||||
|
const OGLRenderRef &OGLRef = *this->ref;
|
||||||
|
|
||||||
if (this->isShaderSupported)
|
if (this->isShaderSupported)
|
||||||
{
|
{
|
||||||
const OGLRenderRef &OGLRef = *this->ref;
|
if ((DRAWMODE != OGLPolyDrawMode_ZeroAlphaPass) && performDepthEqualTest)
|
||||||
|
|
||||||
if (isTranslucent)
|
|
||||||
{
|
{
|
||||||
// Draw the translucent fragments.
|
if (DRAWMODE == OGLPolyDrawMode_DrawTranslucentPolys)
|
||||||
glDrawElements(polyPrimitive, vertIndexCount, GL_UNSIGNED_SHORT, indexBufferPtr);
|
|
||||||
|
|
||||||
// Draw the opaque fragments if they might exist.
|
|
||||||
if (canHaveOpaqueFragments)
|
|
||||||
{
|
{
|
||||||
if (WILLUPDATESTENCILBUFFER)
|
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
|
||||||
|
glDepthMask(GL_FALSE);
|
||||||
|
|
||||||
|
// Use the stencil buffer to determine which fragments pass the lower-side tolerance.
|
||||||
|
glUniform1i(OGLRef.uniformPolyDepthOffsetMode, 1);
|
||||||
|
glDepthFunc(GL_LEQUAL);
|
||||||
|
glStencilFunc(GL_ALWAYS, 0x80, 0x80);
|
||||||
|
glStencilOp(GL_ZERO, GL_ZERO, GL_REPLACE);
|
||||||
|
glStencilMask(0x80);
|
||||||
|
|
||||||
|
glUniform1i(OGLRef.uniformTexDrawOpaque, GL_FALSE);
|
||||||
|
glDrawElements(polyPrimitive, vertIndexCount, GL_UNSIGNED_SHORT, indexBufferPtr);
|
||||||
|
glUniform1i(OGLRef.uniformTexDrawOpaque, GL_TRUE);
|
||||||
|
glDrawElements(polyPrimitive, vertIndexCount, GL_UNSIGNED_SHORT, indexBufferPtr);
|
||||||
|
|
||||||
|
// Use the stencil buffer to determine which fragments pass the higher-side tolerance.
|
||||||
|
glUniform1i(OGLRef.uniformPolyDepthOffsetMode, 2);
|
||||||
|
glDepthFunc(GL_GEQUAL);
|
||||||
|
glStencilFunc(GL_EQUAL, 0x80, 0x80);
|
||||||
|
glStencilOp(GL_ZERO, GL_ZERO, GL_KEEP);
|
||||||
|
glStencilMask(0x80);
|
||||||
|
|
||||||
|
glUniform1i(OGLRef.uniformTexDrawOpaque, GL_FALSE);
|
||||||
|
glDrawElements(polyPrimitive, vertIndexCount, GL_UNSIGNED_SHORT, indexBufferPtr);
|
||||||
|
glUniform1i(OGLRef.uniformTexDrawOpaque, GL_TRUE);
|
||||||
|
glDrawElements(polyPrimitive, vertIndexCount, GL_UNSIGNED_SHORT, indexBufferPtr);
|
||||||
|
|
||||||
|
// Set up the actual drawing of the polygon, using the mask within the stencil buffer to determine which fragments should pass.
|
||||||
|
glUniform1i(OGLRef.uniformPolyDepthOffsetMode, 0);
|
||||||
|
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
||||||
|
glDepthFunc(GL_ALWAYS);
|
||||||
|
glStencilFunc(GL_EQUAL, 0x80 | opaquePolyID, 0x80);
|
||||||
|
glStencilMask(0x7F); // Drawing non-shadow polygons will implicitly reset the shadow volume mask.
|
||||||
|
|
||||||
|
// Draw the translucent fragments.
|
||||||
|
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
|
||||||
|
glDepthMask((enableAlphaDepthWrite) ? GL_TRUE : GL_FALSE);
|
||||||
|
|
||||||
|
glUniform1i(OGLRef.uniformTexDrawOpaque, GL_FALSE);
|
||||||
|
glDrawElements(polyPrimitive, vertIndexCount, GL_UNSIGNED_SHORT, indexBufferPtr);
|
||||||
|
|
||||||
|
// Draw the opaque fragments if they might exist.
|
||||||
|
if (canHaveOpaqueFragments)
|
||||||
{
|
{
|
||||||
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
|
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
|
||||||
glDepthMask(GL_TRUE);
|
glDepthMask(GL_TRUE);
|
||||||
}
|
|
||||||
|
|
||||||
glUniform1i(OGLRef.uniformTexDrawOpaque, GL_TRUE);
|
glUniform1i(OGLRef.uniformTexDrawOpaque, GL_TRUE);
|
||||||
glDrawElements(polyPrimitive, vertIndexCount, GL_UNSIGNED_SHORT, indexBufferPtr);
|
glDrawElements(polyPrimitive, vertIndexCount, GL_UNSIGNED_SHORT, indexBufferPtr);
|
||||||
glUniform1i(OGLRef.uniformTexDrawOpaque, GL_FALSE);
|
glUniform1i(OGLRef.uniformTexDrawOpaque, GL_FALSE);
|
||||||
|
|
||||||
if (WILLUPDATESTENCILBUFFER)
|
|
||||||
{
|
|
||||||
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
|
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
|
||||||
glDepthMask((enableAlphaDepthWrite) ? GL_TRUE : GL_FALSE);
|
glDepthMask((enableAlphaDepthWrite) ? GL_TRUE : GL_FALSE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
|
||||||
|
glDepthMask(GL_FALSE);
|
||||||
|
|
||||||
|
glUniform1i(OGLRef.uniformTexDrawOpaque, GL_TRUE);
|
||||||
|
|
||||||
|
// Use the stencil buffer to determine which fragments pass the lower-side tolerance.
|
||||||
|
glUniform1i(OGLRef.uniformPolyDepthOffsetMode, 1);
|
||||||
|
glDepthFunc(GL_LEQUAL);
|
||||||
|
glStencilFunc(GL_ALWAYS, 0x80, 0x80);
|
||||||
|
glStencilOp(GL_ZERO, GL_ZERO, GL_REPLACE);
|
||||||
|
glStencilMask(0x80);
|
||||||
|
glDrawElements(polyPrimitive, vertIndexCount, GL_UNSIGNED_SHORT, indexBufferPtr);
|
||||||
|
|
||||||
|
// Use the stencil buffer to determine which fragments pass the higher-side tolerance.
|
||||||
|
glUniform1i(OGLRef.uniformPolyDepthOffsetMode, 2);
|
||||||
|
glDepthFunc(GL_GEQUAL);
|
||||||
|
glStencilFunc(GL_EQUAL, 0x80, 0x80);
|
||||||
|
glStencilOp(GL_ZERO, GL_ZERO, GL_KEEP);
|
||||||
|
glStencilMask(0x80);
|
||||||
|
glDrawElements(polyPrimitive, vertIndexCount, GL_UNSIGNED_SHORT, indexBufferPtr);
|
||||||
|
|
||||||
|
// Set up the actual drawing of the polygon, using the mask within the stencil buffer to determine which fragments should pass.
|
||||||
|
glUniform1i(OGLRef.uniformPolyDepthOffsetMode, 0);
|
||||||
|
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
||||||
|
glDepthFunc(GL_ALWAYS);
|
||||||
|
glStencilFunc(GL_EQUAL, 0x80 | opaquePolyID, 0x80);
|
||||||
|
glStencilMask(0x7F); // Drawing non-shadow polygons will implicitly reset the shadow volume mask.
|
||||||
|
|
||||||
|
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
|
||||||
|
glDepthMask(GL_TRUE);
|
||||||
|
|
||||||
|
// Draw the polygon as completely opaque.
|
||||||
|
glDrawElements(polyPrimitive, vertIndexCount, GL_UNSIGNED_SHORT, indexBufferPtr);
|
||||||
|
|
||||||
|
glUniform1i(OGLRef.uniformTexDrawOpaque, GL_FALSE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Draw the polygon as completely opaque.
|
if (DRAWMODE != OGLPolyDrawMode_DrawOpaquePolys)
|
||||||
glUniform1i(OGLRef.uniformTexDrawOpaque, GL_TRUE);
|
{
|
||||||
glDrawElements(polyPrimitive, vertIndexCount, GL_UNSIGNED_SHORT, indexBufferPtr);
|
// Draw the translucent fragments.
|
||||||
glUniform1i(OGLRef.uniformTexDrawOpaque, GL_FALSE);
|
glDrawElements(polyPrimitive, vertIndexCount, GL_UNSIGNED_SHORT, indexBufferPtr);
|
||||||
|
|
||||||
|
// Draw the opaque fragments if they might exist.
|
||||||
|
if (canHaveOpaqueFragments)
|
||||||
|
{
|
||||||
|
if (DRAWMODE != OGLPolyDrawMode_ZeroAlphaPass)
|
||||||
|
{
|
||||||
|
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 (DRAWMODE != OGLPolyDrawMode_ZeroAlphaPass)
|
||||||
|
{
|
||||||
|
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
|
else
|
||||||
|
@ -1700,6 +1796,51 @@ Render3DError OpenGLRenderer::DrawAlphaTexturePolygon(const GLenum polyPrimitive
|
||||||
return OGLERROR_NOERR;
|
return OGLERROR_NOERR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <OGLPolyDrawMode DRAWMODE>
|
||||||
|
Render3DError OpenGLRenderer::DrawOtherPolygon(const GLenum polyPrimitive, const GLsizei vertIndexCount, const GLushort *indexBufferPtr, const bool performDepthEqualTest, const bool enableAlphaDepthWrite, const u8 opaquePolyID)
|
||||||
|
{
|
||||||
|
OGLRenderRef &OGLRef = *this->ref;
|
||||||
|
|
||||||
|
if ((DRAWMODE != OGLPolyDrawMode_ZeroAlphaPass) && performDepthEqualTest && this->isShaderSupported)
|
||||||
|
{
|
||||||
|
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
|
||||||
|
glDepthMask(GL_FALSE);
|
||||||
|
|
||||||
|
// Use the stencil buffer to determine which fragments pass the lower-side tolerance.
|
||||||
|
glUniform1i(OGLRef.uniformPolyDepthOffsetMode, 1);
|
||||||
|
glDepthFunc(GL_LEQUAL);
|
||||||
|
glStencilFunc(GL_ALWAYS, 0x80, 0x80);
|
||||||
|
glStencilOp(GL_ZERO, GL_ZERO, GL_REPLACE);
|
||||||
|
glStencilMask(0x80);
|
||||||
|
glDrawElements(polyPrimitive, vertIndexCount, GL_UNSIGNED_SHORT, indexBufferPtr);
|
||||||
|
|
||||||
|
// Use the stencil buffer to determine which fragments pass the higher-side tolerance.
|
||||||
|
glUniform1i(OGLRef.uniformPolyDepthOffsetMode, 2);
|
||||||
|
glDepthFunc(GL_GEQUAL);
|
||||||
|
glStencilFunc(GL_EQUAL, 0x80, 0x80);
|
||||||
|
glStencilOp(GL_ZERO, GL_ZERO, GL_KEEP);
|
||||||
|
glStencilMask(0x80);
|
||||||
|
glDrawElements(polyPrimitive, vertIndexCount, GL_UNSIGNED_SHORT, indexBufferPtr);
|
||||||
|
|
||||||
|
// Draw the polygon using the mask within the stencil buffer to determine which fragments should pass.
|
||||||
|
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
||||||
|
glDepthMask(((DRAWMODE == OGLPolyDrawMode_DrawOpaquePolys) || enableAlphaDepthWrite) ? GL_TRUE : GL_FALSE);
|
||||||
|
|
||||||
|
glUniform1i(OGLRef.uniformPolyDepthOffsetMode, 0);
|
||||||
|
glDepthFunc(GL_ALWAYS);
|
||||||
|
glStencilFunc(GL_EQUAL, 0x80 | opaquePolyID, 0x80);
|
||||||
|
glStencilOp(GL_KEEP, GL_KEEP, (DRAWMODE == OGLPolyDrawMode_DrawTranslucentPolys) ? GL_KEEP : GL_REPLACE);
|
||||||
|
glStencilMask(0x7F); // Drawing non-shadow polygons will implicitly reset the shadow volume mask.
|
||||||
|
glDrawElements(polyPrimitive, vertIndexCount, GL_UNSIGNED_SHORT, indexBufferPtr);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
glDrawElements(polyPrimitive, vertIndexCount, GL_UNSIGNED_SHORT, indexBufferPtr);
|
||||||
|
}
|
||||||
|
|
||||||
|
return OGLERROR_NOERR;
|
||||||
|
}
|
||||||
|
|
||||||
OpenGLRenderer_1_2::~OpenGLRenderer_1_2()
|
OpenGLRenderer_1_2::~OpenGLRenderer_1_2()
|
||||||
{
|
{
|
||||||
glFinish();
|
glFinish();
|
||||||
|
@ -2058,6 +2199,7 @@ Render3DError OpenGLRenderer_1_2::InitGeometryProgramShaderLocations()
|
||||||
|
|
||||||
OGLRef.uniformTexDrawOpaque = glGetUniformLocation(OGLRef.programGeometryID, "texDrawOpaque");
|
OGLRef.uniformTexDrawOpaque = glGetUniformLocation(OGLRef.programGeometryID, "texDrawOpaque");
|
||||||
OGLRef.uniformPolyDrawShadow = glGetUniformLocation(OGLRef.programGeometryID, "polyDrawShadow");
|
OGLRef.uniformPolyDrawShadow = glGetUniformLocation(OGLRef.programGeometryID, "polyDrawShadow");
|
||||||
|
OGLRef.uniformPolyDepthOffsetMode = glGetUniformLocation(OGLRef.programGeometryID, "polyDepthOffsetMode");
|
||||||
|
|
||||||
return OGLERROR_NOERR;
|
return OGLERROR_NOERR;
|
||||||
}
|
}
|
||||||
|
@ -4099,8 +4241,7 @@ void OpenGLRenderer_1_2::SetPolygonIndex(const size_t index)
|
||||||
Render3DError OpenGLRenderer_1_2::SetupPolygon(const POLY &thePoly, bool treatAsTranslucent, bool willChangeStencilBuffer)
|
Render3DError OpenGLRenderer_1_2::SetupPolygon(const POLY &thePoly, bool treatAsTranslucent, bool willChangeStencilBuffer)
|
||||||
{
|
{
|
||||||
// Set up depth test mode
|
// Set up depth test mode
|
||||||
static const GLenum oglDepthFunc[2] = {GL_LESS, GL_EQUAL};
|
glDepthFunc((thePoly.attribute.DepthEqualTest_Enable) ? GL_EQUAL : GL_LESS);
|
||||||
glDepthFunc(oglDepthFunc[thePoly.attribute.DepthEqualTest_Enable]);
|
|
||||||
|
|
||||||
// Set up culling mode
|
// Set up culling mode
|
||||||
static const GLenum oglCullingMode[4] = {GL_FRONT_AND_BACK, GL_FRONT, GL_BACK, 0};
|
static const GLenum oglCullingMode[4] = {GL_FRONT_AND_BACK, GL_FRONT, GL_BACK, 0};
|
||||||
|
@ -4166,6 +4307,7 @@ Render3DError OpenGLRenderer_1_2::SetupPolygon(const POLY &thePoly, bool treatAs
|
||||||
glUniform1i(OGLRef.uniformPolyID, thePoly.attribute.PolygonID);
|
glUniform1i(OGLRef.uniformPolyID, thePoly.attribute.PolygonID);
|
||||||
glUniform1i(OGLRef.uniformPolyIsWireframe, (thePoly.isWireframe()) ? GL_TRUE : GL_FALSE);
|
glUniform1i(OGLRef.uniformPolyIsWireframe, (thePoly.isWireframe()) ? GL_TRUE : GL_FALSE);
|
||||||
glUniform1i(OGLRef.uniformPolySetNewDepthForTranslucent, (thePoly.attribute.TranslucentDepthWrite_Enable) ? GL_TRUE : GL_FALSE);
|
glUniform1i(OGLRef.uniformPolySetNewDepthForTranslucent, (thePoly.attribute.TranslucentDepthWrite_Enable) ? GL_TRUE : GL_FALSE);
|
||||||
|
glUniform1i(OGLRef.uniformPolyDepthOffsetMode, 0);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -4259,8 +4401,10 @@ Render3DError OpenGLRenderer_1_2::SetupViewport(const u32 viewportValue)
|
||||||
return OGLERROR_NOERR;
|
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)
|
Render3DError OpenGLRenderer_1_2::DrawShadowPolygon(const GLenum polyPrimitive, const GLsizei vertIndexCount, const GLushort *indexBufferPtr, const bool performDepthEqualTest, const bool enableAlphaDepthWrite, const bool isTranslucent, const u8 opaquePolyID)
|
||||||
{
|
{
|
||||||
|
OGLRenderRef &OGLRef = *this->ref;
|
||||||
|
|
||||||
// Shadow polygons are actually drawn over the course of multiple passes.
|
// Shadow polygons are actually drawn over the course of multiple passes.
|
||||||
// Note that the 1st and 2nd passes are performed using states from SetupPolygon().
|
// Note that the 1st and 2nd passes are performed using states from SetupPolygon().
|
||||||
//
|
//
|
||||||
|
@ -4275,28 +4419,99 @@ Render3DError OpenGLRenderer_1_2::DrawShadowPolygon(const GLenum polyPrimitive,
|
||||||
// what the NDS does at this point. In OpenGL, this pass is used only to update the
|
// what the NDS does at this point. In OpenGL, this pass is used only to update the
|
||||||
// stencil buffer for the polygon ID check, checking bits 0x3F for the polygon ID,
|
// stencil buffer for the polygon ID check, checking bits 0x3F for the polygon ID,
|
||||||
// and clearing bit 6 (0x40) if this check fails. Color and depth writes are disabled
|
// and clearing bit 6 (0x40) if this check fails. Color and depth writes are disabled
|
||||||
|
//
|
||||||
|
// 3rd pass (emulator driven): This pass only occurs when the shadow polygon is opaque.
|
||||||
|
// Since opaque polygons need to update their polygon IDs, we update only the stencil
|
||||||
|
// buffer with the polygon ID. Color and depth writes are disabled for this pass.
|
||||||
// for this pass.
|
// for this pass.
|
||||||
//
|
//
|
||||||
// 3rd pass (emulator driven): Use stencil buffer bit 6 (0x40) for the shadow volume
|
// 4th pass (emulator driven): Use stencil buffer bit 6 (0x40) for the shadow volume
|
||||||
// mask and render the shadow polygons only within the mask. Color writes are always
|
// mask and render the shadow polygons only within the mask. Color writes are always
|
||||||
// enabled and depth writes are enabled if the shadow polygon is opaque or if transparent
|
// enabled and depth writes are enabled if the shadow polygon is opaque or if transparent
|
||||||
// polygon depth writes are enabled.
|
// polygon depth writes are enabled.
|
||||||
//
|
|
||||||
// 4th pass (emulator driven): This pass only occurs when the shadow polygon is opaque.
|
|
||||||
// Since opaque polygons need to update their polygon IDs, we update only the stencil
|
|
||||||
// buffer with the polygon ID. Color and depth values are disabled for this pass.
|
|
||||||
|
|
||||||
// 1st pass: Create the shadow volume.
|
// 1st pass: Create the shadow volume.
|
||||||
if (opaquePolyID == 0)
|
if (opaquePolyID == 0)
|
||||||
{
|
{
|
||||||
glDrawElements(polyPrimitive, vertIndexCount, GL_UNSIGNED_SHORT, indexBufferPtr);
|
if (performDepthEqualTest && this->isShaderSupported)
|
||||||
|
{
|
||||||
|
// Use the stencil buffer to determine which fragments pass the lower-side tolerance.
|
||||||
|
glUniform1i(OGLRef.uniformPolyDepthOffsetMode, 1);
|
||||||
|
glDepthFunc(GL_LEQUAL);
|
||||||
|
glStencilFunc(GL_ALWAYS, 0x80, 0x80);
|
||||||
|
glStencilOp(GL_ZERO, GL_ZERO, GL_REPLACE);
|
||||||
|
glStencilMask(0x80);
|
||||||
|
glDrawElements(polyPrimitive, vertIndexCount, GL_UNSIGNED_SHORT, indexBufferPtr);
|
||||||
|
|
||||||
|
// Use the stencil buffer to determine which fragments pass the higher-side tolerance.
|
||||||
|
glUniform1i(OGLRef.uniformPolyDepthOffsetMode, 2);
|
||||||
|
glDepthFunc(GL_GEQUAL);
|
||||||
|
glStencilFunc(GL_EQUAL, 0x80, 0x80);
|
||||||
|
glStencilOp(GL_ZERO, GL_ZERO, GL_KEEP);
|
||||||
|
glStencilMask(0x80);
|
||||||
|
glDrawElements(polyPrimitive, vertIndexCount, GL_UNSIGNED_SHORT, indexBufferPtr);
|
||||||
|
|
||||||
|
// Draw the polygon using the mask within the stencil buffer to determine which fragments should pass.
|
||||||
|
glUniform1i(OGLRef.uniformPolyDepthOffsetMode, 0);
|
||||||
|
glDepthFunc(GL_ALWAYS);
|
||||||
|
glStencilFunc(GL_EQUAL, 0xC0, 0x80);
|
||||||
|
glStencilOp(GL_KEEP, GL_REPLACE, GL_KEEP);
|
||||||
|
glStencilMask(0x40);
|
||||||
|
glDrawElements(polyPrimitive, vertIndexCount, GL_UNSIGNED_SHORT, indexBufferPtr);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
glDrawElements(polyPrimitive, vertIndexCount, GL_UNSIGNED_SHORT, indexBufferPtr);
|
||||||
|
}
|
||||||
|
|
||||||
return OGLERROR_NOERR;
|
return OGLERROR_NOERR;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2nd pass: Do the polygon ID check.
|
// 2nd pass: Do the polygon ID check.
|
||||||
|
if (performDepthEqualTest && this->isShaderSupported)
|
||||||
|
{
|
||||||
|
// Use the stencil buffer to determine which fragments pass the lower-side tolerance.
|
||||||
|
glUniform1i(OGLRef.uniformPolyDepthOffsetMode, 1);
|
||||||
|
glDepthFunc(GL_LEQUAL);
|
||||||
|
glStencilFunc(GL_ALWAYS, 0x80, 0x80);
|
||||||
|
glStencilOp(GL_ZERO, GL_ZERO, GL_REPLACE);
|
||||||
|
glStencilMask(0x80);
|
||||||
|
glDrawElements(polyPrimitive, vertIndexCount, GL_UNSIGNED_SHORT, indexBufferPtr);
|
||||||
|
|
||||||
|
// Use the stencil buffer to determine which fragments pass the higher-side tolerance.
|
||||||
|
glUniform1i(OGLRef.uniformPolyDepthOffsetMode, 2);
|
||||||
|
glDepthFunc(GL_GEQUAL);
|
||||||
|
glStencilFunc(GL_EQUAL, 0x80, 0x80);
|
||||||
|
glStencilOp(GL_ZERO, GL_ZERO, GL_KEEP);
|
||||||
|
glStencilMask(0x80);
|
||||||
|
glDrawElements(polyPrimitive, vertIndexCount, GL_UNSIGNED_SHORT, indexBufferPtr);
|
||||||
|
|
||||||
|
// Check both the depth-equals test bit (bit 7) and shadow volume mask bit (bit 6).
|
||||||
|
// Fragments that fail this stencil test are removed from the shadow volume mask.
|
||||||
|
glUniform1i(OGLRef.uniformPolyDepthOffsetMode, 0);
|
||||||
|
glDepthFunc(GL_ALWAYS);
|
||||||
|
glStencilFunc(GL_EQUAL, 0xC0, 0xC0);
|
||||||
|
glStencilOp(GL_ZERO, GL_KEEP, GL_KEEP);
|
||||||
|
glStencilMask(0x40);
|
||||||
|
glDrawElements(polyPrimitive, vertIndexCount, GL_UNSIGNED_SHORT, indexBufferPtr);
|
||||||
|
|
||||||
|
// Set up the polygon ID check as normal.
|
||||||
|
glStencilFunc(GL_NOTEQUAL, opaquePolyID, 0x3F);
|
||||||
|
glDrawElements(polyPrimitive, vertIndexCount, GL_UNSIGNED_SHORT, indexBufferPtr);
|
||||||
|
}
|
||||||
|
|
||||||
glDrawElements(polyPrimitive, vertIndexCount, GL_UNSIGNED_SHORT, indexBufferPtr);
|
glDrawElements(polyPrimitive, vertIndexCount, GL_UNSIGNED_SHORT, indexBufferPtr);
|
||||||
|
|
||||||
// 3rd pass: Draw the shadow polygon.
|
// 3rd pass: Update the polygon IDs in the stencil buffer if the shadow polygons are opaque.
|
||||||
|
if (!isTranslucent)
|
||||||
|
{
|
||||||
|
glStencilFunc(GL_EQUAL, 0x40 | opaquePolyID, 0x40);
|
||||||
|
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
|
||||||
|
glStencilMask(0x3F);
|
||||||
|
glDrawElements(polyPrimitive, vertIndexCount, GL_UNSIGNED_SHORT, indexBufferPtr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4th pass: Draw the shadow polygon.
|
||||||
glStencilFunc(GL_EQUAL, 0x40, 0x40);
|
glStencilFunc(GL_EQUAL, 0x40, 0x40);
|
||||||
// Technically, a depth-fail result should also clear the shadow volume mask, but
|
// Technically, a depth-fail result should also clear the shadow volume mask, but
|
||||||
// Mario Kart DS draws shadow polygons better when it doesn't clear bits on depth-fail.
|
// Mario Kart DS draws shadow polygons better when it doesn't clear bits on depth-fail.
|
||||||
|
@ -4308,8 +4523,6 @@ Render3DError OpenGLRenderer_1_2::DrawShadowPolygon(const GLenum polyPrimitive,
|
||||||
|
|
||||||
if (this->isShaderSupported)
|
if (this->isShaderSupported)
|
||||||
{
|
{
|
||||||
const OGLRenderRef &OGLRef = *this->ref;
|
|
||||||
|
|
||||||
glUniform1i(OGLRef.uniformPolyDrawShadow, GL_TRUE);
|
glUniform1i(OGLRef.uniformPolyDrawShadow, GL_TRUE);
|
||||||
glDrawElements(polyPrimitive, vertIndexCount, GL_UNSIGNED_SHORT, indexBufferPtr);
|
glDrawElements(polyPrimitive, vertIndexCount, GL_UNSIGNED_SHORT, indexBufferPtr);
|
||||||
glUniform1i(OGLRef.uniformPolyDrawShadow, GL_FALSE);
|
glUniform1i(OGLRef.uniformPolyDrawShadow, GL_FALSE);
|
||||||
|
@ -4320,21 +4533,12 @@ Render3DError OpenGLRenderer_1_2::DrawShadowPolygon(const GLenum polyPrimitive,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset the OpenGL states back to their original shadow polygon states.
|
// Reset the OpenGL states back to their original shadow polygon states.
|
||||||
|
glStencilFunc(GL_NOTEQUAL, opaquePolyID, 0x3F);
|
||||||
glStencilOp(GL_ZERO, GL_KEEP, GL_KEEP);
|
glStencilOp(GL_ZERO, GL_KEEP, GL_KEEP);
|
||||||
|
glStencilMask(0x40);
|
||||||
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
|
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
|
||||||
glDepthMask(GL_FALSE);
|
glDepthMask(GL_FALSE);
|
||||||
|
|
||||||
// 4th pass: Update the polygon IDs in the stencil buffer if the shadow polygons are opaque.
|
|
||||||
if (!isTranslucent)
|
|
||||||
{
|
|
||||||
glStencilFunc(GL_ALWAYS, opaquePolyID, 0x3F);
|
|
||||||
glStencilMask(0x3F);
|
|
||||||
glDrawElements(polyPrimitive, vertIndexCount, GL_UNSIGNED_SHORT, indexBufferPtr);
|
|
||||||
}
|
|
||||||
|
|
||||||
glStencilFunc(GL_NOTEQUAL, opaquePolyID, 0x3F);
|
|
||||||
glStencilMask(0x40);
|
|
||||||
|
|
||||||
return OGLERROR_NOERR;
|
return OGLERROR_NOERR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -523,6 +523,7 @@ struct OGLRenderRef
|
||||||
GLint uniformTexDrawOpaque;
|
GLint uniformTexDrawOpaque;
|
||||||
|
|
||||||
GLint uniformPolyStateIndex;
|
GLint uniformPolyStateIndex;
|
||||||
|
GLint uniformPolyDepthOffsetMode;
|
||||||
GLint uniformPolyDrawShadow;
|
GLint uniformPolyDrawShadow;
|
||||||
|
|
||||||
GLuint texToonTableID;
|
GLuint texToonTableID;
|
||||||
|
@ -658,7 +659,8 @@ protected:
|
||||||
Render3DError FlushFramebuffer(const FragmentColor *__restrict srcFramebuffer, FragmentColor *__restrict dstFramebufferMain, u16 *__restrict dstFramebuffer16);
|
Render3DError FlushFramebuffer(const FragmentColor *__restrict srcFramebuffer, FragmentColor *__restrict dstFramebufferMain, u16 *__restrict dstFramebuffer16);
|
||||||
OpenGLTexture* GetLoadedTextureFromPolygon(const POLY &thePoly, bool enableTexturing);
|
OpenGLTexture* GetLoadedTextureFromPolygon(const POLY &thePoly, bool enableTexturing);
|
||||||
template<OGLPolyDrawMode DRAWMODE> size_t DrawPolygonsForIndexRange(const POLYLIST *polyList, const INDEXLIST *indexList, size_t firstIndex, size_t lastIndex, size_t &indexOffset, POLYGON_ATTR &lastPolyAttr);
|
template<OGLPolyDrawMode DRAWMODE> size_t DrawPolygonsForIndexRange(const POLYLIST *polyList, const INDEXLIST *indexList, size_t firstIndex, size_t lastIndex, size_t &indexOffset, POLYGON_ATTR &lastPolyAttr);
|
||||||
template<bool WILLUPDATESTENCILBUFFER> Render3DError DrawAlphaTexturePolygon(const GLenum polyPrimitive, const GLsizei vertIndexCount, const GLushort *indexBufferPtr, const bool enableAlphaDepthWrite, const bool isTranslucent, const bool canHaveOpaqueFragments);
|
template<OGLPolyDrawMode DRAWMODE> Render3DError DrawAlphaTexturePolygon(const GLenum polyPrimitive, const GLsizei vertIndexCount, const GLushort *indexBufferPtr, const bool performDepthEqualTest, const bool enableAlphaDepthWrite, const bool canHaveOpaqueFragments, const u8 opaquePolyID);
|
||||||
|
template<OGLPolyDrawMode DRAWMODE> Render3DError DrawOtherPolygon(const GLenum polyPrimitive, const GLsizei vertIndexCount, const GLushort *indexBufferPtr, const bool performDepthEqualTest, const bool enableAlphaDepthWrite, const u8 opaquePolyID);
|
||||||
|
|
||||||
// OpenGL-specific methods
|
// OpenGL-specific methods
|
||||||
virtual Render3DError CreateVBOs() = 0;
|
virtual Render3DError CreateVBOs() = 0;
|
||||||
|
@ -706,7 +708,7 @@ protected:
|
||||||
virtual Render3DError DownsampleFBO() = 0;
|
virtual Render3DError DownsampleFBO() = 0;
|
||||||
virtual Render3DError ReadBackPixels() = 0;
|
virtual Render3DError ReadBackPixels() = 0;
|
||||||
|
|
||||||
virtual Render3DError DrawShadowPolygon(const GLenum polyPrimitive, const GLsizei vertIndexCount, const GLushort *indexBufferPtr, const bool enableAlphaDepthWrite, const bool isTranslucent, const u8 opaquePolyID) = 0;
|
virtual Render3DError DrawShadowPolygon(const GLenum polyPrimitive, const GLsizei vertIndexCount, const GLushort *indexBufferPtr, const bool performDepthEqualTest, const bool enableAlphaDepthWrite, const bool isTranslucent, const u8 opaquePolyID) = 0;
|
||||||
virtual void SetPolygonIndex(const size_t index) = 0;
|
virtual void SetPolygonIndex(const size_t index) = 0;
|
||||||
virtual Render3DError SetupPolygon(const POLY &thePoly, bool treatAsTranslucent, bool willChangeStencilBuffer) = 0;
|
virtual Render3DError SetupPolygon(const POLY &thePoly, bool treatAsTranslucent, bool willChangeStencilBuffer) = 0;
|
||||||
|
|
||||||
|
@ -792,7 +794,7 @@ protected:
|
||||||
virtual Render3DError SetupTexture(const POLY &thePoly, size_t polyRenderIndex);
|
virtual Render3DError SetupTexture(const POLY &thePoly, size_t polyRenderIndex);
|
||||||
virtual Render3DError SetupViewport(const u32 viewportValue);
|
virtual Render3DError SetupViewport(const u32 viewportValue);
|
||||||
|
|
||||||
virtual Render3DError DrawShadowPolygon(const GLenum polyPrimitive, const GLsizei vertIndexCount, const GLushort *indexBufferPtr, const bool enableAlphaDepthWrite, const bool isTranslucent, const u8 opaquePolyID);
|
virtual Render3DError DrawShadowPolygon(const GLenum polyPrimitive, const GLsizei vertIndexCount, const GLushort *indexBufferPtr, const bool performDepthEqualTest, const bool enableAlphaDepthWrite, const bool isTranslucent, const u8 opaquePolyID);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
~OpenGLRenderer_1_2();
|
~OpenGLRenderer_1_2();
|
||||||
|
|
|
@ -191,6 +191,7 @@ static const char *GeometryFragShader_150 = {"\
|
||||||
uniform bool texDrawOpaque;\n\
|
uniform bool texDrawOpaque;\n\
|
||||||
uniform bool polyDrawShadow;\n\
|
uniform bool polyDrawShadow;\n\
|
||||||
uniform int polyIndex;\n\
|
uniform int polyIndex;\n\
|
||||||
|
uniform int polyDepthOffsetMode;\n\
|
||||||
\n\
|
\n\
|
||||||
out vec4 outFragColor;\n\
|
out vec4 outFragColor;\n\
|
||||||
out vec4 outPolyID;\n\
|
out vec4 outPolyID;\n\
|
||||||
|
@ -203,8 +204,9 @@ static const char *GeometryFragShader_150 = {"\
|
||||||
vec4 newFogAttributes = vec4(0.0, 0.0, 0.0, 0.0);\n\
|
vec4 newFogAttributes = vec4(0.0, 0.0, 0.0, 0.0);\n\
|
||||||
\n\
|
\n\
|
||||||
float vertW = (vtxPosition.w == 0.0) ? 0.00000001 : vtxPosition.w;\n\
|
float vertW = (vtxPosition.w == 0.0) ? 0.00000001 : vtxPosition.w;\n\
|
||||||
|
float depthOffset = (polyDepthOffsetMode == 0) ? 0.0 : ((polyDepthOffsetMode == 1) ? -512.0 : 512.0);\n\
|
||||||
// hack: when using z-depth, drop some LSBs so that the overworld map in Dragon Quest IV shows up correctly\n\
|
// hack: when using z-depth, drop some LSBs so that the overworld map in Dragon Quest IV shows up correctly\n\
|
||||||
float newFragDepthValue = (state.useWDepth) ? floor(vtxPosition.w * 4096.0) / 16777215.0 : (floor(clamp(((vtxPosition.z/vertW) * 0.5 + 0.5), 0.0, 1.0) * 32767.0) * 512.0) / 16777215.0;\n\
|
float newFragDepthValue = (state.useWDepth) ? clamp( ( floor(vtxPosition.w * 4096.0) + depthOffset ) / 16777215.0, 0.0, 1.0 ) : clamp( ( (floor(clamp(((vtxPosition.z/vertW) * 0.5 + 0.5), 0.0, 1.0) * 32767.0) * 512.0) + depthOffset ) / 16777215.0, 0.0, 1.0 );\n\
|
||||||
\n\
|
\n\
|
||||||
if ((polyMode != 3u) || polyDrawShadow)\n\
|
if ((polyMode != 3u) || polyDrawShadow)\n\
|
||||||
{\n\
|
{\n\
|
||||||
|
@ -1237,6 +1239,7 @@ Render3DError OpenGLRenderer_3_2::InitGeometryProgramShaderLocations()
|
||||||
OGLRef.uniformTexDrawOpaque = glGetUniformLocation(OGLRef.programGeometryID, "texDrawOpaque");
|
OGLRef.uniformTexDrawOpaque = glGetUniformLocation(OGLRef.programGeometryID, "texDrawOpaque");
|
||||||
OGLRef.uniformPolyDrawShadow = glGetUniformLocation(OGLRef.programGeometryID, "polyDrawShadow");
|
OGLRef.uniformPolyDrawShadow = glGetUniformLocation(OGLRef.programGeometryID, "polyDrawShadow");
|
||||||
OGLRef.uniformPolyStateIndex = glGetUniformLocation(OGLRef.programGeometryID, "polyIndex");
|
OGLRef.uniformPolyStateIndex = glGetUniformLocation(OGLRef.programGeometryID, "polyIndex");
|
||||||
|
OGLRef.uniformPolyDepthOffsetMode = glGetUniformLocation(OGLRef.programGeometryID, "polyDepthOffsetMode");
|
||||||
|
|
||||||
return OGLERROR_NOERR;
|
return OGLERROR_NOERR;
|
||||||
}
|
}
|
||||||
|
@ -1858,9 +1861,11 @@ void OpenGLRenderer_3_2::SetPolygonIndex(const size_t index)
|
||||||
|
|
||||||
Render3DError OpenGLRenderer_3_2::SetupPolygon(const POLY &thePoly, bool treatAsTranslucent, bool willChangeStencilBuffer)
|
Render3DError OpenGLRenderer_3_2::SetupPolygon(const POLY &thePoly, bool treatAsTranslucent, bool willChangeStencilBuffer)
|
||||||
{
|
{
|
||||||
|
OGLRenderRef &OGLRef = *this->ref;
|
||||||
|
|
||||||
// Set up depth test mode
|
// Set up depth test mode
|
||||||
static const GLenum oglDepthFunc[2] = {GL_LESS, GL_EQUAL};
|
glDepthFunc((thePoly.attribute.DepthEqualTest_Enable) ? GL_EQUAL : GL_LESS);
|
||||||
glDepthFunc(oglDepthFunc[thePoly.attribute.DepthEqualTest_Enable]);
|
glUniform1i(OGLRef.uniformPolyDepthOffsetMode, 0);
|
||||||
|
|
||||||
// Set up culling mode
|
// Set up culling mode
|
||||||
static const GLenum oglCullingMode[4] = {GL_FRONT_AND_BACK, GL_FRONT, GL_BACK, 0};
|
static const GLenum oglCullingMode[4] = {GL_FRONT_AND_BACK, GL_FRONT, GL_BACK, 0};
|
||||||
|
|
Loading…
Reference in New Issue