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
@@ -4012,7 +4012,7 @@
08FB7793FE84155DC02AAC07 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0900;
LastUpgradeCheck = 0920;
TargetAttributes = {
FA0B78DC1A958B90000E1D17 = {
CreatedOnToolsVersion = 6.1.1;
@@ -326,7 +326,7 @@
29B97313FDCFA39411CA2CEA /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0900;
LastUpgradeCheck = 0920;
TargetAttributes = {
FA0B7F051A95AAF3000E1D17 = {
CreatedOnToolsVersion = 6.1.1;
+3 -2
View File
@@ -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;
}
+2 -3
View File
@@ -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:
+1 -1
View File
@@ -471,7 +471,7 @@ std::vector<Font::DrawCommand> 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++)
+62 -5
View File
@@ -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++)
{
+68 -4
View File
@@ -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> font;
StrongRef<Shader> shader;
+28 -16
View File
@@ -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);
}
}
+7 -2
View File
@@ -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)
+7 -3
View File
@@ -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
+6 -5
View File
@@ -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);
}
}
+2 -8
View File
@@ -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<CompareMode, COMPARE_MAX_ENUM>::Entry compareModeEntries[] =
{ "greater", COMPARE_GREATER },
{ "notequal", COMPARE_NOTEQUAL },
{ "always", COMPARE_ALWAYS },
{ "never", COMPARE_NEVER },
};
static StringMap<CompareMode, COMPARE_MAX_ENUM> compareModes(compareModeEntries, sizeof(compareModeEntries));
+1
View File
@@ -48,6 +48,7 @@ enum CompareMode
COMPARE_GREATER,
COMPARE_NOTEQUAL,
COMPARE_ALWAYS,
COMPARE_NEVER,
COMPARE_MAX_ENUM
};
+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];
+47
View File
@@ -327,6 +327,23 @@ static StringMap<DataType, DATA_MAX_ENUM>::Entry dataTypeEntries[] =
static StringMap<DataType, DATA_MAX_ENUM> dataTypes(dataTypeEntries, sizeof(dataTypeEntries));
static StringMap<CullMode, CULL_MAX_ENUM>::Entry cullModeEntries[] =
{
{ "none", CULL_NONE },
{ "back", CULL_BACK },
{ "front", CULL_FRONT },
};
static StringMap<CullMode, CULL_MAX_ENUM> cullModes(cullModeEntries, sizeof(cullModeEntries));
static StringMap<Winding, WINDING_MAX_ENUM>::Entry windingEntries[] =
{
{ "cw", WINDING_CW },
{ "ccw", WINDING_CCW },
};
static StringMap<Winding, WINDING_MAX_ENUM> windings(windingEntries, sizeof(windingEntries));
bool getConstant(const char *in, VertexAttribID &out)
{
return attribNames.find(in, out);
@@ -412,6 +429,36 @@ std::vector<std::string> 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<std::string> 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<std::string> getConstants(Winding)
{
return windings.getNames();
}
} // vertex
} // graphics
} // love
+23
View File
@@ -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<std::string> getConstants(DataType);
bool getConstant(const char *in, CullMode &out);
bool getConstant(CullMode in, const char *&out);
std::vector<std::string> getConstants(CullMode);
bool getConstant(const char *in, Winding &out);
bool getConstant(Winding in, const char *&out);
std::vector<std::string> getConstants(Winding);
} // vertex
typedef vertex::XYf_STf_RGBAub Vertex;
+84
View File
@@ -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 },