#include "behavior.h" #include #include #include #include namespace { struct { size_t current = 0; std::vector lights; } roundRobin; } UF_BEHAVIOR_REGISTER_CPP(ext::LightBehavior) #define this (&self) void ext::LightBehavior::initialize( uf::Object& self ) { auto& metadata = this->getComponent(); auto& transform = this->getComponent>(); auto& camera = this->getComponent(); auto& scene = uf::scene::getCurrentScene(); auto& controller = scene.getController(); auto& sceneMetadata = scene.getComponent(); if ( !sceneMetadata["system"]["lights"]["round robin hook bound"].as() ) { 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 ( !metadata["light"]["bias"]["constant"].is() ) { metadata["light"]["bias"]["constant"] = 0.00005f; } if ( !ext::json::isArray( metadata["light"]["color"] ) ) { metadata["light"]["color"][0] = 1; //metadata["light"]["color"]["random"].as() ? (rand() % 100) / 100.0 : 1; metadata["light"]["color"][1] = 1; //metadata["light"]["color"]["random"].as() ? (rand() % 100) / 100.0 : 1; metadata["light"]["color"][2] = 1; //metadata["light"]["color"]["random"].as() ? (rand() % 100) / 100.0 : 1; } // metadata["light"]["shadows"] = false; if ( metadata["light"]["shadows"].as() ) { auto& cameraTransform = camera.getTransform(); cameraTransform.reference = &transform; camera.setStereoscopic(false); if ( ext::json::isArray( metadata["light"]["radius"] ) ) { auto bounds = camera.getBounds(); bounds.x = metadata["light"]["radius"][0].as(); bounds.y = metadata["light"]["radius"][1].as(); camera.setBounds(bounds); } if ( ext::json::isArray( metadata["light"]["resolution"] ) ) { camera.setSize( uf::vector::decode( metadata["light"]["resolution"], pod::Vector2ui{ uf::renderer::settings::width, uf::renderer::settings::height } ) ); } else if ( metadata["light"]["resolution"].is() ) { size_t size = metadata["light"]["resolution"].as(); 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(); renderMode.metadata["type"] = "depth"; renderMode.metadata["depth bias"] = metadata["light"]["bias"]; if ( metadata["light"]["type"].as() == "point" ) { metadata["light"]["fov"] = 90.0f; renderMode.metadata["subpasses"] = 6; } if ( metadata["light"]["fov"].is() ) { camera.setFov( metadata["light"]["fov"].as() ); } 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; } } void ext::LightBehavior::tick( uf::Object& self ) { auto& metadata = this->getComponent(); if ( this->hasComponent() ) { auto& renderMode = this->getComponent(); renderMode.setTarget(""); } auto& transform = this->getComponent>(); auto& parent = this->getParent(); auto& parentMetadata = parent.getComponent(); auto& parentTransform = parent.getComponent>(); // copy from our parent // if ( parentMetadata["system"]["name"] == "Light" ) { if ( metadata["light"]["bound"].as() ) { metadata["light"] = parentMetadata["light"]; metadata["light"]["bound"] = true; if ( metadata["light"]["static"].as() ) { auto flatten = uf::transform::flatten( parentTransform ); transform.position = flatten.position; } } else { // light fade action if ( ext::json::isObject( metadata["light"]["fade"] ) ) { if ( ext::json::isNull( metadata["light"]["backup"]["power"] ) ) { metadata["light"]["backup"]["power"] = metadata["light"]["power"]; } if ( ext::json::isNull( metadata["light"]["backup"]["color"] ) ) { metadata["light"]["backup"]["color"] = metadata["light"]["color"]; } // fade towards int direction = metadata["light"]["fade"]["increment"].as() ? 1 : -1; metadata["light"]["fade"]["timer"] = metadata["light"]["fade"]["timer"].as() + metadata["light"]["fade"]["rate"].as() * uf::physics::time::delta * direction; // 0 .. delta .. 1 .. (1 + timeout * 0.5) if ( direction == 1 && metadata["light"]["fade"]["timer"].as() >= 0.5f * metadata["light"]["fade"]["timeout"].as() + 1.0f ) { metadata["light"]["fade"]["increment"] = false; } else if ( direction == -1 && metadata["light"]["fade"]["timer"].as() <= -0.5f * metadata["light"]["fade"]["timeout"].as() ) { metadata["light"]["fade"]["increment"] = true; } { float delta = metadata["light"]["fade"]["timer"].as(); delta = std::clamp( delta, 0.f, 1.f ); if ( metadata["light"]["fade"]["power"].is() ) { metadata["light"]["power"] = uf::math::lerp( metadata["light"]["backup"]["power"].as(), metadata["light"]["fade"]["power"].as(), delta ); } if ( ext::json::isArray( metadata["light"]["fade"]["color"] ) ) { pod::Vector3f fadeColor; { fadeColor.x = metadata["light"]["fade"]["color"][0].as(); fadeColor.y = metadata["light"]["fade"]["color"][1].as(); fadeColor.z = metadata["light"]["fade"]["color"][2].as(); } pod::Vector3f origColor; { origColor.x = metadata["light"]["backup"]["color"][0].as(); origColor.y = metadata["light"]["backup"]["color"][1].as(); origColor.z = metadata["light"]["backup"]["color"][2].as(); } pod::Vector3f color = uf::vector::lerp( origColor, fadeColor, delta ); metadata["light"]["color"][0] = color[0]; metadata["light"]["color"][1] = color[1]; metadata["light"]["color"][2] = color[2]; } } } // light flicker action if ( ext::json::isObject( metadata["light"]["flicker"] ) ) { float r = (rand() % 100) / 100.0; float rate = metadata["light"]["flicker"]["rate"].as(); if ( ext::json::isNull( metadata["light"]["backup"]["power"] ) ) { metadata["light"]["backup"]["power"] = metadata["light"]["power"]; } metadata["light"]["flicker"]["timer"] = metadata["light"]["flicker"]["timer"].as() + uf::physics::time::delta; if ( metadata["light"]["flicker"]["timer"].as() >= metadata["light"]["flicker"]["timeout"].as() ) { metadata["light"]["flicker"]["timer"] = 0; metadata["light"]["power"] = (r > rate) ? metadata["light"]["flicker"]["power"].as() : metadata["light"]["backup"]["power"].as(); } } } // skip if we're handling the camera view matrix position ourselves if ( !metadata["light"]["external update"].as() ) { auto& camera = this->getComponent(); // omni light if ( metadata["light"]["shadows"].as() && metadata["light"]["type"].as() == "point" ) { auto transform = camera.getTransform(); std::vector> rotations = { /* {0, 0, 0, 1}, {0, 0.707107, 0, -0.707107}, {0, 1, 0, 0}, {0, 0.707107, 0, 0.707107}, {-0.707107, 0, 0, -0.707107}, {0.707107, 0, 0, -0.707107}, */ 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(); } // for ( std::size_t i = 0; i < 2; ++i ) camera.setView( uf::matrix::inverse( uf::transform::model( transform ) ), i ); } // limit updating our shadow map if ( this->hasComponent() ) { bool execute = true; // enable renderer every X seconds if ( metadata["system"]["renderer"]["limiter"].is() ) { if ( metadata["system"]["renderer"]["timer"].as() > metadata["system"]["renderer"]["limiter"].as() ) { metadata["system"]["renderer"]["timer"] = 0; execute = true; } else { metadata["system"]["renderer"]["timer"] = metadata["system"]["renderer"]["timer"].as() + uf::physics::time::delta; execute = false; } } else if ( metadata["system"]["renderer"]["mode"].is() ) { std::string mode = metadata["system"]["renderer"]["mode"].as(); // round robin, enable if it's the light's current turn if ( mode == "round robin" ) { if ( ::roundRobin.current < ::roundRobin.lights.size() ) execute = ::roundRobin.lights[::roundRobin.current] == this; // render only if the light is used } else if ( mode == "occlusion" ) { execute = metadata["system"]["renderer"]["rendered"].as(); // light baking, but sadly re-bakes every time the command buffer is recorded } else if ( mode == "once" ) { execute = !metadata["system"]["renderer"]["rendered"].as(); metadata["system"]["renderer"]["rendered"] = true; } } auto& renderMode = this->getComponent(); renderMode.execute = execute; } } void ext::LightBehavior::render( uf::Object& self ){ } void ext::LightBehavior::destroy( uf::Object& self ){ if ( this->hasComponent() ) { auto& renderMode = this->getComponent(); uf::renderer::removeRenderMode( &renderMode, false ); this->deleteComponent(); } ::roundRobin.lights.erase(std::remove(::roundRobin.lights.begin(), ::roundRobin.lights.end(), this), ::roundRobin.lights.end()); ::roundRobin.current = 0; } #undef this