#include "MLSpriteBatch.h"
using MLRenderer::MLSpriteBatch;
MLSpriteBatch::MLSpriteBatch(void)
{
m_shader = new MLShader();
//load the default sprite batch shaders
m_shader->LoadShaderFile("shaders/SBatch.vert", GL_VERTEX_SHADER);
m_shader->LoadShaderFile("shaders/SBatch.frag", GL_FRAGMENT_SHADER);
//lock the shader in place to add the attributes and link
m_shader->lock();
{
m_shader->bindAtt(MLRenderer::ML_ATT_VERTEXPOS, "VertexPosition");
m_shader->bindAtt(MLRenderer::ML_ATT_TEXTURE0, "TextureCoord");
m_shader->LinkShaders();
}
m_shader->unlock(); //unlock the shader
m_beginCall = false;
MLError::ErrorHandler::ML_Printf(ML_SpriteBatch, "Creating batch with max %i sprites\n", ML_MAX_SPRITES);
//initalise the batcher
Initalise();
MLError::ErrorHandler::ML_Printf(ML_SpriteBatch, "Created Sprite Batch\n");
}
MLSpriteBatch::MLSpriteBatch(MLShader* inShader) : m_shader(inShader)
{
m_beginCall = false;
Initalise();
}
MLSpriteBatch::~MLSpriteBatch(void)
{
Release(this);
}
void MLSpriteBatch::release()
{
if (indicies)
delete [] indicies;
if (Vcount)
delete [] Vcount;
}
void MLSpriteBatch::releaseShader()
{
if (m_shader)
delete m_shader;
}
void MLSpriteBatch::Initalise()
{
//generate 2 buffers for the vertex and UV VBO
GLuint buffID[2];
glGenBuffers(2, buffID);
m_VposBuffer = buffID[0];
m_UVBuffer = buffID[1];
//set blank data to both and set them to the max amount of sprites
glBindBuffer(GL_ARRAY_BUFFER, m_VposBuffer);
glBufferData(GL_ARRAY_BUFFER, (ML_MAX_SPRITES * 4) * sizeof(Vector3f), 0, GL_DYNAMIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, m_UVBuffer);
glBufferData(GL_ARRAY_BUFFER, (ML_MAX_SPRITES * 4) * sizeof(Vector3f), 0, GL_DYNAMIC_DRAW);
//generate the VAO
glGenVertexArrays(1, &m_BatchVAO);
//bind VAO
glBindVertexArray(m_BatchVAO);
//enable the vertex attributes for the VAO in this case vertex pos and texture are needed
glEnableVertexAttribArray((GLuint)MLRenderer::ML_ATT_VERTEXPOS); // Vertex position
glEnableVertexAttribArray((GLuint)MLRenderer::ML_ATT_TEXTURE0); //vertex texture coord
//set the vertex attribute pointers
glBindBuffer(GL_ARRAY_BUFFER, m_VposBuffer);
glVertexAttribPointer(MLRenderer::ML_ATT_VERTEXPOS, 3, GL_FLOAT, GL_FALSE, 0, (GLubyte *)NULL );
glBindBuffer(GL_ARRAY_BUFFER, m_UVBuffer);
glVertexAttribPointer(MLRenderer::ML_ATT_TEXTURE0, 2, GL_FLOAT, GL_FALSE, 0, (GLubyte *)NULL );
//unbind buffers
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
//create an index array for the primitives
indicies = new GLint[ML_MAX_SPRITES]();
Vcount = new GLsizei[ML_MAX_SPRITES]();
for(int i = 0; i < ML_MAX_SPRITES; i++)
{
indicies[i] = i * 4;
Vcount[i] = 4;
}
//set the total sprite count to 0
totalSpriteCount = 0;
}
void MLSpriteBatch::setShader(MLShader* inShader)
{
m_shader = inShader;
}
void MLSpriteBatch::Begin(bool Alpha)
{
//simple check for alpha. This is pre-set to true
if (Alpha)
{
glEnable (GL_BLEND);
glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}
else
glDisable(GL_BLEND);
//set the begincall to true so now end can be called without error
m_beginCall = true;
}
void MLSpriteBatch::Draw(MLSprite* Sprite)
{
//check to see if the sprite is enabled or not
if (!Sprite->getEnabled())
return;
bool NewBatch = false;
//a pointer for a batch item
MLBatchItem::batchInfo* tmpBatch;
//iterate through the batched items and see if the texture has been called before
BatchMap::iterator it = m_BatchItems.find(Sprite->getTexture());
if (it != m_BatchItems.end()) //if it has then set the pointer to that batch
tmpBatch = m_BatchItems[Sprite->getTexture()];
else // otherwise create a new batch
{
tmpBatch = new MLBatchItem::batchInfo();
tmpBatch->spriteCount = 0;
NewBatch = true;
}
//create the points of the source rectangle
Vector3f topLeft = Vector3f(Sprite->getPos().x, Sprite->getPos().y , 0.0f);
Vector3f topRight = Vector3f((Sprite->getPos().x + Sprite->getSize().x), Sprite->getPos().y, 0.0f);
Vector3f bottmLeft = Vector3f(Sprite->getPos().x, (Sprite->getPos().y + Sprite->getSize().y), 0.0f);
Vector3f bottomRight = Vector3f((Sprite->getPos().x + Sprite->getSize().x), (Sprite->getPos().y + Sprite->getSize().y), 0.0f);
//add the points to the batch
tmpBatch->addPoint(topLeft, Vector2f(0.0f, 1.0f));
tmpBatch->addPoint(topRight, Vector2f(1.0f, 1.0f));
tmpBatch->addPoint(bottomRight, Vector2f(1.0f, 0.0f));
tmpBatch->addPoint(bottmLeft, Vector2f(0.0f, 0.0f));
//increase the batch sprite and total sprite count
tmpBatch->spriteCount++;
totalSpriteCount++;
//if it was a new batch created add it to the map
if (NewBatch)
m_BatchItems[Sprite->getTexture()] = tmpBatch;
//if the max sprite count is reached then render them all out
if (totalSpriteCount >= ML_MAX_SPRITES)
Render();
}
void MLSpriteBatch::Draw(GLuint Texture, Vector2f Pos, Vector2f Size)
{
bool NewBatch = false;
MLBatchItem::batchInfo* tmpBatch;
BatchMap::iterator it = m_BatchItems.find(Texture);
if (it != m_BatchItems.end())
tmpBatch = m_BatchItems[Texture];
else
{
tmpBatch = new MLBatchItem::batchInfo();
tmpBatch->spriteCount = 0;
NewBatch = true;
}
Vector3f topLeft = Vector3f(Pos.x, Pos.y , 0.0f);
Vector3f topRight = Vector3f((Pos.x + Size.x), Pos.y, 0.0f);
Vector3f bottmLeft = Vector3f(Pos.x, (Pos.y + Size.y), 0.0f);
Vector3f bottomRight = Vector3f((Pos.x + Size.x), (Pos.y + Size.y), 0.0f);
tmpBatch->addPoint(topLeft, Vector2f(0.0f, 1.0f));
tmpBatch->addPoint(topRight, Vector2f(1.0f, 1.0f));
tmpBatch->addPoint(bottomRight, Vector2f(1.0f, 0.0f));
tmpBatch->addPoint(bottmLeft, Vector2f(0.0f, 0.0f));
tmpBatch->spriteCount++;
if (NewBatch)
m_BatchItems[Texture] = tmpBatch;
}
void MLSpriteBatch::End()
{
//If begin has not been called then there is an error
if (!m_beginCall)
{
MLError::ErrorHandler::printError("Begin must be called before end");
return;
}
//if the sprite count is greater than 0 the something will need to be rendered still
if (totalSpriteCount > 0)
Render();
//clear the batch after done just in case
m_BatchItems.clear();
}
void MLSpriteBatch::Render()
{
//lock the shader
m_shader->lock();
//apply the uniforms
m_shader->addUniform("inMVP", (MLRenderer::MLCoreGL::getOrtho()));
m_shader->addUniform("Tex1", 0);
//bind the vertex array
glBindVertexArray(m_BatchVAO);
//start iterating through the batchs
BatchMap::iterator it = m_BatchItems.begin();
for(; it != m_BatchItems.end(); it++)
{
//create a pointer to the current batch
MLBatchItem::batchInfo* currentBatch = it->second;
//bind the current texture
glBindTexture(GL_TEXTURE_2D, it->first);
//bind the vertex buffer and set the data from the batch
glBindBuffer(GL_ARRAY_BUFFER, m_VposBuffer);
glBufferSubData(GL_ARRAY_BUFFER, 0, (currentBatch->spriteCount * 4) * sizeof(Vector3f), currentBatch->VPos);
//bind the texture buffer and set the data from the batch
glBindBuffer(GL_ARRAY_BUFFER, m_UVBuffer);
glBufferSubData(GL_ARRAY_BUFFER, 0, (currentBatch->spriteCount * 4) * sizeof(Vector2f), currentBatch->UV);
//draw using the indicies and vert count set when the batch was created
glMultiDrawArrays(GL_QUADS, indicies, Vcount, currentBatch->spriteCount);
//unbind the texture
glBindTexture(GL_TEXTURE_2D, 0);
//delete the batch
delete it->second;
}
//unbind the buffers
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
//unlock the shader
m_shader->unlock();
//set the total sprite count to 0
totalSpriteCount = 0;
//clear the batchs
m_BatchItems.clear();
}