mirror of
https://github.com/Almamu/linux-wallpaperengine.git
synced 2025-09-14 05:46:48 +08:00
317 lines
12 KiB
C++
317 lines
12 KiB
C++
#include "WallpaperEngine/Render/Objects/CImage.h"
|
|
#include "WallpaperEngine/Render/Objects/CSound.h"
|
|
|
|
#include "WallpaperEngine/Render/CWallpaperState.h"
|
|
|
|
#include "CScene.h"
|
|
#include "WallpaperEngine/Logging/CLog.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, CRenderContext& context, CAudioContext& audioContext,
|
|
const CWallpaperState::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<CCamera> (*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);
|
|
}
|
|
}
|
|
|
|
Render::CObject* CScene::createObject (const Object& object) {
|
|
Render::CObject* renderObject = nullptr;
|
|
|
|
// ensure the item is not loaded already
|
|
const auto current = this->m_objects.find (object.id);
|
|
|
|
if (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::find_if (
|
|
this->getScene ().objects.begin (),
|
|
this->getScene ().objects.end (),
|
|
[&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> ());
|
|
}
|
|
|
|
if (renderObject != nullptr)
|
|
this->m_objects.emplace (renderObject->getId (), renderObject);
|
|
|
|
return renderObject;
|
|
}
|
|
|
|
void CScene::addObjectToRenderOrder (const Object& object) {
|
|
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::find_if (
|
|
this->getScene ().objects.begin (),
|
|
this->getScene ().objects.end (),
|
|
[&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::find_if (
|
|
this->m_objectsByRenderOrder.begin (),
|
|
this->m_objectsByRenderOrder.end (),
|
|
[&object] (const auto& o) {
|
|
return o->getId () == object.id;
|
|
}
|
|
);
|
|
|
|
if (renderIt == this->m_objectsByRenderOrder.end ()) {
|
|
this->m_objectsByRenderOrder.emplace_back (obj->second);
|
|
}
|
|
}
|
|
|
|
CCamera& CScene::getCamera () const {
|
|
return *this->m_camera;
|
|
}
|
|
|
|
void CScene::renderFrame (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 && !this->getContext ().getApp ().getContext ().settings.mouse.disableparallax) {
|
|
const float influence = this->getScene ().camera.parallax.mouseInfluence;
|
|
const float amount = this->getScene ().camera.parallax.amount;
|
|
const float delay =
|
|
glm::min (static_cast<float> (this->getScene ().camera.parallax.delay), 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 (glm::ivec4 viewport) {
|
|
// update virtual mouse position first
|
|
const glm::dvec2 position = this->getContext ().getInputContext ().getMouseInput ().position ();
|
|
// TODO: PROPERLY TRANSLATE THESE TO WHAT'S VISIBLE ON SCREEN (FOR BACKGROUNDS THAT DO NOT EXACTLY FIT ON SCREEN)
|
|
|
|
// rollover the position to the last
|
|
this->m_mousePositionLast = this->m_mousePosition;
|
|
|
|
// calculate the current position of the mouse
|
|
this->m_mousePosition.x = glm::clamp ((position.x - viewport.x) / viewport.z, 0.0, 1.0);
|
|
this->m_mousePosition.y = glm::clamp ((position.y - viewport.y) / viewport.w, 0.0, 1.0);
|
|
|
|
// screen-space positions have to be transposed to what the screen will actually show
|
|
}
|
|
|
|
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;
|
|
}
|