#include "behavior.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../../ext.h" #include "../../gui/gui.h" UF_BEHAVIOR_REGISTER_CPP(ext::ExtSceneBehavior) #define this ((uf::Scene*) &self) void ext::ExtSceneBehavior::initialize( uf::Object& self ) { uf::Asset& assetLoader = this->getComponent(); uf::Serializer& metadata = this->getComponent(); this->addHook( "system:Quit.%UID%", [&](ext::json::Value& json){ std::cout << json << std::endl; ext::ready = false; }); this->addHook( "world:Music.LoadPrevious.%UID%", [&](ext::json::Value& json){ if ( metadata["previous bgm"]["filename"] == "" ) return; std::string filename = metadata["previous bgm"]["filename"].as(); float timestamp = metadata["previous bgm"]["timestamp"].as(); // std::cout << metadata["previous bgm"] << std::endl; uf::Audio& audio = this->getComponent(); if ( audio.playing() ) { metadata["previous bgm"]["filename"] = audio.getFilename(); metadata["previous bgm"]["timestamp"] = audio.getTime(); audio.stop(); } audio.load(filename); audio.setVolume(metadata["volumes"]["bgm"].as()); audio.setTime(timestamp); audio.play(); }); this->addHook( "asset:Load.%UID%", [&](ext::json::Value& json){ std::string filename = json["filename"].as(); if ( uf::io::extension(filename) != "ogg" ) return; const uf::Audio* audioPointer = NULL; try { audioPointer = &assetLoader.get(filename); } catch ( ... ) {} if ( !audioPointer ) return; uf::Audio& audio = this->getComponent(); if ( audio.playing() ) audio.stop(); audio.load(filename); audio.setVolume(metadata["volumes"]["bgm"].as()); audio.play(); }); this->addHook( "menu:Pause", [&](ext::json::Value& json){ static uf::Timer timer(false); if ( !timer.running() ) timer.start( uf::Time<>(-1000000) ); if ( timer.elapsed().asDouble() < 1 ) return; timer.reset(); uf::Object* manager = (uf::Object*) this->globalFindByName("Gui Manager"); if ( !manager ) return; uf::Serializer payload; std::string config = metadata["menus"]["pause"].is() ? metadata["menus"]["pause"].as() : "/scenes/worldscape/gui/pause/menu.json"; uf::Object& gui = manager->loadChild(config, false); payload["uid"] = gui.getUid(); uf::Serializer& metadata = gui.getComponent(); metadata["menu"] = json["menu"]; gui.initialize(); // return payload; }); this->addHook( "world:Entity.LoadAsset", [&](ext::json::Value& json){ std::string asset = json["asset"].as(); std::string uid = json["uid"].as(); assetLoader.load(asset, "asset:Load." + uid); }); this->addHook( "shader:Update.%UID%", [&](ext::json::Value& _json){ uf::Serializer json = _json; json["mode"] = json["mode"].as() | metadata["system"]["renderer"]["shader"]["mode"].as(); metadata["system"]["renderer"]["shader"]["mode"] = json["mode"]; metadata["system"]["renderer"]["shader"]["parameters"] = json["parameters"]; }); /* store viewport size */ { // metadata["system"]["window"]["size"]["x"] = uf::renderer::settings::width; // metadata["system"]["window"]["size"]["y"] = uf::renderer::settings::height; // ext::gui::size.current.x = uf::renderer::settings::width; // ext::gui::size.current.y = uf::renderer::settings::height; this->addHook( "window:Resized", [&](ext::json::Value& json){ pod::Vector2ui size; { size.x = json["window"]["size"]["x"].as(); size.y = json["window"]["size"]["y"].as(); } metadata["system"]["window"] = json["system"]["window"]; ext::gui::size.current = size; }); } // lock control { uf::Serializer payload; payload["state"] = false; uf::hooks.call("window:Mouse.CursorVisibility", payload); uf::hooks.call("window:Mouse.Lock"); } // initialize perlin noise { auto& texture = this->getComponent(); texture.sampler.descriptor.addressMode = { VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT, VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT, VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT }; auto& noiseGenerator = this->getComponent(); auto& metadata = this->getComponent(); noiseGenerator.seed(rand()); float high = std::numeric_limits::min(); float low = std::numeric_limits::max(); float amplitude = metadata["noise"]["amplitude"].is() ? metadata["noise"]["amplitude"].as() : 1.5; pod::Vector3ui size = uf::vector::decode(metadata["noise"]["size"], pod::Vector3ui{256, 256, 256}); pod::Vector3d coefficients = uf::vector::decode(metadata["noise"]["coefficients"], pod::Vector3d{3.0, 3.0, 3.0}); std::vector pixels(size.x * size.y * size.z); std::vector perlins(size.x * size.y * size.z); #pragma omp parallel for for ( size_t z = 0; z < size.z; ++z ) { for ( size_t y = 0; y < size.y; ++y ) { for ( size_t x = 0; x < size.x; ++x ) { float nx = (float) x / (float) size.x; float ny = (float) y / (float) size.y; float nz = (float) z / (float) size.z; float n = amplitude * noiseGenerator.noise(coefficients.x * nx, coefficients.y * ny, coefficients.z * nz); high = std::max( high, n ); low = std::min( low, n ); perlins[x + y * size.x + z * size.x * size.y] = n; } } } for ( size_t i = 0; i < perlins.size(); ++i ) { float n = perlins[i]; n = n - floor(n); float normalized = (n - low) / (high - low); if ( normalized >= 1.0f ) normalized = 1.0f; pixels[i] = static_cast(floor(normalized * 255)); } texture.fromBuffers( (void*) pixels.data(), pixels.size(), VK_FORMAT_R8_UNORM, size.x, size.y, size.z, 1, ext::vulkan::device ); } } void ext::ExtSceneBehavior::tick( uf::Object& self ) { uf::Serializer& metadata = this->getComponent(); uf::Asset& assetLoader = this->getComponent(); /* check if audio needs to loop */ { auto& bgm = this->getComponent(); float current = bgm.getTime(); float end = bgm.getDuration(); float epsilon = 0.005f; if ( current + epsilon >= end || !bgm.playing() ) { // intro to main transition std::string filename = bgm.getFilename(); filename = assetLoader.getOriginal(filename); if ( filename.find("_intro") != std::string::npos ) { assetLoader.load(uf::string::replace( filename, "_intro", "" ), this->formatHookName("asset:Load.%UID%")); // loop } else { bgm.setTime(0); if ( !bgm.playing() ) bgm.play(); } } } { uf::hooks.call("game:Frame.Start"); } /* Regain control if nothing requests it */ { uf::Object* menu = (uf::Object*) this->globalFindByName("Gui: Menu"); if ( !menu ) { uf::Serializer payload; payload["state"] = false; uf::hooks.call("window:Mouse.CursorVisibility", payload); uf::hooks.call("window:Mouse.Lock"); } else { uf::Serializer payload; payload["state"] = true; uf::hooks.call("window:Mouse.CursorVisibility", payload); } } /* Print World Tree */ { TIMER(1, uf::Window::isKeyPressed("U") && ) { std::function filter = []( uf::Entity* entity, int indent ) { for ( int i = 0; i < indent; ++i ) uf::iostream << "\t"; uf::iostream << uf::string::toString(entity->as()) << " "; if ( entity->hasComponent>() ) { pod::Transform<> t = uf::transform::flatten(entity->getComponent>()); uf::iostream << uf::string::toString(t.position) << " " << uf::string::toString(t.orientation); } uf::iostream << "\n"; }; for ( uf::Scene* scene : uf::scene::scenes ) { if ( !scene ) continue; uf::iostream << "Scene: " << scene->getName() << ": " << scene << "\n"; scene->process(filter, 1); } } } /* Print World Tree */ { TIMER(1, uf::Window::isKeyPressed("U") && false && ) { std::function filter = []( uf::Entity* entity, int indent ) { for ( int i = 0; i < indent; ++i ) uf::iostream << "\t"; uf::iostream << uf::string::toString(entity->as()) << " ["; for ( auto& behavior : entity->getBehaviors() ) { uf::iostream << uf::instantiator::behaviors->names[behavior.type] << ", "; } uf::iostream << "]\n"; }; for ( uf::Scene* scene : uf::scene::scenes ) { if ( !scene ) continue; uf::iostream << "Scene: " << scene->getName() << ": " << scene << "\n"; scene->process(filter, 1); } uf::Serializer instantiator; { int i = 0; for ( auto& pair : uf::instantiator::objects->names ) { instantiator["objects"][i++] = pair.second; } } { int i = 0; for ( auto& pair : uf::instantiator::behaviors->names ) { instantiator["behaviors"][i++] = pair.second; } } uf::iostream << instantiator << "\n"; } } /* Updates Sound Listener */ { auto& controller = this->getController(); // copy pod::Transform<> transform = controller.getComponent>(); if ( controller.hasComponent() ) { auto& camera = controller.getComponent(); transform.position += camera.getTransform().position; transform = uf::transform::reorient( transform ); } transform.forward *= -1; ext::oal.listener( "POSITION", { transform.position.x, transform.position.y, transform.position.z } ); ext::oal.listener( "VELOCITY", { 0, 0, 0 } ); ext::oal.listener( "ORIENTATION", { transform.forward.x, transform.forward.y, transform.forward.z, transform.up.x, transform.up.y, transform.up.z } ); } /* Update lights */ if ( metadata["light"]["should"].as() ) { auto& scene = uf::scene::getCurrentScene(); auto& controller = scene.getController(); auto& camera = controller.getComponent(); auto& renderMode = uf::renderer::getRenderMode("", true); auto& controllerMetadata = controller.getComponent(); auto& controllerTransform = controller.getComponent>(); std::vector blitters = renderMode.getBlitters(); size_t maxTextures = metadata["system"]["config"]["engine"]["scenes"]["textures"]["max"].as(); struct UniformDescriptor { struct Matrices { alignas(16) pod::Matrix4f view[2]; alignas(16) pod::Matrix4f projection[2]; } matrices; struct Mode { alignas(8) pod::Vector2ui type; alignas(8) pod::Vector2ui padding; alignas(16) pod::Vector4f parameters; } mode; struct { alignas(16) pod::Vector4f color; // w: stepScale alignas(16) pod::Vector4f offset; // w: densityScale alignas(4) float densityThreshold; alignas(4) float densityMultiplier; alignas(4) float absorbtion; alignas(4) float padding1; alignas(8) pod::Vector2f range; alignas(4) float padding2; alignas(4) float padding3; } fog; struct { alignas(4) uint32_t lights = 0; alignas(4) uint32_t materials = 0; alignas(4) uint32_t textures = 0; alignas(4) uint32_t drawCalls = 0; } lengths; alignas(16) pod::Vector4f ambient; }; struct SpecializationConstant { uint32_t maxTextures = 512; } specializationConstants; specializationConstants.maxTextures = maxTextures; struct LightInfo { uf::Entity* entity = NULL; pod::Vector3f position = {0,0,0}; float distance = 0; bool shadows = false; }; std::vector entities; std::vector graphs; // std::vector graphs; this->process([&]( uf::Entity* entity ) { if ( !entity ) return; auto& metadata = entity->getComponent(); if ( entity == &controller ) return; if ( entity == this ) return; if ( entity->hasComponent() ) graphs.emplace_back(&entity->getComponent()); // if ( entity->hasComponent() && entity->hasComponent() ) graphs.emplace_back(entity); // if ( entity->getName() != "Light" && !ext::json::isObject( metadata["light"] ) ) return; // if ( entity->hasComponent() ) { auto& renderMode = entity->getComponent(); metadata["system"]["renderer"]["rendered"] = false; if ( metadata["system"]["renderer"]["mode"].as() == "in-range" ) { renderMode.execute = false; } } // is a component of an shadowing point light if ( metadata["light"]["bound"].as() ) return; LightInfo& info = entities.emplace_back(); auto& transform = entity->getComponent>(); auto flatten = uf::transform::flatten( transform ); info.entity = entity; info.position = flatten.position; info.distance = uf::vector::magnitude( uf::vector::subtract( flatten.position, controllerTransform.position ) ); info.shadows = metadata["light"]["shadows"].as(); }); std::sort( entities.begin(), entities.end(), [&]( LightInfo& l, LightInfo& r ){ return l.distance < r.distance; }); int shadowThreshold = metadata["system"]["config"]["engine"]["scenes"]["lights"]["shadow threshold"].as(); if ( shadowThreshold <= 0 ) shadowThreshold = std::numeric_limits::max(); { std::vector scratch; scratch.reserve(entities.size()); for ( size_t i = 0; i < entities.size(); ++i ) { auto& info = entities[i]; auto& metadata = info.entity->getComponent(); if ( info.shadows && --shadowThreshold <= 0 ) info.shadows = false; scratch.emplace_back(info); } entities = scratch; } if ( controllerMetadata["light"]["should"].as() ) { auto& info = entities.emplace_back(); info.entity = &controller; info.position = controllerTransform.position; info.distance = 0; info.shadows = false; } if ( !metadata["light"]["fog"]["step scale"].is() ) metadata["light"]["fog"]["step scale"] = 16.0f; if ( !metadata["light"]["fog"]["absorbtion"].is() ) metadata["light"]["fog"]["absorbtion"] = 0.85f; if ( !metadata["light"]["fog"]["density"]["threshold"].is() ) metadata["light"]["fog"]["density"]["threshold"] = 0.5f; if ( !metadata["light"]["fog"]["density"]["multiplier"].is() ) metadata["light"]["fog"]["density"]["multiplier"] = 5.0f; if ( !metadata["light"]["fog"]["density"]["scale"].is() ) metadata["light"]["fog"]["density"]["scale"] = 50.0f; for ( auto* blitter : blitters ) { auto& graphic = *blitter; auto& shader = graphic.material.getShader("fragment"); auto& uniform = shader.getUniform("UBO"); uint8_t* buffer = (uint8_t*) (void*) uniform; UniformDescriptor* uniforms = (UniformDescriptor*) buffer; for ( std::size_t i = 0; i < 2; ++i ) { uniforms->matrices.view[i] = camera.getView( i ); uniforms->matrices.projection[i] = camera.getProjection( i ); } uniforms->ambient = uf::vector::decode( metadata["light"]["ambient"], uniforms->ambient ); uniforms->fog.color = uf::vector::decode( metadata["light"]["fog"]["color"], uniforms->fog.color ); uniforms->fog.color.w = metadata["light"]["fog"]["step scale"].as(); float timescale = metadata["light"]["fog"]["density"]["timescale"].as(); uniforms->fog.offset = uf::vector::decode( metadata["light"]["fog"]["density"]["offset"], uniforms->fog.offset ) * uf::physics::time::current * timescale; uniforms->fog.offset.w = metadata["light"]["fog"]["density"]["scale"].as(); uniforms->fog.densityThreshold = metadata["light"]["fog"]["density"]["threshold"].as(); uniforms->fog.densityMultiplier = metadata["light"]["fog"]["density"]["multiplier"].as(); uniforms->fog.absorbtion = metadata["light"]["fog"]["absorbtion"].as(); uniforms->fog.range = uf::vector::decode( metadata["light"]["fog"]["range"], uniforms->fog.range ); uniforms->mode.type.x = metadata["system"]["renderer"]["shader"]["mode"].as(); uniforms->mode.type.y = metadata["system"]["renderer"]["shader"]["scalar"].as(); uniforms->mode.parameters = uf::vector::decode( metadata["system"]["renderer"]["shader"]["parameters"], uniforms->mode.parameters ); if ( metadata["system"]["renderer"]["shader"]["parameters"][3].as() == "time" ) { uniforms->mode.parameters.w = uf::physics::time::current; } std::vector previousTextures; for ( auto& texture : graphic.material.textures ) previousTextures.emplace_back(texture.image); graphic.material.textures.clear(); // add noise texture graphic.material.textures.emplace_back().aliasTexture(this->getComponent()); size_t updateThreshold = metadata["system"]["config"]["engine"]["scenes"]["lights"]["update threshold"].as(); size_t maxLights = metadata["system"]["config"]["engine"]["scenes"]["lights"]["max"].as(); size_t textureSlot = 0; std::vector lights; lights.reserve( maxLights ); std::vector materials; materials.reserve(maxTextures); materials.emplace_back().colorBase = {0,0,0,0}; std::vector textures; textures.reserve(maxTextures); std::vector drawCalls; drawCalls.reserve(maxTextures); // add materials { for ( auto* entity : graphs ) { auto& graph = *entity; drawCalls.emplace_back(pod::DrawCall{ materials.size(), graph.materials.size(), textures.size(), graph.textures.size() }); for ( auto& material : graph.materials ) materials.emplace_back( material.storage ); for ( auto& texture : graph.textures ) textures.emplace_back( texture.storage ); for ( auto& texture : graph.textures ) { if ( !texture.texture.device ) continue; graphic.material.textures.emplace_back().aliasTexture(texture.texture); ++textureSlot; if ( graph.atlas ) break; } } uniforms->lengths.materials = std::min( materials.size(), maxTextures ); uniforms->lengths.textures = std::min( textures.size(), maxTextures ); uniforms->lengths.drawCalls = std::min( drawCalls.size(), maxTextures ); } // add lighting for ( size_t i = 0; i < entities.size() && lights.size() < maxLights; ++i ) { auto& info = entities[i]; uf::Entity* entity = info.entity; auto& transform = entity->getComponent>(); auto& metadata = entity->getComponent(); auto& camera = entity->getComponent(); metadata["system"]["renderer"]["rendered"] = true; pod::Light::Storage light; light.position = info.position; light.color = uf::vector::decode( metadata["light"]["color"], light.color ); light.color.w = metadata["light"]["power"].as(); if ( metadata["light"]["type"].is() ) { light.type = metadata["light"]["type"].as(); } else if ( metadata["light"]["type"].is() ) { std::string lightType = metadata["light"]["type"].as(); if ( lightType == "point" ) light.type = 1; else if ( lightType == "spot" ) light.type = 2; } light.mapIndex = -1; light.depthBias = metadata["light"]["bias"]["shader"].as(); if ( info.shadows && entity->hasComponent() ) { auto& renderMode = entity->getComponent(); if ( metadata["system"]["renderer"]["mode"].as() == "in-range" && --updateThreshold > 0 ) { renderMode.execute = true; } size_t view = 0; for ( auto& attachment : renderMode.renderTarget.attachments ) { if ( !(attachment.usage & VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) ) continue; if ( attachment.layout == VK_IMAGE_LAYOUT_PRESENT_SRC_KHR ) continue; graphic.material.textures.emplace_back().aliasAttachment(attachment); light.mapIndex = textureSlot++; light.view = camera.getView(view); light.projection = camera.getProjection(view); lights.emplace_back(light); ++view; } light.mapIndex = -1; } else { lights.emplace_back(light); } uniforms->lengths.lights = std::min( lights.size(), maxLights ); } { bool shouldUpdate = graphic.material.textures.size() != previousTextures.size(); for ( size_t i = 0; !shouldUpdate && i < previousTextures.size() && i < graphic.material.textures.size(); ++i ) { if ( previousTextures[i] != graphic.material.textures[i].image ) shouldUpdate = true; } if ( shouldUpdate ) { size_t lightBufferIndex = renderMode.metadata["lightBufferIndex"].as(); size_t materialBufferIndex = renderMode.metadata["materialBufferIndex"].as(); size_t textureBufferIndex = renderMode.metadata["textureBufferIndex"].as(); size_t drawCallBufferIndex = renderMode.metadata["drawCallBufferIndex"].as(); graphic.updateBuffer( (void*) lights.data(), uniforms->lengths.lights * sizeof(pod::Light::Storage), lightBufferIndex, false ); graphic.updateBuffer( (void*) materials.data(), uniforms->lengths.materials * sizeof(pod::Material::Storage), materialBufferIndex, false ); graphic.updateBuffer( (void*) textures.data(), uniforms->lengths.textures * sizeof(pod::Texture::Storage), textureBufferIndex, false ); graphic.updateBuffer( (void*) drawCalls.data(), uniforms->lengths.drawCalls * sizeof(pod::DrawCall), drawCallBufferIndex, false ); graphic.updatePipelines(); } shader.updateUniform( "UBO", uniform ); } } } } void ext::ExtSceneBehavior::render( uf::Object& self ) {} void ext::ExtSceneBehavior::destroy( uf::Object& self ) {} #undef this