CmGUIWidget.cpp 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  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. namespace CamelotEngine
  12. {
  13. GUISkin GUIWidget::DefaultSkin;
  14. GUIWidget::GUIWidget(const HSceneObject& parent)
  15. :Overlay(parent), mSkin(nullptr)
  16. {
  17. GUIManager::instance().registerWidget(this);
  18. }
  19. GUIWidget::~GUIWidget()
  20. {
  21. GUIManager::instance().unregisterWidget(this);
  22. for(auto& elem : mElements)
  23. {
  24. CM_DELETE(elem, GUIElement, GUIAlloc);
  25. }
  26. mElements.clear();
  27. }
  28. GUILabel* GUIWidget::addLabel(const String& text, UINT32 fixedWidth, UINT32 fixedHeight, bool wordWrap, TextHorzAlign horzAlign, TextVertAlign vertAlign)
  29. {
  30. GUILabel* label = CM_NEW(GUILabel, GUIAlloc) GUILabel(this, text, getGUISkin(), fixedWidth, fixedHeight, wordWrap, horzAlign, vertAlign);
  31. mElements.push_back(label);
  32. return label;
  33. }
  34. GUILabel* GUIWidget::addLabel(const String& text, TextHorzAlign horzAlign, TextVertAlign vertAlign)
  35. {
  36. return addLabel(text, 0, 0, false, horzAlign, vertAlign);
  37. }
  38. void GUIWidget::setSkin(const GUISkin* skin)
  39. {
  40. mSkin = skin;
  41. }
  42. const GUISkin* GUIWidget::getGUISkin() const
  43. {
  44. if(mSkin != nullptr)
  45. return mSkin;
  46. else
  47. return &DefaultSkin;
  48. }
  49. void GUIWidget::updateMeshes() const
  50. {
  51. struct TempMeshData
  52. {
  53. TempMeshData()
  54. :numQuads(0), quadOffset(0), vertices(nullptr),
  55. uvs(nullptr), indices(nullptr)
  56. { }
  57. UINT32 numQuads;
  58. UINT32 quadOffset;
  59. Vector2* vertices;
  60. Vector2* uvs;
  61. UINT32* indices;
  62. HMaterial material;
  63. std::shared_ptr<MeshData> meshData;
  64. };
  65. std::unordered_map<UINT64, TempMeshData> meshDataPerRenderElement;
  66. // Group meshes based on used materials
  67. // Determine mesh sizes per group
  68. for(auto& elem : mElements)
  69. {
  70. UINT32 numRenderElems = elem->getNumRenderElements();
  71. for(UINT32 i = 0; i < numRenderElems; i++)
  72. {
  73. const HMaterial& mat = elem->getMaterial(i);
  74. UINT64 meshGroup = mat->getInternalID(); // TODO - I group based on material ID. So if two widgets used exact copies of the same material
  75. // this system won't detect it. Find a better way of determining material similarity?
  76. UINT32 numQuads = elem->getNumQuads(i);
  77. meshDataPerRenderElement[meshGroup].numQuads += numQuads;
  78. meshDataPerRenderElement[meshGroup].material = mat;
  79. }
  80. }
  81. // Allocate buffers for each group
  82. UINT32 numMeshes = 0;
  83. for(auto& renderElem : meshDataPerRenderElement)
  84. {
  85. renderElem.second.meshData = std::shared_ptr<MeshData>(CM_NEW(MeshData, PoolAlloc) MeshData(),
  86. &MemAllocDeleter<MeshData, PoolAlloc>::deleter);
  87. renderElem.second.vertices = renderElem.second.meshData->addPositionsVec2(renderElem.second.numQuads * 4);
  88. renderElem.second.uvs = renderElem.second.meshData->addUV0(renderElem.second.numQuads * 4);
  89. renderElem.second.indices = renderElem.second.meshData->addIndices32(renderElem.second.numQuads * 6);
  90. numMeshes++;
  91. }
  92. // Fill buffers for each group
  93. for(auto& elem : mElements)
  94. {
  95. UINT32 numRenderElems = elem->getNumRenderElements();
  96. for(UINT32 i = 0; i < numRenderElems; i++)
  97. {
  98. const HMaterial& mat = elem->getMaterial(i);
  99. UINT64 meshGroup = mat->getInternalID();
  100. Vector2* vertices = meshDataPerRenderElement[meshGroup].vertices;
  101. Vector2* uvs = meshDataPerRenderElement[meshGroup].uvs;
  102. UINT32* indices = meshDataPerRenderElement[meshGroup].indices;
  103. UINT32 startingQuad = meshDataPerRenderElement[meshGroup].quadOffset;
  104. UINT32 maxNumQuads = meshDataPerRenderElement[meshGroup].numQuads;
  105. elem->fillBuffer(vertices, uvs, indices, startingQuad, maxNumQuads, i);
  106. UINT32 numQuads = elem->getNumQuads(i);
  107. meshDataPerRenderElement[meshGroup].quadOffset += numQuads;
  108. }
  109. }
  110. // Update meshes
  111. for(UINT32 i = (UINT32)mCachedMeshes.size(); i < numMeshes; i++)
  112. {
  113. HMesh newMesh = Mesh::create();
  114. mCachedMeshes.push_back(newMesh);
  115. newMesh.waitUntilLoaded();
  116. }
  117. while((UINT32)mCachedMeshes.size() > numMeshes && (UINT32)mCachedMeshes.size() > 0)
  118. {
  119. mCachedMeshes.erase(mCachedMeshes.end() - 1); // TODO: Destroying meshes as soon as they're not used might be a perf. penalty?
  120. // Maybe instead pool the meshes and only actually remove them when a certain number of unused ones exists.
  121. }
  122. mCachedMaterials.resize(numMeshes);
  123. UINT32 meshIdx = 0;
  124. for(auto& renderElem : meshDataPerRenderElement)
  125. {
  126. mCachedMeshes[meshIdx]->setMeshData(renderElem.second.meshData);
  127. mCachedMaterials[meshIdx] = renderElem.second.material;
  128. meshIdx++;
  129. }
  130. }
  131. void GUIWidget::render(const Camera* camera, DeferredRenderContextPtr& renderContext) const
  132. {
  133. // Mesh is re-created every frame. There might be a better approach that only recreates it upon change,
  134. // but for now it seems like too much hassle for something like GUI that is pretty dynamic anyway.
  135. updateMeshes();
  136. // Render the meshes
  137. UINT32 meshIdx = 0;
  138. for(auto& mesh : mCachedMeshes)
  139. {
  140. HMaterial material = mCachedMaterials[meshIdx];
  141. // TODO - Possible optimization. I currently divide by width/height inside the shader, while it
  142. // might be more optimal to just scale the mesh as the resolution changes?
  143. material->setFloat("halfViewportWidth", camera->getViewport()->getWidth() * 0.5f);
  144. material->setFloat("halfViewportHeight", camera->getViewport()->getHeight() * 0.5f);
  145. if(material == nullptr || !material.isLoaded())
  146. continue;
  147. if(mesh == nullptr || !mesh.isLoaded())
  148. continue;
  149. // TODO - Set current viewport resolution so that GUI can be rendered properly
  150. for(UINT32 i = 0; i < material->getNumPasses(); i++)
  151. {
  152. PassPtr pass = material->getPass(i);
  153. pass->activate(renderContext);
  154. PassParametersPtr paramsPtr = material->getPassParameters(i);
  155. pass->bindParameters(renderContext, paramsPtr);
  156. renderContext->render(mesh->getRenderOperation());
  157. }
  158. meshIdx++;
  159. }
  160. }
  161. }