#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& metadataJson = 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 ( !metadataJson["light"]["bias"]["shader"].is() ) { metadataJson["light"]["bias"]["shader"] = 0.000000005f; } */ if ( !metadataJson["light"]["bias"]["constant"].is() ) { metadataJson["light"]["bias"]["constant"] = 0.00005f; } if ( !metadataJson["light"]["type"].is() ) { metadataJson["light"]["type"] = "point"; } if ( !ext::json::isArray( metadataJson["light"]["color"] ) ) { metadataJson["light"]["color"][0] = 1; //metadataJson["light"]["color"]["random"].as() ? (rand() % 100) / 100.0 : 1; metadataJson["light"]["color"][1] = 1; //metadataJson["light"]["color"]["random"].as() ? (rand() % 100) / 100.0 : 1; metadataJson["light"]["color"][2] = 1; //metadataJson["light"]["color"]["random"].as() ? (rand() % 100) / 100.0 : 1; } #if UF_USE_OPENGL metadataJson["light"]["shadows"] = false; #endif if ( !sceneMetadata["system"]["config"]["engine"]["scenes"]["lights"]["shadows"].as() ) { metadataJson["light"]["shadows"] = false; } if ( metadataJson["light"]["shadows"].as() ) { 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(); bounds.y = metadataJson["light"]["radius"][1].as(); 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() ) { size_t size = metadataJson["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"] = metadataJson["light"]["bias"]; renderMode.metadata["renderMode"] = metadataJson["renderMode"]; if ( metadataJson["light"]["type"].as() == "point" ) { metadataJson["light"]["fov"] = 90.0f; renderMode.metadata["subpasses"] = 6; } if ( metadataJson["light"]["fov"].is() ) { camera.setFov( metadataJson["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; // 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(); metadata.bias = metadataJson["light"]["bias"]["shader"].as(); metadata.shadows = metadataJson["light"]["shadows"].as(); metadata.renderer.mode = metadataJson["system"]["renderer"]["mode"].as(); metadata.renderer.rendered = false; metadata.renderer.external = metadataJson["light"]["external update"].as(); metadata.renderer.limiter = metadataJson["system"]["renderer"]["timer"].as(); if ( metadataJson["light"]["type"].is() ) { metadata.type = metadataJson["light"]["type"].as(); } else if ( metadataJson["light"]["type"].is() ) { std::string lightType = metadataJson["light"]["type"].as(); 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(); auto& metadataJson = this->getComponent(); if ( this->hasComponent() ) { auto& renderMode = this->getComponent(); 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(); if ( !metadata.bias ) metadata.bias = metadataJson["light"]["bias"]["shader"].as(); if ( !metadata.shadows ) metadata.shadows = metadataJson["light"]["shadows"].as(); if ( metadata.renderer.mode == "" ) metadata.renderer.mode = metadataJson["system"]["renderer"]["mode"].as(); if ( !metadata.renderer.external ) metadata.renderer.external = metadataJson["light"]["external update"].as(); if ( !metadata.renderer.limiter ) metadata.renderer.limiter = metadataJson["system"]["renderer"]["timer"].as(); if ( !metadata.type ) { if ( metadataJson["light"]["type"].is() ) { metadata.type = metadataJson["light"]["type"].as(); } else if ( metadataJson["light"]["type"].is() ) { std::string lightType = metadataJson["light"]["type"].as(); if ( lightType == "point" ) metadata.type = 1; else if ( lightType == "spot" ) metadata.type = 2; } if ( metadataJson["light"]["dynamic"].as() ) 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() ? 1 : -1; metadataJson["light"]["fade"]["timer"] = metadataJson["light"]["fade"]["timer"].as() + metadataJson["light"]["fade"]["rate"].as() * uf::physics::time::delta * direction; // 0 .. delta .. 1 .. (1 + timeout * 0.5) if ( direction == 1 && metadataJson["light"]["fade"]["timer"].as() >= 0.5f * metadataJson["light"]["fade"]["timeout"].as() + 1.0f ) { metadataJson["light"]["fade"]["increment"] = false; } else if ( direction == -1 && metadataJson["light"]["fade"]["timer"].as() <= -0.5f * metadataJson["light"]["fade"]["timeout"].as() ) { metadataJson["light"]["fade"]["increment"] = true; } { float delta = metadataJson["light"]["fade"]["timer"].as(); delta = std::clamp( delta, 0.f, 1.f ); if ( metadataJson["light"]["fade"]["power"].is() ) { metadataJson["light"]["power"] = uf::math::lerp( metadataJson["light"]["backup"]["power"].as(), metadataJson["light"]["fade"]["power"].as(), delta ); } if ( ext::json::isArray( metadataJson["light"]["fade"]["color"] ) ) { pod::Vector3f fadeColor; { fadeColor.x = metadataJson["light"]["fade"]["color"][0].as(); fadeColor.y = metadataJson["light"]["fade"]["color"][1].as(); fadeColor.z = metadataJson["light"]["fade"]["color"][2].as(); } pod::Vector3f origColor; { origColor.x = metadataJson["light"]["backup"]["color"][0].as(); origColor.y = metadataJson["light"]["backup"]["color"][1].as(); origColor.z = metadataJson["light"]["backup"]["color"][2].as(); } 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(); if ( ext::json::isNull( metadataJson["light"]["backup"]["power"] ) ) { metadataJson["light"]["backup"]["power"] = metadataJson["light"]["power"]; } metadataJson["light"]["flicker"]["timer"] = metadataJson["light"]["flicker"]["timer"].as() + uf::physics::time::delta; if ( metadataJson["light"]["flicker"]["timer"].as() >= metadataJson["light"]["flicker"]["timeout"].as() ) { metadataJson["light"]["flicker"]["timer"] = 0; metadataJson["light"]["power"] = (r > rate) ? metadataJson["light"]["flicker"]["power"].as() : metadataJson["light"]["backup"]["power"].as(); } } #endif // limit updating our shadow map if ( this->hasComponent() ) { // skip if we're handling the camera view matrix position ourselves if ( !metadata.renderer.external ) { auto& camera = this->getComponent(); // omni light if ( metadata.shadows && std::abs(metadata.type) == 1 ) { auto transform = camera.getTransform(); std::vector> 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(); 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() ) { 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