💿🐜 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.

1719 lines
60 KiB

  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 "game.hpp"
  20. #include "states/game-state.hpp"
  21. #include "states/splash-state.hpp"
  22. #include "states/sandbox-state.hpp"
  23. #include "paths.hpp"
  24. #include "ui/ui.hpp"
  25. #include "graphics/ui-render-pass.hpp"
  26. #include "graphics/shadow-map-render-pass.hpp"
  27. #include "graphics/clear-render-pass.hpp"
  28. #include "graphics/sky-render-pass.hpp"
  29. #include "graphics/lighting-render-pass.hpp"
  30. #include "graphics/silhouette-render-pass.hpp"
  31. #include "graphics/final-render-pass.hpp"
  32. #include "resources/resource-manager.hpp"
  33. #include "resources/text-file.hpp"
  34. #include "game/camera-rig.hpp"
  35. #include "game/lens.hpp"
  36. #include "game/forceps.hpp"
  37. #include "game/brush.hpp"
  38. #include "entity/component-manager.hpp"
  39. #include "entity/components/transform-component.hpp"
  40. #include "entity/components/model-component.hpp"
  41. #include "entity/entity-manager.hpp"
  42. #include "entity/entity-template.hpp"
  43. #include "entity/system-manager.hpp"
  44. #include "entity/systems/sound-system.hpp"
  45. #include "entity/systems/collision-system.hpp"
  46. #include "entity/systems/render-system.hpp"
  47. #include "entity/systems/tool-system.hpp"
  48. #include "entity/systems/locomotion-system.hpp"
  49. #include "entity/systems/behavior-system.hpp"
  50. #include "entity/systems/steering-system.hpp"
  51. #include "entity/systems/particle-system.hpp"
  52. #include "stb/stb_image_write.h"
  53. #include <algorithm>
  54. #include <cctype>
  55. #include <chrono>
  56. #include <fstream>
  57. #include <iomanip>
  58. #include <sstream>
  59. #include <stdexcept>
  60. #include <thread>
  61. Game::Game(int argc, char* argv[]):
  62. currentState(nullptr),
  63. window(nullptr)
  64. {
  65. // Get paths
  66. dataPath = getDataPath();
  67. configPath = getConfigPath();
  68. // Create config path if it doesn't exist
  69. if (!pathExists(configPath))
  70. {
  71. createDirectory(configPath);
  72. }
  73. std::cout << configPath << std::endl;
  74. // Setup resource manager
  75. resourceManager = new ResourceManager();
  76. resourceManager->include(dataPath);
  77. resourceManager->include(configPath);
  78. // Read strings file
  79. stringTable = resourceManager->load<CSVTable>("strings.csv");
  80. // Build string map
  81. for (int row = 0; row < stringTable->size(); ++row)
  82. {
  83. stringMap[(*stringTable)[row][0]] = row;
  84. }
  85. // Determine number of languages
  86. languageCount = (*stringTable)[0].size() - 1;
  87. // Set current language to English
  88. currentLanguage = 0;
  89. splashState = new SplashState(this);
  90. sandboxState = new SandboxState(this);
  91. }
  92. Game::~Game()
  93. {
  94. if (window)
  95. {
  96. windowManager->destroyWindow(window);
  97. }
  98. }
  99. void Game::changeState(GameState* state)
  100. {
  101. if (currentState != nullptr)
  102. {
  103. currentState->exit();
  104. }
  105. currentState = state;
  106. if (currentState != nullptr)
  107. {
  108. currentState->enter();
  109. }
  110. }
  111. std::string Game::getString(std::size_t languageIndex, const std::string& name) const
  112. {
  113. std::string value;
  114. auto it = stringMap.find(name);
  115. if (it != stringMap.end())
  116. {
  117. value = (*stringTable)[it->second][languageIndex + 1];
  118. if (value.empty())
  119. {
  120. value = std::string("# EMPTY STRING: ") + name + std::string(" #");
  121. }
  122. }
  123. else
  124. {
  125. value = std::string("# MISSING STRING: ") + name + std::string(" #");
  126. }
  127. return value;
  128. }
  129. void Game::changeLanguage(std::size_t languageIndex)
  130. {
  131. currentLanguage = languageIndex;
  132. window->setTitle(getString(getCurrentLanguage(), "title").c_str());
  133. restringUI();
  134. resizeUI(w, h);
  135. }
  136. void Game::toggleFullscreen()
  137. {
  138. fullscreen = !fullscreen;
  139. window->setFullscreen(fullscreen);
  140. }
  141. void Game::setUpdateRate(double frequency)
  142. {
  143. stepScheduler.setStepFrequency(frequency);
  144. }
  145. void Game::setup()
  146. {
  147. // Initialize default parameters
  148. title = getString(currentLanguage, "title");
  149. float windowSizeRatio = 3.0f / 4.0f;
  150. const Display* display = deviceManager->getDisplays()->front();
  151. w = std::get<0>(display->getDimensions()) * windowSizeRatio;
  152. h = std::get<1>(display->getDimensions()) * windowSizeRatio;
  153. w = 1600;
  154. h = 900;
  155. int x = std::get<0>(display->getPosition()) + std::get<0>(display->getDimensions()) / 2 - w / 2;
  156. int y = std::get<1>(display->getPosition()) + std::get<1>(display->getDimensions()) / 2 - h / 2;
  157. unsigned int flags = WindowFlag::RESIZABLE;
  158. fullscreen = false;
  159. bool vsync = true;
  160. double maxFrameDuration = 0.25;
  161. double stepFrequency = 60.0;
  162. // Create window
  163. window = windowManager->createWindow(title.c_str(), x, y, w, h, fullscreen, flags);
  164. if (!window)
  165. {
  166. throw std::runtime_error("Game::Game(): Failed to create window.");
  167. }
  168. // Set v-sync mode
  169. window->setVSync(vsync);
  170. // Setup step scheduler
  171. stepScheduler.setMaxFrameDuration(maxFrameDuration);
  172. stepScheduler.setStepFrequency(stepFrequency);
  173. timestep = stepScheduler.getStepPeriod();
  174. // Setup performance sampling
  175. performanceSampler.setSampleSize(15);
  176. // Get DPI and font size
  177. dpi = display->getDPI();
  178. fontSizePT = 14;
  179. fontSizePX = fontSizePT * (1.0f / 72.0f) * dpi;
  180. // Create scene
  181. scene = new Scene(&stepInterpolator);
  182. // Setup control profile
  183. keyboard = deviceManager->getKeyboards()->front();
  184. mouse = deviceManager->getMice()->front();
  185. closeControl.bindKey(keyboard, Scancode::ESCAPE);
  186. closeControl.setActivatedCallback(std::bind(&Application::close, this, EXIT_SUCCESS));
  187. fullscreenControl.bindKey(keyboard, Scancode::F11);
  188. fullscreenControl.setActivatedCallback(std::bind(&Game::toggleFullscreen, this));
  189. openRadialMenuControl.bindKey(keyboard, Scancode::LSHIFT);
  190. moveForwardControl.bindKey(keyboard, Scancode::W);
  191. moveBackControl.bindKey(keyboard, Scancode::S);
  192. moveLeftControl.bindKey(keyboard, Scancode::A);
  193. moveRightControl.bindKey(keyboard, Scancode::D);
  194. //rotateCCWControl.bindKey(keyboard, Scancode::Q);
  195. //rotateCWControl.bindKey(keyboard, Scancode::E);
  196. moveRightControl.bindKey(keyboard, Scancode::D);
  197. zoomInControl.bindKey(keyboard, Scancode::EQUALS);
  198. zoomInControl.bindMouseWheelAxis(mouse, MouseWheelAxis::POSITIVE_Y);
  199. zoomOutControl.bindKey(keyboard, Scancode::MINUS);
  200. zoomOutControl.bindMouseWheelAxis(mouse, MouseWheelAxis::NEGATIVE_Y);
  201. adjustCameraControl.bindMouseButton(mouse, 2);
  202. dragCameraControl.bindMouseButton(mouse, 3);
  203. toggleNestViewControl.bindKey(keyboard, Scancode::N);
  204. toggleWireframeControl.bindKey(keyboard, Scancode::V);
  205. screenshotControl.bindKey(keyboard, Scancode::B);
  206. toggleEditModeControl.bindKey(keyboard, Scancode::TAB);
  207. controlProfile.registerControl("close", &closeControl);
  208. controlProfile.registerControl("fullscreen", &fullscreenControl);
  209. controlProfile.registerControl("open-radial-menu", &openRadialMenuControl);
  210. controlProfile.registerControl("move-forward", &moveForwardControl);
  211. controlProfile.registerControl("move-back", &moveBackControl);
  212. controlProfile.registerControl("move-left", &moveLeftControl);
  213. controlProfile.registerControl("move-right", &moveRightControl);
  214. controlProfile.registerControl("rotate-ccw", &rotateCCWControl);
  215. controlProfile.registerControl("rotate-cw", &rotateCWControl);
  216. controlProfile.registerControl("zoom-in", &zoomInControl);
  217. controlProfile.registerControl("zoom-out", &zoomOutControl);
  218. controlProfile.registerControl("adjust-camera", &adjustCameraControl);
  219. controlProfile.registerControl("drag-camera", &dragCameraControl);
  220. controlProfile.registerControl("toggle-nest-view", &toggleNestViewControl);
  221. controlProfile.registerControl("toggle-wireframe", &toggleWireframeControl);
  222. controlProfile.registerControl("screenshot", &screenshotControl);
  223. controlProfile.registerControl("toggle-edit-mode", &toggleEditModeControl);
  224. wireframe = false;
  225. toggleWireframeControl.setActivatedCallback(std::bind(&Game::toggleWireframe, this));
  226. screenshotControl.setActivatedCallback(std::bind(&Game::queueScreenshot, this));
  227. screenshotQueued = false;
  228. TestEvent event1, event2, event3;
  229. event1.id = 1;
  230. event2.id = 2;
  231. event3.id = 3;
  232. eventDispatcher.subscribe<TestEvent>(this);
  233. eventDispatcher.schedule(event1, 1.0);
  234. eventDispatcher.schedule(event2, 10.0);
  235. eventDispatcher.schedule(event3, 1.0);
  236. // Load model resources
  237. try
  238. {
  239. lensModel = resourceManager->load<Model>("lens.mdl");
  240. forcepsModel = resourceManager->load<Model>("forceps.mdl");
  241. brushModel = resourceManager->load<Model>("brush.mdl");
  242. smokeMaterial = resourceManager->load<Material>("smoke.mtl");
  243. }
  244. catch (const std::exception& e)
  245. {
  246. std::cerr << "Failed to load one or more models: \"" << e.what() << "\"" << std::endl;
  247. close(EXIT_FAILURE);
  248. }
  249. try
  250. {
  251. splashTexture = resourceManager->load<Texture2D>("epigraph.png");
  252. hudSpriteSheetTexture = resourceManager->load<Texture2D>("hud.png");
  253. // Read texture atlas file
  254. CSVTable* atlasTable = resourceManager->load<CSVTable>("hud-atlas.csv");
  255. // Build texture atlas
  256. for (int row = 0; row < atlasTable->size(); ++row)
  257. {
  258. std::stringstream ss;
  259. float x;
  260. float y;
  261. float w;
  262. float h;
  263. ss << (*atlasTable)[row][1];
  264. ss >> x;
  265. ss.str(std::string());
  266. ss.clear();
  267. ss << (*atlasTable)[row][2];
  268. ss >> y;
  269. ss.str(std::string());
  270. ss.clear();
  271. ss << (*atlasTable)[row][3];
  272. ss >> w;
  273. ss.str(std::string());
  274. ss.clear();
  275. ss << (*atlasTable)[row][4];
  276. ss >> h;
  277. ss.str(std::string());
  278. y = static_cast<float>(hudSpriteSheetTexture->getHeight()) - y - h;
  279. x = (int)(x + 0.5f);
  280. y = (int)(y + 0.5f);
  281. w = (int)(w + 0.5f);
  282. h = (int)(h + 0.5f);
  283. hudTextureAtlas.insert((*atlasTable)[row][0], Rect(Vector2(x, y), Vector2(x + w, y + h)));
  284. }
  285. }
  286. catch (const std::exception& e)
  287. {
  288. std::cerr << "Failed to load one or more textures: \"" << e.what() << "\"" << std::endl;
  289. close(EXIT_FAILURE);
  290. }
  291. // Load font resources
  292. try
  293. {
  294. //labelTypeface = resourceManager->load<Typeface>("open-sans-regular.ttf");
  295. labelTypeface = resourceManager->load<Typeface>("caveat-bold.ttf");
  296. labelFont = labelTypeface->createFont(fontSizePX);
  297. debugTypeface = resourceManager->load<Typeface>("inconsolata-bold.ttf");
  298. debugFont = debugTypeface->createFont(fontSizePX);
  299. debugTypeface->loadCharset(debugFont, UnicodeRange::BASIC_LATIN);
  300. std::set<char32_t> charset;
  301. charset.emplace(U'');
  302. charset.emplace(U'');
  303. labelTypeface->loadCharset(labelFont, UnicodeRange::BASIC_LATIN);
  304. labelTypeface->loadCharset(labelFont, charset);
  305. }
  306. catch (const std::exception& e)
  307. {
  308. std::cerr << "Failed to load one or more fonts: \"" << e.what() << "\"" << std::endl;
  309. close(EXIT_FAILURE);
  310. }
  311. Shader* shader = resourceManager->load<Shader>("depth-pass.glsl");
  312. /*
  313. VertexFormat format;
  314. format.addAttribute<Vector4>(0);
  315. format.addAttribute<Vector3>(1);
  316. format.addAttribute<Vector3>(2);
  317. VertexBuffer* vb = graphicsContext->createVertexBuffer(format);
  318. vb->resize(1000);
  319. vb->setData(bla, 0, 1000);
  320. IndexBuffer* ib = graphicsContext->createIndexBuffer();
  321. ib->resize(300);
  322. ib->setData(bla, 0, 300);
  323. graphicsContext->bind(framebuffer);
  324. graphicsContext->bind(shader);
  325. graphicsContext->bind(vb);
  326. graphicsContext->bind(ib);
  327. graphicsContext->draw(100, TRIANGLES);
  328. */
  329. cameraRig = nullptr;
  330. orbitCam = new OrbitCam();
  331. orbitCam->attachCamera(&camera);
  332. freeCam = new FreeCam();
  333. freeCam->attachCamera(&camera);
  334. silhouetteRenderTarget.width = w;
  335. silhouetteRenderTarget.height = h;
  336. // Silhouette framebuffer texture
  337. glGenTextures(1, &silhouetteRenderTarget.texture);
  338. glBindTexture(GL_TEXTURE_2D, silhouetteRenderTarget.texture);
  339. glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, w, h, 0, GL_RED, GL_UNSIGNED_BYTE, nullptr);
  340. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
  341. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
  342. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
  343. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
  344. // Generate framebuffer
  345. glGenFramebuffers(1, &silhouetteRenderTarget.framebuffer);
  346. glBindFramebuffer(GL_FRAMEBUFFER, silhouetteRenderTarget.framebuffer);
  347. // Attach textures to framebuffer
  348. glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, silhouetteRenderTarget.texture, 0);
  349. glDrawBuffer(GL_COLOR_ATTACHMENT0);
  350. glReadBuffer(GL_NONE);
  351. // Unbind framebuffer and texture
  352. glBindTexture(GL_TEXTURE_2D, 0);
  353. glBindFramebuffer(GL_FRAMEBUFFER, 0);
  354. // Setup rendering
  355. defaultRenderTarget.width = w;
  356. defaultRenderTarget.height = h;
  357. defaultRenderTarget.framebuffer = 0;
  358. clearPass = new ClearRenderPass();
  359. clearPass->setRenderTarget(&defaultRenderTarget);
  360. clearPass->setClear(true, true, false);
  361. clearPass->setClearColor(Vector4(0.0f));
  362. clearPass->setClearDepth(1.0f);
  363. skyPass = new SkyRenderPass(resourceManager);
  364. skyPass->setRenderTarget(&defaultRenderTarget);
  365. uiPass = new UIRenderPass(resourceManager);
  366. uiPass->setRenderTarget(&defaultRenderTarget);
  367. uiCompositor.addPass(uiPass);
  368. uiCompositor.load(nullptr);
  369. // Setup UI batching
  370. uiBatch = new BillboardBatch();
  371. uiBatch->resize(1024);
  372. uiBatcher = new UIBatcher();
  373. // Setup root UI element
  374. uiRootElement = new UIContainer();
  375. eventDispatcher.subscribe<MouseMovedEvent>(uiRootElement);
  376. eventDispatcher.subscribe<MouseButtonPressedEvent>(uiRootElement);
  377. eventDispatcher.subscribe<MouseButtonReleasedEvent>(uiRootElement);
  378. // Create splash screen background element
  379. splashBackgroundImage = new UIImage();
  380. splashBackgroundImage->setLayerOffset(-1);
  381. splashBackgroundImage->setTintColor(Vector4(0.0f, 0.0f, 0.0f, 1.0f));
  382. splashBackgroundImage->setVisible(false);
  383. uiRootElement->addChild(splashBackgroundImage);
  384. // Create splash screen element
  385. splashImage = new UIImage();
  386. splashImage->setTexture(splashTexture);
  387. splashImage->setVisible(false);
  388. uiRootElement->addChild(splashImage);
  389. Rect hudTextureAtlasBounds(Vector2(0), Vector2(hudSpriteSheetTexture->getWidth(), hudSpriteSheetTexture->getHeight()));
  390. auto normalizeTextureBounds = [](const Rect& texture, const Rect& atlas)
  391. {
  392. Vector2 atlasDimensions = Vector2(atlas.getWidth(), atlas.getHeight());
  393. return Rect(texture.getMin() / atlasDimensions, texture.getMax() / atlasDimensions);
  394. };
  395. // Create HUD elements
  396. hudContainer = new UIContainer();
  397. hudContainer->setVisible(false);
  398. uiRootElement->addChild(hudContainer);
  399. toolIndicatorBGImage = new UIImage();
  400. toolIndicatorBGImage->setTexture(hudSpriteSheetTexture);
  401. toolIndicatorBGImage->setTextureBounds(normalizeTextureBounds(hudTextureAtlas.getBounds("tool-indicator"), hudTextureAtlasBounds));
  402. hudContainer->addChild(toolIndicatorBGImage);
  403. toolIndicatorsBounds = new Rect[8];
  404. toolIndicatorsBounds[0] = normalizeTextureBounds(hudTextureAtlas.getBounds("tool-indicator-brush"), hudTextureAtlasBounds);
  405. toolIndicatorsBounds[1] = normalizeTextureBounds(hudTextureAtlas.getBounds("tool-indicator-spade"), hudTextureAtlasBounds);
  406. toolIndicatorsBounds[2] = normalizeTextureBounds(hudTextureAtlas.getBounds("tool-indicator-lens"), hudTextureAtlasBounds);
  407. toolIndicatorsBounds[3] = normalizeTextureBounds(hudTextureAtlas.getBounds("tool-indicator-test-tube"), hudTextureAtlasBounds);
  408. toolIndicatorsBounds[4] = normalizeTextureBounds(hudTextureAtlas.getBounds("tool-indicator-forceps"), hudTextureAtlasBounds);
  409. toolIndicatorsBounds[5] = normalizeTextureBounds(hudTextureAtlas.getBounds("tool-indicator"), hudTextureAtlasBounds);
  410. toolIndicatorsBounds[6] = normalizeTextureBounds(hudTextureAtlas.getBounds("tool-indicator"), hudTextureAtlasBounds);
  411. toolIndicatorsBounds[7] = normalizeTextureBounds(hudTextureAtlas.getBounds("tool-indicator"), hudTextureAtlasBounds);
  412. toolIndicatorIconImage = new UIImage();
  413. toolIndicatorIconImage->setTexture(hudSpriteSheetTexture);
  414. toolIndicatorIconImage->setTextureBounds(normalizeTextureBounds(hudTextureAtlas.getBounds("tool-icon-brush"), hudTextureAtlasBounds));
  415. toolIndicatorBGImage->addChild(toolIndicatorIconImage);
  416. buttonContainer = new UIContainer();
  417. hudContainer->addChild(buttonContainer);
  418. playButtonBGImage = new UIImage();
  419. playButtonBGImage->setTexture(hudSpriteSheetTexture);
  420. playButtonBGImage->setTextureBounds(normalizeTextureBounds(hudTextureAtlas.getBounds("button-background"), hudTextureAtlasBounds));
  421. //buttonContainer->addChild(playButtonBGImage);
  422. pauseButtonBGImage = new UIImage();
  423. pauseButtonBGImage->setTexture(hudSpriteSheetTexture);
  424. pauseButtonBGImage->setTextureBounds(normalizeTextureBounds(hudTextureAtlas.getBounds("button-background"), hudTextureAtlasBounds));
  425. //buttonContainer->addChild(pauseButtonBGImage);
  426. fastForwardButtonBGImage = new UIImage();
  427. fastForwardButtonBGImage->setTexture(hudSpriteSheetTexture);
  428. fastForwardButtonBGImage->setTextureBounds(normalizeTextureBounds(hudTextureAtlas.getBounds("button-background"), hudTextureAtlasBounds));
  429. //buttonContainer->addChild(fastForwardButtonBGImage);
  430. playButtonImage = new UIImage();
  431. playButtonImage->setTexture(hudSpriteSheetTexture);
  432. playButtonImage->setTextureBounds(normalizeTextureBounds(hudTextureAtlas.getBounds("button-play"), hudTextureAtlasBounds));
  433. //buttonContainer->addChild(playButtonImage);
  434. fastForwardButtonImage = new UIImage();
  435. fastForwardButtonImage->setTexture(hudSpriteSheetTexture);
  436. fastForwardButtonImage->setTextureBounds(normalizeTextureBounds(hudTextureAtlas.getBounds("button-fast-forward-2x"), hudTextureAtlasBounds));
  437. //buttonContainer->addChild(fastForwardButtonImage);
  438. pauseButtonImage = new UIImage();
  439. pauseButtonImage->setTexture(hudSpriteSheetTexture);
  440. pauseButtonImage->setTextureBounds(normalizeTextureBounds(hudTextureAtlas.getBounds("button-pause"), hudTextureAtlasBounds));
  441. //buttonContainer->addChild(pauseButtonImage);
  442. radialMenuContainer = new UIContainer();
  443. radialMenuContainer->setVisible(false);
  444. uiRootElement->addChild(radialMenuContainer);
  445. radialMenuBackgroundImage = new UIImage();
  446. radialMenuBackgroundImage->setTintColor(Vector4(Vector3(0.0f), 0.25f));
  447. radialMenuContainer->addChild(radialMenuBackgroundImage);
  448. radialMenuImage = new UIImage();
  449. radialMenuImage->setTexture(hudSpriteSheetTexture);
  450. radialMenuImage->setTextureBounds(normalizeTextureBounds(hudTextureAtlas.getBounds("radial-menu"), hudTextureAtlasBounds));
  451. radialMenuContainer->addChild(radialMenuImage);
  452. radialMenuSelectorImage = new UIImage();
  453. radialMenuSelectorImage->setTexture(hudSpriteSheetTexture);
  454. radialMenuSelectorImage->setTextureBounds(normalizeTextureBounds(hudTextureAtlas.getBounds("radial-menu-selector"), hudTextureAtlasBounds));
  455. radialMenuContainer->addChild(radialMenuSelectorImage);
  456. toolIconBrushImage = new UIImage();
  457. toolIconBrushImage->setTexture(hudSpriteSheetTexture);
  458. toolIconBrushImage->setTextureBounds(normalizeTextureBounds(hudTextureAtlas.getBounds("tool-icon-brush"), hudTextureAtlasBounds));
  459. radialMenuImage->addChild(toolIconBrushImage);
  460. toolIconLensImage = new UIImage();
  461. toolIconLensImage->setTexture(hudSpriteSheetTexture);
  462. toolIconLensImage->setTextureBounds(normalizeTextureBounds(hudTextureAtlas.getBounds("tool-icon-lens"), hudTextureAtlasBounds));
  463. radialMenuImage->addChild(toolIconLensImage);
  464. toolIconForcepsImage = new UIImage();
  465. toolIconForcepsImage->setTexture(hudSpriteSheetTexture);
  466. toolIconForcepsImage->setTextureBounds(normalizeTextureBounds(hudTextureAtlas.getBounds("tool-icon-forceps"), hudTextureAtlasBounds));
  467. radialMenuImage->addChild(toolIconForcepsImage);
  468. toolIconSpadeImage = new UIImage();
  469. toolIconSpadeImage->setTexture(hudSpriteSheetTexture);
  470. toolIconSpadeImage->setTextureBounds(normalizeTextureBounds(hudTextureAtlas.getBounds("tool-icon-spade"), hudTextureAtlasBounds));
  471. //radialMenuImage->addChild(toolIconSpadeImage);
  472. toolIconCameraImage = new UIImage();
  473. toolIconCameraImage->setTexture(hudSpriteSheetTexture);
  474. toolIconCameraImage->setTextureBounds(normalizeTextureBounds(hudTextureAtlas.getBounds("tool-icon-camera"), hudTextureAtlasBounds));
  475. radialMenuImage->addChild(toolIconCameraImage);
  476. toolIconTestTubeImage = new UIImage();
  477. toolIconTestTubeImage->setTexture(hudSpriteSheetTexture);
  478. toolIconTestTubeImage->setTextureBounds(normalizeTextureBounds(hudTextureAtlas.getBounds("tool-icon-test-tube"), hudTextureAtlasBounds));
  479. //radialMenuImage->addChild(toolIconTestTubeImage);
  480. antTag = new UIContainer();
  481. antTag->setLayerOffset(-10);
  482. antTag->setVisible(false);
  483. uiRootElement->addChild(antTag);
  484. antLabelContainer = new UIContainer();
  485. antTag->addChild(antLabelContainer);
  486. antLabelTL = new UIImage();
  487. antLabelTR = new UIImage();
  488. antLabelBL = new UIImage();
  489. antLabelBR = new UIImage();
  490. antLabelCC = new UIImage();
  491. antLabelCT = new UIImage();
  492. antLabelCB = new UIImage();
  493. antLabelCL = new UIImage();
  494. antLabelCR = new UIImage();
  495. antLabelTL->setTexture(hudSpriteSheetTexture);
  496. antLabelTR->setTexture(hudSpriteSheetTexture);
  497. antLabelBL->setTexture(hudSpriteSheetTexture);
  498. antLabelBR->setTexture(hudSpriteSheetTexture);
  499. antLabelCC->setTexture(hudSpriteSheetTexture);
  500. antLabelCT->setTexture(hudSpriteSheetTexture);
  501. antLabelCB->setTexture(hudSpriteSheetTexture);
  502. antLabelCL->setTexture(hudSpriteSheetTexture);
  503. antLabelCR->setTexture(hudSpriteSheetTexture);
  504. Rect labelTLBounds = normalizeTextureBounds(hudTextureAtlas.getBounds("label-tl"), hudTextureAtlasBounds);
  505. Rect labelTRBounds = normalizeTextureBounds(hudTextureAtlas.getBounds("label-tr"), hudTextureAtlasBounds);
  506. Rect labelBLBounds = normalizeTextureBounds(hudTextureAtlas.getBounds("label-bl"), hudTextureAtlasBounds);
  507. Rect labelBRBounds = normalizeTextureBounds(hudTextureAtlas.getBounds("label-br"), hudTextureAtlasBounds);
  508. Rect labelCCBounds = normalizeTextureBounds(hudTextureAtlas.getBounds("label-cc"), hudTextureAtlasBounds);
  509. Rect labelCTBounds = normalizeTextureBounds(hudTextureAtlas.getBounds("label-ct"), hudTextureAtlasBounds);
  510. Rect labelCBBounds = normalizeTextureBounds(hudTextureAtlas.getBounds("label-cb"), hudTextureAtlasBounds);
  511. Rect labelCLBounds = normalizeTextureBounds(hudTextureAtlas.getBounds("label-cl"), hudTextureAtlasBounds);
  512. Rect labelCRBounds = normalizeTextureBounds(hudTextureAtlas.getBounds("label-cr"), hudTextureAtlasBounds);
  513. Vector2 labelTLMin = labelTLBounds.getMin();
  514. Vector2 labelTRMin = labelTRBounds.getMin();
  515. Vector2 labelBLMin = labelBLBounds.getMin();
  516. Vector2 labelBRMin = labelBRBounds.getMin();
  517. Vector2 labelCCMin = labelCCBounds.getMin();
  518. Vector2 labelCTMin = labelCTBounds.getMin();
  519. Vector2 labelCBMin = labelCBBounds.getMin();
  520. Vector2 labelCLMin = labelCLBounds.getMin();
  521. Vector2 labelCRMin = labelCRBounds.getMin();
  522. Vector2 labelTLMax = labelTLBounds.getMax();
  523. Vector2 labelTRMax = labelTRBounds.getMax();
  524. Vector2 labelBLMax = labelBLBounds.getMax();
  525. Vector2 labelBRMax = labelBRBounds.getMax();
  526. Vector2 labelCCMax = labelCCBounds.getMax();
  527. Vector2 labelCTMax = labelCTBounds.getMax();
  528. Vector2 labelCBMax = labelCBBounds.getMax();
  529. Vector2 labelCLMax = labelCLBounds.getMax();
  530. Vector2 labelCRMax = labelCRBounds.getMax();
  531. antLabelTL->setTextureBounds(labelTLBounds);
  532. antLabelTR->setTextureBounds(labelTRBounds);
  533. antLabelBL->setTextureBounds(labelBLBounds);
  534. antLabelBR->setTextureBounds(labelBRBounds);
  535. antLabelCC->setTextureBounds(labelCCBounds);
  536. antLabelCT->setTextureBounds(labelCTBounds);
  537. antLabelCB->setTextureBounds(labelCBBounds);
  538. antLabelCL->setTextureBounds(labelCLBounds);
  539. antLabelCR->setTextureBounds(labelCRBounds);
  540. antLabelContainer->addChild(antLabelTL);
  541. antLabelContainer->addChild(antLabelTR);
  542. antLabelContainer->addChild(antLabelBL);
  543. antLabelContainer->addChild(antLabelBR);
  544. antLabelContainer->addChild(antLabelCC);
  545. antLabelContainer->addChild(antLabelCT);
  546. antLabelContainer->addChild(antLabelCB);
  547. antLabelContainer->addChild(antLabelCL);
  548. antLabelContainer->addChild(antLabelCR);
  549. antLabel = new UILabel();
  550. antLabel->setFont(labelFont);
  551. antLabel->setText("Boggy B.");
  552. antLabel->setTintColor(Vector4(Vector3(0.0f), 1.0f));
  553. antLabel->setLayerOffset(1);
  554. antLabelContainer->addChild(antLabel);
  555. fpsLabel = new UILabel();
  556. fpsLabel->setFont(debugFont);
  557. fpsLabel->setTintColor(Vector4(1, 1, 0, 1));
  558. fpsLabel->setLayerOffset(50);
  559. fpsLabel->setAnchor(Anchor::TOP_LEFT);
  560. uiRootElement->addChild(fpsLabel);
  561. antPin = new UIImage();
  562. antPin->setTexture(hudSpriteSheetTexture);
  563. antPin->setTextureBounds(normalizeTextureBounds(hudTextureAtlas.getBounds("label-pin"), hudTextureAtlasBounds));
  564. antTag->addChild(antPin);
  565. antLabelPinHole = new UIImage();
  566. antLabelPinHole->setTexture(hudSpriteSheetTexture);
  567. antLabelPinHole->setTextureBounds(normalizeTextureBounds(hudTextureAtlas.getBounds("label-pin-hole"), hudTextureAtlasBounds));
  568. antLabelContainer->addChild(antLabelPinHole);
  569. notificationBoxImage = new UIImage();
  570. notificationBoxImage->setTexture(hudSpriteSheetTexture);
  571. notificationBoxImage->setTextureBounds(normalizeTextureBounds(hudTextureAtlas.getBounds("notification-box"), hudTextureAtlasBounds));
  572. notificationBoxImage->setVisible(false);
  573. hudContainer->addChild(notificationBoxImage);
  574. // Construct show notification animation clip
  575. notificationCount = 0;
  576. AnimationChannel<Vector2>* channel2;
  577. showNotificationClip.setInterpolator(easeOutQuint<Vector2>);
  578. channel2 = showNotificationClip.addChannel(0);
  579. channel2->insertKeyframe(0.0f, Vector2(0.0f, 0.0f));
  580. channel2->insertKeyframe(0.5f, Vector2(32.0f, 1.0f));
  581. showNotificationAnimation.setClip(&showNotificationClip);
  582. showNotificationAnimation.setSpeed(1.0f);
  583. showNotificationAnimation.setTimeFrame(showNotificationClip.getTimeFrame());
  584. animator.addAnimation(&showNotificationAnimation);
  585. showNotificationAnimation.setAnimateCallback
  586. (
  587. [this](std::size_t id, const Vector2& values)
  588. {
  589. notificationBoxImage->setVisible(true);
  590. notificationBoxImage->setTintColor(Vector4(Vector3(1.0f), values.y));
  591. //notificationBoxImage->setTranslation(Vector2(0.0f, -32.0f + values.x));
  592. }
  593. );
  594. showNotificationAnimation.setEndCallback
  595. (
  596. [this]()
  597. {
  598. hideNotificationAnimation.rewind();
  599. hideNotificationAnimation.play();
  600. }
  601. );
  602. // Construct hide notification animation clip
  603. hideNotificationClip.setInterpolator(easeOutQuint<float>);
  604. AnimationChannel<float>* channel;
  605. channel = hideNotificationClip.addChannel(0);
  606. channel->insertKeyframe(0.0f, 1.0f);
  607. channel->insertKeyframe(10.5f, 1.0f);
  608. channel->insertKeyframe(12.0f, 0.0f);
  609. hideNotificationAnimation.setClip(&hideNotificationClip);
  610. hideNotificationAnimation.setSpeed(1.0f);
  611. hideNotificationAnimation.setTimeFrame(hideNotificationClip.getTimeFrame());
  612. animator.addAnimation(&hideNotificationAnimation);
  613. hideNotificationAnimation.setAnimateCallback
  614. (
  615. [this](std::size_t id, float opacity)
  616. {
  617. notificationBoxImage->setTintColor(Vector4(Vector3(1.0f), opacity));
  618. }
  619. );
  620. hideNotificationAnimation.setEndCallback
  621. (
  622. [this]()
  623. {
  624. notificationBoxImage->setVisible(false);
  625. --notificationCount;
  626. popNotification();
  627. }
  628. );
  629. // Construct box selection
  630. boxSelectionImageBackground = new UIImage();
  631. boxSelectionImageBackground->setAnchor(Anchor::CENTER);
  632. boxSelectionImageTop = new UIImage();
  633. boxSelectionImageTop->setAnchor(Anchor::TOP_LEFT);
  634. boxSelectionImageBottom = new UIImage();
  635. boxSelectionImageBottom->setAnchor(Anchor::BOTTOM_LEFT);
  636. boxSelectionImageLeft = new UIImage();
  637. boxSelectionImageLeft->setAnchor(Anchor::TOP_LEFT);
  638. boxSelectionImageRight = new UIImage();
  639. boxSelectionImageRight->setAnchor(Anchor::TOP_RIGHT);
  640. boxSelectionContainer = new UIContainer();
  641. boxSelectionContainer->setLayerOffset(80);
  642. boxSelectionContainer->addChild(boxSelectionImageBackground);
  643. boxSelectionContainer->addChild(boxSelectionImageTop);
  644. boxSelectionContainer->addChild(boxSelectionImageBottom);
  645. boxSelectionContainer->addChild(boxSelectionImageLeft);
  646. boxSelectionContainer->addChild(boxSelectionImageRight);
  647. boxSelectionContainer->setVisible(false);
  648. uiRootElement->addChild(boxSelectionContainer);
  649. boxSelectionImageBackground->setTintColor(Vector4(1.0f, 1.0f, 1.0f, 0.5f));
  650. boxSelectionContainer->setTintColor(Vector4(1.0f, 0.0f, 0.0f, 1.0f));
  651. boxSelectionBorderWidth = 2.0f;
  652. cameraGridColor = Vector4(1, 1, 1, 0.5f);
  653. cameraGridY0Image = new UIImage();
  654. cameraGridY0Image->setAnchor(Vector2(0.5f, (1.0f / 3.0f)));
  655. cameraGridY0Image->setTintColor(cameraGridColor);
  656. cameraGridY1Image = new UIImage();
  657. cameraGridY1Image->setAnchor(Vector2(0.5f, (2.0f / 3.0f)));
  658. cameraGridY1Image->setTintColor(cameraGridColor);
  659. cameraGridX0Image = new UIImage();
  660. cameraGridX0Image->setAnchor(Vector2((1.0f / 3.0f), 0.5f));
  661. cameraGridX0Image->setTintColor(cameraGridColor);
  662. cameraGridX1Image = new UIImage();
  663. cameraGridX1Image->setAnchor(Vector2((2.0f / 3.0f), 0.5f));
  664. cameraGridX1Image->setTintColor(cameraGridColor);
  665. cameraGridContainer = new UIContainer();
  666. cameraGridContainer->addChild(cameraGridY0Image);
  667. cameraGridContainer->addChild(cameraGridY1Image);
  668. cameraGridContainer->addChild(cameraGridX0Image);
  669. cameraGridContainer->addChild(cameraGridX1Image);
  670. cameraGridContainer->setVisible(true);
  671. uiRootElement->addChild(cameraGridContainer);
  672. cameraFlashImage = new UIImage();
  673. cameraFlashImage->setLayerOffset(99);
  674. cameraFlashImage->setTintColor(Vector4(1.0f));
  675. cameraFlashImage->setVisible(false);
  676. uiRootElement->addChild(cameraFlashImage);
  677. blackoutImage = new UIImage();
  678. blackoutImage->setLayerOffset(98);
  679. blackoutImage->setTintColor(Vector4(0.0f, 0.0f, 0.0f, 1.0f));
  680. blackoutImage->setVisible(false);
  681. uiRootElement->addChild(blackoutImage);
  682. // Construct fade-in animation clip
  683. fadeInClip.setInterpolator(easeOutCubic<float>);
  684. channel = fadeInClip.addChannel(0);
  685. channel->insertKeyframe(0.0f, 1.0f);
  686. channel->insertKeyframe(1.0f, 0.0f);
  687. // Construct fade-out animation clip
  688. fadeOutClip.setInterpolator(easeOutCubic<float>);
  689. channel = fadeOutClip.addChannel(0);
  690. channel->insertKeyframe(0.0f, 0.0f);
  691. channel->insertKeyframe(1.0f, 1.0f);
  692. // Setup fade-in animation callbacks
  693. fadeInAnimation.setAnimateCallback
  694. (
  695. [this](std::size_t id, float opacity)
  696. {
  697. Vector3 color = Vector3(blackoutImage->getTintColor());
  698. blackoutImage->setTintColor(Vector4(color, opacity));
  699. }
  700. );
  701. fadeInAnimation.setEndCallback
  702. (
  703. [this]()
  704. {
  705. blackoutImage->setVisible(false);
  706. if (fadeInEndCallback != nullptr)
  707. {
  708. fadeInEndCallback();
  709. }
  710. }
  711. );
  712. // Setup fade-out animation callbacks
  713. fadeOutAnimation.setAnimateCallback
  714. (
  715. [this](std::size_t id, float opacity)
  716. {
  717. Vector3 color = Vector3(blackoutImage->getTintColor());
  718. blackoutImage->setTintColor(Vector4(color, opacity));
  719. }
  720. );
  721. fadeOutAnimation.setEndCallback
  722. (
  723. [this]()
  724. {
  725. blackoutImage->setVisible(false);
  726. if (fadeOutEndCallback != nullptr)
  727. {
  728. fadeOutEndCallback();
  729. }
  730. }
  731. );
  732. animator.addAnimation(&fadeInAnimation);
  733. animator.addAnimation(&fadeOutAnimation);
  734. // Construct camera flash animation clip
  735. cameraFlashClip.setInterpolator(easeOutQuad<float>);
  736. channel = cameraFlashClip.addChannel(0);
  737. channel->insertKeyframe(0.0f, 1.0f);
  738. channel->insertKeyframe(1.0f, 0.0f);
  739. // Setup camera flash animation
  740. float flashDuration = 0.5f;
  741. cameraFlashAnimation.setSpeed(1.0f / flashDuration);
  742. cameraFlashAnimation.setLoop(false);
  743. cameraFlashAnimation.setClip(&cameraFlashClip);
  744. cameraFlashAnimation.setTimeFrame(cameraFlashClip.getTimeFrame());
  745. cameraFlashAnimation.setAnimateCallback
  746. (
  747. [this](std::size_t id, float opacity)
  748. {
  749. cameraFlashImage->setTintColor(Vector4(Vector3(1.0f), opacity));
  750. }
  751. );
  752. cameraFlashAnimation.setStartCallback
  753. (
  754. [this]()
  755. {
  756. cameraFlashImage->setVisible(true);
  757. cameraFlashImage->setTintColor(Vector4(1.0f));
  758. cameraFlashImage->resetTweens();
  759. }
  760. );
  761. cameraFlashAnimation.setEndCallback
  762. (
  763. [this]()
  764. {
  765. cameraFlashImage->setVisible(false);
  766. }
  767. );
  768. animator.addAnimation(&cameraFlashAnimation);
  769. // Setup default compositor
  770. {
  771. defaultCompositor.load(nullptr);
  772. }
  773. // Setup shadow map pass and compositor
  774. {
  775. // Set shadow map resolution
  776. shadowMapResolution = 4096;
  777. // Generate shadow map framebuffer
  778. glGenFramebuffers(1, &shadowMapFramebuffer);
  779. glBindFramebuffer(GL_FRAMEBUFFER, shadowMapFramebuffer);
  780. // Generate shadow map depth texture
  781. glGenTextures(1, &shadowMapDepthTextureID);
  782. glBindTexture(GL_TEXTURE_2D, shadowMapDepthTextureID);
  783. glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, shadowMapResolution, shadowMapResolution, 0, GL_DEPTH_COMPONENT, GL_FLOAT, nullptr);
  784. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  785. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  786. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
  787. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
  788. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LESS);
  789. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
  790. // Attach depth texture to framebuffer
  791. glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, shadowMapDepthTextureID, 0);
  792. glDrawBuffer(GL_NONE);
  793. glReadBuffer(GL_NONE);
  794. // Unbind shadow map depth texture
  795. glBindTexture(GL_TEXTURE_2D, 0);
  796. glBindFramebuffer(GL_FRAMEBUFFER, 0);
  797. // Setup shadow map render target
  798. shadowMapRenderTarget.width = shadowMapResolution;
  799. shadowMapRenderTarget.height = shadowMapResolution;
  800. shadowMapRenderTarget.framebuffer = shadowMapFramebuffer;
  801. // Setup texture class
  802. shadowMapDepthTexture.setTextureID(shadowMapDepthTextureID);
  803. shadowMapDepthTexture.setWidth(shadowMapResolution);
  804. shadowMapDepthTexture.setHeight(shadowMapResolution);
  805. // Setup shadow map render pass
  806. shadowMapPass = new ShadowMapRenderPass(resourceManager);
  807. shadowMapPass->setRenderTarget(&shadowMapRenderTarget);
  808. shadowMapPass->setViewCamera(&camera);
  809. shadowMapPass->setLightCamera(&sunlightCamera);
  810. // Setup shadow map compositor
  811. shadowMapCompositor.addPass(shadowMapPass);
  812. shadowMapCompositor.load(nullptr);
  813. }
  814. // Setup scene
  815. {
  816. defaultLayer = scene->addLayer();
  817. // Setup lighting pass
  818. lightingPass = new LightingRenderPass(resourceManager);
  819. lightingPass->setRenderTarget(&defaultRenderTarget);
  820. lightingPass->setShadowMapPass(shadowMapPass);
  821. lightingPass->setShadowMap(&shadowMapDepthTexture);
  822. // Setup clear silhouette pass
  823. clearSilhouettePass = new ClearRenderPass();
  824. clearSilhouettePass->setRenderTarget(&silhouetteRenderTarget);
  825. clearSilhouettePass->setClear(true, false, false);
  826. clearSilhouettePass->setClearColor(Vector4(0.0f));
  827. // Setup silhouette pass
  828. silhouettePass = new SilhouetteRenderPass(resourceManager);
  829. silhouettePass->setRenderTarget(&silhouetteRenderTarget);
  830. // Setup final pass
  831. finalPass = new FinalRenderPass(resourceManager);
  832. finalPass->setRenderTarget(&defaultRenderTarget);
  833. finalPass->setSilhouetteRenderTarget(&silhouetteRenderTarget);
  834. // Setup default compositor
  835. defaultCompositor.addPass(clearPass);
  836. defaultCompositor.addPass(skyPass);
  837. defaultCompositor.addPass(lightingPass);
  838. defaultCompositor.addPass(clearSilhouettePass);
  839. defaultCompositor.addPass(silhouettePass);
  840. defaultCompositor.addPass(finalPass);
  841. defaultCompositor.load(nullptr);
  842. // Setup camera
  843. camera.setPerspective(glm::radians(40.0f), static_cast<float>(w) / static_cast<float>(h), 0.1, 100.0f);
  844. camera.lookAt(Vector3(0.0f, 4.0f, 2.0f), Vector3(0.0f, 0.0f, 0.0f), Vector3(0.0f, 1.0f, 0.0f));
  845. camera.setCompositor(&defaultCompositor);
  846. camera.setCompositeIndex(1);
  847. defaultLayer->addObject(&camera);
  848. // Setup sun
  849. sunlight.setDirection(Vector3(0, -1, 0));
  850. setTimeOfDay(11.0f);
  851. defaultLayer->addObject(&sunlight);
  852. // Setup sunlight camera
  853. sunlightCamera.setOrthographic(-1.0f, 1.0f, -1.0f, 1.0f, -1.0f, 1.0f);
  854. sunlightCamera.setCompositor(&shadowMapCompositor);
  855. sunlightCamera.setCompositeIndex(0);
  856. sunlightCamera.setCullingEnabled(true);
  857. sunlightCamera.setCullingMask(&camera.getViewFrustum());
  858. defaultLayer->addObject(&sunlightCamera);
  859. }
  860. // Setup UI scene
  861. uiLayer = scene->addLayer();
  862. uiLayer->addObject(uiBatch);
  863. uiLayer->addObject(&uiCamera);
  864. // Setup UI camera
  865. uiCamera.lookAt(Vector3(0), Vector3(0, 0, -1), Vector3(0, 1, 0));
  866. uiCamera.resetTweens();
  867. uiCamera.setCompositor(&uiCompositor);
  868. uiCamera.setCompositeIndex(0);
  869. uiCamera.setCullingEnabled(false);
  870. restringUI();
  871. resizeUI(w, h);
  872. time = 0.0f;
  873. // Tools
  874. currentTool = nullptr;
  875. lens = new Lens(lensModel, &animator);
  876. lens->setOrbitCam(orbitCam);
  877. defaultLayer->addObject(lens->getModelInstance());
  878. defaultLayer->addObject(lens->getSpotlight());
  879. lens->setSunDirection(-sunlightCamera.getForward());
  880. ModelInstance* modelInstance = lens->getModelInstance();
  881. for (std::size_t i = 0; i < modelInstance->getModel()->getGroupCount(); ++i)
  882. {
  883. Material* material = modelInstance->getModel()->getGroup(i)->material->clone();
  884. material->setFlags(material->getFlags() | 256);
  885. modelInstance->setMaterialSlot(i, material);
  886. }
  887. // Forceps
  888. forceps = new Forceps(forcepsModel, &animator);
  889. forceps->setOrbitCam(orbitCam);
  890. defaultLayer->addObject(forceps->getModelInstance());
  891. // Brush
  892. brush = new Brush(brushModel, &animator);
  893. brush->setOrbitCam(orbitCam);
  894. defaultLayer->addObject(brush->getModelInstance());
  895. glEnable(GL_MULTISAMPLE);
  896. //
  897. performanceSampler.setSampleSize(30);
  898. // Initialize component manager
  899. componentManager = new ComponentManager();
  900. // Initialize entity manager
  901. entityManager = new EntityManager(componentManager);
  902. // Initialize systems
  903. soundSystem = new SoundSystem(componentManager);
  904. collisionSystem = new CollisionSystem(componentManager);
  905. renderSystem = new RenderSystem(componentManager, defaultLayer);
  906. toolSystem = new ToolSystem(componentManager);
  907. toolSystem->setPickingCamera(&camera);
  908. toolSystem->setPickingViewport(Vector4(0, 0, w, h));
  909. eventDispatcher.subscribe<MouseMovedEvent>(toolSystem);
  910. behaviorSystem = new BehaviorSystem(componentManager);
  911. steeringSystem = new SteeringSystem(componentManager);
  912. locomotionSystem = new LocomotionSystem(componentManager);
  913. particleSystem = new ParticleSystem(componentManager);
  914. particleSystem->resize(1000);
  915. particleSystem->setMaterial(smokeMaterial);
  916. particleSystem->setDirection(Vector3(0, 1, 0));
  917. lens->setParticleSystem(particleSystem);
  918. particleSystem->getBillboardBatch()->setAlignment(&camera, BillboardAlignmentMode::SPHERICAL);
  919. defaultLayer->addObject(particleSystem->getBillboardBatch());
  920. // Initialize system manager
  921. systemManager = new SystemManager();
  922. systemManager->addSystem(soundSystem);
  923. systemManager->addSystem(behaviorSystem);
  924. systemManager->addSystem(steeringSystem);
  925. systemManager->addSystem(locomotionSystem);
  926. systemManager->addSystem(collisionSystem);
  927. systemManager->addSystem(toolSystem);
  928. systemManager->addSystem(particleSystem);
  929. systemManager->addSystem(renderSystem);
  930. EntityID sidewalkPanel;
  931. sidewalkPanel = createInstanceOf("sidewalk-panel");
  932. EntityID antHill = createInstanceOf("ant-hill");
  933. setTranslation(antHill, Vector3(20, 0, 40));
  934. EntityID antNest = createInstanceOf("ant-nest");
  935. setTranslation(antNest, Vector3(20, 0, 40));
  936. lollipop = createInstanceOf("lollipop");
  937. setTranslation(lollipop, Vector3(30.0f, 3.5f * 0.5f, -30.0f));
  938. setRotation(lollipop, glm::angleAxis(glm::radians(8.85f), Vector3(1.0f, 0.0f, 0.0f)));
  939. // Load navmesh
  940. TriangleMesh* navmesh = resourceManager->load<TriangleMesh>("sidewalk.mesh");
  941. // Find surface
  942. TriangleMesh::Triangle* surface = nullptr;
  943. Vector3 barycentricPosition;
  944. Ray ray;
  945. ray.origin = Vector3(0, 100, 0);
  946. ray.direction = Vector3(0, -1, 0);
  947. auto intersection = ray.intersects(*navmesh);
  948. if (std::get<0>(intersection))
  949. {
  950. surface = (*navmesh->getTriangles())[std::get<3>(intersection)];
  951. Vector3 position = ray.extrapolate(std::get<1>(intersection));
  952. Vector3 a = surface->edge->vertex->position;
  953. Vector3 b = surface->edge->next->vertex->position;
  954. Vector3 c = surface->edge->previous->vertex->position;
  955. barycentricPosition = barycentric(position, a, b, c);
  956. }
  957. for (int i = 0; i < 0; ++i)
  958. {
  959. EntityID ant = createInstanceOf("worker-ant");
  960. setTranslation(ant, Vector3(0.0f, 0, 0.0f));
  961. BehaviorComponent* behavior = new BehaviorComponent();
  962. SteeringComponent* steering = new SteeringComponent();
  963. LeggedLocomotionComponent* locomotion = new LeggedLocomotionComponent();
  964. componentManager->addComponent(ant, behavior);
  965. componentManager->addComponent(ant, steering);
  966. componentManager->addComponent(ant, locomotion);
  967. locomotion->surface = surface;
  968. behavior->wanderTriangle = surface;
  969. locomotion->barycentricPosition = barycentricPosition;
  970. }
  971. EntityID tool0 = createInstanceOf("lens");
  972. changeState(splashState);
  973. }
  974. void Game::input()
  975. {
  976. }
  977. void Game::update(float t, float dt)
  978. {
  979. this->time = t;
  980. // Dispatch scheduled events
  981. eventDispatcher.update(t);
  982. // Execute current state
  983. if (currentState != nullptr)
  984. {
  985. currentState->execute();
  986. }
  987. // Update systems
  988. systemManager->update(t, dt);
  989. // Update animations
  990. animator.animate(dt);
  991. std::stringstream stream;
  992. stream.precision(2);
  993. stream << std::fixed << (performanceSampler.getMeanFrameDuration() * 1000.0f);
  994. fpsLabel->setText(stream.str());
  995. uiRootElement->update();
  996. controlProfile.update();
  997. }
  998. void Game::render()
  999. {
  1000. // Perform sub-frame interpolation on UI elements
  1001. uiRootElement->interpolate(stepScheduler.getScheduledSubsteps());
  1002. // Update and batch UI elements
  1003. uiBatcher->batch(uiBatch, uiRootElement);
  1004. // Perform sub-frame interpolation particles
  1005. particleSystem->getBillboardBatch()->interpolate(stepScheduler.getScheduledSubsteps());
  1006. particleSystem->getBillboardBatch()->batch();
  1007. // Render scene
  1008. renderer.render(*scene);
  1009. // Swap window framebuffers
  1010. window->swapBuffers();
  1011. if (screenshotQueued)
  1012. {
  1013. screenshot();
  1014. screenshotQueued = false;
  1015. }
  1016. }
  1017. void Game::exit()
  1018. {
  1019. }
  1020. void Game::handleEvent(const WindowResizedEvent& event)
  1021. {
  1022. w = event.width;
  1023. h = event.height;
  1024. defaultRenderTarget.width = event.width;
  1025. defaultRenderTarget.height = event.height;
  1026. glViewport(0, 0, event.width, event.height);
  1027. camera.setPerspective(glm::radians(40.0f), static_cast<float>(w) / static_cast<float>(h), 0.1, 100.0f);
  1028. toolSystem->setPickingViewport(Vector4(0, 0, w, h));
  1029. resizeUI(event.width, event.height);
  1030. }
  1031. void Game::handleEvent(const KeyPressedEvent& event)
  1032. {
  1033. if (event.scancode == Scancode::SPACE)
  1034. {
  1035. changeLanguage((getCurrentLanguage() + 1) % getLanguageCount());
  1036. }
  1037. }
  1038. void Game::handleEvent(const TestEvent& event)
  1039. {
  1040. std::cout << "Event received!!! ID: " << event.id << std::endl;
  1041. }
  1042. void Game::resizeUI(int w, int h)
  1043. {
  1044. // Adjust root element dimensions
  1045. uiRootElement->setDimensions(Vector2(w, h));
  1046. uiRootElement->update();
  1047. splashBackgroundImage->setDimensions(Vector2(w, h));
  1048. splashBackgroundImage->setAnchor(Anchor::TOP_LEFT);
  1049. // Resize splash screen image
  1050. splashImage->setAnchor(Anchor::CENTER);
  1051. splashImage->setDimensions(Vector2(splashTexture->getWidth(), splashTexture->getHeight()));
  1052. // Adjust UI camera projection matrix
  1053. uiCamera.setOrthographic(0.0f, w, h, 0.0f, -1.0f, 1.0f);
  1054. uiCamera.resetTweens();
  1055. // Resize camera flash image
  1056. cameraFlashImage->setDimensions(Vector2(w, h));
  1057. cameraFlashImage->setAnchor(Anchor::CENTER);
  1058. // Resize blackout image
  1059. blackoutImage->setDimensions(Vector2(w, h));
  1060. blackoutImage->setAnchor(Anchor::CENTER);
  1061. // Resize HUD
  1062. float hudPadding = 20.0f;
  1063. hudContainer->setDimensions(Vector2(w - hudPadding * 2.0f, h - hudPadding * 2.0f));
  1064. hudContainer->setAnchor(Anchor::CENTER);
  1065. // Tool indicator
  1066. Rect toolIndicatorBounds = hudTextureAtlas.getBounds("tool-indicator");
  1067. toolIndicatorBGImage->setDimensions(Vector2(toolIndicatorBounds.getWidth(), toolIndicatorBounds.getHeight()));
  1068. toolIndicatorBGImage->setAnchor(Anchor::TOP_LEFT);
  1069. Rect toolIndicatorIconBounds = hudTextureAtlas.getBounds("tool-indicator-lens");
  1070. toolIndicatorIconImage->setDimensions(Vector2(toolIndicatorIconBounds.getWidth(), toolIndicatorIconBounds.getHeight()));
  1071. toolIndicatorIconImage->setAnchor(Anchor::CENTER);
  1072. // Buttons
  1073. Rect playButtonBounds = hudTextureAtlas.getBounds("button-play");
  1074. Rect fastForwardButtonBounds = hudTextureAtlas.getBounds("button-fast-forward-2x");
  1075. Rect pauseButtonBounds = hudTextureAtlas.getBounds("button-pause");
  1076. Rect buttonBackgroundBounds = hudTextureAtlas.getBounds("button-background");
  1077. Vector2 buttonBGDimensions = Vector2(buttonBackgroundBounds.getWidth(), buttonBackgroundBounds.getHeight());
  1078. float buttonMargin = 10.0f;
  1079. float buttonDepth = 15.0f;
  1080. float buttonContainerWidth = fastForwardButtonBounds.getWidth();
  1081. float buttonContainerHeight = fastForwardButtonBounds.getHeight();
  1082. buttonContainer->setDimensions(Vector2(buttonContainerWidth, buttonContainerHeight));
  1083. buttonContainer->setAnchor(Anchor::TOP_RIGHT);
  1084. playButtonImage->setDimensions(Vector2(playButtonBounds.getWidth(), playButtonBounds.getHeight()));
  1085. playButtonImage->setAnchor(Vector2(0.0f, 0.0f));
  1086. playButtonBGImage->setDimensions(buttonBGDimensions);
  1087. playButtonBGImage->setAnchor(Vector2(0.0f, 1.0f));
  1088. fastForwardButtonImage->setDimensions(Vector2(fastForwardButtonBounds.getWidth(), fastForwardButtonBounds.getHeight()));
  1089. fastForwardButtonImage->setAnchor(Vector2(0.5f, 5.0f));
  1090. fastForwardButtonBGImage->setDimensions(buttonBGDimensions);
  1091. fastForwardButtonBGImage->setAnchor(Vector2(0.5f, 0.5f));
  1092. pauseButtonImage->setDimensions(Vector2(pauseButtonBounds.getWidth(), pauseButtonBounds.getHeight()));
  1093. pauseButtonImage->setAnchor(Vector2(1.0f, 0.0f));
  1094. pauseButtonBGImage->setDimensions(buttonBGDimensions);
  1095. pauseButtonBGImage->setAnchor(Vector2(1.0f, 1.0f));
  1096. // Radial menu
  1097. Rect radialMenuBounds = hudTextureAtlas.getBounds("radial-menu");
  1098. radialMenuContainer->setDimensions(Vector2(w, h));
  1099. radialMenuContainer->setAnchor(Anchor::CENTER);
  1100. radialMenuContainer->setLayerOffset(30);
  1101. radialMenuBackgroundImage->setDimensions(Vector2(w, h));
  1102. radialMenuBackgroundImage->setAnchor(Anchor::CENTER);
  1103. radialMenuBackgroundImage->setLayerOffset(-1);
  1104. //radialMenuImage->setDimensions(Vector2(w * 0.5f, h * 0.5f));
  1105. radialMenuImage->setDimensions(Vector2(radialMenuBounds.getWidth(), radialMenuBounds.getHeight()));
  1106. radialMenuImage->setAnchor(Anchor::CENTER);
  1107. Rect radialMenuSelectorBounds = hudTextureAtlas.getBounds("radial-menu-selector");
  1108. radialMenuSelectorImage->setDimensions(Vector2(radialMenuSelectorBounds.getWidth(), radialMenuSelectorBounds.getHeight()));
  1109. radialMenuSelectorImage->setAnchor(Anchor::CENTER);
  1110. Rect toolIconBrushBounds = hudTextureAtlas.getBounds("tool-icon-brush");
  1111. toolIconBrushImage->setDimensions(Vector2(toolIconBrushBounds.getWidth(), toolIconBrushBounds.getHeight()));
  1112. toolIconBrushImage->setAnchor(Anchor::CENTER);
  1113. Rect toolIconLensBounds = hudTextureAtlas.getBounds("tool-icon-lens");
  1114. toolIconLensImage->setDimensions(Vector2(toolIconLensBounds.getWidth(), toolIconLensBounds.getHeight()));
  1115. toolIconLensImage->setAnchor(Anchor::CENTER);
  1116. Rect toolIconForcepsBounds = hudTextureAtlas.getBounds("tool-icon-forceps");
  1117. toolIconForcepsImage->setDimensions(Vector2(toolIconForcepsBounds.getWidth(), toolIconForcepsBounds.getHeight()));
  1118. toolIconForcepsImage->setAnchor(Anchor::CENTER);
  1119. Rect toolIconSpadeBounds = hudTextureAtlas.getBounds("tool-icon-spade");
  1120. toolIconSpadeImage->setDimensions(Vector2(toolIconSpadeBounds.getWidth(), toolIconSpadeBounds.getHeight()));
  1121. toolIconSpadeImage->setAnchor(Anchor::CENTER);
  1122. Rect toolIconCameraBounds = hudTextureAtlas.getBounds("tool-icon-camera");
  1123. toolIconCameraImage->setDimensions(Vector2(toolIconCameraBounds.getWidth(), toolIconCameraBounds.getHeight()));
  1124. toolIconCameraImage->setAnchor(Anchor::CENTER);
  1125. Rect toolIconTestTubeBounds = hudTextureAtlas.getBounds("tool-icon-test-tube");
  1126. toolIconTestTubeImage->setDimensions(Vector2(toolIconTestTubeBounds.getWidth(), toolIconTestTubeBounds.getHeight()));
  1127. toolIconTestTubeImage->setAnchor(Anchor::CENTER);
  1128. Rect labelCornerBounds = hudTextureAtlas.getBounds("label-tl");
  1129. Vector2 labelCornerDimensions(labelCornerBounds.getWidth(), labelCornerBounds.getHeight());
  1130. Vector2 antLabelPadding(10.0f, 6.0f);
  1131. antLabelContainer->setDimensions(antLabel->getDimensions() + antLabelPadding * 2.0f);
  1132. antLabelContainer->setTranslation(Vector2(0.0f, (int)(-antPin->getDimensions().y * 0.125f)));
  1133. antLabelTL->setDimensions(labelCornerDimensions);
  1134. antLabelTR->setDimensions(labelCornerDimensions);
  1135. antLabelBL->setDimensions(labelCornerDimensions);
  1136. antLabelBR->setDimensions(labelCornerDimensions);
  1137. antLabelCC->setDimensions(Vector2(antLabel->getDimensions().x - labelCornerDimensions.x * 2.0f + antLabelPadding.x * 2.0f, antLabel->getDimensions().y - labelCornerDimensions.y * 2.0f + antLabelPadding.y * 2.0f));
  1138. antLabelCT->setDimensions(Vector2(antLabel->getDimensions().x - labelCornerDimensions.x * 2.0f + antLabelPadding.x * 2.0f, labelCornerDimensions.y));
  1139. antLabelCB->setDimensions(Vector2(antLabel->getDimensions().x - labelCornerDimensions.x * 2.0f + antLabelPadding.x * 2.0f, labelCornerDimensions.y));
  1140. antLabelCL->setDimensions(Vector2(labelCornerDimensions.x, antLabel->getDimensions().y - labelCornerDimensions.y * 2.0f + antLabelPadding.y * 2.0f));
  1141. antLabelCR->setDimensions(Vector2(labelCornerDimensions.x, antLabel->getDimensions().y - labelCornerDimensions.y * 2.0f + antLabelPadding.y * 2.0f));
  1142. antLabelContainer->setAnchor(Vector2(0.5f, 0.5f));
  1143. antLabelTL->setAnchor(Anchor::TOP_LEFT);
  1144. antLabelTR->setAnchor(Anchor::TOP_RIGHT);
  1145. antLabelBL->setAnchor(Anchor::BOTTOM_LEFT);
  1146. antLabelBR->setAnchor(Anchor::BOTTOM_RIGHT);
  1147. antLabelCC->setAnchor(Anchor::CENTER);
  1148. antLabelCT->setAnchor(Vector2(0.5f, 0.0f));
  1149. antLabelCB->setAnchor(Vector2(0.5f, 1.0f));
  1150. antLabelCL->setAnchor(Vector2(0.0f, 0.5f));
  1151. antLabelCR->setAnchor(Vector2(1.0f, 0.5f));
  1152. antLabel->setAnchor(Anchor::CENTER);
  1153. Rect antPinBounds = hudTextureAtlas.getBounds("label-pin");
  1154. antPin->setDimensions(Vector2(antPinBounds.getWidth(), antPinBounds.getHeight()));
  1155. antPin->setAnchor(Vector2(0.5f, 1.0f));
  1156. Rect pinHoleBounds = hudTextureAtlas.getBounds("label-pin-hole");
  1157. antLabelPinHole->setDimensions(Vector2(pinHoleBounds.getWidth(), pinHoleBounds.getHeight()));
  1158. antLabelPinHole->setAnchor(Vector2(0.5f, 0.0f));
  1159. antLabelPinHole->setTranslation(Vector2(0.0f, -antLabelPinHole->getDimensions().y * 0.5f));
  1160. antLabelPinHole->setLayerOffset(2);
  1161. float pinDistance = 20.0f;
  1162. antTag->setAnchor(Anchor::CENTER);
  1163. antTag->setDimensions(Vector2(antLabelContainer->getDimensions().x, antPin->getDimensions().y));
  1164. Rect notificationBoxBounds = hudTextureAtlas.getBounds("notification-box");
  1165. notificationBoxImage->setDimensions(Vector2(notificationBoxBounds.getWidth(), notificationBoxBounds.getHeight()));
  1166. notificationBoxImage->setAnchor(Vector2(0.5f, 0.0f));
  1167. float cameraGridLineWidth = 2.0f;
  1168. cameraGridContainer->setDimensions(Vector2(w, h));
  1169. cameraGridY0Image->setDimensions(Vector2(w, cameraGridLineWidth));
  1170. cameraGridY1Image->setDimensions(Vector2(w, cameraGridLineWidth));
  1171. cameraGridX0Image->setDimensions(Vector2(cameraGridLineWidth, h));
  1172. cameraGridX1Image->setDimensions(Vector2(cameraGridLineWidth, h));
  1173. cameraGridY0Image->setTranslation(Vector2(0));
  1174. cameraGridY1Image->setTranslation(Vector2(0));
  1175. cameraGridX0Image->setTranslation(Vector2(0));
  1176. cameraGridX1Image->setTranslation(Vector2(0));
  1177. UIImage* icons[] =
  1178. {
  1179. toolIconBrushImage,
  1180. nullptr,
  1181. toolIconLensImage,
  1182. nullptr,
  1183. toolIconForcepsImage,
  1184. nullptr,
  1185. toolIconCameraImage,
  1186. nullptr
  1187. };
  1188. Rect radialMenuIconRingBounds = hudTextureAtlas.getBounds("radial-menu-icon-ring");
  1189. float iconOffset = radialMenuIconRingBounds.getWidth() * 0.5f;
  1190. float sectorAngle = (2.0f * 3.14159264f) / 8.0f;
  1191. for (int i = 0; i < 8; ++i)
  1192. {
  1193. float angle = sectorAngle * static_cast<float>(i - 4);
  1194. Vector2 translation = Vector2(std::cos(angle), std::sin(angle)) * iconOffset;
  1195. translation.x = (int)(translation.x + 0.5f);
  1196. translation.y = (int)(translation.y + 0.5f);
  1197. if (icons[i] != nullptr)
  1198. {
  1199. icons[i]->setTranslation(translation);
  1200. }
  1201. }
  1202. }
  1203. void Game::restringUI()
  1204. {
  1205. }
  1206. void Game::setTimeOfDay(float time)
  1207. {
  1208. Vector3 midnight = Vector3(0.0f, 1.0f, 0.0f);
  1209. Vector3 sunrise = Vector3(-1.0f, 0.0f, 0.0f);
  1210. Vector3 noon = Vector3(0, -1.0f, 0.0f);
  1211. Vector3 sunset = Vector3(1.0f, 0.0f, 0.0f);
  1212. float angles[4] =
  1213. {
  1214. glm::radians(270.0f), // 00:00
  1215. glm::radians(0.0f), // 06:00
  1216. glm::radians(90.0f), // 12:00
  1217. glm::radians(180.0f) // 18:00
  1218. };
  1219. int index0 = static_cast<int>(fmod(time, 24.0f) / 6.0f);
  1220. int index1 = (index0 + 1) % 4;
  1221. float t = (time - (static_cast<float>(index0) * 6.0f)) / 6.0f;
  1222. Quaternion rotation0 = glm::angleAxis(angles[index0], Vector3(1, 0, 0));
  1223. Quaternion rotation1 = glm::angleAxis(angles[index1], Vector3(1, 0, 0));
  1224. Quaternion rotation = glm::normalize(glm::slerp(rotation0, rotation1, t));
  1225. Vector3 direction = glm::normalize(rotation * Vector3(0, 0, 1));
  1226. sunlight.setDirection(direction);
  1227. Vector3 up = glm::normalize(rotation * Vector3(0, 1, 0));
  1228. sunlightCamera.lookAt(Vector3(0, 0, 0), sunlight.getDirection(), up);
  1229. }
  1230. void Game::toggleWireframe()
  1231. {
  1232. wireframe = !wireframe;
  1233. float width = (wireframe) ? 1.0f : 0.0f;
  1234. lightingPass->setWireframeLineWidth(width);
  1235. }
  1236. void Game::queueScreenshot()
  1237. {
  1238. screenshotQueued = true;
  1239. cameraFlashImage->setVisible(false);
  1240. cameraGridContainer->setVisible(false);
  1241. soundSystem->scrot();
  1242. }
  1243. void Game::screenshot()
  1244. {
  1245. screenshotQueued = false;
  1246. // Read pixel data from framebuffer
  1247. unsigned char* pixels = new unsigned char[w * h * 3];
  1248. glReadPixels(0, 0, w, h, GL_RGB, GL_UNSIGNED_BYTE, pixels);
  1249. // Get game title in current language
  1250. std::string title = getString(getCurrentLanguage(), "title");
  1251. // Convert title to lowercase
  1252. std::transform(title.begin(), title.end(), title.begin(), ::tolower);
  1253. // Get system time
  1254. auto now = std::chrono::system_clock::now();
  1255. std::time_t tt = std::chrono::system_clock::to_time_t(now);
  1256. std::size_t ms = (std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()) % 1000).count();
  1257. // Create screenshot directory if it doesn't exist
  1258. std::string screenshotDirectory = configPath + std::string("/screenshots/");
  1259. if (!pathExists(screenshotDirectory))
  1260. {
  1261. createDirectory(screenshotDirectory);
  1262. }
  1263. // Build screenshot file name
  1264. std::stringstream stream;
  1265. stream << screenshotDirectory;
  1266. stream << title;
  1267. stream << std::put_time(std::localtime(&tt), "-%Y%m%d-%H%M%S-");
  1268. stream << std::setfill('0') << std::setw(3) << ms;
  1269. stream << ".png";
  1270. std::string filename = stream.str();
  1271. // Write screenshot to file in separate thread
  1272. std::thread screenshotThread(Game::saveScreenshot, filename, w, h, pixels);
  1273. screenshotThread.detach();
  1274. // Play camera flash animation
  1275. cameraFlashAnimation.stop();
  1276. cameraFlashAnimation.rewind();
  1277. cameraFlashAnimation.play();
  1278. // Play camera shutter sound
  1279. // Restore camera UI visibility
  1280. cameraGridContainer->setVisible(true);
  1281. }
  1282. void Game::boxSelect(float x, float y, float w, float h)
  1283. {
  1284. boxSelectionContainer->setTranslation(Vector2(x, y));
  1285. boxSelectionContainer->setDimensions(Vector2(w, h));
  1286. boxSelectionImageBackground->setDimensions(Vector2(w, h));
  1287. boxSelectionImageTop->setDimensions(Vector2(w, boxSelectionBorderWidth));
  1288. boxSelectionImageBottom->setDimensions(Vector2(w, boxSelectionBorderWidth));
  1289. boxSelectionImageLeft->setDimensions(Vector2(boxSelectionBorderWidth, h));
  1290. boxSelectionImageRight->setDimensions(Vector2(boxSelectionBorderWidth, h));
  1291. boxSelectionContainer->setVisible(true);
  1292. }
  1293. void Game::fadeIn(float duration, const Vector3& color, std::function<void()> callback)
  1294. {
  1295. if (fadeInAnimation.isPlaying())
  1296. {
  1297. return;
  1298. }
  1299. fadeOutAnimation.stop();
  1300. this->fadeInEndCallback = callback;
  1301. blackoutImage->setTintColor(Vector4(color, 1.0f));
  1302. blackoutImage->setVisible(true);
  1303. fadeInAnimation.setSpeed(1.0f / duration);
  1304. fadeInAnimation.setLoop(false);
  1305. fadeInAnimation.setClip(&fadeInClip);
  1306. fadeInAnimation.setTimeFrame(fadeInClip.getTimeFrame());
  1307. fadeInAnimation.rewind();
  1308. fadeInAnimation.play();
  1309. blackoutImage->resetTweens();
  1310. uiRootElement->update();
  1311. }
  1312. void Game::fadeOut(float duration, const Vector3& color, std::function<void()> callback)
  1313. {
  1314. if (fadeOutAnimation.isPlaying())
  1315. {
  1316. return;
  1317. }
  1318. fadeInAnimation.stop();
  1319. this->fadeOutEndCallback = callback;
  1320. blackoutImage->setVisible(true);
  1321. blackoutImage->setTintColor(Vector4(color, 0.0f));
  1322. fadeOutAnimation.setSpeed(1.0f / duration);
  1323. fadeOutAnimation.setLoop(false);
  1324. fadeOutAnimation.setClip(&fadeOutClip);
  1325. fadeOutAnimation.setTimeFrame(fadeOutClip.getTimeFrame());
  1326. fadeOutAnimation.rewind();
  1327. fadeOutAnimation.play();
  1328. blackoutImage->resetTweens();
  1329. uiRootElement->update();
  1330. }
  1331. void Game::selectTool(int toolIndex)
  1332. {
  1333. Tool* tools[] =
  1334. {
  1335. brush,
  1336. nullptr,
  1337. lens,
  1338. nullptr,
  1339. forceps,
  1340. nullptr,
  1341. nullptr,
  1342. nullptr
  1343. };
  1344. Tool* nextTool = tools[toolIndex];
  1345. if (nextTool != currentTool)
  1346. {
  1347. if (currentTool)
  1348. {
  1349. currentTool->setActive(false);
  1350. currentTool->update(0.0f);
  1351. }
  1352. currentTool = nextTool;
  1353. if (currentTool)
  1354. {
  1355. currentTool->setActive(true);
  1356. }
  1357. else
  1358. {
  1359. pushNotification("Invalid tool.");
  1360. }
  1361. }
  1362. if (1)
  1363. {
  1364. toolIndicatorIconImage->setTextureBounds(toolIndicatorsBounds[toolIndex]);
  1365. toolIndicatorIconImage->setVisible(true);
  1366. }
  1367. else
  1368. {
  1369. toolIndicatorIconImage->setVisible(false);
  1370. }
  1371. }
  1372. void Game::pushNotification(const std::string& text)
  1373. {
  1374. std::cout << text << std::endl;
  1375. ++notificationCount;
  1376. if (notificationCount == 1)
  1377. {
  1378. popNotification();
  1379. }
  1380. }
  1381. void Game::popNotification()
  1382. {
  1383. if (notificationCount > 0)
  1384. {
  1385. showNotificationAnimation.rewind();
  1386. showNotificationAnimation.play();
  1387. }
  1388. }
  1389. EntityID Game::createInstanceOf(const std::string& templateName)
  1390. {
  1391. EntityTemplate* entityTemplate = resourceManager->load<EntityTemplate>(templateName + ".ent");
  1392. EntityID entity = entityManager->createEntity();
  1393. entityTemplate->apply(entity, componentManager);
  1394. return entity;
  1395. }
  1396. void Game::destroyInstance(EntityID entity)
  1397. {
  1398. entityManager->destroyEntity(entity);
  1399. }
  1400. void Game::setTranslation(EntityID entity, const Vector3& translation)
  1401. {
  1402. TransformComponent* component = static_cast<TransformComponent*>(componentManager->getComponent(entity, ComponentType::TRANSFORM));
  1403. if (!component)
  1404. {
  1405. return;
  1406. }
  1407. component->transform.translation = translation;
  1408. }
  1409. void Game::setRotation(EntityID entity, const Quaternion& rotation)
  1410. {
  1411. TransformComponent* component = static_cast<TransformComponent*>(componentManager->getComponent(entity, ComponentType::TRANSFORM));
  1412. if (!component)
  1413. {
  1414. return;
  1415. }
  1416. component->transform.rotation = rotation;
  1417. }
  1418. void Game::setScale(EntityID entity, const Vector3& scale)
  1419. {
  1420. TransformComponent* component = static_cast<TransformComponent*>(componentManager->getComponent(entity, ComponentType::TRANSFORM));
  1421. if (!component)
  1422. {
  1423. return;
  1424. }
  1425. component->transform.scale = scale;
  1426. }
  1427. void Game::saveScreenshot(const std::string& filename, unsigned int width, unsigned int height, unsigned char* pixels)
  1428. {
  1429. stbi_flip_vertically_on_write(1);
  1430. stbi_write_png(filename.c_str(), width, height, 3, pixels, width * 3);
  1431. delete[] pixels;
  1432. }