CmGUIWidget.cpp 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. #include "CmGUIWidget.h"
  2. #include "CmGUIManager.h"
  3. #include "CmGUISkin.h"
  4. #include "CmGUILabel.h"
  5. #include "CmDeferredRenderContext.h"
  6. #include "CmMaterial.h"
  7. #include "CmPass.h"
  8. #include "CmMesh.h"
  9. #include "CmCamera.h"
  10. #include "CmViewport.h"
  11. #include "CmSceneObject.h"
  12. namespace CamelotEngine
  13. {
  14. GUISkin GUIWidget::DefaultSkin;
  15. GUIWidget::GUIWidget(const HSceneObject& parent)
  16. :Overlay(parent), mSkin(nullptr)
  17. {
  18. GUIManager::instance().registerWidget(this);
  19. }
  20. GUIWidget::~GUIWidget()
  21. {
  22. GUIManager::instance().unregisterWidget(this);
  23. for(auto& elem : mElements)
  24. {
  25. CM_DELETE(elem, GUIElement, GUIAlloc);
  26. }
  27. mElements.clear();
  28. }
  29. GUILabel* GUIWidget::addLabel(const String& text)
  30. {
  31. return addLabel(text, 0, 0, false, THA_Left, TVA_Top);
  32. }
  33. GUILabel* GUIWidget::addLabel(const String& text, TextHorzAlign horzAlign, TextVertAlign vertAlign)
  34. {
  35. return addLabel(text, 0, 0, false, horzAlign, vertAlign);
  36. }
  37. GUILabel* GUIWidget::addLabel(const String& text, UINT32 fixedWidth, UINT32 fixedHeight, bool wordWrap)
  38. {
  39. return addLabel(text, fixedWidth, fixedHeight, wordWrap, THA_Left, TVA_Top);
  40. }
  41. GUILabel* GUIWidget::addLabel(const String& text, UINT32 fixedWidth, UINT32 fixedHeight, bool wordWrap, TextHorzAlign horzAlign, TextVertAlign vertAlign)
  42. {
  43. GUILabel* label = CM_NEW(GUILabel, GUIAlloc) GUILabel(this, text, getGUISkin(), fixedWidth, fixedHeight, wordWrap, horzAlign, vertAlign);
  44. mElements.push_back(label);
  45. return label;
  46. }
  47. void GUIWidget::setSkin(const GUISkin* skin)
  48. {
  49. mSkin = skin;
  50. }
  51. const GUISkin* GUIWidget::getGUISkin() const
  52. {
  53. if(mSkin != nullptr)
  54. return mSkin;
  55. else
  56. return &DefaultSkin;
  57. }
  58. void GUIWidget::mouseEvent(const GUIMouseEvent& ev)
  59. {
  60. }
  61. void GUIWidget::updateMeshes() const
  62. {
  63. struct TempMeshData
  64. {
  65. TempMeshData()
  66. :numQuads(0), quadOffset(0), vertices(nullptr),
  67. uvs(nullptr), indices(nullptr)
  68. { }
  69. UINT32 numQuads;
  70. UINT32 quadOffset;
  71. Vector2* vertices;
  72. Vector2* uvs;
  73. UINT32* indices;
  74. HMaterial material;
  75. std::shared_ptr<MeshData> meshData;
  76. };
  77. std::unordered_map<UINT64, TempMeshData> meshDataPerRenderElement;
  78. // Group meshes based on used materials
  79. // Determine mesh sizes per group
  80. for(auto& elem : mElements)
  81. {
  82. UINT32 numRenderElems = elem->getNumRenderElements();
  83. for(UINT32 i = 0; i < numRenderElems; i++)
  84. {
  85. const HMaterial& mat = elem->getMaterial(i);
  86. UINT64 meshGroup = mat->getInternalID(); // TODO - I group based on material ID. So if two widgets used exact copies of the same material
  87. // this system won't detect it. Find a better way of determining material similarity?
  88. UINT32 numQuads = elem->getNumQuads(i);
  89. meshDataPerRenderElement[meshGroup].numQuads += numQuads;
  90. meshDataPerRenderElement[meshGroup].material = mat;
  91. }
  92. }
  93. // Allocate buffers for each group
  94. UINT32 numMeshes = 0;
  95. for(auto& renderElem : meshDataPerRenderElement)
  96. {
  97. renderElem.second.meshData = std::shared_ptr<MeshData>(CM_NEW(MeshData, PoolAlloc) MeshData(),
  98. &MemAllocDeleter<MeshData, PoolAlloc>::deleter);
  99. renderElem.second.vertices = renderElem.second.meshData->addPositionsVec2(renderElem.second.numQuads * 4);
  100. renderElem.second.uvs = renderElem.second.meshData->addUV0(renderElem.second.numQuads * 4);
  101. renderElem.second.indices = renderElem.second.meshData->addIndices32(renderElem.second.numQuads * 6);
  102. numMeshes++;
  103. }
  104. // TODO - Sorting from scratch every time is not optimal.
  105. // If more performance is needed, try re-sorting only modified elements
  106. // Sort so that farthest away elements get drawn first (needed due to transparency)
  107. std::vector<GUIElement*> sortedElements = mElements;
  108. std::sort(sortedElements.begin(), sortedElements.end(),
  109. [](GUIElement* a, GUIElement* b)
  110. {
  111. return a->getDepth() > b->getDepth();
  112. });
  113. // Fill buffers for each group
  114. for(auto& elem : sortedElements)
  115. {
  116. UINT32 numRenderElems = elem->getNumRenderElements();
  117. for(UINT32 i = 0; i < numRenderElems; i++)
  118. {
  119. const HMaterial& mat = elem->getMaterial(i);
  120. UINT64 meshGroup = mat->getInternalID();
  121. Vector2* vertices = meshDataPerRenderElement[meshGroup].vertices;
  122. Vector2* uvs = meshDataPerRenderElement[meshGroup].uvs;
  123. UINT32* indices = meshDataPerRenderElement[meshGroup].indices;
  124. UINT32 startingQuad = meshDataPerRenderElement[meshGroup].quadOffset;
  125. UINT32 maxNumQuads = meshDataPerRenderElement[meshGroup].numQuads;
  126. elem->fillBuffer(vertices, uvs, indices, startingQuad, maxNumQuads, i);
  127. UINT32 numQuads = elem->getNumQuads(i);
  128. meshDataPerRenderElement[meshGroup].quadOffset += numQuads;
  129. }
  130. }
  131. // Update meshes
  132. for(UINT32 i = (UINT32)mCachedMeshes.size(); i < numMeshes; i++)
  133. {
  134. HMesh newMesh = Mesh::create();
  135. mCachedMeshes.push_back(newMesh);
  136. newMesh.waitUntilLoaded();
  137. }
  138. while((UINT32)mCachedMeshes.size() > numMeshes && (UINT32)mCachedMeshes.size() > 0)
  139. {
  140. mCachedMeshes.erase(mCachedMeshes.end() - 1); // TODO: Destroying meshes as soon as they're not used might be a perf. penalty?
  141. // Maybe instead pool the meshes and only actually remove them when a certain number of unused ones exists.
  142. }
  143. mCachedMaterials.resize(numMeshes);
  144. UINT32 meshIdx = 0;
  145. for(auto& renderElem : meshDataPerRenderElement)
  146. {
  147. mCachedMeshes[meshIdx]->setMeshData(renderElem.second.meshData);
  148. mCachedMaterials[meshIdx] = renderElem.second.material;
  149. meshIdx++;
  150. }
  151. updateBounds();
  152. }
  153. void GUIWidget::updateBounds() const
  154. {
  155. mCachedBounds.clear();
  156. const Matrix4& worldTfrm = SO()->getWorldTfrm();
  157. for(auto& elem : mElements)
  158. {
  159. ORect elemBounds(elem->getBounds());
  160. elemBounds.applyTransform(worldTfrm);
  161. mCachedBounds.push_back(std::make_pair(elemBounds, elem));
  162. }
  163. }
  164. void GUIWidget::render(const Camera* camera, DeferredRenderContextPtr& renderContext) const
  165. {
  166. // Mesh is re-created every frame. There might be a better approach that only recreates it upon change,
  167. // but for now it seems like too much hassle for something like GUI that is pretty dynamic anyway.
  168. updateMeshes();
  169. // Render the meshes
  170. UINT32 meshIdx = 0;
  171. for(auto& mesh : mCachedMeshes)
  172. {
  173. HMaterial material = mCachedMaterials[meshIdx];
  174. // TODO - Possible optimization. I currently divide by width/height inside the shader, while it
  175. // might be more optimal to just scale the mesh as the resolution changes?
  176. material->setFloat("halfViewportWidth", camera->getViewport()->getWidth() * 0.5f);
  177. material->setFloat("halfViewportHeight", camera->getViewport()->getHeight() * 0.5f);
  178. material->setMat4("worldTransform", SO()->getWorldTfrm());
  179. if(material == nullptr || !material.isLoaded())
  180. continue;
  181. if(mesh == nullptr || !mesh.isLoaded())
  182. continue;
  183. for(UINT32 i = 0; i < material->getNumPasses(); i++)
  184. {
  185. PassPtr pass = material->getPass(i);
  186. pass->activate(renderContext);
  187. PassParametersPtr paramsPtr = material->getPassParameters(i);
  188. pass->bindParameters(renderContext, paramsPtr);
  189. renderContext->render(mesh->getRenderOperation());
  190. }
  191. meshIdx++;
  192. }
  193. }
  194. }