Merged default into minor

--HG--
branch : minor
This commit is contained in:
Alex Szpakowski
2014-03-12 02:21:27 -03:00
124 changed files with 3248 additions and 1078 deletions
+1 -1
View File
@@ -55,7 +55,7 @@ public:
* @param kx Shear along the x-axis.
* @param ky Shear along the y-axis.
**/
virtual void draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const = 0;
virtual void draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) = 0;
};
} // graphics
+22
View File
@@ -99,6 +99,16 @@ bool Graphics::getConstant(RendererInfo in, const char *&out)
return rendererInfo.find(in, out);
}
bool Graphics::getConstant(const char *in, SystemLimit &out)
{
return systemLimits.find(in, out);
}
bool Graphics::getConstant(SystemLimit in, const char *&out)
{
return systemLimits.find(in, out);
}
StringMap<Graphics::DrawMode, Graphics::DRAW_MAX_ENUM>::Entry Graphics::drawModeEntries[] =
{
{ "line", Graphics::DRAW_LINE },
@@ -157,6 +167,8 @@ StringMap<Graphics::Support, Graphics::SUPPORT_MAX_ENUM>::Entry Graphics::suppor
{ "mipmap", Graphics::SUPPORT_MIPMAP },
{ "dxt", Graphics::SUPPORT_DXT },
{ "bc5", Graphics::SUPPORT_BC5 },
{ "instancing", Graphics::SUPPORT_INSTANCING },
{ "srgb", Graphics::SUPPORT_SRGB },
};
StringMap<Graphics::Support, Graphics::SUPPORT_MAX_ENUM> Graphics::support(Graphics::supportEntries, sizeof(Graphics::supportEntries));
@@ -171,5 +183,15 @@ StringMap<Graphics::RendererInfo, Graphics::RENDERER_INFO_MAX_ENUM>::Entry Graph
StringMap<Graphics::RendererInfo, Graphics::RENDERER_INFO_MAX_ENUM> Graphics::rendererInfo(Graphics::rendererInfoEntries, sizeof(Graphics::rendererInfoEntries));
StringMap<Graphics::SystemLimit, Graphics::LIMIT_MAX_ENUM>::Entry Graphics::systemLimitEntries[] =
{
{"pointsize", Graphics::LIMIT_POINT_SIZE},
{"texturesize", Graphics::LIMIT_TEXTURE_SIZE},
{"multicanvas", Graphics::LIMIT_MULTI_CANVAS},
{"canvasfsaa", Graphics::LIMIT_CANVAS_FSAA},
};
StringMap<Graphics::SystemLimit, Graphics::LIMIT_MAX_ENUM> Graphics::systemLimits(Graphics::systemLimitEntries, sizeof(Graphics::systemLimitEntries));
} // graphics
} // love
+18 -1
View File
@@ -87,6 +87,8 @@ public:
SUPPORT_MIPMAP,
SUPPORT_DXT,
SUPPORT_BC5,
SUPPORT_INSTANCING,
SUPPORT_SRGB,
SUPPORT_MAX_ENUM
};
@@ -99,6 +101,15 @@ public:
RENDERER_INFO_MAX_ENUM
};
enum SystemLimit
{
LIMIT_POINT_SIZE,
LIMIT_TEXTURE_SIZE,
LIMIT_MULTI_CANVAS,
LIMIT_CANVAS_FSAA,
LIMIT_MAX_ENUM
};
virtual ~Graphics();
/**
@@ -111,7 +122,7 @@ public:
* @param width The viewport width.
* @param height The viewport height.
**/
virtual bool setMode(int width, int height) = 0;
virtual bool setMode(int width, int height, bool &sRGB) = 0;
/**
* Un-sets the current graphics display mode (uninitializing objects if
@@ -140,6 +151,9 @@ public:
static bool getConstant(const char *in, RendererInfo &out);
static bool getConstant(RendererInfo in, const char *&out);
static bool getConstant(const char *in, SystemLimit &out);
static bool getConstant(SystemLimit in, const char *&out);
private:
static StringMap<DrawMode, DRAW_MAX_ENUM>::Entry drawModeEntries[];
@@ -163,6 +177,9 @@ private:
static StringMap<RendererInfo, RENDERER_INFO_MAX_ENUM>::Entry rendererInfoEntries[];
static StringMap<RendererInfo, RENDERER_INFO_MAX_ENUM> rendererInfo;
static StringMap<SystemLimit, LIMIT_MAX_ENUM>::Entry systemLimitEntries[];
static StringMap<SystemLimit, LIMIT_MAX_ENUM> systemLimits;
}; // Graphics
} // graphics
+19
View File
@@ -109,6 +109,16 @@ bool Texture::getConstant(WrapMode in, const char *&out)
return wrapModes.find(in, out);
}
bool Texture::getConstant(const char *in, Format &out)
{
return formats.find(in, out);
}
bool Texture::getConstant(Format in, const char *&out)
{
return formats.find(in, out);
}
StringMap<Texture::FilterMode, Texture::FILTER_MAX_ENUM>::Entry Texture::filterModeEntries[] =
{
{ "linear", Texture::FILTER_LINEAR },
@@ -125,6 +135,15 @@ StringMap<Texture::WrapMode, Texture::WRAP_MAX_ENUM>::Entry Texture::wrapModeEnt
StringMap<Texture::WrapMode, Texture::WRAP_MAX_ENUM> Texture::wrapModes(Texture::wrapModeEntries, sizeof(Texture::wrapModeEntries));
StringMap<Texture::Format, Texture::FORMAT_MAX_ENUM>::Entry Texture::formatEntries[] =
{
{"normal", Texture::FORMAT_NORMAL},
{"hdr", Texture::FORMAT_HDR},
{"srgb", Texture::FORMAT_SRGB},
};
StringMap<Texture::Format, Texture::FORMAT_MAX_ENUM> Texture::formats(Texture::formatEntries, sizeof(Texture::formatEntries));
} // graphics
} // love
+15 -1
View File
@@ -55,6 +55,14 @@ public:
FILTER_MAX_ENUM
};
enum Format
{
FORMAT_NORMAL,
FORMAT_HDR,
FORMAT_SRGB,
FORMAT_MAX_ENUM
};
struct Filter
{
Filter();
@@ -88,7 +96,7 @@ public:
* @param kx Shear along the x-axis.
* @param ky Shear along the y-axis.
**/
virtual void drawq(Quad *quad, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const = 0;
virtual void drawq(Quad *quad, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) = 0;
virtual int getWidth() const;
virtual int getHeight() const;
@@ -111,6 +119,9 @@ public:
static bool getConstant(const char *in, WrapMode &out);
static bool getConstant(WrapMode in, const char *&out);
static bool getConstant(const char *in, Format &out);
static bool getConstant(Format in, const char *&out);
protected:
int width;
@@ -132,6 +143,9 @@ private:
static StringMap<WrapMode, WRAP_MAX_ENUM>::Entry wrapModeEntries[];
static StringMap<WrapMode, WRAP_MAX_ENUM> wrapModes;
static StringMap<Format, FORMAT_MAX_ENUM>::Entry formatEntries[];
static StringMap<Format, FORMAT_MAX_ENUM> formats;
}; // Texture
} // graphics
+347 -83
View File
@@ -55,10 +55,25 @@ struct FramebufferStrategy
/**
* @param[in] width Width of the stencil buffer
* @param[in] height Height of the stencil buffer
* @param[in] samples Number of samples to use
* @param[out] stencil Name for stencil buffer
* @return Whether the stencil buffer was successfully created
**/
virtual bool createStencil(int, int, GLuint &)
virtual bool createStencil(int, int, int, GLuint &)
{
return false;
}
/// Create a MSAA renderbuffer and attach it to the active FBO.
/**
* @param[in] width Width of the MSAA buffer
* @param[in] height Height of the MSAA buffer
* @param[inout] samples Number of samples to use
* @param[in] internalformat The internal format to use for the buffer
* @param[out] buffer Name for the MSAA buffer
* @return Whether the MSAA buffer was successfully created and attached
**/
virtual bool createMSAABuffer(int, int, int &, GLenum, GLuint &)
{
return false;
}
@@ -68,7 +83,7 @@ struct FramebufferStrategy
* @param[in] framebuffer Framebuffer name
* @param[in] depth_stencil Name for packed depth and stencil buffer
*/
virtual void deleteFBO(GLuint, GLuint) {}
virtual void deleteFBO(GLuint, GLuint, GLuint) {}
virtual void bindFBO(GLuint) {}
/// attach additional canvases to the active framebuffer for rendering
@@ -93,8 +108,11 @@ struct FramebufferStrategyGL3 : public FramebufferStrategy
glGenFramebuffers(1, &framebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D, texture, 0);
if (texture != 0)
{
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D, texture, 0);
}
// check status
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
@@ -104,27 +122,62 @@ struct FramebufferStrategyGL3 : public FramebufferStrategy
return status;
}
virtual bool createStencil(int width, int height, GLuint &stencil)
virtual bool createStencil(int width, int height, int samples, 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);
if (samples > 1)
glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, GL_DEPTH_STENCIL, width, height);
else
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_STENCIL, width, height);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
GL_RENDERBUFFER, stencil);
GL_RENDERBUFFER, stencil);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
// check status
return glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE;
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
{
glDeleteRenderbuffers(1, &stencil);
stencil = 0;
return false;
}
return true;
}
virtual void deleteFBO(GLuint framebuffer, GLuint depth_stencil)
virtual bool createMSAABuffer(int width, int height, int &samples, GLenum internalformat, GLuint &buffer)
{
glGenRenderbuffers(1, &buffer);
glBindRenderbuffer(GL_RENDERBUFFER, buffer);
glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, internalformat,
width, height);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_RENDERBUFFER, buffer);
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_SAMPLES, &samples);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
{
glDeleteRenderbuffers(1, &buffer);
buffer = 0;
return false;
}
return true;
}
virtual void deleteFBO(GLuint framebuffer, GLuint depth_stencil, GLuint msaa_buffer)
{
if (depth_stencil != 0)
glDeleteRenderbuffers(1, &depth_stencil);
if (msaa_buffer != 0)
glDeleteRenderbuffers(1, &msaa_buffer);
if (framebuffer != 0)
glDeleteFramebuffers(1, &framebuffer);
}
@@ -189,27 +242,71 @@ struct FramebufferStrategyPackedEXT : public FramebufferStrategy
return status;
}
virtual bool createStencil(int width, int height, GLuint &stencil)
virtual bool createStencil(int width, int height, int samples, GLuint &stencil)
{
// create combined depth/stencil buffer
glDeleteRenderbuffers(1, &stencil);
glDeleteRenderbuffersEXT(1, &stencil);
glGenRenderbuffersEXT(1, &stencil);
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, stencil);
glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_STENCIL_EXT,
width, height);
if (samples > 1)
{
glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER, samples,
GL_DEPTH_STENCIL, width, height);
}
else
{
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;
if (glCheckFramebufferStatusEXT(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
{
glDeleteRenderbuffersEXT(1, &stencil);
stencil = 0;
return false;
}
return true;
}
virtual void deleteFBO(GLuint framebuffer, GLuint depth_stencil)
virtual bool createMSAABuffer(int width, int height, int &samples, GLenum internalformat, GLuint &buffer)
{
if (!GLEE_EXT_framebuffer_multisample)
return false;
glGenRenderbuffersEXT(1, &buffer);
glBindRenderbufferEXT(GL_RENDERBUFFER, buffer);
glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER, samples,
internalformat, width, height);
glFramebufferRenderbufferEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_RENDERBUFFER, buffer);
glGetRenderbufferParameterivEXT(GL_RENDERBUFFER, GL_RENDERBUFFER_SAMPLES, &samples);
glBindRenderbufferEXT(GL_RENDERBUFFER, 0);
if (glCheckFramebufferStatusEXT(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
{
glDeleteRenderbuffersEXT(1, &buffer);
buffer = 0;
return false;
}
return true;
}
virtual void deleteFBO(GLuint framebuffer, GLuint depth_stencil, GLuint msaa_buffer)
{
if (depth_stencil != 0)
glDeleteRenderbuffersEXT(1, &depth_stencil);
if (msaa_buffer != 0)
glDeleteRenderbuffersEXT(1, &msaa_buffer);
if (framebuffer != 0)
glDeleteFramebuffersEXT(1, &framebuffer);
}
@@ -254,33 +351,50 @@ struct FramebufferStrategyPackedEXT : public FramebufferStrategy
struct FramebufferStrategyEXT : public FramebufferStrategyPackedEXT
{
virtual bool createStencil(int width, int height, GLuint &stencil)
virtual bool createStencil(int width, int height, int samples, GLuint &stencil)
{
// create stencil buffer
glDeleteRenderbuffers(1, &stencil);
glDeleteRenderbuffersEXT(1, &stencil);
glGenRenderbuffersEXT(1, &stencil);
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, stencil);
glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_STENCIL_INDEX,
width, height);
if (samples > 1)
{
glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER, samples,
GL_STENCIL_INDEX, width, height);
}
else
{
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;
if (glCheckFramebufferStatusEXT(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
{
glDeleteRenderbuffersEXT(1, &stencil);
stencil = 0;
return false;
}
return true;
}
bool isSupported()
{
GLuint fb = 0, stencil = 0;
GLenum status = createFBO(fb, 0);
deleteFBO(fb, stencil);
deleteFBO(fb, stencil, 0);
return status == GL_FRAMEBUFFER_COMPLETE;
}
};
FramebufferStrategy *strategy = NULL;
FramebufferStrategy *strategy = nullptr;
FramebufferStrategy strategyNone;
@@ -290,8 +404,9 @@ FramebufferStrategyPackedEXT strategyPackedEXT;
FramebufferStrategyEXT strategyEXT;
Canvas *Canvas::current = NULL;
Canvas *Canvas::current = nullptr;
OpenGL::Viewport Canvas::systemViewport = OpenGL::Viewport();
bool Canvas::screenHasSRGB = false;
static void getStrategy()
{
@@ -308,14 +423,15 @@ static void getStrategy()
}
}
static int maxFBOColorAttachments = 0;
static int maxDrawBuffers = 0;
Canvas::Canvas(int width, int height, TextureType texture_type)
Canvas::Canvas(int width, int height, Texture::Format format, int fsaa)
: fbo(0)
, resolve_fbo(0)
, texture(0)
, fsaa_buffer(0)
, depth_stencil(0)
, texture_type(texture_type)
, format(format)
, fsaa_samples(fsaa)
, fsaa_dirty(false)
{
this->width = width;
this->height = height;
@@ -357,9 +473,50 @@ Canvas::~Canvas()
unloadVolatile();
}
bool Canvas::createFSAAFBO(GLenum internalformat)
{
// Create our FBO without a texture.
status = strategy->createFBO(fbo, 0);
GLuint previous = 0;
if (current != this)
{
if (current != nullptr)
previous = current->fbo;
strategy->bindFBO(fbo);
}
// Create and attach the MSAA buffer for our FBO.
if (strategy->createMSAABuffer(width, height, fsaa_samples, internalformat, fsaa_buffer))
status = GL_FRAMEBUFFER_COMPLETE;
else
status = GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
// Create the FBO used for the MSAA resolve, and attach the texture.
if (status == GL_FRAMEBUFFER_COMPLETE)
status = strategy->createFBO(resolve_fbo, texture);
if (status != GL_FRAMEBUFFER_COMPLETE)
{
// Clean up.
strategy->deleteFBO(fbo, 0, fsaa_buffer);
strategy->deleteFBO(resolve_fbo, 0, 0);
fbo = fsaa_buffer = resolve_fbo = 0;
fsaa_samples = 0;
}
if (current != this)
strategy->bindFBO(previous);
return status == GL_FRAMEBUFFER_COMPLETE;
}
bool Canvas::loadVolatile()
{
fbo = depth_stencil = texture = 0;
resolve_fbo = fsaa_buffer = 0;
status = GL_FRAMEBUFFER_COMPLETE;
// glTexImage2D is guaranteed to error in this case.
if (width > gl.getMaxTextureSize() || height > gl.getMaxTextureSize())
@@ -376,13 +533,17 @@ bool Canvas::loadVolatile()
GLint internalformat;
GLenum textype;
switch (texture_type)
switch (format)
{
case TYPE_HDR:
case Texture::FORMAT_HDR:
internalformat = GL_RGBA16F;
textype = GL_FLOAT;
break;
case TYPE_NORMAL:
case Texture::FORMAT_SRGB:
internalformat = GL_SRGB8_ALPHA8;
textype = GL_UNSIGNED_BYTE;
break;
case Texture::FORMAT_NORMAL:
default:
internalformat = GL_RGBA8;
textype = GL_UNSIGNED_BYTE;
@@ -406,21 +567,44 @@ bool Canvas::loadVolatile()
return false;
}
status = strategy->createFBO(fbo, texture);
int max_samples = 0;
if (GLEE_VERSION_3_0 || GLEE_ARB_framebuffer_object
|| GLEE_EXT_framebuffer_multisample)
{
glGetIntegerv(GL_MAX_SAMPLES, &max_samples);
}
if (fsaa_samples > max_samples)
fsaa_samples = max_samples;
// Try to create a FSAA FBO if requested.
bool fsaasuccess = false;
if (fsaa_samples > 1)
fsaasuccess = createFSAAFBO(internalformat);
// On failure (or no requested FSAA), fall back to a regular FBO.
if (!fsaasuccess)
status = strategy->createFBO(fbo, texture);
if (status != GL_FRAMEBUFFER_COMPLETE)
return false;
clear(Color(0, 0, 0, 0));
fsaa_dirty = (fsaa_buffer != 0);
return true;
}
void Canvas::unloadVolatile()
{
strategy->deleteFBO(fbo, depth_stencil);
strategy->deleteFBO(fbo, depth_stencil, fsaa_buffer);
strategy->deleteFBO(resolve_fbo, 0, 0);
gl.deleteTexture(texture);
fbo = depth_stencil = texture = 0;
resolve_fbo = fsaa_buffer = 0;
for (size_t i = 0; i < attachedCanvases.size(); i++)
attachedCanvases[i]->release();
@@ -428,13 +612,12 @@ void Canvas::unloadVolatile()
attachedCanvases.clear();
}
void Canvas::drawv(const Matrix &t, const Vertex *v) const
void Canvas::drawv(const Matrix &t, const Vertex *v)
{
glPushMatrix();
glMultMatrixf((const GLfloat *)t.getElements());
gl.bindTexture(texture);
predraw();
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
@@ -447,11 +630,13 @@ void Canvas::drawv(const Matrix &t, const Vertex *v) const
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
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) const
void Canvas::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky)
{
static Matrix t;
t.setTransformation(x, y, angle, sx, sy, ox, oy, kx, ky);
@@ -459,7 +644,7 @@ void Canvas::draw(float x, float y, float angle, float sx, float sy, float ox, f
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
void Canvas::drawq(Quad *quad, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky)
{
static Matrix t;
t.setTransformation(x, y, angle, sx, sy, ox, oy, kx, ky);
@@ -487,8 +672,12 @@ GLuint Canvas::getGLTexture() const
return texture;
}
void Canvas::predraw() const
void Canvas::predraw()
{
// We need to make sure the texture is up-to-date by resolving the MSAA
// buffer (which we render to when the canvas is active) to it.
resolveMSAA();
gl.bindTexture(texture);
}
@@ -498,12 +687,19 @@ void Canvas::setupGrab()
if (current == this)
return;
// cleanup after previous fbo
if (current != NULL)
current->stopGrab();
// cleanup after previous Canvas
if (current != nullptr)
{
systemViewport = current->systemViewport;
current->stopGrab(true);
}
else
systemViewport = gl.getViewport();
// indicate we are using this Canvas.
current = this;
// bind the framebuffer object.
systemViewport = gl.getViewport();
strategy->bindFBO(fbo);
gl.setViewport(OpenGL::Viewport(0, 0, width, height));
@@ -518,8 +714,14 @@ void Canvas::setupGrab()
// Switch back to modelview matrix
glMatrixMode(GL_MODELVIEW);
// indicate we are using this fbo
current = this;
// Make sure the correct sRGB setting is used when drawing to the canvas.
if (format == FORMAT_SRGB)
glEnable(GL_FRAMEBUFFER_SRGB);
else if (screenHasSRGB)
glDisable(GL_FRAMEBUFFER_SRGB);
if (fsaa_buffer != 0)
fsaa_dirty = true;
}
void Canvas::startGrab(const std::vector<Canvas *> &canvases)
@@ -533,8 +735,11 @@ void Canvas::startGrab(const std::vector<Canvas *> &canvases)
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))
if ((int) canvases.size() + 1 > gl.getMaxRenderTargets())
throw love::Exception("This system can't simultaniously render to %d canvases.", canvases.size()+1);
if (fsaa_samples != 0)
throw love::Exception("Multi-canvas rendering is not supported with FSAA.");
}
for (size_t i = 0; i < canvases.size(); i++)
@@ -542,8 +747,11 @@ void Canvas::startGrab(const std::vector<Canvas *> &canvases)
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 (canvases[i]->getTextureFormat() != format)
throw love::Exception("All canvas arguments must have the same texture format.");
if (canvases[i]->getFSAA() != 0)
throw love::Exception("Multi-canvas rendering is not supported with FSAA.");
if (!canvaseschanged && canvases[i] != attachedCanvases[i])
canvaseschanged = true;
@@ -584,21 +792,33 @@ void Canvas::startGrab()
attachedCanvases.clear();
}
void Canvas::stopGrab()
void Canvas::stopGrab(bool switchingToOtherCanvas)
{
// i am not grabbing. leave me alone
if (current != this)
return;
// bind default
strategy->bindFBO(0);
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
current = nullptr;
if (switchingToOtherCanvas)
{
if (format == FORMAT_SRGB)
glDisable(GL_FRAMEBUFFER_SRGB);
}
else
{
// bind system framebuffer.
strategy->bindFBO(0);
current = nullptr;
gl.setViewport(systemViewport);
gl.setViewport(systemViewport);
if (format == FORMAT_SRGB && !screenHasSRGB)
glDisable(GL_FRAMEBUFFER_SRGB);
else if (format != FORMAT_SRGB && screenHasSRGB)
glEnable(GL_FRAMEBUFFER_SRGB);
}
}
void Canvas::clear(Color c)
@@ -651,6 +871,9 @@ void Canvas::clear(Color c)
if (current != this)
strategy->bindFBO(previous);
if (fsaa_buffer != 0)
fsaa_dirty = true;
}
bool Canvas::checkCreateStencil()
@@ -662,7 +885,7 @@ bool Canvas::checkCreateStencil()
if (current != this)
strategy->bindFBO(fbo);
bool success = strategy->createStencil(width, height, depth_stencil);
bool success = strategy->createStencil(width, height, fsaa_samples, depth_stencil);
if (current && current != this)
strategy->bindFBO(current->fbo);
@@ -674,12 +897,22 @@ bool Canvas::checkCreateStencil()
love::image::ImageData *Canvas::getImageData(love::image::Image *image)
{
resolveMSAA();
int row = 4 * width;
int size = row * height;
GLubyte *pixels = new GLubyte[size];
strategy->bindFBO(fbo);
// Our texture is attached to 'resolve_fbo' when we use MSAA.
if (fsaa_samples > 1 && (GLEE_VERSION_3_0 || GLEE_ARB_framebuffer_object))
glBindFramebuffer(GL_READ_FRAMEBUFFER, resolve_fbo);
else if (fsaa_samples > 1 && GLEE_EXT_framebuffer_multisample)
glBindFramebufferEXT(GL_READ_FRAMEBUFFER, resolve_fbo);
else
strategy->bindFBO(fbo);
glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
if (current)
strategy->bindFBO(current->fbo);
else
@@ -692,10 +925,17 @@ love::image::ImageData *Canvas::getImageData(love::image::Image *image)
void Canvas::getPixel(unsigned char* pixel_rgba, int x, int y)
{
if (current != this)
resolveMSAA();
// Our texture is attached to 'resolve_fbo' when we use MSAA.
if (fsaa_samples > 1 && (GLEE_VERSION_3_0 || GLEE_ARB_framebuffer_object))
glBindFramebuffer(GL_READ_FRAMEBUFFER, resolve_fbo);
else if (fsaa_samples > 1 && GLEE_EXT_framebuffer_multisample)
glBindFramebufferEXT(GL_READ_FRAMEBUFFER, resolve_fbo);
else if (current != this)
strategy->bindFBO(fbo);
glReadPixels(x, height - y, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel_rgba);
glReadPixels(x, y, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel_rgba);
if (current && current != this)
strategy->bindFBO(current->fbo);
@@ -703,6 +943,44 @@ void Canvas::getPixel(unsigned char* pixel_rgba, int x, int y)
strategy->bindFBO(0);
}
bool Canvas::resolveMSAA()
{
if (resolve_fbo == 0 || fsaa_buffer == 0)
return false;
if (!fsaa_dirty)
return true;
GLuint previous = 0;
if (current != nullptr)
previous = current->fbo;
// Do the MSAA resolve by blitting the MSAA renderbuffer to the texture.
if (GLEE_VERSION_3_0 || GLEE_ARB_framebuffer_object)
{
glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, resolve_fbo);
glBlitFramebuffer(0, 0, width, height, 0, 0, width, height,
GL_COLOR_BUFFER_BIT, GL_NEAREST);
}
else if (GLEE_EXT_framebuffer_multisample && GLEE_EXT_framebuffer_blit)
{
glBindFramebufferEXT(GL_READ_FRAMEBUFFER, fbo);
glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER, resolve_fbo);
glBlitFramebufferEXT(0, 0, width, height, 0, 0, width, height,
GL_COLOR_BUFFER_BIT, GL_NEAREST);
}
else
return false;
strategy->bindFBO(previous);
if (current != this)
fsaa_dirty = false;
return true;
}
bool Canvas::isSupported()
{
getStrategy();
@@ -714,19 +992,22 @@ bool Canvas::isHDRSupported()
return GLEE_VERSION_3_0 || (isSupported() && GLEE_ARB_texture_float);
}
bool Canvas::isMultiCanvasSupported()
bool Canvas::isSRGBSupported()
{
if (!(isSupported() && (GLEE_VERSION_2_0 || GLEE_ARB_draw_buffers)))
if (GLEE_VERSION_3_0)
return true;
if (!isSupported())
return false;
if (maxFBOColorAttachments == 0 || maxDrawBuffers == 0)
{
glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, &maxFBOColorAttachments);
glGetIntegerv(GL_MAX_DRAW_BUFFERS, &maxDrawBuffers);
}
return (GLEE_ARB_framebuffer_sRGB || GLEE_EXT_framebuffer_sRGB)
&& GLEE_EXT_texture_sRGB;
}
// system must support at least 4 simultanious active canvases
return maxFBOColorAttachments >= 4 && maxDrawBuffers >= 4;
bool Canvas::isMultiCanvasSupported()
{
// system must support at least 4 simultanious active canvases.
return gl.getMaxRenderTargets() >= 4;
}
void Canvas::bindDefaultCanvas()
@@ -735,23 +1016,6 @@ void Canvas::bindDefaultCanvas()
current->stopGrab();
}
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
+30 -23
View File
@@ -39,14 +39,7 @@ class Canvas : public Texture
{
public:
enum TextureType
{
TYPE_NORMAL,
TYPE_HDR,
TYPE_MAX_ENUM
};
Canvas(int width, int height, TextureType texture_type = TYPE_NORMAL);
Canvas(int width, int height, Texture::Format format = Texture::FORMAT_NORMAL, int fsaa = 0);
virtual ~Canvas();
// Implements Volatile.
@@ -54,14 +47,14 @@ public:
virtual void unloadVolatile();
// Implements Drawable.
virtual void draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const;
virtual void draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky);
// Implements Texture.
virtual void drawq(Quad *quad, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const;
virtual void drawq(Quad *quad, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky);
virtual void setFilter(const Texture::Filter &f);
virtual void setWrap(const Texture::Wrap &w);
virtual GLuint getGLTexture() const;
virtual void predraw() const;
virtual void predraw();
/**
* @param canvases A list of other canvases to temporarily attach to this one,
@@ -69,7 +62,7 @@ public:
**/
void startGrab(const std::vector<Canvas *> &canvases);
void startGrab();
void stopGrab();
void stopGrab(bool switchingToOtherCanvas = false);
void clear(Color c);
@@ -92,42 +85,56 @@ public:
return status;
}
inline TextureType getTextureType() const
inline Texture::Format getTextureFormat() const
{
return texture_type;
return format;
}
inline int getFSAA() const
{
return fsaa_samples;
}
bool resolveMSAA();
static bool isSupported();
static bool isHDRSupported();
static bool isSRGBSupported();
static bool isMultiCanvasSupported();
static bool getConstant(const char *in, TextureType &out);
static bool getConstant(TextureType in, const char *&out);
static Canvas *current;
static void bindDefaultCanvas();
// The viewport dimensions of the system (default) framebuffer.
static OpenGL::Viewport systemViewport;
// Whether the main screen should have linear -> sRGB conversions enabled.
static bool screenHasSRGB;
private:
bool createFSAAFBO(GLenum internalformat);
GLuint fbo;
GLuint resolve_fbo;
GLuint texture;
GLuint fsaa_buffer;
GLuint depth_stencil;
TextureType texture_type;
Format format;
GLenum status;
std::vector<Canvas *> attachedCanvases;
void setupGrab();
void drawv(const Matrix &t, const Vertex *v) const;
int fsaa_samples;
bool fsaa_dirty;
static StringMap<TextureType, TYPE_MAX_ENUM>::Entry textureTypeEntries[];
static StringMap<TextureType, TYPE_MAX_ENUM> textureTypes;
};
void setupGrab();
void drawv(const Matrix &t, const Vertex *v);
}; // Canvas
} // opengl
} // graphics
+35 -4
View File
@@ -46,6 +46,7 @@ Font::Font(love::font::Rasterizer *r, const Texture::Filter &filter)
, lineHeight(1)
, mSpacing(1)
, filter(filter)
, useSpacesAsTab(false)
{
this->filter.mipmap = Texture::FILTER_NONE;
@@ -67,13 +68,16 @@ Font::Font(love::font::Rasterizer *r, const Texture::Filter &filter)
textureWidth = TEXTURE_WIDTHS[textureSizeIndex];
textureHeight = TEXTURE_HEIGHTS[textureSizeIndex];
love::font::GlyphData *gd = 0;
love::font::GlyphData *gd = nullptr;
try
{
gd = r->getGlyphData(32);
gd = r->getGlyphData(32); // Space character.
type = (gd->getFormat() == love::font::GlyphData::FORMAT_LUMINANCE_ALPHA) ? FONT_TRUETYPE : FONT_IMAGE;
if (!r->hasGlyph(9)) // No tab character in the Rasterizer.
useSpacesAsTab = true;
loadVolatile();
}
catch (love::Exception &)
@@ -172,7 +176,26 @@ void Font::createTexture()
Font::Glyph *Font::addGlyph(uint32 glyph)
{
love::font::GlyphData *gd = rasterizer->getGlyphData(glyph);
love::font::GlyphData *gd = nullptr;
// Use spaces for the tab 'glyph'.
if (glyph == 9 && useSpacesAsTab)
{
love::font::GlyphData *spacegd = rasterizer->getGlyphData(32);
love::font::GlyphMetrics gm = {};
gm.advance = spacegd->getAdvance() * SPACES_PER_TAB;
gm.bearingX = spacegd->getBearingX();
gm.bearingY = spacegd->getBearingY();
love::font::GlyphData::Format f = spacegd->getFormat();
spacegd->release();
gd = new love::font::GlyphData(glyph, gm, f);
}
else
gd = rasterizer->getGlyphData(glyph);
int w = gd->getWidth();
int h = gd->getHeight();
@@ -186,7 +209,15 @@ Font::Glyph *Font::addGlyph(uint32 glyph)
if (textureY + h + TEXTURE_PADDING > textureHeight)
{
// totally out of space - new texture!
createTexture();
try
{
createTexture();
}
catch (love::Exception &)
{
gd->release();
throw;
}
}
Glyph *g = new Glyph;
+8 -3
View File
@@ -175,7 +175,7 @@ private:
return texture < other.texture;
else
return startvertex < other.startvertex;
};
}
};
bool initializeTexture(GLenum format);
@@ -202,14 +202,19 @@ private:
FontType type;
Texture::Filter filter;
int textureX, textureY;
int rowHeight;
bool useSpacesAsTab;
static const int NUM_TEXTURE_SIZES = 7;
static const int TEXTURE_WIDTHS[NUM_TEXTURE_SIZES];
static const int TEXTURE_HEIGHTS[NUM_TEXTURE_SIZES];
static const int TEXTURE_PADDING = 1;
int textureX, textureY;
int rowHeight;
// This will be used if the Rasterizer doesn't have a tab character itself.
static const int SPACES_PER_TAB = 4;
}; // Font
+182 -96
View File
@@ -53,12 +53,18 @@ Graphics::Graphics()
, width(0)
, height(0)
, created(false)
, activeStencil(false)
, savedState()
{
currentWindow = love::window::sdl::Window::createSingleton();
int w, h;
love::window::WindowSettings wsettings;
currentWindow->getWindow(w, h, wsettings);
if (currentWindow->isCreated())
setMode(currentWindow->getWidth(), currentWindow->getHeight());
setMode(w, h, wsettings.sRGB);
}
Graphics::~Graphics()
@@ -95,6 +101,8 @@ DisplayState Graphics::saveState()
for (int i = 0; i < 4; i++)
s.colorMask[i] = colorMask[i];
wireframe = isWireframe();
return s;
}
@@ -111,6 +119,7 @@ void Graphics::restoreState(const DisplayState &s)
else
setScissor();
setColorMask(s.colorMask[0], s.colorMask[1], s.colorMask[2], s.colorMask[3]);
setWireframe(s.wireframe);
}
void Graphics::setViewportSize(int width, int height)
@@ -147,7 +156,7 @@ void Graphics::setViewportSize(int width, int height)
c->startGrab(c->getAttachedCanvases());
}
bool Graphics::setMode(int width, int height)
bool Graphics::setMode(int width, int height, bool &sRGB)
{
this->width = width;
this->height = height;
@@ -166,9 +175,6 @@ bool Graphics::setMode(int width, int height)
// Enable blending
glEnable(GL_BLEND);
// "Normal" blending
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// Enable all color component writes.
setColorMask(true, true, true, true);
@@ -207,6 +213,31 @@ bool Graphics::setMode(int width, int height)
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)
{
if (sRGB)
glEnable(GL_FRAMEBUFFER_SRGB);
else
glDisable(GL_FRAMEBUFFER_SRGB);
}
else
sRGB = false;
Canvas::screenHasSRGB = sRGB;
bool enabledebug = false;
if (GLEE_VERSION_3_0)
{
// Enable OpenGL's debug output if a debug context has been created.
GLint flags = 0;
glGetIntegerv(GL_CONTEXT_FLAGS, &flags);
enabledebug = (flags & GL_CONTEXT_FLAG_DEBUG_BIT) != 0;
}
setDebug(enabledebug);
return true;
}
@@ -223,6 +254,61 @@ void Graphics::unSetMode()
gl.deInitContext();
}
static void APIENTRY debugCB(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei /*len*/, const GLchar *msg, GLvoid* /*usr*/)
{
// Human-readable strings for the debug info.
const char *sourceStr = OpenGL::debugSourceString(source);
const char *typeStr = OpenGL::debugTypeString(type);
const char *severityStr = OpenGL::debugSeverityString(severity);
const char *fmt = "OpenGL: %s [source=%s, type=%s, severity=%s, id=%d]\n";
printf(fmt, msg, sourceStr, typeStr, severityStr, id);
}
void Graphics::setDebug(bool enable)
{
// Make sure debug output is supported. The AMD ext. is a bit different
// so we don't make use of it, since AMD drivers now support KHR_debug.
if (!(GLEE_VERSION_4_3 || GLEE_KHR_debug || GLEE_ARB_debug_output))
return;
// Ugly hack to reduce code duplication.
if (GLEE_ARB_debug_output && !(GLEE_VERSION_4_3 || GLEE_KHR_debug))
{
glDebugMessageCallback = (GLEEPFNGLDEBUGMESSAGECALLBACKPROC) glDebugMessageCallbackARB;
glDebugMessageControl = (GLEEPFNGLDEBUGMESSAGECONTROLPROC) glDebugMessageControlARB;
}
if (!enable)
{
// Disable the debug callback function.
glDebugMessageCallback(nullptr, nullptr);
// We can disable debug output entirely with KHR_debug.
if (GLEE_VERSION_4_3 || GLEE_KHR_debug)
glDisable(GL_DEBUG_OUTPUT);
return;
}
// We don't want asynchronous debug output.
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
glDebugMessageCallback(debugCB, nullptr);
// Initially, enable everything.
glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, 0, GL_TRUE);
// Disable messages about deprecated OpenGL functionality.
glDebugMessageControl(GL_DEBUG_SOURCE_API, GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR, GL_DONT_CARE, 0, 0, GL_FALSE);
glDebugMessageControl(GL_DEBUG_SOURCE_SHADER_COMPILER, GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR, GL_DONT_CARE, 0, 0, GL_FALSE);
if (GLEE_VERSION_4_3 || GLEE_KHR_debug)
glEnable(GL_DEBUG_OUTPUT);
::printf("OpenGL debug output enabled (LOVE_GRAPHICS_DEBUG=1)\n");
}
void Graphics::reset()
{
DisplayState s;
@@ -295,6 +381,8 @@ void Graphics::defineStencil()
glEnable(GL_STENCIL_TEST);
glStencilFunc(GL_ALWAYS, 1, 1);
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
activeStencil = true;
}
void Graphics::useStencil(bool invert)
@@ -306,19 +394,18 @@ void Graphics::useStencil(bool invert)
void Graphics::discardStencil()
{
if (!activeStencil)
return;
setColorMask(colorMask[0], colorMask[1], colorMask[2], colorMask[3]);
glDisable(GL_STENCIL_TEST);
activeStencil = false;
}
int Graphics::getMaxTextureSize() const
{
return gl.getMaxTextureSize();
}
Image *Graphics::newImage(love::image::ImageData *data)
Image *Graphics::newImage(love::image::ImageData *data, Texture::Format format)
{
// Create the image.
Image *image = new Image(data);
Image *image = new Image(data, format);
if (!isCreated())
return image;
@@ -342,10 +429,10 @@ Image *Graphics::newImage(love::image::ImageData *data)
return image;
}
Image *Graphics::newImage(love::image::CompressedData *cdata)
Image *Graphics::newImage(love::image::CompressedData *cdata, Texture::Format format)
{
// Create the image.
Image *image = new Image(cdata);
Image *image = new Image(cdata, format);
if (!isCreated())
return image;
@@ -389,11 +476,14 @@ ParticleSystem *Graphics::newParticleSystem(Texture *texture, int size)
return new ParticleSystem(texture, size);
}
Canvas *Graphics::newCanvas(int width, int height, Canvas::TextureType texture_type)
Canvas *Graphics::newCanvas(int width, int height, Texture::Format format, int fsaa)
{
if (texture_type == Canvas::TYPE_HDR && !Canvas::isHDRSupported())
if (format == Texture::FORMAT_HDR && !Canvas::isHDRSupported())
throw Exception("HDR Canvases are not supported by your OpenGL implementation");
if (format == Texture::FORMAT_SRGB && !Canvas::isSRGBSupported())
throw Exception("sRGB Canvases are not supported by your OpenGL implementation");
if (width > gl.getMaxTextureSize())
throw Exception("Cannot create canvas: width of %d pixels is too large for this system.", width);
else if (height > gl.getMaxTextureSize())
@@ -402,7 +492,7 @@ Canvas *Graphics::newCanvas(int width, int height, Canvas::TextureType texture_t
while (GL_NO_ERROR != glGetError())
/* clear opengl error flag */;
Canvas *canvas = new Canvas(width, height, texture_type);
Canvas *canvas = new Canvas(width, height, format, fsaa);
GLenum err = canvas->getStatus();
// everything ok, return canvas (early out)
@@ -457,6 +547,11 @@ Mesh *Graphics::newMesh(const std::vector<Vertex> &vertices, Mesh::DrawMode mode
return new Mesh(vertices, mode);
}
Mesh *Graphics::newMesh(int vertexcount, Mesh::DrawMode mode)
{
return new Mesh(vertexcount, mode);
}
void Graphics::setColor(const Color &c)
{
gl.setColor(c);
@@ -509,22 +604,16 @@ const bool *Graphics::getColorMask() const
void Graphics::setBlendMode(Graphics::BlendMode mode)
{
const int gl_1_4 = GLEE_VERSION_1_4;
GLenum func = GL_FUNC_ADD;
GLenum src_rgb = GL_ONE;
GLenum src_a = GL_ONE;
GLenum dst_rgb = GL_ZERO;
GLenum dst_a = GL_ZERO;
OpenGL::BlendState state = {GL_ONE, GL_ONE, GL_ZERO, GL_ZERO, GL_FUNC_ADD};
switch (mode)
{
case BLEND_ALPHA:
if (gl_1_4 || GLEE_EXT_blend_func_separate)
if (GLEE_VERSION_1_4 || GLEE_EXT_blend_func_separate)
{
src_rgb = GL_SRC_ALPHA;
src_a = GL_ONE;
dst_rgb = dst_a = GL_ONE_MINUS_SRC_ALPHA;
state.srcRGB = GL_SRC_ALPHA;
state.srcA = GL_ONE;
state.dstRGB = state.dstA = GL_ONE_MINUS_SRC_ALPHA;
}
else
{
@@ -532,98 +621,56 @@ 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).
src_rgb = src_a = GL_SRC_ALPHA;
dst_rgb = dst_a = GL_ONE_MINUS_SRC_ALPHA;
state.srcRGB = state.srcA = GL_SRC_ALPHA;
state.dstRGB = state.dstA = GL_ONE_MINUS_SRC_ALPHA;
}
break;
case BLEND_MULTIPLICATIVE:
src_rgb = src_a = GL_DST_COLOR;
dst_rgb = dst_a = GL_ZERO;
state.srcRGB = state.srcA = GL_DST_COLOR;
state.dstRGB = state.dstA = GL_ZERO;
break;
case BLEND_PREMULTIPLIED:
src_rgb = src_a = GL_ONE;
dst_rgb = dst_a = GL_ONE_MINUS_SRC_ALPHA;
state.srcRGB = state.srcA = GL_ONE;
state.dstRGB = state.dstA = GL_ONE_MINUS_SRC_ALPHA;
break;
case BLEND_SUBTRACTIVE:
func = GL_FUNC_REVERSE_SUBTRACT;
state.func = GL_FUNC_REVERSE_SUBTRACT;
case BLEND_ADDITIVE:
src_rgb = src_a = GL_SRC_ALPHA;
dst_rgb = dst_a = GL_ONE;
state.srcRGB = state.srcA = GL_SRC_ALPHA;
state.dstRGB = state.dstA = GL_ONE;
break;
case BLEND_REPLACE:
default:
src_rgb = src_a = GL_ONE;
dst_rgb = dst_a = GL_ZERO;
state.srcRGB = state.srcA = GL_ONE;
state.dstRGB = state.dstA = GL_ZERO;
break;
}
if (gl_1_4 || GLEE_ARB_imaging)
glBlendEquation(func);
else if (GLEE_EXT_blend_minmax && GLEE_EXT_blend_subtract)
glBlendEquationEXT(func);
else
{
if (func == GL_FUNC_REVERSE_SUBTRACT)
throw Exception("This graphics card does not support the subtractive blend mode!");
// GL_FUNC_ADD is the default even without access to glBlendEquation, so that'll still work.
}
if (src_rgb == src_a && dst_rgb == dst_a)
glBlendFunc(src_rgb, dst_rgb);
else
{
if (gl_1_4)
glBlendFuncSeparate(src_rgb, dst_rgb, src_a, dst_a);
else if (GLEE_EXT_blend_func_separate)
glBlendFuncSeparateEXT(src_rgb, dst_rgb, src_a, dst_a);
else
throw Exception("This graphics card does not support separated rgb and alpha blend functions!");
}
gl.setBlendState(state);
}
Graphics::BlendMode Graphics::getBlendMode() const
{
const int gl_1_4 = GLEE_VERSION_1_4;
OpenGL::BlendState state = gl.getBlendState();
GLint src_rgb, src_a, dst_rgb, dst_a;
GLint equation = GL_FUNC_ADD;
if (gl_1_4 || GLEE_EXT_blend_func_separate)
{
glGetIntegerv(GL_BLEND_SRC_RGB, &src_rgb);
glGetIntegerv(GL_BLEND_SRC_ALPHA, &src_a);
glGetIntegerv(GL_BLEND_DST_RGB, &dst_rgb);
glGetIntegerv(GL_BLEND_DST_ALPHA, &dst_a);
}
else
{
glGetIntegerv(GL_BLEND_SRC, &src_rgb);
glGetIntegerv(GL_BLEND_DST, &dst_rgb);
src_a = src_rgb;
dst_a = dst_rgb;
}
if (gl_1_4 || GLEE_ARB_imaging || (GLEE_EXT_blend_minmax && GLEE_EXT_blend_subtract))
glGetIntegerv(GL_BLEND_EQUATION, &equation);
if (equation == GL_FUNC_REVERSE_SUBTRACT) // && src == GL_SRC_ALPHA && dst == GL_ONE
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 (src_rgb == src_a && dst_rgb == dst_a)
else if (state.srcRGB == state.srcA && state.dstRGB == state.dstA)
{
if (src_rgb == GL_SRC_ALPHA && dst_rgb == GL_ONE)
if (state.srcRGB == GL_SRC_ALPHA && state.dstRGB == GL_ONE)
return BLEND_ADDITIVE;
else if (src_rgb == GL_SRC_ALPHA && dst_rgb == GL_ONE_MINUS_SRC_ALPHA)
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 (src_rgb == GL_DST_COLOR && dst_rgb == GL_ZERO)
else if (state.srcRGB == GL_DST_COLOR && state.dstRGB == GL_ZERO)
return BLEND_MULTIPLICATIVE;
else if (src_rgb == GL_ONE && dst_rgb == GL_ONE_MINUS_SRC_ALPHA)
else if (state.srcRGB == GL_ONE && state.dstRGB == GL_ONE_MINUS_SRC_ALPHA)
return BLEND_PREMULTIPLIED;
else if (src_rgb == GL_ONE && dst_rgb == GL_ZERO)
else if (state.srcRGB == GL_ONE && state.dstRGB == GL_ZERO)
return BLEND_REPLACE;
}
else if (src_rgb == GL_SRC_ALPHA && src_a == GL_ONE &&
dst_rgb == GL_ONE_MINUS_SRC_ALPHA && dst_a == GL_ONE_MINUS_SRC_ALPHA)
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");
@@ -693,11 +740,15 @@ float Graphics::getPointSize() const
return (float)size;
}
int Graphics::getMaxPointSize() const
void Graphics::setWireframe(bool enable)
{
GLint max;
glGetIntegerv(GL_POINT_SIZE_MAX, &max);
return (int)max;
wireframe = enable;
glPolygonMode(GL_FRONT_AND_BACK, enable ? GL_LINE : GL_FILL);
}
bool Graphics::isWireframe() const
{
return 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)
@@ -1007,6 +1058,41 @@ std::string Graphics::getRendererInfo(Graphics::RendererInfo infotype) const
return std::string(infostr);
}
double Graphics::getSystemLimit(SystemLimit limittype) const
{
double limit = 0.0;
switch (limittype)
{
case Graphics::LIMIT_POINT_SIZE:
{
GLfloat limits[2];
glGetFloatv(GL_ALIASED_POINT_SIZE_RANGE, limits);
limit = limits[1];
}
break;
case Graphics::LIMIT_TEXTURE_SIZE:
limit = (double) gl.getMaxTextureSize();
break;
case Graphics::LIMIT_MULTI_CANVAS:
limit = (double) gl.getMaxRenderTargets();
break;
case Graphics::LIMIT_CANVAS_FSAA:
if (GLEE_VERSION_3_0 || GLEE_ARB_framebuffer_object
|| GLEE_EXT_framebuffer_multisample)
{
GLint intlimit = 0;
glGetIntegerv(GL_MAX_SAMPLES, &intlimit);
limit = (double) intlimit;
}
break;
default:
break;
}
return limit;
}
void Graphics::push()
{
if (userMatrices == matrixLimit)
+28 -12
View File
@@ -80,6 +80,8 @@ struct DisplayState
// Color mask.
bool colorMask[4];
bool wireframe;
// Default values.
DisplayState()
{
@@ -91,6 +93,7 @@ struct DisplayState
pointSize = 1.0f;
scissor = false;
colorMask[0] = colorMask[1] = colorMask[2] = colorMask[3] = true;
wireframe = false;
}
};
@@ -110,9 +113,11 @@ public:
void restoreState(const DisplayState &s);
virtual void setViewportSize(int width, int height);
virtual bool setMode(int width, int height);
virtual bool setMode(int width, int height, bool &sRGB);
virtual void unSetMode();
void setDebug(bool enable);
/**
* Resets the current color, background color,
* line style, and so forth. (This will be called
@@ -184,16 +189,11 @@ public:
*/
void discardStencil();
/**
* Gets the maximum supported width or height of Textures on this system.
**/
int getMaxTextureSize() const;
/**
* Creates an Image object with padding and/or optimization.
**/
Image *newImage(love::image::ImageData *data);
Image *newImage(love::image::CompressedData *cdata);
Image *newImage(love::image::ImageData *data, Texture::Format format = Texture::FORMAT_NORMAL);
Image *newImage(love::image::CompressedData *cdata, Texture::Format format = Texture::FORMAT_NORMAL);
Quad *newQuad(Quad::Viewport v, float sw, float sh);
@@ -206,11 +206,12 @@ public:
ParticleSystem *newParticleSystem(Texture *texture, int size);
Canvas *newCanvas(int width, int height, Canvas::TextureType texture_type = Canvas::TYPE_NORMAL);
Canvas *newCanvas(int width, int height, Texture::Format format = Texture::FORMAT_NORMAL, int fsaa = 0);
Shader *newShader(const Shader::ShaderSources &sources);
Mesh *newMesh(const std::vector<Vertex> &vertices, Mesh::DrawMode mode = Mesh::DRAW_MODE_FAN);
Mesh *newMesh(int vertexcount, Mesh::DrawMode mode = Mesh::DRAW_MODE_FAN);
/**
* Sets the foreground color.
@@ -324,10 +325,17 @@ public:
float getPointSize() const;
/**
* Gets the maximum point size supported.
* This may vary from computer to computer.
* Sets whether graphics will be drawn as wireframe lines instead of filled
* triangles (has no effect for drawn points.)
* This should only be used as a debugging tool. The wireframe lines do not
* behave the same as regular love.graphics lines.
**/
int getMaxPointSize() const;
void setWireframe(bool enable);
/**
* Gets whether wireframe drawing mode is enabled.
**/
bool isWireframe() const;
/**
* Draws text at the specified coordinates, with rotation and
@@ -430,6 +438,11 @@ public:
**/
std::string getRendererInfo(Graphics::RendererInfo infotype) const;
/**
* Gets the system-dependent numeric limit for the specified parameter.
**/
double getSystemLimit(SystemLimit limittype) const;
void push();
void pop();
void rotate(float r);
@@ -450,11 +463,14 @@ private:
GLint matrixLimit;
GLint userMatrices;
bool colorMask[4];
bool wireframe;
int width;
int height;
bool created;
bool activeStencil;
DisplayState savedState;
}; // Graphics
+46 -15
View File
@@ -36,7 +36,7 @@ float Image::maxMipmapSharpness = 0.0f;
Texture::FilterMode Image::defaultMipmapFilter = Texture::FILTER_NONE;
float Image::defaultMipmapSharpness = 0.0f;
Image::Image(love::image::ImageData *data)
Image::Image(love::image::ImageData *data, Texture::Format format)
: data(data)
, cdata(nullptr)
, paddedWidth(width)
@@ -45,6 +45,7 @@ Image::Image(love::image::ImageData *data)
, mipmapSharpness(defaultMipmapSharpness)
, mipmapsCreated(false)
, compressed(false)
, format(format)
, usingDefaultTexture(false)
{
width = data->getWidth();
@@ -54,7 +55,7 @@ Image::Image(love::image::ImageData *data)
preload();
}
Image::Image(love::image::CompressedData *cdata)
Image::Image(love::image::CompressedData *cdata, Texture::Format format)
: data(nullptr)
, cdata(cdata)
, paddedWidth(width)
@@ -63,6 +64,7 @@ Image::Image(love::image::CompressedData *cdata)
, mipmapSharpness(defaultMipmapSharpness)
, mipmapsCreated(false)
, compressed(true)
, format(format)
, usingDefaultTexture(false)
{
width = cdata->getWidth(0);
@@ -91,7 +93,7 @@ love::image::CompressedData *Image::getCompressedData() const
return cdata;
}
void Image::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const
void Image::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky)
{
Matrix t;
t.setTransformation(x, y, angle, sx, sy, ox, oy, kx, ky);
@@ -99,7 +101,7 @@ void Image::draw(float x, float y, float angle, float sx, float sy, float ox, fl
drawv(t, vertices);
}
void Image::drawq(Quad *quad, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const
void Image::drawq(Quad *quad, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky)
{
Matrix t;
t.setTransformation(x, y, angle, sx, sy, ox, oy, kx, ky);
@@ -107,7 +109,7 @@ void Image::drawq(Quad *quad, float x, float y, float angle, float sx, float sy,
drawv(t, quad->getVertices());
}
void Image::predraw() const
void Image::predraw()
{
bind();
@@ -121,7 +123,7 @@ void Image::predraw() const
}
}
void Image::postdraw() const
void Image::postdraw()
{
if (width != paddedWidth || height != paddedHeight)
{
@@ -328,6 +330,9 @@ void Image::unload()
bool Image::loadVolatile()
{
if (format == FORMAT_SRGB && !hasSRGBSupport())
throw love::Exception("sRGB images are not supported on this system.");
if (isCompressed() && cdata && !hasCompressedTextureSupport(cdata->getFormat()))
{
const char *str;
@@ -400,9 +405,10 @@ void Image::uploadTexturePadded()
}
else if (data)
{
GLenum iformat = (format == FORMAT_SRGB) ? GL_SRGB8_ALPHA8 : GL_RGBA8;
glTexImage2D(GL_TEXTURE_2D,
0,
GL_RGBA8,
iformat,
(GLsizei)paddedWidth,
(GLsizei)paddedHeight,
0,
@@ -437,9 +443,10 @@ void Image::uploadTexture()
}
else if (data)
{
GLenum iformat = (format == FORMAT_SRGB) ? GL_SRGB8_ALPHA8 : GL_RGBA8;
glTexImage2D(GL_TEXTURE_2D,
0,
GL_RGBA8,
iformat,
(GLsizei)width,
(GLsizei)height,
0,
@@ -497,6 +504,11 @@ bool Image::refresh()
return true;
}
Texture::Format Image::getFormat() const
{
return format;
}
void Image::uploadDefaultTexture()
{
usingDefaultTexture = true;
@@ -511,7 +523,7 @@ void Image::uploadDefaultTexture()
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, px);
}
void Image::drawv(const Matrix &t, const Vertex *v) const
void Image::drawv(const Matrix &t, const Vertex *v)
{
predraw();
@@ -561,16 +573,27 @@ bool Image::isCompressed() const
return compressed;
}
GLenum Image::getCompressedFormat(image::CompressedData::Format format) const
GLenum Image::getCompressedFormat(image::CompressedData::Format cformat) const
{
switch (format)
bool srgb = format == FORMAT_SRGB;
switch (cformat)
{
case image::CompressedData::FORMAT_DXT1:
return GL_COMPRESSED_RGB_S3TC_DXT1_EXT;
if (srgb)
return GL_COMPRESSED_SRGB_S3TC_DXT1_EXT;
else
return GL_COMPRESSED_RGB_S3TC_DXT1_EXT;
case image::CompressedData::FORMAT_DXT3:
return GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
if (srgb)
return GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT;
else
return GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
case image::CompressedData::FORMAT_DXT5:
return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
if (srgb)
return GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT;
else
return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
case image::CompressedData::FORMAT_BC4:
return GL_COMPRESSED_RED_RGTC1;
case image::CompressedData::FORMAT_BC4s:
@@ -580,7 +603,10 @@ GLenum Image::getCompressedFormat(image::CompressedData::Format format) const
case image::CompressedData::FORMAT_BC5s:
return GL_COMPRESSED_SIGNED_RG_RGTC2;
default:
return GL_RGBA8;
if (srgb)
return GL_SRGB8_ALPHA8;
else
return GL_RGBA8;
}
}
@@ -632,6 +658,11 @@ bool Image::hasCompressedTextureSupport(image::CompressedData::Format format)
return false;
}
bool Image::hasSRGBSupport()
{
return GLEE_VERSION_2_1 || GLEE_EXT_texture_sRGB;
}
} // opengl
} // graphics
} // love
+15 -8
View File
@@ -56,14 +56,14 @@ public:
*
* @param data The data from which to load the image.
**/
Image(love::image::ImageData *data);
Image(love::image::ImageData *data, Texture::Format format = Texture::FORMAT_NORMAL);
/**
* Creates a new Image with compressed image data.
*
* @param cdata The compressed data from which to load the image.
**/
Image(love::image::CompressedData *cdata);
Image(love::image::CompressedData *cdata, Texture::Format format = Texture::FORMAT_NORMAL);
/**
* Destructor. Deletes the hardware texture and other resources.
@@ -76,20 +76,20 @@ public:
/**
* @copydoc Drawable::draw()
**/
void draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const;
void draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky);
/**
* @copydoc Texture::drawq()
**/
void drawq(Quad *quad, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const;
void drawq(Quad *quad, float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky);
/**
* Call before using this Image's texture to draw. Binds the texture,
* globally scales texture coordinates if the Image has NPOT dimensions and
* NPOT isn't supported, etc.
**/
virtual void predraw() const;
virtual void postdraw() const;
virtual void predraw();
virtual void postdraw();
virtual GLuint getGLTexture() const;
@@ -120,6 +120,8 @@ public:
**/
bool refresh();
Texture::Format getFormat() const;
static void setDefaultMipmapSharpness(float sharpness);
static float getDefaultMipmapSharpness();
static void setDefaultMipmapFilter(FilterMode f);
@@ -133,11 +135,13 @@ public:
static bool hasCompressedTextureSupport();
static bool hasCompressedTextureSupport(image::CompressedData::Format format);
static bool hasSRGBSupport();
private:
void uploadDefaultTexture();
void drawv(const Matrix &t, const Vertex *v) const;
void drawv(const Matrix &t, const Vertex *v);
// The ImageData from which the texture is created. May be null if
// Compressed image data was used to create the texture.
@@ -162,6 +166,9 @@ private:
// Whether this Image is using a compressed texture.
bool compressed;
// The format to interpret the texture's data as.
Texture::Format format;
// True if the image wasn't able to be properly created and it had to fall
// back to a default texture.
bool usingDefaultTexture;
@@ -180,7 +187,7 @@ private:
static FilterMode defaultMipmapFilter;
static float defaultMipmapSharpness;
GLenum getCompressedFormat(image::CompressedData::Format format) const;
GLenum getCompressedFormat(image::CompressedData::Format cformat) const;
}; // Image
+197 -45
View File
@@ -23,6 +23,9 @@
#include "common/Matrix.h"
#include "common/Exception.h"
// C++
#include <algorithm>
namespace love
{
namespace graphics
@@ -35,14 +38,46 @@ Mesh::Mesh(const std::vector<Vertex> &verts, Mesh::DrawMode mode)
, vertex_count(0)
, ibo(nullptr)
, element_count(0)
, element_data_type(getGLDataTypeFromMax(verts.size()))
, instance_count(1)
, draw_mode(mode)
, range_min(-1)
, range_max(-1)
, texture(nullptr)
, colors_enabled(false)
, wireframe(false)
{
setVertices(verts);
}
Mesh::Mesh(int vertexcount, Mesh::DrawMode mode)
: vbo(nullptr)
, vertex_count(0)
, ibo(nullptr)
, element_count(0)
, element_data_type(getGLDataTypeFromMax(vertexcount))
, draw_mode(mode)
, range_min(-1)
, range_max(-1)
, texture(nullptr)
, colors_enabled(false)
{
if (vertexcount < 1)
throw love::Exception("Invalid number of vertices.");
std::vector<Vertex> verts(vertexcount);
// Default-initialized vertices should have a white opaque color.
for (size_t i = 0; i < verts.size(); i++)
{
verts[i].r = 255;
verts[i].g = 255;
verts[i].b = 255;
verts[i].a = 255;
}
setVertices(verts);
}
Mesh::~Mesh()
{
delete vbo;
@@ -51,8 +86,8 @@ Mesh::~Mesh()
void Mesh::setVertices(const std::vector<Vertex> &verts)
{
if (verts.size() < 3)
throw love::Exception("At least 3 vertices are required.");
if (verts.size() == 0)
throw love::Exception("At least one vertex is required.");
size_t size = sizeof(Vertex) * verts.size();
@@ -118,15 +153,42 @@ size_t Mesh::getVertexCount() const
return vertex_count;
}
/**
* Copies index data from a vector to a mapped index buffer.
**/
template <typename T>
static void copyToIndexBuffer(const std::vector<uint32> &indices, VertexBuffer::Mapper &buffermap, size_t maxval)
{
T *elems = (T *) buffermap.get();
for (size_t i = 0; i < indices.size(); i++)
{
if (indices[i] >= maxval)
throw love::Exception("Invalid vertex map value: %d", indices[i] + 1);
elems[i] = (T) indices[i];
}
}
void Mesh::setVertexMap(const std::vector<uint32> &map)
{
for (size_t i = 0; i < map.size(); i++)
{
if (map[i] >= vertex_count)
throw love::Exception("Invalid vertex map value: %d", map[i] + 1);
}
GLenum datatype = getGLDataTypeFromMax(vertex_count);
size_t size = sizeof(uint32) * map.size();
// Calculate the size in bytes of the index buffer data.
size_t size = map.size();
switch (datatype)
{
case GL_UNSIGNED_BYTE:
size *= sizeof(uint8);
break;
case GL_UNSIGNED_SHORT:
size *= sizeof(uint16);
break;
case GL_UNSIGNED_INT:
default:
size *= sizeof(uint32);
break;
}
if (ibo && size > ibo->getSize())
{
@@ -142,27 +204,68 @@ void Mesh::setVertexMap(const std::vector<uint32> &map)
element_count = map.size();
if (ibo && element_count > 0)
{
VertexBuffer::Bind ibo_bind(*ibo);
VertexBuffer::Mapper ibo_map(*ibo);
if (!ibo || element_count == 0)
return;
// Fill the buffer.
memcpy(ibo_map.get(), &map[0], size);
VertexBuffer::Bind ibo_bind(*ibo);
VertexBuffer::Mapper ibo_map(*ibo);
// Fill the buffer with the index values from the vector.
switch (datatype)
{
case GL_UNSIGNED_BYTE:
copyToIndexBuffer<uint8>(map, ibo_map, vertex_count);
break;
case GL_UNSIGNED_SHORT:
copyToIndexBuffer<uint16>(map, ibo_map, vertex_count);
break;
case GL_UNSIGNED_INT:
default:
copyToIndexBuffer<uint32>(map, ibo_map, vertex_count);
break;
}
element_data_type = datatype;
}
const uint32 *Mesh::getVertexMap() const
/**
* Copies index data from a mapped buffer to a vector.
**/
template <typename T>
static void copyFromIndexBuffer(void *buffer, std::vector<uint32> &indices, size_t maxval)
{
if (ibo && element_count > 0)
T *elems = (T *) buffer;
for (size_t i = 0; i < maxval; i++)
indices.push_back((uint32) elems[i]);
}
void Mesh::getVertexMap(std::vector<uint32> &map) const
{
if (!ibo || element_count == 0)
return;
map.clear();
map.reserve(element_count);
VertexBuffer::Bind ibo_bind(*ibo);
// We unmap the buffer in Mesh::draw and Mesh::setVertexMap.
void *buffer = ibo->map();
// Fill the vector from the buffer.
switch (element_data_type)
{
VertexBuffer::Bind ibo_bind(*ibo);
// We unmap the buffer in Mesh::draw and Mesh::setVertexMap.
return (uint32 *) ibo->map();
case GL_UNSIGNED_BYTE:
copyFromIndexBuffer<uint8>(buffer, map, vertex_count);
break;
case GL_UNSIGNED_SHORT:
copyFromIndexBuffer<uint16>(buffer, map, vertex_count);
break;
case GL_UNSIGNED_INT:
default:
copyFromIndexBuffer<uint32>(buffer, map, vertex_count);
break;
}
return 0;
}
size_t Mesh::getVertexMapCount() const
@@ -170,6 +273,16 @@ size_t Mesh::getVertexMapCount() const
return element_count;
}
void Mesh::setInstanceCount(int count)
{
instance_count = std::max(count, 1);
}
int Mesh::getInstanceCount() const
{
return instance_count;
}
void Mesh::setTexture(Texture *tex)
{
tex->retain();
@@ -203,6 +316,26 @@ Mesh::DrawMode Mesh::getDrawMode() const
return draw_mode;
}
void Mesh::setDrawRange(int min, int max)
{
if (min < 0 || max < 0 || min > max)
throw love::Exception("Invalid draw range.");
range_min = min;
range_max = max;
}
void Mesh::setDrawRange()
{
range_min = range_max = -1;
}
void Mesh::getDrawRange(int &min, int &max) const
{
min = range_min;
max = range_max;
}
void Mesh::setVertexColors(bool enable)
{
colors_enabled = enable;
@@ -213,17 +346,7 @@ bool Mesh::hasVertexColors() const
return colors_enabled;
}
void Mesh::setWireframe(bool enable)
{
wireframe = enable;
}
bool Mesh::isWireframe() const
{
return wireframe;
}
void Mesh::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const
void Mesh::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky)
{
const size_t pos_offset = offsetof(Vertex, x);
const size_t tex_offset = offsetof(Vertex, s);
@@ -261,31 +384,50 @@ void Mesh::draw(float x, float y, float angle, float sx, float sy, float ox, flo
glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(Vertex), vbo->getPointer(color_offset));
}
if (wireframe)
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
GLenum mode = getGLDrawMode(draw_mode);
gl.prepareDraw();
if (ibo && element_count > 0)
{
// Use the custom vertex map (index buffer) to draw the vertices.
VertexBuffer::Bind ibo_bind(*ibo);
// Make sure the index buffer isn't mapped (sends data to GPU if needed.)
ibo->unmap();
// Use the custom vertex map to draw the vertices.
glDrawElements(mode, element_count, GL_UNSIGNED_INT, ibo->getPointer(0));
int max = element_count - 1;
if (range_max >= 0)
max = std::min(std::max(range_max, 0), (int) element_count - 1);
int min = 0;
if (range_min >= 0)
min = std::min(std::max(range_min, 0), max);
const void *indices = ibo->getPointer(min * sizeof(uint32));
GLenum type = element_data_type;
if (instance_count > 1)
gl.drawElementsInstanced(mode, max - min + 1, type, indices, instance_count);
else
glDrawElements(mode, max - min + 1, type, indices);
}
else
{
// Normal non-indexed drawing (no custom vertex map.)
glDrawArrays(mode, 0, vertex_count);
}
int max = vertex_count - 1;
if (range_max >= 0)
max = std::min(std::max(range_max, 0), (int) vertex_count - 1);
if (wireframe)
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
int min = 0;
if (range_min >= 0)
min = std::min(std::max(range_min, 0), max);
// Normal non-indexed drawing (no custom vertex map.)
if (instance_count > 1)
gl.drawArraysInstanced(mode, min, max - min + 1, instance_count);
else
glDrawArrays(mode, min, max - min + 1);
}
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
@@ -303,7 +445,7 @@ void Mesh::draw(float x, float y, float angle, float sx, float sy, float ox, flo
texture->postdraw();
}
GLenum Mesh::getGLDrawMode(Mesh::DrawMode mode) const
GLenum Mesh::getGLDrawMode(DrawMode mode) const
{
switch (mode)
{
@@ -322,6 +464,16 @@ GLenum Mesh::getGLDrawMode(Mesh::DrawMode mode) const
return GL_TRIANGLES;
}
GLenum Mesh::getGLDataTypeFromMax(size_t maxvalue) const
{
if (maxvalue > LOVE_UINT16_MAX)
return GL_UNSIGNED_INT;
else if (maxvalue > LOVE_UINT8_MAX)
return GL_UNSIGNED_SHORT;
else
return GL_UNSIGNED_BYTE;
}
bool Mesh::getConstant(const char *in, Mesh::DrawMode &out)
{
return drawModes.find(in, out);
+35 -16
View File
@@ -22,6 +22,7 @@
#define LOVE_GRAPHICS_OPENGL_MESH_H
// LOVE
#include "common/config.h"
#include "common/int.h"
#include "common/math.h"
#include "common/StringMap.h"
@@ -64,6 +65,16 @@ public:
* @param mode The draw mode to use when drawing the Mesh.
**/
Mesh(const std::vector<Vertex> &verts, DrawMode mode = DRAW_MODE_FAN);
/**
* Constructor.
* Creates a Mesh with a certain number of default-initialized (hidden)
* vertices.
* @param vertexcount The number of vertices to use in the Mesh.
* @param mode The draw mode to use when drawing the Mesh.
**/
Mesh(int vertexcount, DrawMode mode = DRAW_MODE_FAN);
virtual ~Mesh();
/**
@@ -98,17 +109,25 @@ public:
void setVertexMap(const std::vector<uint32> &map);
/**
* Gets a pointer to the vertex map array. The pointer is only valid until
* the next function call in the graphics module.
* May return null if the vertex map is empty.
* Fills the uint32 vector passed into the method with the previously set
* vertex map (index buffer) values.
**/
const uint32 *getVertexMap() const;
void getVertexMap(std::vector<uint32> &map) const;
/**
* Gets the total number of elements in the vertex map array.
**/
size_t getVertexMapCount() const;
/**
* Sets the number of instances of this Mesh to draw (uses hardware
* instancing when possible.)
* A custom vertex shader is necessary in order to introduce differences
* in each instance.
**/
void setInstanceCount(int count);
int getInstanceCount() const;
/**
* Sets the texture used when drawing the Mesh.
**/
@@ -131,6 +150,10 @@ public:
void setDrawMode(DrawMode mode);
DrawMode getDrawMode() const;
void setDrawRange(int min, int max);
void setDrawRange();
void getDrawRange(int &min, int &max) const;
/**
* Sets whether per-vertex colors are enabled. If this is disabled, the
* global color (love.graphics.setColor) will be used for the entire Mesh.
@@ -138,17 +161,8 @@ public:
void setVertexColors(bool enable);
bool hasVertexColors() const;
/**
* Sets whether the Mesh will be drawn as wireframe lines instead of filled
* triangles (has no effect for DRAW_MODE_POINTS.)
* This should only be used as a debugging tool. The wireframe lines do not
* behave the same as regular love.graphics lines.
**/
void setWireframe(bool enable);
bool isWireframe() const;
// Implements Drawable.
void draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const;
void draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky);
static bool getConstant(const char *in, DrawMode &out);
static bool getConstant(DrawMode in, const char *&out);
@@ -156,6 +170,7 @@ public:
private:
GLenum getGLDrawMode(DrawMode mode) const;
GLenum getGLDataTypeFromMax(size_t maxvalue) const;
// Vertex buffer.
VertexBuffer *vbo;
@@ -164,16 +179,20 @@ private:
// Element (vertex index) buffer, for the vertex map.
VertexBuffer *ibo;
size_t element_count;
GLenum element_data_type;
int instance_count;
DrawMode draw_mode;
int range_min;
int range_max;
Texture *texture;
// Whether the per-vertex colors are used when drawing.
bool colors_enabled;
bool wireframe;
static StringMap<DrawMode, DRAW_MODE_MAX_ENUM>::Entry drawModeEntries[];
static StringMap<DrawMode, DRAW_MODE_MAX_ENUM> drawModes;
+193 -4
View File
@@ -43,6 +43,7 @@ OpenGL::OpenGL()
: contextInitialized(false)
, maxAnisotropy(1.0f)
, maxTextureSize(0)
, maxRenderTargets(0)
, vendor(VENDOR_UNKNOWN)
, state()
{
@@ -110,9 +111,14 @@ void OpenGL::initContext()
glGetIntegerv(GL_TEXTURE_BINDING_2D, (GLint *) &state.textureUnits[0]);
}
BlendState blend = {GL_ONE, GL_ONE, GL_ZERO, GL_ZERO, GL_FUNC_ADD};
setBlendState(blend);
initMaxValues();
createDefaultTexture();
state.lastPseudoInstanceID = -1;
contextInitialized = true;
}
@@ -188,6 +194,19 @@ void OpenGL::initMaxValues()
maxAnisotropy = 1.0f;
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
if (Canvas::isSupported() && (GLEE_VERSION_2_0 || GLEE_ARB_draw_buffers))
{
int maxattachments = 0;
glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, &maxattachments);
int maxdrawbuffers = 0;
glGetIntegerv(GL_MAX_DRAW_BUFFERS, &maxdrawbuffers);
maxRenderTargets = std::min(maxattachments, maxdrawbuffers);
}
else
maxRenderTargets = 0;
}
void OpenGL::createDefaultTexture()
@@ -214,10 +233,81 @@ void OpenGL::createDefaultTexture()
void OpenGL::prepareDraw()
{
// Make sure the active shader has the correct values for the built-in
// screen params uniform.
if (Shader::current)
Shader::current->checkSetScreenParams();
Shader *shader = Shader::current;
if (shader != nullptr)
{
// Make sure the active shader has the correct values for its
// love-provided uniforms.
shader->checkSetScreenParams();
// Make sure the Instance ID variable is up-to-date when
// pseudo-instancing is used.
if (state.lastPseudoInstanceID != 0 && shader->hasVertexAttrib(ATTRIB_PSEUDO_INSTANCE_ID))
{
glVertexAttrib1f((GLuint) ATTRIB_PSEUDO_INSTANCE_ID, 0.0f);
state.lastPseudoInstanceID = 0;
}
// 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<std::string, Object *> &r = shader->getBoundRetainables();
for (auto it = r.begin(); it != r.end(); ++it)
{
// Even bigger hack! D:
Canvas *canvas = dynamic_cast<Canvas *>(it->second);
if (canvas != nullptr)
canvas->resolveMSAA();
}
}
}
void OpenGL::drawArraysInstanced(GLenum mode, GLint first, GLsizei count, GLsizei primcount)
{
Shader *shader = Shader::current;
if (GLEE_ARB_draw_instanced)
glDrawArraysInstancedARB(mode, first, count, primcount);
else
{
bool shaderHasID = shader && shader->hasVertexAttrib(ATTRIB_PSEUDO_INSTANCE_ID);
// Pseudo-instancing fallback.
for (int i = 0; i < primcount; i++)
{
if (shaderHasID)
glVertexAttrib1f((GLuint) ATTRIB_PSEUDO_INSTANCE_ID, (GLfloat) i);
glDrawArrays(mode, first, count);
}
if (shaderHasID)
state.lastPseudoInstanceID = primcount - 1;
}
}
void OpenGL::drawElementsInstanced(GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei primcount)
{
Shader *shader = Shader::current;
if (GLEE_ARB_draw_instanced)
glDrawElementsInstancedARB(mode, count, type, indices, primcount);
else
{
bool shaderHasID = shader && shader->hasVertexAttrib(ATTRIB_PSEUDO_INSTANCE_ID);
// Pseudo-instancing fallback.
for (int i = 0; i < primcount; i++)
{
if (shaderHasID)
glVertexAttrib1f((GLuint) ATTRIB_PSEUDO_INSTANCE_ID, (GLfloat) i);
glDrawElements(mode, count, type, indices);
}
if (shaderHasID)
state.lastPseudoInstanceID = primcount - 1;
}
}
void OpenGL::setColor(const Color &c)
@@ -277,6 +367,39 @@ OpenGL::Viewport OpenGL::getScissor() const
return state.scissor;
}
void OpenGL::setBlendState(const BlendState &blend)
{
if (GLEE_VERSION_1_4 || GLEE_ARB_imaging)
glBlendEquation(blend.func);
else if (GLEE_EXT_blend_minmax && GLEE_EXT_blend_subtract)
glBlendEquationEXT(blend.func);
else
{
if (blend.func == GL_FUNC_REVERSE_SUBTRACT)
throw love::Exception("This graphics card does not support the subtractive blend mode!");
// GL_FUNC_ADD is the default even without access to glBlendEquation, so that'll still work.
}
if (blend.srcRGB == blend.srcA && blend.dstRGB == blend.dstA)
glBlendFunc(blend.srcRGB, blend.dstRGB);
else
{
if (GLEE_VERSION_1_4)
glBlendFuncSeparate(blend.srcRGB, blend.dstRGB, blend.srcA, blend.dstA);
else if (GLEE_EXT_blend_func_separate)
glBlendFuncSeparateEXT(blend.srcRGB, blend.dstRGB, blend.srcA, blend.dstA);
else
throw love::Exception("This graphics card does not support separated rgb and alpha blend functions!");
}
state.blend = blend;
}
OpenGL::BlendState OpenGL::getBlendState() const
{
return state.blend;
}
void OpenGL::setTextureUnit(int textureunit)
{
if (textureunit < 0 || (size_t) textureunit >= state.textureUnits.size())
@@ -504,11 +627,77 @@ int OpenGL::getMaxTextureSize() const
return maxTextureSize;
}
int OpenGL::getMaxRenderTargets() const
{
return maxRenderTargets;
}
OpenGL::Vendor OpenGL::getVendor() const
{
return vendor;
}
const char *OpenGL::debugSeverityString(GLenum severity)
{
switch (severity)
{
case GL_DEBUG_SEVERITY_HIGH:
return "high";
case GL_DEBUG_SEVERITY_MEDIUM:
return "medium";
case GL_DEBUG_SEVERITY_LOW:
return "low";
default:
break;
}
return "unknown";
}
const char *OpenGL::debugSourceString(GLenum source)
{
switch (source)
{
case GL_DEBUG_SOURCE_API:
return "API";
case GL_DEBUG_SOURCE_WINDOW_SYSTEM:
return "window";
case GL_DEBUG_SOURCE_SHADER_COMPILER:
return "shader";
case GL_DEBUG_SOURCE_THIRD_PARTY:
return "external";
case GL_DEBUG_SOURCE_APPLICATION:
return "LOVE";
case GL_DEBUG_SOURCE_OTHER:
return "other";
default:
break;
}
return "unknown";
}
const char *OpenGL::debugTypeString(GLenum type)
{
switch (type)
{
case GL_DEBUG_TYPE_ERROR:
return "error";
case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR:
return "deprecated behavior";
case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR:
return "undefined behavior";
case GL_DEBUG_TYPE_PERFORMANCE:
return "performance";
case GL_DEBUG_TYPE_PORTABILITY:
return "portability";
case GL_DEBUG_TYPE_OTHER:
return "other";
default:
break;
}
return "unknown";
}
// OpenGL class instance singleton.
OpenGL gl;
+60
View File
@@ -63,6 +63,17 @@ public:
VENDOR_UNKNOWN
};
// Vertex attributes used in shaders by LOVE. The values map to OpenGL
// generic vertex attribute indices, when applicable.
// LOVE uses the old hard-coded attribute APIs for positions, colors, etc.
// (for now.)
enum VertexAttrib
{
// Instance ID when pseudo-instancing is used.
ATTRIB_PSEUDO_INSTANCE_ID = 1,
ATTRIB_MAX_ENUM
};
// A rectangle representing an OpenGL viewport or a scissor box.
struct Viewport
{
@@ -76,6 +87,18 @@ public:
Viewport(int _x, int _y, int _w, int _h)
: x(_x), y(_y), w(_w), h(_h)
{}
bool operator == (const Viewport &rhs) const
{
return x == rhs.x && y == rhs.y && w == rhs.w && h == rhs.h;
}
};
struct BlendState
{
GLenum srcRGB, srcA;
GLenum dstRGB, dstA;
GLenum func;
};
OpenGL();
@@ -99,6 +122,16 @@ public:
**/
void prepareDraw();
/**
* glDrawArraysInstanced with a pseudo-instancing fallback.
**/
void drawArraysInstanced(GLenum mode, GLint first, GLsizei count, GLsizei primcount);
/**
* glDrawElementsInstanced with a pseudo-instancing fallback.
**/
void drawElementsInstanced(GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei primcount);
/**
* Sets the current constant color.
**/
@@ -141,6 +174,17 @@ public:
**/
Viewport getScissor() const;
/**
* Sets blending functionality.
* Note: This does not globally enable or disable blending.
**/
void setBlendState(const BlendState &blend);
/**
* Gets the currently set blending functionality.
**/
BlendState getBlendState() const;
/**
* Helper for setting the active texture unit.
*
@@ -194,11 +238,21 @@ public:
**/
int getMaxTextureSize() const;
/**
* Returns the maximum supported number of simultaneous render targets.
**/
int getMaxRenderTargets() const;
/**
* Get the GPU vendor of this OpenGL context.
**/
Vendor getVendor() const;
// Get human-readable strings for debug info.
static const char *debugSeverityString(GLenum severity);
static const char *debugSourceString(GLenum source);
static const char *debugTypeString(GLenum type);
private:
void initVendor();
@@ -210,6 +264,7 @@ private:
float maxAnisotropy;
int maxTextureSize;
int maxRenderTargets;
Vendor vendor;
@@ -230,6 +285,11 @@ private:
Viewport viewport;
Viewport scissor;
BlendState blend;
// The last ID value used for pseudo-instancing.
int lastPseudoInstanceID;
} state;
}; // OpenGL
+78 -44
View File
@@ -94,6 +94,7 @@ ParticleSystem::ParticleSystem(Texture *texture, uint32 size)
, spinVariation(0)
, offsetX(float(texture->getWidth())*0.5f)
, offsetY(float(texture->getHeight())*0.5f)
, relativeRotation(false)
{
if (size == 0 || size > MAX_PARTICLES)
throw love::Exception("Invalid ParticleSystem size.");
@@ -118,6 +119,7 @@ ParticleSystem::ParticleSystem(const ParticleSystem &p)
, emissionRate(p.emissionRate)
, emitCounter(0.0f)
, position(p.position)
, prevPosition(p.prevPosition)
, areaSpreadDistribution(p.areaSpreadDistribution)
, areaSpread(p.areaSpread)
, lifetime(p.lifetime)
@@ -144,6 +146,7 @@ ParticleSystem::ParticleSystem(const ParticleSystem &p)
, offsetX(p.offsetX)
, offsetY(p.offsetY)
, colors(p.colors)
, relativeRotation(p.relativeRotation)
{
setBufferSize(maxParticles);
@@ -168,7 +171,7 @@ void ParticleSystem::createBuffers(size_t size)
{
try
{
pFree = pMem = new particle[size];
pFree = pMem = new Particle[size];
particleVerts = new love::Vertex[size * 4];
maxParticles = (uint32) size;
}
@@ -205,14 +208,14 @@ uint32 ParticleSystem::getBufferSize() const
return maxParticles;
}
void ParticleSystem::addParticle()
void ParticleSystem::addParticle(float t)
{
if (isFull())
return;
// Gets a free particle and updates the allocation pointer.
particle *p = pFree++;
initParticle(p);
Particle *p = pFree++;
initParticle(p, t);
switch (insertMode)
{
@@ -231,10 +234,13 @@ void ParticleSystem::addParticle()
activeParticles++;
}
void ParticleSystem::initParticle(particle *p)
void ParticleSystem::initParticle(Particle *p, float t)
{
float min,max;
// Linearly interpolate between the previous and current emitter position.
love::Vector pos = prevPosition + (position - prevPosition) * t;
min = particleLifeMin;
max = particleLifeMax;
if (min == max)
@@ -243,8 +249,8 @@ void ParticleSystem::initParticle(particle *p)
p->life = (float) rng.random(min, max);
p->lifetime = p->life;
p->position[0] = position.getX();
p->position[1] = position.getY();
p->position[0] = pos.x;
p->position[1] = pos.y;
switch (areaSpreadDistribution)
{
@@ -265,7 +271,7 @@ void ParticleSystem::initParticle(particle *p)
max = direction + spread/2.0f;
p->direction = (float) rng.random(min, max);
p->origin = position;
p->origin = pos;
min = speedMin;
max = speedMax;
@@ -294,10 +300,14 @@ void ParticleSystem::initParticle(particle *p)
p->spinEnd = calculate_variation(spinEnd, spinStart, spinVariation);
p->rotation = (float) rng.random(min, max);
p->angle = p->rotation;
if (relativeRotation)
p->angle += atan2f(p->speed.y, p->speed.x);
p->color = colors[0];
}
void ParticleSystem::insertTop(particle *p)
void ParticleSystem::insertTop(Particle *p)
{
if (pHead == nullptr)
{
@@ -313,7 +323,7 @@ void ParticleSystem::insertTop(particle *p)
pTail = p;
}
void ParticleSystem::insertBottom(particle *p)
void ParticleSystem::insertBottom(Particle *p)
{
if (pTail == nullptr)
{
@@ -329,7 +339,7 @@ void ParticleSystem::insertBottom(particle *p)
pHead = p;
}
void ParticleSystem::insertRandom(particle *p)
void ParticleSystem::insertRandom(Particle *p)
{
// Nonuniform, but 64-bit is so large nobody will notice. Hopefully.
uint64 pos = rng.rand() % ((int64) activeParticles + 1);
@@ -337,7 +347,7 @@ void ParticleSystem::insertRandom(particle *p)
// Special case where the particle gets inserted before the head.
if (pos == activeParticles)
{
particle *pA = pHead;
Particle *pA = pHead;
if (pA)
pA->prev = p;
p->prev = nullptr;
@@ -347,8 +357,8 @@ void ParticleSystem::insertRandom(particle *p)
}
// Inserts the particle after the randomly selected particle.
particle *pA = pMem + pos;
particle *pB = pA->next;
Particle *pA = pMem + pos;
Particle *pB = pA->next;
pA->next = p;
if (pB)
pB->prev = p;
@@ -358,12 +368,12 @@ void ParticleSystem::insertRandom(particle *p)
p->next = pB;
}
ParticleSystem::particle *ParticleSystem::removeParticle(particle *p)
ParticleSystem::Particle *ParticleSystem::removeParticle(Particle *p)
{
// The linked list is updated in this function and old pointers may be
// invalidated. The returned pointer will inform the caller of the new
// pointer to the next particle.
particle *pNext = nullptr;
Particle *pNext = nullptr;
// Removes the particle from the linked list.
if (p->prev)
@@ -428,14 +438,14 @@ ParticleSystem::InsertMode ParticleSystem::getInsertMode() const
return insertMode;
}
void ParticleSystem::setEmissionRate(int rate)
void ParticleSystem::setEmissionRate(float rate)
{
if (rate < 0)
if (rate < 0.0f)
throw love::Exception("Invalid emission rate");
emissionRate = rate;
}
int ParticleSystem::getEmissionRate() const
float ParticleSystem::getEmissionRate() const
{
return emissionRate;
}
@@ -470,6 +480,7 @@ void ParticleSystem::getParticleLifetime(float *min, float *max) const
void ParticleSystem::setPosition(float x, float y)
{
position = love::Vector(x, y);
prevPosition = position;
}
const love::Vector &ParticleSystem::getPosition() const
@@ -477,6 +488,11 @@ const love::Vector &ParticleSystem::getPosition() const
return position;
}
void ParticleSystem::moveTo(float x, float y)
{
position = love::Vector(x, y);
}
void ParticleSystem::setAreaSpread(AreaSpreadDistribution distribution, float x, float y)
{
areaSpread = love::Vector(x, y);
@@ -705,6 +721,16 @@ std::vector<Color> ParticleSystem::getColor() const
return ncolors;
}
void ParticleSystem::setRelativeRotation(bool enable)
{
relativeRotation = enable;
}
bool ParticleSystem::hasRelativeRotation() const
{
return relativeRotation;
}
uint32 ParticleSystem::getCount() const
{
return activeParticles;
@@ -748,7 +774,7 @@ void ParticleSystem::emit(uint32 num)
num = std::min(num, maxParticles - activeParticles);
while(num--)
addParticle();
addParticle(1.0f);
}
bool ParticleSystem::isActive() const
@@ -776,7 +802,7 @@ bool ParticleSystem::isFull() const
return activeParticles == maxParticles;
}
void ParticleSystem::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const
void ParticleSystem::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky)
{
uint32 pCount = getCount();
if (pCount == 0 || texture == nullptr || pMem == nullptr || particleVerts == nullptr)
@@ -792,13 +818,13 @@ void ParticleSystem::draw(float x, float y, float angle, float sx, float sy, flo
const Vertex *textureVerts = texture->getVertices();
Vertex *pVerts = particleVerts;
particle *p = pHead;
Particle *p = pHead;
// set the vertex data for each particle (transformation, texcoords, color)
while (p)
{
// particle vertices are image vertices transformed by particle information
t.setTransformation(p->position[0], p->position[1], p->rotation, p->size, p->size, offsetX, offsetY, 0.0f, 0.0f);
t.setTransformation(p->position[0], p->position[1], p->angle, p->size, p->size, offsetX, offsetY, 0.0f, 0.0f);
t.transform(pVerts, textureVerts, 4);
// set the texture coordinate and color data for particle vertices
@@ -847,27 +873,8 @@ void ParticleSystem::update(float dt)
if (pMem == nullptr || dt == 0.0f)
return;
// Make some more particles.
if (active)
{
float rate = 1.0f / emissionRate; // the amount of time between each particle emit
emitCounter += dt;
while (emitCounter > rate)
{
addParticle();
emitCounter -= rate;
}
/*int particles = (int)(emissionRate * dt);
for (int i = 0; i != particles; i++)
add();*/
life -= dt;
if (lifetime != -1 && life < 0)
stop();
}
// Traverse all particles and update.
particle *p = pHead;
Particle *p = pHead;
while (p)
{
@@ -912,7 +919,12 @@ void ParticleSystem::update(float dt)
const float t = 1.0f - p->life / p->lifetime;
// Rotate.
p->rotation += (p->spinStart * (1.0f - t) + p->spinEnd * t)*dt;
p->rotation += (p->spinStart * (1.0f - t) + p->spinEnd * t) * dt;
p->angle = p->rotation;
if (relativeRotation)
p->angle += atan2f(p->speed.y, p->speed.x);
// Change size according to given intervals:
// i = 0 1 2 3 n-1
@@ -940,6 +952,28 @@ void ParticleSystem::update(float dt)
p = p->next;
}
}
// Make some more particles.
if (active)
{
float rate = 1.0f / emissionRate; // the amount of time between each particle emit
emitCounter += dt;
float total = emitCounter - rate;
while (emitCounter > rate)
{
addParticle(1.0f - (emitCounter - rate) / total);
emitCounter -= rate;
}
/*int particles = (int)(emissionRate * dt);
for (int i = 0; i != particles; i++)
add();*/
life -= dt;
if (lifetime != -1 && life < 0)
stop();
}
prevPosition = position;
}
bool ParticleSystem::getConstant(const char *in, AreaSpreadDistribution &out)
+39 -19
View File
@@ -65,7 +65,7 @@ public:
INSERT_MODE_TOP,
INSERT_MODE_BOTTOM,
INSERT_MODE_RANDOM,
INSERT_MODE_MAX_ENUM,
INSERT_MODE_MAX_ENUM
};
/**
@@ -131,12 +131,12 @@ public:
* Sets the emission rate.
* @param rate The amount of particles per second.
**/
void setEmissionRate(int rate);
void setEmissionRate(float rate);
/**
* Returns the number of particles created per second.
**/
int getEmissionRate() const;
float getEmissionRate() const;
/**
* Sets the lifetime of the particle emitter (-1 means eternal)
@@ -176,6 +176,15 @@ public:
**/
const love::Vector &getPosition() const;
/**
* Moves the position of the center of the emitter.
* When update is called, newly spawned particles will appear in a line
* between the old emitter position and where the emitter was moved to,
* resulting in a smoother-feeling particle system if moveTo is called
* repeatedly.
**/
void moveTo(float x, float y);
/**
* Sets the emission area spread parameters and distribution type. The interpretation of
* the parameters depends on the distribution type:
@@ -412,6 +421,12 @@ public:
**/
std::vector<Color> getColor() const;
/**
* sets whether particle angles & rotations are relative to their velocities.
**/
void setRelativeRotation(bool enable);
bool hasRelativeRotation() const;
/**
* Returns the amount of particles that are currently active in the system.
**/
@@ -470,7 +485,7 @@ public:
* @param x The x-coordinate.
* @param y The y-coordinate.
**/
virtual void draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const;
virtual void draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky);
/**
* Updates the particle system.
@@ -485,11 +500,12 @@ public:
static bool getConstant(InsertMode in, const char *&out);
protected:
// Represents a single particle.
struct particle
struct Particle
{
particle *prev;
particle *next;
Particle *prev;
Particle *next;
float lifetime;
float life;
@@ -509,7 +525,8 @@ protected:
float sizeOffset;
float sizeIntervalSize;
float rotation;
float rotation; // Amount of rotation applied to the final angle.
float angle;
float spinStart;
float spinEnd;
@@ -517,16 +534,16 @@ protected:
};
// Pointer to the beginning of the allocated memory.
particle *pMem;
Particle *pMem;
// Pointer to a free particle.
particle *pFree;
Particle *pFree;
// Pointer to the start of the linked list.
particle *pHead;
Particle *pHead;
// Pointer to the end of the linked list.
particle *pTail;
Particle *pTail;
// array of transformed vertex data for all particles, for drawing
Vertex *particleVerts;
@@ -547,13 +564,14 @@ protected:
uint32 activeParticles;
// The emission rate (particles/sec).
int emissionRate;
float emissionRate;
// Used to determine when a particle should be emitted.
float emitCounter;
// The relative position of the particle emitter.
love::Vector position;
love::Vector prevPosition;
// Emission area spread.
AreaSpreadDistribution areaSpreadDistribution;
@@ -607,17 +625,19 @@ protected:
// Color.
std::vector<Colorf> colors;
bool relativeRotation;
void createBuffers(size_t size);
void deleteBuffers();
void addParticle();
particle *removeParticle(particle *p);
void addParticle(float t);
Particle *removeParticle(Particle *p);
// Called by addParticle.
void initParticle(particle *p);
void insertTop(particle *p);
void insertBottom(particle *p);
void insertRandom(particle *p);
void initParticle(Particle *p, float t);
void insertTop(Particle *p);
void insertBottom(Particle *p);
void insertRandom(Particle *p);
static StringMap<AreaSpreadDistribution, DISTRIBUTION_MAX_ENUM>::Entry distributionsEntries[];
static StringMap<AreaSpreadDistribution, DISTRIBUTION_MAX_ENUM> distributions;
+52 -9
View File
@@ -70,7 +70,9 @@ Shader::Shader(const ShaderSources &sources)
: shaderSources(sources)
, program(0)
, builtinUniforms()
, vertexAttributes()
, lastCanvas((Canvas *) -1)
, lastViewport()
{
if (shaderSources.empty())
throw love::Exception("Cannot create shader: no source code!");
@@ -181,6 +183,14 @@ void Shader::createProgram(const std::vector<GLuint> &shaderids)
for (it = shaderids.begin(); it != shaderids.end(); ++it)
glAttachShader(program, *it);
// Bind generic vertex attribute indices to names in the shader.
for (int i = 0; i < int(OpenGL::ATTRIB_MAX_ENUM); i++)
{
const char *name = nullptr;
if (attribNames.find((OpenGL::VertexAttrib) i, name))
glBindAttribLocation(program, i, (const GLchar *) name);
}
glLinkProgram(program);
// flag shaders for auto-deletion when the program object is deleted.
@@ -273,6 +283,15 @@ bool Shader::loadVolatile()
// Retreive all active uniform variables in this shader from OpenGL.
mapActiveUniforms();
for (int i = 0; i < int(OpenGL::ATTRIB_MAX_ENUM); i++)
{
const char *name = nullptr;
if (attribNames.find(OpenGL::VertexAttrib(i), name))
vertexAttributes[i] = glGetAttribLocation(program, name);
else
vertexAttributes[i] = -1;
}
if (current == this)
{
// make sure glUseProgram gets called.
@@ -633,6 +652,11 @@ int Shader::getTextureUnit(const std::string &name)
return texunit;
}
bool Shader::hasVertexAttrib(OpenGL::VertexAttrib attrib) const
{
return vertexAttributes[int(attrib)] != -1;
}
bool Shader::hasBuiltinExtern(BuiltinExtern builtin) const
{
return builtinUniforms[int(builtin)] != -1;
@@ -670,30 +694,42 @@ bool Shader::sendBuiltinFloat(BuiltinExtern builtin, int size, const GLfloat *ve
void Shader::checkSetScreenParams()
{
if (lastCanvas == Canvas::current)
OpenGL::Viewport view = gl.getViewport();
if (view == lastViewport && lastCanvas == Canvas::current)
return;
// In the shader, we do pixcoord.y = gl_FragCoord.y * params[0] + params[1].
// In the shader, we do pixcoord.y = gl_FragCoord.y * params.z + params.w.
// This lets us flip pixcoord.y when needed, to be consistent (Canvases
// have flipped y-values for pixel coordinates.)
GLfloat params[] = {0.0f, 0.0f};
GLfloat params[] = {
(GLfloat) view.w, (GLfloat) view.h,
0.0f, 0.0f,
};
if (Canvas::current != nullptr)
{
// gl_FragCoord.y is flipped in Canvases, so we un-flip:
// pixcoord.y = gl_FragCoord.y * -1.0 + height.
params[0] = -1.0f;
params[1] = (float) Canvas::current->getHeight();
params[2] = -1.0f;
params[3] = (GLfloat) view.h;
}
else
{
// No flipping: pixcoord.y = gl_FragCoord.y * 1.0 + 0.0.
params[0] = 1.0f;
params[1] = 0.0f;
params[2] = 1.0f;
params[3] = 0.0f;
}
sendBuiltinFloat(BUILTIN_SCREEN_PARAMS, 2, params, 1);
sendBuiltinFloat(BUILTIN_SCREEN_SIZE, 4, params, 1);
lastCanvas = Canvas::current;
lastViewport = view;
}
const std::map<std::string, Object *> &Shader::getBoundRetainables() const
{
return boundRetainables;
}
std::string Shader::getGLSLVersion()
@@ -730,9 +766,16 @@ StringMap<Shader::ShaderType, Shader::TYPE_MAX_ENUM>::Entry Shader::typeNameEntr
StringMap<Shader::ShaderType, Shader::TYPE_MAX_ENUM> Shader::typeNames(Shader::typeNameEntries, sizeof(Shader::typeNameEntries));
StringMap<OpenGL::VertexAttrib, OpenGL::ATTRIB_MAX_ENUM>::Entry Shader::attribNameEntries[] =
{
{"love_PseudoInstanceID", OpenGL::ATTRIB_PSEUDO_INSTANCE_ID},
};
StringMap<OpenGL::VertexAttrib, OpenGL::ATTRIB_MAX_ENUM> Shader::attribNames(Shader::attribNameEntries, sizeof(Shader::attribNameEntries));
StringMap<Shader::BuiltinExtern, Shader::BUILTIN_MAX_ENUM>::Entry Shader::builtinNameEntries[] =
{
{"love_ScreenParams", Shader::BUILTIN_SCREEN_PARAMS},
{"love_ScreenSize", Shader::BUILTIN_SCREEN_SIZE},
};
StringMap<Shader::BuiltinExtern, Shader::BUILTIN_MAX_ENUM> Shader::builtinNames(Shader::builtinNameEntries, sizeof(Shader::builtinNameEntries));
+12 -1
View File
@@ -59,7 +59,7 @@ public:
// Built-in extern (uniform) variables.
enum BuiltinExtern
{
BUILTIN_SCREEN_PARAMS,
BUILTIN_SCREEN_SIZE,
BUILTIN_MAX_ENUM
};
@@ -138,10 +138,13 @@ public:
/**
* Internal use only.
**/
bool hasVertexAttrib(OpenGL::VertexAttrib attrib) const;
bool hasBuiltinExtern(BuiltinExtern builtin) const;
bool sendBuiltinFloat(BuiltinExtern builtin, int size, const GLfloat *m, int count);
void checkSetScreenParams();
const std::map<std::string, Object *> &getBoundRetainables() const;
static std::string getGLSLVersion();
static bool isSupported();
@@ -198,6 +201,9 @@ private:
// Location values for any built-in uniform variables.
GLint builtinUniforms[BUILTIN_MAX_ENUM];
// Location values for any generic vertex attribute variables.
GLint vertexAttributes[OpenGL::ATTRIB_MAX_ENUM];
// Uniform location buffer map
std::map<std::string, Uniform> uniforms;
@@ -210,6 +216,7 @@ private:
// Pointer to the active Canvas when the screen params were last checked.
Canvas *lastCanvas;
OpenGL::Viewport lastViewport;
// Max GPU texture units available for sent images
static GLint maxTexUnits;
@@ -220,6 +227,10 @@ private:
static StringMap<ShaderType, TYPE_MAX_ENUM>::Entry typeNameEntries[];
static StringMap<ShaderType, TYPE_MAX_ENUM> typeNames;
// Names for the generic vertex attributes used by love.
static StringMap<OpenGL::VertexAttrib, OpenGL::ATTRIB_MAX_ENUM>::Entry attribNameEntries[];
static StringMap<OpenGL::VertexAttrib, OpenGL::ATTRIB_MAX_ENUM> attribNames;
// Names for the built-in uniform variables.
static StringMap<BuiltinExtern, BUILTIN_MAX_ENUM>::Entry builtinNameEntries[];
static StringMap<BuiltinExtern, BUILTIN_MAX_ENUM> builtinNames;
+5 -1
View File
@@ -105,6 +105,8 @@ int SpriteBatch::add(float x, float y, float a, float sx, float sy, float ox, fl
if ((index == -1 && next >= size) || index < -1 || index >= size)
return -1;
Vertex sprite[4];
// Needed for colors.
memcpy(sprite, texture->getVertices(), sizeof(Vertex) * 4);
@@ -131,6 +133,8 @@ int SpriteBatch::addq(Quad *quad, float x, float y, float a, float sx, float sy,
if ((index == -1 && next >= size) || index < -1 || index >= next)
return -1;
Vertex sprite[4];
// Needed for colors.
memcpy(sprite, quad->getVertices(), sizeof(Vertex) * 4);
@@ -263,7 +267,7 @@ int SpriteBatch::getBufferSize() const
return size;
}
void SpriteBatch::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const
void SpriteBatch::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky)
{
const size_t vertex_offset = offsetof(Vertex, x);
const size_t texel_offset = offsetof(Vertex, s);
+1 -3
View File
@@ -109,7 +109,7 @@ public:
int getBufferSize() const;
// Implements Drawable.
void draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky) const;
void draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky);
static bool getConstant(const char *in, UsageHint &out);
static bool getConstant(UsageHint in, const char *&out);
@@ -135,8 +135,6 @@ private:
// The next free element.
int next;
Vertex sprite[4];
// Current color. This color, if present, will be applied to the next
// added sprite.
Color *color;
+2 -2
View File
@@ -48,12 +48,12 @@ public:
* Any setup the texture might need to do before drawing, e.g. binding
* the OpenGL texture for use.
**/
virtual void predraw() const {}
virtual void predraw() {}
/**
* Any cleanup the texture might need to do directly after drawing.
**/
virtual void postdraw() const {}
virtual void postdraw() {}
}; // Texture
+25 -13
View File
@@ -34,22 +34,21 @@ Canvas *luax_checkcanvas(lua_State *L, int idx)
int w_Canvas_renderTo(lua_State *L)
{
// As startGrab() clears the framebuffer, better not allow
// grabbing inside another grabbing
if (Canvas::current != NULL)
{
Canvas::bindDefaultCanvas();
return luaL_error(L, "Current render target not the default canvas!");
}
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;
EXCEPT_GUARD(canvas->startGrab();)
lua_settop(L, 2); // make sure the function is on top of the stack
lua_call(L, 0, 0);
canvas->stopGrab();
if (oldcanvas != nullptr)
oldcanvas->startGrab(oldcanvas->getAttachedCanvases());
else
Canvas::bindDefaultCanvas();
return 0;
}
@@ -111,16 +110,25 @@ int w_Canvas_clear(lua_State *L)
return 0;
}
int w_Canvas_getType(lua_State *L)
int w_Canvas_getFormat(lua_State *L)
{
Canvas *canvas = luax_checkcanvas(L, 1);
Canvas::TextureType type = canvas->getTextureType();
Texture::Format format = canvas->getTextureFormat();
const char *str;
Canvas::getConstant(type, str);
if (!Texture::getConstant(format, str))
return luaL_error(L, "Unknown texture format.");
lua_pushstring(L, str);
return 1;
}
int w_Canvas_getFSAA(lua_State *L)
{
Canvas *canvas = luax_checkcanvas(L, 1);
lua_pushinteger(L, canvas->getFSAA());
return 1;
}
static const luaL_Reg functions[] =
{
// From wrap_Texture.
@@ -136,7 +144,11 @@ static const luaL_Reg functions[] =
{ "getImageData", w_Canvas_getImageData },
{ "getPixel", w_Canvas_getPixel },
{ "clear", w_Canvas_clear },
{ "getType", w_Canvas_getType },
{ "getFormat", w_Canvas_getFormat },
{ "getFSAA", w_Canvas_getFSAA },
// Deprecated since 0.9.1.
{ "getType", w_Canvas_getFormat },
{ 0, 0 }
};
+2 -1
View File
@@ -39,7 +39,8 @@ int w_Canvas_renderTo(lua_State *L);
int w_Canvas_getImageData(lua_State *L);
int w_Canvas_getPixel(lua_State * L);
int w_Canvas_clear(lua_State *L);
int w_Canvas_getType(lua_State *L);
int w_Canvas_getFormat(lua_State *L);
int w_Canvas_getFSAA(lua_State *L);
extern "C" int luaopen_canvas(lua_State *L);
} // opengl
+116 -59
View File
@@ -146,14 +146,23 @@ int w_setInvertedStencil(lua_State *L)
int w_getMaxTextureSize(lua_State *L)
{
lua_pushinteger(L, instance->getMaxTextureSize());
lua_pushinteger(L, instance->getSystemLimit(Graphics::LIMIT_TEXTURE_SIZE));
return 1;
}
int w_newImage(lua_State *L)
{
love::image::ImageData *data = 0;
love::image::CompressedData *cdata = 0;
love::image::ImageData *data = nullptr;
love::image::CompressedData *cdata = nullptr;
Texture::Format format = Texture::FORMAT_NORMAL;
const char *fstr = lua_isnoneornil(L, 2) ? nullptr : luaL_checkstring(L, 2);
if (fstr != nullptr && !Texture::getConstant(fstr, format))
return luaL_error(L, "Invalid texture format: %s", fstr);
if (format == Texture::FORMAT_HDR) // For now...
return luaL_error(L, "HDR images are not supported.");
// Convert to FileData, if necessary.
if (lua_isstring(L, 1) || luax_istype(L, 1, FILESYSTEM_FILE_T))
@@ -185,15 +194,15 @@ int w_newImage(lua_State *L)
return luaL_error(L, "Error creating image.");
// Create the image.
Image *image = 0;
Image *image = nullptr;
EXCEPT_GUARD(
if (cdata)
image = instance->newImage(cdata);
image = instance->newImage(cdata, format);
else if (data)
image = instance->newImage(data);
image = instance->newImage(data, format);
)
if (image == 0)
if (image == nullptr)
return luaL_error(L, "Could not load image.");
// Push the type.
@@ -323,15 +332,16 @@ int w_newCanvas(lua_State *L)
int width = luaL_optint(L, 1, instance->getWidth());
int height = luaL_optint(L, 2, instance->getHeight());
const char *str = luaL_optstring(L, 3, "normal");
int fsaa = luaL_optint(L, 4, 0);
Canvas::TextureType texture_type;
if (!Canvas::getConstant(str, texture_type))
return luaL_error(L, "Invalid canvas type: %s", str);
Texture::Format format;
if (!Texture::getConstant(str, format))
return luaL_error(L, "Invalid texture format: %s", str);
Canvas *canvas = 0;
EXCEPT_GUARD(canvas = instance->newCanvas(width, height, texture_type);)
Canvas *canvas = nullptr;
EXCEPT_GUARD(canvas = instance->newCanvas(width, height, format, fsaa);)
if (canvas == 0)
if (canvas == nullptr)
return luaL_error(L, "Canvas not created, but no error thrown. I don't even...");
luax_pushtype(L, "Canvas", GRAPHICS_CANVAS_T, canvas);
@@ -440,8 +450,10 @@ int w_newShader(lua_State *L)
int w_newMesh(lua_State *L)
{
// Check first argument: mandatory table of vertices.
luaL_checktype(L, 1, LUA_TTABLE);
// Check first argument: table of vertices or number of vertices.
int ttype = lua_type(L, 1);
if (ttype != LUA_TTABLE && ttype != LUA_TNUMBER)
luaL_argerror(L, 1, "table or number expected");
// Second argument: optional texture.
Texture *tex = nullptr;
@@ -456,52 +468,60 @@ int w_newMesh(lua_State *L)
if (str && !Mesh::getConstant(str, mode))
return luaL_error(L, "Invalid mesh draw mode: %s", str);
size_t vertex_count = lua_objlen(L, 1);
std::vector<Vertex> vertices;
vertices.reserve(vertex_count);
bool use_colors = false;
// Get the vertices from the table.
for (size_t i = 1; i <= vertex_count; i++)
{
lua_rawgeti(L, 1, i);
if (lua_type(L, -1) != LUA_TTABLE)
return luax_typerror(L, 1, "table of tables");
for (int j = 1; j <= 8; j++)
lua_rawgeti(L, -j, j);
Vertex v;
v.x = (float) luaL_checknumber(L, -8);
v.y = (float) luaL_checknumber(L, -7);
v.s = (float) luaL_checknumber(L, -6);
v.t = (float) luaL_checknumber(L, -5);
v.r = (unsigned char) luaL_optinteger(L, -4, 255);
v.g = (unsigned char) luaL_optinteger(L, -3, 255);
v.b = (unsigned char) luaL_optinteger(L, -2, 255);
v.a = (unsigned char) luaL_optinteger(L, -1, 255);
// Enable per-vertex coloring if any color is not the default.
if (!use_colors && (v.r != 255 || v.g != 255 || v.b != 255 || v.a != 255))
use_colors = true;
lua_pop(L, 9);
vertices.push_back(v);
}
Mesh *t = nullptr;
EXCEPT_GUARD(t = instance->newMesh(vertices, mode);)
if (ttype == LUA_TTABLE)
{
size_t vertex_count = lua_objlen(L, 1);
std::vector<Vertex> vertices;
vertices.reserve(vertex_count);
bool use_colors = false;
// Get the vertices from the table.
for (size_t i = 1; i <= vertex_count; i++)
{
lua_rawgeti(L, 1, i);
if (lua_type(L, -1) != LUA_TTABLE)
return luax_typerror(L, 1, "table of tables");
for (int j = 1; j <= 8; j++)
lua_rawgeti(L, -j, j);
Vertex v;
v.x = (float) luaL_checknumber(L, -8);
v.y = (float) luaL_checknumber(L, -7);
v.s = (float) luaL_optnumber(L, -6, 0.0);
v.t = (float) luaL_optnumber(L, -5, 0.0);
v.r = (unsigned char) luaL_optinteger(L, -4, 255);
v.g = (unsigned char) luaL_optinteger(L, -3, 255);
v.b = (unsigned char) luaL_optinteger(L, -2, 255);
v.a = (unsigned char) luaL_optinteger(L, -1, 255);
// Enable per-vertex coloring if any color is not the default.
if (!use_colors && (v.r != 255 || v.g != 255 || v.b != 255 || v.a != 255))
use_colors = true;
lua_pop(L, 9);
vertices.push_back(v);
}
EXCEPT_GUARD(t = instance->newMesh(vertices, mode);)
t->setVertexColors(use_colors);
}
else
{
int count = luaL_checkint(L, 1);
EXCEPT_GUARD(t = instance->newMesh(count, mode);)
}
if (tex)
t->setTexture(tex);
t->setVertexColors(use_colors);
luax_pushtype(L, "Mesh", GRAPHICS_MESH_T, t);
return 1;
}
@@ -798,7 +818,19 @@ int w_getPointSize(lua_State *L)
int w_getMaxPointSize(lua_State *L)
{
lua_pushnumber(L, instance->getMaxPointSize());
lua_pushnumber(L, instance->getSystemLimit(Graphics::LIMIT_POINT_SIZE));
return 1;
}
int w_setWireframe(lua_State *L)
{
instance->setWireframe(luax_toboolean(L, 1));
return 0;
}
int w_isWireframe(lua_State *L)
{
luax_pushboolean(L, instance->isWireframe());
return 1;
}
@@ -962,6 +994,14 @@ int w_isSupported(lua_State *L)
if (!Image::hasCompressedTextureSupport(image::CompressedData::FORMAT_BC5))
supported = false;
break;
case Graphics::SUPPORT_INSTANCING:
if (!GLEE_ARB_draw_instanced)
supported = false;
break;
case Graphics::SUPPORT_SRGB:
if (!Canvas::isSRGBSupported())
supported = false;
break;
default:
supported = false;
}
@@ -989,6 +1029,18 @@ int w_getRendererInfo(lua_State *L)
return 4;
}
int w_getSystemLimit(lua_State *L)
{
const char *limitstr = luaL_checkstring(L, 1);
Graphics::SystemLimit limittype;
if (!Graphics::getConstant(limitstr, limittype))
return luaL_error(L, "Invalid system limit type: %s", limitstr);
lua_pushnumber(L, instance->getSystemLimit(limittype));
return 1;
}
int w_draw(lua_State *L)
{
Drawable *drawable = nullptr;
@@ -1100,8 +1152,9 @@ int w_line(lua_State *L)
args = lua_objlen(L, 1);
is_table = true;
}
if (args % 2 != 0)
return luaL_error(L, "Number of vertices must be a multiple of two");
return luaL_error(L, "Number of vertex components must be a multiple of two");
else if (args < 4)
return luaL_error(L, "Need at least two vertices to draw a line");
@@ -1202,7 +1255,7 @@ int w_polygon(lua_State *L)
}
if (args % 2 != 0)
return luaL_error(L, "Number of vertices must be a multiple of two");
return luaL_error(L, "Number of vertex components must be a multiple of two");
else if (args < 6)
return luaL_error(L, "Need at least three vertices to draw a polygon");
@@ -1325,6 +1378,8 @@ static const luaL_Reg functions[] =
{ "getPointSize", w_getPointSize },
{ "getMaxPointSize", w_getMaxPointSize },
{ "getMaxTextureSize", w_getMaxTextureSize },
{ "setWireframe", w_setWireframe },
{ "isWireframe", w_isWireframe },
{ "newScreenshot", w_newScreenshot },
{ "setCanvas", w_setCanvas },
{ "getCanvas", w_getCanvas },
@@ -1334,6 +1389,7 @@ static const luaL_Reg functions[] =
{ "isSupported", w_isSupported },
{ "getRendererInfo", w_getRendererInfo },
{ "getSystemLimit", w_getSystemLimit },
{ "draw", w_draw },
@@ -1369,6 +1425,7 @@ static const luaL_Reg functions[] =
// Deprecated since 0.9.1.
{ "getMaxImageSize", w_getMaxTextureSize },
{ "getMaxPointSize", w_getMaxPointSize },
{ 0, 0 }
};
@@ -83,6 +83,8 @@ int w_getLineJoin(lua_State *L);
int w_setPointSize(lua_State *L);
int w_getPointSize(lua_State *L);
int w_getMaxPointSize(lua_State *L);
int w_setWireframe(lua_State *L);
int w_isWireframe(lua_State *L);
int w_newScreenshot(lua_State *L);
int w_setCanvas(lua_State *L);
int w_getCanvas(lua_State *L);
@@ -90,6 +92,7 @@ int w_setShader(lua_State *L);
int w_getShader(lua_State *L);
int w_isSupported(lua_State *L);
int w_getRendererInfo(lua_State *L);
int w_getSystemLimit(lua_State *L);
int w_draw(lua_State *L);
int w_print(lua_State *L);
int w_printf(lua_State *L);
+62 -27
View File
@@ -53,8 +53,8 @@ int w_Mesh_setVertex(lua_State *L)
v.x = luaL_checknumber(L, -8);
v.y = luaL_checknumber(L, -7);
v.s = luaL_checknumber(L, -6);
v.t = luaL_checknumber(L, -5);
v.s = luaL_optnumber(L, -6, 0.0);
v.t = luaL_optnumber(L, -5, 0.0);
v.r = luaL_optinteger(L, -4, 255);
v.g = luaL_optinteger(L, -3, 255);
v.b = luaL_optinteger(L, -2, 255);
@@ -66,8 +66,8 @@ int w_Mesh_setVertex(lua_State *L)
{
v.x = luaL_checknumber(L, 3);
v.y = luaL_checknumber(L, 4);
v.s = luaL_checknumber(L, 5);
v.t = luaL_checknumber(L, 6);
v.s = luaL_optnumber(L, 5, 0.0);
v.t = luaL_optnumber(L, 6, 0.0);
v.r = luaL_optinteger(L, 7, 255);
v.g = luaL_optinteger(L, 8, 255);
v.b = luaL_optinteger(L, 9, 255);
@@ -122,8 +122,8 @@ int w_Mesh_setVertices(lua_State *L)
v.x = (float) luaL_checknumber(L, -8);
v.y = (float) luaL_checknumber(L, -7);
v.s = (float) luaL_checknumber(L, -6);
v.t = (float) luaL_checknumber(L, -5);
v.s = (float) luaL_optnumber(L, -6, 0.0);
v.t = (float) luaL_optnumber(L, -5, 0.0);
v.r = (unsigned char) luaL_optinteger(L, -4, 255);
v.g = (unsigned char) luaL_optinteger(L, -3, 255);
@@ -220,14 +220,15 @@ int w_Mesh_setVertexMap(lua_State *L)
int w_Mesh_getVertexMap(lua_State *L)
{
Mesh *t = luax_checkmesh(L, 1);
const uint32 *vertex_map = 0;
EXCEPT_GUARD(vertex_map = t->getVertexMap();)
size_t elements = t->getVertexMapCount();
std::vector<uint32> vertex_map;
EXCEPT_GUARD(t->getVertexMap(vertex_map);)
lua_createtable(L, elements, 0);
size_t element_count = vertex_map.size();
for (size_t i = 0; i < elements; i++)
lua_createtable(L, element_count, 0);
for (size_t i = 0; i < element_count; i++)
{
lua_pushinteger(L, lua_Integer(vertex_map[i]) + 1);
lua_rawseti(L, -2, i + 1);
@@ -236,6 +237,20 @@ int w_Mesh_getVertexMap(lua_State *L)
return 1;
}
int w_Mesh_setInstanceCount(lua_State *L)
{
Mesh *t = luax_checkmesh(L, 1);
t->setInstanceCount(luaL_checkint(L, 2));
return 0;
}
int w_Mesh_getInstanceCount(lua_State *L)
{
Mesh *t = luax_checkmesh(L, 1);
lua_pushinteger(L, t->getInstanceCount());
return 1;
}
int w_Mesh_setTexture(lua_State *L)
{
Mesh *t = luax_checkmesh(L, 1);
@@ -301,6 +316,38 @@ int w_Mesh_getDrawMode(lua_State *L)
return 1;
}
int w_Mesh_setDrawRange(lua_State *L)
{
Mesh *t = luax_checkmesh(L, 1);
if (lua_isnoneornil(L, 2))
t->setDrawRange();
else
{
int rangemin = luaL_checkint(L, 2) - 1;
int rangemax = luaL_checkint(L, 3) - 1;
EXCEPT_GUARD(t->setDrawRange(rangemin, rangemax);)
}
return 0;
}
int w_Mesh_getDrawRange(lua_State *L)
{
Mesh *t = luax_checkmesh(L, 1);
int rangemin = -1;
int rangemax = -1;
t->getDrawRange(rangemin, rangemax);
if (rangemin < 0 || rangemax < 0)
return 0;
lua_pushinteger(L, rangemin + 1);
lua_pushinteger(L, rangemax + 1);
return 2;
}
int w_Mesh_setVertexColors(lua_State *L)
{
Mesh *t = luax_checkmesh(L, 1);
@@ -315,20 +362,6 @@ int w_Mesh_hasVertexColors(lua_State *L)
return 1;
}
int w_Mesh_setWireframe(lua_State *L)
{
Mesh *t = luax_checkmesh(L, 1);
t->setWireframe(luax_toboolean(L, 2));
return 0;
}
int w_Mesh_isWireframe(lua_State *L)
{
Mesh *t = luax_checkmesh(L, 1);
luax_pushboolean(L, t->isWireframe());
return 1;
}
static const luaL_Reg functions[] =
{
{ "setVertex", w_Mesh_setVertex },
@@ -338,14 +371,16 @@ static const luaL_Reg functions[] =
{ "getVertexCount", w_Mesh_getVertexCount },
{ "setVertexMap", w_Mesh_setVertexMap },
{ "getVertexMap", w_Mesh_getVertexMap },
{ "setInstanceCount", w_Mesh_setInstanceCount },
{ "getInstanceCount", w_Mesh_getInstanceCount },
{ "setTexture", w_Mesh_setTexture },
{ "getTexture", w_Mesh_getTexture },
{ "setDrawMode", w_Mesh_setDrawMode },
{ "getDrawMode", w_Mesh_getDrawMode },
{ "setDrawRange", w_Mesh_setDrawRange },
{ "getDrawRange", w_Mesh_getDrawRange },
{ "setVertexColors", w_Mesh_setVertexColors },
{ "hasVertexColors", w_Mesh_hasVertexColors },
{ "setWireframe", w_Mesh_setWireframe },
{ "isWireframe", w_Mesh_isWireframe },
// Deprecated since 0.9.1.
{ "setImage", w_Mesh_setTexture },
+4 -2
View File
@@ -41,14 +41,16 @@ int w_Mesh_getVertices(lua_State *L);
int w_Mesh_getVertexCount(lua_State *L);
int w_Mesh_setVertexMap(lua_State *L);
int w_Mesh_getVertexMap(lua_State *L);
int w_Mesh_setInstanceCount(lua_State *L);
int w_Mesh_getInstanceCount(lua_State *L);
int w_Mesh_setTexture(lua_State *L);
int w_Mesh_getTexture(lua_State *L);
int w_Mesh_setDrawMode(lua_State *L);
int w_Mesh_getDrawMode(lua_State *L);
int w_Mesh_setDrawRange(lua_State *L);
int w_Mesh_getDrawRange(lua_State *L);
int w_Mesh_setVertexColors(lua_State *L);
int w_Mesh_hasVertexColors(lua_State *L);
int w_Mesh_setWireframe(lua_State *L);
int w_Mesh_isWireframe(lua_State *L);
extern "C" int luaopen_mesh(lua_State *L);
@@ -127,7 +127,7 @@ int w_ParticleSystem_getInsertMode(lua_State *L)
int w_ParticleSystem_setEmissionRate(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
int arg1 = luaL_checkint(L, 2);
float arg1 = (float) luaL_checknumber(L, 2);
EXCEPT_GUARD(t->setEmissionRate(arg1);)
return 0;
}
@@ -135,7 +135,7 @@ int w_ParticleSystem_setEmissionRate(lua_State *L)
int w_ParticleSystem_getEmissionRate(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
lua_pushinteger(L, t->getEmissionRate());
lua_pushnumber(L, t->getEmissionRate());
return 1;
}
@@ -191,6 +191,15 @@ int w_ParticleSystem_getPosition(lua_State *L)
return 2;
}
int w_ParticleSystem_moveTo(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
float arg1 = (float)luaL_checknumber(L, 2);
float arg2 = (float)luaL_checknumber(L, 3);
t->moveTo(arg1, arg2);
return 0;
}
int w_ParticleSystem_setAreaSpread(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
@@ -560,6 +569,20 @@ int w_ParticleSystem_getColors(lua_State *L)
return colors.size();
}
int w_ParticleSystem_setRelativeRotation(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
t->setRelativeRotation(luax_toboolean(L, 2));
return 0;
}
int w_ParticleSystem_hasRelativeRotation(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
luax_pushboolean(L, t->hasRelativeRotation());
return 1;
}
int w_ParticleSystem_getCount(lua_State *L)
{
ParticleSystem *t = luax_checkparticlesystem(L, 1);
@@ -649,6 +672,7 @@ static const luaL_Reg functions[] =
{ "getParticleLifetime", w_ParticleSystem_getParticleLifetime },
{ "setPosition", w_ParticleSystem_setPosition },
{ "getPosition", w_ParticleSystem_getPosition },
{ "moveTo", w_ParticleSystem_moveTo },
{ "setAreaSpread", w_ParticleSystem_setAreaSpread },
{ "getAreaSpread", w_ParticleSystem_getAreaSpread },
{ "setDirection", w_ParticleSystem_setDirection },
@@ -677,6 +701,8 @@ static const luaL_Reg functions[] =
{ "getColors", w_ParticleSystem_getColors },
{ "setOffset", w_ParticleSystem_setOffset },
{ "getOffset", w_ParticleSystem_getOffset },
{ "setRelativeRotation", w_ParticleSystem_setRelativeRotation },
{ "hasRelativeRotation", w_ParticleSystem_hasRelativeRotation },
{ "getCount", w_ParticleSystem_getCount },
{ "start", w_ParticleSystem_start },
{ "stop", w_ParticleSystem_stop },
@@ -48,6 +48,7 @@ int w_ParticleSystem_setParticleLifetime(lua_State *L);
int w_ParticleSystem_getParticleLifetime(lua_State *L);
int w_ParticleSystem_setPosition(lua_State *L);
int w_ParticleSystem_getPosition(lua_State *L);
int w_ParticleSystem_moveTo(lua_State *L);
int w_ParticleSystem_setAreaSpread(lua_State *L);
int w_ParticleSystem_getAreaSpread(lua_State *L);
int w_ParticleSystem_setDirection(lua_State *L);
@@ -76,6 +77,8 @@ int w_ParticleSystem_setColors(lua_State *L);
int w_ParticleSystem_getColors(lua_State *L);
int w_ParticleSystem_setOffset(lua_State *L);
int w_ParticleSystem_getOffset(lua_State *L);
int w_ParticleSystem_setRelativeRotation(lua_State *L);
int w_ParticleSystem_hasRelativeRotation(lua_State *L);
int w_ParticleSystem_getCount(lua_State *L);
int w_ParticleSystem_start(lua_State *L);
int w_ParticleSystem_stop(lua_State *L);