diff --git a/platform/xcode/liblove.xcodeproj/project.pbxproj b/platform/xcode/liblove.xcodeproj/project.pbxproj index 6c96a6601..8aebce8f0 100644 --- a/platform/xcode/liblove.xcodeproj/project.pbxproj +++ b/platform/xcode/liblove.xcodeproj/project.pbxproj @@ -4012,7 +4012,7 @@ 08FB7793FE84155DC02AAC07 /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0900; + LastUpgradeCheck = 0920; TargetAttributes = { FA0B78DC1A958B90000E1D17 = { CreatedOnToolsVersion = 6.1.1; diff --git a/platform/xcode/love.xcodeproj/project.pbxproj b/platform/xcode/love.xcodeproj/project.pbxproj index 0a2ceda46..d6b17fc1e 100644 --- a/platform/xcode/love.xcodeproj/project.pbxproj +++ b/platform/xcode/love.xcodeproj/project.pbxproj @@ -326,7 +326,7 @@ 29B97313FDCFA39411CA2CEA /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0900; + LastUpgradeCheck = 0920; TargetAttributes = { FA0B7F051A95AAF3000E1D17 = { CreatedOnToolsVersion = 6.1.1; diff --git a/src/common/Matrix.cpp b/src/common/Matrix.cpp index a9374765a..38f30e280 100644 --- a/src/common/Matrix.cpp +++ b/src/common/Matrix.cpp @@ -382,16 +382,17 @@ Matrix4 Matrix4::inverse() const return inv; } -Matrix4 Matrix4::ortho(float left, float right, float bottom, float top) +Matrix4 Matrix4::ortho(float left, float right, float bottom, float top, float near, float far) { Matrix4 m; m.e[0] = 2.0f / (right - left); m.e[5] = 2.0f / (top - bottom); - m.e[10] = -1.0; + m.e[10] = -2.0f / (far - near); m.e[12] = -(right + left) / (right - left); m.e[13] = -(top + bottom) / (top - bottom); + m.e[14] = -(far + near) / (far - near); return m; } diff --git a/src/common/Matrix.h b/src/common/Matrix.h index 5001e4842..6411a6885 100644 --- a/src/common/Matrix.h +++ b/src/common/Matrix.h @@ -218,10 +218,9 @@ public: Matrix4 inverse() const; /** - * Creates a new orthographic projection matrix with depth in the range of - * [-1, 1]. + * Creates a new orthographic projection matrix. **/ - static Matrix4 ortho(float left, float right, float bottom, float top); + static Matrix4 ortho(float left, float right, float bottom, float top, float near, float far); private: diff --git a/src/modules/graphics/Font.cpp b/src/modules/graphics/Font.cpp index 48b032add..6a1700c2a 100644 --- a/src/modules/graphics/Font.cpp +++ b/src/modules/graphics/Font.cpp @@ -471,7 +471,7 @@ std::vector Font::generateVertices(const ColoredCodepoints &c // Add kerning to the current horizontal offset. dx += getKerning(prevglyph, g); - if (glyph.texture != 0) + if (glyph.texture != nullptr) { // Copy the vertices and set their colors and relative positions. for (int j = 0; j < 4; j++) diff --git a/src/modules/graphics/Graphics.cpp b/src/modules/graphics/Graphics.cpp index dba3ee72f..99a479918 100644 --- a/src/modules/graphics/Graphics.cpp +++ b/src/modules/graphics/Graphics.cpp @@ -374,6 +374,10 @@ void Graphics::restoreState(const DisplayState &s) setScissor(); setStencilTest(s.stencilCompare, s.stencilTestValue); + setDepthMode(s.depthTest, s.depthWrite); + + setMeshCullMode(s.meshCullMode); + setFrontFaceWinding(s.winding); setFont(s.font.get()); setShader(s.shader.get()); @@ -417,6 +421,14 @@ void Graphics::restoreStateChecked(const DisplayState &s) if (s.stencilCompare != cur.stencilCompare || s.stencilTestValue != cur.stencilTestValue) setStencilTest(s.stencilCompare, s.stencilTestValue); + if (s.depthTest != cur.depthTest || s.depthWrite != cur.depthWrite) + setDepthMode(s.depthTest, s.depthWrite); + + setMeshCullMode(s.meshCullMode); + + if (s.winding != cur.winding) + setFrontFaceWinding(s.winding); + setFont(s.font.get()); setShader(s.shader.get()); @@ -788,13 +800,46 @@ bool Graphics::getScissor(Rect &rect) const return state.scissor; } -void Graphics::getStencilTest(CompareMode &compare, int &value) +void Graphics::setStencilTest() +{ + setStencilTest(COMPARE_ALWAYS, 0); +} + +void Graphics::getStencilTest(CompareMode &compare, int &value) const { const DisplayState &state = states.back(); compare = state.stencilCompare; value = state.stencilTestValue; } +void Graphics::setDepthMode() +{ + setDepthMode(COMPARE_ALWAYS, false); +} + +void Graphics::getDepthMode(CompareMode &compare, bool &write) const +{ + const DisplayState &state = states.back(); + compare = state.depthTest; + write = state.depthWrite; +} + +void Graphics::setMeshCullMode(CullMode cull) +{ + // Handled inside the draw() graphics API implementations. + states.back().meshCullMode = cull; +} + +CullMode Graphics::getMeshCullMode() const +{ + return states.back().meshCullMode; +} + +vertex::Winding Graphics::getFrontFaceWinding() const +{ + return states.back().winding; +} + Graphics::ColorMask Graphics::getColorMask() const { return states.back().colorMask; @@ -1051,14 +1096,26 @@ void Graphics::flushStreamDraws() if (sbstate.indexCount > 0) { usedsizes[2] = sizeof(uint16) * sbstate.indexCount; - size_t offset = sbstate.indexBuffer->unmap(usedsizes[2]); + + DrawIndexedCommand cmd(&attributes, &buffers, sbstate.indexBuffer); + cmd.primitiveType = sbstate.primitiveMode; + cmd.indexCount = sbstate.indexCount; + cmd.indexType = INDEX_UINT16; + cmd.indexBufferOffset = sbstate.indexBuffer->unmap(usedsizes[2]); + cmd.texture = sbstate.texture; + draw(cmd); sbstate.indexBufferMap = StreamBuffer::MapInfo(); - - drawIndexed(sbstate.primitiveMode, sbstate.indexCount, 1, INDEX_UINT16, sbstate.indexBuffer, offset, attributes, buffers, sbstate.texture); } else - draw(sbstate.primitiveMode, 0, sbstate.vertexCount, 1, attributes, buffers, sbstate.texture); + { + DrawCommand cmd(&attributes, &buffers); + cmd.primitiveType = sbstate.primitiveMode; + cmd.vertexStart = 0; + cmd.vertexCount = sbstate.vertexCount; + cmd.texture = sbstate.texture; + draw(cmd); + } for (int i = 0; i < 2; i++) { diff --git a/src/modules/graphics/Graphics.h b/src/modules/graphics/Graphics.h index 33126555a..1326a314e 100644 --- a/src/modules/graphics/Graphics.h +++ b/src/modules/graphics/Graphics.h @@ -250,6 +250,54 @@ public: } }; + struct DrawCommand + { + PrimitiveType primitiveType = PRIMITIVE_TRIANGLES; + + const vertex::Attributes *attributes; + const vertex::Buffers *buffers; + + int vertexStart = 0; + int vertexCount = 0; + int instanceCount = 1; + + Texture *texture = nullptr; + + // TODO: This should be moved out to a state transition API? + CullMode cullMode = CULL_NONE; + + DrawCommand(const vertex::Attributes *attribs, const vertex::Buffers *buffers) + : attributes(attribs) + , buffers(buffers) + {} + }; + + struct DrawIndexedCommand + { + PrimitiveType primitiveType = PRIMITIVE_TRIANGLES; + + const vertex::Attributes *attributes; + const vertex::Buffers *buffers; + + int indexCount = 0; + int instanceCount = 1; + + IndexDataType indexType = INDEX_UINT16; + Resource *indexBuffer; + size_t indexBufferOffset = 0; + + Texture *texture = nullptr; + + // TODO: This should be moved out to a state transition API? + CullMode cullMode = CULL_NONE; + + DrawIndexedCommand(const vertex::Attributes *attribs, const vertex::Buffers *buffers, Resource *indexbuffer) + : attributes(attribs) + , buffers(buffers) + , indexBuffer(indexbuffer) + {} + }; + struct StreamDrawCommand { PrimitiveType primitiveMode = PRIMITIVE_TRIANGLES; @@ -537,8 +585,18 @@ public: * Sets whether stencil testing is enabled. **/ virtual void setStencilTest(CompareMode compare, int value) = 0; - virtual void setStencilTest() = 0; - void getStencilTest(CompareMode &compare, int &value); + void setStencilTest(); + void getStencilTest(CompareMode &compare, int &value) const; + + virtual void setDepthMode(CompareMode compare, bool write) = 0; + void setDepthMode(); + void getDepthMode(CompareMode &compare, bool &write) const; + + void setMeshCullMode(CullMode cull); + CullMode getMeshCullMode() const; + + virtual void setFrontFaceWinding(vertex::Winding winding) = 0; + vertex::Winding getFrontFaceWinding() const; /** * Sets the enabled color components when rendering. @@ -769,8 +827,8 @@ public: Vector2 transformPoint(Vector2 point); Vector2 inverseTransformPoint(Vector2 point); - virtual void draw(PrimitiveType primtype, int vertexstart, int vertexcount, int instancecount, const vertex::Attributes &attribs, const vertex::Buffers &buffers, Texture *texture) = 0; - virtual void drawIndexed(PrimitiveType primtype, int indexcount, int instancecount, IndexDataType datatype, Resource *indexbuffer, size_t indexoffset, const vertex::Attributes &attribs, const vertex::Buffers &buffers, Texture *texture) = 0; + virtual void draw(const DrawCommand &cmd) = 0; + virtual void draw(const DrawIndexedCommand &cmd) = 0; void flushStreamDraws(); StreamVertexData requestStreamDraw(const StreamDrawCommand &command); @@ -852,6 +910,12 @@ protected: CompareMode stencilCompare = COMPARE_ALWAYS; int stencilTestValue = 0; + CompareMode depthTest = COMPARE_ALWAYS; + bool depthWrite = false; + + CullMode meshCullMode = CULL_NONE; + vertex::Winding winding = vertex::WINDING_CCW; + StrongRef font; StrongRef shader; diff --git a/src/modules/graphics/Mesh.cpp b/src/modules/graphics/Mesh.cpp index ded75f39c..d666b6bb7 100644 --- a/src/modules/graphics/Mesh.cpp +++ b/src/modules/graphics/Mesh.cpp @@ -633,37 +633,49 @@ void Mesh::drawInstanced(Graphics *gfx, const Matrix4 &m, int instancecount) Graphics::TempTransform transform(gfx, m); - int start = 0; - int count = 0; - if (useIndexBuffer && ibo != nullptr && indexCount > 0) { // Make sure the index buffer isn't mapped (sends data to GPU if needed.) ibo->unmap(); - start = std::min(std::max(0, rangeStart), (int) indexCount - 1); + Graphics::DrawIndexedCommand cmd(&attributes, &buffers, ibo); - count = (int) indexCount; + cmd.primitiveType = primitiveType; + cmd.indexType = indexDataType; + cmd.instanceCount = instancecount; + cmd.texture = texture; + cmd.cullMode = gfx->getMeshCullMode(); + + int start = std::min(std::max(0, rangeStart), (int) indexCount - 1); + cmd.indexBufferOffset = start * vertex::getIndexDataSize(indexDataType); + + cmd.indexCount = (int) indexCount; if (rangeCount > 0) - count = std::min(count, rangeCount); + cmd.indexCount = std::min(cmd.indexCount, rangeCount); - count = std::min(count, (int) indexCount - start); + cmd.indexCount = std::min(cmd.indexCount, (int) indexCount - start); - size_t offset = start * vertex::getIndexDataSize(indexDataType); - if (count > 0) - gfx->drawIndexed(primitiveType, count, instancecount, indexDataType, ibo, offset, attributes, buffers, texture); + if (cmd.indexCount > 0) + gfx->draw(cmd); } - else + else if (vertexCount > 0) { - start = std::min(std::max(0, rangeStart), (int) vertexCount - 1); + Graphics::DrawCommand cmd(&attributes, &buffers); - count = (int) vertexCount; + cmd.primitiveType = primitiveType; + cmd.vertexStart = std::min(std::max(0, rangeStart), (int) vertexCount - 1); + + cmd.vertexCount = (int) vertexCount; if (rangeCount > 0) - count = std::min(count, rangeCount); + cmd.vertexCount = std::min(cmd.vertexCount, rangeCount); - count = std::min(count, (int) vertexCount - start); + cmd.vertexCount = std::min(cmd.vertexCount, (int) vertexCount - cmd.vertexStart); + cmd.instanceCount = instancecount; + cmd.texture = texture; + cmd.cullMode = gfx->getMeshCullMode(); - gfx->draw(primitiveType, start, count, instancecount, attributes, buffers, texture); + if (cmd.vertexCount > 0) + gfx->draw(cmd); } } diff --git a/src/modules/graphics/ParticleSystem.cpp b/src/modules/graphics/ParticleSystem.cpp index fb2041d53..d5cf597f6 100644 --- a/src/modules/graphics/ParticleSystem.cpp +++ b/src/modules/graphics/ParticleSystem.cpp @@ -1101,8 +1101,13 @@ void ParticleSystem::draw(Graphics *gfx, const Matrix4 &m) Graphics::TempTransform transform(gfx, m); - int count = quadIndices.getIndexCount(getCount()); - gfx->drawIndexed(PRIMITIVE_TRIANGLES, count, 1, quadIndices.getType(), quadIndices.getBuffer(), 0, vertexAttributes, vertexbuffers, texture); + Graphics::DrawIndexedCommand cmd(&vertexAttributes, &vertexbuffers, quadIndices.getBuffer()); + cmd.primitiveType = PRIMITIVE_TRIANGLES; + cmd.indexBufferOffset = 0; + cmd.indexCount = (int) quadIndices.getIndexCount(pCount); + cmd.indexType = quadIndices.getType(); + cmd.texture = texture; + gfx->draw(cmd); } bool ParticleSystem::getConstant(const char *in, AreaSpreadDistribution &out) diff --git a/src/modules/graphics/SpriteBatch.cpp b/src/modules/graphics/SpriteBatch.cpp index 5e74e17fb..790dfe3ab 100644 --- a/src/modules/graphics/SpriteBatch.cpp +++ b/src/modules/graphics/SpriteBatch.cpp @@ -385,6 +385,8 @@ void SpriteBatch::draw(Graphics *gfx, const Matrix4 &m) } } + Graphics::DrawIndexedCommand cmd(&attributes, &buffers, quad_indices.getBuffer()); + int start = std::min(std::max(0, range_start), next - 1); int count = next; @@ -393,13 +395,15 @@ void SpriteBatch::draw(Graphics *gfx, const Matrix4 &m) count = std::min(count, next - start); - size_t indexbytestart = quad_indices.getIndexCount(start) * quad_indices.getElementSize(); - size_t indexcount = quad_indices.getIndexCount(count); + cmd.indexBufferOffset = quad_indices.getIndexCount(start) * quad_indices.getElementSize(); + cmd.indexCount = (int) quad_indices.getIndexCount(count); + cmd.indexType = quad_indices.getType(); + cmd.texture = texture; Graphics::TempTransform transform(gfx, m); if (count > 0) - gfx->drawIndexed(PRIMITIVE_TRIANGLES, indexcount, 1, quad_indices.getType(), quad_indices.getBuffer(), indexbytestart, attributes, buffers, texture); + gfx->draw(cmd); } } // graphics diff --git a/src/modules/graphics/Text.cpp b/src/modules/graphics/Text.cpp index eb19a47c9..d98b38c44 100644 --- a/src/modules/graphics/Text.cpp +++ b/src/modules/graphics/Text.cpp @@ -266,15 +266,16 @@ void Text::draw(Graphics *gfx, const Matrix4 &m) Graphics::TempTransform transform(gfx, m); + Graphics::DrawIndexedCommand drawcmd(&vertexAttributes, &vertexBuffers, quadIndices.getBuffer()); + drawcmd.indexType = quadIndices.getType(); size_t elemsize = quadIndices.getElementSize(); - IndexDataType datatype = quadIndices.getType(); - Buffer *indexbuffer = quadIndices.getBuffer(); for (const Font::DrawCommand &cmd : draw_commands) { - int count = (cmd.vertexcount / 4) * 6; - size_t offset = (cmd.startvertex / 4) * 6 * elemsize; - gfx->drawIndexed(PRIMITIVE_TRIANGLES, count, 1, datatype, indexbuffer, offset, vertexAttributes, vertexBuffers, cmd.texture); + drawcmd.indexCount = (cmd.vertexcount / 4) * 6; + drawcmd.indexBufferOffset = (cmd.startvertex / 4) * 6 * elemsize; + drawcmd.texture = cmd.texture; + gfx->draw(drawcmd); } } diff --git a/src/modules/graphics/depthstencil.cpp b/src/modules/graphics/depthstencil.cpp index 0fca4748c..68c193aeb 100644 --- a/src/modules/graphics/depthstencil.cpp +++ b/src/modules/graphics/depthstencil.cpp @@ -34,19 +34,12 @@ CompareMode getReversedCompareMode(CompareMode mode) return COMPARE_GREATER; case COMPARE_LEQUAL: return COMPARE_GEQUAL; - case COMPARE_EQUAL: - return COMPARE_EQUAL; case COMPARE_GEQUAL: return COMPARE_LEQUAL; case COMPARE_GREATER: return COMPARE_LESS; - case COMPARE_NOTEQUAL: - return COMPARE_NOTEQUAL; - case COMPARE_ALWAYS: - return COMPARE_ALWAYS; - case COMPARE_MAX_ENUM: default: - return COMPARE_MAX_ENUM; + return mode; } } @@ -71,6 +64,7 @@ static StringMap::Entry compareModeEntries[] = { "greater", COMPARE_GREATER }, { "notequal", COMPARE_NOTEQUAL }, { "always", COMPARE_ALWAYS }, + { "never", COMPARE_NEVER }, }; static StringMap compareModes(compareModeEntries, sizeof(compareModeEntries)); diff --git a/src/modules/graphics/depthstencil.h b/src/modules/graphics/depthstencil.h index d7d434ddc..4905137d1 100644 --- a/src/modules/graphics/depthstencil.h +++ b/src/modules/graphics/depthstencil.h @@ -48,6 +48,7 @@ enum CompareMode COMPARE_GREATER, COMPARE_NOTEQUAL, COMPARE_ALWAYS, + COMPARE_NEVER, COMPARE_MAX_ENUM }; diff --git a/src/modules/graphics/opengl/Canvas.cpp b/src/modules/graphics/opengl/Canvas.cpp index 34eab614c..6c35ff05e 100644 --- a/src/modules/graphics/opengl/Canvas.cpp +++ b/src/modules/graphics/opengl/Canvas.cpp @@ -68,9 +68,16 @@ static GLenum createFBO(GLuint &framebuffer, TextureType texType, PixelFormat fo if (isPixelFormatDepthStencil(format)) { + bool hadDepthWrites = gl.hasDepthWrites(); + if (!hadDepthWrites) // glDepthMask also affects glClear. + gl.setDepthWrites(true); + gl.clearDepth(1.0); glClearStencil(0); glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + + if (!hadDepthWrites) + gl.setDepthWrites(hadDepthWrites); } else { @@ -128,9 +135,16 @@ static bool createRenderbuffer(int width, int height, int &samples, PixelFormat { if (isPixelFormatDepthStencil(pixelformat)) { + bool hadDepthWrites = gl.hasDepthWrites(); + if (!hadDepthWrites) // glDepthMask also affects glClear. + gl.setDepthWrites(true); + gl.clearDepth(1.0); glClearStencil(0); glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + + if (!hadDepthWrites) + gl.setDepthWrites(hadDepthWrites); } else { diff --git a/src/modules/graphics/opengl/Graphics.cpp b/src/modules/graphics/opengl/Graphics.cpp index 7939690be..0ea1eca25 100644 --- a/src/modules/graphics/opengl/Graphics.cpp +++ b/src/modules/graphics/opengl/Graphics.cpp @@ -149,7 +149,7 @@ void Graphics::setViewportSize(int width, int height, int pixelwidth, int pixelh setScissor(states.back().scissorRect); // Set up the projection matrix - projectionMatrix = Matrix4::ortho(0.0, (float) width, (float) height, 0.0); + projectionMatrix = Matrix4::ortho(0.0, (float) width, (float) height, 0.0, -10.0f, 10.0f); } } @@ -206,7 +206,7 @@ bool Graphics::setMode(int width, int height, int pixelwidth, int pixelheight, b || GLAD_ES_VERSION_3_0 || GLAD_EXT_sRGB) { if (GLAD_VERSION_1_0 || GLAD_EXT_sRGB_write_control) - gl.setFramebufferSRGB(isGammaCorrect()); + gl.setEnableState(OpenGL::ENABLE_FRAMEBUFFER_SRGB, isGammaCorrect()); } else setGammaCorrect(false); @@ -316,38 +316,40 @@ void Graphics::setActive(bool enable) active = enable; } -void Graphics::draw(PrimitiveType primtype, int vertexstart, int vertexcount, int instancecount, const vertex::Attributes &attribs, const vertex::Buffers &buffers, love::graphics::Texture *texture) +void Graphics::draw(const DrawCommand &cmd) { gl.prepareDraw(); - gl.setVertexAttributes(attribs, buffers); - gl.bindTextureToUnit(texture, 0, false); + gl.setVertexAttributes(*cmd.attributes, *cmd.buffers); + gl.bindTextureToUnit(cmd.texture, 0, false); + gl.setCullMode(cmd.cullMode); - GLenum glprimitivetype = OpenGL::getGLPrimitiveType(primtype); + GLenum glprimitivetype = OpenGL::getGLPrimitiveType(cmd.primitiveType); - if (instancecount > 1) - glDrawArraysInstanced(glprimitivetype, vertexstart, vertexcount, instancecount); + if (cmd.instanceCount > 1) + glDrawArraysInstanced(glprimitivetype, cmd.vertexStart, cmd.vertexCount, cmd.instanceCount); else - glDrawArrays(glprimitivetype, vertexstart, vertexcount); + glDrawArrays(glprimitivetype, cmd.vertexStart, cmd.vertexCount); ++drawCalls; } -void Graphics::drawIndexed(PrimitiveType primtype, int indexcount, int instancecount, IndexDataType datatype, Resource *indexbuffer, size_t indexoffset, const vertex::Attributes &attribs, const vertex::Buffers &buffers, love::graphics::Texture *texture) +void Graphics::draw(const DrawIndexedCommand &cmd) { gl.prepareDraw(); - gl.setVertexAttributes(attribs, buffers); - gl.bindTextureToUnit(texture, 0, false); + gl.setVertexAttributes(*cmd.attributes, *cmd.buffers); + gl.bindTextureToUnit(cmd.texture, 0, false); + gl.setCullMode(cmd.cullMode); - const void *gloffset = BUFFER_OFFSET(indexoffset); - GLenum glprimitivetype = OpenGL::getGLPrimitiveType(primtype); - GLenum gldatatype = OpenGL::getGLIndexDataType(datatype); + const void *gloffset = BUFFER_OFFSET(cmd.indexBufferOffset); + GLenum glprimitivetype = OpenGL::getGLPrimitiveType(cmd.primitiveType); + GLenum gldatatype = OpenGL::getGLIndexDataType(cmd.indexType); - gl.bindBuffer(BUFFER_INDEX, indexbuffer->getHandle()); + gl.bindBuffer(BUFFER_INDEX, cmd.indexBuffer->getHandle()); - if (instancecount > 1) - glDrawElementsInstanced(glprimitivetype, indexcount, gldatatype, gloffset, instancecount); + if (cmd.instanceCount > 1) + glDrawElementsInstanced(glprimitivetype, cmd.indexCount, gldatatype, gloffset, cmd.instanceCount); else - glDrawElements(glprimitivetype, indexcount, gldatatype, gloffset); + glDrawElements(glprimitivetype, cmd.indexCount, gldatatype, gloffset); ++drawCalls; } @@ -424,20 +426,22 @@ void Graphics::setCanvasInternal(const RenderTargets &rts, int w, int h, int pix gl.setViewport({0, 0, pixelw, pixelh}); + // Flip front face winding when rendering to a canvas, since our projection + // matrix is flipped. + glFrontFace(state.winding == vertex::WINDING_CW ? GL_CCW : GL_CW); + // Re-apply the scissor if it was active, since the rectangle passed to // glScissor is affected by the viewport dimensions. if (state.scissor) setScissor(state.scissorRect); - projectionMatrix = Matrix4::ortho(0.0, (float) w, 0.0, (float) h); + projectionMatrix = Matrix4::ortho(0.0, (float) w, 0.0, (float) h, -10.0f, 10.0f); // Make sure the correct sRGB setting is used when drawing to the canvases. if (GLAD_VERSION_1_0 || GLAD_EXT_sRGB_write_control) { - if (hasSRGBcanvas && !gl.hasFramebufferSRGB()) - gl.setFramebufferSRGB(true); - else if (!hasSRGBcanvas && gl.hasFramebufferSRGB()) - gl.setFramebufferSRGB(false); + if (hasSRGBcanvas != gl.isStateEnabled(OpenGL::ENABLE_FRAMEBUFFER_SRGB)) + gl.setEnableState(OpenGL::ENABLE_FRAMEBUFFER_SRGB, hasSRGBcanvas); } } @@ -453,6 +457,10 @@ void Graphics::setCanvas() flushStreamDraws(); endPass(); + // Re-apply the correct front face winding, since it may have been flipped + // if we were previously rendering to a canvas. + glFrontFace(state.winding == vertex::WINDING_CW ? GL_CW : GL_CCW); + state.renderTargets = RenderTargetsStrongRef(); gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, gl.getDefaultFBO()); @@ -466,14 +474,12 @@ void Graphics::setCanvas() // The projection matrix is flipped compared to rendering to a canvas, due // to OpenGL considering (0,0) bottom-left instead of top-left. - projectionMatrix = Matrix4::ortho(0.0, (float) width, (float) height, 0.0); + projectionMatrix = Matrix4::ortho(0.0, (float) width, (float) height, 0.0, -10.0f, 10.0f); if (GLAD_VERSION_1_0 || GLAD_EXT_sRGB_write_control) { - if (isGammaCorrect() && !gl.hasFramebufferSRGB()) - gl.setFramebufferSRGB(true); - else if (!isGammaCorrect() && gl.hasFramebufferSRGB()) - gl.setFramebufferSRGB(false); + if (isGammaCorrect() != gl.isStateEnabled(OpenGL::ENABLE_FRAMEBUFFER_SRGB)) + gl.setEnableState(OpenGL::ENABLE_FRAMEBUFFER_SRGB, isGammaCorrect()); } canvasSwitchCount++; @@ -571,8 +577,13 @@ void Graphics::clear(OptionalColorf c, OptionalInt stencil, OptionalDouble depth flags |= GL_STENCIL_BUFFER_BIT; } + bool hadDepthWrites = gl.hasDepthWrites(); + if (depth.hasValue) { + if (!hadDepthWrites) // glDepthMask also affects glClear. + gl.setDepthWrites(true); + gl.clearDepth(depth.value); flags |= GL_DEPTH_BUFFER_BIT; } @@ -580,6 +591,9 @@ void Graphics::clear(OptionalColorf c, OptionalInt stencil, OptionalDouble depth if (flags != 0) glClear(flags); + if (depth.hasValue && !hadDepthWrites) + gl.setDepthWrites(hadDepthWrites); + if (c.hasValue && gl.bugs.clearRequiresDriverTextureStateUpdate && Shader::current) { // This seems to be enough to fix the bug for me. Other methods I've @@ -652,8 +666,13 @@ void Graphics::clear(const std::vector &colors, OptionalInt sten flags |= GL_STENCIL_BUFFER_BIT; } + bool hadDepthWrites = gl.hasDepthWrites(); + if (depth.hasValue) { + if (!hadDepthWrites) // glDepthMask also affects glClear. + gl.setDepthWrites(true); + gl.clearDepth(depth.value); flags |= GL_DEPTH_BUFFER_BIT; } @@ -661,6 +680,9 @@ void Graphics::clear(const std::vector &colors, OptionalInt sten if (flags != 0) glClear(flags); + if (depth.hasValue && !hadDepthWrites) + gl.setDepthWrites(hadDepthWrites); + if (gl.bugs.clearRequiresDriverTextureStateUpdate && Shader::current) { // This seems to be enough to fix the bug for me. Other methods I've @@ -986,7 +1008,8 @@ void Graphics::setScissor(const Rect &rect) DisplayState &state = states.back(); - glEnable(GL_SCISSOR_TEST); + if (!gl.isStateEnabled(OpenGL::ENABLE_SCISSOR_TEST)) + gl.setEnableState(OpenGL::ENABLE_SCISSOR_TEST, true); double dpiscale = getCurrentDPIScale(); @@ -1009,7 +1032,9 @@ void Graphics::setScissor() flushStreamDraws(); states.back().scissor = false; - glDisable(GL_SCISSOR_TEST); + + if (gl.isStateEnabled(OpenGL::ENABLE_SCISSOR_TEST)) + gl.setEnableState(OpenGL::ENABLE_SCISSOR_TEST, false); } void Graphics::drawToStencilBuffer(StencilAction action, int value) @@ -1055,7 +1080,9 @@ void Graphics::drawToStencilBuffer(StencilAction action, int value) } // The stencil test must be enabled in order to write to the stencil buffer. - glEnable(GL_STENCIL_TEST); + if (!gl.isStateEnabled(OpenGL::ENABLE_STENCIL_TEST)) + gl.setEnableState(OpenGL::ENABLE_STENCIL_TEST, true); + glStencilFunc(GL_ALWAYS, value, 0xFFFFFFFF); glStencilOp(GL_KEEP, GL_KEEP, glaction); } @@ -1093,7 +1120,8 @@ void Graphics::setStencilTest(CompareMode compare, int value) if (compare == COMPARE_ALWAYS) { - glDisable(GL_STENCIL_TEST); + if (gl.isStateEnabled(OpenGL::ENABLE_STENCIL_TEST)) + gl.setEnableState(OpenGL::ENABLE_STENCIL_TEST, false); return; } @@ -1107,14 +1135,48 @@ void Graphics::setStencilTest(CompareMode compare, int value) **/ GLenum glcompare = OpenGL::getGLCompareMode(getReversedCompareMode(compare)); - glEnable(GL_STENCIL_TEST); + if (!gl.isStateEnabled(OpenGL::ENABLE_STENCIL_TEST)) + gl.setEnableState(OpenGL::ENABLE_STENCIL_TEST, true); + glStencilFunc(glcompare, value, 0xFFFFFFFF); glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); } -void Graphics::setStencilTest() +void Graphics::setDepthMode(CompareMode compare, bool write) { - setStencilTest(COMPARE_ALWAYS, 0); + DisplayState &state = states.back(); + + if (state.depthTest != compare || state.depthWrite != write) + flushStreamDraws(); + + state.depthTest = compare; + state.depthWrite = write; + + bool depthenable = compare != COMPARE_ALWAYS || write; + + if (depthenable != gl.isStateEnabled(OpenGL::ENABLE_DEPTH_TEST)) + gl.setEnableState(OpenGL::ENABLE_DEPTH_TEST, depthenable); + + if (depthenable) + { + glDepthFunc(OpenGL::getGLCompareMode(compare)); + glDepthMask(write ? GL_TRUE : GL_FALSE); + } +} + +void Graphics::setFrontFaceWinding(vertex::Winding winding) +{ + DisplayState &state = states.back(); + + if (state.winding != winding) + flushStreamDraws(); + + state.winding = winding; + + if (isCanvasActive()) + winding = winding == vertex::WINDING_CW ? vertex::WINDING_CCW : vertex::WINDING_CW; + + glFrontFace(winding == vertex::WINDING_CW ? GL_CW : GL_CCW); } void Graphics::setColor(Colorf c) @@ -1142,12 +1204,6 @@ void Graphics::setBlendMode(BlendMode mode, BlendAlpha alphamode) if (mode != states.back().blendMode || alphamode != states.back().blendAlphaMode) flushStreamDraws(); - GLenum func = GL_FUNC_ADD; - GLenum srcRGB = GL_ONE; - GLenum srcA = GL_ONE; - GLenum dstRGB = GL_ZERO; - GLenum dstA = GL_ZERO; - if (mode == BLEND_LIGHTEN || mode == BLEND_DARKEN) { if (!capabilities.features[FEATURE_LIGHTEN]) @@ -1170,6 +1226,12 @@ void Graphics::setBlendMode(BlendMode mode, BlendAlpha alphamode) } } + GLenum func = GL_FUNC_ADD; + GLenum srcRGB = GL_ONE; + GLenum srcA = GL_ONE; + GLenum dstRGB = GL_ZERO; + GLenum dstA = GL_ZERO; + switch (mode) { case BLEND_ALPHA: diff --git a/src/modules/graphics/opengl/Graphics.h b/src/modules/graphics/opengl/Graphics.h index bd5c4f75d..46f1e67c1 100644 --- a/src/modules/graphics/opengl/Graphics.h +++ b/src/modules/graphics/opengl/Graphics.h @@ -69,8 +69,8 @@ public: void setActive(bool active) override; - void draw(PrimitiveType primtype, int vertexstart, int vertexcount, int instancecount, const vertex::Attributes &attribs, const vertex::Buffers &buffers, Texture *texture) override; - void drawIndexed(PrimitiveType primtype, int indexcount, int instancecount, IndexDataType datatype, Resource *indexbuffer, size_t indexoffset, const vertex::Attributes &attribs, const vertex::Buffers &buffers, Texture *texture) override; + void draw(const DrawCommand &cmd) override; + void draw(const DrawIndexedCommand &cmd) override; void clear(OptionalColorf color, OptionalInt stencil, OptionalDouble depth) override; void clear(const std::vector &colors, OptionalInt stencil, OptionalDouble depth) override; @@ -90,7 +90,10 @@ public: void stopDrawToStencilBuffer() override; void setStencilTest(CompareMode compare, int value) override; - void setStencilTest() override; + + void setDepthMode(CompareMode compare, bool write) override; + + void setFrontFaceWinding(vertex::Winding winding) override; void setColorMask(ColorMask mask) override; diff --git a/src/modules/graphics/opengl/OpenGL.cpp b/src/modules/graphics/opengl/OpenGL.cpp index 322ac57b4..189195c9f 100644 --- a/src/modules/graphics/opengl/OpenGL.cpp +++ b/src/modules/graphics/opengl/OpenGL.cpp @@ -191,13 +191,22 @@ void OpenGL::setupContext() state.boundFramebuffers[i] = std::numeric_limits::max(); bindFramebuffer(FRAMEBUFFER_ALL, getDefaultFBO()); + setEnableState(ENABLE_DEPTH_TEST, state.enableState[ENABLE_DEPTH_TEST]); + setEnableState(ENABLE_STENCIL_TEST, state.enableState[ENABLE_STENCIL_TEST]); + setEnableState(ENABLE_SCISSOR_TEST, state.enableState[ENABLE_SCISSOR_TEST]); + setEnableState(ENABLE_FACE_CULL, state.enableState[ENABLE_FACE_CULL]); + if (GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_sRGB || GLAD_EXT_framebuffer_sRGB || GLAD_EXT_sRGB_write_control) { - state.framebufferSRGBEnabled = (glIsEnabled(GL_FRAMEBUFFER_SRGB) == GL_TRUE); + setEnableState(ENABLE_FRAMEBUFFER_SRGB, state.enableState[ENABLE_FRAMEBUFFER_SRGB]); } else - state.framebufferSRGBEnabled = false; + state.enableState[ENABLE_FRAMEBUFFER_SRGB] = false; + + GLint faceCull = GL_BACK; + glGetIntegerv(GL_CULL_FACE_MODE, &faceCull); + state.faceCullMode = faceCull; for (int i = 0; i < (int) BUFFER_MAX_ENUM; i++) { @@ -228,6 +237,8 @@ void OpenGL::setupContext() glActiveTexture(GL_TEXTURE0); state.curTextureUnit = 0; + setDepthWrites(state.depthWritesEnabled); + createDefaultTexture(); contextInitialized = true; @@ -673,6 +684,24 @@ void OpenGL::setVertexAttributes(const vertex::Attributes &attributes, const ver glVertexAttrib4f(ATTRIB_COLOR, 1.0f, 1.0f, 1.0f, 1.0f); } +void OpenGL::setCullMode(CullMode mode) +{ + bool enabled = mode != CULL_NONE; + + if (enabled != isStateEnabled(ENABLE_FACE_CULL)) + setEnableState(ENABLE_FACE_CULL, enabled); + + if (enabled) + { + GLenum glmode = mode == CULL_BACK ? GL_BACK : GL_FRONT; + if (glmode != state.faceCullMode) + { + glCullFace(glmode); + state.faceCullMode = glmode; + } + } +} + void OpenGL::clearDepth(double value) { if (GLAD_ES_VERSION_2_0) @@ -729,19 +758,42 @@ float OpenGL::getPointSize() const return state.pointSize; } -void OpenGL::setFramebufferSRGB(bool enable) +void OpenGL::setEnableState(EnableState enablestate, bool enable) { - if (enable) - glEnable(GL_FRAMEBUFFER_SRGB); - else - glDisable(GL_FRAMEBUFFER_SRGB); + GLenum glstate = GL_NONE; - state.framebufferSRGBEnabled = enable; + switch (enablestate) + { + case ENABLE_DEPTH_TEST: + glstate = GL_DEPTH_TEST; + break; + case ENABLE_STENCIL_TEST: + glstate = GL_STENCIL_TEST; + break; + case ENABLE_SCISSOR_TEST: + glstate = GL_SCISSOR_TEST; + break; + case ENABLE_FACE_CULL: + glstate = GL_CULL_FACE; + break; + case ENABLE_FRAMEBUFFER_SRGB: + glstate = GL_FRAMEBUFFER_SRGB; + break; + case ENABLE_MAX_ENUM: + break; + } + + if (enable) + glEnable(glstate); + else + glDisable(glstate); + + state.enableState[enablestate] = enable; } -bool OpenGL::hasFramebufferSRGB() const +bool OpenGL::isStateEnabled(EnableState enablestate) const { - return state.framebufferSRGBEnabled; + return state.enableState[enablestate]; } void OpenGL::bindFramebuffer(FramebufferTarget target, GLuint framebuffer) @@ -816,6 +868,17 @@ void OpenGL::framebufferTexture(GLenum attachment, TextureType texType, GLuint t } } +void OpenGL::setDepthWrites(bool enable) +{ + glDepthMask(enable ? GL_TRUE : GL_FALSE); + state.depthWritesEnabled = enable; +} + +bool OpenGL::hasDepthWrites() const +{ + return state.depthWritesEnabled; +} + void OpenGL::useProgram(GLuint program) { glUseProgram(program); @@ -954,7 +1017,6 @@ GLint OpenGL::getGLWrapMode(Texture::WrapMode wmode) case Texture::WRAP_MIRRORED_REPEAT: return GL_MIRRORED_REPEAT; } - } GLint OpenGL::getGLCompareMode(CompareMode mode) @@ -975,6 +1037,8 @@ GLint OpenGL::getGLCompareMode(CompareMode mode) return GL_NOTEQUAL; case COMPARE_ALWAYS: return GL_ALWAYS; + case COMPARE_NEVER: + return GL_NEVER; default: return GL_NEVER; } diff --git a/src/modules/graphics/opengl/OpenGL.h b/src/modules/graphics/opengl/OpenGL.h index be671af19..522aa78c0 100644 --- a/src/modules/graphics/opengl/OpenGL.h +++ b/src/modules/graphics/opengl/OpenGL.h @@ -92,6 +92,16 @@ public: FRAMEBUFFER_ALL = (FRAMEBUFFER_READ | FRAMEBUFFER_DRAW), }; + enum EnableState + { + ENABLE_DEPTH_TEST, + ENABLE_STENCIL_TEST, + ENABLE_SCISSOR_TEST, + ENABLE_FACE_CULL, + ENABLE_FRAMEBUFFER_SRGB, + ENABLE_MAX_ENUM + }; + struct TextureFormat { GLenum internalformat = 0; @@ -204,6 +214,11 @@ public: **/ void setVertexAttributes(const vertex::Attributes &attributes, const vertex::Buffers &buffers); + /** + * Wrapper for glCullFace which eliminates redundant state setting. + **/ + void setCullMode(CullMode mode); + /** * Wrapper for glClearDepth and glClearDepthf. **/ @@ -236,10 +251,10 @@ public: float getPointSize() const; /** - * Calls glEnable/glDisable(GL_FRAMEBUFFER_SRGB). + * State-tracked version of glEnable. **/ - void setFramebufferSRGB(bool enable); - bool hasFramebufferSRGB() const; + void setEnableState(EnableState state, bool enable); + bool isStateEnabled(EnableState state) const; /** * Binds a Framebuffer Object to the specified target. @@ -250,6 +265,12 @@ public: void framebufferTexture(GLenum attachment, TextureType texType, GLuint texture, int level, int layer = 0, int face = 0); + /** + * Calls glDepthMask. + **/ + void setDepthWrites(bool enable); + bool hasDepthWrites() const; + /** * Calls glUseProgram. **/ @@ -415,7 +436,10 @@ private: // Texture unit state (currently bound texture for each texture unit.) std::vector boundTextures[TEXTURE_MAX_ENUM]; - // Currently active texture unit. + bool enableState[ENABLE_MAX_ENUM]; + + GLenum faceCullMode; + int curTextureUnit; uint32 enabledAttribArrays; @@ -429,9 +453,9 @@ private: float pointSize; - GLuint boundFramebuffers[2]; + bool depthWritesEnabled; - bool framebufferSRGBEnabled; + GLuint boundFramebuffers[2]; GLuint defaultTexture[TEXTURE_MAX_ENUM]; diff --git a/src/modules/graphics/vertex.cpp b/src/modules/graphics/vertex.cpp index c16245ca5..80d2cd86d 100644 --- a/src/modules/graphics/vertex.cpp +++ b/src/modules/graphics/vertex.cpp @@ -327,6 +327,23 @@ static StringMap::Entry dataTypeEntries[] = static StringMap dataTypes(dataTypeEntries, sizeof(dataTypeEntries)); +static StringMap::Entry cullModeEntries[] = +{ + { "none", CULL_NONE }, + { "back", CULL_BACK }, + { "front", CULL_FRONT }, +}; + +static StringMap cullModes(cullModeEntries, sizeof(cullModeEntries)); + +static StringMap::Entry windingEntries[] = +{ + { "cw", WINDING_CW }, + { "ccw", WINDING_CCW }, +}; + +static StringMap windings(windingEntries, sizeof(windingEntries)); + bool getConstant(const char *in, VertexAttribID &out) { return attribNames.find(in, out); @@ -412,6 +429,36 @@ std::vector getConstants(DataType) return dataTypes.getNames(); } +bool getConstant(const char *in, CullMode &out) +{ + return cullModes.find(in, out); +} + +bool getConstant(CullMode in, const char *&out) +{ + return cullModes.find(in, out); +} + +std::vector getConstants(CullMode) +{ + return cullModes.getNames(); +} + +bool getConstant(const char *in, Winding &out) +{ + return windings.find(in, out); +} + +bool getConstant(Winding in, const char *&out) +{ + return windings.find(in, out); +} + +std::vector getConstants(Winding) +{ + return windings.getNames(); +} + } // vertex } // graphics } // love diff --git a/src/modules/graphics/vertex.h b/src/modules/graphics/vertex.h index b2d06ba07..18761f072 100644 --- a/src/modules/graphics/vertex.h +++ b/src/modules/graphics/vertex.h @@ -86,6 +86,14 @@ enum AttributeStep STEP_MAX_ENUM }; +enum CullMode +{ + CULL_NONE, + CULL_BACK, + CULL_FRONT, + CULL_MAX_ENUM +}; + namespace vertex { @@ -106,6 +114,13 @@ enum DataType DATA_MAX_ENUM }; +enum Winding +{ + WINDING_CW, + WINDING_CCW, + WINDING_MAX_ENUM +}; + enum class TriangleIndexMode { NONE, @@ -305,6 +320,14 @@ bool getConstant(const char *in, DataType &out); bool getConstant(DataType in, const char *&out); std::vector getConstants(DataType); +bool getConstant(const char *in, CullMode &out); +bool getConstant(CullMode in, const char *&out); +std::vector getConstants(CullMode); + +bool getConstant(const char *in, Winding &out); +bool getConstant(Winding in, const char *&out); +std::vector getConstants(Winding); + } // vertex typedef vertex::XYf_STf_RGBAub Vertex; diff --git a/src/modules/graphics/wrap_Graphics.cpp b/src/modules/graphics/wrap_Graphics.cpp index a336a67ee..db8794bcd 100644 --- a/src/modules/graphics/wrap_Graphics.cpp +++ b/src/modules/graphics/wrap_Graphics.cpp @@ -1962,6 +1962,84 @@ int w_getPointSize(lua_State *L) return 1; } +int w_setDepthMode(lua_State *L) +{ + if (lua_isnoneornil(L, 1) && lua_isnoneornil(L, 2)) + luax_catchexcept(L, [&]() { instance()->setDepthMode(); }); + else + { + CompareMode compare = COMPARE_ALWAYS; + const char *str = luaL_checkstring(L, 1); + bool write = luax_checkboolean(L, 2); + + if (!getConstant(str, compare)) + return luax_enumerror(L, "compare mode", getConstants(compare), str); + + luax_catchexcept(L, [&]() { instance()->setDepthMode(compare, write); }); + } + + return 0; +} + +int w_getDepthMode(lua_State *L) +{ + CompareMode compare = COMPARE_ALWAYS; + bool write = false; + instance()->getDepthMode(compare, write); + + const char *str; + if (!getConstant(compare, str)) + return luaL_error(L, "Unknown compare mode"); + + lua_pushstring(L, str); + luax_pushboolean(L, write); + return 2; +} + +int w_setMeshCullMode(lua_State *L) +{ + const char *str = luaL_checkstring(L, 1); + CullMode mode; + + if (!vertex::getConstant(str, mode)) + return luax_enumerror(L, "cull mode", vertex::getConstants(mode), str); + + luax_catchexcept(L, [&]() { instance()->setMeshCullMode(mode); }); + return 0; +} + +int w_getMeshCullMode(lua_State *L) +{ + CullMode mode = instance()->getMeshCullMode(); + const char *str; + if (!vertex::getConstant(mode, str)) + return luaL_error(L, "Unknown cull mode"); + lua_pushstring(L, str); + return 1; +} + +int w_setFrontFaceWinding(lua_State *L) +{ + const char *str = luaL_checkstring(L, 1); + vertex::Winding winding; + + if (!vertex::getConstant(str, winding)) + return luax_enumerror(L, "vertex winding", vertex::getConstants(winding), str); + + luax_catchexcept(L, [&]() { instance()->setFrontFaceWinding(winding); }); + return 0; +} + +int w_getFrontFaceWinding(lua_State *L) +{ + vertex::Winding winding = instance()->getFrontFaceWinding(); + const char *str; + if (!vertex::getConstant(winding, str)) + return luaL_error(L, "Unknown vertex winding"); + lua_pushstring(L, str); + return 1; +} + int w_setWireframe(lua_State *L) { instance()->setWireframe(luax_checkboolean(L, 1)); @@ -2858,6 +2936,12 @@ static const luaL_Reg functions[] = { "getLineJoin", w_getLineJoin }, { "setPointSize", w_setPointSize }, { "getPointSize", w_getPointSize }, + { "setDepthMode", w_setDepthMode }, + { "getDepthMode", w_getDepthMode }, + { "setMeshCullMode", w_setMeshCullMode }, + { "getMeshCullMode", w_getMeshCullMode }, + { "setFrontFaceWinding", w_setFrontFaceWinding }, + { "getFrontFaceWinding", w_getFrontFaceWinding }, { "setWireframe", w_setWireframe }, { "isWireframe", w_isWireframe },