BsGUICanvas.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640
  1. //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
  2. //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
  3. #include "GUI/BsGUICanvas.h"
  4. #include "GUI/BsGUISkin.h"
  5. #include "2D/BsSpriteTexture.h"
  6. #include "GUI/BsGUIDimensions.h"
  7. #include "GUI/BsGUITexture.h"
  8. #include "Utility/BsShapeMeshes2D.h"
  9. #include "2D/BsSpriteManager.h"
  10. #include "2D/BsSpriteMaterials.h"
  11. #include "Mesh/BsMeshUtility.h"
  12. #include "Error/BsException.h"
  13. namespace bs
  14. {
  15. const float GUICanvas::LINE_SMOOTH_BORDER_WIDTH = 3.0f;
  16. const String& GUICanvas::getGUITypeName()
  17. {
  18. static String name = "Canvas";
  19. return name;
  20. }
  21. GUICanvas::GUICanvas(const String& styleName, const GUIDimensions& dimensions)
  22. : GUIElement(styleName, dimensions), mNumRenderElements(0), mDepthRange(1), mLastOffset(BsZero)
  23. , mForceTriangleBuild(false)
  24. {
  25. }
  26. GUICanvas::~GUICanvas()
  27. {
  28. clear();
  29. }
  30. GUICanvas* GUICanvas::create(const GUIOptions& options, const String& styleName)
  31. {
  32. return new (bs_alloc<GUICanvas>()) GUICanvas(getStyleName<GUICanvas>(styleName), GUIDimensions::create(options));
  33. }
  34. GUICanvas* GUICanvas::create(const String& styleName)
  35. {
  36. return new (bs_alloc<GUICanvas>()) GUICanvas(getStyleName<GUICanvas>(styleName), GUIDimensions::create());
  37. }
  38. void GUICanvas::drawLine(const Vector2I& a, const Vector2I& b, const Color& color, UINT8 depth)
  39. {
  40. drawPolyLine({ a, b }, color, depth);
  41. }
  42. void GUICanvas::drawPolyLine(const Vector<Vector2I>& vertices, const Color& color, UINT8 depth)
  43. {
  44. if(vertices.size() < 2)
  45. return;
  46. mElements.push_back(CanvasElement());
  47. CanvasElement& element = mElements.back();
  48. element.type = CanvasElementType::Line;
  49. element.color = color;
  50. element.dataId = (UINT32)mTriangleElementData.size();
  51. element.vertexStart = (UINT32)mVertexData.size();
  52. element.numVertices = (UINT32)vertices.size();
  53. element.depth = depth;
  54. mDepthRange = std::max(mDepthRange, (UINT8)(depth + 1));
  55. mTriangleElementData.push_back(TriangleElementData());
  56. TriangleElementData& elemData = mTriangleElementData.back();
  57. elemData.matInfo.groupId = 0;
  58. elemData.matInfo.tint = color;
  59. for (auto& vertex : vertices)
  60. {
  61. Vector2 point = Vector2((float)vertex.x, (float)vertex.y);
  62. point += Vector2(0.5f, 0.5f); // Offset to the middle of the pixel
  63. mVertexData.push_back(point);
  64. }
  65. mForceTriangleBuild = true;
  66. _markContentAsDirty();
  67. }
  68. void GUICanvas::drawTexture(const HSpriteTexture& texture, const Rect2I& area, TextureScaleMode scaleMode,
  69. const Color& color, UINT8 depth)
  70. {
  71. mElements.push_back(CanvasElement());
  72. CanvasElement& element = mElements.back();
  73. element.type = CanvasElementType::Image;
  74. element.color = color;
  75. element.dataId = (UINT32)mImageData.size();
  76. element.scaleMode = scaleMode;
  77. element.imageSprite = bs_new<ImageSprite>();
  78. element.depth = depth;
  79. mDepthRange = std::max(mDepthRange, (UINT8)(depth + 1));
  80. mImageData.push_back({ texture, area });
  81. _markContentAsDirty();
  82. }
  83. void GUICanvas::drawTriangleStrip(const Vector<Vector2I>& vertices, const Color& color, UINT8 depth)
  84. {
  85. if (vertices.size() < 3)
  86. {
  87. LOGWRN("Invalid number of vertices. Ignoring call.");
  88. return;
  89. }
  90. mElements.push_back(CanvasElement());
  91. CanvasElement& element = mElements.back();
  92. element.type = CanvasElementType::Triangle;
  93. element.color = color;
  94. element.dataId = (UINT32)mTriangleElementData.size();
  95. element.vertexStart = (UINT32)mVertexData.size();
  96. element.numVertices = (UINT32)(vertices.size() - 2) * 3;
  97. element.depth = depth;
  98. mDepthRange = std::max(mDepthRange, (UINT8)(depth + 1));
  99. // Convert strip to list
  100. for(UINT32 i = 2; i < (UINT32)vertices.size(); i++)
  101. {
  102. if (i % 2 == 0)
  103. {
  104. mVertexData.push_back(Vector2((float)vertices[i - 2].x + 0.5f, (float)vertices[i - 2].y + 0.5f));
  105. mVertexData.push_back(Vector2((float)vertices[i - 1].x + 0.5f, (float)vertices[i - 1].y + 0.5f));
  106. mVertexData.push_back(Vector2((float)vertices[i - 0].x + 0.5f, (float)vertices[i - 0].y + 0.5f));
  107. }
  108. else
  109. {
  110. mVertexData.push_back(Vector2((float)vertices[i - 0].x + 0.5f, (float)vertices[i - 0].y + 0.5f));
  111. mVertexData.push_back(Vector2((float)vertices[i - 1].x + 0.5f, (float)vertices[i - 1].y + 0.5f));
  112. mVertexData.push_back(Vector2((float)vertices[i - 2].x + 0.5f, (float)vertices[i - 2].y + 0.5f));
  113. }
  114. }
  115. mTriangleElementData.push_back(TriangleElementData());
  116. TriangleElementData& elemData = mTriangleElementData.back();
  117. elemData.matInfo.groupId = 0;
  118. elemData.matInfo.tint = color;
  119. mForceTriangleBuild = true;
  120. _markContentAsDirty();
  121. }
  122. void GUICanvas::drawTriangleList(const Vector<Vector2I>& vertices, const Color& color, UINT8 depth)
  123. {
  124. if (vertices.size() < 3 || vertices.size() % 3 != 0)
  125. {
  126. LOGWRN("Invalid number of vertices. Ignoring call.");
  127. return;
  128. }
  129. mElements.push_back(CanvasElement());
  130. CanvasElement& element = mElements.back();
  131. element.type = CanvasElementType::Triangle;
  132. element.color = color;
  133. element.dataId = (UINT32)mTriangleElementData.size();
  134. element.vertexStart = (UINT32)mVertexData.size();
  135. element.numVertices = (UINT32)vertices.size();
  136. element.depth = depth;
  137. mDepthRange = std::max(mDepthRange, (UINT8)(depth + 1));
  138. for (auto& vertex : vertices)
  139. mVertexData.push_back(Vector2((float)vertex.x + 0.5f, (float)vertex.y + 0.5f));
  140. mTriangleElementData.push_back(TriangleElementData());
  141. TriangleElementData& elemData = mTriangleElementData.back();
  142. elemData.matInfo.groupId = 0;
  143. elemData.matInfo.tint = color;
  144. mForceTriangleBuild = true;
  145. _markContentAsDirty();
  146. }
  147. void GUICanvas::drawText(const WString& text, const Vector2I& position, const HFont& font, UINT32 size,
  148. const Color& color, UINT8 depth)
  149. {
  150. mElements.push_back(CanvasElement());
  151. CanvasElement& element = mElements.back();
  152. element.type = CanvasElementType::Text;
  153. element.color = color;
  154. element.dataId = (UINT32)mTextData.size();
  155. element.size = size;
  156. element.textSprite = bs_new<TextSprite>();
  157. element.depth = depth;
  158. mDepthRange = std::max(mDepthRange, (UINT8)(depth + 1));
  159. mTextData.push_back({ text, font, position });
  160. _markContentAsDirty();
  161. }
  162. void GUICanvas::clear()
  163. {
  164. for (auto& element : mElements)
  165. {
  166. if(element.type == CanvasElementType::Image && element.imageSprite != nullptr)
  167. bs_delete(element.imageSprite);
  168. if (element.type == CanvasElementType::Text && element.textSprite != nullptr)
  169. bs_delete(element.textSprite);
  170. }
  171. mElements.clear();
  172. mNumRenderElements = 0;
  173. mDepthRange = 1;
  174. mVertexData.clear();
  175. mImageData.clear();
  176. mTextData.clear();
  177. mTriangleElementData.clear();
  178. mClippedVertices.clear();
  179. mClippedLineVertices.clear();
  180. mForceTriangleBuild = false;
  181. }
  182. UINT32 GUICanvas::_getNumRenderElements() const
  183. {
  184. return mNumRenderElements;
  185. }
  186. UINT32 GUICanvas::_getRenderElementDepth(UINT32 renderElementIdx) const
  187. {
  188. const CanvasElement& element = findElement(renderElementIdx);
  189. return _getDepth() + element.depth;
  190. }
  191. const SpriteMaterialInfo& GUICanvas::_getMaterial(UINT32 renderElementIdx, SpriteMaterial** material) const
  192. {
  193. static const SpriteMaterialInfo defaultMatInfo;
  194. Vector2 offset((float)mLayoutData.area.x, (float)mLayoutData.area.y);
  195. Rect2I clipRect = mLayoutData.getLocalClipRect();
  196. buildAllTriangleElementsIfDirty(offset, clipRect);
  197. const CanvasElement& element = findElement(renderElementIdx);
  198. renderElementIdx -= element.renderElemStart;
  199. switch (element.type)
  200. {
  201. case CanvasElementType::Line:
  202. *material = SpriteManager::instance().getLineMaterial();
  203. return mTriangleElementData[element.dataId].matInfo;
  204. case CanvasElementType::Image:
  205. *material = element.imageSprite->getMaterial(0);
  206. return element.imageSprite->getMaterialInfo(0);
  207. case CanvasElementType::Text:
  208. *material = element.imageSprite->getMaterial(renderElementIdx);
  209. return element.textSprite->getMaterialInfo(renderElementIdx);
  210. case CanvasElementType::Triangle:
  211. *material = SpriteManager::instance().getImageTransparentMaterial();
  212. return mTriangleElementData[element.dataId].matInfo;
  213. default:
  214. *material = nullptr;
  215. return defaultMatInfo;
  216. }
  217. }
  218. void GUICanvas::_getMeshInfo(UINT32 renderElementIdx, UINT32& numVertices, UINT32& numIndices, GUIMeshType& type) const
  219. {
  220. Vector2 offset((float)mLayoutData.area.x, (float)mLayoutData.area.y);
  221. Rect2I clipRect = mLayoutData.getLocalClipRect();
  222. buildAllTriangleElementsIfDirty(offset, clipRect);
  223. const CanvasElement& element = findElement(renderElementIdx);
  224. renderElementIdx -= element.renderElemStart;
  225. switch (element.type)
  226. {
  227. case CanvasElementType::Image:
  228. {
  229. UINT32 numQuads = element.imageSprite->getNumQuads(renderElementIdx);
  230. numVertices = numQuads * 4;
  231. numIndices = numQuads * 6;
  232. type = GUIMeshType::Triangle;
  233. break;
  234. }
  235. case CanvasElementType::Text:
  236. {
  237. UINT32 numQuads = element.textSprite->getNumQuads(renderElementIdx);
  238. numVertices = numQuads * 4;
  239. numIndices = numQuads * 6;
  240. type = GUIMeshType::Triangle;
  241. break;
  242. }
  243. case CanvasElementType::Line:
  244. numVertices = element.clippedNumVertices;
  245. numIndices = element.clippedNumVertices;
  246. type = GUIMeshType::Line;
  247. break;
  248. case CanvasElementType::Triangle:
  249. numVertices = element.clippedNumVertices;
  250. numIndices = element.clippedNumVertices;
  251. type = GUIMeshType::Triangle;
  252. break;
  253. default:
  254. numVertices = 0;
  255. numIndices = 0;
  256. type = GUIMeshType::Triangle;
  257. break;
  258. }
  259. }
  260. void GUICanvas::updateRenderElementsInternal()
  261. {
  262. mNumRenderElements = 0;
  263. for(auto& element : mElements)
  264. {
  265. switch(element.type)
  266. {
  267. case CanvasElementType::Image:
  268. buildImageElement(element);
  269. element.renderElemStart = mNumRenderElements;
  270. element.renderElemEnd = element.renderElemStart + element.imageSprite->getNumRenderElements();
  271. break;
  272. case CanvasElementType::Text:
  273. buildTextElement(element);
  274. element.renderElemStart = mNumRenderElements;
  275. element.renderElemEnd = element.renderElemStart + element.textSprite->getNumRenderElements();
  276. break;
  277. case CanvasElementType::Line:
  278. case CanvasElementType::Triangle:
  279. element.renderElemStart = mNumRenderElements;
  280. element.renderElemEnd = element.renderElemStart + 1;
  281. mTriangleElementData[element.dataId].matInfo.groupId = (UINT64)_getParentWidget();
  282. // Actual mesh build happens when reading from it, because the mesh size varies due to clipping rectangle/offset
  283. break;
  284. }
  285. mNumRenderElements = element.renderElemEnd;
  286. }
  287. GUIElement::updateRenderElementsInternal();
  288. }
  289. Vector2I GUICanvas::_getOptimalSize() const
  290. {
  291. return Vector2I(10, 10);
  292. }
  293. void GUICanvas::_fillBuffer(UINT8* vertices, UINT32* indices, UINT32 vertexOffset, UINT32 indexOffset,
  294. UINT32 maxNumVerts, UINT32 maxNumIndices, UINT32 renderElementIdx) const
  295. {
  296. UINT8* uvs = vertices + sizeof(Vector2);
  297. UINT32 indexStride = sizeof(UINT32);
  298. Vector2I offset(mLayoutData.area.x, mLayoutData.area.y);
  299. Rect2I clipRect = mLayoutData.getLocalClipRect();
  300. Vector2 floatOffset((float)offset.x, (float)offset.y);
  301. buildAllTriangleElementsIfDirty(floatOffset, clipRect);
  302. const CanvasElement& element = findElement(renderElementIdx);
  303. renderElementIdx -= element.renderElemStart;
  304. switch(element.type)
  305. {
  306. case CanvasElementType::Image:
  307. {
  308. UINT32 vertexStride = sizeof(Vector2) * 2;
  309. const Rect2I& area = mImageData[element.dataId].area;
  310. offset.x += area.x;
  311. offset.y += area.y;
  312. clipRect.x -= area.x;
  313. clipRect.y -= area.y;
  314. element.imageSprite->fillBuffer(vertices, uvs, indices, vertexOffset, indexOffset, maxNumVerts, maxNumIndices,
  315. vertexStride, indexStride, renderElementIdx, offset, clipRect);
  316. }
  317. break;
  318. case CanvasElementType::Text:
  319. {
  320. UINT32 vertexStride = sizeof(Vector2) * 2;
  321. const Vector2I& position = mTextData[element.dataId].position;
  322. offset += position;
  323. clipRect.x -= position.x;
  324. clipRect.y -= position.y;
  325. element.textSprite->fillBuffer(vertices, uvs, indices, vertexOffset, indexOffset, maxNumVerts, maxNumIndices,
  326. vertexStride, indexStride, renderElementIdx, offset, clipRect);
  327. }
  328. break;
  329. case CanvasElementType::Triangle:
  330. {
  331. UINT32 vertexStride = sizeof(Vector2) * 2;
  332. UINT32 startVert = vertexOffset;
  333. UINT32 startIndex = indexOffset;
  334. UINT32 maxVertIdx = maxNumVerts;
  335. UINT32 maxIndexIdx = maxNumIndices;
  336. UINT32 numVertices = element.clippedNumVertices;
  337. UINT32 numIndices = numVertices;
  338. assert((startVert + numVertices) <= maxVertIdx);
  339. assert((startIndex + numIndices) <= maxIndexIdx);
  340. UINT8* vertDst = vertices + startVert * vertexStride;
  341. UINT8* uvDst = uvs + startVert * vertexStride;
  342. UINT32* indexDst = indices + startIndex;
  343. Vector2 zeroUV(BsZero);
  344. for(UINT32 i = 0; i < element.clippedNumVertices; i++)
  345. {
  346. memcpy(vertDst, &mClippedVertices[element.clippedVertexStart + i], sizeof(Vector2));
  347. memcpy(uvDst, &zeroUV, sizeof(Vector2));
  348. vertDst += vertexStride;
  349. uvDst += vertexStride;
  350. indexDst[i] = i;
  351. }
  352. }
  353. break;
  354. case CanvasElementType::Line:
  355. {
  356. UINT32 vertexStride = sizeof(Vector2);
  357. UINT32 startVert = vertexOffset;
  358. UINT32 startIndex = indexOffset;
  359. UINT32 maxVertIdx = maxNumVerts;
  360. UINT32 maxIndexIdx = maxNumIndices;
  361. UINT32 numVertices = element.clippedNumVertices;
  362. UINT32 numIndices = numVertices;
  363. assert((startVert + numVertices) <= maxVertIdx);
  364. assert((startIndex + numIndices) <= maxIndexIdx);
  365. UINT8* vertDst = vertices + startVert * vertexStride;
  366. UINT32* indexDst = indices + startIndex;
  367. for (UINT32 i = 0; i < element.clippedNumVertices; i++)
  368. {
  369. const Vector2& point = mClippedLineVertices[element.clippedVertexStart + i];
  370. memcpy(vertDst, &point, sizeof(Vector2));
  371. vertDst += vertexStride;
  372. indexDst[i] = i;
  373. }
  374. }
  375. break;
  376. }
  377. }
  378. void GUICanvas::buildImageElement(const CanvasElement& element)
  379. {
  380. assert(element.type == CanvasElementType::Image);
  381. const ImageElementData& imageData = mImageData[element.dataId];
  382. IMAGE_SPRITE_DESC desc;
  383. desc.width = imageData.area.width;
  384. desc.height = imageData.area.height;
  385. desc.transparent = true;
  386. desc.color = element.color;
  387. Vector2I textureSize;
  388. if (SpriteTexture::checkIsLoaded(imageData.texture))
  389. {
  390. desc.texture = imageData.texture.getInternalPtr();
  391. textureSize.x = desc.texture->getWidth();
  392. textureSize.y = desc.texture->getHeight();
  393. }
  394. Vector2I destSize(mLayoutData.area.width, mLayoutData.area.height);
  395. desc.uvScale = ImageSprite::getTextureUVScale(textureSize, destSize, element.scaleMode);
  396. element.imageSprite->update(desc, (UINT64)_getParentWidget());
  397. }
  398. void GUICanvas::buildTextElement(const CanvasElement& element)
  399. {
  400. assert(element.type == CanvasElementType::Text);
  401. const TextElementData& textData = mTextData[element.dataId];
  402. TEXT_SPRITE_DESC desc;
  403. desc.font = textData.font;
  404. desc.fontSize = element.size;
  405. desc.text = textData.string;
  406. desc.color = element.color;
  407. element.textSprite->update(desc, (UINT64)_getParentWidget());
  408. }
  409. void GUICanvas::buildTriangleElement(const CanvasElement& element, const Vector2& offset, const Rect2I& clipRect) const
  410. {
  411. assert(element.type == CanvasElementType::Triangle || element.type == CanvasElementType::Line);
  412. if (element.type == CanvasElementType::Triangle)
  413. {
  414. UINT8* verticesToClip = (UINT8*)&mVertexData[element.vertexStart];
  415. UINT32 trianglesToClip = element.numVertices / 3;
  416. auto writeCallback = [&](Vector2* vertices, Vector2* uvs, UINT32 count)
  417. {
  418. for (UINT32 i = 0; i < count; i++)
  419. mClippedVertices.push_back(vertices[i] + offset);
  420. element.clippedNumVertices += count;
  421. };
  422. element.clippedVertexStart = (UINT32)mClippedVertices.size();
  423. element.clippedNumVertices = 0;
  424. ImageSprite::clipTrianglesToRect(verticesToClip, nullptr, trianglesToClip, sizeof(Vector2), clipRect,
  425. writeCallback);
  426. }
  427. else
  428. {
  429. UINT32 numLines = element.numVertices - 1;
  430. const Vector2* linePoints = &mVertexData[element.vertexStart];
  431. struct Plane2D
  432. {
  433. Plane2D(const Vector2& normal, float d)
  434. :normal(normal), d(d)
  435. { }
  436. Vector2 normal;
  437. float d;
  438. };
  439. std::array<Plane2D, 4> clipPlanes =
  440. {
  441. Plane2D(Vector2(1.0f, 0.0f), (float)clipRect.x),
  442. Plane2D(Vector2(-1.0f, 0.0f), (float)-(clipRect.x + (INT32)clipRect.width)),
  443. Plane2D(Vector2(0.0f, 1.0f), (float)clipRect.y),
  444. Plane2D(Vector2(0.0f, -1.0f), (float)-(clipRect.y + (INT32)clipRect.height))
  445. };
  446. element.clippedVertexStart = (UINT32)mClippedLineVertices.size();
  447. element.clippedNumVertices = 0;
  448. for (UINT32 i = 0; i < numLines; i++)
  449. {
  450. Vector2 a = linePoints[i];
  451. Vector2 b = linePoints[i + 1];
  452. bool isVisible = true;
  453. for(UINT32 j = 0; j < (UINT32)clipPlanes.size(); j++)
  454. {
  455. const Plane2D& plane = clipPlanes[j];
  456. float d0 = plane.normal.dot(a) - plane.d;
  457. float d1 = plane.normal.dot(b) - plane.d;
  458. // Line not visible
  459. if (d0 <= 0 && d1 <= 0)
  460. {
  461. isVisible = false;
  462. break;
  463. }
  464. // Line visible completely
  465. if (d0 >= 0 && d1 >= 0)
  466. continue;
  467. // The line is split by the plane, compute the point of intersection.
  468. float t = d0 / (d0 - d1);
  469. Vector2 intersectPt = (1 - t)*a + t*b;
  470. if (d0 > 0)
  471. b = intersectPt;
  472. else
  473. a = intersectPt;
  474. }
  475. if (!isVisible)
  476. continue;
  477. mClippedLineVertices.push_back(a + offset);
  478. mClippedLineVertices.push_back(b + offset);
  479. element.clippedNumVertices += 2;
  480. }
  481. }
  482. }
  483. void GUICanvas::buildAllTriangleElementsIfDirty(const Vector2& offset, const Rect2I& clipRect) const
  484. {
  485. // We need to rebuild if new triangle element(s) were added, or if offset or clip rectangle changed
  486. bool isDirty = mForceTriangleBuild || (mLastOffset != offset) || (mLastClipRect != clipRect);
  487. if (!isDirty)
  488. return;
  489. mClippedVertices.clear();
  490. mClippedLineVertices.clear();
  491. for(auto& element : mElements)
  492. {
  493. if (element.type != CanvasElementType::Triangle && element.type != CanvasElementType::Line)
  494. continue;
  495. buildTriangleElement(element, offset, clipRect);
  496. }
  497. mLastOffset = offset;
  498. mLastClipRect = clipRect;
  499. mForceTriangleBuild = false;
  500. }
  501. const GUICanvas::CanvasElement& GUICanvas::findElement(UINT32 renderElementIdx) const
  502. {
  503. INT32 start = 0;
  504. INT32 end = (INT32)(mElements.size() - 1);
  505. while (start <= end)
  506. {
  507. INT32 middle = (start + end) / 2;
  508. const CanvasElement& current = mElements[middle];
  509. if (renderElementIdx >= current.renderElemStart && renderElementIdx < current.renderElemEnd)
  510. return current;
  511. if (renderElementIdx < current.renderElemStart)
  512. end = middle - 1;
  513. else
  514. start = middle + 1;
  515. }
  516. BS_EXCEPT(InvalidParametersException, "Cannot find requested GUI render element.");
  517. static CanvasElement dummyElement;
  518. return dummyElement;
  519. }
  520. }