BsSprite.cpp 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339
  1. //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
  2. //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
  3. #include "2D/BsTextSprite.h"
  4. #include "Math/BsVector2.h"
  5. #include "Math/BsPlane.h"
  6. #include "Mesh/BsMeshUtility.h"
  7. namespace bs
  8. {
  9. Sprite::Sprite()
  10. { }
  11. Sprite::~Sprite()
  12. { }
  13. Rect2I Sprite::getBounds(const Vector2I& offset, const Rect2I& clipRect) const
  14. {
  15. Rect2I bounds = mBounds;
  16. if(clipRect.width > 0 && clipRect.height > 0)
  17. bounds.clip(clipRect);
  18. bounds.x += offset.x;
  19. bounds.y += offset.y;
  20. return bounds;
  21. }
  22. UINT32 Sprite::getNumRenderElements() const
  23. {
  24. return (UINT32)mCachedRenderElements.size();
  25. }
  26. const SpriteMaterialInfo& Sprite::getMaterialInfo(UINT32 renderElementIdx) const
  27. {
  28. return mCachedRenderElements.at(renderElementIdx).matInfo;
  29. }
  30. SpriteMaterial* Sprite::getMaterial(UINT32 renderElementIdx) const
  31. {
  32. return mCachedRenderElements.at(renderElementIdx).material;
  33. }
  34. UINT32 Sprite::getNumQuads(UINT32 renderElementIdx) const
  35. {
  36. return mCachedRenderElements.at(renderElementIdx).numQuads;
  37. }
  38. UINT32 Sprite::fillBuffer(UINT8* vertices, UINT8* uv, UINT32* indices, UINT32 vertexOffset, UINT32 indexOffset,
  39. UINT32 maxNumVerts, UINT32 maxNumIndices, UINT32 vertexStride, UINT32 indexStride, UINT32 renderElementIdx,
  40. const Vector2I& offset, const Rect2I& clipRect, bool clip) const
  41. {
  42. const auto& renderElem = mCachedRenderElements.at(renderElementIdx);
  43. UINT32 startVert = vertexOffset;
  44. UINT32 startIndex = indexOffset;
  45. UINT32 maxVertIdx = maxNumVerts;
  46. UINT32 maxIndexIdx = maxNumIndices;
  47. UINT32 numVertices = renderElem.numQuads * 4;
  48. UINT32 numIndices = renderElem.numQuads * 6;
  49. assert((startVert + numVertices) <= maxVertIdx);
  50. assert((startIndex + numIndices) <= maxIndexIdx);
  51. UINT8* vertDst = vertices + startVert * vertexStride;
  52. UINT8* uvDst = uv + startVert * vertexStride;
  53. // TODO - I'm sure this can be done in a more cache friendly way. Profile it later.
  54. Vector2 vecOffset((float)offset.x, (float)offset.y);
  55. if(clip)
  56. {
  57. for(UINT32 i = 0; i < renderElem.numQuads; i++)
  58. {
  59. UINT8* vecStart = vertDst;
  60. UINT8* uvStart = uvDst;
  61. UINT32 vertIdx = i * 4;
  62. memcpy(vertDst, &renderElem.vertices[vertIdx + 0], sizeof(Vector2));
  63. memcpy(uvDst, &renderElem.uvs[vertIdx + 0], sizeof(Vector2));
  64. vertDst += vertexStride;
  65. uvDst += vertexStride;
  66. memcpy(vertDst, &renderElem.vertices[vertIdx + 1], sizeof(Vector2));
  67. memcpy(uvDst, &renderElem.uvs[vertIdx + 1], sizeof(Vector2));
  68. vertDst += vertexStride;
  69. uvDst += vertexStride;
  70. memcpy(vertDst, &renderElem.vertices[vertIdx + 2], sizeof(Vector2));
  71. memcpy(uvDst, &renderElem.uvs[vertIdx + 2], sizeof(Vector2));
  72. vertDst += vertexStride;
  73. uvDst += vertexStride;
  74. memcpy(vertDst, &renderElem.vertices[vertIdx + 3], sizeof(Vector2));
  75. memcpy(uvDst, &renderElem.uvs[vertIdx + 3], sizeof(Vector2));
  76. clipQuadsToRect(vecStart, uvStart, 1, vertexStride, clipRect);
  77. vertDst = vecStart;
  78. Vector2* curVec = (Vector2*)vertDst;
  79. *curVec += vecOffset;
  80. vertDst += vertexStride;
  81. curVec = (Vector2*)vertDst;
  82. *curVec += vecOffset;
  83. vertDst += vertexStride;
  84. curVec = (Vector2*)vertDst;
  85. *curVec += vecOffset;
  86. vertDst += vertexStride;
  87. curVec = (Vector2*)vertDst;
  88. *curVec += vecOffset;
  89. vertDst += vertexStride;
  90. uvDst += vertexStride;
  91. }
  92. }
  93. else
  94. {
  95. for(UINT32 i = 0; i < renderElem.numQuads; i++)
  96. {
  97. UINT8* vecStart = vertDst;
  98. UINT32 vertIdx = i * 4;
  99. memcpy(vertDst, &renderElem.vertices[vertIdx + 0], sizeof(Vector2));
  100. memcpy(uvDst, &renderElem.uvs[vertIdx + 0], sizeof(Vector2));
  101. vertDst += vertexStride;
  102. uvDst += vertexStride;
  103. memcpy(vertDst, &renderElem.vertices[vertIdx + 1], sizeof(Vector2));
  104. memcpy(uvDst, &renderElem.uvs[vertIdx + 1], sizeof(Vector2));
  105. vertDst += vertexStride;
  106. uvDst += vertexStride;
  107. memcpy(vertDst, &renderElem.vertices[vertIdx + 2], sizeof(Vector2));
  108. memcpy(uvDst, &renderElem.uvs[vertIdx + 2], sizeof(Vector2));
  109. vertDst += vertexStride;
  110. uvDst += vertexStride;
  111. memcpy(vertDst, &renderElem.vertices[vertIdx + 3], sizeof(Vector2));
  112. memcpy(uvDst, &renderElem.uvs[vertIdx + 3], sizeof(Vector2));
  113. vertDst = vecStart;
  114. Vector2* curVec = (Vector2*)vertDst;
  115. *curVec += vecOffset;
  116. vertDst += vertexStride;
  117. curVec = (Vector2*)vertDst;
  118. *curVec += vecOffset;
  119. vertDst += vertexStride;
  120. curVec = (Vector2*)vertDst;
  121. *curVec += vecOffset;
  122. vertDst += vertexStride;
  123. curVec = (Vector2*)vertDst;
  124. *curVec += vecOffset;
  125. vertDst += vertexStride;
  126. uvDst += vertexStride;
  127. }
  128. }
  129. if(indices != nullptr)
  130. memcpy(&indices[startIndex], renderElem.indexes, numIndices * sizeof(UINT32));
  131. return renderElem.numQuads;
  132. }
  133. Vector2I Sprite::getAnchorOffset(SpriteAnchor anchor, UINT32 width, UINT32 height)
  134. {
  135. switch(anchor)
  136. {
  137. case SA_TopLeft:
  138. return -Vector2I(0, 0);
  139. case SA_TopCenter:
  140. return -Vector2I(width / 2, 0);
  141. case SA_TopRight:
  142. return -Vector2I(width, 0);
  143. case SA_MiddleLeft:
  144. return -Vector2I(0, height / 2);
  145. case SA_MiddleCenter:
  146. return -Vector2I(width / 2, height / 2);
  147. case SA_MiddleRight:
  148. return -Vector2I(width, height / 2);
  149. case SA_BottomLeft:
  150. return -Vector2I(0, height);
  151. case SA_BottomCenter:
  152. return -Vector2I(width / 2, height);
  153. case SA_BottomRight:
  154. return -Vector2I(width, height);
  155. }
  156. return Vector2I();
  157. }
  158. void Sprite::updateBounds() const
  159. {
  160. Vector2 min;
  161. Vector2 max;
  162. // Find starting point
  163. bool foundStartingPoint = false;
  164. for(auto& renderElem : mCachedRenderElements)
  165. {
  166. if(renderElem.vertices != nullptr && renderElem.numQuads > 0)
  167. {
  168. min = renderElem.vertices[0];
  169. max = renderElem.vertices[0];
  170. foundStartingPoint = true;
  171. break;
  172. }
  173. }
  174. if(!foundStartingPoint)
  175. {
  176. mBounds = Rect2I(0, 0, 0, 0);
  177. return;
  178. }
  179. // Calculate bounds
  180. for(auto& renderElem : mCachedRenderElements)
  181. {
  182. if(renderElem.vertices != nullptr && renderElem.numQuads > 0)
  183. {
  184. UINT32 vertexCount = renderElem.numQuads * 4;
  185. for(UINT32 i = 0; i < vertexCount; i++)
  186. {
  187. min = Vector2::min(min, renderElem.vertices[i]);
  188. max = Vector2::max(max, renderElem.vertices[i]);
  189. }
  190. }
  191. }
  192. mBounds = Rect2I((int)min.x, (int)min.y, (int)(max.x - min.x), (int)(max.y - min.y));
  193. }
  194. // This will only properly clip an array of quads
  195. // Vertices in the quad must be in a specific order: top left, top right, bottom left, bottom right
  196. // (0, 0) represents top left of the screen
  197. void Sprite::clipQuadsToRect(UINT8* vertices, UINT8* uv, UINT32 numQuads, UINT32 vertStride, const Rect2I& clipRect)
  198. {
  199. float left = (float)clipRect.x;
  200. float right = (float)clipRect.x + clipRect.width;
  201. float top = (float)clipRect.y;
  202. float bottom = (float)clipRect.y + clipRect.height;
  203. for(UINT32 i = 0; i < numQuads; i++)
  204. {
  205. Vector2* vecA = (Vector2*)(vertices);
  206. Vector2* vecB = (Vector2*)(vertices + vertStride);
  207. Vector2* vecC = (Vector2*)(vertices + vertStride * 2);
  208. Vector2* vecD = (Vector2*)(vertices + vertStride * 3);
  209. Vector2* uvA = (Vector2*)(uv);
  210. Vector2* uvB = (Vector2*)(uv + vertStride);
  211. Vector2* uvC = (Vector2*)(uv + vertStride * 2);
  212. Vector2* uvD = (Vector2*)(uv + vertStride * 3);
  213. // Attempt to skip those that are definitely not clipped
  214. if(vecA->x >= left && vecB->x <= right &&
  215. vecA->y >= top && vecC->y <= bottom)
  216. {
  217. continue;
  218. }
  219. // TODO - Skip those that are 100% clipped as well
  220. float du = (uvB->x - uvA->x) / (vecB->x - vecA->x);
  221. float dv = (uvA->y - uvC->y) / (vecA->y - vecD->y);
  222. if(right < left)
  223. std::swap(left, right);
  224. if(bottom < top)
  225. std::swap(bottom, top);
  226. // Clip left
  227. float newLeft = Math::clamp(vecA->x, left, right);
  228. float uvLeftOffset = (newLeft - vecA->x) * du;
  229. // Clip right
  230. float newRight = Math::clamp(vecB->x, left, right);
  231. float uvRightOffset = (vecB->x - newRight) * du;
  232. // Clip top
  233. float newTop = Math::clamp(vecA->y, top, bottom);
  234. float uvTopOffset = (newTop - vecA->y) * dv;
  235. // Clip bottom
  236. float newBottom = Math::clamp(vecC->y, top, bottom);
  237. float uvBottomOffset = (vecC->y - newBottom) * dv;
  238. vecA->x = newLeft;
  239. vecC->x = newLeft;
  240. vecB->x = newRight;
  241. vecD->x = newRight;
  242. vecA->y = newTop;
  243. vecB->y = newTop;
  244. vecC->y = newBottom;
  245. vecD->y = newBottom;
  246. uvA->x += uvLeftOffset;
  247. uvC->x += uvLeftOffset;
  248. uvB->x -= uvRightOffset;
  249. uvD->x -= uvRightOffset;
  250. uvA->y += uvTopOffset;
  251. uvB->y += uvTopOffset;
  252. uvC->y -= uvBottomOffset;
  253. uvD->y -= uvBottomOffset;
  254. vertices += vertStride * 4;
  255. uv += vertStride * 4;
  256. }
  257. }
  258. void Sprite::clipTrianglesToRect(UINT8* vertices, UINT8* uv, UINT32 numTris, UINT32 vertStride, const Rect2I& clipRect,
  259. const std::function<void(Vector2*, Vector2*, UINT32)>& writeCallback)
  260. {
  261. Vector<Plane> clipPlanes =
  262. {
  263. Plane(Vector3(1.0f, 0.0f, 0.0f), (float)clipRect.x),
  264. Plane(Vector3(-1.0f, 0.0f, 0.0f), (float)-(clipRect.x + (INT32)clipRect.width)),
  265. Plane(Vector3(0.0f, 1.0f, 0.0f), (float)clipRect.y),
  266. Plane(Vector3(0.0f, -1.0f, 0.0f), (float)-(clipRect.y + (INT32)clipRect.height))
  267. };
  268. MeshUtility::clip2D(vertices, uv, numTris, vertStride, clipPlanes, writeCallback);
  269. }
  270. }