Added support for camera bloom

Added support for virtual files

Signed-off-by: Alexis Maiquez <almamu@almamu.com>
This commit is contained in:
Alexis Maiquez 2022-11-03 23:07:13 +01:00
parent 625397ea0c
commit cdaebbe893
19 changed files with 358 additions and 20 deletions

View File

@ -42,6 +42,8 @@ add_executable(
src/WallpaperEngine/Assets/CAssetLoadException.h
src/WallpaperEngine/Assets/CContainer.h
src/WallpaperEngine/Assets/CContainer.cpp
src/WallpaperEngine/Assets/CVirtualContainer.h
src/WallpaperEngine/Assets/CVirtualContainer.cpp
src/WallpaperEngine/Assets/CCombinedContainer.h
src/WallpaperEngine/Assets/CCombinedContainer.cpp
src/WallpaperEngine/Assets/CDirectory.h

113
main.cpp
View File

@ -18,6 +18,7 @@
#include "WallpaperEngine/Assets/CPackage.h"
#include "WallpaperEngine/Assets/CDirectory.h"
#include "WallpaperEngine/Assets/CVirtualContainer.h"
#include "WallpaperEngine/Assets/CCombinedContainer.h"
#include "WallpaperEngine/Assets/CPackageLoadException.h"
@ -160,6 +161,116 @@ void addPkg (CCombinedContainer* containers, const std::string& path, std::strin
}
}
CVirtualContainer* buildVirtualContainer ()
{
CVirtualContainer* container = new WallpaperEngine::Assets::CVirtualContainer ();
//
// Had to get a little creative with the effects to achieve the same bloom effect without any custom code
// these virtual files are loaded by an image in the scene that takes current _rt_FullFrameBuffer and
// applies the bloom effect to render it out to the screen
//
// add the effect file for screen bloom
container->add (
"effects/wpenginelinux/bloomeffect.json",
"{"
"\t\"name\":\"camerabloom_wpengine_linux\","
"\t\"group\":\"wpengine_linux_camera\","
"\t\"dependencies\":[],"
"\t\"passes\":"
"\t["
"\t\t{"
"\t\t\t\"material\": \"materials/util/downsample_quarter_bloom.json\","
"\t\t\t\"target\": \"_rt_4FrameBuffer\","
"\t\t\t\"bind\":"
"\t\t\t["
"\t\t\t\t{"
"\t\t\t\t\t\"name\": \"_rt_FullFrameBuffer\","
"\t\t\t\t\t\"index\": 0"
"\t\t\t\t}"
"\t\t\t]"
"\t\t},"
"\t\t{"
"\t\t\t\"material\": \"materials/util/downsample_eighth_blur_v.json\","
"\t\t\t\"target\": \"_rt_8FrameBuffer\","
"\t\t\t\"bind\":"
"\t\t\t["
"\t\t\t\t{"
"\t\t\t\t\t\"name\": \"_rt_4FrameBuffer\","
"\t\t\t\t\t\"index\": 0"
"\t\t\t\t}"
"\t\t\t]"
"\t\t},"
"\t\t{"
"\t\t\t\"material\": \"materials/util/blur_h_bloom.json\","
"\t\t\t\"target\": \"_rt_Bloom\","
"\t\t\t\"bind\":"
"\t\t\t["
"\t\t\t\t{"
"\t\t\t\t\t\"name\": \"_rt_8FrameBuffer\","
"\t\t\t\t\t\"index\": 0"
"\t\t\t\t}"
"\t\t\t]"
"\t\t},"
"\t\t{"
"\t\t\t\"material\": \"materials/util/combine.json\","
"\t\t\t\"target\": \"_rt_imageLayerComposite_-1_b\","
"\t\t\t\"bind\":"
"\t\t\t["
"\t\t\t\t{"
"\t\t\t\t\t\"name\": \"_rt_FullFrameBuffer\","
"\t\t\t\t\t\"index\": 0"
"\t\t\t\t},"
"\t\t\t\t{"
"\t\t\t\t\t\"name\": \"_rt_Bloom\","
"\t\t\t\t\t\"index\": 1"
"\t\t\t\t}"
"\t\t\t]"
"\t\t},"
"\t\t{"
"\t\t\t\"material\": \"materials/wpenginelinux.json\","
"\t\t\t\"bind\":"
"\t\t\t["
"\t\t\t\t{"
"\t\t\t\t\t\"name\": \"_rt_imageLayerComposite_-1_b\","
"\t\t\t\t\t\"index\": 0"
"\t\t\t\t}"
"\t\t\t]"
"\t\t}"
"\t]"
"}"
);
// add some model for the image element even if it's going to waste rendering cycles
container->add (
"models/wpenginelinux.json",
"{"
"\t\"material\":\"materials/wpenginelinux.json\""
"}"
);
// models require materials, so add that too
container->add (
"materials/wpenginelinux.json",
"{"
"\t\"passes\":"
"\t\t["
"\t\t\t{"
"\t\t\t\t\"blending\": \"normal\","
"\t\t\t\t\"cullmode\": \"nocull\","
"\t\t\t\t\"depthtest\": \"disabled\","
"\t\t\t\t\"depthwrite\": \"disabled\","
"\t\t\t\t\"shader\": \"genericimage2\","
"\t\t\t\t\"textures\": [\"_rt_FullFrameBuffer\"]"
"\t\t\t}"
"\t\t]"
"}"
);
return container;
}
int main (int argc, char* argv[])
{
std::vector <std::string> screens;
@ -275,6 +386,8 @@ int main (int argc, char* argv[])
// the background's path is required to load project.json regardless of the type of background we're using
containers->add (new WallpaperEngine::Assets::CDirectory (path));
// add the virtual container for mocked up files
containers->add (buildVirtualContainer ());
// try to add the common packages
addPkg (containers, path, "scene.pkg");
addPkg (containers, path, "gifscene.pkg");

View File

@ -1,6 +1,3 @@
//
// Created by almamu on 8/8/21.
//
#pragma once
#include <string>

View File

@ -0,0 +1,36 @@
#include <memory.h>
#include "CVirtualContainer.h"
#include "CAssetLoadException.h"
using namespace WallpaperEngine::Assets;
void CVirtualContainer::add (const std::string& filename, void* contents, uint32_t length)
{
this->m_virtualFiles.insert (
std::make_pair (filename, CFileEntry (contents, length))
);
}
void CVirtualContainer::add (const std::string& filename, const std::string& contents)
{
char* copy = new char [contents.length () + 1];
// copy the text AND the \0
memcpy (copy, contents.c_str (), contents.length () + 1);
// finally add to the container
this->add (filename, copy, contents.length () + 1);
}
const void* CVirtualContainer::readFile (std::string filename, uint32_t* length) const
{
auto cur = this->m_virtualFiles.find (filename);
if (cur == this->m_virtualFiles.end ())
throw CAssetLoadException (filename, "Cannot find file in the virtual container");
*length = (*cur).second.length;
return (*cur).second.address;
}

View File

@ -0,0 +1,39 @@
#pragma once
#include <string>
#include <map>
#include "CFileEntry.h"
#include "CContainer.h"
namespace WallpaperEngine::Assets
{
class CVirtualContainer : public CContainer
{
public:
CVirtualContainer () {}
~CVirtualContainer () {}
/**
* Adds a new file to the virtual container
*
* @param filename
* @param contents
* @param length
*/
void add (const std::string& filename, void* contents, uint32_t length);
/**
* Adds a new file to the virtual container
*
* @param filename
* @param contents
*/
void add (const std::string& filename, const std::string& contents);
const void* readFile (std::string filename, uint32_t* length) const override;
private:
std::map <std::string, CFileEntry> m_virtualFiles;
};
}

View File

@ -28,7 +28,7 @@ CObject::CObject (
{
}
CObject* CObject::fromJSON (json data, CContainer* container)
CObject* CObject::fromJSON (json data, const CContainer* container)
{
std::string json = data.dump ();

View File

@ -18,7 +18,7 @@ namespace WallpaperEngine::Core
class CObject
{
public:
static CObject* fromJSON (json data, CContainer* container);
static CObject* fromJSON (json data, const CContainer* container);
template<class T> const T* as () const { assert (is <T> ()); return (const T*) this; }
template<class T> T* as () { assert (is <T> ()); return (T*) this; }

View File

@ -62,8 +62,8 @@ CScene* CScene::fromFile (const std::string& filename, CContainer* container)
// TODO: FIND IF THESE DEFAULTS ARE SENSIBLE OR NOT AND PERFORM PROPER VALIDATION WHEN CAMERA PREVIEW AND CAMERA PARALLAX ARE PRESENT
auto ambientcolor = jsonFindDefault <std::string> (*general_it, "ambientcolor", "0 0 0");
auto bloom = jsonFindDefault <bool> (*general_it, "bloom", false);
auto bloomstrength = jsonFindDefault <double> (*general_it, "bloomstrength", 0.0f);
auto bloomthreshold = jsonFindDefault <double> (*general_it, "bloomthreshold", 0.0f);
auto bloomstrength = (*general_it).find ("bloomstrength");
auto bloomthreshold = (*general_it).find ("bloomthreshold");
auto camerafade = jsonFindDefault <bool> (*general_it, "camerafade", false);
auto cameraparallax = jsonFindDefault <bool> (*general_it, "cameraparallax", true);
auto cameraparallaxamount = jsonFindDefault <double> (*general_it, "cameraparallaxamount", 1.0f);
@ -77,14 +77,48 @@ CScene* CScene::fromFile (const std::string& filename, CContainer* container)
auto clearcolor_it = jsonFindRequired (*general_it, "clearcolor", "General section must have clear color");
auto orthogonalprojection_it = jsonFindRequired (*general_it, "orthogonalprojection", "General section must have orthogonal projection info");
auto skylightcolor = jsonFindDefault <std::string> (*general_it, "skylightcolor", "0 0 0");
double bloomstrength_val = 0.0f;
double bloomthreshold_val = 0.0f;
if (bloomstrength != (*general_it).end ())
{
if ((*bloomstrength).is_object ())
{
auto value = (*bloomstrength).find ("value");
if (value != (*bloomstrength).end ())
{
bloomstrength = value;
}
}
if ((*bloomstrength).is_number ())
bloomstrength_val = (*bloomstrength);
}
if (bloomthreshold != (*general_it).end ())
{
if ((*bloomthreshold).is_object ())
{
auto value = (*bloomthreshold).find ("value");
if (value != (*bloomthreshold).end ())
{
bloomthreshold = value;
}
}
if ((*bloomthreshold).is_number ())
bloomthreshold_val = (*bloomthreshold);
}
CScene* scene = new CScene (
container,
Scenes::CCamera::fromJSON (*camera_it),
WallpaperEngine::Core::aToColorf(ambientcolor),
bloom,
bloomstrength,
bloomthreshold,
bloomstrength_val,
bloomthreshold_val,
camerafade,
cameraparallax,
cameraparallaxamount,

View File

@ -28,7 +28,7 @@ CEffect::CEffect (
{
}
CEffect* CEffect::fromJSON (json data, Core::CObject* object, CContainer* container)
CEffect* CEffect::fromJSON (json data, Core::CObject* object, const CContainer* container)
{
auto file_it = jsonFindRequired (data, "file", "Object effect must have a file");
auto effectpasses_it = data.find ("passes");
@ -216,7 +216,7 @@ void CEffect::dependencyFromJSON (json::const_iterator dependencies_it, CEffect*
}
}
void CEffect::materialsFromJSON (json::const_iterator passes_it, CEffect* effect, CContainer* container)
void CEffect::materialsFromJSON (json::const_iterator passes_it, CEffect* effect, const CContainer* container)
{
auto cur = (*passes_it).begin ();
auto end = (*passes_it).end ();

View File

@ -28,7 +28,7 @@ namespace WallpaperEngine::Core::Objects
Core::CObject* object
);
static CEffect* fromJSON (json data, Core::CObject* object, CContainer* container);
static CEffect* fromJSON (json data, Core::CObject* object, const CContainer* container);
const std::vector<std::string>& getDependencies () const;
const std::vector<Images::CMaterial*>& getMaterials () const;
@ -40,7 +40,7 @@ namespace WallpaperEngine::Core::Objects
static void combosFromJSON (json::const_iterator combos_it, Core::Objects::Images::Materials::CPass* pass);
static void fbosFromJSON (json::const_iterator fbos_it, CEffect* effect);
static void dependencyFromJSON (json::const_iterator dependencies_it, CEffect* effect);
static void materialsFromJSON (json::const_iterator passes_it, CEffect* effect, CContainer* container);
static void materialsFromJSON (json::const_iterator passes_it, CEffect* effect, const CContainer* container);
void insertDependency (const std::string& dep);
void insertMaterial (Images::CMaterial* material);

View File

@ -37,7 +37,7 @@ CImage::CImage (
WallpaperEngine::Core::CObject* CImage::fromJSON (
json data,
CContainer* container,
const CContainer* container,
bool visible,
uint32_t id,
std::string name,

View File

@ -19,7 +19,7 @@ namespace WallpaperEngine::Core::Objects
public:
static CObject* fromJSON (
json data,
CContainer* container,
const CContainer* container,
bool visible,
uint32_t id,
std::string name,

View File

@ -5,7 +5,7 @@ using namespace WallpaperEngine::Core::Objects;
CParticle* CParticle::fromFile (
const std::string& filename,
CContainer* container,
const CContainer* container,
uint32_t id,
std::string name,
const glm::vec3& origin,

View File

@ -18,7 +18,7 @@ namespace WallpaperEngine::Core::Objects
public:
static CParticle* fromFile (
const std::string& filename,
CContainer* container,
const CContainer* container,
uint32_t id,
std::string name,
const glm::vec3& origin,

View File

@ -16,7 +16,7 @@ CPass* CPass::fromJSON (json data)
{
// TODO: FIGURE OUT DEFAULT BLENDING MODE
auto blending = jsonFindDefault <std::string> (data, "blending", "normal");
auto cullmode_it = jsonFindRequired (data, "cullmode", "Material pass must have cullmode specified");
auto cullmode = jsonFindDefault <std::string> (data, "cullmode", "nocull");
auto depthtest_it = jsonFindRequired (data, "depthtest", "Material pass must have depthtest specified");
auto depthwrite_it = jsonFindRequired (data, "depthwrite", "Material pass must have depthwrite specified");
auto shader_it = jsonFindRequired (data, "shader", "Material pass must have shader specified");
@ -34,7 +34,7 @@ CPass* CPass::fromJSON (json data)
CPass* pass = new CPass (
blending,
*cullmode_it,
cullmode,
*depthtest_it,
*depthwrite_it,
*shader_it

View File

@ -72,6 +72,106 @@ CScene::CScene (Core::CScene* scene, CContext* context) :
this->m_objectsByRenderOrder.emplace_back ((*obj).second);
}
uint32_t sceneWidth = scene->getOrthogonalProjection ()->getWidth ();
uint32_t sceneHeight = scene->getOrthogonalProjection ()->getHeight ();
// create extra framebuffers for the bloom effect
this->_rt_4FrameBuffer = this->createFBO (
"_rt_4FrameBuffer",
ITexture::TextureFormat::ARGB8888,
ITexture::TextureFlags::NoInterpolation,
1.0,
sceneWidth / 4, sceneHeight / 4,
sceneWidth / 4, sceneHeight / 4
);
this->_rt_8FrameBuffer = this->createFBO (
"_rt_8FrameBuffer",
ITexture::TextureFormat::ARGB8888,
ITexture::TextureFlags::NoInterpolation,
1.0,
sceneWidth / 8, sceneHeight / 8,
sceneWidth / 8, sceneHeight / 8
);
this->_rt_Bloom = this->createFBO (
"_rt_Bloom",
ITexture::TextureFormat::ARGB8888,
ITexture::TextureFlags::NoInterpolation,
1.0,
sceneWidth / 8, sceneHeight / 8,
sceneWidth / 8, sceneHeight / 8
);
this->_rt_FullFrameBuffer_b = this->createFBO (
"_rt_FullFrameBuffer_b",
ITexture::TextureFormat::ARGB8888,
ITexture::TextureFlags::NoInterpolation,
1.0,
sceneWidth, sceneHeight,
sceneWidth, sceneHeight
);
//
// 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)
//
std::string imagejson =
"{"
"\t\"image\": \"models/wpenginelinux.json\","
"\t\"name\": \"bloomimagewpenginelinux\","
"\t\"visible\": true,"
"\t\"scale\": \"1.0 1.0 1.0\","
"\t\"angles\": \"0.0 0.0 0.0\","
"\t\"origin\": \"" + std::to_string (sceneWidth / 2) + " " + std::to_string (sceneHeight / 2) + " 0.0\","
"\t\"id\": " + std::to_string (0xFFFFFFFF) + ","
"\t\"effects\":"
"\t["
"\t\t{"
"\t\t\t\"file\": \"effects/wpenginelinux/bloomeffect.json\","
"\t\t\t\"id\": 15242000,"
"\t\t\t\"name\": \"\","
"\t\t\t\"passes\":"
"\t\t\t["
"\t\t\t\t{"
"\t\t\t\t\t\"constantshadervalues\":"
"\t\t\t\t\t{"
"\t\t\t\t\t\t\"bloomstrength\": " + std::to_string (this->getScene ()->getBloomStrength ()) + ","
"\t\t\t\t\t\t\"bloomthreshold\": " + std::to_string (this->getScene ()->getBloomThreshold ()) +
"\t\t\t\t\t}"
"\t\t\t\t},"
"\t\t\t\t{"
"\t\t\t\t\t\"constantshadervalues\":"
"\t\t\t\t\t{"
"\t\t\t\t\t\t\"bloomstrength\": " + std::to_string (this->getScene ()->getBloomStrength ()) + ","
"\t\t\t\t\t\t\"bloomthreshold\": " + std::to_string (this->getScene ()->getBloomThreshold ()) +
"\t\t\t\t\t}"
"\t\t\t\t},"
"\t\t\t\t{"
"\t\t\t\t\t\"constantshadervalues\":"
"\t\t\t\t\t{"
"\t\t\t\t\t\t\"bloomstrength\": " + std::to_string (this->getScene ()->getBloomStrength ()) + ","
"\t\t\t\t\t\t\"bloomthreshold\": " + std::to_string (this->getScene ()->getBloomThreshold ()) +
"\t\t\t\t\t}"
"\t\t\t\t}"
"\t\t\t]"
"\t\t}"
"\t],"
"\t\"size\": \"" + std::to_string (sceneWidth) + " " + std::to_string (sceneHeight) + "\""
"}";
auto json = nlohmann::json::parse (imagejson);
// create image for bloom passes
auto bloomPass = this->createObject (
WallpaperEngine::Core::CObject::fromJSON (
json, this->getContainer ()
)
);
if (this->getScene ()->isBloom () == true)
this->m_objectsByRenderOrder.emplace_back (bloomPass);
}
Render::CObject* CScene::createObject (Core::CObject* object)

View File

@ -40,5 +40,9 @@ namespace WallpaperEngine::Render
std::vector<CObject*> m_objectsByRenderOrder;
glm::vec2 m_mousePosition;
glm::vec2 m_parallaxDisplacement;
CFBO* _rt_4FrameBuffer;
CFBO* _rt_8FrameBuffer;
CFBO* _rt_Bloom;
CFBO* _rt_FullFrameBuffer_b; // this one doesn't exist on the official wallpaper engine, but our approach requires it
};
}

View File

@ -92,6 +92,14 @@ CImage::CImage (CScene* scene, Core::Objects::CImage* image) :
GLfloat realWidth = this->m_texture->getRealWidth () / 2.0f;
GLfloat realHeight = this->m_texture->getRealHeight () / 2.0f;
// TODO: XXXHACK: QUICK HACK TO MAKE BLOOM LAYER BEHAVE IN A SPECIAL WAY, PREVENTS VERTICAL FLIP
if (this->getId () == 0xFFFFFFFF)
{
float tmpy = this->m_pos.y;
this->m_pos.y = this->m_pos.w;
this->m_pos.w = tmpy;
}
// build a list of vertices, these might need some change later (or maybe invert the camera)
GLfloat sceneSpacePosition [] = {
this->m_pos.x, this->m_pos.y, 0.0f,

View File

@ -56,8 +56,9 @@ const ITexture* CPass::resolveTexture (const ITexture* expected, int index, cons
// the bind actually has a name, search the FBO in the effect and return it
auto fbo = this->m_material->m_effect->findFBO ((*it).second->getName ());
// try scene FBOs, these are our last resort, i guess the exception is better than a nullpo
if (fbo == nullptr)
return nullptr;
return this->m_material->getImage ()->getScene ()->findFBO ((*it).second->getName ());
return fbo;
}
@ -556,6 +557,8 @@ void CPass::setupUniforms ()
}
}
auto projection = this->getMaterial ()->getImage ()->getScene ()->getScene ()->getOrthogonalProjection ();
// register variables like brightness and alpha with some default value
this->addUniform ("g_Brightness", this->m_material->getImage ()->getImage ()->getBrightness ());
this->addUniform ("g_UserAlpha", this->m_material->getImage ()->getImage ()->getAlpha ());
@ -570,6 +573,8 @@ void CPass::setupUniforms ()
this->addUniform ("g_PointerPosition", this->m_material->getImage ()->getScene ()->getMousePosition ());
this->addUniform ("g_EffectTextureProjectionMatrix", glm::mat4(1.0));
this->addUniform ("g_EffectTextureProjectionMatrixInverse", glm::mat4(1.0));
this->addUniform ("g_TexelSize", glm::vec2 (1.0 / projection->getWidth (), 1.0 / projection->getHeight ()));
this->addUniform ("g_TexelSize", glm::vec2 (0.5 / projection->getWidth (), 0.5 / projection->getHeight ()));
}
void CPass::addAttribute (const std::string& name, GLint type, GLint elements, const GLuint* value)