mirror of
https://github.com/love2d/love-android.git
synced 2026-08-19 12:14:49 +02:00
imported Löve GLES branch (changeset 1ba9037e558b)
This commit is contained in:
@@ -0,0 +1,813 @@
|
||||
/**
|
||||
* Copyright (c) 2006-2013 LOVE Development Team
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
**/
|
||||
|
||||
#include "Canvas.h"
|
||||
#include "Image.h"
|
||||
#include "Graphics.h"
|
||||
#include "common/Matrix.h"
|
||||
#include "common/config.h"
|
||||
|
||||
#include <cstring> // For memcpy
|
||||
#include <limits>
|
||||
|
||||
namespace love
|
||||
{
|
||||
namespace graphics
|
||||
{
|
||||
namespace opengl
|
||||
{
|
||||
|
||||
// strategy for fbo creation, interchangable at runtime:
|
||||
// none, opengl >= 3.0, extensions
|
||||
struct FramebufferStrategy
|
||||
{
|
||||
virtual ~FramebufferStrategy() {}
|
||||
|
||||
/// create a new framebuffer and texture
|
||||
/**
|
||||
* @param[out] framebuffer Framebuffer name
|
||||
* @param[out] img Texture name
|
||||
* @param[in] width Width of framebuffer
|
||||
* @param[in] height Height of framebuffer
|
||||
* @param[in] texture_type Type of the canvas texture.
|
||||
* @return Creation status
|
||||
*/
|
||||
virtual GLenum createFBO(GLuint &, GLuint &, int, int, Canvas::TextureType)
|
||||
{
|
||||
return GL_FRAMEBUFFER_UNSUPPORTED;
|
||||
}
|
||||
|
||||
/// Create a stencil buffer and attach it to the active framebuffer object
|
||||
/**
|
||||
* @param[in] width Width of the stencil buffer
|
||||
* @param[in] height Height of the stencil buffer
|
||||
* @param[out] stencil Name for stencil buffer
|
||||
* @return Whether the stencil buffer was successfully created
|
||||
**/
|
||||
virtual bool createStencil(int, int, GLuint &)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/// remove objects
|
||||
/**
|
||||
* @param[in] framebuffer Framebuffer name
|
||||
* @param[in] depth_stencil Name for packed depth and stencil buffer
|
||||
* @param[in] img Texture name
|
||||
*/
|
||||
virtual void deleteFBO(GLuint, GLuint, GLuint) {}
|
||||
virtual void bindFBO(GLuint) {}
|
||||
|
||||
/// attach additional canvases to the active framebuffer for rendering
|
||||
/**
|
||||
* @param[in] canvases List of canvases to attach
|
||||
**/
|
||||
virtual void setAttachments(const std::vector<Canvas *> &) {}
|
||||
|
||||
/// stop using all additional attached canvases
|
||||
virtual void setAttachments() {}
|
||||
};
|
||||
|
||||
struct FramebufferStrategyCore : public FramebufferStrategy
|
||||
{
|
||||
virtual GLenum createFBO(GLuint &framebuffer, GLuint &img, int width, int height, Canvas::TextureType texture_type)
|
||||
{
|
||||
// get currently bound fbo to reset to it later
|
||||
GLint current_fbo;
|
||||
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, ¤t_fbo);
|
||||
|
||||
// create framebuffer
|
||||
glGenFramebuffers(1, &framebuffer);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
|
||||
|
||||
// generate texture save target
|
||||
GLint internalFormat;
|
||||
GLenum format;
|
||||
switch (texture_type)
|
||||
{
|
||||
case Canvas::TYPE_HDR:
|
||||
internalFormat = GL_RGBA16F;
|
||||
format = GL_FLOAT;
|
||||
break;
|
||||
case Canvas::TYPE_NORMAL:
|
||||
default:
|
||||
internalFormat = GL_RGBA;
|
||||
format = GL_UNSIGNED_BYTE;
|
||||
}
|
||||
|
||||
glGenTextures(1, &img);
|
||||
gl.bindTexture(img);
|
||||
|
||||
gl.setTextureFilter(Image::getDefaultFilter());
|
||||
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height,
|
||||
0, GL_RGBA, format, NULL);
|
||||
gl.bindTexture(0);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
|
||||
GL_TEXTURE_2D, img, 0);
|
||||
|
||||
// check status
|
||||
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
||||
|
||||
// unbind framebuffer
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, (GLuint) current_fbo);
|
||||
return status;
|
||||
}
|
||||
|
||||
virtual bool createStencil(int width, int height, GLuint &stencil)
|
||||
{
|
||||
// create stencil buffer
|
||||
glDeleteRenderbuffers(1, &stencil);
|
||||
glGenRenderbuffers(1, &stencil);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, stencil);
|
||||
glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, width, height);
|
||||
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
|
||||
GL_RENDERBUFFER, stencil);
|
||||
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, 0);
|
||||
|
||||
// check status
|
||||
return glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE;
|
||||
}
|
||||
|
||||
virtual void deleteFBO(GLuint framebuffer, GLuint depth_stencil, GLuint img)
|
||||
{
|
||||
gl.deleteTexture(img);
|
||||
if (depth_stencil != 0)
|
||||
glDeleteRenderbuffers(1, &depth_stencil);
|
||||
if (framebuffer != 0)
|
||||
glDeleteFramebuffers(1, &framebuffer);
|
||||
}
|
||||
|
||||
virtual void bindFBO(GLuint framebuffer)
|
||||
{
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
|
||||
}
|
||||
|
||||
virtual void setAttachments()
|
||||
{
|
||||
// set a single render target
|
||||
glDrawBuffer(GL_COLOR_ATTACHMENT0);
|
||||
}
|
||||
|
||||
virtual void setAttachments(const std::vector<Canvas *> &canvases)
|
||||
{
|
||||
if (canvases.size() == 0)
|
||||
{
|
||||
setAttachments();
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<GLenum> drawbuffers;
|
||||
drawbuffers.push_back(GL_COLOR_ATTACHMENT0);
|
||||
|
||||
// Attach the canvas textures to the currently bound framebuffer.
|
||||
for (size_t i = 0; i < canvases.size(); i++)
|
||||
{
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1 + i,
|
||||
GL_TEXTURE_2D, canvases[i]->getTextureName(), 0);
|
||||
drawbuffers.push_back(GL_COLOR_ATTACHMENT1 + i);
|
||||
}
|
||||
|
||||
// set up multiple render targets
|
||||
if (GLAD_VERSION_2_0 || GLAD_ES_VERSION_3_0)
|
||||
glDrawBuffers(drawbuffers.size(), &drawbuffers[0]);
|
||||
else if (GLAD_ARB_draw_buffers)
|
||||
glDrawBuffersARB(drawbuffers.size(), &drawbuffers[0]);
|
||||
}
|
||||
};
|
||||
|
||||
struct FramebufferStrategyCorePacked : public FramebufferStrategyCore
|
||||
{
|
||||
virtual bool createStencil(int width, int height, GLuint &stencil)
|
||||
{
|
||||
// create combined depth/stencil buffer
|
||||
glDeleteRenderbuffers(1, &stencil);
|
||||
glGenRenderbuffers(1, &stencil);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, stencil);
|
||||
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_STENCIL, width, height);
|
||||
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
|
||||
GL_RENDERBUFFER, stencil);
|
||||
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, 0);
|
||||
|
||||
// check status
|
||||
return glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE;
|
||||
}
|
||||
};
|
||||
|
||||
struct FramebufferStrategyPackedEXT : public FramebufferStrategy
|
||||
{
|
||||
virtual GLenum createFBO(GLuint &framebuffer, GLuint &img, int width, int height, Canvas::TextureType texture_type)
|
||||
{
|
||||
GLint current_fbo;
|
||||
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING_EXT, ¤t_fbo);
|
||||
|
||||
// create framebuffer
|
||||
glGenFramebuffersEXT(1, &framebuffer);
|
||||
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, framebuffer);
|
||||
|
||||
// generate texture save target
|
||||
GLint internalFormat;
|
||||
GLenum format;
|
||||
switch (texture_type)
|
||||
{
|
||||
case Canvas::TYPE_HDR:
|
||||
internalFormat = GL_RGBA16F;
|
||||
format = GL_FLOAT;
|
||||
break;
|
||||
case Canvas::TYPE_NORMAL:
|
||||
default:
|
||||
internalFormat = GL_RGBA;
|
||||
format = GL_UNSIGNED_BYTE;
|
||||
}
|
||||
|
||||
glGenTextures(1, &img);
|
||||
gl.bindTexture(img);
|
||||
|
||||
gl.setTextureFilter(Image::getDefaultFilter());
|
||||
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height,
|
||||
0, GL_RGBA, format, NULL);
|
||||
gl.bindTexture(0);
|
||||
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
|
||||
GL_TEXTURE_2D, img, 0);
|
||||
|
||||
// check status
|
||||
GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
|
||||
|
||||
// unbind framebuffer
|
||||
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, (GLuint) current_fbo);
|
||||
return status;
|
||||
}
|
||||
|
||||
virtual bool createStencil(int width, int height, GLuint &stencil)
|
||||
{
|
||||
// create combined depth/stencil buffer
|
||||
glDeleteRenderbuffers(1, &stencil);
|
||||
glGenRenderbuffersEXT(1, &stencil);
|
||||
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, stencil);
|
||||
glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_STENCIL_EXT,
|
||||
width, height);
|
||||
glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT,
|
||||
GL_RENDERBUFFER_EXT, stencil);
|
||||
|
||||
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0);
|
||||
|
||||
// check status
|
||||
return glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT) == GL_FRAMEBUFFER_COMPLETE_EXT;
|
||||
}
|
||||
|
||||
virtual void deleteFBO(GLuint framebuffer, GLuint depth_stencil, GLuint img)
|
||||
{
|
||||
gl.deleteTexture(img);
|
||||
if (depth_stencil != 0)
|
||||
glDeleteRenderbuffersEXT(1, &depth_stencil);
|
||||
if (framebuffer != 0)
|
||||
glDeleteFramebuffersEXT(1, &framebuffer);
|
||||
}
|
||||
|
||||
virtual void bindFBO(GLuint framebuffer)
|
||||
{
|
||||
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, framebuffer);
|
||||
}
|
||||
|
||||
virtual void setAttachments()
|
||||
{
|
||||
// set a single render target
|
||||
glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT);
|
||||
}
|
||||
|
||||
virtual void setAttachments(const std::vector<Canvas *> &canvases)
|
||||
{
|
||||
if (canvases.size() == 0)
|
||||
{
|
||||
setAttachments();
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<GLenum> drawbuffers;
|
||||
drawbuffers.push_back(GL_COLOR_ATTACHMENT0_EXT);
|
||||
|
||||
// Attach the canvas textures to the currently bound framebuffer.
|
||||
for (size_t i = 0; i < canvases.size(); i++)
|
||||
{
|
||||
glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1_EXT + i,
|
||||
GL_TEXTURE_2D, canvases[i]->getTextureName(), 0);
|
||||
drawbuffers.push_back(GL_COLOR_ATTACHMENT1_EXT + i);
|
||||
}
|
||||
|
||||
// set up multiple render targets
|
||||
if (GLAD_VERSION_2_0 || GLAD_ES_VERSION_3_0)
|
||||
glDrawBuffers(drawbuffers.size(), &drawbuffers[0]);
|
||||
else if (GLAD_ARB_draw_buffers)
|
||||
glDrawBuffersARB(drawbuffers.size(), &drawbuffers[0]);
|
||||
}
|
||||
};
|
||||
|
||||
struct FramebufferStrategyEXT : public FramebufferStrategyPackedEXT
|
||||
{
|
||||
virtual bool createStencil(int width, int height, GLuint &stencil)
|
||||
{
|
||||
// create stencil buffer
|
||||
glDeleteRenderbuffers(1, &stencil);
|
||||
glGenRenderbuffersEXT(1, &stencil);
|
||||
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, stencil);
|
||||
glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_STENCIL_INDEX,
|
||||
width, height);
|
||||
glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT,
|
||||
GL_RENDERBUFFER_EXT, stencil);
|
||||
|
||||
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0);
|
||||
|
||||
// check status
|
||||
return glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT) == GL_FRAMEBUFFER_COMPLETE_EXT;
|
||||
}
|
||||
|
||||
bool isSupported()
|
||||
{
|
||||
GLuint fb = 0, stencil = 0, img = 0;
|
||||
GLenum status = createFBO(fb, img, 2, 2, Canvas::TYPE_NORMAL);
|
||||
deleteFBO(fb, stencil, img);
|
||||
return status == GL_FRAMEBUFFER_COMPLETE;
|
||||
}
|
||||
};
|
||||
|
||||
FramebufferStrategy *strategy = NULL;
|
||||
|
||||
FramebufferStrategy strategyNone;
|
||||
|
||||
FramebufferStrategyCore strategyCore;
|
||||
|
||||
FramebufferStrategyCorePacked strategyCorePacked;
|
||||
|
||||
FramebufferStrategyPackedEXT strategyPackedEXT;
|
||||
|
||||
FramebufferStrategyEXT strategyEXT;
|
||||
|
||||
Canvas *Canvas::current = NULL;
|
||||
|
||||
static void getStrategy()
|
||||
{
|
||||
if (!strategy)
|
||||
{
|
||||
if (GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object)
|
||||
strategy = &strategyCorePacked;
|
||||
else if (GLAD_ES_VERSION_2_0)
|
||||
strategy = &strategyCore;
|
||||
else if (GLAD_EXT_framebuffer_object && GLAD_EXT_packed_depth_stencil)
|
||||
strategy = &strategyPackedEXT;
|
||||
else if (GLAD_EXT_framebuffer_object && strategyEXT.isSupported())
|
||||
strategy = &strategyEXT;
|
||||
else
|
||||
strategy = &strategyNone;
|
||||
}
|
||||
}
|
||||
|
||||
static int maxFBOColorAttachments = 0;
|
||||
static int maxDrawBuffers = 0;
|
||||
|
||||
Canvas::Canvas(int width, int height, TextureType texture_type)
|
||||
: width(width)
|
||||
, height(height)
|
||||
, fbo(0)
|
||||
, depth_stencil(0)
|
||||
, img(0)
|
||||
, texture_type(texture_type)
|
||||
{
|
||||
float w = static_cast<float>(width);
|
||||
float h = static_cast<float>(height);
|
||||
|
||||
// world coordinates
|
||||
vertices[0].x = 0;
|
||||
vertices[0].y = h;
|
||||
vertices[1].x = w;
|
||||
vertices[1].y = h;
|
||||
vertices[2].x = w;
|
||||
vertices[2].y = 0;
|
||||
vertices[3].x = 0;
|
||||
vertices[3].y = 0;
|
||||
|
||||
// texture coordinates
|
||||
vertices[0].s = 0;
|
||||
vertices[0].t = 0;
|
||||
vertices[1].s = 1;
|
||||
vertices[1].t = 0;
|
||||
vertices[2].s = 1;
|
||||
vertices[2].t = 1;
|
||||
vertices[3].s = 0;
|
||||
vertices[3].t = 1;
|
||||
|
||||
settings.filter = Image::getDefaultFilter();
|
||||
|
||||
getStrategy();
|
||||
|
||||
loadVolatile();
|
||||
}
|
||||
|
||||
Canvas::~Canvas()
|
||||
{
|
||||
// reset framebuffer if still using this one
|
||||
if (current == this)
|
||||
stopGrab();
|
||||
|
||||
unloadVolatile();
|
||||
}
|
||||
|
||||
bool Canvas::isSupported()
|
||||
{
|
||||
getStrategy();
|
||||
return (strategy != &strategyNone);
|
||||
}
|
||||
|
||||
bool Canvas::isHDRSupported()
|
||||
{
|
||||
return GLAD_VERSION_3_0 || (isSupported() && GLAD_ARB_texture_float);
|
||||
}
|
||||
|
||||
bool Canvas::isMultiCanvasSupported()
|
||||
{
|
||||
if (!(isSupported() && (GLAD_VERSION_2_0 || GLAD_ES_VERSION_3_0 || GLAD_ARB_draw_buffers)))
|
||||
return false;
|
||||
|
||||
if (maxFBOColorAttachments == 0 || maxDrawBuffers == 0)
|
||||
{
|
||||
glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, &maxFBOColorAttachments);
|
||||
glGetIntegerv(GL_MAX_DRAW_BUFFERS, &maxDrawBuffers);
|
||||
}
|
||||
|
||||
// system must support at least 4 simultanious active canvases
|
||||
return maxFBOColorAttachments >= 4 && maxDrawBuffers >= 4;
|
||||
}
|
||||
|
||||
void Canvas::bindDefaultCanvas()
|
||||
{
|
||||
if (current != NULL)
|
||||
current->stopGrab();
|
||||
}
|
||||
|
||||
void Canvas::setupGrab()
|
||||
{
|
||||
// already grabbing
|
||||
if (current == this)
|
||||
return;
|
||||
|
||||
// cleanup after previous fbo
|
||||
if (current != NULL)
|
||||
current->stopGrab();
|
||||
|
||||
// bind the framebuffer object.
|
||||
glPushAttrib(GL_VIEWPORT_BIT | GL_TRANSFORM_BIT);
|
||||
strategy->bindFBO(fbo);
|
||||
gl.setViewport(OpenGL::Viewport(0, 0, width, height));
|
||||
|
||||
// Set up orthographic view (no depth)
|
||||
gl.matrices.projection.push(Matrix::ortho(0.0, width, height, 0.0));
|
||||
|
||||
// indicate we are using this fbo
|
||||
current = this;
|
||||
}
|
||||
|
||||
void Canvas::startGrab(const std::vector<Canvas *> &canvases)
|
||||
{
|
||||
// Whether the new canvas list is different from the old one.
|
||||
// A more thorough check is done below.
|
||||
bool canvaseschanged = canvases.size() != attachedCanvases.size();
|
||||
|
||||
if (canvases.size() > 0)
|
||||
{
|
||||
if (!isMultiCanvasSupported())
|
||||
throw love::Exception("Multi-canvas rendering is not supported on this system.");
|
||||
|
||||
if (canvases.size()+1 > size_t(maxDrawBuffers) || canvases.size()+1 > size_t(maxFBOColorAttachments))
|
||||
throw love::Exception("This system can't simultaniously render to %d canvases.", canvases.size()+1);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < canvases.size(); i++)
|
||||
{
|
||||
if (canvases[i]->getWidth() != width || canvases[i]->getHeight() != height)
|
||||
throw love::Exception("All canvas arguments must have the same dimensions.");
|
||||
|
||||
if (canvases[i]->getTextureType() != texture_type)
|
||||
throw love::Exception("All canvas arguments must have the same texture type.");
|
||||
|
||||
if (!canvaseschanged && canvases[i] != attachedCanvases[i])
|
||||
canvaseschanged = true;
|
||||
}
|
||||
|
||||
setupGrab();
|
||||
|
||||
// Don't attach anything if there's nothing to change.
|
||||
if (!canvaseschanged)
|
||||
return;
|
||||
|
||||
// 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();
|
||||
|
||||
attachedCanvases = canvases;
|
||||
}
|
||||
|
||||
void Canvas::startGrab()
|
||||
{
|
||||
setupGrab();
|
||||
|
||||
if (attachedCanvases.size() == 0)
|
||||
return;
|
||||
|
||||
// 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();
|
||||
}
|
||||
|
||||
void Canvas::stopGrab()
|
||||
{
|
||||
// i am not grabbing. leave me alone
|
||||
if (current != this)
|
||||
return;
|
||||
|
||||
// bind default
|
||||
strategy->bindFBO(gl.getDefaultFBO());
|
||||
gl.matrices.projection.pop();
|
||||
glPopAttrib();
|
||||
current = NULL;
|
||||
}
|
||||
|
||||
void Canvas::clear(Color c)
|
||||
{
|
||||
if (strategy == &strategyNone)
|
||||
return;
|
||||
|
||||
GLuint previous = gl.getDefaultFBO();
|
||||
|
||||
if (current != this)
|
||||
{
|
||||
if (current != NULL)
|
||||
previous = current->fbo;
|
||||
|
||||
strategy->bindFBO(fbo);
|
||||
}
|
||||
|
||||
GLfloat glcolor[] = {c.r/255.f, c.g/255.f, c.b/255.f, c.a/255.f};
|
||||
|
||||
// We don't need to worry about multiple FBO attachments or global clear
|
||||
// color state when OpenGL 3.0+ is supported.
|
||||
if (GLAD_VERSION_3_0 || GLAD_ES_VERSION_3_0)
|
||||
{
|
||||
glClearBufferfv(GL_COLOR, 0, glcolor);
|
||||
|
||||
if (depth_stencil != 0)
|
||||
{
|
||||
GLint stencilvalue = 0;
|
||||
glClearBufferiv(GL_STENCIL, 0, &stencilvalue);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// glClear will clear all active draw buffers, so we need to temporarily
|
||||
// detach any other canvases (when MRT is being used.)
|
||||
if (attachedCanvases.size() > 0)
|
||||
strategy->setAttachments();
|
||||
|
||||
// Don't use the state-shadowed gl.setClearColor because we want to save
|
||||
// the previous clear color.
|
||||
glClearColor(glcolor[0], glcolor[1], glcolor[2], glcolor[3]);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
|
||||
|
||||
if (attachedCanvases.size() > 0)
|
||||
strategy->setAttachments(attachedCanvases);
|
||||
|
||||
// Restore the global clear color.
|
||||
gl.setClearColor(gl.getClearColor());
|
||||
}
|
||||
|
||||
if (current != this)
|
||||
strategy->bindFBO(previous);
|
||||
}
|
||||
|
||||
void Canvas::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const
|
||||
{
|
||||
static Matrix t;
|
||||
t.setTransformation(x, y, angle, sx, sy, ox, oy, kx, ky);
|
||||
|
||||
drawv(t, vertices);
|
||||
}
|
||||
|
||||
void Canvas::drawq(Quad *quad, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const
|
||||
{
|
||||
static Matrix t;
|
||||
t.setTransformation(x, y, angle, sx, sy, ox, oy, kx, ky);
|
||||
|
||||
const Vertex *v = quad->getVertices();
|
||||
|
||||
// flip texture coordinates vertically.
|
||||
Vertex w[4];
|
||||
memcpy(w, v, sizeof(Vertex) * 4);
|
||||
for (size_t i = 0; i < 4; i++)
|
||||
w[i].t = 1.0f - w[i].t;
|
||||
|
||||
drawv(t, w);
|
||||
}
|
||||
|
||||
bool Canvas::checkCreateStencil()
|
||||
{
|
||||
// Do nothing if we've already created the stencil buffer.
|
||||
if (depth_stencil != 0)
|
||||
return true;
|
||||
|
||||
if (current != this)
|
||||
strategy->bindFBO(fbo);
|
||||
|
||||
bool success = strategy->createStencil(width, height, depth_stencil);
|
||||
|
||||
if (current && current != this)
|
||||
strategy->bindFBO(current->fbo);
|
||||
else if (!current)
|
||||
strategy->bindFBO(gl.getDefaultFBO());
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
love::image::ImageData *Canvas::getImageData(love::image::Image *image)
|
||||
{
|
||||
int row = 4 * width;
|
||||
int size = row * height;
|
||||
GLubyte *pixels = new GLubyte[size];
|
||||
GLubyte *flipped = new GLubyte[size];
|
||||
|
||||
strategy->bindFBO(fbo);
|
||||
glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
|
||||
if (current)
|
||||
strategy->bindFBO(current->fbo);
|
||||
else
|
||||
strategy->bindFBO(gl.getDefaultFBO());
|
||||
|
||||
GLubyte *src = pixels, *dst = flipped + size - row;
|
||||
for (int i = 0; i < height; ++i, dst -= row, src += row)
|
||||
memcpy(dst, src, row);
|
||||
|
||||
love::image::ImageData *img = image->newImageData(width, height, (void *)flipped, true);
|
||||
|
||||
// The new ImageData now owns the flipped data, so we don't delete it here.
|
||||
delete[] pixels;
|
||||
|
||||
return img;
|
||||
}
|
||||
|
||||
void Canvas::getPixel(unsigned char* pixel_rgba, int x, int y)
|
||||
{
|
||||
if (current != this)
|
||||
strategy->bindFBO(fbo);
|
||||
|
||||
glReadPixels(x, height - y, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel_rgba);
|
||||
|
||||
if (current && current != this)
|
||||
strategy->bindFBO(current->fbo);
|
||||
else if (!current)
|
||||
strategy->bindFBO(gl.getDefaultFBO());
|
||||
}
|
||||
|
||||
const std::vector<Canvas *> &Canvas::getAttachedCanvases() const
|
||||
{
|
||||
return attachedCanvases;
|
||||
}
|
||||
|
||||
void Canvas::setFilter(const Image::Filter &f)
|
||||
{
|
||||
settings.filter = f;
|
||||
gl.bindTexture(img);
|
||||
settings.filter.anisotropy = gl.setTextureFilter(f);
|
||||
}
|
||||
|
||||
Image::Filter Canvas::getFilter() const
|
||||
{
|
||||
gl.bindTexture(img);
|
||||
return gl.getTextureFilter();
|
||||
}
|
||||
|
||||
void Canvas::setWrap(const Image::Wrap &w)
|
||||
{
|
||||
settings.wrap = w;
|
||||
gl.bindTexture(img);
|
||||
gl.setTextureWrap(w);
|
||||
}
|
||||
|
||||
Image::Wrap Canvas::getWrap() const
|
||||
{
|
||||
return settings.wrap;
|
||||
}
|
||||
|
||||
bool Canvas::loadVolatile()
|
||||
{
|
||||
fbo = depth_stencil = img = 0;
|
||||
|
||||
// glTexImage2D is guaranteed to error in this case.
|
||||
if (width > gl.getMaxTextureSize() || height > gl.getMaxTextureSize())
|
||||
{
|
||||
status = GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
|
||||
return false;
|
||||
}
|
||||
|
||||
status = strategy->createFBO(fbo, img, width, height, texture_type);
|
||||
if (status != GL_FRAMEBUFFER_COMPLETE)
|
||||
return false;
|
||||
|
||||
setFilter(settings.filter);
|
||||
setWrap(settings.wrap);
|
||||
clear(Color(0, 0, 0, 0));
|
||||
return true;
|
||||
}
|
||||
|
||||
void Canvas::unloadVolatile()
|
||||
{
|
||||
strategy->deleteFBO(fbo, depth_stencil, img);
|
||||
fbo = depth_stencil = img = 0;
|
||||
|
||||
for (size_t i = 0; i < attachedCanvases.size(); i++)
|
||||
attachedCanvases[i]->release();
|
||||
|
||||
attachedCanvases.clear();
|
||||
}
|
||||
|
||||
int Canvas::getWidth()
|
||||
{
|
||||
return width;
|
||||
}
|
||||
|
||||
int Canvas::getHeight()
|
||||
{
|
||||
return height;
|
||||
}
|
||||
|
||||
void Canvas::drawv(const Matrix &t, const Vertex *v) const
|
||||
{
|
||||
gl.matrices.transform.push(gl.matrices.transform.top());
|
||||
gl.matrices.transform.top() *= t;
|
||||
|
||||
gl.prepareDraw();
|
||||
|
||||
gl.bindTexture(img);
|
||||
|
||||
gl.enableVertexAttribArray(OpenGL::ATTRIB_POS);
|
||||
gl.enableVertexAttribArray(OpenGL::ATTRIB_TEXCOORD);
|
||||
|
||||
gl.setVertexAttribArray(OpenGL::ATTRIB_POS, 2, GL_FLOAT, sizeof(Vertex), (GLvoid *) &v[0].x);
|
||||
gl.setVertexAttribArray(OpenGL::ATTRIB_TEXCOORD, 2, GL_FLOAT, sizeof(Vertex), (GLvoid *) &v[0].s);
|
||||
|
||||
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
|
||||
|
||||
gl.disableVertexAttribArray(OpenGL::ATTRIB_POS);
|
||||
gl.disableVertexAttribArray(OpenGL::ATTRIB_TEXCOORD);
|
||||
|
||||
gl.matrices.transform.pop();
|
||||
}
|
||||
|
||||
bool Canvas::getConstant(const char *in, Canvas::TextureType &out)
|
||||
{
|
||||
return textureTypes.find(in, out);
|
||||
}
|
||||
|
||||
bool Canvas::getConstant(Canvas::TextureType in, const char *&out)
|
||||
{
|
||||
return textureTypes.find(in, out);
|
||||
}
|
||||
|
||||
StringMap<Canvas::TextureType, Canvas::TYPE_MAX_ENUM>::Entry Canvas::textureTypeEntries[] =
|
||||
{
|
||||
{"normal", Canvas::TYPE_NORMAL},
|
||||
{"hdr", Canvas::TYPE_HDR},
|
||||
};
|
||||
StringMap<Canvas::TextureType, Canvas::TYPE_MAX_ENUM> Canvas::textureTypes(Canvas::textureTypeEntries, sizeof(Canvas::textureTypeEntries));
|
||||
|
||||
} // opengl
|
||||
} // graphics
|
||||
} // love
|
||||
Reference in New Issue
Block a user