BsGUIManager.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528
  1. #include "BsGUIManager.h"
  2. #include "BsGUIWidget.h"
  3. #include "BsGUIElement.h"
  4. #include "BsGUIMouseEvent.h"
  5. #include "CmSceneObject.h"
  6. #include "CmMaterial.h"
  7. #include "CmMeshData.h"
  8. #include "CmMesh.h"
  9. #include "CmUtil.h"
  10. #include "CmRenderWindowManager.h"
  11. #include "CmCursor.h"
  12. #include "CmRect.h"
  13. #include "CmApplication.h"
  14. #include "CmException.h"
  15. #include "CmInput.h"
  16. #include "CmPass.h"
  17. using namespace CamelotFramework;
  18. namespace BansheeEngine
  19. {
  20. struct GUIGroupElement
  21. {
  22. GUIGroupElement()
  23. { }
  24. GUIGroupElement(GUIElement* _element, UINT32 _renderElement)
  25. :element(_element), renderElement(_renderElement)
  26. { }
  27. GUIElement* element;
  28. UINT32 renderElement;
  29. };
  30. struct GUIMaterialGroup
  31. {
  32. HMaterial material;
  33. UINT32 numQuads;
  34. UINT32 depth;
  35. Rect bounds;
  36. vector<GUIGroupElement>::type elements;
  37. };
  38. GUIManager::GUIManager()
  39. :mMouseOverElement(nullptr), mMouseOverWidget(nullptr), mSeparateMeshesByWidget(true)
  40. {
  41. for(int i = 0; i < MB_Count; i++)
  42. mLastFrameButtonState[i] = false;
  43. }
  44. GUIManager::~GUIManager()
  45. {
  46. }
  47. void GUIManager::registerWidget(GUIWidget* widget)
  48. {
  49. mWidgets.push_back(widget);
  50. const Viewport* renderTarget = widget->getTarget();
  51. auto findIter = mCachedGUIData.find(renderTarget);
  52. if(findIter == end(mCachedGUIData))
  53. mCachedGUIData[renderTarget] = GUIRenderData();
  54. GUIRenderData& windowData = mCachedGUIData[renderTarget];
  55. windowData.widgets.push_back(widget);
  56. windowData.isDirty = true;
  57. }
  58. void GUIManager::unregisterWidget(GUIWidget* widget)
  59. {
  60. auto findIter = std::find(begin(mWidgets), end(mWidgets), widget);
  61. if(findIter != end(mWidgets))
  62. mWidgets.erase(findIter);
  63. if(mMouseOverWidget == widget)
  64. {
  65. mMouseOverWidget = nullptr;
  66. mMouseOverElement = nullptr;
  67. }
  68. const Viewport* renderTarget = widget->getTarget();
  69. GUIRenderData& renderData = mCachedGUIData[renderTarget];
  70. findIter = std::find(begin(renderData.widgets), end(renderData.widgets), widget);
  71. if(findIter != end(renderData.widgets))
  72. renderData.widgets.erase(findIter);
  73. if(renderData.widgets.size() == 0)
  74. mCachedGUIData.erase(renderTarget);
  75. else
  76. renderData.isDirty = true;
  77. }
  78. void GUIManager::update()
  79. {
  80. // Update layouts
  81. for(auto& widget : mWidgets)
  82. {
  83. widget->_updateLayout();
  84. }
  85. updateMeshes();
  86. updateInput();
  87. }
  88. void GUIManager::render(ViewportPtr& target, RenderContext& renderContext)
  89. {
  90. auto findIter = mCachedGUIData.find(target.get());
  91. if(findIter == mCachedGUIData.end())
  92. return;
  93. renderContext.setViewport(target);
  94. GUIRenderData& renderData = findIter->second;
  95. // Render the meshes
  96. if(mSeparateMeshesByWidget)
  97. {
  98. UINT32 meshIdx = 0;
  99. for(auto& mesh : renderData.cachedMeshes)
  100. {
  101. HMaterial material = renderData.cachedMaterials[meshIdx];
  102. GUIWidget* widget = renderData.cachedWidgetsPerMesh[meshIdx];
  103. // TODO - Possible optimization. I currently divide by width/height inside the shader, while it
  104. // might be more optimal to just scale the mesh as the resolution changes?
  105. float invViewportWidth = 1.0f / (target->getWidth() * 0.5f);
  106. float invViewportHeight = 1.0f / (target->getHeight() * 0.5f);
  107. material->setFloat("invViewportWidth", invViewportWidth);
  108. material->setFloat("invViewportHeight", invViewportHeight);
  109. material->setMat4("worldTransform", widget->SO()->getWorldTfrm());
  110. if(material == nullptr || !material.isLoaded())
  111. continue;
  112. if(mesh == nullptr || !mesh.isLoaded())
  113. continue;
  114. for(UINT32 i = 0; i < material->getNumPasses(); i++)
  115. {
  116. PassPtr pass = material->getPass(i);
  117. pass->activate(renderContext);
  118. PassParametersPtr paramsPtr = material->getPassParameters(i);
  119. pass->bindParameters(renderContext, paramsPtr);
  120. renderContext.render(mesh->getRenderOperation());
  121. }
  122. meshIdx++;
  123. }
  124. }
  125. else
  126. {
  127. // TODO: I want to avoid separating meshes by widget in the future. On DX11 and GL I can set up a shader
  128. // that accepts multiple world transforms (one for each widget). Then I can add some instance information to vertices
  129. // and render elements using multiple different transforms with a single call.
  130. // Separating meshes can then be used as a compatibility mode for DX9
  131. CM_EXCEPT(NotImplementedException, "Not implemented");
  132. }
  133. }
  134. void GUIManager::updateMeshes()
  135. {
  136. for(auto& cachedMeshData : mCachedGUIData)
  137. {
  138. GUIRenderData& renderData = cachedMeshData.second;
  139. // Check if anything is dirty. If nothing is we can skip the update
  140. bool isDirty = renderData.isDirty;
  141. renderData.isDirty = false;
  142. for(auto& widget : renderData.widgets)
  143. {
  144. if(widget->isDirty(true))
  145. {
  146. isDirty = true;
  147. }
  148. }
  149. if(!isDirty)
  150. continue;
  151. // Make a list of all GUI elements, sorted from farthest to nearest (highest depth to lowest)
  152. auto elemComp = [](const GUIGroupElement& a, const GUIGroupElement& b)
  153. {
  154. UINT32 aDepth = a.element->_getRenderElementDepth(a.renderElement);
  155. UINT32 bDepth = b.element->_getRenderElementDepth(b.renderElement);
  156. // Compare pointers just to differentiate between two elements with the same depth, their order doesn't really matter, but std::set
  157. // requires all elements to be unique
  158. return aDepth > bDepth || (aDepth ==bDepth && a.element > b.element);
  159. };
  160. set<GUIGroupElement, std::function<bool(const GUIGroupElement&, const GUIGroupElement&)>>::type allElements(elemComp);
  161. for(auto& widget : renderData.widgets)
  162. {
  163. const vector<GUIElement*>::type& elements = widget->getElements();
  164. for(auto& element : elements)
  165. {
  166. UINT32 numRenderElems = element->getNumRenderElements();
  167. for(UINT32 i = 0; i < numRenderElems; i++)
  168. {
  169. allElements.insert(GUIGroupElement(element, i));
  170. }
  171. }
  172. }
  173. // Group the elements in such a way so that we end up with a smallest amount of
  174. // meshes, without breaking back to front rendering order
  175. unordered_map<UINT64, vector<GUIMaterialGroup>::type>::type materialGroups;
  176. for(auto& elem : allElements)
  177. {
  178. GUIElement* guiElem = elem.element;
  179. UINT32 renderElemIdx = elem.renderElement;
  180. UINT32 elemDepth = guiElem->_getRenderElementDepth(renderElemIdx);
  181. Rect tfrmedBounds = guiElem->_getBounds();
  182. tfrmedBounds.transform(guiElem->_getParentWidget().SO()->getWorldTfrm());
  183. const HMaterial& mat = guiElem->getMaterial(renderElemIdx);
  184. UINT64 materialId = mat->getInternalID(); // TODO - I group based on material ID. So if two widgets used exact copies of the same material
  185. // this system won't detect it. Find a better way of determining material similarity?
  186. // If this is a new material, add a new list of groups
  187. auto findIterMaterial = materialGroups.find(materialId);
  188. if(findIterMaterial == end(materialGroups))
  189. materialGroups[materialId] = vector<GUIMaterialGroup>::type();
  190. // Try to find a group this material will fit in:
  191. // - Group that has a depth value same or one below elements depth will always be a match
  192. // - Otherwise, we search higher depth values as well, but we only use them if no elements in between those depth values
  193. // overlap the current elements bounds.
  194. vector<GUIMaterialGroup>::type& allGroups = materialGroups[materialId];
  195. GUIMaterialGroup* foundGroup = nullptr;
  196. for(auto groupIter = allGroups.rbegin(); groupIter != allGroups.rend(); ++groupIter)
  197. {
  198. // If we separate meshes by widget, ignore any groups with widget parents other than mine
  199. if(mSeparateMeshesByWidget)
  200. {
  201. if(groupIter->elements.size() > 0)
  202. {
  203. GUIElement* otherElem = groupIter->elements.begin()->element; // We only need to check the first element
  204. if(&otherElem->_getParentWidget() != &guiElem->_getParentWidget())
  205. continue;
  206. }
  207. }
  208. GUIMaterialGroup& group = *groupIter;
  209. if(group.depth == elemDepth || group.depth == (elemDepth - 1))
  210. {
  211. foundGroup = &group;
  212. break;
  213. }
  214. else
  215. {
  216. UINT32 startDepth = elemDepth;
  217. UINT32 endDepth = group.depth;
  218. bool foundOverlap = false;
  219. for(auto& material : materialGroups)
  220. {
  221. for(auto& group : material.second)
  222. {
  223. if(group.depth > startDepth && group.depth < endDepth)
  224. {
  225. if(group.bounds.overlaps(tfrmedBounds))
  226. {
  227. foundOverlap = true;
  228. break;
  229. }
  230. }
  231. }
  232. }
  233. if(!foundOverlap)
  234. {
  235. foundGroup = &group;
  236. break;
  237. }
  238. }
  239. }
  240. if(foundGroup == nullptr)
  241. {
  242. allGroups.push_back(GUIMaterialGroup());
  243. foundGroup = &allGroups[allGroups.size() - 1];
  244. foundGroup->depth = elemDepth;
  245. foundGroup->bounds = tfrmedBounds;
  246. foundGroup->elements.push_back(GUIGroupElement(guiElem, renderElemIdx));
  247. foundGroup->material = mat;
  248. foundGroup->numQuads = guiElem->getNumQuads(renderElemIdx);
  249. }
  250. else
  251. {
  252. foundGroup->bounds.encapsulate(tfrmedBounds);
  253. foundGroup->elements.push_back(GUIGroupElement(guiElem, renderElemIdx));
  254. foundGroup->numQuads += guiElem->getNumQuads(renderElemIdx);
  255. }
  256. }
  257. // Make a list of all GUI elements, sorted from farthest to nearest (highest depth to lowest)
  258. auto groupComp = [](GUIMaterialGroup* a, GUIMaterialGroup* b)
  259. {
  260. return (a->depth > b->depth) || (a->depth == b->depth && a > b);
  261. // Compare pointers just to differentiate between two elements with the same depth, their order doesn't really matter, but std::set
  262. // requires all elements to be unique
  263. };
  264. set<GUIMaterialGroup*, std::function<bool(GUIMaterialGroup*, GUIMaterialGroup*)>>::type sortedGroups(groupComp);
  265. for(auto& material : materialGroups)
  266. {
  267. for(auto& group : material.second)
  268. {
  269. sortedGroups.insert(&group);
  270. }
  271. }
  272. UINT32 numMeshes = (UINT32)sortedGroups.size();
  273. UINT32 oldNumMeshes = (UINT32)renderData.cachedMeshes.size();
  274. if(numMeshes < oldNumMeshes)
  275. renderData.cachedMeshes.resize(numMeshes);
  276. renderData.cachedMaterials.resize(numMeshes);
  277. if(mSeparateMeshesByWidget)
  278. renderData.cachedWidgetsPerMesh.resize(numMeshes);
  279. // Fill buffers for each group and update their meshes
  280. UINT32 groupIdx = 0;
  281. for(auto& group : sortedGroups)
  282. {
  283. renderData.cachedMaterials[groupIdx] = group->material;
  284. if(mSeparateMeshesByWidget)
  285. {
  286. if(group->elements.size() == 0)
  287. renderData.cachedWidgetsPerMesh[groupIdx] = nullptr;
  288. else
  289. {
  290. GUIElement* elem = group->elements.begin()->element;
  291. renderData.cachedWidgetsPerMesh[groupIdx] = &elem->_getParentWidget();
  292. }
  293. }
  294. MeshDataPtr meshData = cm_shared_ptr<MeshData, PoolAlloc>(group->numQuads * 4);
  295. meshData->beginDesc();
  296. meshData->addVertElem(VET_FLOAT2, VES_POSITION);
  297. meshData->addVertElem(VET_FLOAT2, VES_TEXCOORD);
  298. meshData->addSubMesh(group->numQuads * 6);
  299. meshData->endDesc();
  300. UINT8* vertices = meshData->getElementData(VES_POSITION);
  301. UINT8* uvs = meshData->getElementData(VES_TEXCOORD);
  302. UINT32* indices = meshData->getIndices32();
  303. UINT32 vertexStride = meshData->getVertexStride();
  304. UINT32 indexStride = meshData->getIndexElementSize();
  305. UINT32 quadOffset = 0;
  306. for(auto& matElement : group->elements)
  307. {
  308. matElement.element->fillBuffer(vertices, uvs, indices, quadOffset, group->numQuads, vertexStride, indexStride, matElement.renderElement);
  309. UINT32 numQuads = matElement.element->getNumQuads(matElement.renderElement);
  310. UINT32 indexStart = quadOffset * 6;
  311. UINT32 indexEnd = indexStart + numQuads * 6;
  312. UINT32 vertOffset = quadOffset * 4;
  313. for(UINT32 i = indexStart; i < indexEnd; i++)
  314. indices[i] += vertOffset;
  315. quadOffset += numQuads;
  316. }
  317. if(groupIdx >= (UINT32)renderData.cachedMeshes.size())
  318. {
  319. renderData.cachedMeshes.push_back(Mesh::create());
  320. }
  321. gMainSyncedRC().writeSubresource(renderData.cachedMeshes[groupIdx].getInternalPtr(), 0, *meshData);
  322. gMainSyncedRC().submitToGpu(true); // TODO - Remove this once I make writeSubresource accept a shared_ptr for MeshData
  323. groupIdx++;
  324. }
  325. }
  326. }
  327. void GUIManager::updateInput()
  328. {
  329. #if CM_DEBUG_MODE
  330. // Checks if all referenced windows actually exist
  331. vector<RenderWindow*>::type activeWindows = RenderWindowManager::instance().getRenderWindows();
  332. for(auto& widget : mWidgets)
  333. {
  334. auto iterFind = std::find(begin(activeWindows), end(activeWindows), widget->getOwnerWindow());
  335. if(iterFind == activeWindows.end())
  336. {
  337. CM_EXCEPT(InternalErrorException, "GUI manager has a reference to a window that doesn't exist. \
  338. Please detach all GUIWidgets from windows before destroying a window.");
  339. }
  340. }
  341. #endif
  342. GUIWidget* widgetInFocus = nullptr;
  343. GUIElement* topMostElement = nullptr;
  344. UINT32 topMostDepth = std::numeric_limits<UINT32>::max();
  345. for(auto& widget : mWidgets)
  346. {
  347. const RenderWindow* window = widget->getOwnerWindow();
  348. if(window->hasFocus())
  349. {
  350. widgetInFocus = widget;
  351. break;
  352. }
  353. }
  354. Int2 screenPos;
  355. if(widgetInFocus != nullptr)
  356. {
  357. const RenderWindow* window = widgetInFocus->getOwnerWindow();
  358. screenPos = Cursor::getWindowPosition(*window);
  359. Vector4 vecScreenPos((float)screenPos.x, (float)screenPos.y, 0.0f, 1.0f);
  360. for(auto& widget : mWidgets)
  361. {
  362. if(widget->inBounds(screenPos))
  363. {
  364. const Matrix4& worldTfrm = widget->SO()->getWorldTfrm();
  365. Vector4 vecLocalPos = worldTfrm.inverse() * vecScreenPos;
  366. Int2 localPos(Math::RoundToInt(vecLocalPos.x), Math::RoundToInt(vecLocalPos.y));
  367. vector<GUIElement*>::type sortedElements = widget->getElements();
  368. std::sort(sortedElements.begin(), sortedElements.end(),
  369. [](GUIElement* a, GUIElement* b)
  370. {
  371. return a->_getDepth() < b->_getDepth();
  372. });
  373. // Elements with lowest depth (most to the front) get handled first
  374. for(auto iter = sortedElements.begin(); iter != sortedElements.end(); ++iter)
  375. {
  376. GUIElement* element = *iter;
  377. const Rect& bounds = element->_getContentBounds();
  378. if(bounds.contains(localPos) && element->_getDepth() < topMostDepth)
  379. {
  380. topMostElement = element;
  381. topMostDepth = element->_getDepth();
  382. break;
  383. }
  384. }
  385. }
  386. }
  387. }
  388. if(topMostElement != mMouseOverElement)
  389. {
  390. if(mMouseOverElement != nullptr)
  391. {
  392. // Send MouseOut event
  393. const RenderWindow* window = mMouseOverWidget->getOwnerWindow();
  394. Int2 oldScreenPos = Cursor::getWindowPosition(*window);
  395. GUIMouseEvent event(GUIMouseEventType::MouseOut, oldScreenPos); // TODO - This pos will be wrong as it might be related to completely other window
  396. mMouseOverWidget->_mouseEvent(mMouseOverElement, event);
  397. }
  398. if(topMostElement != nullptr)
  399. {
  400. // Send MouseOver event
  401. GUIMouseEvent event(GUIMouseEventType::MouseOver, screenPos);
  402. widgetInFocus->_mouseEvent(topMostElement, event);
  403. }
  404. mMouseOverElement = topMostElement;
  405. mMouseOverWidget = widgetInFocus;
  406. }
  407. if(mMouseOverElement != nullptr)
  408. {
  409. // Send MouseMove event
  410. if(mLastCursorPos != screenPos)
  411. {
  412. GUIMouseEvent event(GUIMouseEventType::MouseMove, screenPos);
  413. mMouseOverWidget->_mouseEvent(mMouseOverElement, event);
  414. }
  415. // Send MouseDown and MouseUp events
  416. for(int i = 0; i < MB_Count; i++)
  417. {
  418. bool buttonDown = gInput().isButtonDown((MouseButton)i);
  419. if(mLastFrameButtonState[i] != buttonDown)
  420. {
  421. if(buttonDown)
  422. {
  423. GUIMouseEvent event(GUIMouseEventType::MouseDown, screenPos, (MouseButton)i);
  424. mMouseOverWidget->_mouseEvent(mMouseOverElement, event);
  425. }
  426. else
  427. {
  428. GUIMouseEvent event(GUIMouseEventType::MouseUp, screenPos, (MouseButton)i);
  429. mMouseOverWidget->_mouseEvent(mMouseOverElement, event);
  430. }
  431. mLastFrameButtonState[i] = buttonDown;
  432. }
  433. }
  434. }
  435. }
  436. }