mirror of
https://github.com/love2d/love.git
synced 2026-08-16 08:11:02 +02:00
Merged default into minor
--HG-- branch : minor
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 }
|
||||
};
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user