285 lines
13 KiB
C++
285 lines
13 KiB
C++
#include "behavior.h"
|
|
|
|
#include <uf/utils/renderer/renderer.h>
|
|
|
|
#include <uf/utils/math/matrix.h>
|
|
#include <uf/utils/math/transform.h>
|
|
#include <uf/utils/math/physics.h>
|
|
#include <uf/utils/camera/camera.h>
|
|
|
|
namespace {
|
|
struct {
|
|
size_t current = 0;
|
|
uf::stl::vector<uf::Object*> lights;
|
|
} roundRobin;
|
|
const pod::Quaternion<> rotations[6] = {
|
|
uf::quaternion::axisAngle( { 0, 1, 0 }, 1 * 1.57079633f ), // right
|
|
uf::quaternion::axisAngle( { 0, 1, 0 }, 3 * 1.57079633f ), // left
|
|
uf::quaternion::axisAngle( { 1, 0, 0 }, 3 * 1.57079633f ), // down
|
|
uf::quaternion::axisAngle( { 1, 0, 0 }, 1 * 1.57079633f ), // up
|
|
uf::quaternion::axisAngle( { 0, 1, 0 }, 0 * 1.57079633f ), // front
|
|
uf::quaternion::axisAngle( { 0, 1, 0 }, 2 * 1.57079633f ), // back
|
|
};
|
|
}
|
|
|
|
UF_BEHAVIOR_REGISTER_CPP(ext::LightBehavior)
|
|
UF_BEHAVIOR_TRAITS_CPP(ext::LightBehavior, ticks = true, renders = false, multithread = true)
|
|
#define this (&self)
|
|
void ext::LightBehavior::initialize( uf::Object& self ) {
|
|
auto& metadata = this->getComponent<ext::LightBehavior::Metadata>();
|
|
auto& metadataJson = this->getComponent<uf::Serializer>();
|
|
auto& transform = this->getComponent<pod::Transform<>>();
|
|
auto& camera = this->getComponent<uf::Camera>();
|
|
auto& scene = uf::scene::getCurrentScene();
|
|
auto& controller = scene.getController();
|
|
auto& sceneMetadataJson = scene.getComponent<uf::Serializer>();
|
|
|
|
if ( !sceneMetadataJson["system"]["lights"]["round robin hook bound"].as<bool>() ) {
|
|
sceneMetadataJson["system"]["lights"]["round robin hook bound"] = true;
|
|
scene.addHook("game:Frame.Start", [&]( ext::json::Value& payload ){
|
|
if ( ++::roundRobin.current >= ::roundRobin.lights.size() ) ::roundRobin.current = 0;
|
|
});
|
|
}
|
|
/*
|
|
if ( !metadataJson["light"]["bias"]["shader"].is<float>() ) metadataJson["light"]["bias"]["shader"] = 0.000000005f;
|
|
*/
|
|
if ( !metadataJson["light"]["bias"]["constant"].is<float>() ) metadataJson["light"]["bias"]["constant"] = 0.00005f;
|
|
if ( !metadataJson["light"]["type"].is<uf::stl::string>() ) metadataJson["light"]["type"] = "point";
|
|
if ( !ext::json::isArray( metadataJson["light"]["color"] ) ) {
|
|
metadataJson["light"]["color"][0] = 1; //metadataJson["light"]["color"]["random"].as<bool>() ? (rand() % 100) / 100.0 : 1;
|
|
metadataJson["light"]["color"][1] = 1; //metadataJson["light"]["color"]["random"].as<bool>() ? (rand() % 100) / 100.0 : 1;
|
|
metadataJson["light"]["color"][2] = 1; //metadataJson["light"]["color"]["random"].as<bool>() ? (rand() % 100) / 100.0 : 1;
|
|
}
|
|
if ( metadataJson["light"]["type"].as<uf::stl::string>() == "point" ) {
|
|
transform.orientation = uf::quaternion::identity();
|
|
}
|
|
#if UF_USE_OPENGL
|
|
metadataJson["light"]["shadows"] = false;
|
|
#endif
|
|
if ( !sceneMetadataJson["lights"]["shadows"]["enabled"].as<bool>(true) ) {
|
|
metadataJson["light"]["shadows"] = false;
|
|
}
|
|
if ( metadataJson["light"]["shadows"].as<bool>() ) {
|
|
auto& cameraTransform = camera.getTransform();
|
|
cameraTransform.reference = &transform;
|
|
camera.setStereoscopic(false);
|
|
|
|
::roundRobin.lights.emplace_back(this);
|
|
|
|
auto& renderMode = this->getComponent<uf::renderer::RenderTargetRenderMode>();
|
|
renderMode.metadata.type = "depth";
|
|
renderMode.metadata.pipeline = "depth";
|
|
if ( uf::renderer::settings::pipelines::culling ) {
|
|
renderMode.metadata.pipelines.emplace_back("culling");
|
|
}
|
|
renderMode.metadata.json["descriptor"]["depth bias"] = metadataJson["light"]["bias"];
|
|
renderMode.metadata.json["descriptor"]["renderMode"] = metadataJson["renderMode"];
|
|
|
|
if ( metadataJson["light"]["type"].as<uf::stl::string>() == "point" ) {
|
|
metadataJson["light"]["fov"] = 90.0f;
|
|
renderMode.metadata.subpasses = 6;
|
|
}
|
|
|
|
float fov = metadataJson["light"]["fov"].as<float>(90) * (3.14159265358f / 180.0f);
|
|
pod::Vector2f radius = uf::vector::decode( metadataJson["light"]["radius"], pod::Vector2f{0.001, 32} );
|
|
pod::Vector2ui size{};
|
|
if ( ext::json::isArray( metadataJson["light"]["resolution"] ) ) {
|
|
size = uf::vector::decode( metadataJson["light"]["resolution"], pod::Vector2ui{ uf::renderer::settings::width, uf::renderer::settings::height } );
|
|
} else if ( metadataJson["light"]["resolution"].is<float>() ) {
|
|
size_t r = metadataJson["light"]["resolution"].as<size_t>();
|
|
size.x = r;
|
|
size.y = r;
|
|
} else {
|
|
size = pod::Vector2ui{ uf::renderer::settings::width, uf::renderer::settings::height };
|
|
}
|
|
if ( radius.y < radius.x ) radius.y = 256;
|
|
camera.setProjection( uf::matrix::perspective( fov, (float) size.x / (float) size.y, radius.x, radius.y ) );
|
|
// camera.update(true);
|
|
|
|
uf::stl::string name = "RT:" + std::to_string((int) this->getUid());
|
|
renderMode.blitter.process = false;
|
|
renderMode.width = size.x;
|
|
renderMode.height = size.y;
|
|
uf::renderer::addRenderMode( &renderMode, name );
|
|
}
|
|
|
|
UF_BEHAVIOR_METADATA_BIND_SERIALIZER_HOOKS(metadata, metadataJson);
|
|
}
|
|
void ext::LightBehavior::tick( uf::Object& self ) {
|
|
#if !UF_ENV_DREAMCAST
|
|
auto& metadata = this->getComponent<ext::LightBehavior::Metadata>();
|
|
auto& metadataJson = this->getComponent<uf::Serializer>();
|
|
if ( this->hasComponent<uf::renderer::RenderTargetRenderMode>() ) {
|
|
auto& renderMode = this->getComponent<uf::renderer::RenderTargetRenderMode>();
|
|
renderMode.setTarget("");
|
|
}
|
|
#if UF_ENTITY_METADATA_USE_JSON
|
|
metadata.deserialize(self, metadataJson);
|
|
#endif
|
|
#if 0
|
|
// light fade action
|
|
if ( ext::json::isObject( metadataJson["light"]["fade"] ) ) {
|
|
if ( ext::json::isNull( metadataJson["light"]["backup"]["power"] ) ) {
|
|
metadataJson["light"]["backup"]["power"] = metadataJson["light"]["power"];
|
|
}
|
|
if ( ext::json::isNull( metadataJson["light"]["backup"]["color"] ) ) {
|
|
metadataJson["light"]["backup"]["color"] = metadataJson["light"]["color"];
|
|
}
|
|
// fade towards
|
|
int direction = metadataJson["light"]["fade"]["increment"].as<bool>() ? 1 : -1;
|
|
metadataJson["light"]["fade"]["timer"] = metadataJson["light"]["fade"]["timer"].as<float>() + metadataJson["light"]["fade"]["rate"].as<float>() * uf::physics::time::delta * direction;
|
|
|
|
// 0 .. delta .. 1 .. (1 + timeout * 0.5)
|
|
if ( direction == 1 && metadataJson["light"]["fade"]["timer"].as<float>() >= 0.5f * metadataJson["light"]["fade"]["timeout"].as<float>() + 1.0f ) {
|
|
metadataJson["light"]["fade"]["increment"] = false;
|
|
} else if ( direction == -1 && metadataJson["light"]["fade"]["timer"].as<float>() <= -0.5f * metadataJson["light"]["fade"]["timeout"].as<float>() ) {
|
|
metadataJson["light"]["fade"]["increment"] = true;
|
|
}
|
|
{
|
|
float delta = metadataJson["light"]["fade"]["timer"].as<float>();
|
|
delta = std::clamp( delta, 0.f, 1.f );
|
|
if ( metadataJson["light"]["fade"]["power"].is<double>() ) {
|
|
metadataJson["light"]["power"] = uf::math::lerp( metadataJson["light"]["backup"]["power"].as<float>(), metadataJson["light"]["fade"]["power"].as<float>(), delta );
|
|
}
|
|
if ( ext::json::isArray( metadataJson["light"]["fade"]["color"] ) ) {
|
|
pod::Vector3f fadeColor; {
|
|
fadeColor.x = metadataJson["light"]["fade"]["color"][0].as<float>();
|
|
fadeColor.y = metadataJson["light"]["fade"]["color"][1].as<float>();
|
|
fadeColor.z = metadataJson["light"]["fade"]["color"][2].as<float>();
|
|
}
|
|
pod::Vector3f origColor; {
|
|
origColor.x = metadataJson["light"]["backup"]["color"][0].as<float>();
|
|
origColor.y = metadataJson["light"]["backup"]["color"][1].as<float>();
|
|
origColor.z = metadataJson["light"]["backup"]["color"][2].as<float>();
|
|
}
|
|
pod::Vector3f color = uf::vector::lerp( origColor, fadeColor, delta );
|
|
|
|
metadataJson["light"]["color"][0] = color[0];
|
|
metadataJson["light"]["color"][1] = color[1];
|
|
metadataJson["light"]["color"][2] = color[2];
|
|
}
|
|
}
|
|
}
|
|
// light flicker action
|
|
if ( ext::json::isObject( metadataJson["light"]["flicker"] ) ) {
|
|
float r = (rand() % 100) / 100.0;
|
|
float rate = metadataJson["light"]["flicker"]["rate"].as<float>();
|
|
if ( ext::json::isNull( metadataJson["light"]["backup"]["power"] ) ) {
|
|
metadataJson["light"]["backup"]["power"] = metadataJson["light"]["power"];
|
|
}
|
|
metadataJson["light"]["flicker"]["timer"] = metadataJson["light"]["flicker"]["timer"].as<float>() + uf::physics::time::delta;
|
|
if ( metadataJson["light"]["flicker"]["timer"].as<float>() >= metadataJson["light"]["flicker"]["timeout"].as<float>() ) {
|
|
metadataJson["light"]["flicker"]["timer"] = 0;
|
|
metadataJson["light"]["power"] = (r > rate) ? metadataJson["light"]["flicker"]["power"].as<float>() : metadataJson["light"]["backup"]["power"].as<float>();
|
|
}
|
|
}
|
|
#endif
|
|
// limit updating our shadow map
|
|
#if UF_USE_VULKAN
|
|
if ( this->hasComponent<uf::renderer::RenderTargetRenderMode>() ) {
|
|
auto& renderMode = this->getComponent<uf::renderer::RenderTargetRenderMode>();
|
|
// enable renderer every X seconds
|
|
if ( metadata.renderer.limiter > 0 ) {
|
|
if ( metadata.renderer.timer > metadata.renderer.limiter ) {
|
|
metadata.renderer.timer = 0;
|
|
renderMode.execute = true;
|
|
renderMode.metadata.limiter.execute = true;
|
|
} else {
|
|
metadata.renderer.timer = metadata.renderer.timer + uf::physics::time::delta;
|
|
renderMode.execute = false;
|
|
renderMode.metadata.limiter.execute = false;
|
|
}
|
|
} else {
|
|
// round robin, enable if it's the light's current turn
|
|
if ( metadata.renderer.mode == "round robin" ) {
|
|
if ( ::roundRobin.current < ::roundRobin.lights.size() ) {
|
|
renderMode.execute = ::roundRobin.lights[::roundRobin.current] == this;
|
|
renderMode.metadata.limiter.execute = ::roundRobin.lights[::roundRobin.current] == this;
|
|
}
|
|
// render only if the light is used
|
|
} else if ( metadata.renderer.mode == "occlusion" ) {
|
|
renderMode.execute = metadata.renderer.rendered;
|
|
renderMode.metadata.limiter.execute = metadata.renderer.rendered;
|
|
// light baking, but sadly re-bakes every time the command buffer is recorded
|
|
} else if ( metadata.renderer.mode == "once" ) {
|
|
renderMode.execute = !metadata.renderer.rendered;
|
|
renderMode.metadata.limiter.execute = !metadata.renderer.rendered;
|
|
|
|
metadata.renderer.rendered = true;
|
|
} else if ( metadata.renderer.mode == "in-range" ) {
|
|
metadata.renderer.rendered = false;
|
|
}
|
|
}
|
|
// skip if we're handling the camera view matrix position ourselves
|
|
if ( /*renderMode.execute*/ renderMode.metadata.limiter.execute && !metadata.renderer.external ) {
|
|
auto& camera = this->getComponent<uf::Camera>();
|
|
// omni light
|
|
if ( metadata.shadows && std::abs(metadata.type) == 1 ) {
|
|
auto transform = camera.getTransform();
|
|
for ( size_t i = 0; i < 6; ++i ) {
|
|
transform.orientation = ::rotations[i];
|
|
auto model = uf::transform::model( transform, false );
|
|
camera.setView( uf::matrix::inverse( model ), i );
|
|
}
|
|
} else {
|
|
// camera.update(true);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
#if UF_ENTITY_METADATA_USE_JSON
|
|
metadata.serialize(self, metadataJson);
|
|
#endif
|
|
#endif
|
|
}
|
|
void ext::LightBehavior::render( uf::Object& self ){}
|
|
void ext::LightBehavior::destroy( uf::Object& self ){
|
|
if ( this->hasComponent<uf::renderer::RenderTargetRenderMode>() ) {
|
|
auto& renderMode = this->getComponent<uf::renderer::RenderTargetRenderMode>();
|
|
|
|
uf::renderer::removeRenderMode( &renderMode, false );
|
|
this->deleteComponent<uf::renderer::RenderTargetRenderMode>();
|
|
}
|
|
|
|
::roundRobin.lights.erase(std::remove(::roundRobin.lights.begin(), ::roundRobin.lights.end(), this), ::roundRobin.lights.end());
|
|
::roundRobin.current = 0;
|
|
}
|
|
void ext::LightBehavior::Metadata::serialize( uf::Object& self, uf::Serializer& serializer ){
|
|
serializer["light"]["color"] = uf::vector::encode( /*this->*/color );
|
|
serializer["light"]["power"] = /*this->*/power;
|
|
serializer["light"]["bias"]["shader"] = /*this->*/bias;
|
|
serializer["light"]["shadows"] = /*this->*/shadows;
|
|
serializer["light"]["global"] = /*this->*/global;
|
|
serializer["system"]["renderer"]["mode"] = /*this->*/renderer.mode;
|
|
serializer["light"]["external update"] = /*this->*/renderer.external;
|
|
// serializer["system"]["renderer"]["timer"] = /*this->*/renderer.limiter;
|
|
switch ( abs(/*this->*/type) ) {
|
|
case 0: serializer["light"]["type"] = "point"; break;
|
|
case 1: serializer["light"]["type"] = "point"; break;
|
|
case 2: serializer["light"]["type"] = "spot"; break;
|
|
}
|
|
if ( type < 0 ) {
|
|
serializer["light"]["dynamic"] = true;
|
|
}
|
|
}
|
|
void ext::LightBehavior::Metadata::deserialize( uf::Object& self, uf::Serializer& serializer ){
|
|
/*this->*/color = uf::vector::decode( serializer["light"]["color"], /*this->*/color );
|
|
/*this->*/power = serializer["light"]["power"].as(/*this->*/power);
|
|
/*this->*/bias = serializer["light"]["bias"]["shader"].as(/*this->*/bias);
|
|
/*this->*/shadows = serializer["light"]["shadows"].as(/*this->*/shadows);
|
|
/*this->*/global = serializer["light"]["global"].as(/*this->*/global);
|
|
/*this->*/renderer.mode = serializer["system"]["renderer"]["mode"].as(/*this->*/renderer.mode);
|
|
// /*this->*/renderer.rendered = false;
|
|
/*this->*/renderer.external = serializer["light"]["external update"].as(/*this->*/renderer.external);
|
|
// /*this->*/renderer.limiter = serializer["system"]["renderer"]["timer"].as<float>(/*this->*/renderer.limiter);
|
|
|
|
if ( serializer["light"]["type"].is<int32_t>() ) {
|
|
/*this->*/type = serializer["light"]["type"].as(/*this->*/type);
|
|
} else if ( serializer["light"]["type"].is<uf::stl::string>() ) {
|
|
uf::stl::string lightType = serializer["light"]["type"].as<uf::stl::string>();
|
|
if ( lightType == "point" ) /*this->*/type = 1;
|
|
else if ( lightType == "spot" ) /*this->*/type = 2;
|
|
}
|
|
if ( serializer["light"]["dynamic"].as<bool>() ) /*this->*/type = -/*this->*/type;
|
|
}
|
|
#undef this |