Canvases can now be used in SpriteBatches, ParticleSystems, and Meshes (resolves issue #782).

Canvases and Images are now subclasses of the new Texture class.
Added setTexture and getTexture methods for Meshes, ParticleSystems, and SpriteBatches (the old set/getImage methods are deprecated but not removed.)
Canvas texture coordinates are no longer flipped.
This commit is contained in:
Alex Szpakowski
2014-01-03 17:07:12 -04:00
parent 1d3f6e967d
commit aa69a695d3
43 changed files with 1011 additions and 852 deletions
+134 -55
View File
@@ -18,11 +18,13 @@
* 3. This notice may not be removed or altered from any source distribution.
**/
// LOVE
#include "common/config.h"
#include "Shader.h"
#include "Graphics.h"
#include "Canvas.h"
// C++
#include <algorithm>
namespace love
@@ -47,7 +49,7 @@ namespace
~TemporaryAttacher()
{
if (prevShader != NULL)
if (prevShader != nullptr)
prevShader->attach();
else
curShader->detach();
@@ -59,28 +61,30 @@ namespace
} // anonymous namespace
Shader *Shader::current = NULL;
Shader *Shader::current = nullptr;
GLint Shader::maxTextureUnits = 0;
GLint Shader::maxTexUnits = 0;
std::vector<int> Shader::textureCounters;
Shader::Shader(const ShaderSources &sources)
: shaderSources(sources)
, program(0)
, builtinUniforms()
, lastCanvas((Canvas *) -1)
{
if (shaderSources.empty())
throw love::Exception("Cannot create shader: no source code!");
if (maxTextureUnits <= 0)
if (maxTexUnits <= 0)
{
GLint maxtexunits;
glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxtexunits);
maxTextureUnits = std::max(maxtexunits - 1, 0);
maxTexUnits = std::max(maxtexunits - 1, 0);
}
// initialize global texture id counters if needed
if (textureCounters.size() < (size_t) maxTextureUnits)
textureCounters.resize(maxTextureUnits, 0);
if (textureCounters.size() < (size_t) maxTexUnits)
textureCounters.resize(maxTexUnits, 0);
// load shader source and create program object
loadVolatile();
@@ -147,7 +151,7 @@ GLuint Shader::compileCode(ShaderType type, const std::string &code)
glGetShaderiv(shaderid, GL_INFO_LOG_LENGTH, &infologlen);
GLchar *infolog = new GLchar[infologlen + 1];
glGetShaderInfoLog(shaderid, infologlen, NULL, infolog);
glGetShaderInfoLog(shaderid, infologlen, nullptr, infolog);
// Save any warnings for later querying.
if (infologlen > 0)
@@ -232,6 +236,11 @@ void Shader::mapActiveUniforms()
u.name.erase(u.name.length() - 3);
}
// If this is a built-in (LOVE-created) uniform, store the location.
BuiltinExtern builtin;
if (builtinNames.find(u.name.c_str(), builtin))
builtinUniforms[int(builtin)] = u.location;
if (u.location != -1)
uniforms[u.name] = u;
}
@@ -240,8 +249,12 @@ void Shader::mapActiveUniforms()
bool Shader::loadVolatile()
{
// zero out active texture list
activeTextureUnits.clear();
activeTextureUnits.insert(activeTextureUnits.begin(), maxTextureUnits, 0);
activeTexUnits.clear();
activeTexUnits.insert(activeTexUnits.begin(), maxTexUnits, 0);
// Built-in uniform locations default to -1 (nonexistant.)
for (int i = 0; i < int(BUILTIN_MAX_ENUM); i++)
builtinUniforms[i] = -1;
std::vector<GLuint> shaderids;
@@ -263,7 +276,7 @@ bool Shader::loadVolatile()
if (current == this)
{
// make sure glUseProgram gets called.
current = NULL;
current = nullptr;
attach();
}
@@ -282,19 +295,23 @@ void Shader::unloadVolatile()
}
// decrement global texture id counters for texture units which had textures bound from this shader
for (size_t i = 0; i < activeTextureUnits.size(); ++i)
for (size_t i = 0; i < activeTexUnits.size(); ++i)
{
if (activeTextureUnits[i] > 0)
if (activeTexUnits[i] > 0)
textureCounters[i] = std::max(textureCounters[i] - 1, 0);
}
// active texture list is probably invalid, clear it
activeTextureUnits.clear();
activeTextureUnits.insert(activeTextureUnits.begin(), maxTextureUnits, 0);
activeTexUnits.clear();
activeTexUnits.insert(activeTexUnits.begin(), maxTexUnits, 0);
// same with uniform location list
uniforms.clear();
// And the locations of any built-in uniform variables.
for (int i = 0; i < int(BUILTIN_MAX_ENUM); i++)
builtinUniforms[i] = -1;
shaderWarnings.clear();
}
@@ -337,7 +354,7 @@ void Shader::attach(bool temporary)
{
if (current != this)
{
if (current != NULL)
if (current != nullptr)
current->release();
glUseProgram(program);
@@ -350,10 +367,10 @@ void Shader::attach(bool temporary)
{
// make sure all sent textures are properly bound to their respective texture units
// note: list potentially contains texture ids of deleted/invalid textures!
for (size_t i = 0; i < activeTextureUnits.size(); ++i)
for (size_t i = 0; i < activeTexUnits.size(); ++i)
{
if (activeTextureUnits[i] > 0)
gl.bindTextureToUnit(activeTextureUnits[i], i + 1, false);
if (activeTexUnits[i] > 0)
gl.bindTextureToUnit(activeTexUnits[i], i + 1, false);
}
// We always want to use texture unit 0 for everyhing else.
@@ -363,10 +380,10 @@ void Shader::attach(bool temporary)
void Shader::detach()
{
if (current != NULL)
if (current != nullptr)
glUseProgram(0);
current = NULL;
current = nullptr;
}
const Shader::Uniform &Shader::getUniform(const std::string &name) const
@@ -544,61 +561,53 @@ void Shader::sendMatrix(const std::string &name, int size, const GLfloat *m, int
}
}
void Shader::sendTexture(const std::string &name, GLuint texture)
void Shader::sendTexture(const std::string &name, Texture *texture)
{
GLuint gltex = texture->getGLTexture();
TemporaryAttacher attacher(this);
int textureunit = getTextureUnit(name);
int texunit = getTextureUnit(name);
const Uniform &u = getUniform(name);
checkSetUniformError(u, 1, 1, UNIFORM_SAMPLER);
// bind texture to assigned texture unit and send uniform to shader program
gl.bindTextureToUnit(texture, textureunit, false);
gl.bindTextureToUnit(gltex, texunit, false);
glUniform1i(u.location, textureunit);
glUniform1i(u.location, texunit);
// reset texture unit
gl.setTextureUnit(0);
// increment global shader texture id counter for this texture unit, if we haven't already
if (activeTextureUnits[textureunit-1] == 0)
++textureCounters[textureunit-1];
if (activeTexUnits[texunit-1] == 0)
++textureCounters[texunit-1];
// store texture id so it can be re-bound to the proper texture unit later
activeTextureUnits[textureunit-1] = texture;
activeTexUnits[texunit-1] = gltex;
retainObject(name, texture);
}
void Shader::retainTexture(const std::string &name, Object *texture)
void Shader::retainObject(const std::string &name, Object *object)
{
auto it = boundRetainables.find(name);
if (it != boundRetainables.end())
it->second->release();
texture->retain();
boundRetainables[name] = texture;
}
void Shader::sendImage(const std::string &name, Image &image)
{
sendTexture(name, image.getTextureName());
retainTexture(name, &image);
}
void Shader::sendCanvas(const std::string &name, Canvas &canvas)
{
sendTexture(name, canvas.getTextureName());
retainTexture(name, &canvas);
object->retain();
boundRetainables[name] = object;
}
int Shader::getTextureUnit(const std::string &name)
{
auto it = textureUnitPool.find(name);
auto it = texUnitPool.find(name);
if (it != textureUnitPool.end())
if (it != texUnitPool.end())
return it->second;
int textureunit = 1;
int texunit = 1;
// prefer texture units which are unused by all other shaders
auto freeunit_it = std::find(textureCounters.begin(), textureCounters.end(), 0);
@@ -606,33 +615,96 @@ int Shader::getTextureUnit(const std::string &name)
if (freeunit_it != textureCounters.end())
{
// we don't want to use unit 0
textureunit = std::distance(textureCounters.begin(), freeunit_it) + 1;
texunit = std::distance(textureCounters.begin(), freeunit_it) + 1;
}
else
{
// no completely unused texture units exist, try to use next free slot in our own list
auto nextunit_it = std::find(activeTextureUnits.begin(), activeTextureUnits.end(), 0);
auto nextunit_it = std::find(activeTexUnits.begin(), activeTexUnits.end(), 0);
if (nextunit_it == activeTextureUnits.end())
if (nextunit_it == activeTexUnits.end())
throw love::Exception("No more texture units available for shader.");
// we don't want to use unit 0
textureunit = std::distance(activeTextureUnits.begin(), nextunit_it) + 1;
texunit = std::distance(activeTexUnits.begin(), nextunit_it) + 1;
}
textureUnitPool[name] = textureunit;
return textureunit;
texUnitPool[name] = texunit;
return texunit;
}
bool Shader::hasBuiltinExtern(BuiltinExtern builtin) const
{
return builtinUniforms[int(builtin)] != -1;
}
bool Shader::sendBuiltinFloat(BuiltinExtern builtin, int size, const GLfloat *vec, int count)
{
if (!hasBuiltinExtern(builtin))
return false;
GLint location = builtinUniforms[int(builtin)];
TemporaryAttacher attacher(this);
switch (size)
{
case 1:
glUniform1fv(location, count, vec);
break;
case 2:
glUniform2fv(location, count, vec);
break;
case 3:
glUniform3fv(location, count, vec);
break;
case 4:
glUniform4fv(location, count, vec);
break;
default:
return false;
}
return true;
}
void Shader::checkSetScreenParams()
{
if (lastCanvas == Canvas::current)
return;
// In the shader, we do pixcoord.y = gl_FragCoord.y * params[0] + params[1].
// 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};
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();
}
else
{
// No flipping: pixcoord.y = gl_FragCoord.y * 1.0 + 0.0.
params[0] = 1.0f;
params[1] = 0.0f;
}
sendBuiltinFloat(BUILTIN_SCREEN_PARAMS, 2, params, 1);
lastCanvas = Canvas::current;
}
std::string Shader::getGLSLVersion()
{
const char *tmp = 0;
const char *tmp = nullptr;
// GL_SHADING_LANGUAGE_VERSION isn't available in OpenGL < 2.0.
if (GLEE_VERSION_2_0 || GLEE_ARB_shading_language_100)
tmp = (const char *) glGetString(GL_SHADING_LANGUAGE_VERSION);
if (tmp == 0)
if (tmp == nullptr)
return "0.0";
// the version string always begins with a version number of the format
@@ -658,6 +730,13 @@ 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<Shader::BuiltinExtern, Shader::BUILTIN_MAX_ENUM>::Entry Shader::builtinNameEntries[] =
{
{"love_ScreenParams", Shader::BUILTIN_SCREEN_PARAMS},
};
StringMap<Shader::BuiltinExtern, Shader::BUILTIN_MAX_ENUM> Shader::builtinNames(Shader::builtinNameEntries, sizeof(Shader::builtinNameEntries));
} // opengl
} // graphics
} // love