| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312 |
- #include "WallpaperEngine/Render/Objects/CImage.h"
- #include "WallpaperEngine/Render/Objects/CParticle.h"
- #include "WallpaperEngine/Render/Objects/CSound.h"
- #include "WallpaperEngine/Render/WallpaperState.h"
- #include "CScene.h"
- #include "WallpaperEngine/Logging/Log.h"
- #include "WallpaperEngine/Data/Model/Wallpaper.h"
- #include "WallpaperEngine/Data/Parsers/ObjectParser.h"
- extern float g_Time;
- extern float g_TimeLast;
- using namespace WallpaperEngine;
- using namespace WallpaperEngine::Render;
- using namespace WallpaperEngine::Data::Model;
- using namespace WallpaperEngine::Data::Parsers;
- using namespace WallpaperEngine::Render::Wallpapers;
- using JSON = WallpaperEngine::Data::JSON::JSON;
- CScene::CScene (
- const Wallpaper& wallpaper, RenderContext& context, AudioContext& audioContext,
- const WallpaperState::TextureUVsScaling& scalingMode, const uint32_t& clampMode
- ) : CWallpaper (wallpaper, context, audioContext, scalingMode, clampMode) {
- // caller should check this, if not a std::bad_cast is good to throw
- auto scene = wallpaper.as<Scene> ();
- // setup the scene camera
- this->m_camera = std::make_unique<Camera> (*this, scene->camera);
- float width = scene->camera.projection.width;
- float height = scene->camera.projection.height;
- // detect size if the orthogonal project is auto
- if (scene->camera.projection.isAuto) {
- // TODO: CALCULATE ORTHOGONAL PROJECTION BASED ON CONTENT'S SIZE HERE
- }
- this->m_parallaxDisplacement = { 0, 0 };
- // TODO: CONVERSION
- this->m_camera->setOrthogonalProjection (width, height);
- // setup framebuffers here as they're required for the scene setup
- this->setupFramebuffers ();
- const uint32_t sceneWidth = this->m_camera->getWidth ();
- const uint32_t sceneHeight = this->m_camera->getHeight ();
- this->_rt_shadowAtlas = this->create (
- "_rt_shadowAtlas", TextureFormat_ARGB8888, TextureFlags_ClampUVs, 1.0, { sceneWidth, sceneHeight },
- { sceneWidth, sceneHeight }
- );
- this->alias ("_alias_lightCookie", "_rt_shadowAtlas");
- // set clear color
- const glm::vec3 clearColor = scene->colors.clear->value->getVec3 ();
- glClearColor (clearColor.r, clearColor.g, clearColor.b, 1.0f);
- // create all objects based off their dependencies
- for (const auto& object : scene->objects) {
- this->createObject (*object);
- }
- // copy over objects by render order
- for (const auto& object : scene->objects) {
- this->addObjectToRenderOrder (*object);
- }
- // create extra framebuffers for the bloom effect
- this->_rt_4FrameBuffer = this->create (
- "_rt_4FrameBuffer", TextureFormat_ARGB8888, TextureFlags_ClampUVs, 1.0, { sceneWidth / 4, sceneHeight / 4 },
- { sceneWidth / 4, sceneHeight / 4 }
- );
- this->_rt_8FrameBuffer = this->create (
- "_rt_8FrameBuffer", TextureFormat_ARGB8888, TextureFlags_ClampUVs, 1.0, { sceneWidth / 8, sceneHeight / 8 },
- { sceneWidth / 8, sceneHeight / 8 }
- );
- this->_rt_Bloom = this->create (
- "_rt_Bloom", TextureFormat_ARGB8888, TextureFlags_ClampUVs, 1.0, { sceneWidth / 8, sceneHeight / 8 },
- { sceneWidth / 8, sceneHeight / 8 }
- );
- //
- // Had to get a little creative with the effects to achieve the same bloom effect without any custom code
- // this custom image loads some effect files from the virtual container to achieve the same bloom effect
- // this approach requires of two extra draw calls due to the way the effect works in official WPE
- // (it renders directly to the screen, whereas here we never do that from a scene)
- //
- const auto bloomOrigin = glm::vec3 { sceneWidth / 2, sceneHeight / 2, 0.0f };
- const auto bloomSize = glm::vec2 { sceneWidth, sceneHeight };
- const JSON bloom
- = { { "image", "models/wpenginelinux.json" },
- { "name", "bloomimagewpenginelinux" },
- { "visible", true },
- { "scale", "1.0 1.0 1.0" },
- { "angles", "0.0 0.0 0.0" },
- { "origin",
- std::to_string (bloomOrigin.x) + " " + std::to_string (bloomOrigin.y) + " "
- + std::to_string (bloomOrigin.z) },
- { "size", std::to_string (bloomSize.x) + " " + std::to_string (bloomSize.y) },
- { "id", -1 },
- { "effects",
- JSON::array (
- { { { "file", "effects/wpenginelinux/bloomeffect.json" },
- { "id", 15242000 },
- { "name", "" },
- { "passes",
- JSON::array (
- { { { "constantshadervalues",
- { { "bloomstrength", this->getScene ().camera.bloom.strength->value->getFloat () },
- { "bloomthreshold",
- this->getScene ().camera.bloom.threshold->value->getFloat () } } } },
- { { "constantshadervalues",
- { { "bloomstrength", this->getScene ().camera.bloom.strength->value->getFloat () },
- { "bloomthreshold",
- this->getScene ().camera.bloom.threshold->value->getFloat () } } } },
- { { "constantshadervalues",
- { { "bloomstrength", this->getScene ().camera.bloom.strength->value->getFloat () },
- { "bloomthreshold",
- this->getScene ().camera.bloom.threshold->value->getFloat () } } } } }
- ) } } }
- ) } };
- // create image for bloom passes
- if (scene->camera.bloom.enabled->value->getBool ()) {
- this->m_bloomObjectData = ObjectParser::parse (bloom, scene->project);
- this->m_bloomObject = this->createObject (*this->m_bloomObjectData);
- this->m_objectsByRenderOrder.push_back (this->m_bloomObject);
- }
- }
- CScene::~CScene () {
- // bloom object is in the objects list, so no need to explicitly delete it
- this->m_bloomObject = nullptr;
- for (const auto& val : this->m_objects | std::views::values) {
- delete val;
- }
- this->m_objectsByRenderOrder.clear ();
- this->m_objects.clear ();
- }
- Render::CObject* CScene::createObject (const Object& object) {
- Render::CObject* renderObject = nullptr;
- // ensure the item is not loaded already
- if (const auto current = this->m_objects.find (object.id); current != this->m_objects.end ()) {
- return current->second;
- }
- // check dependencies too!
- for (const auto& cur : object.dependencies) {
- // self-dependency is a possibility...
- if (cur == object.id) {
- continue;
- }
- const auto dep
- = std::ranges::find_if (this->getScene ().objects, [&cur] (const auto& o) { return o->id == cur; });
- if (dep != this->getScene ().objects.end ()) {
- this->createObject (**dep);
- }
- }
- if (object.is<Image> ()) {
- auto* image = new Objects::CImage (*this, *object.as<Image> ());
- try {
- image->setup ();
- } catch (std::runtime_error&) {
- // this error message is already printed, so just show extra info about it
- sLog.error ("Cannot setup image ", image->getImage ().name);
- }
- renderObject = image;
- } else if (object.is<Sound> ()) {
- renderObject = new Objects::CSound (*this, *object.as<Sound> ());
- } else if (object.is<Particle> ()) {
- if (this->getContext ().getApp ().getContext ().settings.general.disableParticles == true) {
- sLog.debug ("Ignoring particle system (disabled in settings): ", object.as<Particle> ()->name);
- return nullptr;
- }
- auto* particle = new Objects::CParticle (*this, *object.as<Particle> ());
- try {
- particle->setup ();
- } catch (std::runtime_error&) {
- sLog.error ("Cannot setup particle ", particle->getParticle ().name);
- }
- renderObject = particle;
- }
- if (renderObject != nullptr) {
- this->m_objects.emplace (renderObject->getId (), renderObject);
- }
- return renderObject;
- }
- void CScene::addObjectToRenderOrder (const Object& object) {
- const auto obj = this->m_objects.find (object.id);
- // ignores not created objects like particle systems
- if (obj == this->m_objects.end ()) {
- return;
- }
- // take into account any dependency first
- for (const auto& dep : object.dependencies) {
- // self-dependency is possible
- if (dep == object.id) {
- continue;
- }
- // add the dependency to the list if it's created
- auto depIt = std::ranges::find_if (this->getScene ().objects, [&dep] (const auto& o) { return o->id == dep; });
- if (depIt != this->getScene ().objects.end ()) {
- this->addObjectToRenderOrder (**depIt);
- } else {
- sLog.error ("Cannot find dependency ", dep, " for object ", object.id);
- }
- }
- // ensure we're added only once to the render list
- const auto renderIt = std::ranges::find_if (this->m_objectsByRenderOrder, [&object] (const auto& o) {
- return o->getId () == object.id;
- });
- if (renderIt == this->m_objectsByRenderOrder.end ()) {
- this->m_objectsByRenderOrder.emplace_back (obj->second);
- }
- }
- Camera& CScene::getCamera () const { return *this->m_camera; }
- void CScene::renderFrame (const glm::ivec4& viewport) {
- // ensure the virtual mouse position is up to date
- this->updateMouse (viewport);
- // update the parallax position if required
- if (this->getScene ().camera.parallax.enabled->value->getBool ()
- && !this->getContext ().getApp ().getContext ().settings.mouse.disableparallax) {
- const float influence = this->getScene ().camera.parallax.mouseInfluence->value->getFloat ();
- const float amount = this->getScene ().camera.parallax.amount->value->getFloat ();
- const float delay = glm::min (
- static_cast<float> (this->getScene ().camera.parallax.delay->value->getBool ()), g_Time - g_TimeLast
- );
- this->m_parallaxDisplacement
- = glm::mix (this->m_parallaxDisplacement, (this->m_mousePosition * amount) * influence, delay);
- }
- // use the scene's framebuffer by default
- glBindFramebuffer (GL_FRAMEBUFFER, this->getWallpaperFramebuffer ());
- // ensure we render over the whole framebuffer
- glViewport (0, 0, this->m_sceneFBO->getRealWidth (), this->m_sceneFBO->getRealHeight ());
- glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
- for (const auto& cur : this->m_objectsByRenderOrder) {
- cur->render ();
- }
- }
- void CScene::updateMouse (const glm::ivec4& viewport) {
- // update virtual mouse position first
- const glm::dvec2 position = this->getContext ().getInputContext ().getMouseInput ().position ();
- // rollover the position to the last
- this->m_mousePositionLast = this->m_mousePosition;
- // calculate the current position of the mouse in viewport space [0, 1]
- double mouseX = glm::clamp ((position.x - viewport.x) / viewport.z, 0.0, 1.0);
- // Normalize Y coordinate (OpenGL convention: 0=bottom, 1=top)
- // Particle code expects this convention: 0=bottom results in negative Y (down), 1=top results in positive Y (up)
- double mouseY = glm::clamp ((position.y - viewport.y) / viewport.w, 0.0, 1.0);
- // Account for UV cropping when using fill/fit scaling modes
- // The scene may be rendered larger than viewport and cropped via UVs
- const auto uvs = this->getState ().getTextureUVs ();
- // Map mouse position from viewport space to scene UV space
- // UVs define what portion of the scene texture is visible
- this->m_mousePosition.x = uvs.ustart + mouseX * (uvs.uend - uvs.ustart);
- this->m_mousePosition.y = uvs.vstart + mouseY * (uvs.vend - uvs.vstart);
- }
- const Scene& CScene::getScene () const { return *this->getWallpaperData ().as<Scene> (); }
- int CScene::getWidth () const { return this->m_camera->getWidth (); }
- int CScene::getHeight () const { return this->m_camera->getHeight (); }
- const glm::vec2* CScene::getMousePosition () const { return &this->m_mousePosition; }
- const glm::vec2* CScene::getMousePositionLast () const { return &this->m_mousePositionLast; }
- const glm::vec2* CScene::getParallaxDisplacement () const { return &this->m_parallaxDisplacement; }
- const std::vector<CObject*>& CScene::getObjectsByRenderOrder () const { return this->m_objectsByRenderOrder; }
|