diff --git a/bin/data/shaders/graph/baking/frag.h b/bin/data/shaders/graph/baking/frag.h index 9fa63061..585d4b52 100644 --- a/bin/data/shaders/graph/baking/frag.h +++ b/bin/data/shaders/graph/baking/frag.h @@ -126,7 +126,7 @@ void main() { const Instance instance = instances[instanceID]; const Material material = materials[instance.materialID]; - const uint mapID = instance.auxID; + const uint mapID = instance.lightmapID; // was auxID vec4 A = material.colorBase; surface.material.metallic = material.factorMetallic; diff --git a/bin/exe/default/renderer b/bin/exe/default/renderer index 53395cff..91caa7c1 100644 --- a/bin/exe/default/renderer +++ b/bin/exe/default/renderer @@ -1 +1 @@ -vulkan \ No newline at end of file +opengl \ No newline at end of file diff --git a/engine/inc/uf/engine/graph/graph.h b/engine/inc/uf/engine/graph/graph.h index 0ca15a70..8bcdfc7d 100644 --- a/engine/inc/uf/engine/graph/graph.h +++ b/engine/inc/uf/engine/graph/graph.h @@ -28,9 +28,7 @@ namespace pod { uf::stl::vector nodes; // // Render information - uf::stl::vector instances; // uf::stl::vector primitives; // - uf::stl::vector drawCommands; // uf::stl::vector meshes; // uf::stl::vector images; // @@ -49,9 +47,6 @@ namespace pod { // Animation queue uf::stl::queue sequence; - // Streaming stuff - uf::stl::unordered_map buffer_paths; // probably will go unused since cramming it all in here is pain - struct { struct { bool loop = true; @@ -84,10 +79,8 @@ namespace pod { // Local storage, used for save/load struct Storage { - uf::stl::KeyMap instances; uf::stl::KeyMap instanceAddresses; uf::stl::KeyMap> primitives; - uf::stl::KeyMap> drawCommands; uf::stl::KeyMap meshes; uf::stl::KeyMap images; @@ -141,13 +134,12 @@ namespace uf { void UF_API initializeGraphics( pod::Graph& graph, uf::Object& entity, uf::Mesh& mesh ); void UF_API process( pod::Graph& graph ); void UF_API process( pod::Graph& graph, int32_t, uf::Object& parent ); - void UF_API cleanup( pod::Graph& graph ); void UF_API reload( pod::Graph& ); void UF_API initialize( pod::Graph& graph ); void UF_API update( pod::Graph& ); void UF_API update( pod::Graph&, float ); - + void UF_API updateAnimation( pod::Graph&, float ); void UF_API updateAnimation( pod::Graph&, pod::Node& ); void UF_API override( pod::Graph& ); diff --git a/engine/inc/uf/utils/mesh/mesh.h b/engine/inc/uf/utils/mesh/mesh.h index 0b33b4a5..0dc177cb 100644 --- a/engine/inc/uf/utils/mesh/mesh.h +++ b/engine/inc/uf/utils/mesh/mesh.h @@ -59,35 +59,42 @@ namespace ext { } namespace pod { + // stores information for a draw call + // used for GPU-driven indirection + // to-do: probably repurpose auxID and materialIDs struct UF_API DrawCommand { alignas(4) uint32_t indices = 0; // triangle count alignas(4) uint32_t instances = 0; // instance count alignas(4) uint32_t indexID = 0; // starting triangle position alignas(4) int32_t vertexID = 0; // starting vertex position alignas(4) uint32_t instanceID = 0; // starting instance position - // extra data - alignas(4) uint32_t auxID = 0; // - alignas(4) uint32_t materialID = 0; // - alignas(4) uint32_t vertices = 0; // + // extra data for padding + alignas(4) uint32_t auxID = 0; // used for storing which grid this belongs to when slicing, otherwise unused + alignas(4) uint32_t materialID = 0; // unused + alignas(4) uint32_t vertices = 0; // stores vertex count, should be unused }; + // stores information about how to transform a draw call + // to-do: clean up this mess + struct UF_API Instance { + // these could easily be tied to objectID + pod::Matrix4f model; // current model matrix + pod::Matrix4f previous; // previous model matrix, used for the "motion" output - struct UF_API Instance { - pod::Matrix4f model; - pod::Matrix4f previous; + pod::Vector4f color = {1,1,1,1}; // additional color information - pod::Vector4f color = {1,1,1,1}; + alignas(4) uint32_t materialID = 0; // index for material information + alignas(4) uint32_t primitiveID = 0; // index to reference the primitive(?) + alignas(4) uint32_t meshID = 0; // unused + alignas(4) uint32_t objectID = 0; // unused - alignas(4) uint32_t materialID = 0; - alignas(4) uint32_t primitiveID = 0; - alignas(4) uint32_t meshID = 0; - alignas(4) uint32_t objectID = 0; - - alignas(4) int32_t jointID = -1; - alignas(4) int32_t lightmapID = -1; - alignas(4) uint32_t imageID = 0; - alignas(4) uint32_t auxID = 0; + alignas(4) int32_t jointID = -1; // offset for skins(?) + alignas(4) int32_t lightmapID = -1; // index for lightmap to use + alignas(4) uint32_t imageID = 0; // unused? + alignas(4) uint32_t auxID = 0; // also the lightmap ID? + // AABB for this primitive + // should be for the specific draw call itself, rather than the mesh(let) entirely struct Bounds { pod::Vector3f min = { std::numeric_limits::max(), std::numeric_limits::max(), std::numeric_limits::max() }; alignas(4) float padding1 = 0; @@ -95,6 +102,7 @@ namespace pod { alignas(4) float padding2 = 0; } bounds; + // stores "pointers" on the GPU side for buffer locations, used for RT / recalculating barycentrics struct UF_API Addresses { alignas(8) uint64_t vertex{}; alignas(8) uint64_t index{}; diff --git a/engine/src/engine/asset/asset.cpp b/engine/src/engine/asset/asset.cpp index 6de9f710..d119c214 100644 --- a/engine/src/engine/asset/asset.cpp +++ b/engine/src/engine/asset/asset.cpp @@ -263,7 +263,7 @@ uf::stl::string uf::asset::load( uf::asset::Payload& payload ) { if ( asset.metadata["debug"]["print"]["stats"].as() ) UF_MSG_INFO("{}", uf::graph::stats( asset )); if ( asset.metadata["debug"]["print"]["tree"].as() ) UF_MSG_INFO("{}", uf::graph::print( asset )); #endif - if ( !asset.metadata["debug"]["no cleanup"].as() ) uf::graph::cleanup( asset ); + //if ( !asset.metadata["debug"]["no cleanup"].as() ) uf::graph::cleanup( asset ); } break; default: { UF_MSG_ERROR("Failed to parse {}: unimplemented extension: {}", filename, extension ); diff --git a/engine/src/engine/ext/baking/behavior.cpp b/engine/src/engine/ext/baking/behavior.cpp index ce2e275b..efc233e6 100644 --- a/engine/src/engine/ext/baking/behavior.cpp +++ b/engine/src/engine/ext/baking/behavior.cpp @@ -90,7 +90,7 @@ void ext::BakingBehavior::initialize( uf::Object& self ) { for ( auto& texture : storage.shadow2Ds ) textures2D.emplace_back().aliasTexture(texture); for ( auto& texture : storage.shadowCubes ) texturesCube.emplace_back().aliasTexture(texture); - ::totalIDs = storage.instances.keys.size(); + ::totalIDs = storage.primitives.keys.size(); metadata.buffers.baked.fromBuffers( NULL, 0, uf::renderer::enums::Format::R8G8B8A8_UNORM, metadata.size.x, metadata.size.y, metadata.max.layers, 1, VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT, VK_IMAGE_LAYOUT_GENERAL ); diff --git a/engine/src/engine/ext/raytrace/behavior.cpp b/engine/src/engine/ext/raytrace/behavior.cpp index 0f703ffc..29c9d841 100644 --- a/engine/src/engine/ext/raytrace/behavior.cpp +++ b/engine/src/engine/ext/raytrace/behavior.cpp @@ -95,7 +95,12 @@ void ext::RayTraceSceneBehavior::tick( uf::Object& self ) { } static uf::stl::vector previousInstances; - uf::stl::vector instances = storage.instances.flatten(); + uf::stl::vector instances; instances.reserve(storage.primitives.map.size()); + for ( auto& key : storage.primitives.keys ) { + for ( auto& primitive : storage.primitives.map[key] ) { + instances.emplace_back( primitive.instance ); + } + } if ( instances.empty() ) return; static uf::stl::vector previousGraphics; diff --git a/engine/src/engine/graph/convert.cpp b/engine/src/engine/graph/convert.cpp index d2b351b6..780332a9 100644 --- a/engine/src/engine/graph/convert.cpp +++ b/engine/src/engine/graph/convert.cpp @@ -17,9 +17,6 @@ namespace { // grab relevant IDs size_t nodeID = graph.nodes.size(); - size_t instanceID = storage.instances.keys.size(); // graph.instances.size(); - size_t primitiveID = graph.primitives.size(); - size_t drawCommandID = graph.drawCommands.size(); size_t meshID = graph.meshes.size(); size_t objectID = storage.entities.keys.size(); @@ -55,50 +52,62 @@ namespace { } // graphic.material.textures.clear(); + // to-do: import all draw commands from indirect buffer + #if 0 if ( object.hasComponent() ) { node.mesh = meshID; - // import - auto& instance = storage.instances[graph.instances.emplace_back(keyName)]; - auto& drawCommands = storage.drawCommands[graph.drawCommands.emplace_back(keyName)]; - auto& drawCommand = drawCommands.emplace_back(); auto& primitives = storage.primitives[graph.primitives.emplace_back(keyName)]; - auto& primitive = primitives.emplace_back(); auto& mesh = (storage.meshes[graph.meshes.emplace_back(keyName)] = object.getComponent()); - - pod::Vector3f boundsMin = { std::numeric_limits::max(), std::numeric_limits::max(), std::numeric_limits::max() }; - pod::Vector3f boundsMax = { -std::numeric_limits::max(), -std::numeric_limits::max(), -std::numeric_limits::max() }; + + auto& attribute = mesh.indirect.attributes.front(); + auto& buffer = mesh.buffers[mesh.isInterleaved(mesh.indirect.interleaved) ? mesh.indirect.interleaved : attribute.buffer]; + pod::DrawCommand* drawCommands = (pod::DrawCommand*) buffer.data(); - for ( auto& attribute : mesh.vertex.attributes ) { - if ( attribute.descriptor.name != "position" ) continue; - for ( size_t i = 0; i < mesh.vertex.count; ++i ) { - auto& position = *(const pod::Vector3f*) ( attribute.pointer + attribute.stride * (mesh.vertex.first + i)); - boundsMin = uf::vector::min( boundsMin, position ); - boundsMax = uf::vector::max( boundsMax, position ); + // import + for ( auto drawCommandID = 0; drawCommandID < mesh.indirect.count; ++drawCommandID ) { + size_t primitiveID = primitives.size(); + size_t instanceID = primitiveID; + + auto& primitive = primitives.emplace_back(); + auto& drawCommand = primitive.drawCommand; + auto& instance = primitive.instance; + + pod::Vector3f boundsMin = { std::numeric_limits::max(), std::numeric_limits::max(), std::numeric_limits::max() }; + pod::Vector3f boundsMax = { -std::numeric_limits::max(), -std::numeric_limits::max(), -std::numeric_limits::max() }; + + for ( auto& attribute : mesh.vertex.attributes ) { + if ( attribute.descriptor.name != "position" ) continue; + for ( size_t i = 0; i < mesh.vertex.count; ++i ) { + auto& position = *(const pod::Vector3f*) ( attribute.pointer + attribute.stride * (mesh.vertex.first + i)); + boundsMin = uf::vector::min( boundsMin, position ); + boundsMax = uf::vector::max( boundsMax, position ); + } } + + instance.materialID = materialID; + instance.primitiveID = primitiveID; + instance.meshID = meshID; + instance.objectID = objectID; + instance.bounds.min = boundsMin; + instance.bounds.max = boundsMax; + + drawCommand.indices = mesh.index.count; + drawCommand.instances = 1; + drawCommand.indexID = 0; + drawCommand.vertexID = 0; + drawCommand.instanceID = instanceID; + drawCommand.vertices = mesh.vertex.count; + + primitive.instance = instance; + primitive.drawCommand = drawCommand; } - instance.materialID = materialID; - instance.primitiveID = primitiveID; - instance.meshID = meshID; - instance.objectID = objectID; - instance.bounds.min = boundsMin; - instance.bounds.max = boundsMax; - - drawCommand.indices = mesh.index.count; - drawCommand.instances = 1; - drawCommand.indexID = 0; - drawCommand.vertexID = 0; - drawCommand.instanceID = instanceID; - drawCommand.vertices = mesh.vertex.count; - - primitive.instance = instance; - primitive.drawCommand = drawCommand; - - mesh.insertIndirects(drawCommands); + //mesh.insertIndirects(drawCommands); // to-do mesh.updateDescriptor(); uf::graph::initializeGraphics( graph, object, mesh ); } + #endif } @@ -137,19 +146,7 @@ pod::Graph& uf::graph::convert( uf::Object& object, bool process ) { 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 for ( auto& name : graph.materials ) { @@ -161,86 +158,52 @@ pod::Graph& uf::graph::convert( uf::Object& object, bool process ) { 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 - for ( auto& name : graph.instances ) { - auto& instance = storage.instances[name]; - - if ( 0 <= instance.materialID && instance.materialID < graph.materials.size() ) { - auto& keys = /*graph.storage*/storage.materials.keys; - auto& indices = /*graph.storage*/storage.materials.indices; + for ( auto& name : graph.primitives ) { + auto& primitives = storage.primitives[name]; + for ( auto& primitive : primitives ) { + auto& instance = primitive.instance; - if ( !(0 <= instance.materialID && instance.materialID < graph.materials.size()) ) continue; + if ( 0 <= instance.materialID && instance.materialID < graph.materials.size() ) { + 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; + auto& needle = graph.materials[instance.materialID]; + instance.materialID = indices[needle]; } - #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; + if ( 0 <= instance.lightmapID && instance.lightmapID < graph.textures.size() ) { + auto& keys = storage.textures.keys; + auto& indices = storage.textures.indices; - if ( !(0 <= instance.lightmapID && instance.lightmapID < graph.textures.size()) ) continue; + 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; + auto& needle = graph.textures[instance.lightmapID]; + instance.lightmapID = indices[needle]; } - #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 + // 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 it = std::find( keys.begin(), keys.end(), graph.images[instance.imageID] ); - UF_ASSERT( it != keys.end() ); - instance.imageID = it - keys.begin(); - } - #endif - // remap a skinID as an actual jointID - if ( 0 <= instance.jointID && instance.jointID < graph.skins.size() ) { - auto& name = graph.skins[instance.jointID]; - instance.jointID = 0; - for ( auto key : storage.joints.keys ) { - if ( key == name ) break; - auto& joints = storage.joints[key]; - instance.jointID += joints.size(); + if ( 0 <= instance.imageID && instance.imageID < graph.images.size() ) { + 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(); + } + #endif + // remap a skinID as an actual jointID + if ( 0 <= instance.jointID && instance.jointID < graph.skins.size() ) { + auto& name = graph.skins[instance.jointID]; + instance.jointID = 0; + for ( auto key : storage.joints.keys ) { + if ( key == name ) break; + auto& joints = storage.joints[key]; + instance.jointID += joints.size(); + } } } } diff --git a/engine/src/engine/graph/decode.cpp b/engine/src/engine/graph/decode.cpp index 4580630f..f94832e4 100644 --- a/engine/src/engine/graph/decode.cpp +++ b/engine/src/engine/graph/decode.cpp @@ -420,21 +420,6 @@ void uf::graph::load( pod::Graph& graph, const uf::stl::string& filename, const key += ":"; } - tasks.queue([&]{ - // load images - UF_DEBUG_TIMER_MULTITRACE("Reading instances..."); - graph.instances.reserve( serializer["instances"].size() ); - ext::json::forEach( serializer["instances"], [&]( ext::json::Value& value ){ - auto name = key + value["name"].as(); - // UF_MSG_DEBUG("{}", name); - /*graph.storage*/storage.instances[name] = decodeInstance( value, graph ); - graph.instances.emplace_back(name); - }); - UF_DEBUG_TIMER_MULTITRACE("Read instances"); - #if UF_ENV_DREAMCAST - DC_STATS(); - #endif - }); tasks.queue([&]{ // load images UF_DEBUG_TIMER_MULTITRACE("Reading primitives..."); @@ -450,21 +435,6 @@ void uf::graph::load( pod::Graph& graph, const uf::stl::string& filename, const DC_STATS(); #endif }); - tasks.queue([&]{ - // load images - UF_DEBUG_TIMER_MULTITRACE("Reading drawCommands..."); - graph.drawCommands.reserve( serializer["drawCommands"].size() ); - ext::json::forEach( serializer["drawCommands"], [&]( ext::json::Value& value ){ - auto name = key + value["name"].as(); - // UF_MSG_DEBUG("{}", name); - /*graph.storage*/storage.drawCommands[name] = decodeDrawCommands( value, graph ); - graph.drawCommands.emplace_back(name); - }); - UF_DEBUG_TIMER_MULTITRACE("Read drawCommands"); - #if UF_ENV_DREAMCAST - DC_STATS(); - #endif - }); tasks.queue([&]{ // load mesh information UF_DEBUG_TIMER_MULTITRACE("Reading meshes..."); diff --git a/engine/src/engine/graph/encode.cpp b/engine/src/engine/graph/encode.cpp index 259640a8..c199ac62 100644 --- a/engine/src/engine/graph/encode.cpp +++ b/engine/src/engine/graph/encode.cpp @@ -290,16 +290,6 @@ uf::stl::string uf::graph::save( const pod::Graph& graph, const uf::stl::string& auto& scene = uf::scene::getCurrentScene(); auto& storage = uf::graph::globalStorage ? uf::graph::storage : scene.getComponent(); - tasks.queue([&]{ - ext::json::reserve( serializer["instances"], graph.instances.size() ); - for ( size_t i = 0; i < graph.instances.size(); ++i ) { - auto& name = graph.instances[i]; - auto& instance = /*graph.storage*/storage.instances.map.at(name); - uf::Serializer json = encode( instance, settings, graph ); - json["name"] = name; - serializer["instances"].emplace_back( json ); - } - }); tasks.queue([&]{ ext::json::reserve( serializer["primitives"], graph.primitives.size() ); for ( size_t i = 0; i < graph.primitives.size(); ++i ) { @@ -313,19 +303,6 @@ uf::stl::string uf::graph::save( const pod::Graph& graph, const uf::stl::string& } } }); - tasks.queue([&]{ - ext::json::reserve( serializer["drawCommands"], graph.drawCommands.size() ); - for ( size_t i = 0; i < graph.drawCommands.size(); ++i ) { - auto& name = graph.drawCommands[i]; - auto& drawCommands = /*graph.storage*/storage.drawCommands.map.at(name); - auto& json = serializer["drawCommands"].emplace_back(); - json["name"] = name; - // ext::json::reserve( json["drawCommands"], drawCommands.size() ); - for ( auto& drawCommand : drawCommands ) { - json["drawCommands"].emplace_back( encode( drawCommand, settings, graph ) ); - } - } - }); tasks.queue([&]{ // store mesh information ext::json::reserve( serializer["meshes"], graph.meshes.size() ); diff --git a/engine/src/engine/graph/graph.cpp b/engine/src/engine/graph/graph.cpp index 0c0dc3de..68fdd073 100644 --- a/engine/src/engine/graph/graph.cpp +++ b/engine/src/engine/graph/graph.cpp @@ -26,7 +26,7 @@ #if UF_USE_OPENGL #define UF_GRAPH_SPARSE_READ_MESH 1 #else - #define UF_GRAPH_SPARSE_READ_MESH 1 + #define UF_GRAPH_SPARSE_READ_MESH 0 #endif #define UF_GRAPH_EXTENDED 1 @@ -520,13 +520,9 @@ namespace { for ( size_t drawID = 0; drawID < mesh.indirect.count; ++drawID ) { auto& drawCommand = drawCommands[drawID]; auto instanceID = drawCommand.instanceID; - auto instanceKeyName = std::to_string(instanceID); + auto instanceKeyName = std::to_string(instanceID); // it *should* be fine as the instance IDs are already objective to the scene graph - if ( storage.instanceAddresses.map.count(instanceKeyName) > 0 ) { - // UF_MSG_ERROR("DUPLICATE INSTANCE ID: {}", instanceKeyName); - } - - auto& instanceAddresses = storage.instanceAddresses.map[instanceKeyName]; + auto& instanceAddresses = storage.instanceAddresses[instanceKeyName]; if ( mesh.vertex.count ) { if ( mesh.isInterleaved( mesh.vertex ) ) { instanceAddresses.vertex = graphic.buffers.at(graphic.descriptor.inputs.vertex.interleaved).getAddress(); @@ -556,6 +552,7 @@ namespace { instanceAddresses.drawID = drawID; } + } } #endif @@ -780,12 +777,6 @@ void uf::graph::process( pod::Graph& graph ) { uf::stl::unordered_map lightmapIDs; uint32_t lightmapCount = 0; - for ( auto& name : graph.instances ) { - auto& instance = storage.instances[name]; - filenames[instance.auxID] = uf::string::replace(UF_GRAPH_DEFAULT_LIGHTMAP, "%i", std::to_string(instance.auxID)); - - lightmapCount = std::max( lightmapCount, instance.auxID + 1 ); - } for ( auto& name : graph.primitives ) { auto& primitives = storage.primitives[name]; for ( auto& primitive : primitives ) { @@ -859,11 +850,6 @@ void uf::graph::process( pod::Graph& graph ) { } } - for ( auto& name : graph.instances ) { - auto& instance = storage.instances[name]; - if ( lightmapIDs.count( instance.auxID ) == 0 ) continue; - instance.lightmapID = lightmapIDs[instance.auxID]; - } for ( auto& name : graph.primitives ) { auto& primitives = storage.primitives[name]; for ( auto& primitive : primitives ) { @@ -1038,80 +1024,60 @@ void uf::graph::process( pod::Graph& graph ) { } } // remap instance variables - UF_DEBUG_TIMER_MULTITRACE("Remapping instances"); - for ( auto& name : graph.instances ) { - auto& instance = storage.instances[name]; - - if ( 0 <= instance.materialID && instance.materialID < graph.materials.size() ) { - auto& keys = storage.materials.keys; - auto& indices = storage.materials.indices; - - if ( !(0 <= instance.materialID && instance.materialID < graph.materials.size()) ) continue; + UF_DEBUG_TIMER_MULTITRACE("Remapping primitive"); + for ( auto& name : graph.primitives ) { + auto& primitives = storage.primitives[name]; + for ( auto& primitive : primitives ) { + auto& drawCommand = primitive.drawCommand; + auto& instance = primitive.instance; - auto& needle = graph.materials[instance.materialID]; - instance.materialID = indices[needle]; - } - if ( 0 <= instance.lightmapID && instance.lightmapID < graph.textures.size() ) { - 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]; - instance.lightmapID = indices[needle]; - } - #if 0 - // i genuinely dont remember what this is used for - - if ( 0 <= instance.imageID && instance.imageID < graph.images.size() ) { - 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(); - } - #endif - // remap a skinID as an actual jointID - if ( 0 <= instance.jointID && instance.jointID < graph.skins.size() ) { - auto& name = graph.skins[instance.jointID]; - instance.jointID = 0; - for ( auto key : storage.joints.keys ) { - if ( key == name ) break; - auto& joints = storage.joints[key]; - instance.jointID += joints.size(); - } - } - } - - // 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 = storage.instances.keys; - auto& indices = storage.instances.indices; - - if ( !(0 <= drawCommand.instanceID && drawCommand.instanceID < graph.instances.size()) ) continue; - - auto& needle = graph.instances[drawCommand.instanceID]; - #if 1 - drawCommand.instanceID = indices[needle]; - #elif 1 - for ( size_t i = 0; i < keys.size(); ++i ) { - if ( keys[i] != needle ) continue; - drawCommand.instanceID = i; - break; + // remap instance ID + if ( 0 <= drawCommand.instanceID && drawCommand.instanceID < graph.primitives.size() ) { + for ( auto key : storage.primitives.keys ) { + if ( key == name ) break; + drawCommand.instanceID += storage.primitives[key].size(); } - #else - auto it = std::find( keys.begin(), keys.end(), needle ); + } + + if ( 0 <= instance.materialID && instance.materialID < graph.materials.size() ) { + 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]; + instance.materialID = indices[needle]; + } + if ( 0 <= instance.lightmapID && instance.lightmapID < graph.textures.size() ) { + 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]; + instance.lightmapID = indices[needle]; + } + #if 0 + // i genuinely dont remember what this is used for + + if ( 0 <= instance.imageID && instance.imageID < graph.images.size() ) { + auto& keys = storage.images.keys; + auto it = std::find( keys.begin(), keys.end(), graph.images[instance.imageID] ); UF_ASSERT( it != keys.end() ); - drawCommand.instanceID = it - keys.begin(); - #endif + instance.imageID = it - keys.begin(); + } + #endif + // remap a skinID as an actual jointID + if ( 0 <= instance.jointID && instance.jointID < graph.skins.size() ) { + auto& name = graph.skins[instance.jointID]; + instance.jointID = 0; + for ( auto key : storage.joints.keys ) { + if ( key == name ) break; + instance.jointID += storage.joints[key].size(); + } } } } -#endif if ( graphMetadataJson["debug"]["print"]["lights"].as() ) { UF_MSG_DEBUG("Lights: {}", graph.lights.size()); @@ -1126,16 +1092,21 @@ void uf::graph::process( pod::Graph& graph ) { } } - if ( graphMetadataJson["debug"]["print"]["instances"].as() ) { - UF_MSG_DEBUG("Instances: {}", graph.instances.size()); - for ( auto& name : graph.instances ) { - auto& instance = storage.instances[name]; - UF_MSG_DEBUG("\tInstance: {} | {} | {}", name, - instance.materialID, - instance.lightmapID - ); + #if 0 + if ( graphMetadataJson["debug"]["print"]["primitives"].as() ) { + UF_MSG_DEBUG("Instances: {}", graph.primitives.size()); + for ( auto& name : graph.primitives ) { + auto& primitive = storage.primitives[name]; + for ( auto& primitive : primitives ) { + auto& instance = primitive.instance; + UF_MSG_DEBUG("\tInstance: {} | {} | {}", name, + instance.materialID, + instance.lightmapID + ); + } } } + #endif if ( graphMetadataJson["debug"]["print"]["materials"].as() ) { UF_MSG_DEBUG("Materials: {}", graph.materials.size()); for ( auto& name : graph.materials ) { @@ -1163,7 +1134,6 @@ void uf::graph::process( pod::Graph& graph ) { #endif uf::graph::reload(); - storage.instanceAddresses.keys = storage.instances.keys; UF_DEBUG_TIMER_MULTITRACE_END("Processed graph."); } void uf::graph::process( pod::Graph& graph, int32_t index, uf::Object& parent ) { @@ -1313,22 +1283,25 @@ void uf::graph::process( pod::Graph& graph, int32_t index, uf::Object& parent ) storage.entities[objectKeyName] = &entity; // -#if !UF_GRAPH_EXTENDED if ( 0 <= node.mesh && node.mesh < graph.meshes.size() ) { auto model = uf::transform::model( transform ); auto& mesh = storage.meshes.map[graph.meshes[node.mesh]]; auto& primitives = storage.primitives.map[graph.primitives[node.mesh]]; pod::Instance::Bounds bounds; + pod::DrawCommand* drawCommands = NULL; + if ( mesh.indirect.count && mesh.indirect.count <= primitives.size() ) { + auto& attribute = mesh.indirect.attributes.front(); + auto& buffer = mesh.buffers[mesh.isInterleaved(mesh.indirect.interleaved) ? mesh.indirect.interleaved : attribute.buffer]; + drawCommands = (pod::DrawCommand*) buffer.data(); + } // setup instances for ( auto i = 0; i < primitives.size(); ++i ) { auto& primitive = primitives[i]; + auto& instance = primitive.instance; + auto& drawCommand = primitive.drawCommand; - size_t instanceID = storage.instances.keys.size(); - auto instanceKeyName = graph.instances.emplace_back(std::to_string(instanceID)); - - auto& instance = storage.instances[instanceKeyName]; - instance = primitive.instance; + auto instanceID = i; instance.model = model; instance.previous = model; @@ -1338,82 +1311,14 @@ void uf::graph::process( pod::Graph& graph, int32_t index, uf::Object& parent ) bounds.min = uf::vector::min( bounds.min, instance.bounds.min ); bounds.max = uf::vector::max( bounds.max, instance.bounds.max ); - if ( mesh.indirect.count && mesh.indirect.count <= primitives.size() ) { - auto& attribute = mesh.indirect.attributes.front(); - auto& buffer = mesh.buffers[mesh.isInterleaved(mesh.indirect.interleaved) ? mesh.indirect.interleaved : attribute.buffer]; - pod::DrawCommand* drawCommands = (pod::DrawCommand*) buffer.data(); - auto& drawCommand = drawCommands[i]; - drawCommand.instanceID = instanceID; - } + drawCommand.instanceID = instanceID; + if ( drawCommands ) drawCommands[i].instanceID = instanceID; } + #if !UF_GRAPH_EXTENDED if ( graphMetadataJson["renderer"]["render"].as() ) { uf::graph::initializeGraphics( graph, entity, mesh ); } - - { - auto phyziks = tag["physics"]; - if ( !ext::json::isObject( phyziks ) ) phyziks = metadataJson["physics"]; - else metadataJson["physics"] = phyziks; - - if ( ext::json::isObject( phyziks ) ) { - uf::stl::string type = phyziks["type"].as(); - - if ( type == "mesh" ) { - auto& collider = entity.getComponent(); - collider.stats.mass = phyziks["mass"].as(collider.stats.mass); - collider.stats.friction = phyziks["friction"].as(collider.stats.friction); - collider.stats.restitution = phyziks["restitution"].as(collider.stats.restitution); - collider.stats.inertia = uf::vector::decode( phyziks["inertia"], collider.stats.inertia ); - collider.stats.gravity = uf::vector::decode( phyziks["gravity"], collider.stats.gravity ); - - uf::physics::impl::create( entity.as(), mesh, !phyziks["static"].as(true) ); - } else { - auto min = uf::matrix::multiply( model, bounds.min, 1.0f ); - auto max = uf::matrix::multiply( model, bounds.max, 1.0f ); - - pod::Vector3f center = (max + min) * 0.5f; - pod::Vector3f corner = uf::vector::abs(max - min) * 0.5f; - - metadataJson["physics"]["center"] = uf::vector::encode( center ); - metadataJson["physics"]["corner"] = uf::vector::encode( corner ); - } - } - } - } -#else - if ( 0 <= node.mesh && node.mesh < graph.meshes.size() ) { - auto model = uf::transform::model( transform ); - auto& mesh = storage.meshes.map[graph.meshes[node.mesh]]; - auto& primitives = storage.primitives.map[graph.primitives[node.mesh]]; - - pod::Instance::Bounds bounds; - // setup instances - for ( auto i = 0; i < primitives.size(); ++i ) { - auto& primitive = primitives[i]; - - size_t instanceID = storage.instances.keys.size(); - auto instanceKeyName = graph.instances.emplace_back(std::to_string(instanceID)); - - auto& instance = storage.instances[instanceKeyName]; - instance = primitive.instance; - - instance.model = model; - instance.previous = model; - instance.objectID = objectID; - 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 ); - - primitive.drawCommand.instanceID = instanceID; - if ( mesh.indirect.count && mesh.indirect.count <= primitives.size() ) { - auto& attribute = mesh.indirect.attributes.front(); - auto& buffer = mesh.buffers[mesh.isInterleaved(mesh.indirect.interleaved) ? mesh.indirect.interleaved : attribute.buffer]; - pod::DrawCommand* drawCommands = (pod::DrawCommand*) buffer.data(); - auto& drawCommand = drawCommands[i]; - drawCommand.instanceID = instanceID; - } - } + #endif { auto phyziks = tag["physics"]; @@ -1433,10 +1338,21 @@ void uf::graph::process( pod::Graph& graph, int32_t index, uf::Object& parent ) metadataJson["physics"]["center"] = uf::vector::encode( center ); metadataJson["physics"]["corner"] = uf::vector::encode( corner ); } + #if UF_GRAPH_EXTENDED + if ( type == "mesh" ) { + auto& collider = entity.getComponent(); + collider.stats.mass = phyziks["mass"].as(collider.stats.mass); + collider.stats.friction = phyziks["friction"].as(collider.stats.friction); + collider.stats.restitution = phyziks["restitution"].as(collider.stats.restitution); + collider.stats.inertia = uf::vector::decode( phyziks["inertia"], collider.stats.inertia ); + collider.stats.gravity = uf::vector::decode( phyziks["gravity"], collider.stats.gravity ); + + uf::physics::impl::create( entity.as(), mesh, !phyziks["static"].as(true) ); + } + #endif } } } -#endif for ( auto index : node.children ) uf::graph::process( graph, index, entity ); } @@ -1488,17 +1404,22 @@ void uf::graph::tick( uf::Object& object ) { } bool uf::graph::tick( pod::Graph::Storage& storage ) { bool rebuild = false; - uf::stl::vector instances = storage.instances.flatten(); + uf::stl::vector instances; instances.reserve(storage.primitives.map.size()); uf::stl::vector instanceAddresses = storage.instanceAddresses.flatten(); uf::stl::vector joints; joints.reserve(storage.joints.map.size()); + for ( auto& key : storage.primitives.keys ) { + for ( auto& primitive : storage.primitives.map[key] ) { + instances.emplace_back( primitive.instance ); + } + } 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 ); } - + 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; @@ -1506,9 +1427,13 @@ bool uf::graph::tick( pod::Graph::Storage& storage ) { if ( ::newGraphAdded ) { uf::stl::vector materials = storage.materials.flatten(); uf::stl::vector textures = storage.textures.flatten(); - uf::stl::vector drawCommands; drawCommands.reserve(storage.drawCommands.map.size()); + uf::stl::vector drawCommands; drawCommands.reserve(storage.primitives.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.primitives.keys ) { + for ( auto& primitive : storage.primitives.map[key] ) { + drawCommands.emplace_back( primitive.drawCommand ); + } + } 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; @@ -1600,10 +1525,8 @@ void uf::graph::destroy( pod::Graph::Storage& storage, bool soft ) { for ( auto pair : storage.meshes.map ) pair.second.destroy(); // cleanup storage cache - storage.instances.clear(); storage.instanceAddresses.clear(); storage.primitives.clear(); - storage.drawCommands.clear(); storage.meshes.clear(); storage.images.clear(); storage.materials.clear(); @@ -2044,27 +1967,29 @@ void uf::graph::update( pod::Graph& graph, float delta ) { #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; + for ( auto& name : graph.primitives ) { + auto& primitives = storage.primitives[name]; + for ( auto& primitive : primitives ) { + // auto& drawCommand = primitive.drawCommand; + auto& instance = primitive.instance; + instance.previous = instance.model; + if ( instanceCache.count( instance.objectID ) > 0 ) { + instance.model = instanceCache[instance.objectID]; + continue; + } - 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 )); } - - 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 diff --git a/engine/src/ext/gltf/gltf.cpp b/engine/src/ext/gltf/gltf.cpp index f34bed0b..7e551a4b 100644 --- a/engine/src/ext/gltf/gltf.cpp +++ b/engine/src/ext/gltf/gltf.cpp @@ -156,12 +156,12 @@ void ext::gltf::load( pod::Graph& graph, const uf::stl::string& filename, const // load images { graph.images.reserve(model.images.size()); - /*graph.storage*/storage.images.reserve(model.images.size()); + storage.images.reserve(model.images.size()); for ( auto& i : model.images ) { auto imageID = graph.images.size(); auto keyName = graph.images.emplace_back(key + i.name); - auto& image = /*graph.storage*/storage.images[keyName]; + auto& image = storage.images[keyName]; if ( graph.metadata["debug"]["print"]["images"].as() ) { UF_MSG_DEBUG("Image: {}", i.name ); } @@ -171,11 +171,11 @@ void ext::gltf::load( pod::Graph& graph, const uf::stl::string& filename, const } // load samplers { - /*graph.storage*/storage.samplers.reserve(model.samplers.size()); + storage.samplers.reserve(model.samplers.size()); for ( auto& s : model.samplers ) { auto samplerID = graph.samplers.size(); auto keyName = graph.samplers.emplace_back(key + s.name); - auto& sampler = /*graph.storage*/storage.samplers[keyName]; + auto& sampler = storage.samplers[keyName]; if ( graph.metadata["debug"]["print"]["samplers"].as() ) { UF_MSG_DEBUG("Sampler: {}", s.name ); } @@ -190,12 +190,12 @@ void ext::gltf::load( pod::Graph& graph, const uf::stl::string& filename, const // load textures { graph.textures.reserve(model.textures.size()); - /*graph.storage*/storage.textures.reserve(model.textures.size()); + storage.textures.reserve(model.textures.size()); for ( auto& t : model.textures ) { auto textureID = graph.textures.size(); auto keyName = graph.textures.emplace_back((t.name == "" ? graph.images[t.source] : (key + t.name))); - auto& texture = /*graph.storage*/storage.textures[keyName]; + auto& texture = storage.textures[keyName]; if ( graph.metadata["debug"]["print"]["textures"].as() ) { UF_MSG_DEBUG("Texture: {}", t.name ); } @@ -207,12 +207,12 @@ void ext::gltf::load( pod::Graph& graph, const uf::stl::string& filename, const // load materials { graph.materials.reserve(model.materials.size()); - /*graph.storage*/storage.materials.reserve(model.materials.size()); + storage.materials.reserve(model.materials.size()); for ( auto& m : model.materials ) { auto materialID = graph.materials.size(); auto keyName = graph.materials.emplace_back(key + m.name); - auto& material = /*graph.storage*/storage.materials[keyName]; + auto& material = storage.materials[keyName]; if ( graph.metadata["debug"]["print"]["materials"].as() ) { UF_MSG_DEBUG("Material: {}", m.name ); } @@ -255,7 +255,7 @@ void ext::gltf::load( pod::Graph& graph, const uf::stl::string& filename, const { size_t masterAuxID = 0; graph.meshes.reserve(model.meshes.size()); - /*graph.storage*/storage.meshes.reserve(model.meshes.size()); + storage.meshes.reserve(model.meshes.size()); for ( auto& m : model.meshes ) { auto meshID = graph.meshes.size(); @@ -265,11 +265,9 @@ void ext::gltf::load( pod::Graph& graph, const uf::stl::string& filename, const } graph.primitives.emplace_back(keyName); - graph.drawCommands.emplace_back(keyName); - auto& drawCommands = /*graph.storage*/storage.drawCommands[keyName]; - auto& primitives = /*graph.storage*/storage.primitives[keyName]; - auto& mesh = /*graph.storage*/storage.meshes[keyName]; + auto& primitives = storage.primitives[keyName]; + auto& mesh = storage.meshes[keyName]; struct { uf::meshgrid::Grid grid; @@ -349,12 +347,12 @@ void ext::gltf::load( pod::Graph& graph, const uf::stl::string& filename, const // load skins { graph.skins.reserve( model.skins.size() ); - /*graph.storage*/storage.skins.reserve( model.skins.size() ); + storage.skins.reserve( model.skins.size() ); for ( auto& s : model.skins ) { auto skinID = graph.skins.size(); auto keyName = graph.skins.emplace_back(key + s.name); - auto& skin = /*graph.storage*/storage.skins[keyName]; + auto& skin = storage.skins[keyName]; if ( graph.metadata["debug"]["print"]["skins"].as() ) { UF_MSG_DEBUG("Skin: {}", s.name ); } @@ -386,12 +384,12 @@ void ext::gltf::load( pod::Graph& graph, const uf::stl::string& filename, const // load animations { graph.animations.reserve( model.animations.size() ); - /*graph.storage*/storage.animations.reserve( model.animations.size() ); + storage.animations.reserve( model.animations.size() ); for ( auto& a : model.animations ) { auto animationID = graph.animations.size(); auto keyName = graph.animations.emplace_back(key + a.name); - auto& animation = /*graph.storage*/storage.animations[keyName]; + auto& animation = storage.animations[keyName]; if ( graph.metadata["debug"]["print"]["animations"].as() ) { UF_MSG_DEBUG("Animation: {}", a.name ); } @@ -477,17 +475,17 @@ void ext::gltf::load( pod::Graph& graph, const uf::stl::string& filename, const // generate atlas if ( graph.metadata["renderer"]["atlas"].as() ) { auto atlasName = filename + "/" + "atlas"; - auto& atlas = /*graph.storage*/storage.atlases[atlasName]; + auto& atlas = storage.atlases[atlasName]; auto atlasImageIndex = graph.images.size(); auto atlasTextureIndex = graph.textures.size(); - for ( auto& keyName : graph.images ) atlas.addImage( /*graph.storage*/storage.images[keyName] ); + for ( auto& keyName : graph.images ) atlas.addImage( storage.images[keyName] ); atlas.generate(); for ( auto& keyName : graph.images ) { - auto& texture = /*graph.storage*/storage.textures[keyName]; + auto& texture = storage.textures[keyName]; if ( texture.index < 0 ) continue; - auto& image = /*graph.storage*/storage.images[keyName]; + auto& image = storage.images[keyName]; const auto& hash = image.getHash(); auto min = atlas.mapUv( {0, 0}, hash ); @@ -499,11 +497,11 @@ void ext::gltf::load( pod::Graph& graph, const uf::stl::string& filename, const { graph.images.emplace_back(atlasName); - auto& image = /*graph.storage*/storage.images[atlasName]; + auto& image = storage.images[atlasName]; image = atlas.getAtlas(); graph.textures.emplace_back(atlasName); - auto& texture = /*graph.storage*/storage.textures[atlasName]; + auto& texture = storage.textures[atlasName]; texture.index = atlasImageIndex; } } @@ -543,7 +541,7 @@ void ext::gltf::load( pod::Graph& graph, const uf::stl::string& filename, const if ( !should ) continue; } - auto& mesh = /*graph.storage*/storage.meshes[keyName]; + auto& mesh = storage.meshes[keyName]; UF_MSG_DEBUG("Optimizing mesh at level {}: {}", level, keyName); if ( !ext::meshopt::optimize( mesh, simplify, level, print ) ) { UF_MSG_ERROR("Mesh optimization failed: {}", keyName ); diff --git a/engine/src/ext/gltf/processPrimitives.inl b/engine/src/ext/gltf/processPrimitives.inl index 1cded6d5..fcc59c18 100644 --- a/engine/src/ext/gltf/processPrimitives.inl +++ b/engine/src/ext/gltf/processPrimitives.inl @@ -378,13 +378,18 @@ if ( meshopt.should ) { mesh.bindIndirect(); mesh.bind(false); // default to de-interleaved regardless of requirement (makes things easier) + uf::stl::vector drawCommands; + drawCommands.reserve( meshlets.size() ); + primitives.reserve( meshlets.size() ); + for ( auto& meshlet : meshlets ) { + // to-do: check against the meshlet's actual primitive information auto& drawCommand = drawCommands.emplace_back(pod::DrawCommand{ .indices = meshlet.indices.size(), .instances = 1, .indexID = indexID, .vertexID = vertexID, - .instanceID = 0, + .instanceID = meshlet.primitive.drawCommand.instanceID, .auxID = meshlet.primitive.drawCommand.auxID, .materialID = meshlet.primitive.drawCommand.materialID, .vertices = meshlet.vertices.size(),