engine/ext/behaviors/light/behavior.cpp
2021-04-20 00:00:00 -05:00

291 lines
14 KiB
C++

#include "behavior.h"
#include <uf/utils/renderer/renderer.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;
std::vector<uf::Object*> lights;
} roundRobin;
}
UF_BEHAVIOR_REGISTER_CPP(ext::LightBehavior)
#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& sceneMetadata = scene.getComponent<uf::Serializer>();
if ( !sceneMetadata["system"]["lights"]["round robin hook bound"].as<bool>() ) {
sceneMetadata["system"]["lights"]["round robin hook bound"] = true;
scene.addHook("game:Frame.Start", [&]( const 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<std::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 UF_USE_OPENGL
metadataJson["light"]["shadows"] = false;
#endif
if ( !sceneMetadata["system"]["config"]["engine"]["scenes"]["lights"]["shadows"].as<bool>() ) {
metadataJson["light"]["shadows"] = false;
}
if ( metadataJson["light"]["shadows"].as<bool>() ) {
auto& cameraTransform = camera.getTransform();
cameraTransform.reference = &transform;
camera.setStereoscopic(false);
if ( ext::json::isArray( metadataJson["light"]["radius"] ) ) {
auto bounds = camera.getBounds();
bounds.x = metadataJson["light"]["radius"][0].as<float>();
bounds.y = metadataJson["light"]["radius"][1].as<float>();
camera.setBounds(bounds);
}
if ( ext::json::isArray( metadataJson["light"]["resolution"] ) ) {
camera.setSize( 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 size = metadataJson["light"]["resolution"].as<size_t>();
camera.setSize( {size, size} );
} else {
camera.setSize( pod::Vector2ui{ uf::renderer::settings::width, uf::renderer::settings::height } );
}
::roundRobin.lights.emplace_back(this);
auto& renderMode = this->getComponent<uf::renderer::RenderTargetRenderMode>();
renderMode.metadata["type"] = "depth";
renderMode.metadata["depth bias"] = metadataJson["light"]["bias"];
renderMode.metadata["renderMode"] = metadataJson["renderMode"];
if ( metadataJson["light"]["type"].as<std::string>() == "point" ) {
metadataJson["light"]["fov"] = 90.0f;
renderMode.metadata["subpasses"] = 6;
}
if ( metadataJson["light"]["fov"].is<float>() ) {
camera.setFov( metadataJson["light"]["fov"].as<float>() );
}
camera.updateView();
camera.updateProjection();
std::string name = "RT:" + std::to_string((int) this->getUid());
uf::renderer::addRenderMode( &renderMode, name );
renderMode.blitter.process = false;
auto& cameraSize = camera.getSize();
renderMode.width = cameraSize.x;
renderMode.height = cameraSize.y;
// UF_DEBUG_MSG(this->getName() << ": " << renderMode.metadata << " | " << metadataJson["light"]);
}
metadata.serialize = [&](){
metadataJson["light"]["color"] = uf::vector::encode( metadata.color );
metadataJson["light"]["power"] = metadata.power;
metadataJson["light"]["bias"]["shader"] = metadata.bias;
metadataJson["light"]["shadows"] = metadata.shadows;
metadataJson["system"]["renderer"]["mode"] = metadata.renderer.mode;
metadataJson["light"]["external update"] = metadata.renderer.external;
metadataJson["system"]["renderer"]["timer"] = metadata.renderer.limiter;
switch ( metadata.type ) {
case 0: metadataJson["light"]["type"] = "point"; break;
case 1: metadataJson["light"]["type"] = "spot"; break;
case 2: metadataJson["light"]["type"] = "spot"; break;
}
};
metadata.deserialize = [&](){
metadata.color = uf::vector::decode( metadataJson["light"]["color"], pod::Vector3f{1,1,1} );
metadata.power = metadataJson["light"]["power"].as<float>();
metadata.bias = metadataJson["light"]["bias"]["shader"].as<float>();
metadata.shadows = metadataJson["light"]["shadows"].as<bool>();
metadata.renderer.mode = metadataJson["system"]["renderer"]["mode"].as<std::string>();
metadata.renderer.rendered = false;
metadata.renderer.external = metadataJson["light"]["external update"].as<bool>();
metadata.renderer.limiter = metadataJson["system"]["renderer"]["timer"].as<float>();
if ( metadataJson["light"]["type"].is<size_t>() ) {
metadata.type = metadataJson["light"]["type"].as<size_t>();
} else if ( metadataJson["light"]["type"].is<std::string>() ) {
std::string lightType = metadataJson["light"]["type"].as<std::string>();
if ( lightType == "point" ) metadata.type = 0;
else if ( lightType == "spot" ) metadata.type = 1;
}
};
this->addHook( "object:UpdateMetadata.%UID%", metadata.deserialize);
metadata.deserialize();
}
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();
#else
if ( !metadata.color ) metadata.color = uf::vector::decode( metadataJson["light"]["color"], pod::Vector3f{1,1,1} );
if ( !metadata.power ) metadata.power = metadataJson["light"]["power"].as<float>();
if ( !metadata.bias ) metadata.bias = metadataJson["light"]["bias"]["shader"].as<float>();
if ( !metadata.shadows ) metadata.shadows = metadataJson["light"]["shadows"].as<bool>();
if ( metadata.renderer.mode == "" ) metadata.renderer.mode = metadataJson["system"]["renderer"]["mode"].as<std::string>();
if ( !metadata.renderer.external ) metadata.renderer.external = metadataJson["light"]["external update"].as<bool>();
if ( !metadata.renderer.limiter ) metadata.renderer.limiter = metadataJson["system"]["renderer"]["timer"].as<float>();
if ( !metadata.type ) {
if ( metadataJson["light"]["type"].is<size_t>() ) {
metadata.type = metadataJson["light"]["type"].as<size_t>();
} else if ( metadataJson["light"]["type"].is<std::string>() ) {
std::string lightType = metadataJson["light"]["type"].as<std::string>();
if ( lightType == "point" ) metadata.type = 1;
else if ( lightType == "spot" ) metadata.type = 2;
}
if ( metadataJson["light"]["dynamic"].as<bool>() ) metadata.type = -metadata.type;
}
#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 ( this->hasComponent<uf::renderer::RenderTargetRenderMode>() ) {
// skip if we're handling the camera view matrix position ourselves
if ( !metadata.renderer.external ) {
auto& camera = this->getComponent<uf::Camera>();
// omni light
if ( metadata.shadows && std::abs(metadata.type) == 1 ) {
auto transform = camera.getTransform();
std::vector<pod::Quaternion<>> rotations = {
uf::quaternion::axisAngle( { 0, 1, 0 }, 0 * 1.57079633 ),
uf::quaternion::axisAngle( { 0, 1, 0 }, 1 * 1.57079633 ),
uf::quaternion::axisAngle( { 0, 1, 0 }, 2 * 1.57079633 ),
uf::quaternion::axisAngle( { 0, 1, 0 }, 3 * 1.57079633 ),
uf::quaternion::axisAngle( { 1, 0, 0 }, 1 * 1.57079633 ),
uf::quaternion::axisAngle( { 1, 0, 0 }, 3 * 1.57079633 ),
};
for ( size_t i = 0; i < rotations.size(); ++i ) {
auto transform = camera.getTransform();
transform.orientation = rotations[i];
camera.setView( uf::matrix::inverse( uf::transform::model( transform, false ) ), i );
}
} else {
camera.updateView();
}
}
bool execute = true;
// enable renderer every X seconds
if ( metadata.renderer.limiter > 0 ) {
if ( metadata.renderer.timer > metadata.renderer.limiter ) {
metadata.renderer.timer = 0;
execute = true;
} else {
metadata.renderer.timer = metadata.renderer.timer + uf::physics::time::delta;
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() ) execute = ::roundRobin.lights[::roundRobin.current] == this;
// render only if the light is used
} else if ( metadata.renderer.mode == "occlusion" ) {
execute = metadata.renderer.rendered;
// light baking, but sadly re-bakes every time the command buffer is recorded
} else if ( metadata.renderer.mode == "once" ) {
execute = !metadata.renderer.rendered;
metadata.renderer.rendered = true;
}
}
auto& renderMode = this->getComponent<uf::renderer::RenderTargetRenderMode>();
renderMode.execute = execute;
}
#if UF_ENTITY_METADATA_USE_JSON
metadata.serialize();
#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;
}
#undef this