💿🐜 Antkeeper source code https://antkeeper.com
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

459 lines
10 KiB

7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
  1. /*
  2. * Copyright (C) 2017-2019 Christopher J. Howard
  3. *
  4. * This file is part of Antkeeper Source Code.
  5. *
  6. * Antkeeper Source Code is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * Antkeeper Source Code is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with Antkeeper Source Code. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. #include "ui.hpp"
  20. UIMaterial::UIMaterial()
  21. {
  22. texture = addVariable<const Texture2D*>("texture");
  23. textureOffset = addVariable<Vector2>("offset");
  24. textureScale = addVariable<Vector2>("scale");
  25. texture->setValue(nullptr);
  26. textureOffset->setValue(Vector2(0.0f));
  27. textureScale->setValue(Vector2(1.0f));
  28. }
  29. UIElement::UIElement():
  30. parent(nullptr),
  31. anchor(Anchor::TOP_LEFT),
  32. layerOffset(0),
  33. layer(0),
  34. bounds(Vector2(0.0f), Vector2(0.0f)),
  35. tintColor(1.0f),
  36. color(tintColor),
  37. visible(true),
  38. callbacksEnabled(true),
  39. mouseOver(false),
  40. mouseOverCallback(nullptr),
  41. mouseOutCallback(nullptr),
  42. mouseMovedCallback(nullptr),
  43. mousePressedCallback(nullptr),
  44. mouseReleasedCallback(nullptr),
  45. origin(0.0f),
  46. translation(0.0f),
  47. rotation(0.0f),
  48. dimensions(0.0f),
  49. position(0.0f),
  50. originTween(&origin, lerp<Vector2>),
  51. translationTween(&translation, lerp<Vector2>),
  52. rotationTween(&rotation, lerp<float>),
  53. dimensionsTween(&dimensions, lerp<Vector2>),
  54. positionTween(&position, lerp<Vector2>),
  55. tintColorTween(&tintColor, lerp<Vector4>),
  56. colorTween(&color, lerp<Vector4>)
  57. {}
  58. UIElement::~UIElement()
  59. {}
  60. void UIElement::update()
  61. {
  62. resetTweens();
  63. // Calculate position
  64. if (parent != nullptr)
  65. {
  66. // Calculate world-space position
  67. Vector2 anchorPoint = parent->position + parent->dimensions * anchor - dimensions * anchor;
  68. position = anchorPoint + origin + translation;
  69. // Calculate layer
  70. layer = parent->layer + 1 + layerOffset;
  71. // Calculate color
  72. color = parent->color * tintColor;
  73. }
  74. else
  75. {
  76. position = origin + translation;
  77. layer = layerOffset;
  78. color = tintColor;
  79. }
  80. // Calculate bounds
  81. bounds.setMin(position);
  82. bounds.setMax(position + dimensions);
  83. // Update children
  84. for (UIElement* child: children)
  85. {
  86. child->update();
  87. }
  88. }
  89. void UIElement::addChild(UIElement* element)
  90. {
  91. children.push_back(element);
  92. element->parent = this;
  93. }
  94. void UIElement::removeChild(UIElement* element)
  95. {
  96. for (auto it = children.begin(); it != children.end(); ++it)
  97. {
  98. if (*it == element)
  99. {
  100. children.erase(it);
  101. return;
  102. }
  103. }
  104. }
  105. void UIElement::setMouseOverCallback(std::function<void()> callback)
  106. {
  107. mouseOverCallback = callback;
  108. }
  109. void UIElement::setMouseOutCallback(std::function<void()> callback)
  110. {
  111. mouseOutCallback = callback;
  112. }
  113. void UIElement::setMouseMovedCallback(std::function<void(int, int)> callback)
  114. {
  115. mouseMovedCallback = callback;
  116. }
  117. void UIElement::setMousePressedCallback(std::function<void(int, int, int)> callback)
  118. {
  119. mousePressedCallback = callback;
  120. }
  121. void UIElement::setMouseReleasedCallback(std::function<void(int, int, int)> callback)
  122. {
  123. mouseReleasedCallback = callback;
  124. }
  125. void UIElement::handleEvent(const MouseMovedEvent& event)
  126. {
  127. if (!callbacksEnabled)
  128. {
  129. return;
  130. }
  131. if (bounds.contains(Vector2(event.x, event.y)))
  132. {
  133. if (!mouseOver)
  134. {
  135. mouseOver = true;
  136. if (mouseOverCallback)
  137. {
  138. mouseOverCallback();
  139. }
  140. }
  141. if (mouseMovedCallback)
  142. {
  143. mouseMovedCallback(event.x, event.y);
  144. }
  145. }
  146. else if (mouseOver)
  147. {
  148. mouseOver = false;
  149. if (mouseOutCallback)
  150. {
  151. mouseOutCallback();
  152. }
  153. }
  154. for (UIElement* child: children)
  155. {
  156. child->handleEvent(event);
  157. }
  158. }
  159. void UIElement::handleEvent(const MouseButtonPressedEvent& event)
  160. {
  161. if (!callbacksEnabled)
  162. {
  163. return;
  164. }
  165. if (bounds.contains(Vector2(event.x, event.y)))
  166. {
  167. if (mousePressedCallback)
  168. {
  169. mousePressedCallback(event.button, event.x, event.y);
  170. }
  171. for (UIElement* child: children)
  172. {
  173. child->handleEvent(event);
  174. }
  175. }
  176. }
  177. void UIElement::handleEvent(const MouseButtonReleasedEvent& event)
  178. {
  179. if (!callbacksEnabled)
  180. {
  181. return;
  182. }
  183. if (bounds.contains(Vector2(event.x, event.y)))
  184. {
  185. if (mouseReleasedCallback)
  186. {
  187. mouseReleasedCallback(event.button, event.x , event.y);
  188. }
  189. for (UIElement* child: children)
  190. {
  191. child->handleEvent(event);
  192. }
  193. }
  194. }
  195. void UIElement::interpolate(float dt)
  196. {
  197. originTween.interpolate(dt);
  198. translationTween.interpolate(dt);
  199. rotationTween.interpolate(dt);
  200. dimensionsTween.interpolate(dt);
  201. positionTween.interpolate(dt);
  202. tintColorTween.interpolate(dt);
  203. colorTween.interpolate(dt);
  204. for (UIElement* child: children)
  205. {
  206. child->interpolate(dt);
  207. }
  208. }
  209. void UIElement::resetTweens()
  210. {
  211. originTween.reset();
  212. translationTween.reset();
  213. rotationTween.reset();
  214. dimensionsTween.reset();
  215. positionTween.reset();
  216. tintColorTween.reset();
  217. colorTween.reset();
  218. for (UIElement* child: children)
  219. {
  220. child->resetTweens();
  221. }
  222. }
  223. UILabel::UILabel():
  224. font(nullptr)
  225. {}
  226. UILabel::~UILabel()
  227. {}
  228. void UILabel::setFont(Font* font)
  229. {
  230. this->font = font;
  231. material.texture->setValue(font->getTexture());
  232. calculateDimensions();
  233. }
  234. void UILabel::setText(const std::string& text)
  235. {
  236. this->text = text;
  237. calculateDimensions();
  238. }
  239. void UILabel::calculateDimensions()
  240. {
  241. if (font != nullptr && !text.empty())
  242. {
  243. float width = font->getWidth(text);
  244. float height = font->getMetrics().getHeight();
  245. setDimensions(Vector2(width, height));
  246. }
  247. else
  248. {
  249. setDimensions(Vector2(0.0f));
  250. }
  251. }
  252. UIImage::UIImage():
  253. textureBounds(Vector2(0.0f), Vector2(1.0f))
  254. {}
  255. UIImage::~UIImage()
  256. {}
  257. void UIBatcher::batch(BillboardBatch* result, const UIElement* ui)
  258. {
  259. // Create list of visible UI elements
  260. std::list<const UIElement*> elements;
  261. queueElements(&elements, ui);
  262. // Sort UI elements according to layer and texture
  263. elements.sort([](const UIElement* a, const UIElement* b)
  264. {
  265. if (a->getLayer() < b->getLayer())
  266. {
  267. return true;
  268. }
  269. else if (b->getLayer() < a->getLayer())
  270. {
  271. return false;
  272. }
  273. return (a->getMaterial()->texture->getValue() < b->getMaterial()->texture->getValue());
  274. });
  275. // Clear previous ranges
  276. result->removeRanges();
  277. // Batch UI elements
  278. for (const UIElement* element: elements)
  279. {
  280. batchElement(result, element);
  281. }
  282. // Update batch
  283. result->batch();
  284. }
  285. void UIBatcher::queueElements(std::list<const UIElement*>* elements, const UIElement* element) const
  286. {
  287. if (element->isVisible())
  288. {
  289. elements->push_back(element);
  290. for (std::size_t i = 0; i < element->getChildCount(); ++i)
  291. {
  292. queueElements(elements, element->getChild(i));
  293. }
  294. }
  295. }
  296. BillboardBatch::Range* UIBatcher::getRange(BillboardBatch* result, const UIElement* element) const
  297. {
  298. BillboardBatch::Range* range = nullptr;
  299. if (!result->getRangeCount())
  300. {
  301. // Create initial range
  302. range = result->addRange();
  303. range->material = (Material*)element->getMaterial();
  304. range->start = 0;
  305. range->length = 0;
  306. }
  307. else
  308. {
  309. range = result->getRange(result->getRangeCount() - 1);
  310. const UIMaterial* material = static_cast<UIMaterial*>(range->material);
  311. if (material->texture->getValue() != element->getMaterial()->texture->getValue())
  312. {
  313. // Create new range for the element
  314. range = result->addRange();
  315. BillboardBatch::Range* precedingRange = result->getRange(result->getRangeCount() - 2);
  316. range->material = (Material*)element->getMaterial();
  317. range->start = precedingRange->start + precedingRange->length;
  318. range->length = 0;
  319. }
  320. }
  321. return range;
  322. }
  323. void UIBatcher::batchElement(BillboardBatch* result, const UIElement* element)
  324. {
  325. switch (element->getElementType())
  326. {
  327. case UIElement::Type::LABEL:
  328. batchLabel(result, static_cast<const UILabel*>(element));
  329. break;
  330. case UIElement::Type::IMAGE:
  331. batchImage(result, static_cast<const UIImage*>(element));
  332. break;
  333. case UIElement::Type::CONTAINER:
  334. break;
  335. default:
  336. break;
  337. }
  338. }
  339. void UIBatcher::batchLabel(BillboardBatch* result, const UILabel* label)
  340. {
  341. if (label->getFont() != nullptr && !label->getText().empty())
  342. {
  343. // Get range
  344. BillboardBatch::Range* range = getRange(result, label);
  345. // Pixel-perfect
  346. //Vector3 origin = Vector3((int)(label->getPosition().x + 0.5f), (int)(label->getPosition().y + 0.5f), label->getLayer() * 0.01f);
  347. Vector3 origin = Vector3((int)(label->getPositionTween()->getSubstate().x), (int)(label->getPositionTween()->getSubstate().y), label->getLayer() * 0.01f);
  348. // Print billboards
  349. const Font* font = label->getFont();
  350. std::size_t index = range->start + range->length;
  351. std::size_t count = 0;
  352. font->puts(result, origin, label->getText(), label->getColorTween()->getSubstate(), index, &count);
  353. for (std::size_t i = index; i < index + count; ++i)
  354. {
  355. result->getBillboard(i)->resetTweens();
  356. }
  357. // Increment range length
  358. range->length += count;
  359. }
  360. }
  361. void UIBatcher::batchImage(BillboardBatch* result, const UIImage* image)
  362. {
  363. // Get range
  364. BillboardBatch::Range* range = getRange(result, image);
  365. // Pixel-perfect
  366. //Vector2 position = Vector2((int)(image->getPosition().x + 0.5f), (int)(image->getPosition().y + 0.5f));
  367. //Vector2 dimensions = Vector2((int)(image->getDimensions().x + 0.5f), (int)(image->getDimensions().y + 0.5f));
  368. //Vector3 translation = Vector3(position.x + dimensions.x * 0.5f, position.y + dimensions.y * 0.5f, image->getLayer() * 0.01f);
  369. Vector2 position = Vector2((int)(image->getPositionTween()->getSubstate().x), (int)(image->getPositionTween()->getSubstate().y));
  370. Vector2 dimensions = Vector2((int)(image->getDimensionsTween()->getSubstate().x), (int)(image->getDimensionsTween()->getSubstate().y));
  371. Vector3 translation = Vector3(position.x + dimensions.x * 0.5f, position.y + dimensions.y * 0.5f, image->getLayer() * 0.01f);
  372. // Create billboard
  373. std::size_t index = range->start + range->length;
  374. Billboard* billboard = result->getBillboard(index);
  375. billboard->setDimensions(dimensions);
  376. billboard->setTranslation(translation);
  377. if (image->getRotationTween()->getSubstate() != 0.0f)
  378. {
  379. billboard->setRotation(glm::angleAxis(image->getRotationTween()->getSubstate(), Vector3(0, 0, -1.0f)));
  380. }
  381. else
  382. {
  383. billboard->setRotation(Quaternion(1, 0, 0, 0));
  384. }
  385. billboard->setTextureCoordinates(image->getTextureBounds().getMin(), image->getTextureBounds().getMax());
  386. billboard->setTintColor(image->getColorTween()->getSubstate());
  387. billboard->resetTweens();
  388. // Increment range length
  389. ++(range->length);
  390. }