diff --git a/src/common/Matrix.cpp b/src/common/Matrix.cpp index a8a8c5e68..d403e8b02 100644 --- a/src/common/Matrix.cpp +++ b/src/common/Matrix.cpp @@ -194,5 +194,19 @@ void Matrix::transform(Vertex *dst, const Vertex *src, int size) const } } +Matrix Matrix::ortho(float left, float right, float bottom, float top) +{ + Matrix m; + + m.e[0] = 2.0f / (right - left); + m.e[5] = 2.0f / (top - bottom); + m.e[10] = -1.0; + + m.e[12] = -(right + left) / (right - left); + m.e[13] = -(top + bottom) / (top - bottom); + + return m; +} + } // love diff --git a/src/common/Matrix.h b/src/common/Matrix.h index 2a0091448..0e33b37d9 100644 --- a/src/common/Matrix.h +++ b/src/common/Matrix.h @@ -150,6 +150,12 @@ public: **/ void transform(Vertex *dst, const Vertex *src, int size) const; + /** + * Creates a new orthographic projection matrix with depth in the range of + * [-1, 1]. + **/ + static Matrix ortho(float left, float right, float bottom, float top); + private: /** diff --git a/src/modules/graphics/Graphics.cpp b/src/modules/graphics/Graphics.cpp index 5688e5650..d6e16cd37 100644 --- a/src/modules/graphics/Graphics.cpp +++ b/src/modules/graphics/Graphics.cpp @@ -109,6 +109,16 @@ bool Graphics::getConstant(SystemLimit in, const char *&out) return systemLimits.find(in, out); } +bool Graphics::getConstant(const char *in, StackType &out) +{ + return stackTypes.find(in, out); +} + +bool Graphics::getConstant(StackType in, const char *&out) +{ + return stackTypes.find(in, out); +} + StringMap::Entry Graphics::drawModeEntries[] = { { "line", Graphics::DRAW_LINE }, @@ -192,5 +202,13 @@ StringMap::Entry Graphics::syst StringMap Graphics::systemLimits(Graphics::systemLimitEntries, sizeof(Graphics::systemLimitEntries)); +StringMap::Entry Graphics::stackTypeEntries[] = +{ + {"all", Graphics::STACK_ALL}, + {"transform", Graphics::STACK_TRANSFORM}, +}; + +StringMap Graphics::stackTypes(Graphics::stackTypeEntries, sizeof(Graphics::stackTypeEntries)); + } // graphics } // love diff --git a/src/modules/graphics/Graphics.h b/src/modules/graphics/Graphics.h index ed66de5d2..8cdeb68d2 100644 --- a/src/modules/graphics/Graphics.h +++ b/src/modules/graphics/Graphics.h @@ -112,6 +112,13 @@ public: LIMIT_MAX_ENUM }; + enum StackType + { + STACK_ALL, + STACK_TRANSFORM, + STACK_MAX_ENUM + }; + struct RendererInfo { std::string name; @@ -167,6 +174,9 @@ public: static bool getConstant(const char *in, SystemLimit &out); static bool getConstant(SystemLimit in, const char *&out); + static bool getConstant(const char *in, StackType &out); + static bool getConstant(StackType in, const char *&out); + private: static StringMap::Entry drawModeEntries[]; @@ -193,6 +203,9 @@ private: static StringMap::Entry systemLimitEntries[]; static StringMap systemLimits; + static StringMap::Entry stackTypeEntries[]; + static StringMap stackTypes; + }; // Graphics } // graphics diff --git a/src/modules/graphics/opengl/Canvas.cpp b/src/modules/graphics/opengl/Canvas.cpp index 854dd938e..e6739d695 100644 --- a/src/modules/graphics/opengl/Canvas.cpp +++ b/src/modules/graphics/opengl/Canvas.cpp @@ -594,16 +594,13 @@ void Canvas::unloadVolatile() fbo = depth_stencil = texture = 0; resolve_fbo = msaa_buffer = 0; - for (size_t i = 0; i < attachedCanvases.size(); i++) - attachedCanvases[i]->release(); - attachedCanvases.clear(); } void Canvas::drawv(const Matrix &t, const Vertex *v) { - glPushMatrix(); - glMultMatrixf((const GLfloat *)t.getElements()); + OpenGL::TempTransform transform(gl); + transform.get() *= t; predraw(); @@ -620,8 +617,6 @@ void Canvas::drawv(const Matrix &t, const Vertex *v) glDisableClientState(GL_VERTEX_ARRAY); postdraw(); - - glPopMatrix(); } void Canvas::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) @@ -691,16 +686,8 @@ void Canvas::setupGrab() strategy->bindFBO(fbo); gl.setViewport(OpenGL::Viewport(0, 0, width, height)); - // Reset the projection matrix - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - glLoadIdentity(); - - // Set up orthographic view (no depth) - glOrtho(0.0, width, 0.0, height, -1.0, 1.0); - - // Switch back to modelview matrix - glMatrixMode(GL_MODELVIEW); + // Set up the projection matrix + gl.matrices.projection.push_back(Matrix::ortho(0.0, width, 0.0, height)); // Make sure the correct sRGB setting is used when drawing to the canvas. if (format == FORMAT_SRGB) @@ -754,11 +741,8 @@ void Canvas::startGrab(const std::vector &canvases) // Attach the canvas textures to the active FBO and set up MRTs. strategy->setAttachments(canvases); - for (size_t i = 0; i < canvases.size(); i++) - canvases[i]->retain(); - - for (size_t i = 0; i < attachedCanvases.size(); i++) - attachedCanvases[i]->release(); + // We want to avoid reference cycles, so we don't retain the attached + // Canvases here. The code in Graphics::setCanvas retains them. attachedCanvases = canvases; } @@ -773,10 +757,6 @@ void Canvas::startGrab() // make sure the FBO is only using a single canvas strategy->setAttachments(); - // release any previously attached canvases - for (size_t i = 0; i < attachedCanvases.size(); i++) - attachedCanvases[i]->release(); - attachedCanvases.clear(); } @@ -786,9 +766,7 @@ void Canvas::stopGrab(bool switchingToOtherCanvas) if (current != this) return; - glMatrixMode(GL_PROJECTION); - glPopMatrix(); - glMatrixMode(GL_MODELVIEW); + gl.matrices.projection.pop_back(); if (switchingToOtherCanvas) { @@ -1123,12 +1101,6 @@ bool Canvas::isFormatSupported(Canvas::Format format) return supported; } -void Canvas::bindDefaultCanvas() -{ - if (current != nullptr) - current->stopGrab(); -} - bool Canvas::getConstant(const char *in, Format &out) { return formats.find(in, out); diff --git a/src/modules/graphics/opengl/Canvas.h b/src/modules/graphics/opengl/Canvas.h index d12d5ed42..a0fd6f1a1 100644 --- a/src/modules/graphics/opengl/Canvas.h +++ b/src/modules/graphics/opengl/Canvas.h @@ -120,7 +120,6 @@ public: static bool isFormatSupported(Format format); static Canvas *current; - static void bindDefaultCanvas(); // The viewport dimensions of the system (default) framebuffer. static OpenGL::Viewport systemViewport; diff --git a/src/modules/graphics/opengl/Font.cpp b/src/modules/graphics/opengl/Font.cpp index 5cb062cb3..578c8e465 100644 --- a/src/modules/graphics/opengl/Font.cpp +++ b/src/modules/graphics/opengl/Font.cpp @@ -368,11 +368,11 @@ void Font::print(const std::string &text, float x, float y, float extra_spacing, // second (using the struct's < operator). std::sort(glyphinfolist.begin(), glyphinfolist.end()); - glPushMatrix(); - Matrix t; t.setTransformation(ceilf(x), ceilf(y), angle, sx, sy, ox, oy, kx, ky); - glMultMatrixf((const GLfloat *)t.getElements()); + + OpenGL::TempTransform transform(gl); + transform.get() *= t; glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); @@ -393,8 +393,6 @@ void Font::print(const std::string &text, float x, float y, float extra_spacing, glDisableClientState(GL_TEXTURE_COORD_ARRAY); glDisableClientState(GL_VERTEX_ARRAY); - - glPopMatrix(); } int Font::getWidth(const std::string &str) diff --git a/src/modules/graphics/opengl/Graphics.cpp b/src/modules/graphics/opengl/Graphics.cpp index fecc889ba..735d5852a 100644 --- a/src/modules/graphics/opengl/Graphics.cpp +++ b/src/modules/graphics/opengl/Graphics.cpp @@ -44,19 +44,14 @@ namespace opengl { Graphics::Graphics() - : currentFont(0) - , lineStyle(LINE_SMOOTH) - , lineJoin(LINE_JOIN_MITER) - , lineWidth(1) - , matrixLimit(0) - , userMatrices(0) - , colorMask() - , width(0) + : width(0) , height(0) , created(false) , activeStencil(false) - , savedState() { + states.reserve(10); + states.push_back(DisplayState()); + currentWindow = love::window::sdl::Window::createSingleton(); int w, h; @@ -70,8 +65,8 @@ Graphics::Graphics() Graphics::~Graphics() { - if (currentFont != 0) - currentFont->release(); + // We do this manually so the love objects get released before the window. + states.clear(); currentWindow->release(); } @@ -81,53 +76,90 @@ const char *Graphics::getName() const return "love.graphics.opengl"; } -DisplayState Graphics::saveState() -{ - DisplayState s; - - s.color = getColor(); - s.backgroundColor = getBackgroundColor(); - - s.blendMode = getBlendMode(); - //get line style - s.lineStyle = getLineStyle(); - s.lineJoin = getLineJoin(); - //get the point size - glGetFloatv(GL_POINT_SIZE, &s.pointSize); - //get point style - s.pointStyle = (glIsEnabled(GL_POINT_SMOOTH) == GL_TRUE) ? Graphics::POINT_SMOOTH : Graphics::POINT_ROUGH; - //get scissor status - s.scissor = (glIsEnabled(GL_SCISSOR_TEST) == GL_TRUE); - //do we have scissor, if so, store the box - if (s.scissor) - s.scissorBox = gl.getScissor(); - - for (int i = 0; i < 4; i++) - s.colorMask[i] = colorMask[i]; - - s.wireframe = isWireframe(); - - return s; -} - void Graphics::restoreState(const DisplayState &s) { setColor(s.color); setBackgroundColor(s.backgroundColor); + setBlendMode(s.blendMode); - setLineWidth(lineWidth); + + setLineWidth(s.lineWidth); setLineStyle(s.lineStyle); setLineJoin(s.lineJoin); + setPointSize(s.pointSize); setPointStyle(s.pointStyle); + if (s.scissor) setScissor(s.scissorBox.x, s.scissorBox.y, s.scissorBox.w, s.scissorBox.h); else setScissor(); - setColorMask(s.colorMask[0], s.colorMask[1], s.colorMask[2], s.colorMask[3]); + + setFont(s.font); + setShader(s.shader); + setCanvas(s.canvases); + + setColorMask(s.colorMask); setWireframe(s.wireframe); } +void Graphics::restoreStateChecked(const DisplayState &s) +{ + const DisplayState &cur = states.back(); + + if (*(uint32 *) &s.color.r != *(uint32 *) &cur.color.r) + setColor(s.color); + + if (*(uint32 *) &s.backgroundColor.r != *(uint32 *) &cur.backgroundColor.r) + setBackgroundColor(s.backgroundColor); + + if (s.blendMode != cur.blendMode) + setBlendMode(s.blendMode); + + // These are just simple assignments. + setLineWidth(s.lineWidth); + setLineStyle(s.lineStyle); + setLineJoin(s.lineJoin); + + if (s.pointSize != cur.pointSize) + setPointSize(s.pointSize); + + if (s.pointStyle != cur.pointStyle) + setPointStyle(s.pointStyle); + + if (s.scissor != cur.scissor || (s.scissor && !(s.scissorBox == cur.scissorBox))) + { + if (s.scissor) + setScissor(s.scissorBox.x, s.scissorBox.y, s.scissorBox.w, s.scissorBox.h); + else + setScissor(); + } + + setFont(s.font); + setShader(s.shader); + + for (size_t i = 0; i < s.canvases.size() && i < cur.canvases.size(); i++) + { + if (s.canvases[i] != cur.canvases[i]) + { + setCanvas(s.canvases); + break; + } + } + + for (int i = 0; i < 4; i++) + { + if (s.colorMask[i] != cur.colorMask[i]) + { + setColorMask(s.colorMask); + break; + } + } + + if (s.wireframe != cur.wireframe) + setWireframe(s.wireframe); +} + void Graphics::setViewportSize(int width, int height) { this->width = width; @@ -138,8 +170,8 @@ void Graphics::setViewportSize(int width, int height) // We want to affect the main screen, not any Canvas that's currently active // (not that any *should* be active when this is called.) - Canvas *c = Canvas::current; - Canvas::bindDefaultCanvas(); + std::vector canvases = getCanvas(); + setCanvas(); // Set the viewport to top-left corner. gl.setViewport(OpenGL::Viewport(0, 0, width, height)); @@ -148,18 +180,11 @@ void Graphics::setViewportSize(int width, int height) // made aware of the new system viewport size. Canvas::systemViewport = gl.getViewport(); - // Reset the projection matrix - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - - // Set up orthographic view (no depth) - glOrtho(0.0, width, height, 0.0, -1.0, 1.0); - - glMatrixMode(GL_MODELVIEW); + // Set up the projection matrix + gl.matrices.projection.back() = Matrix::ortho(0.0, width, height, 0.0); // Restore the previously active Canvas. - if (c != nullptr) - c->startGrab(c->getAttachedCanvases()); + setCanvas(canvases); } bool Graphics::setMode(int width, int height, bool &sRGB) @@ -182,7 +207,8 @@ bool Graphics::setMode(int width, int height, bool &sRGB) glEnable(GL_BLEND); // Enable all color component writes. - setColorMask(true, true, true, true); + bool colormask[] = {true, true, true, true}; + setColorMask(colormask); // Enable line/point smoothing. setLineStyle(LINE_SMOOTH); @@ -197,28 +223,9 @@ bool Graphics::setMode(int width, int height, bool &sRGB) glEnable(GL_TEXTURE_2D); gl.setTextureUnit(0); - // Reset modelview matrix - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - // Set pixel row alignment glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - // Reload all volatile objects. - if (!Volatile::loadAll()) - std::cerr << "Could not reload all volatile objects." << std::endl; - - // Restore the display state. - restoreState(savedState); - pixel_size_stack.clear(); - pixel_size_stack.reserve(5); - pixel_size_stack.push_back(1); - - // Get the maximum number of matrices - // subtract a few to give the engine some room. - glGetIntegerv(GL_MAX_MODELVIEW_STACK_DEPTH, &matrixLimit); - matrixLimit -= 5; - // Set whether drawing converts input from linear -> sRGB colorspace. if (GLEE_VERSION_3_0 || GLEE_ARB_framebuffer_sRGB || GLEE_EXT_framebuffer_sRGB) { @@ -244,6 +251,17 @@ bool Graphics::setMode(int width, int height, bool &sRGB) setDebug(enabledebug); + // Reload all volatile objects. + if (!Volatile::loadAll()) + std::cerr << "Could not reload all volatile objects." << std::endl; + + // Restore the graphics state. + restoreState(states.back()); + + pixel_size_stack.clear(); + pixel_size_stack.reserve(5); + pixel_size_stack.push_back(1); + return true; } @@ -252,9 +270,6 @@ void Graphics::unSetMode() if (!isCreated()) return; - // Window re-creation may destroy the GL context, so we must save the state. - savedState = saveState(); - // Unload all volatile objects. These must be reloaded after the display // mode change. Volatile::unloadAll(); @@ -323,8 +338,6 @@ void Graphics::reset() { DisplayState s; discardStencil(); - Canvas::bindDefaultCanvas(); - Shader::detach(); origin(); restoreState(s); } @@ -356,13 +369,18 @@ bool Graphics::isCreated() const void Graphics::setScissor(int x, int y, int width, int height) { + OpenGL::Viewport box(x, y, width, height); + + states.back().scissor = true; glEnable(GL_SCISSOR_TEST); // OpenGL's reversed y-coordinate is compensated for in OpenGL::setScissor. - gl.setScissor(OpenGL::Viewport(x, y, width, height)); + gl.setScissor(box); + states.back().scissorBox = box; } void Graphics::setScissor() { + states.back().scissor = false; glDisable(GL_SCISSOR_TEST); } @@ -375,7 +393,7 @@ bool Graphics::getScissor(int &x, int &y, int &width, int &height) const width = scissor.w; height = scissor.h; - return glIsEnabled(GL_SCISSOR_TEST) == GL_TRUE; + return states.back().scissor; } void Graphics::defineStencil() @@ -399,7 +417,7 @@ void Graphics::useStencil(bool invert) { glStencilFunc(GL_EQUAL, (GLint)(!invert), 1); // invert ? 0 : 1 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); - setColorMask(colorMask[0], colorMask[1], colorMask[2], colorMask[3]); + setColorMask(states.back().colorMask); } void Graphics::discardStencil() @@ -407,7 +425,7 @@ void Graphics::discardStencil() if (!activeStencil) return; - setColorMask(colorMask[0], colorMask[1], colorMask[2], colorMask[3]); + setColorMask(states.back().colorMask); glDisable(GL_STENCIL_TEST); activeStencil = false; } @@ -566,65 +584,160 @@ Mesh *Graphics::newMesh(int vertexcount, Mesh::DrawMode mode) void Graphics::setColor(const Color &c) { gl.setColor(c); + states.back().color = c; } Color Graphics::getColor() const { - return gl.getColor(); + return states.back().color; } void Graphics::setBackgroundColor(const Color &c) { gl.setClearColor(c); + states.back().backgroundColor = c; } Color Graphics::getBackgroundColor() const { - return gl.getClearColor(); + return states.back().backgroundColor; } void Graphics::setFont(Font *font) { - Object::AutoRelease fontrelease(currentFont); + DisplayState &state = states.back(); - currentFont = font; + if (font != nullptr) + font->retain(); - if (font != 0) - currentFont->retain(); + if (state.font != nullptr) + state.font->release(); + + state.font = font; } Font *Graphics::getFont() const { - return currentFont; + return states.back().font; } -void Graphics::setColorMask(bool r, bool g, bool b, bool a) +void Graphics::setShader(Shader *shader) { - colorMask[0] = r; - colorMask[1] = g; - colorMask[2] = b; - colorMask[3] = a; + if (shader == nullptr) + return setShader(); - glColorMask((GLboolean) r, (GLboolean) g, (GLboolean) b, (GLboolean) a); + DisplayState &state = states.back(); + + shader->attach(); + + if (shader) + shader->retain(); + + if (state.shader) + state.shader->release(); + + state.shader = shader; +} + +void Graphics::setShader() +{ + DisplayState &state = states.back(); + + Shader::detach(); + + if (state.shader) + state.shader->release(); + + state.shader = nullptr; +} + +Shader *Graphics::getShader() const +{ + return states.back().shader; +} + +void Graphics::setCanvas(Canvas *canvas) +{ + if (canvas == nullptr) + return setCanvas(); + + DisplayState &state = states.back(); + + canvas->startGrab(); + + canvas->retain(); + + for (Canvas *c : state.canvases) + c->release(); + + state.canvases.clear(); + state.canvases.push_back(canvas); +} + +void Graphics::setCanvas(const std::vector &canvases) +{ + if (canvases.size() == 0) + return setCanvas(); + else if (canvases.size() == 1) + return setCanvas(canvases[0]); + + DisplayState &state = states.back(); + + auto attachments = std::vector(canvases.begin() + 1, canvases.end()); + canvases[0]->startGrab(attachments); + + for (Canvas *c : canvases) + c->retain(); + + for (Canvas *c : state.canvases) + c->release(); + + state.canvases = canvases; +} + +void Graphics::setCanvas() +{ + DisplayState &state = states.back(); + + if (Canvas::current != nullptr) + Canvas::current->stopGrab(); + + for (Canvas *c : state.canvases) + c->release(); + + state.canvases.clear(); +} + +std::vector Graphics::getCanvas() const +{ + return states.back().canvases; +} + +void Graphics::setColorMask(const bool mask[4]) +{ + for (int i = 0; i < 4; i++) + states.back().colorMask[i] = mask[i]; + + glColorMask(mask[0], mask[1], mask[2], mask[3]); } const bool *Graphics::getColorMask() const { - return colorMask; + return states.back().colorMask; } void Graphics::setBlendMode(Graphics::BlendMode mode) { - OpenGL::BlendState state = {GL_ONE, GL_ONE, GL_ZERO, GL_ZERO, GL_FUNC_ADD}; + OpenGL::BlendState blend = {GL_ONE, GL_ONE, GL_ZERO, GL_ZERO, GL_FUNC_ADD}; switch (mode) { case BLEND_ALPHA: if (GLEE_VERSION_1_4 || GLEE_EXT_blend_func_separate) { - state.srcRGB = GL_SRC_ALPHA; - state.srcA = GL_ONE; - state.dstRGB = state.dstA = GL_ONE_MINUS_SRC_ALPHA; + blend.srcRGB = GL_SRC_ALPHA; + blend.srcA = GL_ONE; + blend.dstRGB = blend.dstA = GL_ONE_MINUS_SRC_ALPHA; } else { @@ -632,65 +745,42 @@ void Graphics::setBlendMode(Graphics::BlendMode mode) // This will most likely only be used for the Microsoft software renderer and // since it's still stuck with OpenGL 1.1, the only expected difference is a // different alpha value when reading back the default framebuffer (newScreenshot). - state.srcRGB = state.srcA = GL_SRC_ALPHA; - state.dstRGB = state.dstA = GL_ONE_MINUS_SRC_ALPHA; + blend.srcRGB = blend.srcA = GL_SRC_ALPHA; + blend.dstRGB = blend.dstA = GL_ONE_MINUS_SRC_ALPHA; } break; case BLEND_MULTIPLICATIVE: - state.srcRGB = state.srcA = GL_DST_COLOR; - state.dstRGB = state.dstA = GL_ZERO; + blend.srcRGB = blend.srcA = GL_DST_COLOR; + blend.dstRGB = blend.dstA = GL_ZERO; break; case BLEND_PREMULTIPLIED: - state.srcRGB = state.srcA = GL_ONE; - state.dstRGB = state.dstA = GL_ONE_MINUS_SRC_ALPHA; + blend.srcRGB = blend.srcA = GL_ONE; + blend.dstRGB = blend.dstA = GL_ONE_MINUS_SRC_ALPHA; break; case BLEND_SUBTRACTIVE: - state.func = GL_FUNC_REVERSE_SUBTRACT; + blend.func = GL_FUNC_REVERSE_SUBTRACT; case BLEND_ADDITIVE: - state.srcRGB = state.srcA = GL_SRC_ALPHA; - state.dstRGB = state.dstA = GL_ONE; + blend.srcRGB = blend.srcA = GL_SRC_ALPHA; + blend.dstRGB = blend.dstA = GL_ONE; break; case BLEND_SCREEN: - state.srcRGB = state.srcA = GL_ONE; - state.dstRGB = state.dstA = GL_ONE_MINUS_SRC_COLOR; + blend.srcRGB = blend.srcA = GL_ONE; + blend.dstRGB = blend.dstA = GL_ONE_MINUS_SRC_COLOR; break; case BLEND_REPLACE: default: - state.srcRGB = state.srcA = GL_ONE; - state.dstRGB = state.dstA = GL_ZERO; + blend.srcRGB = blend.srcA = GL_ONE; + blend.dstRGB = blend.dstA = GL_ZERO; break; } - gl.setBlendState(state); + gl.setBlendState(blend); + states.back().blendMode = mode; } Graphics::BlendMode Graphics::getBlendMode() const { - OpenGL::BlendState state = gl.getBlendState(); - - if (state.func == GL_FUNC_REVERSE_SUBTRACT) // && src == GL_SRC_ALPHA && dst == GL_ONE - return BLEND_SUBTRACTIVE; - // Everything else has equation == GL_FUNC_ADD. - else if (state.srcRGB == state.srcA && state.dstRGB == state.dstA) - { - if (state.srcRGB == GL_SRC_ALPHA && state.dstRGB == GL_ONE) - return BLEND_ADDITIVE; - else if (state.srcRGB == GL_SRC_ALPHA && state.dstRGB == GL_ONE_MINUS_SRC_ALPHA) - return BLEND_ALPHA; // alpha blend mode fallback for very old OpenGL versions. - else if (state.srcRGB == GL_DST_COLOR && state.dstRGB == GL_ZERO) - return BLEND_MULTIPLICATIVE; - else if (state.srcRGB == GL_ONE && state.dstRGB == GL_ONE_MINUS_SRC_ALPHA) - return BLEND_PREMULTIPLIED; - else if (state.srcRGB == GL_ONE && state.dstRGB == GL_ONE_MINUS_SRC_COLOR) - return BLEND_SCREEN; - else if (state.srcRGB == GL_ONE && state.dstRGB == GL_ZERO) - return BLEND_REPLACE; - } - else if (state.srcRGB == GL_SRC_ALPHA && state.srcA == GL_ONE && - state.dstRGB == GL_ONE_MINUS_SRC_ALPHA && state.dstA == GL_ONE_MINUS_SRC_ALPHA) - return BLEND_ALPHA; - - throw Exception("Unknown blend mode"); + return states.back().blendMode; } void Graphics::setDefaultFilter(const Texture::Filter &f) @@ -717,37 +807,38 @@ void Graphics::getDefaultMipmapFilter(Texture::FilterMode *filter, float *sharpn void Graphics::setLineWidth(float width) { - lineWidth = width; + states.back().lineWidth = width; } void Graphics::setLineStyle(Graphics::LineStyle style) { - lineStyle = style; + states.back().lineStyle = style; } void Graphics::setLineJoin(Graphics::LineJoin join) { - lineJoin = join; + states.back().lineJoin = join; } float Graphics::getLineWidth() const { - return lineWidth; + return states.back().lineWidth; } Graphics::LineStyle Graphics::getLineStyle() const { - return lineStyle; + return states.back().lineStyle; } Graphics::LineJoin Graphics::getLineJoin() const { - return lineJoin; + return states.back().lineJoin; } void Graphics::setPointSize(float size) { - glPointSize((GLfloat)size); + glPointSize(size); + states.back().pointSize = size; } void Graphics::setPointStyle(Graphics::PointStyle style) @@ -756,43 +847,44 @@ void Graphics::setPointStyle(Graphics::PointStyle style) glEnable(GL_POINT_SMOOTH); else // love::POINT_ROUGH glDisable(GL_POINT_SMOOTH); + + states.back().pointStyle = style; } float Graphics::getPointSize() const { - GLfloat size; - glGetFloatv(GL_POINT_SIZE, &size); - return (float)size; + return states.back().pointSize; } Graphics::PointStyle Graphics::getPointStyle() const { - if (glIsEnabled(GL_POINT_SMOOTH) == GL_TRUE) - return POINT_SMOOTH; - else - return POINT_ROUGH; + return states.back().pointStyle; } void Graphics::setWireframe(bool enable) { - wireframe = enable; glPolygonMode(GL_FRONT_AND_BACK, enable ? GL_LINE : GL_FILL); + states.back().wireframe = enable; } bool Graphics::isWireframe() const { - return wireframe; + return states.back().wireframe; } void Graphics::print(const std::string &str, float x, float y , float angle, float sx, float sy, float ox, float oy, float kx, float ky) { - if (currentFont != nullptr) - currentFont->print(str, x, y, 0.0, angle, sx, sy, ox, oy, kx, ky); + DisplayState &state = states.back(); + + if (state.font != nullptr) + state.font->print(str, x, y, 0.0, angle, sx, sy, ox, oy, kx, ky); } void Graphics::printf(const std::string &str, float x, float y, float wrap, AlignMode align, float angle, float sx, float sy, float ox, float oy, float kx, float ky) { - if (currentFont == nullptr) + DisplayState &state = states.back(); + + if (state.font == nullptr) return; if (wrap < 0.0f) @@ -804,59 +896,49 @@ void Graphics::printf(const std::string &str, float x, float y, float wrap, Alig // wrappedlines indicates which lines were automatically wrapped. It's // guaranteed to have the same number of elements as lines_to_draw. vector wrappedlines; - vector lines_to_draw = currentFont->getWrap(str, wrap, 0, &wrappedlines); - - glPushMatrix(); + vector lines_to_draw = state.font->getWrap(str, wrap, 0, &wrappedlines); static Matrix t; t.setTransformation(ceilf(x), ceilf(y), angle, sx, sy, ox, oy, kx, ky); - glMultMatrixf((const GLfloat *)t.getElements()); + + OpenGL::TempTransform transform(gl); + transform.get() *= t; x = y = 0.0f; - try - { - // now for the actual printing - vector::const_iterator line_iter, line_end = lines_to_draw.end(); - float extra_spacing = 0.0f; - int num_spaces = 0; - int i = 0; + // now for the actual printing + vector::const_iterator line_iter, line_end = lines_to_draw.end(); + float extra_spacing = 0.0f; + int num_spaces = 0; + int i = 0; - for (line_iter = lines_to_draw.begin(); line_iter != line_end; ++line_iter) + for (line_iter = lines_to_draw.begin(); line_iter != line_end; ++line_iter) + { + float width = static_cast(state.font->getWidth(*line_iter)); + switch (align) { - float width = static_cast(currentFont->getWidth(*line_iter)); - switch (align) - { - case ALIGN_RIGHT: - currentFont->print(*line_iter, ceilf(x + (wrap - width)), ceilf(y), 0.0f); - break; - case ALIGN_CENTER: - currentFont->print(*line_iter, ceilf(x + (wrap - width) / 2), ceilf(y), 0.0f); - break; - case ALIGN_JUSTIFY: - num_spaces = std::count(line_iter->begin(), line_iter->end(), ' '); - if (wrappedlines[i] && num_spaces >= 1) - extra_spacing = (wrap - width) / float(num_spaces); - else - extra_spacing = 0.0f; - currentFont->print(*line_iter, ceilf(x), ceilf(y), extra_spacing); - break; - case ALIGN_LEFT: - default: - currentFont->print(*line_iter, ceilf(x), ceilf(y), 0.0f); - break; - } - y += currentFont->getHeight() * currentFont->getLineHeight(); - i++; + case ALIGN_RIGHT: + state.font->print(*line_iter, ceilf(x + (wrap - width)), ceilf(y), 0.0f); + break; + case ALIGN_CENTER: + state.font->print(*line_iter, ceilf(x + (wrap - width) / 2), ceilf(y), 0.0f); + break; + case ALIGN_JUSTIFY: + num_spaces = std::count(line_iter->begin(), line_iter->end(), ' '); + if (wrappedlines[i] && num_spaces >= 1) + extra_spacing = (wrap - width) / float(num_spaces); + else + extra_spacing = 0.0f; + state.font->print(*line_iter, ceilf(x), ceilf(y), extra_spacing); + break; + case ALIGN_LEFT: + default: + state.font->print(*line_iter, ceilf(x), ceilf(y), 0.0f); + break; } + y += state.font->getHeight() * state.font->getLineHeight(); + i++; } - catch (love::Exception &) - { - glPopMatrix(); - throw; - } - - glPopMatrix(); } /** @@ -874,22 +956,24 @@ void Graphics::point(float x, float y) void Graphics::polyline(const float *coords, size_t count) { - if (lineJoin == LINE_JOIN_NONE) + DisplayState &state = states.back(); + + if (state.lineJoin == LINE_JOIN_NONE) { NoneJoinPolyline line; - line.render(coords, count, lineWidth * .5f, float(pixel_size_stack.back()), lineStyle == LINE_SMOOTH); + line.render(coords, count, state.lineWidth * .5f, float(pixel_size_stack.back()), state.lineStyle == LINE_SMOOTH); line.draw(); } - else if (lineJoin == LINE_JOIN_BEVEL) + else if (state.lineJoin == LINE_JOIN_BEVEL) { BevelJoinPolyline line; - line.render(coords, count, lineWidth * .5f, float(pixel_size_stack.back()), lineStyle == LINE_SMOOTH); + line.render(coords, count, state.lineWidth * .5f, float(pixel_size_stack.back()), state.lineStyle == LINE_SMOOTH); line.draw(); } else // LINE_JOIN_MITER { MiterJoinPolyline line; - line.render(coords, count, lineWidth * .5f, float(pixel_size_stack.back()), lineStyle == LINE_SMOOTH); + line.render(coords, count, state.lineWidth * .5f, float(pixel_size_stack.back()), state.lineStyle == LINE_SMOOTH); line.draw(); } } @@ -996,9 +1080,8 @@ love::image::ImageData *Graphics::newScreenshot(love::image::Image *image, bool { // Temporarily unbind the currently active canvas (glReadPixels reads the // active framebuffer, not the main one.) - Canvas *curcanvas = Canvas::current; - if (curcanvas) - Canvas::bindDefaultCanvas(); + std::vector canvases = getCanvas(); + setCanvas(); int w = getWidth(); int h = getHeight(); @@ -1019,8 +1102,7 @@ love::image::ImageData *Graphics::newScreenshot(love::image::Image *image, bool { delete[] pixels; delete[] screenshot; - if (curcanvas) - curcanvas->startGrab(curcanvas->getAttachedCanvases()); + setCanvas(canvases); throw love::Exception("Out of memory."); } @@ -1052,14 +1134,12 @@ love::image::ImageData *Graphics::newScreenshot(love::image::Image *image, bool catch (love::Exception &) { delete[] screenshot; - if (curcanvas) - curcanvas->startGrab(curcanvas->getAttachedCanvases()); + setCanvas(canvases); throw; } // Re-bind the active canvas, if necessary. - if (curcanvas) - curcanvas->startGrab(curcanvas->getAttachedCanvases()); + setCanvas(canvases); return img; } @@ -1157,53 +1237,177 @@ bool Graphics::isSupported(Support feature) const } } -void Graphics::push() +void Graphics::push(StackType type) { - if (userMatrices == matrixLimit) - throw Exception("Maximum stack depth reached. (More pushes than pops?)"); - glPushMatrix(); - ++userMatrices; + if (stackTypes.size() == MAX_USER_STACK_DEPTH) + throw Exception("Maximum stack depth reached (more pushes than pops?)"); + + gl.pushTransform(); + pixel_size_stack.push_back(pixel_size_stack.back()); + + if (type == STACK_ALL) + states.push_back(states.back()); + + stackTypes.push_back(type); } void Graphics::pop() { - if (userMatrices < 1) - throw Exception("Minimum stack depth reached. (More pops than pushes?)"); - glPopMatrix(); - --userMatrices; + if (stackTypes.size() < 1) + throw Exception("Minimum stack depth reached (more pops than pushes?)"); + + gl.popTransform(); pixel_size_stack.pop_back(); + + if (stackTypes.back() == STACK_ALL) + { + DisplayState &newstate = states[states.size() - 2]; + + // Hack: the Lua-facing love.graphics.print function will set the current + // font if needed, but only on its first call... we always want a font. + if (newstate.font == nullptr) + { + newstate.font = states.back().font; + if (newstate.font != nullptr) + newstate.font->retain(); + } + + restoreStateChecked(newstate); + + // The last two states in the stack should be equal now. + states.pop_back(); + } + + stackTypes.pop_back(); } void Graphics::rotate(float r) { - glRotatef(LOVE_TODEG(r), 0, 0, 1); + gl.getTransform().rotate(r); } void Graphics::scale(float x, float y) { - glScalef(x, y, 1); + gl.getTransform().scale(x, y); pixel_size_stack.back() *= 2. / (fabs(x) + fabs(y)); } void Graphics::translate(float x, float y) { - glTranslatef(x, y, 0); + gl.getTransform().translate(x, y); } void Graphics::shear(float kx, float ky) { - Matrix t; - t.setShear(kx, ky); - glMultMatrixf((const GLfloat *)t.getElements()); + gl.getTransform().setShear(kx, ky); } void Graphics::origin() { - glLoadIdentity(); + gl.getTransform().setIdentity(); pixel_size_stack.back() = 1; } +Graphics::DisplayState::DisplayState() + : color(255, 255, 255, 255) + , backgroundColor(0, 0, 0, 255) + , blendMode(BLEND_ALPHA) + , lineWidth(1.0f) + , lineStyle(LINE_SMOOTH) + , lineJoin(LINE_JOIN_MITER) + , pointSize(1.0f) + , pointStyle(POINT_SMOOTH) + , scissor(false) + , scissorBox() + , font(nullptr) + , shader(nullptr) + , colorMask{true, true, true, true} + , wireframe(false) +{ +} + +Graphics::DisplayState::DisplayState(const DisplayState &other) + : color(other.color) + , backgroundColor(other.backgroundColor) + , blendMode(other.blendMode) + , lineWidth(other.lineWidth) + , lineStyle(other.lineStyle) + , lineJoin(other.lineJoin) + , pointSize(other.pointSize) + , pointStyle(other.pointStyle) + , scissor(other.scissor) + , scissorBox(other.scissorBox) + , font(other.font) + , shader(other.shader) + , canvases(other.canvases) + , wireframe(other.wireframe) +{ + for (int i = 0; i < 4; i++) + colorMask[i] = other.colorMask[i]; + + if (font) + font->retain(); + + if (shader) + shader->retain(); + + for (Canvas *c : canvases) + c->retain(); +} + +Graphics::DisplayState::~DisplayState() +{ + for (Canvas *c : canvases) + c->release(); + + if (shader) + shader->release(); + + if (font) + font->release(); +} + +Graphics::DisplayState &Graphics::DisplayState::operator = (const DisplayState &other) +{ + color = other.color; + backgroundColor = other.backgroundColor; + blendMode = other.blendMode; + lineWidth = other.lineWidth; + lineStyle = other.lineStyle; + lineJoin = other.lineJoin; + pointSize = other.pointSize; + pointStyle = other.pointStyle; + scissor = other.scissor; + scissorBox = other.scissorBox; + + Object::AutoRelease fontrelease(font); + + font = other.font; + if (font) + font->retain(); + + Object::AutoRelease shaderrelease(shader); + + shader = other.shader; + if (shader) + shader->retain(); + + for (Canvas *c : other.canvases) + c->retain(); + for (Canvas *c : canvases) + c->release(); + + canvases = other.canvases; + + for (int i = 0; i < 4; i++) + colorMask[i] = other.colorMask[i]; + + wireframe = other.wireframe; + + return *this; +} + } // opengl } // graphics } // love diff --git a/src/modules/graphics/opengl/Graphics.h b/src/modules/graphics/opengl/Graphics.h index bad07f0d9..bb52b2b6d 100644 --- a/src/modules/graphics/opengl/Graphics.h +++ b/src/modules/graphics/opengl/Graphics.h @@ -57,48 +57,7 @@ namespace opengl // During display mode changing, certain // variables about the OpenGL context are // lost. -struct DisplayState -{ - // Colors. - Color color; - Color backgroundColor; - // Blend mode. - Graphics::BlendMode blendMode; - - // Line. - Graphics::LineStyle lineStyle; - Graphics::LineJoin lineJoin; - - // Point. - float pointSize; - Graphics::PointStyle pointStyle; - - // Scissor. - bool scissor; - OpenGL::Viewport scissorBox; - - // Color mask. - bool colorMask[4]; - - bool wireframe; - - // Default values. - DisplayState() - { - color.set(255,255,255,255); - backgroundColor.set(0, 0, 0, 255); - blendMode = Graphics::BLEND_ALPHA; - lineStyle = Graphics::LINE_SMOOTH; - lineJoin = Graphics::LINE_JOIN_MITER; - pointSize = 1.0f; - pointStyle = Graphics::POINT_SMOOTH; - scissor = false; - colorMask[0] = colorMask[1] = colorMask[2] = colorMask[3] = true; - wireframe = false; - } - -}; class Graphics : public love::graphics::Graphics { @@ -110,10 +69,6 @@ public: // Implements Module. const char *getName() const; - DisplayState saveState(); - - void restoreState(const DisplayState &s); - virtual void setViewportSize(int width, int height); virtual bool setMode(int width, int height, bool &sRGB); virtual void unSetMode(); @@ -246,10 +201,21 @@ public: **/ Font *getFont() const; + void setShader(Shader *shader); + void setShader(); + + Shader *getShader() const; + + void setCanvas(Canvas *canvas); + void setCanvas(const std::vector &canvases); + void setCanvas(); + + std::vector getCanvas() const; + /** * Sets the enabled color components when rendering. **/ - void setColorMask(bool r, bool g, bool b, bool a); + void setColorMask(const bool mask[4]); /** * Gets the current color mask. @@ -460,8 +426,9 @@ public: **/ bool isSupported(Support feature) const; - void push(); + void push(StackType type = STACK_TRANSFORM); void pop(); + void rotate(float r); void scale(float x, float y = 1.0f); void translate(float x, float y); @@ -470,17 +437,50 @@ public: private: - Font *currentFont; + struct DisplayState + { + // Colors. + Color color; + Color backgroundColor; + + // Blend mode. + BlendMode blendMode; + + // Line. + float lineWidth; + LineStyle lineStyle; + LineJoin lineJoin; + + // Point. + float pointSize; + PointStyle pointStyle; + + // Scissor. + bool scissor; + OpenGL::Viewport scissorBox; + + Font *font; + Shader *shader; + std::vector canvases; + + // Color mask. + bool colorMask[4]; + + bool wireframe; + + DisplayState(); + DisplayState(const DisplayState &other); + ~DisplayState(); + + DisplayState &operator = (const DisplayState &other); + }; + + void restoreState(const DisplayState &s); + void restoreStateChecked(const DisplayState &s); + love::window::Window *currentWindow; std::vector pixel_size_stack; // stores current size of a pixel (needed for line drawing) - LineStyle lineStyle; - LineJoin lineJoin; - float lineWidth; - GLint matrixLimit; - GLint userMatrices; - bool colorMask[4]; - bool wireframe; int width; int height; @@ -488,7 +488,10 @@ private: bool activeStencil; - DisplayState savedState; + std::vector states; + std::vector stackTypes; // Keeps track of the pushed stack types. + + static const size_t MAX_USER_STACK_DEPTH = 64; }; // Graphics diff --git a/src/modules/graphics/opengl/Image.cpp b/src/modules/graphics/opengl/Image.cpp index 2651e2292..7fdbe9886 100644 --- a/src/modules/graphics/opengl/Image.cpp +++ b/src/modules/graphics/opengl/Image.cpp @@ -526,12 +526,11 @@ void Image::uploadDefaultTexture() void Image::drawv(const Matrix &t, const Vertex *v) { + OpenGL::TempTransform transform(gl); + transform.get() *= t; + predraw(); - glPushMatrix(); - - glMultMatrixf((const GLfloat *)t.getElements()); - glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); @@ -544,8 +543,6 @@ void Image::drawv(const Matrix &t, const Vertex *v) glDisableClientState(GL_TEXTURE_COORD_ARRAY); glDisableClientState(GL_VERTEX_ARRAY); - glPopMatrix(); - postdraw(); } diff --git a/src/modules/graphics/opengl/Mesh.cpp b/src/modules/graphics/opengl/Mesh.cpp index ec79b4b2c..467b7af2c 100644 --- a/src/modules/graphics/opengl/Mesh.cpp +++ b/src/modules/graphics/opengl/Mesh.cpp @@ -342,8 +342,8 @@ void Mesh::draw(float x, float y, float angle, float sx, float sy, float ox, flo Matrix m; m.setTransformation(x, y, angle, sx, sy, ox, oy, kx, ky); - glPushMatrix(); - glMultMatrixf(m.getElements()); + OpenGL::TempTransform transform(gl); + transform.get() *= m; VertexBuffer::Bind vbo_bind(*vbo); @@ -412,8 +412,6 @@ void Mesh::draw(float x, float y, float angle, float sx, float sy, float ox, flo gl.setColor(gl.getColor()); } - glPopMatrix(); - if (texture) texture->postdraw(); } diff --git a/src/modules/graphics/opengl/OpenGL.cpp b/src/modules/graphics/opengl/OpenGL.cpp index fea9ca877..6798b0b00 100644 --- a/src/modules/graphics/opengl/OpenGL.cpp +++ b/src/modules/graphics/opengl/OpenGL.cpp @@ -28,6 +28,7 @@ // C++ #include +#include // C #include @@ -47,6 +48,8 @@ OpenGL::OpenGL() , vendor(VENDOR_UNKNOWN) , state() { + matrices.transform.reserve(10); + matrices.projection.reserve(2); } void OpenGL::initContext() @@ -56,6 +59,7 @@ void OpenGL::initContext() initOpenGLFunctions(); initVendor(); + initMatrices(); // Store the current color so we don't have to get it through GL later. GLfloat glcolor[4]; @@ -119,6 +123,13 @@ void OpenGL::initContext() state.lastPseudoInstanceID = -1; + // Invalidate the cached matrices by setting some elements to NaN. + float nan = std::numeric_limits::quiet_NaN(); + state.lastProjectionMatrix.setTranslation(nan, nan); + state.lastTransformMatrix.setTranslation(nan, nan); + + glMatrixMode(GL_MODELVIEW); + contextInitialized = true; } @@ -209,6 +220,15 @@ void OpenGL::initMaxValues() maxRenderTargets = 0; } +void OpenGL::initMatrices() +{ + matrices.transform.clear(); + matrices.projection.clear(); + + matrices.transform.push_back(Matrix()); + matrices.projection.push_back(Matrix()); +} + void OpenGL::createDefaultTexture() { // Set the 'default' texture (id 0) as a repeating white pixel. Otherwise, @@ -231,6 +251,21 @@ void OpenGL::createDefaultTexture() bindTexture(curtexture); } +void OpenGL::pushTransform() +{ + matrices.transform.push_back(matrices.transform.back()); +} + +void OpenGL::popTransform() +{ + matrices.transform.pop_back(); +} + +Matrix &OpenGL::getTransform() +{ + return matrices.transform.back(); +} + void OpenGL::prepareDraw() { Shader *shader = Shader::current; @@ -251,15 +286,37 @@ void OpenGL::prepareDraw() // We need to make sure antialiased Canvases are properly resolved // before sampling from their textures in a shader. // This is kind of a big hack. :( - const std::map &r = shader->getBoundRetainables(); - for (auto it = r.begin(); it != r.end(); ++it) + for (auto &r : shader->getBoundRetainables()) { // Even bigger hack! D: - Canvas *canvas = dynamic_cast(it->second); + Canvas *canvas = dynamic_cast(r.second); if (canvas != nullptr) canvas->resolveMSAA(); } } + + const float *curproj = matrices.projection.back().getElements(); + const float *lastproj = state.lastProjectionMatrix.getElements(); + + // We only need to re-upload the projection matrix if it's changed. + if (memcmp(curproj, lastproj, sizeof(float) * 16) != 0) + { + glMatrixMode(GL_PROJECTION); + glLoadMatrixf(curproj); + glMatrixMode(GL_MODELVIEW); + + state.lastProjectionMatrix = matrices.projection.back(); + } + + const float *curxform = matrices.transform.back().getElements(); + const float *lastxform = state.lastTransformMatrix.getElements(); + + // Same with the transform matrix. + if (memcmp(curxform, lastxform, sizeof(float) * 16) != 0) + { + glLoadMatrixf(curxform); + state.lastTransformMatrix = matrices.transform.back(); + } } void OpenGL::drawArraysInstanced(GLenum mode, GLint first, GLsizei count, GLsizei primcount) diff --git a/src/modules/graphics/opengl/OpenGL.h b/src/modules/graphics/opengl/OpenGL.h index b912d4aac..1cc174ff3 100644 --- a/src/modules/graphics/opengl/OpenGL.h +++ b/src/modules/graphics/opengl/OpenGL.h @@ -26,9 +26,11 @@ // LOVE #include "graphics/Color.h" #include "graphics/Texture.h" +#include "common/Matrix.h" // C++ #include +#include // The last argument to AttribPointer takes a buffer offset casted to a pointer. #define BUFFER_OFFSET(i) ((char *) NULL + (i)) @@ -101,6 +103,36 @@ public: GLenum func; }; + struct + { + std::vector transform; + std::vector projection; + } matrices; + + class TempTransform + { + public: + + TempTransform(OpenGL &gl) + : gl(gl) + { + gl.pushTransform(); + } + + ~TempTransform() + { + gl.popTransform(); + } + + Matrix &get() + { + return gl.getTransform(); + } + + private: + OpenGL ≷ + }; + OpenGL(); /** @@ -116,6 +148,10 @@ public: **/ void deInitContext(); + void pushTransform(); + void popTransform(); + Matrix &getTransform(); + /** * Set up necessary state (LOVE-provided shader uniforms, etc.) for drawing. * This *MUST* be called directly before OpenGL drawing functions. @@ -258,6 +294,7 @@ private: void initVendor(); void initOpenGLFunctions(); void initMaxValues(); + void initMatrices(); void createDefaultTexture(); bool contextInitialized; @@ -290,6 +327,9 @@ private: // The last ID value used for pseudo-instancing. int lastPseudoInstanceID; + Matrix lastProjectionMatrix; + Matrix lastTransformMatrix; + } state; }; // OpenGL diff --git a/src/modules/graphics/opengl/ParticleSystem.cpp b/src/modules/graphics/opengl/ParticleSystem.cpp index 8c1dd3484..b09ff8f17 100644 --- a/src/modules/graphics/opengl/ParticleSystem.cpp +++ b/src/modules/graphics/opengl/ParticleSystem.cpp @@ -843,11 +843,11 @@ void ParticleSystem::draw(float x, float y, float angle, float sx, float sy, flo Color curcolor = gl.getColor(); - glPushMatrix(); - static Matrix t; t.setTransformation(x, y, angle, sx, sy, ox, oy, kx, ky); - glMultMatrixf((const GLfloat *)t.getElements()); + + OpenGL::TempTransform transform(gl); + transform.get() *= t; const Vertex *textureVerts = texture->getVertices(); Vertex *pVerts = particleVerts; @@ -901,8 +901,6 @@ void ParticleSystem::draw(float x, float y, float angle, float sx, float sy, flo texture->postdraw(); - glPopMatrix(); - gl.setColor(curcolor); } diff --git a/src/modules/graphics/opengl/Shader.cpp b/src/modules/graphics/opengl/Shader.cpp index 6cd08ad3a..698c58bd7 100644 --- a/src/modules/graphics/opengl/Shader.cpp +++ b/src/modules/graphics/opengl/Shader.cpp @@ -97,8 +97,8 @@ Shader::~Shader() if (current == this) detach(); - for (auto it = boundRetainables.begin(); it != boundRetainables.end(); ++it) - it->second->release(); + for (const auto &retainable : boundRetainables) + retainable.second->release(); boundRetainables.clear(); @@ -178,9 +178,8 @@ void Shader::createProgram(const std::vector &shaderids) if (program == 0) throw love::Exception("Cannot create shader program object."); - std::vector::const_iterator it; - for (it = shaderids.begin(); it != shaderids.end(); ++it) - glAttachShader(program, *it); + for (GLuint id : shaderids) + glAttachShader(program, id); // Bind generic vertex attribute indices to names in the shader. for (int i = 0; i < int(OpenGL::ATTRIB_MAX_ENUM); i++) @@ -201,8 +200,8 @@ void Shader::createProgram(const std::vector &shaderids) glLinkProgram(program); // flag shaders for auto-deletion when the program object is deleted. - for (it = shaderids.begin(); it != shaderids.end(); ++it) - glDeleteShader(*it); + for (GLuint id : shaderids) + glDeleteShader(id); GLint status; glGetProgramiv(program, GL_LINK_STATUS, &status); @@ -275,10 +274,9 @@ bool Shader::loadVolatile() std::vector shaderids; - ShaderSources::const_iterator source; - for (source = shaderSources.begin(); source != shaderSources.end(); ++source) + for (const auto &source : shaderSources) { - GLuint shaderid = compileCode(source->first, source->second); + GLuint shaderid = compileCode(source.first, source.second); shaderids.push_back(shaderid); } @@ -364,11 +362,10 @@ std::string Shader::getWarnings() const const char *typestr; // Get the individual shader stage warnings - std::map::const_iterator it; - for (it = shaderWarnings.begin(); it != shaderWarnings.end(); ++it) + for (const auto &warning : shaderWarnings) { - if (typeNames.find(it->first, typestr)) - warnings += std::string(typestr) + std::string(" shader:\n") + it->second; + if (typeNames.find(warning.first, typestr)) + warnings += std::string(typestr) + std::string(" shader:\n") + warning.second; } warnings += getProgramWarnings(); @@ -380,13 +377,9 @@ void Shader::attach(bool temporary) { if (current != this) { - if (current != nullptr) - current->release(); - glUseProgram(program); current = this; - - current->retain(); + // retain/release happens in Graphics::setShader. } if (!temporary) diff --git a/src/modules/graphics/opengl/SpriteBatch.cpp b/src/modules/graphics/opengl/SpriteBatch.cpp index b59618ee3..82f026441 100644 --- a/src/modules/graphics/opengl/SpriteBatch.cpp +++ b/src/modules/graphics/opengl/SpriteBatch.cpp @@ -271,11 +271,10 @@ void SpriteBatch::draw(float x, float y, float angle, float sx, float sy, float return; static Matrix t; - - glPushMatrix(); - t.setTransformation(x, y, angle, sx, sy, ox, oy, kx, ky); - glMultMatrixf((const GLfloat *)t.getElements()); + + OpenGL::TempTransform transform(gl); + transform.get() *= t; texture->predraw(); @@ -314,8 +313,6 @@ void SpriteBatch::draw(float x, float y, float angle, float sx, float sy, float } texture->postdraw(); - - glPopMatrix(); } void SpriteBatch::addv(const Vertex *v, int index) diff --git a/src/modules/graphics/opengl/wrap_Canvas.cpp b/src/modules/graphics/opengl/wrap_Canvas.cpp index 8d00e4dbe..92addd5ae 100644 --- a/src/modules/graphics/opengl/wrap_Canvas.cpp +++ b/src/modules/graphics/opengl/wrap_Canvas.cpp @@ -19,6 +19,7 @@ **/ #include "wrap_Canvas.h" +#include "Graphics.h" namespace love { @@ -37,18 +38,26 @@ int w_Canvas_renderTo(lua_State *L) Canvas *canvas = luax_checkcanvas(L, 1); luaL_checktype(L, 2, LUA_TFUNCTION); - // Save the current Canvas so we can restore it when we're done. - Canvas *oldcanvas = Canvas::current; + Graphics *graphics = Module::getInstance(Module::M_GRAPHICS); - luax_catchexcept(L, [&](){ canvas->startGrab(); }); + if (graphics) + { + // Save the current Canvas so we can restore it when we're done. + std::vector oldcanvases = graphics->getCanvas(); - lua_settop(L, 2); // make sure the function is on top of the stack - lua_call(L, 0, 0); + for (Canvas *c : oldcanvases) + c->retain(); - if (oldcanvas != nullptr) - oldcanvas->startGrab(oldcanvas->getAttachedCanvases()); - else - Canvas::bindDefaultCanvas(); + luax_catchexcept(L, [&](){ graphics->setCanvas(canvas); }); + + lua_settop(L, 2); // make sure the function is on top of the stack + lua_call(L, 0, 0); + + graphics->setCanvas(oldcanvases); + + for (Canvas *c : oldcanvases) + c->release(); + } return 0; } diff --git a/src/modules/graphics/opengl/wrap_Graphics.cpp b/src/modules/graphics/opengl/wrap_Graphics.cpp index bd60c3b89..0a88cd2f4 100644 --- a/src/modules/graphics/opengl/wrap_Graphics.cpp +++ b/src/modules/graphics/opengl/wrap_Graphics.cpp @@ -651,8 +651,7 @@ int w_setColorMask(lua_State *L) mask[i] = luax_toboolean(L, i + 1); } - // r, g, b, a - instance()->setColorMask(mask[0], mask[1], mask[2], mask[3]); + instance()->setColorMask(mask); return 0; } @@ -893,43 +892,35 @@ int w_setCanvas(lua_State *L) instance()->discardStencil(); // called with none -> reset to default buffer - if (lua_isnoneornil(L,1)) + if (lua_isnoneornil(L, 1)) { - Canvas::bindDefaultCanvas(); + instance()->setCanvas(); return 0; } bool is_table = lua_istable(L, 1); - std::vector attachments; - - Canvas *canvas = 0; + std::vector canvases; if (is_table) { - // grab the first canvas in the array and attach the rest - lua_rawgeti(L, 1, 1); - canvas = luax_checkcanvas(L, -1); - lua_pop(L, 1); - - for (size_t i = 2; i <= lua_objlen(L, 1); i++) + for (size_t i = 1; i <= lua_objlen(L, 1); i++) { lua_rawgeti(L, 1, i); - attachments.push_back(luax_checkcanvas(L, -1)); + canvases.push_back(luax_checkcanvas(L, -1)); lua_pop(L, 1); } } else { - canvas = luax_checkcanvas(L, 1); - for (int i = 2; i <= lua_gettop(L); i++) - attachments.push_back(luax_checkcanvas(L, i)); + for (int i = 1; i <= lua_gettop(L); i++) + canvases.push_back(luax_checkcanvas(L, i)); } luax_catchexcept(L, [&]() { - if (attachments.size() > 0) - canvas->startGrab(attachments); + if (canvases.size() > 0) + instance()->setCanvas(canvases); else - canvas->startGrab(); + instance()->setCanvas(); }); return 0; @@ -937,24 +928,23 @@ int w_setCanvas(lua_State *L) int w_getCanvas(lua_State *L) { - Canvas *canvas = Canvas::current; - int n = 1; + const std::vector canvases = instance()->getCanvas(); + int n = 0; - if (canvas) + if (!canvases.empty()) { - canvas->retain(); - luax_pushtype(L, "Canvas", GRAPHICS_CANVAS_T, canvas); - - const std::vector &attachments = canvas->getAttachedCanvases(); - for (size_t i = 0; i < attachments.size(); i++) + for (Canvas *c : canvases) { - attachments[i]->retain(); - luax_pushtype(L, "Canvas", GRAPHICS_CANVAS_T, attachments[i]); + c->retain(); + luax_pushtype(L, "Canvas", GRAPHICS_CANVAS_T, c); n++; } } else + { lua_pushnil(L); + n = 1; + } return n; } @@ -963,18 +953,18 @@ int w_setShader(lua_State *L) { if (lua_isnoneornil(L,1)) { - Shader::detach(); + instance()->setShader(); return 0; } Shader *shader = luax_checkshader(L, 1); - shader->attach(); + instance()->setShader(shader); return 0; } int w_getShader(lua_State *L) { - Shader *shader = Shader::current; + Shader *shader = instance()->getShader(); if (shader) { shader->retain(); @@ -1323,7 +1313,12 @@ int w_polygon(lua_State *L) int w_push(lua_State *L) { - luax_catchexcept(L, [&](){ instance()->push(); }); + Graphics::StackType stype = Graphics::STACK_TRANSFORM; + const char *sname = lua_isnoneornil(L, 1) ? nullptr : luaL_checkstring(L, 1); + if (sname && !Graphics::getConstant(sname, stype)) + return luaL_error(L, "Invalid graphics stack type: %s", sname); + + luax_catchexcept(L, [&](){ instance()->push(stype); }); return 0; }