CScene.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. #include "WallpaperEngine/Render/Objects/CImage.h"
  2. #include "WallpaperEngine/Render/Objects/CParticle.h"
  3. #include "WallpaperEngine/Render/Objects/CSound.h"
  4. #include "WallpaperEngine/Render/WallpaperState.h"
  5. #include "CScene.h"
  6. #include "WallpaperEngine/Logging/Log.h"
  7. #include "WallpaperEngine/Data/Model/Wallpaper.h"
  8. #include "WallpaperEngine/Data/Parsers/ObjectParser.h"
  9. extern float g_Time;
  10. extern float g_TimeLast;
  11. using namespace WallpaperEngine;
  12. using namespace WallpaperEngine::Render;
  13. using namespace WallpaperEngine::Data::Model;
  14. using namespace WallpaperEngine::Data::Parsers;
  15. using namespace WallpaperEngine::Render::Wallpapers;
  16. using JSON = WallpaperEngine::Data::JSON::JSON;
  17. CScene::CScene (
  18. const Wallpaper& wallpaper, RenderContext& context, AudioContext& audioContext,
  19. const WallpaperState::TextureUVsScaling& scalingMode, const uint32_t& clampMode
  20. ) : CWallpaper (wallpaper, context, audioContext, scalingMode, clampMode) {
  21. // caller should check this, if not a std::bad_cast is good to throw
  22. auto scene = wallpaper.as<Scene> ();
  23. // setup the scene camera
  24. this->m_camera = std::make_unique<Camera> (*this, scene->camera);
  25. float width = scene->camera.projection.width;
  26. float height = scene->camera.projection.height;
  27. // detect size if the orthogonal project is auto
  28. if (scene->camera.projection.isAuto) {
  29. // TODO: CALCULATE ORTHOGONAL PROJECTION BASED ON CONTENT'S SIZE HERE
  30. }
  31. this->m_parallaxDisplacement = { 0, 0 };
  32. // TODO: CONVERSION
  33. this->m_camera->setOrthogonalProjection (width, height);
  34. // setup framebuffers here as they're required for the scene setup
  35. this->setupFramebuffers ();
  36. const uint32_t sceneWidth = this->m_camera->getWidth ();
  37. const uint32_t sceneHeight = this->m_camera->getHeight ();
  38. this->_rt_shadowAtlas = this->create (
  39. "_rt_shadowAtlas", TextureFormat_ARGB8888, TextureFlags_ClampUVs, 1.0, { sceneWidth, sceneHeight },
  40. { sceneWidth, sceneHeight }
  41. );
  42. this->alias ("_alias_lightCookie", "_rt_shadowAtlas");
  43. // set clear color
  44. const glm::vec3 clearColor = scene->colors.clear->value->getVec3 ();
  45. glClearColor (clearColor.r, clearColor.g, clearColor.b, 1.0f);
  46. // create all objects based off their dependencies
  47. for (const auto& object : scene->objects) {
  48. this->createObject (*object);
  49. }
  50. // copy over objects by render order
  51. for (const auto& object : scene->objects) {
  52. this->addObjectToRenderOrder (*object);
  53. }
  54. // create extra framebuffers for the bloom effect
  55. this->_rt_4FrameBuffer = this->create (
  56. "_rt_4FrameBuffer", TextureFormat_ARGB8888, TextureFlags_ClampUVs, 1.0, { sceneWidth / 4, sceneHeight / 4 },
  57. { sceneWidth / 4, sceneHeight / 4 }
  58. );
  59. this->_rt_8FrameBuffer = this->create (
  60. "_rt_8FrameBuffer", TextureFormat_ARGB8888, TextureFlags_ClampUVs, 1.0, { sceneWidth / 8, sceneHeight / 8 },
  61. { sceneWidth / 8, sceneHeight / 8 }
  62. );
  63. this->_rt_Bloom = this->create (
  64. "_rt_Bloom", TextureFormat_ARGB8888, TextureFlags_ClampUVs, 1.0, { sceneWidth / 8, sceneHeight / 8 },
  65. { sceneWidth / 8, sceneHeight / 8 }
  66. );
  67. //
  68. // Had to get a little creative with the effects to achieve the same bloom effect without any custom code
  69. // this custom image loads some effect files from the virtual container to achieve the same bloom effect
  70. // this approach requires of two extra draw calls due to the way the effect works in official WPE
  71. // (it renders directly to the screen, whereas here we never do that from a scene)
  72. //
  73. const auto bloomOrigin = glm::vec3 { sceneWidth / 2, sceneHeight / 2, 0.0f };
  74. const auto bloomSize = glm::vec2 { sceneWidth, sceneHeight };
  75. const JSON bloom
  76. = { { "image", "models/wpenginelinux.json" },
  77. { "name", "bloomimagewpenginelinux" },
  78. { "visible", true },
  79. { "scale", "1.0 1.0 1.0" },
  80. { "angles", "0.0 0.0 0.0" },
  81. { "origin",
  82. std::to_string (bloomOrigin.x) + " " + std::to_string (bloomOrigin.y) + " "
  83. + std::to_string (bloomOrigin.z) },
  84. { "size", std::to_string (bloomSize.x) + " " + std::to_string (bloomSize.y) },
  85. { "id", -1 },
  86. { "effects",
  87. JSON::array (
  88. { { { "file", "effects/wpenginelinux/bloomeffect.json" },
  89. { "id", 15242000 },
  90. { "name", "" },
  91. { "passes",
  92. JSON::array (
  93. { { { "constantshadervalues",
  94. { { "bloomstrength", this->getScene ().camera.bloom.strength->value->getFloat () },
  95. { "bloomthreshold",
  96. this->getScene ().camera.bloom.threshold->value->getFloat () } } } },
  97. { { "constantshadervalues",
  98. { { "bloomstrength", this->getScene ().camera.bloom.strength->value->getFloat () },
  99. { "bloomthreshold",
  100. this->getScene ().camera.bloom.threshold->value->getFloat () } } } },
  101. { { "constantshadervalues",
  102. { { "bloomstrength", this->getScene ().camera.bloom.strength->value->getFloat () },
  103. { "bloomthreshold",
  104. this->getScene ().camera.bloom.threshold->value->getFloat () } } } } }
  105. ) } } }
  106. ) } };
  107. // create image for bloom passes
  108. if (scene->camera.bloom.enabled->value->getBool ()) {
  109. this->m_bloomObjectData = ObjectParser::parse (bloom, scene->project);
  110. this->m_bloomObject = this->createObject (*this->m_bloomObjectData);
  111. this->m_objectsByRenderOrder.push_back (this->m_bloomObject);
  112. }
  113. }
  114. CScene::~CScene () {
  115. // bloom object is in the objects list, so no need to explicitly delete it
  116. this->m_bloomObject = nullptr;
  117. for (const auto& val : this->m_objects | std::views::values) {
  118. delete val;
  119. }
  120. this->m_objectsByRenderOrder.clear ();
  121. this->m_objects.clear ();
  122. }
  123. Render::CObject* CScene::createObject (const Object& object) {
  124. Render::CObject* renderObject = nullptr;
  125. // ensure the item is not loaded already
  126. if (const auto current = this->m_objects.find (object.id); current != this->m_objects.end ()) {
  127. return current->second;
  128. }
  129. // check dependencies too!
  130. for (const auto& cur : object.dependencies) {
  131. // self-dependency is a possibility...
  132. if (cur == object.id) {
  133. continue;
  134. }
  135. const auto dep
  136. = std::ranges::find_if (this->getScene ().objects, [&cur] (const auto& o) { return o->id == cur; });
  137. if (dep != this->getScene ().objects.end ()) {
  138. this->createObject (**dep);
  139. }
  140. }
  141. if (object.is<Image> ()) {
  142. auto* image = new Objects::CImage (*this, *object.as<Image> ());
  143. try {
  144. image->setup ();
  145. } catch (std::runtime_error&) {
  146. // this error message is already printed, so just show extra info about it
  147. sLog.error ("Cannot setup image ", image->getImage ().name);
  148. }
  149. renderObject = image;
  150. } else if (object.is<Sound> ()) {
  151. renderObject = new Objects::CSound (*this, *object.as<Sound> ());
  152. } else if (object.is<Particle> ()) {
  153. if (this->getContext ().getApp ().getContext ().settings.general.disableParticles == true) {
  154. sLog.debug ("Ignoring particle system (disabled in settings): ", object.as<Particle> ()->name);
  155. return nullptr;
  156. }
  157. auto* particle = new Objects::CParticle (*this, *object.as<Particle> ());
  158. try {
  159. particle->setup ();
  160. } catch (std::runtime_error&) {
  161. sLog.error ("Cannot setup particle ", particle->getParticle ().name);
  162. }
  163. renderObject = particle;
  164. }
  165. if (renderObject != nullptr) {
  166. this->m_objects.emplace (renderObject->getId (), renderObject);
  167. }
  168. return renderObject;
  169. }
  170. void CScene::addObjectToRenderOrder (const Object& object) {
  171. const auto obj = this->m_objects.find (object.id);
  172. // ignores not created objects like particle systems
  173. if (obj == this->m_objects.end ()) {
  174. return;
  175. }
  176. // take into account any dependency first
  177. for (const auto& dep : object.dependencies) {
  178. // self-dependency is possible
  179. if (dep == object.id) {
  180. continue;
  181. }
  182. // add the dependency to the list if it's created
  183. auto depIt = std::ranges::find_if (this->getScene ().objects, [&dep] (const auto& o) { return o->id == dep; });
  184. if (depIt != this->getScene ().objects.end ()) {
  185. this->addObjectToRenderOrder (**depIt);
  186. } else {
  187. sLog.error ("Cannot find dependency ", dep, " for object ", object.id);
  188. }
  189. }
  190. // ensure we're added only once to the render list
  191. const auto renderIt = std::ranges::find_if (this->m_objectsByRenderOrder, [&object] (const auto& o) {
  192. return o->getId () == object.id;
  193. });
  194. if (renderIt == this->m_objectsByRenderOrder.end ()) {
  195. this->m_objectsByRenderOrder.emplace_back (obj->second);
  196. }
  197. }
  198. Camera& CScene::getCamera () const { return *this->m_camera; }
  199. void CScene::renderFrame (const glm::ivec4& viewport) {
  200. // ensure the virtual mouse position is up to date
  201. this->updateMouse (viewport);
  202. // update the parallax position if required
  203. if (this->getScene ().camera.parallax.enabled->value->getBool ()
  204. && !this->getContext ().getApp ().getContext ().settings.mouse.disableparallax) {
  205. const float influence = this->getScene ().camera.parallax.mouseInfluence->value->getFloat ();
  206. const float amount = this->getScene ().camera.parallax.amount->value->getFloat ();
  207. const float delay = glm::min (
  208. static_cast<float> (this->getScene ().camera.parallax.delay->value->getBool ()), g_Time - g_TimeLast
  209. );
  210. this->m_parallaxDisplacement
  211. = glm::mix (this->m_parallaxDisplacement, (this->m_mousePosition * amount) * influence, delay);
  212. }
  213. // use the scene's framebuffer by default
  214. glBindFramebuffer (GL_FRAMEBUFFER, this->getWallpaperFramebuffer ());
  215. // ensure we render over the whole framebuffer
  216. glViewport (0, 0, this->m_sceneFBO->getRealWidth (), this->m_sceneFBO->getRealHeight ());
  217. glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  218. for (const auto& cur : this->m_objectsByRenderOrder) {
  219. cur->render ();
  220. }
  221. }
  222. void CScene::updateMouse (const glm::ivec4& viewport) {
  223. // update virtual mouse position first
  224. const glm::dvec2 position = this->getContext ().getInputContext ().getMouseInput ().position ();
  225. // rollover the position to the last
  226. this->m_mousePositionLast = this->m_mousePosition;
  227. // calculate the current position of the mouse in viewport space [0, 1]
  228. double mouseX = glm::clamp ((position.x - viewport.x) / viewport.z, 0.0, 1.0);
  229. // Normalize Y coordinate (OpenGL convention: 0=bottom, 1=top)
  230. // Particle code expects this convention: 0=bottom results in negative Y (down), 1=top results in positive Y (up)
  231. double mouseY = glm::clamp ((position.y - viewport.y) / viewport.w, 0.0, 1.0);
  232. // Account for UV cropping when using fill/fit scaling modes
  233. // The scene may be rendered larger than viewport and cropped via UVs
  234. const auto uvs = this->getState ().getTextureUVs ();
  235. // Map mouse position from viewport space to scene UV space
  236. // UVs define what portion of the scene texture is visible
  237. this->m_mousePosition.x = uvs.ustart + mouseX * (uvs.uend - uvs.ustart);
  238. this->m_mousePosition.y = uvs.vstart + mouseY * (uvs.vend - uvs.vstart);
  239. }
  240. const Scene& CScene::getScene () const { return *this->getWallpaperData ().as<Scene> (); }
  241. int CScene::getWidth () const { return this->m_camera->getWidth (); }
  242. int CScene::getHeight () const { return this->m_camera->getHeight (); }
  243. const glm::vec2* CScene::getMousePosition () const { return &this->m_mousePosition; }
  244. const glm::vec2* CScene::getMousePositionLast () const { return &this->m_mousePositionLast; }
  245. const glm::vec2* CScene::getParallaxDisplacement () const { return &this->m_parallaxDisplacement; }
  246. const std::vector<CObject*>& CScene::getObjectsByRenderOrder () const { return this->m_objectsByRenderOrder; }