SpriteBatch.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475
  1. #include "Base.h"
  2. #include "SpriteBatch.h"
  3. #include "Game.h"
  4. #include "Material.h"
  5. // Default size of a newly created sprite batch
  6. #define SPRITE_BATCH_DEFAULT_SIZE 128
  7. // Factor to grow a sprite batch by when its size is exceeded
  8. #define SPRITE_BATCH_GROW_FACTOR 2.0f
  9. // Macro for adding a sprite to the batch
  10. #define SPRITE_ADD_VERTEX(vtx, vx, vy, vz, vu, vv, vr, vg, vb, va) \
  11. vtx.x = vx; vtx.y = vy; vtx.z = vz; \
  12. vtx.u = vu; vtx.v = vv; \
  13. vtx.r = vr; vtx.g = vg; vtx.b = vb; vtx.a = va
  14. // Default sprite shaders
  15. #define SPRITE_VSH "res/shaders/sprite.vert"
  16. #define SPRITE_FSH "res/shaders/sprite.frag"
  17. namespace gameplay
  18. {
  19. static Effect* __spriteEffect = NULL;
  20. SpriteBatch::SpriteBatch()
  21. : _batch(NULL), _sampler(NULL), _textureWidthRatio(0.0f), _textureHeightRatio(0.0f)
  22. {
  23. }
  24. SpriteBatch::~SpriteBatch()
  25. {
  26. SAFE_DELETE(_batch);
  27. SAFE_RELEASE(_sampler);
  28. if (!_customEffect)
  29. {
  30. if (__spriteEffect && __spriteEffect->getRefCount() == 1)
  31. {
  32. __spriteEffect->release();
  33. __spriteEffect = NULL;
  34. }
  35. else
  36. {
  37. __spriteEffect->release();
  38. }
  39. }
  40. }
  41. SpriteBatch* SpriteBatch::create(const char* texturePath, Effect* effect, unsigned int initialCapacity)
  42. {
  43. Texture* texture = Texture::create(texturePath);
  44. SpriteBatch* batch = SpriteBatch::create(texture, effect, initialCapacity);
  45. SAFE_RELEASE(texture);
  46. return batch;
  47. }
  48. SpriteBatch* SpriteBatch::create(Texture* texture, Effect* effect, unsigned int initialCapacity)
  49. {
  50. GP_ASSERT(texture != NULL);
  51. GP_ASSERT(texture->getType() == Texture::TEXTURE_2D);
  52. bool customEffect = (effect != NULL);
  53. if (!customEffect)
  54. {
  55. // Create our static sprite effect.
  56. if (__spriteEffect == NULL)
  57. {
  58. __spriteEffect = Effect::createFromFile(SPRITE_VSH, SPRITE_FSH);
  59. if (__spriteEffect == NULL)
  60. {
  61. GP_ERROR("Unable to load sprite effect.");
  62. return NULL;
  63. }
  64. effect = __spriteEffect;
  65. }
  66. else
  67. {
  68. effect = __spriteEffect;
  69. __spriteEffect->addRef();
  70. }
  71. }
  72. // Search for the first sampler uniform in the effect.
  73. Uniform* samplerUniform = NULL;
  74. for (unsigned int i = 0, count = effect->getUniformCount(); i < count; ++i)
  75. {
  76. Uniform* uniform = effect->getUniform(i);
  77. if (uniform && uniform->getType() == GL_SAMPLER_2D)
  78. {
  79. samplerUniform = uniform;
  80. break;
  81. }
  82. }
  83. if (!samplerUniform)
  84. {
  85. GP_ERROR("No uniform of type GL_SAMPLER_2D found in sprite effect.");
  86. SAFE_RELEASE(effect);
  87. return NULL;
  88. }
  89. // Wrap the effect in a material
  90. Material* material = Material::create(effect);
  91. // Set initial material state
  92. material->getStateBlock()->setBlend(true);
  93. material->getStateBlock()->setBlendSrc(RenderState::BLEND_SRC_ALPHA);
  94. material->getStateBlock()->setBlendDst(RenderState::BLEND_ONE_MINUS_SRC_ALPHA);
  95. // Bind the texture to the material as a sampler
  96. Texture::Sampler* sampler = Texture::Sampler::create(texture);
  97. material->getParameter(samplerUniform->getName())->setValue(sampler);
  98. // Define the vertex format for the batch
  99. VertexFormat::Element vertexElements[] =
  100. {
  101. VertexFormat::Element(VertexFormat::POSITION, 3),
  102. VertexFormat::Element(VertexFormat::TEXCOORD0, 2),
  103. VertexFormat::Element(VertexFormat::COLOR, 4)
  104. };
  105. VertexFormat vertexFormat(vertexElements, 3);
  106. // Create the mesh batch
  107. MeshBatch* meshBatch = MeshBatch::create(vertexFormat, Mesh::TRIANGLE_STRIP, material, true, initialCapacity > 0 ? initialCapacity : SPRITE_BATCH_DEFAULT_SIZE);
  108. material->release(); // don't call SAFE_RELEASE since material is used below
  109. // Create the batch
  110. SpriteBatch* batch = new SpriteBatch();
  111. batch->_sampler = sampler;
  112. batch->_customEffect = customEffect;
  113. batch->_batch = meshBatch;
  114. batch->_textureWidthRatio = 1.0f / (float)texture->getWidth();
  115. batch->_textureHeightRatio = 1.0f / (float)texture->getHeight();
  116. // Bind an ortho projection to the material by default (user can override with setProjectionMatrix)
  117. Game* game = Game::getInstance();
  118. Matrix::createOrthographicOffCenter(0, game->getViewport().width, game->getViewport().height, 0, 0, 1, &batch->_projectionMatrix);
  119. material->getParameter("u_projectionMatrix")->bindValue(batch, &SpriteBatch::getProjectionMatrix);
  120. return batch;
  121. }
  122. void SpriteBatch::start()
  123. {
  124. _batch->start();
  125. }
  126. bool SpriteBatch::isStarted() const
  127. {
  128. return _batch->isStarted();
  129. }
  130. void SpriteBatch::draw(const Rectangle& dst, const Rectangle& src, const Vector4& color)
  131. {
  132. // Calculate uvs.
  133. float u1 = _textureWidthRatio * src.x;
  134. float v1 = 1.0f - _textureHeightRatio * src.y;
  135. float u2 = u1 + _textureWidthRatio * src.width;
  136. float v2 = v1 - _textureHeightRatio * src.height;
  137. draw(dst.x, dst.y, dst.width, dst.height, u1, v1, u2, v2, color);
  138. }
  139. void SpriteBatch::draw(const Vector3& dst, const Rectangle& src, const Vector2& scale, const Vector4& color)
  140. {
  141. // Calculate uvs.
  142. float u1 = _textureWidthRatio * src.x;
  143. float v1 = 1.0f - _textureHeightRatio * src.y;
  144. float u2 = u1 + _textureWidthRatio * src.width;
  145. float v2 = v1 - _textureHeightRatio * src.height;
  146. draw(dst.x, dst.y, dst.z, scale.x, scale.y, u1, v1, u2, v2, color);
  147. }
  148. void SpriteBatch::draw(const Vector3& dst, const Rectangle& src, const Vector2& scale, const Vector4& color,
  149. const Vector2& rotationPoint, float rotationAngle)
  150. {
  151. // Calculate uvs.
  152. float u1 = _textureWidthRatio * src.x;
  153. float v1 = 1.0f - _textureHeightRatio * src.y;
  154. float u2 = u1 + _textureWidthRatio * src.width;
  155. float v2 = v1 - _textureHeightRatio * src.height;
  156. draw(dst, scale.x, scale.y, u1, v1, u2, v2, color, rotationPoint, rotationAngle);
  157. }
  158. void SpriteBatch::draw(const Vector3& dst, float width, float height, float u1, float v1, float u2, float v2, const Vector4& color,
  159. const Vector2& rotationPoint, float rotationAngle, bool positionIsCenter)
  160. {
  161. draw(dst.x, dst.y, dst.z, width, height, u1, v1, u2, v2, color, rotationPoint, rotationAngle, positionIsCenter);
  162. }
  163. void SpriteBatch::draw(float x, float y, float z, float width, float height, float u1, float v1, float u2, float v2, const Vector4& color,
  164. const Vector2& rotationPoint, float rotationAngle, bool positionIsCenter)
  165. {
  166. // Treat the given position as the center if the user specified it as such.
  167. if (positionIsCenter)
  168. {
  169. x -= 0.5f * width;
  170. y -= 0.5f * height;
  171. }
  172. // Expand the destination position by scale into 4 points.
  173. float x2 = x + width;
  174. float y2 = y + height;
  175. Vector2 upLeft(x, y);
  176. Vector2 upRight(x2, y);
  177. Vector2 downLeft(x, y2);
  178. Vector2 downRight(x2, y2);
  179. // Rotate points around rotationAxis by rotationAngle.
  180. if (rotationAngle != 0)
  181. {
  182. Vector2 pivotPoint(rotationPoint);
  183. pivotPoint.x *= width;
  184. pivotPoint.y *= height;
  185. pivotPoint.x += x;
  186. pivotPoint.y += y;
  187. upLeft.rotate(pivotPoint, rotationAngle);
  188. upRight.rotate(pivotPoint, rotationAngle);
  189. downLeft.rotate(pivotPoint, rotationAngle);
  190. downRight.rotate(pivotPoint, rotationAngle);
  191. }
  192. // Write sprite vertex data.
  193. static SpriteVertex v[4];
  194. SPRITE_ADD_VERTEX(v[0], downLeft.x, downLeft.y, z, u1, v1, color.x, color.y, color.z, color.w);
  195. SPRITE_ADD_VERTEX(v[1], upLeft.x, upLeft.y, z, u1, v2, color.x, color.y, color.z, color.w);
  196. SPRITE_ADD_VERTEX(v[2], downRight.x, downRight.y, z, u2, v1, color.x, color.y, color.z, color.w);
  197. SPRITE_ADD_VERTEX(v[3], upRight.x, upRight.y, z, u2, v2, color.x, color.y, color.z, color.w);
  198. static unsigned short indices[4] = { 0, 1, 2, 3 };
  199. _batch->add(v, 4, indices, 4);
  200. }
  201. void SpriteBatch::draw(const Vector3& position, const Vector3& right, const Vector3& forward, float width, float height,
  202. float u1, float v1, float u2, float v2, const Vector4& color, const Vector2& rotationPoint, float rotationAngle)
  203. {
  204. // Calculate the vertex positions.
  205. Vector3 tRight(right);
  206. tRight *= width * 0.5f;
  207. Vector3 tForward(forward);
  208. tForward *= height * 0.5f;
  209. Vector3 p0 = position;
  210. p0 -= tRight;
  211. p0 -= tForward;
  212. Vector3 p1 = position;
  213. p1 += tRight;
  214. p1 -= tForward;
  215. tForward = forward;
  216. tForward *= height;
  217. Vector3 p2 = p0;
  218. p2 += tForward;
  219. Vector3 p3 = p1;
  220. p3 += tForward;
  221. // Calculate the rotation point.
  222. if (rotationAngle != 0)
  223. {
  224. Vector3 rp = p0;
  225. tRight = right;
  226. tRight *= width * rotationPoint.x;
  227. tForward *= rotationPoint.y;
  228. rp += tRight;
  229. rp += tForward;
  230. // Rotate all points the specified amount about the given point (about the up vector).
  231. static Vector3 u;
  232. Vector3::cross(right, forward, &u);
  233. static Matrix rotation;
  234. Matrix::createRotation(u, rotationAngle, &rotation);
  235. p0 -= rp;
  236. p0 *= rotation;
  237. p0 += rp;
  238. p1 -= rp;
  239. p1 *= rotation;
  240. p1 += rp;
  241. p2 -= rp;
  242. p2 *= rotation;
  243. p2 += rp;
  244. p3 -= rp;
  245. p3 *= rotation;
  246. p3 += rp;
  247. }
  248. // Add the sprite vertex data to the batch.
  249. static SpriteVertex v[4];
  250. SPRITE_ADD_VERTEX(v[0], p0.x, p0.y, p0.z, u1, v1, color.x, color.y, color.z, color.w);
  251. SPRITE_ADD_VERTEX(v[1], p1.x, p1.y, p1.z, u2, v1, color.x, color.y, color.z, color.w);
  252. SPRITE_ADD_VERTEX(v[2], p2.x, p2.y, p2.z, u1, v2, color.x, color.y, color.z, color.w);
  253. SPRITE_ADD_VERTEX(v[3], p3.x, p3.y, p3.z, u2, v2, color.x, color.y, color.z, color.w);
  254. static const unsigned short indices[4] = { 0, 1, 2, 3 };
  255. _batch->add(v, 4, const_cast<unsigned short*>(indices), 4);
  256. }
  257. void SpriteBatch::draw(float x, float y, float width, float height, float u1, float v1, float u2, float v2, const Vector4& color)
  258. {
  259. draw(x, y, 0, width, height, u1, v1, u2, v2, color);
  260. }
  261. 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)
  262. {
  263. draw(x, y, 0, width, height, u1, v1, u2, v2, color, clip);
  264. }
  265. 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)
  266. {
  267. // TODO: Perform software clipping instead of culling the entire sprite.
  268. // Only draw if at least part of the sprite is within the clip region.
  269. if (clipSprite(clip, x, y, width, height, u1, v1, u2, v2))
  270. draw(x, y, z, width, height, u1, v1, u2, v2, color);
  271. }
  272. 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)
  273. {
  274. GP_ASSERT(vertices);
  275. const float x2 = x + width;
  276. const float y2 = y + height;
  277. SPRITE_ADD_VERTEX(vertices[0], x, y, 0, u1, v1, color.x, color.y, color.z, color.w);
  278. SPRITE_ADD_VERTEX(vertices[1], x, y2, 0, u1, v2, color.x, color.y, color.z, color.w);
  279. SPRITE_ADD_VERTEX(vertices[2], x2, y, 0, u2, v1, color.x, color.y, color.z, color.w);
  280. SPRITE_ADD_VERTEX(vertices[3], x2, y2, 0, u2, v2, color.x, color.y, color.z, color.w);
  281. }
  282. 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)
  283. {
  284. GP_ASSERT(vertices);
  285. // Only add a sprite if at least part of the sprite is within the clip region.
  286. if (clipSprite(clip, x, y, width, height, u1, v1, u2, v2))
  287. {
  288. const float x2 = x + width;
  289. const float y2 = y + height;
  290. SPRITE_ADD_VERTEX(vertices[0], x, y, 0, u1, v1, color.x, color.y, color.z, color.w);
  291. SPRITE_ADD_VERTEX(vertices[1], x, y2, 0, u1, v2, color.x, color.y, color.z, color.w);
  292. SPRITE_ADD_VERTEX(vertices[2], x2, y, 0, u2, v1, color.x, color.y, color.z, color.w);
  293. SPRITE_ADD_VERTEX(vertices[3], x2, y2, 0, u2, v2, color.x, color.y, color.z, color.w);
  294. }
  295. }
  296. void SpriteBatch::draw(SpriteBatch::SpriteVertex* vertices, unsigned int vertexCount, unsigned short* indices, unsigned int indexCount)
  297. {
  298. GP_ASSERT(vertices);
  299. GP_ASSERT(indices);
  300. _batch->add(vertices, vertexCount, indices, indexCount);
  301. }
  302. 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)
  303. {
  304. // Treat the given position as the center if the user specified it as such.
  305. if (positionIsCenter)
  306. {
  307. x -= 0.5f * width;
  308. y -= 0.5f * height;
  309. }
  310. // Write sprite vertex data.
  311. const float x2 = x + width;
  312. const float y2 = y + height;
  313. static SpriteVertex v[4];
  314. SPRITE_ADD_VERTEX(v[0], x, y, z, u1, v1, color.x, color.y, color.z, color.w);
  315. SPRITE_ADD_VERTEX(v[1], x, y2, z, u1, v2, color.x, color.y, color.z, color.w);
  316. SPRITE_ADD_VERTEX(v[2], x2, y, z, u2, v1, color.x, color.y, color.z, color.w);
  317. SPRITE_ADD_VERTEX(v[3], x2, y2, z, u2, v2, color.x, color.y, color.z, color.w);
  318. static unsigned short indices[4] = { 0, 1, 2, 3 };
  319. _batch->add(v, 4, indices, 4);
  320. }
  321. void SpriteBatch::finish()
  322. {
  323. // Finish and draw the batch
  324. _batch->finish();
  325. _batch->draw();
  326. }
  327. RenderState::StateBlock* SpriteBatch::getStateBlock() const
  328. {
  329. return _batch->getMaterial()->getStateBlock();
  330. }
  331. Texture::Sampler* SpriteBatch::getSampler() const
  332. {
  333. return _sampler;
  334. }
  335. Material* SpriteBatch::getMaterial() const
  336. {
  337. return _batch->getMaterial();
  338. }
  339. void SpriteBatch::setProjectionMatrix(const Matrix& matrix)
  340. {
  341. _projectionMatrix = matrix;
  342. }
  343. const Matrix& SpriteBatch::getProjectionMatrix() const
  344. {
  345. return _projectionMatrix;
  346. }
  347. bool SpriteBatch::clipSprite(const Rectangle& clip, float& x, float& y, float& width, float& height, float& u1, float& v1, float& u2, float& v2)
  348. {
  349. // Clip the rectangle given by { x, y, width, height } into clip.
  350. // We need to scale the uvs accordingly as we do this.
  351. // First check to see if we need to draw at all.
  352. if (x + width < clip.x || x > clip.x + clip.width ||
  353. y + height < clip.y || y > clip.y + clip.height)
  354. {
  355. return false;
  356. }
  357. float uvWidth = u2 - u1;
  358. float uvHeight = v2 - v1;
  359. // Moving x to the right.
  360. if (x < clip.x)
  361. {
  362. const float percent = (clip.x - x) / width;
  363. const float dx = clip.x - x;
  364. const float du = uvWidth * percent;
  365. x = clip.x;
  366. width -= dx;
  367. u1 += du;
  368. uvWidth -= du;
  369. }
  370. // Moving y down.
  371. if (y < clip.y)
  372. {
  373. const float percent = (clip.y - y) / height;
  374. const float dy = clip.y - y;
  375. const float dv = uvHeight * percent;
  376. y = clip.y;
  377. height -= dy;
  378. v1 += dv;
  379. uvHeight -= dv;
  380. }
  381. // Moving width to the left.
  382. const float clipX2 = clip.x + clip.width;
  383. float x2 = x + width;
  384. if (x2 > clipX2)
  385. {
  386. const float percent = (x2 - clipX2) / width;
  387. width = clipX2 - x;
  388. u2 -= uvWidth * percent;
  389. }
  390. // Moving height up.
  391. const float clipY2 = clip.y + clip.height;
  392. float y2 = y + height;
  393. if (y2 > clipY2)
  394. {
  395. const float percent = (y2 - clipY2) / height;
  396. height = clipY2 - y;
  397. v2 -= uvHeight * percent;
  398. }
  399. return true;
  400. }
  401. }