| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475 |
- #include "Base.h"
- #include "SpriteBatch.h"
- #include "Game.h"
- #include "Material.h"
- // Default size of a newly created sprite batch
- #define SPRITE_BATCH_DEFAULT_SIZE 128
- // Factor to grow a sprite batch by when its size is exceeded
- #define SPRITE_BATCH_GROW_FACTOR 2.0f
- // Macro for adding a sprite to the batch
- #define SPRITE_ADD_VERTEX(vtx, vx, vy, vz, vu, vv, vr, vg, vb, va) \
- vtx.x = vx; vtx.y = vy; vtx.z = vz; \
- vtx.u = vu; vtx.v = vv; \
- vtx.r = vr; vtx.g = vg; vtx.b = vb; vtx.a = va
- // Default sprite shaders
- #define SPRITE_VSH "res/shaders/sprite.vert"
- #define SPRITE_FSH "res/shaders/sprite.frag"
- namespace gameplay
- {
- static Effect* __spriteEffect = NULL;
- SpriteBatch::SpriteBatch()
- : _batch(NULL), _sampler(NULL), _textureWidthRatio(0.0f), _textureHeightRatio(0.0f)
- {
- }
- SpriteBatch::~SpriteBatch()
- {
- SAFE_DELETE(_batch);
- SAFE_RELEASE(_sampler);
- if (!_customEffect)
- {
- if (__spriteEffect && __spriteEffect->getRefCount() == 1)
- {
- __spriteEffect->release();
- __spriteEffect = NULL;
- }
- else
- {
- __spriteEffect->release();
- }
- }
- }
- SpriteBatch* SpriteBatch::create(const char* texturePath, Effect* effect, unsigned int initialCapacity)
- {
- Texture* texture = Texture::create(texturePath);
- SpriteBatch* batch = SpriteBatch::create(texture, effect, initialCapacity);
- SAFE_RELEASE(texture);
- return batch;
- }
- SpriteBatch* SpriteBatch::create(Texture* texture, Effect* effect, unsigned int initialCapacity)
- {
- GP_ASSERT(texture != NULL);
- GP_ASSERT(texture->getType() == Texture::TEXTURE_2D);
- bool customEffect = (effect != NULL);
- if (!customEffect)
- {
- // Create our static sprite effect.
- if (__spriteEffect == NULL)
- {
- __spriteEffect = Effect::createFromFile(SPRITE_VSH, SPRITE_FSH);
- if (__spriteEffect == NULL)
- {
- GP_ERROR("Unable to load sprite effect.");
- return NULL;
- }
- effect = __spriteEffect;
- }
- else
- {
- effect = __spriteEffect;
- __spriteEffect->addRef();
- }
- }
- // Search for the first sampler uniform in the effect.
- Uniform* samplerUniform = NULL;
- for (unsigned int i = 0, count = effect->getUniformCount(); i < count; ++i)
- {
- Uniform* uniform = effect->getUniform(i);
- if (uniform && uniform->getType() == GL_SAMPLER_2D)
- {
- samplerUniform = uniform;
- break;
- }
- }
- if (!samplerUniform)
- {
- GP_ERROR("No uniform of type GL_SAMPLER_2D found in sprite effect.");
- SAFE_RELEASE(effect);
- return NULL;
- }
- // Wrap the effect in a material
- Material* material = Material::create(effect);
- // Set initial material state
- material->getStateBlock()->setBlend(true);
- material->getStateBlock()->setBlendSrc(RenderState::BLEND_SRC_ALPHA);
- material->getStateBlock()->setBlendDst(RenderState::BLEND_ONE_MINUS_SRC_ALPHA);
- // Bind the texture to the material as a sampler
- Texture::Sampler* sampler = Texture::Sampler::create(texture);
- material->getParameter(samplerUniform->getName())->setValue(sampler);
-
- // Define the vertex format for the batch
- VertexFormat::Element vertexElements[] =
- {
- VertexFormat::Element(VertexFormat::POSITION, 3),
- VertexFormat::Element(VertexFormat::TEXCOORD0, 2),
- VertexFormat::Element(VertexFormat::COLOR, 4)
- };
- VertexFormat vertexFormat(vertexElements, 3);
- // Create the mesh batch
- MeshBatch* meshBatch = MeshBatch::create(vertexFormat, Mesh::TRIANGLE_STRIP, material, true, initialCapacity > 0 ? initialCapacity : SPRITE_BATCH_DEFAULT_SIZE);
- material->release(); // don't call SAFE_RELEASE since material is used below
- // Create the batch
- SpriteBatch* batch = new SpriteBatch();
- batch->_sampler = sampler;
- batch->_customEffect = customEffect;
- batch->_batch = meshBatch;
- batch->_textureWidthRatio = 1.0f / (float)texture->getWidth();
- batch->_textureHeightRatio = 1.0f / (float)texture->getHeight();
- // Bind an ortho projection to the material by default (user can override with setProjectionMatrix)
- Game* game = Game::getInstance();
- Matrix::createOrthographicOffCenter(0, game->getViewport().width, game->getViewport().height, 0, 0, 1, &batch->_projectionMatrix);
- material->getParameter("u_projectionMatrix")->bindValue(batch, &SpriteBatch::getProjectionMatrix);
-
- return batch;
- }
- void SpriteBatch::start()
- {
- _batch->start();
- }
- bool SpriteBatch::isStarted() const
- {
- return _batch->isStarted();
- }
- void SpriteBatch::draw(const Rectangle& dst, const Rectangle& src, const Vector4& color)
- {
- // Calculate uvs.
- float u1 = _textureWidthRatio * src.x;
- float v1 = 1.0f - _textureHeightRatio * src.y;
- float u2 = u1 + _textureWidthRatio * src.width;
- float v2 = v1 - _textureHeightRatio * src.height;
- draw(dst.x, dst.y, dst.width, dst.height, u1, v1, u2, v2, color);
- }
- void SpriteBatch::draw(const Vector3& dst, const Rectangle& src, const Vector2& scale, const Vector4& color)
- {
- // Calculate uvs.
- float u1 = _textureWidthRatio * src.x;
- float v1 = 1.0f - _textureHeightRatio * src.y;
- float u2 = u1 + _textureWidthRatio * src.width;
- float v2 = v1 - _textureHeightRatio * src.height;
- draw(dst.x, dst.y, dst.z, scale.x, scale.y, u1, v1, u2, v2, color);
- }
- void SpriteBatch::draw(const Vector3& dst, const Rectangle& src, const Vector2& scale, const Vector4& color,
- const Vector2& rotationPoint, float rotationAngle)
- {
- // Calculate uvs.
- float u1 = _textureWidthRatio * src.x;
- float v1 = 1.0f - _textureHeightRatio * src.y;
- float u2 = u1 + _textureWidthRatio * src.width;
- float v2 = v1 - _textureHeightRatio * src.height;
- draw(dst, scale.x, scale.y, u1, v1, u2, v2, color, rotationPoint, rotationAngle);
- }
- void SpriteBatch::draw(const Vector3& dst, float width, float height, float u1, float v1, float u2, float v2, const Vector4& color,
- const Vector2& rotationPoint, float rotationAngle, bool positionIsCenter)
- {
- draw(dst.x, dst.y, dst.z, width, height, u1, v1, u2, v2, color, rotationPoint, rotationAngle, positionIsCenter);
- }
- void SpriteBatch::draw(float x, float y, float z, float width, float height, float u1, float v1, float u2, float v2, const Vector4& color,
- const Vector2& rotationPoint, float rotationAngle, bool positionIsCenter)
- {
- // Treat the given position as the center if the user specified it as such.
- if (positionIsCenter)
- {
- x -= 0.5f * width;
- y -= 0.5f * height;
- }
- // Expand the destination position by scale into 4 points.
- float x2 = x + width;
- float y2 = y + height;
-
- Vector2 upLeft(x, y);
- Vector2 upRight(x2, y);
- Vector2 downLeft(x, y2);
- Vector2 downRight(x2, y2);
- // Rotate points around rotationAxis by rotationAngle.
- if (rotationAngle != 0)
- {
- Vector2 pivotPoint(rotationPoint);
- pivotPoint.x *= width;
- pivotPoint.y *= height;
- pivotPoint.x += x;
- pivotPoint.y += y;
- upLeft.rotate(pivotPoint, rotationAngle);
- upRight.rotate(pivotPoint, rotationAngle);
- downLeft.rotate(pivotPoint, rotationAngle);
- downRight.rotate(pivotPoint, rotationAngle);
- }
- // Write sprite vertex data.
- static SpriteVertex v[4];
- SPRITE_ADD_VERTEX(v[0], downLeft.x, downLeft.y, z, u1, v1, color.x, color.y, color.z, color.w);
- SPRITE_ADD_VERTEX(v[1], upLeft.x, upLeft.y, z, u1, v2, color.x, color.y, color.z, color.w);
- SPRITE_ADD_VERTEX(v[2], downRight.x, downRight.y, z, u2, v1, color.x, color.y, color.z, color.w);
- SPRITE_ADD_VERTEX(v[3], upRight.x, upRight.y, z, u2, v2, color.x, color.y, color.z, color.w);
-
- static unsigned short indices[4] = { 0, 1, 2, 3 };
- _batch->add(v, 4, indices, 4);
- }
- void SpriteBatch::draw(const Vector3& position, const Vector3& right, const Vector3& forward, float width, float height,
- float u1, float v1, float u2, float v2, const Vector4& color, const Vector2& rotationPoint, float rotationAngle)
- {
- // Calculate the vertex positions.
- Vector3 tRight(right);
- tRight *= width * 0.5f;
- Vector3 tForward(forward);
- tForward *= height * 0.5f;
-
- Vector3 p0 = position;
- p0 -= tRight;
- p0 -= tForward;
- Vector3 p1 = position;
- p1 += tRight;
- p1 -= tForward;
- tForward = forward;
- tForward *= height;
- Vector3 p2 = p0;
- p2 += tForward;
- Vector3 p3 = p1;
- p3 += tForward;
- // Calculate the rotation point.
- if (rotationAngle != 0)
- {
- Vector3 rp = p0;
- tRight = right;
- tRight *= width * rotationPoint.x;
- tForward *= rotationPoint.y;
- rp += tRight;
- rp += tForward;
- // Rotate all points the specified amount about the given point (about the up vector).
- static Vector3 u;
- Vector3::cross(right, forward, &u);
- static Matrix rotation;
- Matrix::createRotation(u, rotationAngle, &rotation);
- p0 -= rp;
- p0 *= rotation;
- p0 += rp;
- p1 -= rp;
- p1 *= rotation;
- p1 += rp;
- p2 -= rp;
- p2 *= rotation;
- p2 += rp;
- p3 -= rp;
- p3 *= rotation;
- p3 += rp;
- }
- // Add the sprite vertex data to the batch.
- static SpriteVertex v[4];
- SPRITE_ADD_VERTEX(v[0], p0.x, p0.y, p0.z, u1, v1, color.x, color.y, color.z, color.w);
- SPRITE_ADD_VERTEX(v[1], p1.x, p1.y, p1.z, u2, v1, color.x, color.y, color.z, color.w);
- SPRITE_ADD_VERTEX(v[2], p2.x, p2.y, p2.z, u1, v2, color.x, color.y, color.z, color.w);
- SPRITE_ADD_VERTEX(v[3], p3.x, p3.y, p3.z, u2, v2, color.x, color.y, color.z, color.w);
-
- static const unsigned short indices[4] = { 0, 1, 2, 3 };
- _batch->add(v, 4, const_cast<unsigned short*>(indices), 4);
- }
- void SpriteBatch::draw(float x, float y, float width, float height, float u1, float v1, float u2, float v2, const Vector4& color)
- {
- draw(x, y, 0, width, height, u1, v1, u2, v2, color);
- }
- void SpriteBatch::draw(float x, float y, float width, float height, float u1, float v1, float u2, float v2, const Vector4& color, const Rectangle& clip)
- {
- draw(x, y, 0, width, height, u1, v1, u2, v2, color, clip);
- }
- void SpriteBatch::draw(float x, float y, float z, float width, float height, float u1, float v1, float u2, float v2, const Vector4& color, const Rectangle& clip)
- {
- // TODO: Perform software clipping instead of culling the entire sprite.
- // Only draw if at least part of the sprite is within the clip region.
- if (clipSprite(clip, x, y, width, height, u1, v1, u2, v2))
- draw(x, y, z, width, height, u1, v1, u2, v2, color);
- }
- void SpriteBatch::addSprite(float x, float y, float width, float height, float u1, float v1, float u2, float v2, const Vector4& color, SpriteBatch::SpriteVertex* vertices)
- {
- GP_ASSERT(vertices);
- const float x2 = x + width;
- const float y2 = y + height;
- SPRITE_ADD_VERTEX(vertices[0], x, y, 0, u1, v1, color.x, color.y, color.z, color.w);
- SPRITE_ADD_VERTEX(vertices[1], x, y2, 0, u1, v2, color.x, color.y, color.z, color.w);
- SPRITE_ADD_VERTEX(vertices[2], x2, y, 0, u2, v1, color.x, color.y, color.z, color.w);
- SPRITE_ADD_VERTEX(vertices[3], x2, y2, 0, u2, v2, color.x, color.y, color.z, color.w);
- }
- void SpriteBatch::addSprite(float x, float y, float width, float height, float u1, float v1, float u2, float v2, const Vector4& color, const Rectangle& clip, SpriteBatch::SpriteVertex* vertices)
- {
- GP_ASSERT(vertices);
- // Only add a sprite if at least part of the sprite is within the clip region.
- if (clipSprite(clip, x, y, width, height, u1, v1, u2, v2))
- {
- const float x2 = x + width;
- const float y2 = y + height;
- SPRITE_ADD_VERTEX(vertices[0], x, y, 0, u1, v1, color.x, color.y, color.z, color.w);
- SPRITE_ADD_VERTEX(vertices[1], x, y2, 0, u1, v2, color.x, color.y, color.z, color.w);
- SPRITE_ADD_VERTEX(vertices[2], x2, y, 0, u2, v1, color.x, color.y, color.z, color.w);
- SPRITE_ADD_VERTEX(vertices[3], x2, y2, 0, u2, v2, color.x, color.y, color.z, color.w);
- }
- }
- void SpriteBatch::draw(SpriteBatch::SpriteVertex* vertices, unsigned int vertexCount, unsigned short* indices, unsigned int indexCount)
- {
- GP_ASSERT(vertices);
- GP_ASSERT(indices);
- _batch->add(vertices, vertexCount, indices, indexCount);
- }
- void SpriteBatch::draw(float x, float y, float z, float width, float height, float u1, float v1, float u2, float v2, const Vector4& color, bool positionIsCenter)
- {
- // Treat the given position as the center if the user specified it as such.
- if (positionIsCenter)
- {
- x -= 0.5f * width;
- y -= 0.5f * height;
- }
- // Write sprite vertex data.
- const float x2 = x + width;
- const float y2 = y + height;
- static SpriteVertex v[4];
- SPRITE_ADD_VERTEX(v[0], x, y, z, u1, v1, color.x, color.y, color.z, color.w);
- SPRITE_ADD_VERTEX(v[1], x, y2, z, u1, v2, color.x, color.y, color.z, color.w);
- SPRITE_ADD_VERTEX(v[2], x2, y, z, u2, v1, color.x, color.y, color.z, color.w);
- SPRITE_ADD_VERTEX(v[3], x2, y2, z, u2, v2, color.x, color.y, color.z, color.w);
- static unsigned short indices[4] = { 0, 1, 2, 3 };
- _batch->add(v, 4, indices, 4);
- }
- void SpriteBatch::finish()
- {
- // Finish and draw the batch
- _batch->finish();
- _batch->draw();
- }
- RenderState::StateBlock* SpriteBatch::getStateBlock() const
- {
- return _batch->getMaterial()->getStateBlock();
- }
- Texture::Sampler* SpriteBatch::getSampler() const
- {
- return _sampler;
- }
- Material* SpriteBatch::getMaterial() const
- {
- return _batch->getMaterial();
- }
- void SpriteBatch::setProjectionMatrix(const Matrix& matrix)
- {
- _projectionMatrix = matrix;
- }
- const Matrix& SpriteBatch::getProjectionMatrix() const
- {
- return _projectionMatrix;
- }
- bool SpriteBatch::clipSprite(const Rectangle& clip, float& x, float& y, float& width, float& height, float& u1, float& v1, float& u2, float& v2)
- {
- // Clip the rectangle given by { x, y, width, height } into clip.
- // We need to scale the uvs accordingly as we do this.
- // First check to see if we need to draw at all.
- if (x + width < clip.x || x > clip.x + clip.width ||
- y + height < clip.y || y > clip.y + clip.height)
- {
- return false;
- }
- float uvWidth = u2 - u1;
- float uvHeight = v2 - v1;
- // Moving x to the right.
- if (x < clip.x)
- {
- const float percent = (clip.x - x) / width;
- const float dx = clip.x - x;
- const float du = uvWidth * percent;
- x = clip.x;
- width -= dx;
- u1 += du;
- uvWidth -= du;
- }
- // Moving y down.
- if (y < clip.y)
- {
- const float percent = (clip.y - y) / height;
- const float dy = clip.y - y;
- const float dv = uvHeight * percent;
- y = clip.y;
- height -= dy;
- v1 += dv;
- uvHeight -= dv;
- }
- // Moving width to the left.
- const float clipX2 = clip.x + clip.width;
- float x2 = x + width;
- if (x2 > clipX2)
- {
- const float percent = (x2 - clipX2) / width;
- width = clipX2 - x;
- u2 -= uvWidth * percent;
- }
- // Moving height up.
- const float clipY2 = clip.y + clip.height;
- float y2 = y + height;
- if (y2 > clipY2)
- {
- const float percent = (y2 - clipY2) / height;
- height = clipY2 - y;
- v2 -= uvHeight * percent;
- }
- return true;
- }
- }
|