Add (low level) functionality to allow rendering 3D Meshes.

- Add love.graphics.setDepthMode(testcomparison, enablewrites).
- Add love.graphics.setMeshCullMode.
- Add love.graphics.setFrontFaceWinding.

Depth testing and depth writes require a depth buffer to work. The mesh cull mode controls whether front/back faces of a mesh are culled. The front face winding determines whether a clockwise or counterclockwise-oriented face in a mesh is considered front facing.

--HG--
branch : minor
This commit is contained in:
Alex Szpakowski
2017-12-09 23:34:08 -04:00
parent 915e6c8eec
commit 7354de2059
21 changed files with 572 additions and 113 deletions
+14
View File
@@ -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
{
+104 -42
View File
@@ -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<OptionalColorf> &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<OptionalColorf> &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:
+6 -3
View File
@@ -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<OptionalColorf> &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;
+75 -11
View File
@@ -191,13 +191,22 @@ void OpenGL::setupContext()
state.boundFramebuffers[i] = std::numeric_limits<GLuint>::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;
}
+30 -6
View File
@@ -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<GLuint> 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];