diff --git a/bin/data/config.json b/bin/data/config.json index d93534d5..f75a6dbf 100644 --- a/bin/data/config.json +++ b/bin/data/config.json @@ -104,7 +104,7 @@ "dedicated thread": false, "memory budget": false, "register render modes": true, - "skip render on rebuild": true + "skip render on rebuild": false }, "invariant": { "default stage buffers": true, @@ -380,7 +380,8 @@ "auto validate": false }, "loader": { - "assert": true + "assert": true, + "async": true }, "hooks": { "defer lazy calls": true diff --git a/engine/inc/uf/engine/asset/asset.h b/engine/inc/uf/engine/asset/asset.h index b63f1f12..2d2e9232 100644 --- a/engine/inc/uf/engine/asset/asset.h +++ b/engine/inc/uf/engine/asset/asset.h @@ -27,6 +27,8 @@ namespace uf { #endif extern UF_API bool assertionLoad; + extern UF_API bool asyncQueue; + extern UF_API uf::stl::unordered_map map; extern UF_API Job::container_t jobs; extern UF_API uf::Serializer metadata; diff --git a/engine/inc/uf/engine/graph/graph.h b/engine/inc/uf/engine/graph/graph.h index c8b1188a..0ca15a70 100644 --- a/engine/inc/uf/engine/graph/graph.h +++ b/engine/inc/uf/engine/graph/graph.h @@ -145,11 +145,13 @@ namespace uf { void UF_API reload( pod::Graph& ); void UF_API initialize( pod::Graph& graph ); - void UF_API override( pod::Graph& ); - void UF_API animate( pod::Graph&, const uf::stl::string&, float = 1, bool = true ); void UF_API update( pod::Graph& ); void UF_API update( pod::Graph&, float ); - void UF_API update( pod::Graph&, pod::Node& ); + + void UF_API updateAnimation( pod::Graph&, float ); + void UF_API updateAnimation( pod::Graph&, pod::Node& ); + void UF_API override( pod::Graph& ); + void UF_API animate( pod::Graph&, const uf::stl::string&, float = 1, bool = true ); void UF_API destroy( pod::Graph& ); diff --git a/engine/src/engine/asset/asset.cpp b/engine/src/engine/asset/asset.cpp index 2946cff1..6de9f710 100644 --- a/engine/src/engine/asset/asset.cpp +++ b/engine/src/engine/asset/asset.cpp @@ -50,18 +50,13 @@ namespace { // uf::asset uf::asset::masterAssetLoader; bool uf::asset::assertionLoad = true; +bool uf::asset::asyncQueue = true; uf::asset::Job::container_t uf::asset::jobs; uf::stl::unordered_map uf::asset::map; uf::Serializer uf::asset::metadata; -#define UF_ASSET_MULTITHREAD 1 - void uf::asset::processQueue() { -#if UF_ASSET_MULTITHREAD - auto tasks = uf::thread::schedule(true, false); -#else - auto tasks = uf::thread::schedule(false); -#endif + auto tasks = uf::asset::asyncQueue ? uf::thread::schedule(false) : uf::thread::schedule(true, false); mutex.lock(); auto jobs = std::move(uf::asset::jobs); diff --git a/engine/src/engine/ext/ext.cpp b/engine/src/engine/ext/ext.cpp index 9453026e..af40af2c 100644 --- a/engine/src/engine/ext/ext.cpp +++ b/engine/src/engine/ext/ext.cpp @@ -142,6 +142,7 @@ void UF_API uf::load( ext::json::Value& json ) { uf::Object::assertionLoad = json["engine"]["debug"]["loader"]["assert"].as( uf::Object::assertionLoad ); uf::asset::assertionLoad = json["engine"]["debug"]["loader"]["assert"].as( uf::asset::assertionLoad ); + uf::asset::asyncQueue = json["engine"]["debug"]["loader"]["async"].as( uf::asset::asyncQueue ); uf::userdata::autoDestruct = json["engine"]["debug"]["userdata"]["auto destruct"].as( uf::userdata::autoDestruct ); uf::userdata::autoValidate = json["engine"]["debug"]["userdata"]["auto validate"].as( uf::userdata::autoValidate ); diff --git a/engine/src/engine/graph/animation.cpp b/engine/src/engine/graph/animation.cpp new file mode 100644 index 00000000..eefbc1e5 --- /dev/null +++ b/engine/src/engine/graph/animation.cpp @@ -0,0 +1,256 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace { + pod::Graph::Storage& getGraphStorage( uf::Object& object ) { + return uf::graph::globalStorage ? uf::graph::storage : object.getComponent(); + } + + // lazy load animations if requested + void loadAnimation( const uf::stl::string& name ) { + auto& scene = uf::scene::getCurrentScene(); + auto& storage = ::getGraphStorage( scene ); + + auto& animation = storage.animations.map[name]; + + UF_ASSERT( animation.path != "" ); + + uf::Serializer json; + json.readFromFile( animation.path ); + + animation.name = json["name"].as(animation.name); + animation.start = json["start"].as(animation.start); + animation.end = json["end"].as(animation.end); + + if ( animation.samplers.empty() ) ext::json::forEach( json["samplers"], [&]( ext::json::Value& value ){ + auto& sampler = animation.samplers.emplace_back(); + sampler.interpolator = value["interpolator"].as(sampler.interpolator); + + sampler.inputs.reserve( value["inputs"].size() ); + ext::json::forEach( value["inputs"], [&]( ext::json::Value& input ){ + sampler.inputs.emplace_back( input.as() ); + }); + + sampler.outputs.reserve( value["outputs"].size() ); + ext::json::forEach( value["outputs"], [&]( ext::json::Value& output ){ + sampler.outputs.emplace_back( uf::vector::decode( output, pod::Vector4f{} ) ); + }); + }); + + if ( animation.channels.empty() ) ext::json::forEach( json["channels"], [&]( ext::json::Value& value ){ + auto& channel = animation.channels.emplace_back(); + channel.path = value["path"].as(channel.path); + channel.node = value["node"].as(channel.node); + channel.sampler = value["sampler"].as(channel.sampler); + }); + } + + void unloadAnimation( const uf::stl::string& name ) { + auto& scene = uf::scene::getCurrentScene(); + auto& storage = ::getGraphStorage( scene ); + + auto& animation = storage.animations.map[name]; + + animation.samplers.clear(); + animation.channels.clear(); + #if UF_ENV_DREAMCAST + animation.samplers.shrink_to_fit(); + animation.channels.shrink_to_fit(); + #endif + } + + pod::Matrix4f localMatrix( pod::Graph& graph, int32_t index ) { + auto& node = 0 < index && index <= graph.nodes.size() ? graph.nodes[index] : graph.root; + return + uf::matrix::translate( uf::matrix::identity(), node.transform.position ) * + uf::quaternion::matrix(node.transform.orientation) * + uf::matrix::scale( uf::matrix::identity(), node.transform.scale ) * + node.transform.model; + } + pod::Matrix4f worldMatrix( pod::Graph& graph, int32_t index ) { + pod::Matrix4f matrix = ::localMatrix( graph, index ); + auto& node = *uf::graph::find( graph, index ); + int32_t parent = node.parent; + while ( 0 < parent && parent <= graph.nodes.size() ) { + matrix = ::localMatrix( graph, parent ) * matrix; + parent = graph.nodes[parent].parent; + } + return matrix; + } +} + +pod::Node* uf::graph::find( pod::Graph& graph, int32_t index ) { + return 0 <= index && index < graph.nodes.size() ? &graph.nodes[index] : NULL; +} +pod::Node* uf::graph::find( pod::Graph& graph, const uf::stl::string& name ) { + for ( auto& node : graph.nodes ) if ( node.name == name ) return &node; + return NULL; +} + +void uf::graph::override( pod::Graph& graph ) { + auto& scene = uf::scene::getCurrentScene(); + auto& storage = ::getGraphStorage( scene ); + + graph.settings.animations.override.a = 0; + graph.settings.animations.override.map.clear(); + bool toNeutralPose = graph.sequence.empty(); + // store every node's current transform + for ( auto& node : graph.nodes ) { + graph.settings.animations.override.map[node.index].first = node.transform; + graph.settings.animations.override.map[node.index].second = node.transform; + if ( toNeutralPose ) { + graph.settings.animations.override.map[node.index].second.position = { 0, 0, 0 }; + graph.settings.animations.override.map[node.index].second.orientation = { 0, 0, 0, 1 }; + graph.settings.animations.override.map[node.index].second.scale = { 1, 1, 1 }; + } + } + // set our destination transform per node + if ( !toNeutralPose ) { + uf::stl::string name = graph.sequence.front(); + pod::Animation& animation = storage.animations.map[name]; + + // load animation data + if ( animation.channels.empty() || animation.samplers.empty() ) ::loadAnimation( name ); + + for ( auto& channel : animation.channels ) { + auto& override = graph.settings.animations.override.map[channel.node]; + auto& sampler = animation.samplers[channel.sampler]; + if ( sampler.interpolator != "LINEAR" ) continue; + for ( size_t i = 0; i < sampler.inputs.size() - 1; ++i ) { + if ( !(animation.start >= sampler.inputs[i] && animation.start <= sampler.inputs[i+1]) ) continue; + if ( channel.path == "translation" ) { + override.second.position = sampler.outputs[i]; + } else if ( channel.path == "rotation" ) { + override.second.orientation = uf::quaternion::normalize( sampler.outputs[i] ); + } else if ( channel.path == "scale" ) { + override.second.scale = sampler.outputs[i]; + } + } + } + } +} + +void uf::graph::animate( pod::Graph& graph, const uf::stl::string& _name, float speed, bool immediate ) { + auto& scene = uf::scene::getCurrentScene(); + auto& storage = ::getGraphStorage( scene ); + + if ( !(graph.metadata["renderer"]["skinned"].as()) ) return; + const uf::stl::string name = _name; + if ( storage.animations.map.count( name ) > 0 ) { + // if already playing, ignore it + if ( !graph.sequence.empty() && graph.sequence.front() == name ) return; + if ( immediate ) { + while ( !graph.sequence.empty() ) { + // unload + if ( graph.settings.stream.animations ) ::unloadAnimation( graph.sequence.front() ); + graph.sequence.pop(); + } + } + bool empty = graph.sequence.empty(); + graph.sequence.emplace(name); + if ( empty ) uf::graph::override( graph ); + graph.settings.animations.speed = speed; + } + updateAnimation( graph, 0 ); +} + +void uf::graph::updateAnimation( pod::Graph& graph, float delta ) { + // update our instances + + // no skins + if ( !(graph.metadata["renderer"]["skinned"].as()) ) { + return; + } + + if ( graph.sequence.empty() ) goto UPDATE; + if ( graph.settings.animations.override.a >= 0 ) goto OVERRIDE; + { + uf::stl::string name = graph.sequence.front(); + pod::Animation* animation = &storage.animations.map[name]; // &graph.animations[name]; + animation->cur += delta * graph.settings.animations.speed; // * graph.settings.animations.override.speed; + if ( animation->end < animation->cur ) { + animation->cur = graph.settings.animations.loop ? animation->cur - animation->end : 0; + // go-to next animation + if ( !graph.settings.animations.loop ) { + // unload + if ( graph.settings.stream.animations ) ::unloadAnimation( graph.sequence.front() ); + graph.sequence.pop(); + + // out of animations, set to neutral pose + if ( graph.sequence.empty() ) { + uf::graph::override( graph ); + goto OVERRIDE; + } + name = graph.sequence.front(); + animation = &storage.animations.map[name]; // &graph.animations[name]; + } + } + + // load animation data + if ( animation->channels.empty() || animation->samplers.empty() ) ::loadAnimation( name ); + + for ( auto& channel : animation->channels ) { + auto& sampler = animation->samplers[channel.sampler]; + if ( sampler.interpolator != "LINEAR" ) continue; + for ( size_t i = 0; i < sampler.inputs.size() - 1; ++i ) { + if ( !(animation->cur >= sampler.inputs[i] && animation->cur <= sampler.inputs[i+1]) ) continue; + float a = (animation->cur - sampler.inputs[i]) / (sampler.inputs[i+1] - sampler.inputs[i]); + auto& transform = graph.nodes[channel.node].transform; + if ( channel.path == "translation" ) { + transform.position = uf::vector::mix( sampler.outputs[i], sampler.outputs[i+1], a ); + } else if ( channel.path == "rotation" ) { + transform.orientation = uf::quaternion::normalize( uf::quaternion::slerp(sampler.outputs[i], sampler.outputs[i+1], a) ); + } else if ( channel.path == "scale" ) { + transform.scale = uf::vector::mix( sampler.outputs[i], sampler.outputs[i+1], a ); + } + } + } + goto UPDATE; + } +OVERRIDE: + for ( auto pair : graph.settings.animations.override.map ) { + graph.nodes[pair.first].transform.position = uf::vector::mix( pair.second.first.position, pair.second.second.position, graph.settings.animations.override.a ); + graph.nodes[pair.first].transform.orientation = uf::quaternion::normalize( uf::quaternion::slerp(pair.second.first.orientation, pair.second.second.orientation, graph.settings.animations.override.a) ); + graph.nodes[pair.first].transform.scale = uf::vector::mix( pair.second.first.scale, pair.second.second.scale, graph.settings.animations.override.a ); + } + // finished our overrided interpolation, clear it + if ( (graph.settings.animations.override.a += delta * graph.settings.animations.override.speed) >= 1 ) { + graph.settings.animations.override.a = -std::numeric_limits::max(); + graph.settings.animations.override.map.clear(); + } +UPDATE: + for ( auto& node : graph.nodes ) uf::graph::updateAnimation( graph, node ); +} +void uf::graph::updateAnimation( pod::Graph& graph, pod::Node& node ) { + auto& scene = uf::scene::getCurrentScene(); + auto& storage = ::getGraphStorage( scene ); + + if ( 0 <= node.skin && node.skin < graph.skins.size() ) { + pod::Matrix4f nodeMatrix = ::worldMatrix( graph, node.index ); + pod::Matrix4f inverseTransform = uf::matrix::inverse( nodeMatrix ); + + auto& name = graph.skins[node.skin]; + auto& skin = storage.skins[name]; + auto& joints = storage.joints[name]; + joints.resize( skin.joints.size() ); + for ( size_t i = 0; i < skin.joints.size(); ++i ) joints[i] = uf::matrix::identity(); + + if ( graph.settings.animations.override.a >= 0 || !graph.sequence.empty() ) { + for ( size_t i = 0; i < skin.joints.size(); ++i ) { + joints[i] = inverseTransform * (::worldMatrix(graph, skin.joints[i]) * skin.inverseBindMatrices[i]); + } + } + } +} \ No newline at end of file diff --git a/engine/src/engine/graph/graph.cpp b/engine/src/engine/graph/graph.cpp index ba6f012e..0c0dc3de 100644 --- a/engine/src/engine/graph/graph.cpp +++ b/engine/src/engine/graph/graph.cpp @@ -44,6 +44,10 @@ namespace { return hash; } + pod::Graph::Storage& getGraphStorage( uf::Object& object ) { + return uf::graph::globalStorage ? uf::graph::storage : object.getComponent(); + } + // removes non-uniform aliased buffers void resetBuffers( uf::renderer::Shader& shader ) { shader.metadata.aliases.buffers.clear(); @@ -53,14 +57,7 @@ namespace { graphic.material.textures.clear(); auto& scene = uf::scene::getCurrentScene(); - auto& storage = uf::graph::globalStorage ? uf::graph::storage : scene.getComponent(); - - // for ( auto& s : graph.samplers ) graphic.material.samplers.emplace_back( storage.samplers.map[s] ); - // for ( auto pair : storage.samplers.map ) graphic.material.samplers.emplace_back( pair.second ); - // for ( auto& key : storage.samplers.keys ) graphic.material.samplers.emplace_back( storage.samplers.map[key] ); - - // for ( auto& i : graph.images ) graphic.material.textures.emplace_back().aliasTexture( storage.texture2Ds.map[i] ); - // for ( auto pair : storage.texture2Ds.map ) graphic.material.textures.emplace_back().aliasTexture( pair.second ); + auto& storage = ::getGraphStorage( scene ); for ( auto& key : storage.texture2Ds.keys ) graphic.material.textures.emplace_back().aliasTexture( storage.texture2Ds.map[key] ); @@ -87,9 +84,10 @@ namespace { auto& scene = uf::scene::getCurrentScene(); auto& sceneTextures = scene.getComponent(); auto& sceneMetadataJson = scene.getComponent(); - auto& storage = uf::graph::globalStorage ? uf::graph::storage : scene.getComponent(); + auto& storage = ::getGraphStorage( scene ); auto& graphic = entity.getComponent(); + auto& graphMetadataJson = graph.metadata; uf::stl::string root = uf::io::directory( graph.name ); size_t texture2Ds = 0; @@ -101,18 +99,18 @@ namespace { // standard pipeline { - uf::stl::string vertexShaderFilename = graph.metadata["shaders"]["vertex"].as("/graph/base/vert.spv"); { + uf::stl::string vertexShaderFilename = graphMetadataJson["shaders"]["vertex"].as("/graph/base/vert.spv"); { std::pair settings[] = { - { graph.metadata["renderer"]["skinned"].as(), "skinned.vert" }, - { !graph.metadata["renderer"]["separate"].as(), "instanced.vert" }, + { graphMetadataJson["renderer"]["skinned"].as(), "skinned.vert" }, + { !graphMetadataJson["renderer"]["separate"].as(), "instanced.vert" }, }; FOR_ARRAY(settings) if ( settings[i].first ) vertexShaderFilename = uf::string::replace( vertexShaderFilename, "vert", settings[i].second ); vertexShaderFilename = entity.resolveURI( vertexShaderFilename, root ); } - uf::stl::string geometryShaderFilename = graph.metadata["shaders"]["geometry"].as(""); if ( geometryShaderFilename != "" ) { + uf::stl::string geometryShaderFilename = graphMetadataJson["shaders"]["geometry"].as(""); if ( geometryShaderFilename != "" ) { geometryShaderFilename = entity.resolveURI( geometryShaderFilename, root ); } - uf::stl::string fragmentShaderFilename = graph.metadata["shaders"]["fragment"].as("/graph/base/frag.spv"); { + uf::stl::string fragmentShaderFilename = graphMetadataJson["shaders"]["fragment"].as("/graph/base/frag.spv"); { fragmentShaderFilename = entity.resolveURI( fragmentShaderFilename, root ); } @@ -139,7 +137,7 @@ namespace { { auto& shader = graphic.material.getShader("fragment"); #if UF_USE_VULKAN - uint32_t maxTextures = graph.textures.size(); + uint32_t maxTextures = storage.textures.map.size(); shader.setSpecializationConstants({ { "TEXTURES", maxTextures }, }); @@ -153,7 +151,7 @@ namespace { #if UF_USE_VULKAN // depth only pipeline { - uf::stl::string fragmentShaderFilename = graph.metadata["shaders"]["depth"]["fragmment"].as("/graph/depth/frag.spv"); + uf::stl::string fragmentShaderFilename = graphMetadataJson["shaders"]["depth"]["fragmment"].as("/graph/depth/frag.spv"); fragmentShaderFilename = entity.resolveURI( fragmentShaderFilename, root ); graphic.material.metadata.autoInitializeUniformBuffers = false; @@ -163,7 +161,7 @@ namespace { // fragment shader auto& shader = graphic.material.getShader("fragment", "depth"); - uint32_t maxTextures = graph.textures.size(); + uint32_t maxTextures = storage.textures.map.size(); shader.setSpecializationConstants({ { "TEXTURES", maxTextures }, }); @@ -173,7 +171,7 @@ namespace { } // culling pipeline if ( uf::renderer::settings::pipelines::culling && graphic.descriptor.inputs.indirect.count ) { - uf::stl::string compShaderFilename = graph.metadata["shaders"][uf::renderer::settings::pipelines::names::culling]["compute"].as("/graph/cull/comp.spv"); + uf::stl::string compShaderFilename = graphMetadataJson["shaders"][uf::renderer::settings::pipelines::names::culling]["compute"].as("/graph/cull/comp.spv"); compShaderFilename = entity.resolveURI( compShaderFilename, root ); graphic.material.metadata.autoInitializeUniformBuffers = false; @@ -190,10 +188,10 @@ namespace { } // vxgi pipeline if ( uf::renderer::settings::pipelines::vxgi ) { - uf::stl::string geometryShaderFilename = graph.metadata["shaders"][uf::renderer::settings::pipelines::names::vxgi]["geometry"].as("/graph/voxelize/geom.spv"); { + uf::stl::string geometryShaderFilename = graphMetadataJson["shaders"][uf::renderer::settings::pipelines::names::vxgi]["geometry"].as("/graph/voxelize/geom.spv"); { geometryShaderFilename = entity.resolveURI( geometryShaderFilename, root ); } - uf::stl::string fragmentShaderFilename = graph.metadata["shaders"][uf::renderer::settings::pipelines::names::vxgi]["fragment"].as("/graph/voxelize/frag.spv"); if ( geometryShaderFilename != "" ) { + uf::stl::string fragmentShaderFilename = graphMetadataJson["shaders"][uf::renderer::settings::pipelines::names::vxgi]["fragment"].as("/graph/voxelize/frag.spv"); if ( geometryShaderFilename != "" ) { fragmentShaderFilename = entity.resolveURI( fragmentShaderFilename, root ); } @@ -242,7 +240,7 @@ namespace { } } // baking pipeline - if ( graph.metadata["baking"]["enabled"].as() ) { + if ( graphMetadataJson["baking"]["enabled"].as() ) { uf::stl::string vertexShaderFilename = uf::io::resolveURI("/graph/baking/vert.spv"); uf::stl::string fragmentShaderFilename = uf::io::resolveURI("/graph/baking/frag.spv"); std::pair settings[] = { @@ -285,13 +283,13 @@ namespace { // rt pipeline // to-do: segregate out buffer updating code - if ( uf::renderer::settings::pipelines::rt && mesh.vertex.count && graph.metadata["renderer"]["skinned"].as() ) { + if ( uf::renderer::settings::pipelines::rt && mesh.vertex.count && graphMetadataJson["renderer"]["skinned"].as() ) { struct PushConstant { uint32_t jointID; }; if ( mesh.isInterleaved( mesh.vertex ) ) { - uf::stl::string compShaderFilename = graph.metadata["shaders"]["skinning"]["compute"].as("/graph/skinning/skinning.interleaved.comp.spv"); { + uf::stl::string compShaderFilename = graphMetadataJson["shaders"]["skinning"]["compute"].as("/graph/skinning/skinning.interleaved.comp.spv"); { compShaderFilename = entity.resolveURI( compShaderFilename, root ); } graphic.material.metadata.autoInitializeUniformBuffers = false; @@ -326,7 +324,7 @@ namespace { shader.aliasBuffer( vertexIn ); shader.aliasBuffer( vertexOut ); } else { - uf::stl::string compShaderFilename = graph.metadata["shaders"]["skinning"]["compute"].as("/graph/skinning/skinning.deinterleaved.comp.spv"); { + uf::stl::string compShaderFilename = graphMetadataJson["shaders"]["skinning"]["compute"].as("/graph/skinning/skinning.deinterleaved.comp.spv"); { compShaderFilename = entity.resolveURI( compShaderFilename, root ); } @@ -401,7 +399,7 @@ namespace { auto& scene = uf::scene::getCurrentScene(); auto& sceneTextures = scene.getComponent(); auto& sceneMetadataJson = scene.getComponent(); - auto& storage = uf::graph::globalStorage ? uf::graph::storage : scene.getComponent(); + auto& storage = ::getGraphStorage( scene ); // draw command buffer for binding uf::renderer::Buffer* indirect = NULL; @@ -483,7 +481,7 @@ namespace { } } // baking pipeline - if ( graphic.material.hasShader("vertex", "baking") /*graph.metadata["baking"]["enabled"].as()*/ ) { + if ( graphic.material.hasShader("vertex", "baking") ) { // vertex shader { auto& shader = graphic.material.getShader("vertex", "baking"); @@ -531,19 +529,19 @@ namespace { auto& instanceAddresses = storage.instanceAddresses.map[instanceKeyName]; if ( mesh.vertex.count ) { if ( mesh.isInterleaved( mesh.vertex ) ) { - instanceAddresses.vertex = graphic.buffers.at(graphic.descriptor.inputs.vertex.interleaved).getAddress()/* + (drawCommand.vertexID * mesh.vertex.size)*/; + instanceAddresses.vertex = graphic.buffers.at(graphic.descriptor.inputs.vertex.interleaved).getAddress(); } else { for ( auto& attribute : graphic.descriptor.inputs.vertex.attributes ) { if ( attribute.buffer < 0 ) continue; - if ( attribute.descriptor.name == "position" ) instanceAddresses.position = graphic.buffers.at(attribute.buffer).getAddress()/* + (drawCommand.vertexID * attribute.stride)*/; - else if ( attribute.descriptor.name == "uv" ) instanceAddresses.uv = graphic.buffers.at(attribute.buffer).getAddress()/* + (drawCommand.vertexID * attribute.stride)*/; - else if ( attribute.descriptor.name == "color" ) instanceAddresses.color = graphic.buffers.at(attribute.buffer).getAddress()/* + (drawCommand.vertexID * attribute.stride)*/; - else if ( attribute.descriptor.name == "st" ) instanceAddresses.st = graphic.buffers.at(attribute.buffer).getAddress()/* + (drawCommand.vertexID * attribute.stride)*/; - else if ( attribute.descriptor.name == "normal" ) instanceAddresses.normal = graphic.buffers.at(attribute.buffer).getAddress()/* + (drawCommand.vertexID * attribute.stride)*/; - else if ( attribute.descriptor.name == "tangent" ) instanceAddresses.tangent = graphic.buffers.at(attribute.buffer).getAddress()/* + (drawCommand.vertexID * attribute.stride)*/; - else if ( attribute.descriptor.name == "joints" ) instanceAddresses.joints = graphic.buffers.at(attribute.buffer).getAddress()/* + (drawCommand.vertexID * attribute.stride)*/; - else if ( attribute.descriptor.name == "weights" ) instanceAddresses.weights = graphic.buffers.at(attribute.buffer).getAddress()/* + (drawCommand.vertexID * attribute.stride)*/; - else if ( attribute.descriptor.name == "id" ) instanceAddresses.id = graphic.buffers.at(attribute.buffer).getAddress()/* + (drawCommand.vertexID * attribute.stride)*/; + if ( attribute.descriptor.name == "position" ) instanceAddresses.position = graphic.buffers.at(attribute.buffer).getAddress(); + else if ( attribute.descriptor.name == "uv" ) instanceAddresses.uv = graphic.buffers.at(attribute.buffer).getAddress(); + else if ( attribute.descriptor.name == "color" ) instanceAddresses.color = graphic.buffers.at(attribute.buffer).getAddress(); + else if ( attribute.descriptor.name == "st" ) instanceAddresses.st = graphic.buffers.at(attribute.buffer).getAddress(); + else if ( attribute.descriptor.name == "normal" ) instanceAddresses.normal = graphic.buffers.at(attribute.buffer).getAddress(); + else if ( attribute.descriptor.name == "tangent" ) instanceAddresses.tangent = graphic.buffers.at(attribute.buffer).getAddress(); + else if ( attribute.descriptor.name == "joints" ) instanceAddresses.joints = graphic.buffers.at(attribute.buffer).getAddress(); + else if ( attribute.descriptor.name == "weights" ) instanceAddresses.weights = graphic.buffers.at(attribute.buffer).getAddress(); + else if ( attribute.descriptor.name == "id" ) instanceAddresses.id = graphic.buffers.at(attribute.buffer).getAddress(); } } } @@ -562,59 +560,6 @@ namespace { } #endif } - - // lazy load animations if requested - void loadAnimation( const uf::stl::string& name ) { - auto& scene = uf::scene::getCurrentScene(); - auto& storage = uf::graph::globalStorage ? uf::graph::storage : scene.getComponent(); - - auto& animation = storage.animations.map[name]; - - UF_ASSERT( animation.path != "" ); - - uf::Serializer json; - json.readFromFile( animation.path ); - - animation.name = json["name"].as(animation.name); - animation.start = json["start"].as(animation.start); - animation.end = json["end"].as(animation.end); - - if ( animation.samplers.empty() ) ext::json::forEach( json["samplers"], [&]( ext::json::Value& value ){ - auto& sampler = animation.samplers.emplace_back(); - sampler.interpolator = value["interpolator"].as(sampler.interpolator); - - sampler.inputs.reserve( value["inputs"].size() ); - ext::json::forEach( value["inputs"], [&]( ext::json::Value& input ){ - sampler.inputs.emplace_back( input.as() ); - }); - - sampler.outputs.reserve( value["outputs"].size() ); - ext::json::forEach( value["outputs"], [&]( ext::json::Value& output ){ - sampler.outputs.emplace_back( uf::vector::decode( output, pod::Vector4f{} ) ); - }); - }); - - if ( animation.channels.empty() ) ext::json::forEach( json["channels"], [&]( ext::json::Value& value ){ - auto& channel = animation.channels.emplace_back(); - channel.path = value["path"].as(channel.path); - channel.node = value["node"].as(channel.node); - channel.sampler = value["sampler"].as(channel.sampler); - }); - } - - void unloadAnimation( const uf::stl::string& name ) { - auto& scene = uf::scene::getCurrentScene(); - auto& storage = uf::graph::globalStorage ? uf::graph::storage : scene.getComponent(); - - auto& animation = storage.animations.map[name]; - - animation.samplers.clear(); - animation.channels.clear(); - #if UF_ENV_DREAMCAST - animation.samplers.shrink_to_fit(); - animation.channels.shrink_to_fit(); - #endif - } } size_t uf::graph::initialBufferElements = 1024; @@ -636,12 +581,10 @@ UF_VERTEX_INTERPOLATE(uf::graph::mesh::Base, { return { uf::vector::lerp( p1.position, p2.position, t ), uf::vector::lerp( p1.uv, p2.uv, t ), - //uf::vector::lerp( p1.color, p2.color, t ), t < 0.5 ? p1.color : p2.color, uf::vector::lerp( p1.st, p2.st, t ), uf::vector::normalize( uf::vector::lerp( p1.normal, p2.normal, t ) ), uf::vector::normalize( uf::vector::lerp( p1.tangent, p2.tangent, t ) ), - //uf::vector::lerp( p1.id, p2.id, t ), t < 0.5 ? p1.id : p2.id, }; }) @@ -661,14 +604,11 @@ UF_VERTEX_INTERPOLATE(uf::graph::mesh::Skinned, { return { uf::vector::lerp( p1.position, p2.position, t ), uf::vector::lerp( p1.uv, p2.uv, t ), - //uf::vector::lerp( p1.color, p2.color, t ), t < 0.5 ? p1.color : p2.color, uf::vector::lerp( p1.st, p2.st, t ), uf::vector::normalize( uf::vector::lerp( p1.normal, p2.normal, t ) ), uf::vector::normalize( uf::vector::lerp( p1.tangent, p2.tangent, t ) ), - //uf::vector::lerp( p1.id, p2.id, t ), t < 0.5 ? p1.id : p2.id, - //uf::vector::lerp( p1.joints, p2.joints, t ), t < 0.5 ? p1.joints : p2.joints, uf::vector::lerp( p1.weights, p2.weights, t ), }; @@ -686,17 +626,6 @@ UF_VERTEX_DESCRIPTOR(uf::graph::mesh::Base_16f, ); UF_VERTEX_INTERPOLATE(uf::graph::mesh::Base_16f, { return t < 0.5 ? p1 : p2; -/* - return { - uf::vector::lerp( p1.position, p2.position, t ), - uf::vector::lerp( p1.uv, p2.uv, t ), - uf::vector::lerp( p1.color, p2.color, t ), - uf::vector::lerp( p1.st, p2.st, t ), - uf::vector::normalize( uf::vector::lerp( p1.normal, p2.normal, t ) ), - uf::vector::normalize( uf::vector::lerp( p1.tangent, p2.tangent, t ) ), - uf::vector::lerp( p1.id, p2.id, t ), - }; -*/ }) UF_VERTEX_DESCRIPTOR(uf::graph::mesh::Skinned_16f, @@ -712,19 +641,6 @@ UF_VERTEX_DESCRIPTOR(uf::graph::mesh::Skinned_16f, ); UF_VERTEX_INTERPOLATE(uf::graph::mesh::Skinned_16f, { return t < 0.5 ? p1 : p2; -/* - return { - uf::vector::lerp( p1.position, p2.position, t ), - uf::vector::lerp( p1.uv, p2.uv, t ), - uf::vector::lerp( p1.color, p2.color, t ), - uf::vector::lerp( p1.st, p2.st, t ), - uf::vector::normalize( uf::vector::lerp( p1.normal, p2.normal, t ) ), - uf::vector::normalize( uf::vector::lerp( p1.tangent, p2.tangent, t ) ), - uf::vector::lerp( p1.id, p2.id, t ), - uf::vector::lerp( p1.joints, p2.joints, t ), - uf::vector::lerp( p1.weights, p2.weights, t ), - }; -*/ }) #endif @@ -739,17 +655,6 @@ UF_VERTEX_DESCRIPTOR(uf::graph::mesh::Base_u16q, ); UF_VERTEX_INTERPOLATE(uf::graph::mesh::Base_u16q, { return t < 0.5 ? p1 : p2; -/* - return { - uf::vector::lerp( p1.position, p2.position, t ), - uf::vector::lerp( p1.uv, p2.uv, t ), - uf::vector::lerp( p1.color, p2.color, t ), - uf::vector::lerp( p1.st, p2.st, t ), - uf::vector::normalize( uf::vector::lerp( p1.normal, p2.normal, t ) ), - uf::vector::normalize( uf::vector::lerp( p1.tangent, p2.tangent, t ) ), - uf::vector::lerp( p1.id, p2.id, t ), - }; -*/ }) UF_VERTEX_DESCRIPTOR(uf::graph::mesh::Skinned_u16q, @@ -766,65 +671,28 @@ UF_VERTEX_DESCRIPTOR(uf::graph::mesh::Skinned_u16q, UF_VERTEX_INTERPOLATE(uf::graph::mesh::Skinned_u16q, { return t < 0.5 ? p1 : p2; -/* - return { - uf::vector::lerp( p1.position, p2.position, t ), - uf::vector::lerp( p1.uv, p2.uv, t ), - uf::vector::lerp( p1.color, p2.color, t ), - uf::vector::lerp( p1.st, p2.st, t ), - uf::vector::normalize( uf::vector::lerp( p1.normal, p2.normal, t ) ), - uf::vector::normalize( uf::vector::lerp( p1.tangent, p2.tangent, t ) ), - uf::vector::lerp( p1.id, p2.id, t ), - uf::vector::lerp( p1.joints, p2.joints, t ), - uf::vector::lerp( p1.weights, p2.weights, t ), - }; -*/ }) -pod::Matrix4f uf::graph::local( pod::Graph& graph, int32_t index ) { - auto& node = 0 < index && index <= graph.nodes.size() ? graph.nodes[index] : graph.root; - return - uf::matrix::translate( uf::matrix::identity(), node.transform.position ) * - uf::quaternion::matrix(node.transform.orientation) * - uf::matrix::scale( uf::matrix::identity(), node.transform.scale ) * - node.transform.model; -} -pod::Matrix4f uf::graph::matrix( pod::Graph& graph, int32_t index ) { - pod::Matrix4f matrix = local( graph, index ); - auto& node = *uf::graph::find( graph, index ); - int32_t parent = node.parent; - while ( 0 < parent && parent <= graph.nodes.size() ) { - matrix = local( graph, parent ) * matrix; - parent = graph.nodes[parent].parent; - } - return matrix; -} -pod::Node* uf::graph::find( pod::Graph& graph, int32_t index ) { - return 0 <= index && index < graph.nodes.size() ? &graph.nodes[index] : NULL; -} -pod::Node* uf::graph::find( pod::Graph& graph, const uf::stl::string& name ) { - for ( auto& node : graph.nodes ) if ( node.name == name ) return &node; - return NULL; -} - void uf::graph::initializeGraphics( pod::Graph& graph, uf::Object& entity, uf::Mesh& mesh ) { auto& scene = uf::scene::getCurrentScene(); auto& sceneTextures = scene.getComponent(); auto& sceneMetadataJson = scene.getComponent(); - auto& storage = uf::graph::globalStorage ? uf::graph::storage : scene.getComponent(); + auto& storage = ::getGraphStorage( scene ); + auto& graphMetadataJson = graph.metadata; + auto& graphic = entity.getComponent(); graphic.initialize(); graphic.initializeMesh( mesh ); graphic.device = &uf::renderer::device; graphic.material.device = &uf::renderer::device; - graphic.descriptor.frontFace = graph.metadata["renderer"]["invert"].as(true) ? uf::renderer::enums::Face::CW : uf::renderer::enums::Face::CCW; + graphic.descriptor.frontFace = graphMetadataJson["renderer"]["invert"].as(true) ? uf::renderer::enums::Face::CW : uf::renderer::enums::Face::CCW; graphic.descriptor.cullMode = uf::renderer::enums::CullMode::BACK; - auto tag = ext::json::find( entity.getName(), graph.metadata["tags"] ); + auto tag = ext::json::find( entity.getName(), graphMetadataJson["tags"] ); if ( !ext::json::isObject( tag ) ) { - tag["renderer"] = graph.metadata["renderer"]; + tag["renderer"] = graphMetadataJson["renderer"]; } if ( tag["renderer"]["front face"].is() ) { @@ -833,10 +701,9 @@ void uf::graph::initializeGraphics( pod::Graph& graph, uf::Object& entity, uf::M else if ( mode == "ccw" ) graphic.descriptor.frontFace = uf::renderer::enums::Face::CCW; else if ( mode == "auto" ) { if ( uf::matrix::reverseInfiniteProjection ) { - graphic.descriptor.frontFace = graph.metadata["renderer"]["invert"].as(true) ? uf::renderer::enums::Face::CW : uf::renderer::enums::Face::CCW; + graphic.descriptor.frontFace = graphMetadataJson["renderer"]["invert"].as(true) ? uf::renderer::enums::Face::CW : uf::renderer::enums::Face::CCW; } else { - graphic.descriptor.frontFace = graph.metadata["renderer"]["invert"].as(true) ? uf::renderer::enums::Face::CW : uf::renderer::enums::Face::CCW; - // graphic.descriptor.frontFace = graph.metadata["renderer"]["invert"].as(true) ? uf::renderer::enums::Face::CCW : uf::renderer::enums::Face::CW; + graphic.descriptor.frontFace = graphMetadataJson["renderer"]["invert"].as(true) ? uf::renderer::enums::Face::CW : uf::renderer::enums::Face::CCW; } } else UF_MSG_WARNING("Invalid Face enum string specified: {}", mode); @@ -850,15 +717,6 @@ void uf::graph::initializeGraphics( pod::Graph& graph, uf::Object& entity, uf::M else UF_MSG_WARNING("Invalid CullMode enum string specified: {}", mode); } -/* -#if UF_USE_OPENGL - if ( !uf::matrix::reverseInfiniteProjection ) { - if ( graphic.descriptor.cullMode == uf::renderer::enums::CullMode::FRONT ) graphic.descriptor.cullMode = uf::renderer::enums::CullMode::BACK; - if ( graphic.descriptor.cullMode == uf::renderer::enums::CullMode::BACK ) graphic.descriptor.cullMode = uf::renderer::enums::CullMode::FRONT; - } -#endif -*/ - ::bindTextures( graphic ); ::bindShaders( graph, entity, mesh ); ::bindBuffers( graphic, mesh ); @@ -875,12 +733,12 @@ void uf::graph::process( pod::Graph& graph ) { // copy lighting settings from graph auto& scene = uf::scene::getCurrentScene(); auto& sceneMetadataJson = scene.getComponent(); - auto& storage = uf::graph::globalStorage ? uf::graph::storage : scene.getComponent(); + auto& graphMetadataJson = graph.metadata; + auto& storage = ::getGraphStorage( scene ); -#if 1 // merge light settings with global settings { - const auto& globalSettings = graph.metadata["light"]; + const auto& globalSettings = graphMetadataJson["light"]; ext::json::forEach( globalSettings, [&]( const uf::stl::string& key, const ext::json::Value& value ){ if ( !ext::json::isNull( sceneMetadataJson["light"][key] ) ) return; sceneMetadataJson["light"][key] = value; @@ -888,7 +746,7 @@ void uf::graph::process( pod::Graph& graph ) { } // merge bloom settings with global settings { - const auto& globalSettings = graph.metadata["light"]["bloom"]; + const auto& globalSettings = graphMetadataJson["light"]["bloom"]; ext::json::forEach( globalSettings, [&]( const uf::stl::string& key, const ext::json::Value& value ){ if ( !ext::json::isNull( sceneMetadataJson["light"]["bloom"][key] ) ) return; sceneMetadataJson["light"]["bloom"][key] = value; @@ -896,7 +754,7 @@ void uf::graph::process( pod::Graph& graph ) { } // merge shadows settings with global settings { - const auto& globalSettings = graph.metadata["light"]["shadows"]; + const auto& globalSettings = graphMetadataJson["light"]["shadows"]; ext::json::forEach( globalSettings, [&]( const uf::stl::string& key, const ext::json::Value& value ){ if ( !ext::json::isNull( sceneMetadataJson["light"]["shadows"][key] ) ) return; sceneMetadataJson["light"]["shadows"][key] = value; @@ -904,18 +762,12 @@ void uf::graph::process( pod::Graph& graph ) { } // merge fog settings with global settings { - const auto& globalSettings = graph.metadata["light"]["fog"]; + const auto& globalSettings = graphMetadataJson["light"]["fog"]; ext::json::forEach( globalSettings, [&]( const uf::stl::string& key, const ext::json::Value& value ){ if ( !ext::json::isNull( sceneMetadataJson["light"]["fog"][key] ) ) return; sceneMetadataJson["light"]["fog"][key] = value; } ); } -#endif -#if 0 - if ( ext::json::isObject(graph.metadata["assets"]) || ext::json::isArray(graph.metadata["assets"]) ) { - scene.loadAssets(graph.metadata["assets"]); - } -#endif // uf::stl::unordered_map isSrgb; @@ -943,15 +795,15 @@ void uf::graph::process( pod::Graph& graph ) { } } - if ( graph.metadata["lights"]["lightmap"].is() && !graph.metadata["lights"]["lightmap"].as() ) { - graph.metadata["baking"]["enabled"] = false; + if ( graphMetadataJson["lights"]["lightmap"].is() && !graphMetadataJson["lights"]["lightmap"].as() ) { + graphMetadataJson["baking"]["enabled"] = false; } if ( !sceneMetadataJson["light"]["useLightmaps"].as(true) ) { - graph.metadata["lights"]["lightmap"] = false; - graph.metadata["baking"]["enabled"] = false; + graphMetadataJson["lights"]["lightmap"] = false; + graphMetadataJson["baking"]["enabled"] = false; } - if ( graph.metadata["lights"]["lightmap"].is() && graph.metadata["lights"]["lightmap"].as() == "auto" ) { + if ( graphMetadataJson["lights"]["lightmap"].is() && graphMetadataJson["lights"]["lightmap"].as() == "auto" ) { uint32_t mtime = uf::io::mtime( graph.name ); // lightmaps are considered stale if they're older than the graph's source bool stale = false; @@ -965,24 +817,24 @@ void uf::graph::process( pod::Graph& graph ) { break; } } - graph.metadata["lights"]["lightmap"] = !stale; + graphMetadataJson["lights"]["lightmap"] = !stale; } - graph.metadata["baking"]["layers"] = lightmapCount; + graphMetadataJson["baking"]["layers"] = lightmapCount; - if ( graph.metadata["lights"]["lightmap"].as() ) { + if ( graphMetadataJson["lights"]["lightmap"].as() ) { for ( auto& pair : filenames ) { auto i = pair.first; auto f = uf::io::sanitize( pair.second, uf::io::directory( graph.name ) ); if ( !uf::io::exists( f ) ) { - graph.metadata["lights"]["lightmap"] = false; + graphMetadataJson["lights"]["lightmap"] = false; UF_MSG_ERROR( "lightmap does not exist: {} {}, disabling lightmaps", i, f ) break; } } } - if ( graph.metadata["lights"]["lightmap"].as() ) { + if ( graphMetadataJson["lights"]["lightmap"].as() ) { for ( auto& pair : filenames ) { auto i = pair.first; auto f = uf::io::sanitize( pair.second, uf::io::directory( graph.name ) ); @@ -990,8 +842,8 @@ void uf::graph::process( pod::Graph& graph ) { auto textureID = graph.textures.size(); auto imageID = graph.images.size(); - auto& texture = /*graph.storage*/storage.textures[graph.textures.emplace_back(f)]; - auto& image = /*graph.storage*/storage.images[graph.images.emplace_back(f)]; + auto& texture = storage.textures[graph.textures.emplace_back(f)]; + auto& image = storage.images[graph.images.emplace_back(f)]; if ( !graph.settings.stream.textures ) { image.open( f, false ); } @@ -1000,8 +852,8 @@ void uf::graph::process( pod::Graph& graph ) { lightmapIDs[i] = textureID; - graph.metadata["lights"]["lightmaps"][i] = f; - graph.metadata["baking"]["enabled"] = false; + graphMetadataJson["lights"]["lightmaps"][i] = f; + graphMetadataJson["baking"]["enabled"] = false; isSrgb[f] = false; } @@ -1020,8 +872,6 @@ void uf::graph::process( pod::Graph& graph ) { } } } - // add atlas - // setup textures storage.texture2Ds.reserve( storage.images.map.size() ); @@ -1056,9 +906,9 @@ void uf::graph::process( pod::Graph& graph ) { } auto filter = uf::renderer::enums::Filter::LINEAR; - auto tag = ext::json::find( key, graph.metadata["tags"] ); + auto tag = ext::json::find( key, graphMetadataJson["tags"] ); if ( !ext::json::isObject( tag ) ) { - tag["renderer"] = graph.metadata["renderer"]; + tag["renderer"] = graphMetadataJson["renderer"]; } if ( tag["renderer"]["filter"].is() ) { const auto mode = uf::string::lowercase( tag["renderer"]["filter"].as("linear") ); @@ -1094,7 +944,7 @@ void uf::graph::process( pod::Graph& graph ) { UF_DEBUG_TIMER_MULTITRACE("Remapping patching/textures"); for ( auto& name : graph.materials ) { auto& material = storage.materials[name]; - auto tag = ext::json::find( name, graph.metadata["tags"] ); + auto tag = ext::json::find( name, graphMetadataJson["tags"] ); if ( ext::json::isObject( tag ) ) { material.colorBase = uf::vector::decode( tag["material"]["base"], material.colorBase); material.colorEmissive = uf::vector::decode( tag["material"]["emissive"], material.colorEmissive); @@ -1170,19 +1020,7 @@ void uf::graph::process( pod::Graph& graph ) { if ( !(0 <= texture.index && texture.index < graph.images.size()) ) continue; auto& needle = graph.images[texture.index]; - #if 1 texture.index = indices[needle]; - #elif 1 - for ( size_t i = 0; i < keys.size(); ++i ) { - if ( keys[i] != needle ) continue; - texture.index = i; - break; - } - #else - auto it = std::find( keys.begin(), keys.end(), needle ); - UF_ASSERT( it != keys.end() ); - texture.index = it - keys.begin(); - #endif } // remap materials->texture IDs @@ -1196,76 +1034,37 @@ void uf::graph::process( pod::Graph& graph ) { auto& ID = *pointer; if ( !(0 <= ID && ID < graph.textures.size()) ) continue; auto& needle = graph.textures[ID]; - #if 1 ID = indices[needle]; - #elif 1 - for ( size_t i = 0; i < keys.size(); ++i ) { - if ( keys[i] != needle ) continue; - ID = i; - break; - } - #else - if ( !(0 <= ID && ID < graph.textures.size()) ) continue; - auto it = std::find( keys.begin(), keys.end(), needle ); - UF_ASSERT( it != keys.end() ); - ID = it - keys.begin(); - #endif } } // remap instance variables UF_DEBUG_TIMER_MULTITRACE("Remapping instances"); for ( auto& name : graph.instances ) { auto& instance = storage.instances[name]; - // auto& instanceAddresses = storage.instanceAddresses[name]; - // instance.addresses = instanceAddresses; if ( 0 <= instance.materialID && instance.materialID < graph.materials.size() ) { - auto& keys = /*graph.storage*/storage.materials.keys; - auto& indices = /*graph.storage*/storage.materials.indices; + auto& keys = storage.materials.keys; + auto& indices = storage.materials.indices; if ( !(0 <= instance.materialID && instance.materialID < graph.materials.size()) ) continue; auto& needle = graph.materials[instance.materialID]; - #if 1 instance.materialID = indices[needle]; - #elif 1 - for ( size_t i = 0; i < keys.size(); ++i ) { - if ( keys[i] != needle ) continue; - instance.materialID = i; - break; - } - #else - auto it = std::find( keys.begin(), keys.end(), needle ); - UF_ASSERT( it != keys.end() ); - instance.materialID = it - keys.begin(); - #endif } if ( 0 <= instance.lightmapID && instance.lightmapID < graph.textures.size() ) { - auto& keys = /*graph.storage*/storage.textures.keys; - auto& indices = /*graph.storage*/storage.textures.indices; + auto& keys = storage.textures.keys; + auto& indices = storage.textures.indices; if ( !(0 <= instance.lightmapID && instance.lightmapID < graph.textures.size()) ) continue; auto& needle = graph.textures[instance.lightmapID]; - #if 1 instance.lightmapID = indices[needle]; - #elif 1 - for ( size_t i = 0; i < keys.size(); ++i ) { - if ( keys[i] != needle ) continue; - instance.lightmapID = i; - break; - } - #else - auto it = std::find( keys.begin(), keys.end(), needle ); - UF_ASSERT( it != keys.end() ); - instance.lightmapID = it - keys.begin(); - #endif } #if 0 // i genuinely dont remember what this is used for if ( 0 <= instance.imageID && instance.imageID < graph.images.size() ) { - auto& keys = /*graph.storage*/storage.images.keys; + auto& keys = storage.images.keys; auto it = std::find( keys.begin(), keys.end(), graph.images[instance.imageID] ); UF_ASSERT( it != keys.end() ); instance.imageID = it - keys.begin(); @@ -1283,15 +1082,15 @@ void uf::graph::process( pod::Graph& graph ) { } } - // remap instance variables + // remap draw commands #if 0 UF_DEBUG_TIMER_MULTITRACE("Remapping drawCommands"); for ( auto& name : graph.drawCommands ) { auto& drawCommands = storage.drawCommands[name]; for ( auto& drawCommand : drawCommands ) { if ( 0 <= drawCommand.instanceID && drawCommand.instanceID < graph.instances.size() ) { - auto& keys = /*graph.storage*/storage.instances.keys; - auto& indices = /*graph.storage*/storage.instances.indices; + auto& keys = storage.instances.keys; + auto& indices = storage.instances.indices; if ( !(0 <= drawCommand.instanceID && drawCommand.instanceID < graph.instances.size()) ) continue; @@ -1314,20 +1113,20 @@ void uf::graph::process( pod::Graph& graph ) { } #endif - if ( graph.metadata["debug"]["print"]["lights"].as() ) { + if ( graphMetadataJson["debug"]["print"]["lights"].as() ) { UF_MSG_DEBUG("Lights: {}", graph.lights.size()); for ( auto& pair : graph.lights ) { UF_MSG_DEBUG("\tLight: {}", pair.first); } } - if ( graph.metadata["debug"]["print"]["meshes"].as() ) { + if ( graphMetadataJson["debug"]["print"]["meshes"].as() ) { UF_MSG_DEBUG("Meshs: {}", graph.meshes.size()); for ( auto& name : graph.meshes ) { UF_MSG_DEBUG("\tMesh: {}", name); } } - if ( graph.metadata["debug"]["print"]["instances"].as() ) { + if ( graphMetadataJson["debug"]["print"]["instances"].as() ) { UF_MSG_DEBUG("Instances: {}", graph.instances.size()); for ( auto& name : graph.instances ) { auto& instance = storage.instances[name]; @@ -1337,21 +1136,21 @@ void uf::graph::process( pod::Graph& graph ) { ); } } - if ( graph.metadata["debug"]["print"]["materials"].as() ) { + if ( graphMetadataJson["debug"]["print"]["materials"].as() ) { UF_MSG_DEBUG("Materials: {}", graph.materials.size()); for ( auto& name : graph.materials ) { auto& material = storage.materials[name]; UF_MSG_DEBUG("\tMaterial: {} | {}", name, material.indexAlbedo); } } - if ( graph.metadata["debug"]["print"]["textures"].as() ) { + if ( graphMetadataJson["debug"]["print"]["textures"].as() ) { UF_MSG_DEBUG("Textures: {}", graph.textures.size()); for ( auto& name : graph.textures ) { auto& texture = storage.textures[name]; UF_MSG_DEBUG("\tTexture: {} | {}", name, texture.index); } } - if ( graph.metadata["debug"]["print"]["images"].as() ) { + if ( graphMetadataJson["debug"]["print"]["images"].as() ) { UF_MSG_DEBUG("Images: {}", graph.images.size()); for ( auto& name : graph.images ) { UF_MSG_DEBUG("\tImage: {}", name); @@ -1369,40 +1168,22 @@ void uf::graph::process( pod::Graph& graph ) { } void uf::graph::process( pod::Graph& graph, int32_t index, uf::Object& parent ) { auto& scene = uf::scene::getCurrentScene(); - auto& storage = uf::graph::globalStorage ? uf::graph::storage : scene.getComponent(); + auto& storage = ::getGraphStorage( scene ); + auto& graphMetadataJson = graph.metadata; auto& node = graph.nodes[index]; // bool ignore = false; // ignore pesky light_Orientation nodes if ( uf::string::split( node.name, "_" ).back() == "Orientation" ) ignore = true; - // for dreamcast, ignore lights if we're baked -/* - if ( graph.metadata["lights"]["lightmap"].as() && graph.metadata["lights"]["disable if lightmapped"].as(true) ) { - if ( graph.lights.count(node.name) > 0 ) ignore = true; - } -*/ - - // on systems where frametime is very, very important, we can set all static nodes to not tick - - ext::json::Value tag = ext::json::find( node.name, graph.metadata["tags"] ); + + ext::json::Value tag = ext::json::find( node.name, graphMetadataJson["tags"] ); if ( ext::json::isObject( tag ) ) { - if ( graph.metadata["baking"]["enabled"].as(false) && !tag["bake"].as(true) ) ignore = true; + if ( graphMetadataJson["baking"]["enabled"].as(false) && !tag["bake"].as(true) ) ignore = true; if ( tag["ignore"].as() ) ignore = true; } bool isLight = graph.lights.count(node.name) > 0; -/* - if ( graph.metadata["lights"]["lightmap"].as() ) - if ( ext::json::isObject( tag ) ) { - if ( !tag["light"]["dynamic"].as() && !tag["light"]["shadows"].as() ) { - ignore = true; - UF_MSG_DEBUG("IGNORING {}", node.name); - } - } else { - ignore = true; - } - } -*/ + if ( ignore ) return; // create child @@ -1421,28 +1202,21 @@ void uf::graph::process( pod::Graph& graph, int32_t index, uf::Object& parent ) if ( ext::json::isObject( tag ) ) { if ( tag["action"].as() == "load" ) { if ( tag["filename"].is() ) { - uf::stl::string filename = uf::io::resolveURI( tag["filename"].as(), graph.metadata["root"].as() ); + uf::stl::string filename = uf::io::resolveURI( tag["filename"].as(), graphMetadataJson["root"].as() ); entity.load(filename); } else if ( ext::json::isObject( tag["payload"] ) ) { uf::Serializer json = tag["payload"]; - json["root"] = graph.metadata["root"]; + json["root"] = graphMetadataJson["root"]; entity.load(json); } } else if ( tag["action"].as() == "attach" ) { - uf::stl::string filename = uf::io::resolveURI( tag["filename"].as(), graph.metadata["root"].as() ); + uf::stl::string filename = uf::io::resolveURI( tag["filename"].as(), graphMetadataJson["root"].as() ); auto& child = entity.loadChild( filename, false ); auto& childTransform = child.getComponent>(); auto& childMetadataJson = child.getComponent(); auto flatten = uf::transform::flatten( node.transform ); - // if ( !tag["preserve position"].as(false) ) childTransform.position = flatten.position; - // if ( !tag["preserve orientation"].as(false) ) childTransform.orientation = flatten.orientation; - - // childTransform.position = flatten.position; - // childTransform.orientation = flatten.orientation; childTransform = flatten; - - // childMetadataJson["transform"] = uf::transform::encode( flatten ); } if ( tag["static"].is() ) { metadata.system.ignoreGraph = tag["static"].as(); @@ -1457,46 +1231,42 @@ void uf::graph::process( pod::Graph& graph, int32_t index, uf::Object& parent ) #if UF_USE_OPENGL metadata.system.ignoreGraph = true; #else - metadata.system.ignoreGraph = graph.metadata["debug"]["static"].as(); + metadata.system.ignoreGraph = graphMetadataJson["debug"]["static"].as(); #endif - float powerScale = graph.metadata["lights"]["scale"].as(1); + float powerScale = graphMetadataJson["lights"]["scale"].as(1); uf::Serializer metadataLight; metadataLight["radius"][0] = 0.001; - metadataLight["radius"][1] = l.range; // l.range <= 0.001f ? graph.metadata["lights"]["range cap"].as() : l.range; - metadataLight["power"] = l.intensity * powerScale; // * graph.metadata["lights"]["power scale"].as(); + metadataLight["radius"][1] = l.range; + metadataLight["power"] = l.intensity * powerScale; + metadataLight["color"][0] = l.color.x; metadataLight["color"][1] = l.color.y; metadataLight["color"][2] = l.color.z; - metadataLight["shadows"] = graph.metadata["lights"]["shadows"].as(); + + metadataLight["shadows"] = graphMetadataJson["lights"]["shadows"].as(); metadataLight["dynamic"] = false; if ( uf::string::matched( node.name, R"(/\bspot\b/)" ) ) { metadataLight["type"] = "spot"; } - if ( ext::json::isArray( graph.metadata["lights"]["radius"] ) ) { - metadataLight["radius"] = graph.metadata["lights"]["radius"]; + if ( ext::json::isArray( graphMetadataJson["lights"]["radius"] ) ) { + metadataLight["radius"] = graphMetadataJson["lights"]["radius"]; } - if ( graph.metadata["lights"]["bias"].is() ) { - metadataLight["bias"] = graph.metadata["lights"]["bias"].as(); + if ( graphMetadataJson["lights"]["bias"].is() ) { + metadataLight["bias"] = graphMetadataJson["lights"]["bias"].as(); } // copy from tag information - ext::json::forEach( graph.metadata["tags"][node.name]["light"], [&]( const uf::stl::string& key, ext::json::Value& value ){ + ext::json::forEach( graphMetadataJson["tags"][node.name]["light"], [&]( const uf::stl::string& key, ext::json::Value& value ){ if ( key == "transform" ) return; metadataLight[key] = value; }); - /* - bool should = false; - if ( !graph.metadata["lights"]["lightmap"].as() ) { - should = true; - } else if ( metadataLight["shadows"].as() || metadataLight["dynamic"].as() ) { - should = true; - } - */ + auto& metadataJson = entity.getComponent(); entity.load("/light.json"); // copy + ext::json::forEach( metadataLight, [&]( const uf::stl::string& key, ext::json::Value& value ) { metadataJson["light"][key] = value; }); @@ -1563,7 +1333,7 @@ void uf::graph::process( pod::Graph& graph, int32_t index, uf::Object& parent ) instance.model = model; instance.previous = model; instance.objectID = objectID; - instance.jointID = graph.metadata["renderer"]["skinned"].as() ? 0 : -1; + instance.jointID = graphMetadataJson["renderer"]["skinned"].as() ? 0 : -1; bounds.min = uf::vector::min( bounds.min, instance.bounds.min ); bounds.max = uf::vector::max( bounds.max, instance.bounds.max ); @@ -1576,7 +1346,7 @@ void uf::graph::process( pod::Graph& graph, int32_t index, uf::Object& parent ) drawCommand.instanceID = instanceID; } } - if ( /*(graph.metadata["renderer"]["separate"].as()) &&*/ graph.metadata["renderer"]["render"].as() ) { + if ( graphMetadataJson["renderer"]["render"].as() ) { uf::graph::initializeGraphics( graph, entity, mesh ); } @@ -1630,7 +1400,7 @@ void uf::graph::process( pod::Graph& graph, int32_t index, uf::Object& parent ) instance.model = model; instance.previous = model; instance.objectID = objectID; - instance.jointID = graph.metadata["renderer"]["skinned"].as() ? 0 : -1; + instance.jointID = graphMetadataJson["renderer"]["skinned"].as() ? 0 : -1; bounds.min = uf::vector::min( bounds.min, instance.bounds.min ); bounds.max = uf::vector::max( bounds.max, instance.bounds.max ); @@ -1670,237 +1440,8 @@ void uf::graph::process( pod::Graph& graph, int32_t index, uf::Object& parent ) for ( auto index : node.children ) uf::graph::process( graph, index, entity ); } -void uf::graph::cleanup( pod::Graph& graph ) { -} -void uf::graph::override( pod::Graph& graph ) { - auto& scene = uf::scene::getCurrentScene(); - auto& storage = uf::graph::globalStorage ? uf::graph::storage : scene.getComponent(); - - graph.settings.animations.override.a = 0; - graph.settings.animations.override.map.clear(); - bool toNeutralPose = graph.sequence.empty(); - // store every node's current transform - for ( auto& node : graph.nodes ) { - graph.settings.animations.override.map[node.index].first = node.transform; - graph.settings.animations.override.map[node.index].second = node.transform; - if ( toNeutralPose ) { - graph.settings.animations.override.map[node.index].second.position = { 0, 0, 0 }; - graph.settings.animations.override.map[node.index].second.orientation = { 0, 0, 0, 1 }; - graph.settings.animations.override.map[node.index].second.scale = { 1, 1, 1 }; - } - } - // set our destination transform per node - if ( !toNeutralPose ) { - uf::stl::string name = graph.sequence.front(); - pod::Animation& animation = storage.animations.map[name]; // graph.animations[name]; - // load animation data - if ( animation.channels.empty() || animation.samplers.empty() ) ::loadAnimation( name ); - - for ( auto& channel : animation.channels ) { - auto& override = graph.settings.animations.override.map[channel.node]; - auto& sampler = animation.samplers[channel.sampler]; - if ( sampler.interpolator != "LINEAR" ) continue; - for ( size_t i = 0; i < sampler.inputs.size() - 1; ++i ) { - if ( !(animation.start >= sampler.inputs[i] && animation.start <= sampler.inputs[i+1]) ) continue; - if ( channel.path == "translation" ) { - override.second.position = sampler.outputs[i]; - } else if ( channel.path == "rotation" ) { - override.second.orientation = uf::quaternion::normalize( sampler.outputs[i] ); - } else if ( channel.path == "scale" ) { - override.second.scale = sampler.outputs[i]; - } - } - } - } -} - -void uf::graph::initialize( pod::Graph& graph ) { - if ( graph.metadata["baking"]["enabled"].as() ) { - auto& metadataJson = graph.root.entity->getComponent(); - metadataJson["baking"] = graph.metadata["baking"]; - metadataJson["baking"]["root"] = uf::io::directory( graph.name ); - uf::instantiator::bind( "BakingBehavior", *graph.root.entity ); - } - - graph.root.entity->initialize(); - graph.root.entity->process([&]( uf::Entity* entity ) { - if ( entity->getUid() == 0 ) entity->initialize(); - }); - - auto& scene = uf::scene::getCurrentScene(); - scene.invalidateGraph(); -} -void uf::graph::animate( pod::Graph& graph, const uf::stl::string& _name, float speed, bool immediate ) { - auto& scene = uf::scene::getCurrentScene(); - auto& storage = uf::graph::globalStorage ? uf::graph::storage : scene.getComponent(); - - if ( !(graph.metadata["renderer"]["skinned"].as()) ) return; - const uf::stl::string name = /*graph.name + "/" +*/ _name; -// if ( graph.animations.count( name ) > 0 ) { - if ( storage.animations.map.count( name ) > 0 ) { - // if already playing, ignore it - if ( !graph.sequence.empty() && graph.sequence.front() == name ) return; - if ( immediate ) { - while ( !graph.sequence.empty() ) { - // unload - if ( graph.settings.stream.animations ) ::unloadAnimation( graph.sequence.front() ); - graph.sequence.pop(); - } - } - bool empty = graph.sequence.empty(); - graph.sequence.emplace(name); - if ( empty ) uf::graph::override( graph ); - graph.settings.animations.speed = speed; - } - update( graph, 0 ); -} -void uf::graph::update( pod::Graph& graph ) { - return update( graph, uf::physics::time::delta ); -} -void uf::graph::update( pod::Graph& graph, float delta ) { - auto& scene = uf::scene::getCurrentScene(); - auto& storage = uf::graph::globalStorage ? uf::graph::storage : scene.getComponent(); - - // rebuild - if ( ::shouldRebind ) { - for ( auto& node : graph.nodes ) { - if ( !(0 <= node.mesh && node.mesh < graph.meshes.size()) ) continue; - if ( !node.entity ) continue; - - auto& entity = node.entity->as(); - - if ( !entity.hasComponent() ) continue; - - auto& graphic = entity.getComponent(); - auto& mesh = storage.meshes.map[graph.meshes[node.mesh]]; - - ::bindBuffers( graphic, mesh ); - } - } - - // get last update time -#if UF_GRAPH_EXTENDED - if ( graph.settings.stream.enabled && graph.settings.stream.hash != 0 && uf::physics::time::current - graph.settings.stream.lastUpdate > graph.settings.stream.every ) { - graph.settings.stream.lastUpdate = uf::physics::time::current; - uf::graph::reload( graph ); - } -#endif - - // update our instances -#if !UF_ENV_DREAMCAST -// UF_TIMER_MULTITRACE_START("Tick Start"); - uf::stl::unordered_map instanceCache; - for ( auto& name : storage.instances.keys ) { - auto& instance = storage.instances[name]; - instance.previous = instance.model; - - if ( instanceCache.count( instance.objectID ) > 0 ) { - instance.model = instanceCache[instance.objectID]; - continue; - } - - auto& entity = *storage.entities[std::to_string(instance.objectID)]; - if ( !entity.isValid() ) continue; - auto& metadata = entity.getComponent(); - if ( metadata.system.ignoreGraph ) continue; - - auto& transform = entity.getComponent>(); - instance.model = (instanceCache[instance.objectID] = uf::transform::model( transform )); - } -// UF_TIMER_MULTITRACE_END("Tick End"); -#endif - - // no skins - if ( !(graph.metadata["renderer"]["skinned"].as()) ) { - return; - } - - if ( graph.sequence.empty() ) goto UPDATE; - if ( graph.settings.animations.override.a >= 0 ) goto OVERRIDE; - { - uf::stl::string name = graph.sequence.front(); - pod::Animation* animation = &storage.animations.map[name]; // &graph.animations[name]; - animation->cur += delta * graph.settings.animations.speed; // * graph.settings.animations.override.speed; - if ( animation->end < animation->cur ) { - animation->cur = graph.settings.animations.loop ? animation->cur - animation->end : 0; - // go-to next animation - if ( !graph.settings.animations.loop ) { - // unload - if ( graph.settings.stream.animations ) ::unloadAnimation( graph.sequence.front() ); - graph.sequence.pop(); - - // out of animations, set to neutral pose - if ( graph.sequence.empty() ) { - uf::graph::override( graph ); - goto OVERRIDE; - } - name = graph.sequence.front(); - animation = &storage.animations.map[name]; // &graph.animations[name]; - } - } - - // load animation data - if ( animation->channels.empty() || animation->samplers.empty() ) ::loadAnimation( name ); - - for ( auto& channel : animation->channels ) { - auto& sampler = animation->samplers[channel.sampler]; - if ( sampler.interpolator != "LINEAR" ) continue; - for ( size_t i = 0; i < sampler.inputs.size() - 1; ++i ) { - if ( !(animation->cur >= sampler.inputs[i] && animation->cur <= sampler.inputs[i+1]) ) continue; - float a = (animation->cur - sampler.inputs[i]) / (sampler.inputs[i+1] - sampler.inputs[i]); - auto& transform = graph.nodes[channel.node].transform; - if ( channel.path == "translation" ) { - transform.position = uf::vector::mix( sampler.outputs[i], sampler.outputs[i+1], a ); - } else if ( channel.path == "rotation" ) { - transform.orientation = uf::quaternion::normalize( uf::quaternion::slerp(sampler.outputs[i], sampler.outputs[i+1], a) ); - } else if ( channel.path == "scale" ) { - transform.scale = uf::vector::mix( sampler.outputs[i], sampler.outputs[i+1], a ); - } - } - } - goto UPDATE; - } -OVERRIDE: - for ( auto pair : graph.settings.animations.override.map ) { - graph.nodes[pair.first].transform.position = uf::vector::mix( pair.second.first.position, pair.second.second.position, graph.settings.animations.override.a ); - graph.nodes[pair.first].transform.orientation = uf::quaternion::normalize( uf::quaternion::slerp(pair.second.first.orientation, pair.second.second.orientation, graph.settings.animations.override.a) ); - graph.nodes[pair.first].transform.scale = uf::vector::mix( pair.second.first.scale, pair.second.second.scale, graph.settings.animations.override.a ); - } - // finished our overrided interpolation, clear it - if ( (graph.settings.animations.override.a += delta * graph.settings.animations.override.speed) >= 1 ) { - graph.settings.animations.override.a = -std::numeric_limits::max(); - graph.settings.animations.override.map.clear(); - } -UPDATE: - for ( auto& node : graph.nodes ) uf::graph::update( graph, node ); -} -void uf::graph::update( pod::Graph& graph, pod::Node& node ) { - auto& scene = uf::scene::getCurrentScene(); - auto& storage = uf::graph::globalStorage ? uf::graph::storage : scene.getComponent(); - - if ( 0 <= node.skin && node.skin < graph.skins.size() ) { - pod::Matrix4f nodeMatrix = uf::graph::matrix( graph, node.index ); - pod::Matrix4f inverseTransform = uf::matrix::inverse( nodeMatrix ); - - auto& name = graph.skins[node.skin]; - auto& skin = storage.skins[name]; - auto& joints = storage.joints[name]; - joints.resize( skin.joints.size() ); - for ( size_t i = 0; i < skin.joints.size(); ++i ) joints[i] = uf::matrix::identity(); - - if ( graph.settings.animations.override.a >= 0 || !graph.sequence.empty() ) { - for ( size_t i = 0; i < skin.joints.size(); ++i ) { - joints[i] = inverseTransform * (uf::graph::matrix(graph, skin.joints[i]) * skin.inverseBindMatrices[i]); - } - } - } -} void uf::graph::destroy( pod::Graph& graph ) { -#if 0 - for ( auto& t : graph.textures ) t.texture.destroy(); - for ( auto& m : graph.meshes ) m.destroy(); -#endif } void uf::graph::initialize() { @@ -1920,56 +1461,55 @@ void uf::graph::initialize( pod::Graph::Storage& storage, size_t initialElements storage.buffers.texture.initialize( (const void*) nullptr, sizeof(pod::Texture) * initialElements, uf::renderer::enums::Buffer::STORAGE ); storage.buffers.light.initialize( (const void*) nullptr, sizeof(pod::Light) * initialElements, uf::renderer::enums::Buffer::STORAGE ); } + +void uf::graph::initialize( pod::Graph& graph ) { + if ( graph.metadata["baking"]["enabled"].as() ) { + auto& metadataJson = graph.root.entity->getComponent(); + metadataJson["baking"] = graph.metadata["baking"]; + metadataJson["baking"]["root"] = uf::io::directory( graph.name ); + uf::instantiator::bind( "BakingBehavior", *graph.root.entity ); + } + + graph.root.entity->initialize(); + graph.root.entity->process([&]( uf::Entity* entity ) { + if ( entity->getUid() == 0 ) entity->initialize(); + }); + + auto& scene = uf::scene::getCurrentScene(); + scene.invalidateGraph(); +} + void uf::graph::tick() { uf::graph::tick( uf::scene::getCurrentScene() ); } void uf::graph::tick( uf::Object& object ) { auto& storage = uf::graph::globalStorage ? uf::graph::storage : object.getComponent(); - auto& graph = object.getComponent(); - ::shouldRebind = uf::graph::tick( storage ); } bool uf::graph::tick( pod::Graph::Storage& storage ) { -/* - uf::stl::vector instances; instances.reserve(storage.instances.map.size()); - for ( auto& key : storage.instances.keys ) instances.emplace_back( storage.instances.map[key] ); - if ( !instances.empty() ) storage.buffers.instance.update( (const void*) instances.data(), instances.size() * sizeof(pod::Instance) ); -*/ bool rebuild = false; uf::stl::vector instances = storage.instances.flatten(); - rebuild = storage.buffers.instance.update( (const void*) instances.data(), instances.size() * sizeof(pod::Instance) ) || rebuild; -/* - uf::stl::vector instanceAddresses; instanceAddresses.reserve(storage.instanceAddresses.map.size()); - for ( auto& key : storage.instances.keys ) instanceAddresses.emplace_back( storage.instanceAddresses.map[key] ); - if ( !instanceAddresses.empty() ) rebuild = storage.buffers.instanceAddresses.update( (const void*) instanceAddresses.data(), instanceAddresses.size() * sizeof(pod::Instance::Addresses) ) || rebuild; -*/ uf::stl::vector instanceAddresses = storage.instanceAddresses.flatten(); - rebuild = storage.buffers.instanceAddresses.update( (const void*) instanceAddresses.data(), instanceAddresses.size() * sizeof(pod::Instance::Addresses) ) || rebuild; - uf::stl::vector joints; joints.reserve(storage.joints.map.size()); + + for ( auto& key : storage.joints.keys ) { auto& matrices = storage.joints.map[key]; joints.reserve( joints.size() + matrices.size() ); for ( auto& mat : matrices ) joints.emplace_back( mat ); } - /*if ( !joints.empty() )*/ rebuild = storage.buffers.joint.update( (const void*) joints.data(), joints.size() * sizeof(pod::Matrix4f) ) || rebuild; + + rebuild = storage.buffers.instance.update( (const void*) instances.data(), instances.size() * sizeof(pod::Instance) ) || rebuild; + rebuild = storage.buffers.instanceAddresses.update( (const void*) instanceAddresses.data(), instanceAddresses.size() * sizeof(pod::Instance::Addresses) ) || rebuild; + rebuild = storage.buffers.joint.update( (const void*) joints.data(), joints.size() * sizeof(pod::Matrix4f) ) || rebuild; if ( ::newGraphAdded ) { - #if 1 uf::stl::vector materials = storage.materials.flatten(); uf::stl::vector textures = storage.textures.flatten(); - uf::stl::vector drawCommands; drawCommands.reserve(storage.drawCommands.map.size()); - for ( auto& key : storage.drawCommands.keys ) drawCommands.insert( drawCommands.end(), storage.drawCommands.map[key].begin(), storage.drawCommands.map[key].end() ); - #else - uf::stl::vector drawCommands; drawCommands.reserve(storage.drawCommands.map.size()); - uf::stl::vector materials; materials.reserve(storage.materials.map.size()); - uf::stl::vector textures; textures.reserve(storage.textures.map.size()); for ( auto& key : storage.drawCommands.keys ) drawCommands.insert( drawCommands.end(), storage.drawCommands.map[key].begin(), storage.drawCommands.map[key].end() ); - for ( auto& key : storage.materials.keys ) materials.emplace_back( storage.materials.map[key] ); - for ( auto& key : storage.textures.keys ) textures.emplace_back( storage.textures.map[key] ); - #endif + rebuild = storage.buffers.drawCommands.update( (const void*) drawCommands.data(), drawCommands.size() * sizeof(pod::DrawCommand) ) || rebuild; rebuild = storage.buffers.material.update( (const void*) materials.data(), materials.size() * sizeof(pod::Material) ) || rebuild; rebuild = storage.buffers.texture.update( (const void*) textures.data(), textures.size() * sizeof(pod::Texture) ) || rebuild; @@ -1983,17 +1523,6 @@ bool uf::graph::tick( pod::Graph::Storage& storage ) { if ( uf::renderer::hasRenderMode("", true) ) { auto& renderMode = uf::renderer::getRenderMode("", true); - - /* - renderMode.metadata.buffers["camera"] = storage.buffers.camera; - renderMode.metadata.buffers["drawCommands"] = storage.buffers.drawCommands; - renderMode.metadata.buffers["instance"] = storage.buffers.instance; - renderMode.metadata.buffers["instanceAddresses"] = storage.buffers.instanceAddresses; - renderMode.metadata.buffers["joint"] = storage.buffers.joint; - renderMode.metadata.buffers["material"] = storage.buffers.material; - renderMode.metadata.buffers["texture"] = storage.buffers.texture; - renderMode.metadata.buffers["light"] = storage.buffers.light; - */ #if UF_USE_VULKAN auto& blitter = renderMode.getBlitter(); @@ -2024,11 +1553,9 @@ void uf::graph::render( pod::Graph::Storage& storage ) { auto& scene = uf::scene::getCurrentScene(); auto& controller = scene.getController(); - auto& camera = scene.getCamera( controller ); // controller.getComponent(); - -// camera.update(); - auto viewport = camera.data().viewport; + auto& camera = scene.getCamera( controller ); + auto viewport = camera.data().viewport; #if UF_USE_FFX_FSR if ( ext::fsr::initialized && renderMode->getType() == "Deferred" ) { auto jitter = ext::fsr::getJitterMatrix(); @@ -2041,19 +1568,9 @@ void uf::graph::render( pod::Graph::Storage& storage ) { storage.buffers.camera.update( (const void*) &viewport, sizeof(pod::Camera::Viewports) ); #if UF_USE_VULKAN - if ( !renderMode ) return; - if ( !renderMode->hasBuffer("camera") ) return; + if ( !renderMode || !renderMode->hasBuffer("camera") ) return; auto& buffer = renderMode->getBuffer("camera"); buffer.update( (const void*) &viewport, sizeof(pod::Camera::Viewports) ); -/* - for ( auto& buffer : renderMode->buffers ) { - if ( !(buffer.usage & uf::renderer::enums::Buffer::UNIFORM) ) continue; - if ( buffer.allocationInfo.size != sizeof(pod::Camera::Viewports) ) continue; - if ( buffer.buffer == storage.buffers.camera.buffer ) continue; - buffer.update( (const void*) &viewport, sizeof(pod::Camera::Viewports) ); - return; - } -*/ #endif } void uf::graph::destroy( bool soft ) { @@ -2115,20 +1632,23 @@ void uf::graph::destroy( pod::Graph::Storage& storage, bool soft ) { uf::renderer::states::rebuild = true; } + void uf::graph::reload( pod::Graph& graph, pod::Node& node ) { if ( !(0 <= node.mesh && node.mesh < graph.meshes.size()) ) return; if ( !node.entity ) return; auto& scene = uf::scene::getCurrentScene(); - auto& storage = uf::graph::globalStorage ? uf::graph::storage : scene.getComponent(); + auto& storage = ::getGraphStorage( scene ); auto& entity = node.entity->as(); auto& metadata = entity.getComponent(); auto& metadataJson = entity.getComponent(); auto& transform = entity.getComponent>(); + + auto& graphMetadataJson = graph.metadata; - ext::json::Value tag = ext::json::find( node.name, graph.metadata["tags"] ); + ext::json::Value tag = ext::json::find( node.name, graphMetadataJson["tags"] ); pod::Vector3f controllerPosition; auto& controller = scene.getController(); @@ -2214,7 +1734,7 @@ void uf::graph::reload( pod::Graph& graph, pod::Node& node ) { #if UF_ENV_DREAMCAST && GL_QUANTIZED_SHORT mesh.convert(); #else - auto conversion = graph.metadata["decode"]["conversion"].as(); + auto conversion = graphMetadataJson["decode"]["conversion"].as(); if ( conversion != "" ) { #if UF_USE_FLOAT16 if ( conversion == "float16" ) mesh.convert(); @@ -2356,9 +1876,9 @@ void uf::graph::reload( pod::Graph& graph, pod::Node& node ) { if ( image.getPixels().empty() ) image.open(image.getFilename(), false); auto filter = uf::renderer::enums::Filter::LINEAR; - auto tag = ext::json::find( key, graph.metadata["tags"] ); + auto tag = ext::json::find( key, graphMetadataJson["tags"] ); if ( !ext::json::isObject( tag ) ) { - tag["renderer"] = graph.metadata["renderer"]; + tag["renderer"] = graphMetadataJson["renderer"]; } if ( tag["renderer"]["filter"].is() ) { const auto mode = uf::string::lowercase( tag["renderer"]["filter"].as("linear") ); @@ -2411,7 +1931,7 @@ void uf::graph::reload( pod::Graph& graph, pod::Node& node ) { #if UF_ENV_DREAMCAST && GL_QUANTIZED_SHORT mesh.convert(); #else - auto conversion = graph.metadata["decode"]["conversion"].as(); + auto conversion = graphMetadataJson["decode"]["conversion"].as(); if ( conversion != "" ) { #if UF_USE_FLOAT16 if ( conversion == "float16" ) mesh.convert(); @@ -2436,7 +1956,7 @@ void uf::graph::reload( pod::Graph& graph, pod::Node& node ) { #endif // update graphic - if ( /*(graph.metadata["renderer"]["separate"].as()) &&*/ graph.metadata["renderer"]["render"].as() ) { + if ( graphMetadataJson["renderer"]["render"].as() ) { bool exists = entity.hasComponent(); if ( exists ) { auto& graphic = entity.getComponent(); @@ -2484,112 +2004,67 @@ void uf::graph::reload( pod::Graph& graph ) { for ( auto& node : graph.nodes ) uf::graph::reload( graph, node ); // setup combined mesh if requested - // disabled for now -#if 0 - if ( !(graph.metadata["renderer"]["separate"].as()) ) { - UF_DEBUG_TIMER_MULTITRACE("Processing root graphic"); - - graph.root.mesh = graph.meshes.size(); - auto keyName = graph.name + "/" + graph.root.name; - auto& mesh = storage.meshes[graph.meshes.emplace_back(keyName)]; - - mesh.bindIndirect(); - #if UF_ENV_DREAMCAST - mesh.bind(); - #else - mesh.bind(); - #endif - - uf::stl::vector drawCommands; - - size_t counts = 0; - for ( auto& name : graph.meshes ) { - if ( name == keyName ) continue; - auto tag = ext::json::find( name, graph.metadata["tags"] ); - if ( ext::json::isObject( tag ) ) { - if ( tag["ignore"].as() ) continue; - } - - auto& m = storage.meshes.map[name]; - m.updateDescriptor(); - - mesh.insertVertices( m ); - mesh.insertIndices( m ); - mesh.insertInstances( m ); - - // mesh.insertIndirects( m ); - pod::DrawCommand* drawCommand = (pod::DrawCommand*) m.getBuffer( m.indirect ).data(); - for ( size_t i = 0; i < m.indirect.count; ++i ) drawCommands.emplace_back( drawCommand[i] ); - } - - // fix up draw command for combined mesh - { - size_t totalIndices = 0; - size_t totalVertices = 0; - for ( auto& drawCommand : drawCommands ) { - drawCommand.indexID = totalIndices; - drawCommand.vertexID = totalVertices; - - totalIndices += drawCommand.indices; - totalVertices += drawCommand.vertices; - } - - mesh.insertIndirects( drawCommands ); - } - - #if UF_ENV_DREAMCAST - { - uf::stl::vector attributesKept = ext::json::vector(graph.metadata["decode"]["attributes"]); - if ( !mesh.isInterleaved() ) { - uf::stl::vector remove; remove.reserve(mesh.vertex.attributes.size()); - - for ( size_t i = 0; i < mesh.vertex.attributes.size(); ++i ) { - auto& attribute = mesh.vertex.attributes[i]; - if ( std::find( attributesKept.begin(), attributesKept.end(), attribute.descriptor.name ) != attributesKept.end() ) continue; - remove.insert(remove.begin(), i); - UF_MSG_DEBUG("Removing mesh attribute: {}", attribute.descriptor.name); - } - for ( auto& i : remove ) { - mesh.buffers[mesh.vertex.attributes[i].buffer].clear(); - mesh.buffers[mesh.vertex.attributes[i].buffer].shrink_to_fit(); - mesh.vertex.attributes.erase(mesh.vertex.attributes.begin() + i); - } - } else { - UF_MSG_DEBUG("Attribute removal requested yet mesh is interleaved, ignoring..."); - } - } - #endif - - { - #if UF_ENV_DREAMCAST && GL_QUANTIZED_SHORT - mesh.convert(); - UF_MSG_DEBUG("Quantizing mesh to GL_QUANTIZED_SHORT"); - #else - auto conversion = graph.metadata["decode"]["conversion"].as(); - if ( conversion != "" ) { - #if UF_USE_FLOAT16 - if ( conversion == "float16" ) mesh.convert(); - else if ( conversion == "float" ) mesh.convert(); - #endif - #if UF_USE_BFLOAT16 - if ( conversion == "bfloat16" ) mesh.convert(); - else if ( conversion == "float" ) mesh.convert(); - #endif - if ( conversion == "uint16_t" ) mesh.convert(); - else if ( conversion == "float" ) mesh.convert(); - } - #endif - } - - mesh.updateDescriptor(); - - { - auto& graphic = graph.root.entity->getComponent(); - uf::graph::initializeGraphics( graph, *graph.root.entity, mesh ); - } - } -#endif + // ::combineMesh( graph ); } void uf::graph::reload() { ::newGraphAdded = true; +} + +void uf::graph::update( pod::Graph& graph ) { + return update( graph, uf::physics::time::delta ); +} +void uf::graph::update( pod::Graph& graph, float delta ) { + auto& scene = uf::scene::getCurrentScene(); + auto& storage = ::getGraphStorage( scene ); + + // rebuild + if ( ::shouldRebind ) { + for ( auto& node : graph.nodes ) { + if ( !(0 <= node.mesh && node.mesh < graph.meshes.size()) ) continue; + if ( !node.entity ) continue; + + auto& entity = node.entity->as(); + + if ( !entity.hasComponent() ) continue; + + auto& graphic = entity.getComponent(); + auto& mesh = storage.meshes.map[graph.meshes[node.mesh]]; + + ::bindBuffers( graphic, mesh ); + } + } + + // get last update time +#if UF_GRAPH_EXTENDED + if ( graph.settings.stream.enabled && graph.settings.stream.hash != 0 && uf::physics::time::current - graph.settings.stream.lastUpdate > graph.settings.stream.every ) { + graph.settings.stream.lastUpdate = uf::physics::time::current; + uf::graph::reload( graph ); + } +#endif + +#if !UF_ENV_DREAMCAST + // update instance model + uf::stl::unordered_map instanceCache; + for ( auto& name : storage.instances.keys ) { + auto& instance = storage.instances[name]; + instance.previous = instance.model; + + if ( instanceCache.count( instance.objectID ) > 0 ) { + instance.model = instanceCache[instance.objectID]; + continue; + } + + auto& entity = *storage.entities[std::to_string(instance.objectID)]; + if ( !entity.isValid() ) continue; + auto& metadata = entity.getComponent(); + if ( metadata.system.ignoreGraph ) continue; + + auto& transform = entity.getComponent>(); + instance.model = (instanceCache[instance.objectID] = uf::transform::model( transform )); + } +#endif + + uf::graph::updateAnimation( graph, delta ); +} +void uf::graph::cleanup( pod::Graph& graph ) { } \ No newline at end of file