BsGUIWidget.cpp 6.5 KB

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