diff --git a/bin/settings.toml b/bin/settings.toml index 6780ef5f..5d90029a 100644 --- a/bin/settings.toml +++ b/bin/settings.toml @@ -56,12 +56,12 @@ holdable = true [vehicle_thr_max] primary = "Z" secondary = "None" -holdable = false +holdable = true [vehicle_thr_min] primary = "X" secondary = "None" -holdable = false +holdable = true [vehicle_thr_more] primary = "LShift" diff --git a/src/adera/activescene/VehicleBuilder.cpp b/src/adera/activescene/VehicleBuilder.cpp index 62e6bbbe..9b927015 100644 --- a/src/adera/activescene/VehicleBuilder.cpp +++ b/src/adera/activescene/VehicleBuilder.cpp @@ -116,26 +116,26 @@ osp::link::MachAnyId VehicleBuilder::create_machine(PartId const part, std::initializer_list const& connections) { auto &rData = m_data.value(); - osp::link::PerMachType &rPerMachType = rData.m_machines.m_perType[machType]; + osp::link::PerMachType &rPerMachType = rData.m_machines.perType[machType]; - MachTypeId const mach = rData.m_machines.m_ids.create(); + MachTypeId const mach = rData.m_machines.ids.create(); - std::size_t const capacity = rData.m_machines.m_ids.capacity(); - rData.m_machines.m_machTypes.resize(capacity); - rData.m_machines.m_machToLocal.resize(capacity); + std::size_t const capacity = rData.m_machines.ids.capacity(); + rData.m_machines.machTypes.resize(capacity); + rData.m_machines.machToLocal.resize(capacity); rData.m_machToPart.resize(capacity); for (PerNodeType &rPerNodeType : rData.m_nodePerType) { - rPerNodeType.m_machToNode.ids_reserve(capacity); + rPerNodeType.machToNode.ids_reserve(capacity); rPerNodeType.m_machToNodeCustom.ids_reserve(capacity); } - MachTypeId const local = rPerMachType.m_localIds.create(); - rPerMachType.m_localToAny.resize(rPerMachType.m_localIds.capacity()); + MachTypeId const local = rPerMachType.localIds.create(); + rPerMachType.localToAny.resize(rPerMachType.localIds.capacity()); - rData.m_machines.m_machTypes[mach] = machType; - rData.m_machines.m_machToLocal[mach] = local; - rPerMachType.m_localToAny[local] = mach; + rData.m_machines.machTypes[mach] = machType; + rData.m_machines.machToLocal[mach] = local; + rPerMachType.localToAny[local] = mach; m_partMachCount[std::size_t(part)] ++; rData.m_machToPart[mach] = part; @@ -152,8 +152,8 @@ void VehicleBuilder::connect(MachAnyId const mach, std::initializer_list nodePortMax(rData.m_nodePerType.size(), 0); for (Connection const& connect : connections) { - int &rPortMax = nodePortMax[connect.m_port.m_type]; - rPortMax = std::max(rPortMax, connect.m_port.m_port + 1); + int &rPortMax = nodePortMax[connect.m_port.type]; + rPortMax = std::max(rPortMax, connect.m_port.port + 1); } for (NodeTypeId nodeType = 0; std::size_t(nodeType) < rData.m_nodePerType.size(); ++nodeType) @@ -163,12 +163,12 @@ void VehicleBuilder::connect(MachAnyId const mach, std::initializer_list const portSpan = rPerNodeType.m_machToNode[mach]; + rPerNodeType.machToNode.emplace(mach, portMax); + lgrn::Span const portSpan = rPerNodeType.machToNode[mach]; std::fill(std::begin(portSpan), std::end(portSpan), lgrn::id_null()); rPerNodeType.m_machToNodeCustom.emplace(mach, portMax); lgrn::Span const customSpan = rPerNodeType.m_machToNodeCustom[mach]; @@ -176,10 +176,10 @@ void VehicleBuilder::connect(MachAnyId const mach, std::initializer_list junction = rPerNodeType.m_nodeToMach[node]; + rPerNodeType.nodeToMach.emplace(node, rPerNodeType.m_nodeConnectCount[node]); + lgrn::Span junction = rPerNodeType.nodeToMach[node]; std::fill(std::begin(junction), std::end(junction), Junction{}); } // assign node-to-machine - for (MachAnyId const mach : rData.m_machines.m_ids.bitview().zeros()) + for (MachAnyId const mach : rData.m_machines.ids.bitview().zeros()) { - lgrn::Span portSpan = rPerNodeType.m_machToNode[mach]; + lgrn::Span portSpan = rPerNodeType.machToNode[mach]; lgrn::Span customSpan = rPerNodeType.m_machToNodeCustom[mach]; for (int i = 0; i < portSpan.size(); ++i) @@ -223,7 +223,7 @@ VehicleData VehicleBuilder::finalize_release() continue; } - lgrn::Span const juncSpan = rPerNodeType.m_nodeToMach[node]; + lgrn::Span const juncSpan = rPerNodeType.nodeToMach[node]; // find empty spot // should always succeed, as they were reserved a few lines ago @@ -231,16 +231,16 @@ VehicleData VehicleBuilder::finalize_release() std::begin(juncSpan), std::end(juncSpan), [] (Junction const& pair) { - return pair.m_type == lgrn::id_null(); + return pair.type == lgrn::id_null(); }); assert(found != std::end(juncSpan)); - MachTypeId const type = rData.m_machines.m_machTypes[mach]; - MachLocalId const local = rData.m_machines.m_machToLocal[mach]; + MachTypeId const type = rData.m_machines.machTypes[mach]; + MachLocalId const local = rData.m_machines.machToLocal[mach]; - found->m_local = local; - found->m_type = type; - found->m_custom = customSpan[i]; + found->local = local; + found->type = type; + found->custom = customSpan[i]; } } } @@ -248,17 +248,17 @@ VehicleData VehicleBuilder::finalize_release() // Reserve part-to-machine partitions using osp::link::MachinePair; rData.m_partToMachines.ids_reserve(rData.m_partIds.capacity()); - rData.m_partToMachines.data_reserve(rData.m_machines.m_ids.capacity()); + rData.m_partToMachines.data_reserve(rData.m_machines.ids.capacity()); for (PartId const part : rData.m_partIds.bitview().zeros()) { rData.m_partToMachines.emplace(part, m_partMachCount[part]); } // Assign part-to-machine partitions - for (MachAnyId const mach : rData.m_machines.m_ids.bitview().zeros()) + for (MachAnyId const mach : rData.m_machines.ids.bitview().zeros()) { - MachLocalId const local = rData.m_machines.m_machToLocal[mach]; - MachTypeId const type = rData.m_machines.m_machTypes[mach]; + MachLocalId const local = rData.m_machines.machToLocal[mach]; + MachTypeId const type = rData.m_machines.machTypes[mach]; PartId const part = rData.m_machToPart[mach]; auto const machines = lgrn::Span{rData.m_partToMachines[part]}; @@ -268,7 +268,7 @@ VehicleData VehicleBuilder::finalize_release() assert(rPartMachCount != 0); -- rPartMachCount; - machines[rPartMachCount] = { .m_local = local, .m_type = type }; + machines[rPartMachCount] = { .local = local, .type = type }; } assert(std::all_of(m_partMachCount.begin(), m_partMachCount.end(), diff --git a/src/adera/activescene/VehicleBuilder.h b/src/adera/activescene/VehicleBuilder.h index a3cd118e..d848ac03 100644 --- a/src/adera/activescene/VehicleBuilder.h +++ b/src/adera/activescene/VehicleBuilder.h @@ -25,6 +25,7 @@ #pragma once #include +#include #include #include @@ -68,11 +69,10 @@ struct VehicleData { using MachToNodeCustom_t = lgrn::IntArrayMultiMap; - using MapPartToMachines_t = osp::active::Parts::MapPartToMachines_t; + using MapPartToMachines_t = osp::active::ACtxParts::MapPartToMachines_t; VehicleData() = default; - VehicleData(VehicleData const& copy) = delete; - VehicleData(VehicleData&& move) = default; + OSP_MOVE_ONLY_CTOR(VehicleData); lgrn::IdRegistryStl m_partIds; std::vector m_partTransformWeld; @@ -114,7 +114,7 @@ class VehicleBuilder : m_pResources{pResources} { auto &rData = m_data.emplace(); - rData.m_machines.m_perType.resize(osp::link::MachTypeReg_t::size()); + rData.m_machines.perType.resize(osp::link::MachTypeReg_t::size()); rData.m_nodePerType.resize(osp::link::NodeTypeReg_t::size()); index_prefabs(); }; @@ -151,7 +151,7 @@ class VehicleBuilder std::size_t node_capacity(NodeTypeId nodeType) const { - return m_data->m_nodePerType[nodeType].m_nodeIds.capacity(); + return m_data->m_nodePerType[nodeType].nodeIds.capacity(); } struct Connection @@ -205,9 +205,9 @@ std::array VehicleBuilder::create_nodes(NodeTypeId const n PerNodeType &rPerNodeType = m_data->m_nodePerType[nodeType]; - rPerNodeType.m_nodeIds.create(std::begin(out), std::end(out)); - std::size_t const capacity = rPerNodeType.m_nodeIds.capacity(); - rPerNodeType.m_nodeToMach.ids_reserve(rPerNodeType.m_nodeIds.capacity()); + rPerNodeType.nodeIds.create(std::begin(out), std::end(out)); + std::size_t const capacity = rPerNodeType.nodeIds.capacity(); + rPerNodeType.nodeToMach.ids_reserve(rPerNodeType.nodeIds.capacity()); rPerNodeType.m_nodeConnectCount.resize(capacity, 0); return out; @@ -230,43 +230,4 @@ VALUES_T& VehicleBuilder::node_values(NodeTypeId nodeType) return rValues; } -struct ACtxVehicleSpawnVB -{ - std::vector m_dataVB; - - // Remap vectors convert IDs from VehicleData to ACtxParts. - // A single vector for remaps is shared for all vehicles to spawn, - // so offsets are used to divide up the vector. - - // PartId srcPart = /* ID from VehicleData */ - // PartId dstPart = m_remapParts[m_remapPartOffsets[newVehicleIndex] + srcPart]; - - inline Corrade::Containers::StridedArrayView2D remap_node_offsets_2d() noexcept - { - return {Corrade::Containers::arrayView(m_remapNodeOffsets.data(), m_remapNodeOffsets.size()), - {m_dataVB.size(), osp::link::NodeTypeReg_t::size()}}; - } - - inline Corrade::Containers::StridedArrayView2D remap_node_offsets_2d() const noexcept - { - return {Corrade::Containers::arrayView(m_remapNodeOffsets.data(), m_remapNodeOffsets.size()), - {m_dataVB.size(), osp::link::NodeTypeReg_t::size()}}; - } - - std::vector m_remapParts; - std::vector m_remapPartOffsets; - - std::vector m_remapWelds; - std::vector m_remapWeldOffsets; - - std::vector m_machtypeCount; - std::vector m_remapMachs; - std::vector m_remapMachOffsets; - - // remapNodes are both shared between all new vehicles and all node types - // An offset can exist for each pair of [New Vehicle, Node Type] - std::vector m_remapNodes; - std::vector m_remapNodeOffsets; -}; - } // namespace testapp diff --git a/src/adera/activescene/vehicles_vb_fn.cpp b/src/adera/activescene/vehicles_vb_fn.cpp new file mode 100644 index 00000000..9313a639 --- /dev/null +++ b/src/adera/activescene/vehicles_vb_fn.cpp @@ -0,0 +1,211 @@ +/** + * Open Space Program + * Copyright © 2019-2023 Open Space Program Project + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "vehicles_vb_fn.h" + +#include + +using namespace osp; +using namespace osp::active; + +namespace adera +{ + +void SysVehicleSpawnVB::create_parts_and_welds(ACtxVehicleSpawn& rVehicleSpawn, ACtxVehicleSpawnVB& rVehicleSpawnVB, ACtxParts& rScnParts) +{ + std::size_t const newVehicleCount = rVehicleSpawn.new_vehicle_count(); + ACtxVehicleSpawnVB &rVSVB = rVehicleSpawnVB; + + rVSVB.remapPartOffsets .resize(newVehicleCount); + rVSVB.remapWeldOffsets .resize(newVehicleCount); + rVehicleSpawn.spawnedPartOffsets .resize(newVehicleCount); + rVehicleSpawn.spawnedWeldOffsets .resize(newVehicleCount); + + // Count total parts and welds, and calculate offsets for remaps + + uint32_t partTotal = 0; + uint32_t remapPartTotal = 0; + + uint32_t weldTotal = 0; + uint32_t remapWeldTotal = 0; + + for (uint32_t spVhInt = 0; spVhInt < newVehicleCount; ++spVhInt) + { + auto const spVhId = SpVehicleId{spVhInt}; + VehicleData const* pVData = rVSVB.dataVB[spVhId]; + if (pVData == nullptr) + { + continue; + } + + rVehicleSpawn.spawnedPartOffsets[spVhId] = SpPartId{partTotal}; + partTotal += pVData->m_partIds.size(); + + rVSVB.remapPartOffsets[spVhId] = remapPartTotal; + remapPartTotal += pVData->m_partIds.capacity(); + + rVehicleSpawn.spawnedWeldOffsets[spVhId] = SpWeldId{weldTotal}; + weldTotal += pVData->m_weldIds.size(); + + rVSVB.remapWeldOffsets[spVhId] = remapWeldTotal; + remapWeldTotal += pVData->m_weldIds.capacity(); + } + + // Resize containers for new IDs + + rVehicleSpawn.spawnedParts .resize(partTotal); + rVehicleSpawn.spawnedWelds .resize(weldTotal); + rVehicleSpawn.spawnedPrefabs .resize(partTotal); + rVSVB.remapParts .resize(remapPartTotal, lgrn::id_null()); + rVSVB.remapWelds .resize(remapPartTotal, lgrn::id_null()); + + // Create new Scene PartIds and WeldIds + + rScnParts.partIds.create(rVehicleSpawn.spawnedParts.begin(), rVehicleSpawn.spawnedParts.end()); + rScnParts.weldIds.create(rVehicleSpawn.spawnedWelds.begin(), rVehicleSpawn.spawnedWelds.end()); + + rScnParts.partDirty.insert(rScnParts.partDirty.begin(), rVehicleSpawn.spawnedParts.begin(), rVehicleSpawn.spawnedParts.end()); + rScnParts.weldDirty.insert(rScnParts.weldDirty.begin(), rVehicleSpawn.spawnedWelds.begin(), rVehicleSpawn.spawnedWelds.end()); + + // Resize scene containers to account for new IDs + + std::size_t const maxParts = rScnParts.partIds.capacity(); + std::size_t const maxWelds = rScnParts.weldIds.capacity(); + rScnParts.partPrefabs .resize(maxParts); + rScnParts.partTransformWeld .resize(maxParts); + rScnParts.partToWeld .resize(maxParts); + rScnParts.weldToParts .data_reserve(maxParts); + rScnParts.weldToParts .ids_reserve(maxWelds); + rScnParts.weldToActive .resize(maxWelds); + rVehicleSpawn.partToSpawned .resize(maxParts); + + // Populate "Scene PartId -> NewPartId" map + for (uint32_t spPartInt = 0; spPartInt < rVehicleSpawn.spawnedParts.size(); ++spPartInt) + { + auto const spPart = SpPartId{spPartInt}; + rVehicleSpawn.partToSpawned[rVehicleSpawn.spawnedParts[spPart]] = spPart; + } + + // Populate remap vectors and set weld connections + + auto itDstPartIds = rVehicleSpawn.spawnedParts.cbegin(); + auto itDstWeldIds = rVehicleSpawn.spawnedWelds.cbegin(); + + for (uint32_t spVhInt = 0; spVhInt < newVehicleCount; ++spVhInt) + { + auto const spVhId = SpVehicleId{spVhInt}; + + VehicleData const* pVData = rVSVB.dataVB[spVhId]; + if (pVData == nullptr) + { + continue; + } + + std::size_t const remapPartOffset = rVSVB.remapPartOffsets[spVhId]; + std::size_t const remapWeldOffset = rVSVB.remapWeldOffsets[spVhId]; + + for (PartId const srcPart : pVData->m_partIds.bitview().zeros()) + { + PartId const dstPart = *itDstPartIds; + ++itDstPartIds; + + // Populate map for "VehicleBuilder PartId -> ACtxParts PartId" + rVSVB.remapParts[remapPartOffset + srcPart] = dstPart; + } + + for (WeldId const srcWeld : pVData->m_weldIds.bitview().zeros()) + { + WeldId const dstWeld = *itDstWeldIds; + ++itDstWeldIds; + + // Populate map for "VehicleBuilder WeldId -> ACtxParts WeldId" + // rVehicleSpawnVB.remapWelds + rVSVB.remapWelds[remapWeldOffset + srcWeld] = dstWeld; + + // Use remaps to connect ACtxParts WeldIds and PartIds + // rScnParts.m_partToWeld and rScnParts.m_weldToParts + + auto const srcWeldPartSpan = pVData->m_weldToParts[srcWeld]; + WeldId *pDstWeldPartOut = rScnParts.weldToParts.emplace(dstWeld, srcWeldPartSpan.size()); + + for (PartId const srcPart : srcWeldPartSpan) + { + PartId const dstPart = rVSVB.remapParts[remapPartOffset + srcPart]; + + (*pDstWeldPartOut) = dstPart; + std::advance(pDstWeldPartOut, 1); + + rScnParts.partToWeld[dstPart] = dstWeld; + } + } + } +} + + +void SysVehicleSpawnVB::request_prefabs(ACtxVehicleSpawn& rVehicleSpawn, ACtxVehicleSpawnVB const& rVehicleSpawnVB, ACtxParts& rScnParts, ACtxPrefabs& rPrefabs, Resources& rResources) +{ + std::size_t const newVehicleCount = rVehicleSpawn.new_vehicle_count(); + + auto itDstPartIds = rVehicleSpawn.spawnedParts.cbegin(); + auto itPrefabOut = rVehicleSpawn.spawnedPrefabs.begin(); + + for (uint32_t spVhInt = 0; spVhInt < newVehicleCount; ++spVhInt) + { + auto const spVhId = SpVehicleId{spVhInt}; + + VehicleData const* pVData = rVehicleSpawnVB.dataVB[spVhId]; + if (pVData == nullptr) + { + continue; + } + + // Copy Part data from VehicleBuilder to scene + for (uint32_t srcPart : pVData->m_partIds.bitview().zeros()) + { + PartId const dstPart = *itDstPartIds; + ++itDstPartIds; + + PrefabPair const& prefabPairSrc = pVData->m_partPrefabs[srcPart]; + PrefabPair prefabPairDst{ + rResources.owner_create(osp::restypes::gc_importer, prefabPairSrc.m_importer), + prefabPairSrc.m_prefabId + }; + rScnParts.partPrefabs[dstPart] = std::move(prefabPairDst); + rScnParts.partTransformWeld[dstPart] = pVData->m_partTransformWeld[srcPart]; + + // Add Prefab and Part init events + (*itPrefabOut) = rPrefabs.spawnRequest.size(); + ++itPrefabOut; + + rPrefabs.spawnRequest.push_back(TmpPrefabRequest{ + .m_importerRes = prefabPairSrc.m_importer, + .m_prefabId = prefabPairSrc.m_prefabId, + .m_pTransform = &pVData->m_partTransformWeld[srcPart] + }); + } + } +} + + +} // namespace adera diff --git a/src/adera/activescene/vehicles_vb_fn.h b/src/adera/activescene/vehicles_vb_fn.h new file mode 100644 index 00000000..1f2efbe2 --- /dev/null +++ b/src/adera/activescene/vehicles_vb_fn.h @@ -0,0 +1,89 @@ +/** + * Open Space Program + * Copyright © 2019-2023 Open Space Program Project + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#pragma once + +#include "VehicleBuilder.h" + +#include +#include + +namespace adera +{ + +struct ACtxVehicleSpawnVB +{ + using SpVehicleId = osp::active::SpVehicleId; + + // Remap vectors convert IDs from VehicleData to ACtxParts. + // A single vector for remaps is shared for all vehicles to spawn, + // so offsets are used to divide up the vector. + + // PartId srcPart = /* ID from VehicleData */ + // PartId dstPart = remapParts[remapPartOffsets[newVehicleIndex] + srcPart]; + + inline Corrade::Containers::StridedArrayView2D remap_node_offsets_2d() noexcept + { + return {Corrade::Containers::arrayView(remapNodeOffsets.data(), remapNodeOffsets.size()), + {dataVB.size(), osp::link::NodeTypeReg_t::size()}}; + } + + inline Corrade::Containers::StridedArrayView2D remap_node_offsets_2d() const noexcept + { + return {Corrade::Containers::arrayView(remapNodeOffsets.data(), remapNodeOffsets.size()), + {dataVB.size(), osp::link::NodeTypeReg_t::size()}}; + } + + osp::KeyedVec dataVB; + + std::vector remapParts; + osp::KeyedVec remapPartOffsets; + + std::vector remapWelds; + osp::KeyedVec remapWeldOffsets; + + std::vector machtypeCount; + std::vector remapMachs; + osp::KeyedVec remapMachOffsets; + + // remapNodes are both shared between all new vehicles and all node types + // An offset can exist for each pair of [New Vehicle, Node Type] + std::vector remapNodes; + osp::KeyedVec remapNodeOffsets; +}; + +class SysVehicleSpawnVB +{ + using ACtxParts = osp::active::ACtxParts; + using ACtxPrefabs = osp::active::ACtxPrefabs; + using ACtxVehicleSpawn = osp::active::ACtxVehicleSpawn; + using Resources = osp::Resources; +public: + static void create_parts_and_welds(ACtxVehicleSpawn& rVehicleSpawn, ACtxVehicleSpawnVB& rVehicleSpawnVB, ACtxParts& rScnParts); + + static void request_prefabs(ACtxVehicleSpawn& rVehicleSpawn, ACtxVehicleSpawnVB const& rVehicleSpawnVB, ACtxParts& rScnParts, ACtxPrefabs& rPrefabs, Resources& rResources); +}; + + +} // namespace adera diff --git a/src/osp/activescene/basic.h b/src/osp/activescene/basic.h index b4901cdb..648d86ff 100644 --- a/src/osp/activescene/basic.h +++ b/src/osp/activescene/basic.h @@ -27,6 +27,7 @@ #include "active_ent.h" #include "../core/bitvector.h" +#include "../core/keyed_vector.h" #include "../core/math_types.h" #include "../core/storage.h" @@ -65,11 +66,11 @@ struct ACtxSceneGraph // descendants is positioned directly after it within the array. // Example for tree structure "A( B(C(D)), E(F(G(H,I))) )" // * Descendant Count array: [A:8, B:2, C:1, D:0, E:4, F:3, G:2, H:0, I:0] - std::vector m_treeToEnt{lgrn::id_null()}; - std::vector m_treeDescendants{std::initializer_list{0}}; + osp::KeyedVec m_treeToEnt{{lgrn::id_null()}}; + osp::KeyedVec m_treeDescendants{std::initializer_list{0}}; - std::vector m_entParent; - std::vector m_entToTreePos; + osp::KeyedVec m_entParent; + osp::KeyedVec m_entToTreePos; std::vector m_delete; @@ -78,7 +79,7 @@ struct ACtxSceneGraph m_treeToEnt .reserve(ents); m_treeDescendants .reserve(ents); m_entParent .resize(ents); - m_entToTreePos .resize(ents); + m_entToTreePos .resize(ents, lgrn::id_null()); } }; diff --git a/src/osp/activescene/basic_fn.cpp b/src/osp/activescene/basic_fn.cpp index edcc3fea..e7a3f26c 100644 --- a/src/osp/activescene/basic_fn.cpp +++ b/src/osp/activescene/basic_fn.cpp @@ -36,10 +36,10 @@ using Corrade::Containers::arrayView; SubtreeBuilder SubtreeBuilder::add_child(ActiveEnt ent, uint32_t descendantCount) { // Place ent into tree at m_first - m_rScnGraph.m_treeToEnt[m_first] = ent; - m_rScnGraph.m_treeDescendants[m_first] = descendantCount; - m_rScnGraph.m_entParent[std::size_t(ent)] = m_root; - m_rScnGraph.m_entToTreePos[std::size_t(ent)] = m_first; + m_rScnGraph.m_treeToEnt[m_first] = ent; + m_rScnGraph.m_treeDescendants[m_first] = descendantCount; + m_rScnGraph.m_entParent[ent] = m_root; + m_rScnGraph.m_entToTreePos[ent] = m_first; TreePos_t const childFirst = m_first + 1; TreePos_t const childLast = childFirst + descendantCount; @@ -53,7 +53,7 @@ SubtreeBuilder SysSceneGraph::add_descendants(ACtxSceneGraph& rScnGraph, uint32_ { TreePos_t const rootPos = (root == lgrn::id_null()) ? 0 - : rScnGraph.m_entToTreePos[std::size_t(root)]; + : rScnGraph.m_entToTreePos[root]; uint32_t rootdescendants = rScnGraph.m_treeDescendants[rootPos]; @@ -73,9 +73,9 @@ SubtreeBuilder SysSceneGraph::add_descendants(ACtxSceneGraph& rScnGraph, uint32_ // Right-shift tree vectors from subFirst and onwards to make // space for the new subtree - for (ActiveEnt const ent : arrayView(rScnGraph.m_treeToEnt).slice(subFirst, treeOldSize)) + for (ActiveEnt const ent : arrayView(rScnGraph.m_treeToEnt.data(), rScnGraph.m_treeToEnt.size()).slice(subFirst, treeOldSize)) { - rScnGraph.m_entToTreePos[std::size_t(ent)] += descendantCount; + rScnGraph.m_entToTreePos[ent] += descendantCount; } std::shift_right(rScnGraph.m_treeToEnt.begin() + subFirst, rScnGraph.m_treeToEnt.end(), @@ -92,9 +92,9 @@ SubtreeBuilder SysSceneGraph::add_descendants(ACtxSceneGraph& rScnGraph, uint32_ while (parentNotNull) { parentNotNull = (parent != lgrn::id_null()); - TreePos_t const parentPos = parentNotNull ? rScnGraph.m_entToTreePos[std::size_t(parent)] : 0; + TreePos_t const parentPos = parentNotNull ? rScnGraph.m_entToTreePos[parent] : 0; rScnGraph.m_treeDescendants[parentPos] += descendantCount; - parent = parentNotNull ? rScnGraph.m_entParent[std::size_t(parent)] : parent; + parent = parentNotNull ? rScnGraph.m_entParent[parent] : parent; } @@ -103,7 +103,7 @@ SubtreeBuilder SysSceneGraph::add_descendants(ACtxSceneGraph& rScnGraph, uint32_ ArrayView SysSceneGraph::descendants(ACtxSceneGraph const& rScnGraph, ActiveEnt root) { - TreePos_t const rootPos = rScnGraph.m_entToTreePos[std::size_t(root)]; + TreePos_t const rootPos = rScnGraph.m_entToTreePos[root]; return descendants(rScnGraph, rootPos); } @@ -115,7 +115,7 @@ ArrayView SysSceneGraph::descendants(ACtxSceneGraph const& rScn return (descendants == 0) ? ArrayView{} - : arrayView(rScnGraph.m_treeToEnt).slice(childFirst, childLast); + : arrayView(rScnGraph.m_treeToEnt.data(), rScnGraph.m_treeToEnt.size()).slice(childFirst, childLast); } @@ -123,7 +123,7 @@ ChildRange_t SysSceneGraph::children(ACtxSceneGraph const& rScnGraph, ActiveEnt { TreePos_t const parentPos = (parent == lgrn::id_null()) ? 0 - : rScnGraph.m_entToTreePos[std::size_t(parent)]; + : rScnGraph.m_entToTreePos[parent]; uint32_t const descendants = rScnGraph.m_treeDescendants[parentPos]; TreePos_t const childFirst = parentPos + 1; @@ -176,9 +176,9 @@ void SysSceneGraph::do_delete(ACtxSceneGraph& rScnGraph) bool parentNotNull = true; while (parentNotNull) { - parent = rScnGraph.m_entParent[std::size_t(parent)]; + parent = rScnGraph.m_entParent[parent]; parentNotNull = (parent != lgrn::id_null()); - TreePos_t const parentPos = parentNotNull ? rScnGraph.m_entToTreePos[std::size_t(parent)] : 0; + TreePos_t const parentPos = parentNotNull ? rScnGraph.m_entToTreePos[parent] : 0; rScnGraph.m_treeDescendants[parentPos] -= removeTotal; } @@ -186,15 +186,15 @@ void SysSceneGraph::do_delete(ACtxSceneGraph& rScnGraph) std::for_each(itTreeEntsFirst + (*itDel), itTreeEntsFirst + (*itDel) + removeTotal, [&rScnGraph] (ActiveEnt const ent) { - rScnGraph.m_entParent[std::size_t(ent)] = lgrn::id_null(); - rScnGraph.m_entToTreePos[std::size_t(ent)] = lgrn::id_null(); + rScnGraph.m_entParent[ent] = lgrn::id_null(); + rScnGraph.m_entToTreePos[ent] = lgrn::id_null(); }); // Update tree positions for elements to shift std::for_each(itTreeEntsFirst + keepFirst, itTreeEntsFirst + keepLast, [&rScnGraph, shift] (ActiveEnt const ent) { - rScnGraph.m_entToTreePos[std::size_t(ent)] -= shift; + rScnGraph.m_entToTreePos[ent] -= shift; }); // Shift over tree data diff --git a/src/osp/activescene/basic_fn.h b/src/osp/activescene/basic_fn.h index 8c02bf77..0124fe26 100644 --- a/src/osp/activescene/basic_fn.h +++ b/src/osp/activescene/basic_fn.h @@ -183,7 +183,7 @@ void SysSceneGraph::cut(ACtxSceneGraph& rScnGraph, ITA_T first, ITB_T const& las while (first != last) { - TreePos_t const pos = rScnGraph.m_entToTreePos[std::size_t(*first)]; + TreePos_t const pos = rScnGraph.m_entToTreePos[*first]; rScnGraph.m_delete.push_back(pos); diff --git a/src/osp/activescene/prefab_fn.cpp b/src/osp/activescene/prefab_fn.cpp index c33e6031..daf181b1 100644 --- a/src/osp/activescene/prefab_fn.cpp +++ b/src/osp/activescene/prefab_fn.cpp @@ -38,8 +38,45 @@ using osp::restypes::gc_importer; namespace osp::active { +void SysPrefabInit::create_activeents( + ACtxPrefabs& rPrefabs, + ACtxBasic& rBasic, + Resources& rResources) +{ + // Count number of entities needed to be created + std::size_t totalEnts = 0; + for (TmpPrefabRequest const& rPfBasic : rPrefabs.spawnRequest) + { + auto const& rPrefabData = rResources.data_get(gc_importer, rPfBasic.m_importerRes); + auto const& objects = rPrefabData.m_prefabs[rPfBasic.m_prefabId]; + + totalEnts += objects.size(); + } + + // Create entities + rPrefabs.newEnts.resize(totalEnts); + rBasic.m_activeIds.create(std::begin(rPrefabs.newEnts), std::end(rPrefabs.newEnts)); + + // Assign new entities to each prefab to create + rPrefabs.spawnedEntsOffset.resize(rPrefabs.spawnRequest.size()); + auto itEntAvailable = std::begin(rPrefabs.newEnts); + auto itPfEntSpanOut = std::begin(rPrefabs.spawnedEntsOffset); + for (TmpPrefabRequest& rPfBasic : rPrefabs.spawnRequest) + { + auto const& rPrefabData = rResources.data_get(gc_importer, rPfBasic.m_importerRes); + auto const& objects = rPrefabData.m_prefabs[rPfBasic.m_prefabId]; + + (*itPfEntSpanOut) = { &(*itEntAvailable), objects.size() }; + + std::advance(itEntAvailable, objects.size()); + std::advance(itPfEntSpanOut, 1); + } + + assert(itEntAvailable == std::end(rPrefabs.newEnts)); +} + void SysPrefabInit::add_to_subtree( - TmpPrefabInitBasic const& basic, + TmpPrefabRequest const& basic, ArrayView ents, Resources const& rResources, SubtreeBuilder& bldPrefab) noexcept @@ -78,13 +115,13 @@ void SysPrefabInit::add_to_subtree( } void SysPrefabInit::init_transforms( - ACtxPrefabInit const& rPrefabInit, + ACtxPrefabs const& rPrefabs, Resources const& rResources, ACompTransformStorage_t& rTransform) noexcept { - auto itPfEnts = std::begin(rPrefabInit.m_ents); + auto itPfEnts = rPrefabs.spawnedEntsOffset.begin(); - for (TmpPrefabInitBasic const& rPfBasic : rPrefabInit.m_basicIn) + for (TmpPrefabRequest const& rPfBasic : rPrefabs.spawnRequest) { auto const &rImportData = rResources.data_get( gc_importer, rPfBasic.m_importerRes); @@ -103,52 +140,17 @@ void SysPrefabInit::init_transforms( rTransform.emplace(ent, transform); } - std::advance(itPfEnts, 1); + ++itPfEnts; } } -#if 0 -void SysPrefabInit::init_drawing( - ACtxPrefabInit const& rPrefabInit, - Resources& rResources, - ACtxDrawing& rDrawing, - ACtxDrawingRes& rDrawingRes, - std::optional material) noexcept +void SysPrefabInit::init_info( + ACtxPrefabs& rPrefabs, + Resources const& rResources) noexcept { - auto itPfEnts = rPrefabInit.m_ents.begin(); + auto itPfEnts = rPrefabs.spawnedEntsOffset.begin(); - // stupidly make drawIDs first - // TODO: separate this into another step. - for (TmpPrefabInitBasic const& rPfBasic : rPrefabInit.m_basicIn) - { - auto const &rImportData = rResources.data_get( - gc_importer, rPfBasic.m_importerRes); - auto const &rPrefabData = rResources.data_get( - gc_importer, rPfBasic.m_importerRes); - - auto const objects = rPrefabData.m_prefabs[rPfBasic.m_prefabId]; - - for (std::size_t i = 0; i < objects.size(); ++i) - { - int const meshImportId = rImportData.m_objMeshes[objects[i]]; - if (meshImportId == -1) - { - continue; - } - - ActiveEnt const ent = (*itPfEnts)[i]; - rDrawing.m_activeToDraw[ent] = rDrawing.m_drawIds.create(); - } - - ++itPfEnts; - } - - // then resize containers - rDrawing.resize_draw(); - - itPfEnts = rPrefabInit.m_ents.begin(); - - for (TmpPrefabInitBasic const& rPfBasic : rPrefabInit.m_basicIn) + for (TmpPrefabRequest const& rPfBasic : rPrefabs.spawnRequest) { auto const &rImportData = rResources.data_get( gc_importer, rPfBasic.m_importerRes); @@ -156,84 +158,36 @@ void SysPrefabInit::init_drawing( gc_importer, rPfBasic.m_importerRes); auto const ents = ArrayView{*itPfEnts}; - auto const objects = lgrn::Span{rPrefabData.m_prefabs[rPfBasic.m_prefabId]}; - auto const parents = lgrn::Span{rPrefabData.m_prefabParents[rPfBasic.m_prefabId]}; - - // All ancestors of each entity that has a mesh - auto const needs_draw_transform - = [&parents, &ents, &rDrawing, &needDrawTf = rDrawing.m_needDrawTf] - (auto && self, int const object, ActiveEnt const ent) noexcept -> void - { - needDrawTf.set(std::size_t(ent)); - - int const parentObj = parents[object]; - - if (parentObj != -1) - { - self(self, parentObj, ents[parentObj]); - } - }; + auto const objects = rPrefabData.m_prefabs[rPfBasic.m_prefabId]; + auto const parents = rPrefabData.m_prefabParents[rPfBasic.m_prefabId]; for (std::size_t i = 0; i < objects.size(); ++i) { - ActiveEnt const ent = (*itPfEnts)[i]; - - // Check if object has mesh - int const meshImportId = rImportData.m_objMeshes[objects[i]]; - if (meshImportId == -1) - { - continue; - } - - needs_draw_transform(needs_draw_transform, objects[i], ent); - - DrawEnt const drawEnt = rDrawing.m_activeToDraw[ent]; + ActiveEnt const ent = ents[i]; + PrefabInstanceInfo &rInfo = rPrefabs.instanceInfo[ent]; + rInfo.importer = rPfBasic.m_importerRes; + rInfo.prefab = rPfBasic.m_prefabId; + rInfo.obj = i; - osp::ResId const meshRes = rImportData.m_meshes[meshImportId]; - MeshId const meshId = SysRender::own_mesh_resource(rDrawing, rDrawingRes, rResources, meshRes); - rDrawing.m_mesh[drawEnt] = rDrawing.m_meshRefCounts.ref_add(meshId); - rDrawing.m_meshDirty.push_back(drawEnt); - - int const matImportId = rImportData.m_objMaterials[objects[i]]; - - if (Magnum::Trade::MaterialData const &mat = *rImportData.m_materials.at(matImportId); - mat.types() & Magnum::Trade::MaterialType::PbrMetallicRoughness) + if (parents[i] == -1) { - auto const& matPbr = mat.as(); - if (int const baseColor = matPbr.baseColorTexture(); - baseColor != -1) - { - osp::ResId const texRes = rImportData.m_textures[baseColor]; - TexId const texId = SysRender::own_texture_resource(rDrawing, rDrawingRes, rResources, texRes); - rDrawing.m_diffuseTex[drawEnt] = rDrawing.m_texRefCounts.ref_add(texId); - rDrawing.m_diffuseDirty.push_back(drawEnt); - } - } - - rDrawing.m_drawBasic[drawEnt] = { .m_opaque = true, .m_transparent = false }; - rDrawing.m_visible.set(std::size_t(drawEnt)); - - if (material.has_value()) - { - material.value().m_ents.set(std::size_t(drawEnt)); - material.value().m_dirty.push_back(drawEnt); + rPrefabs.roots.set(std::size_t(ent)); } } ++itPfEnts; } } -#endif void SysPrefabInit::init_physics( - ACtxPrefabInit const& rPrefabInit, - Resources const& rResources, - ACtxPhysics& rCtxPhys) noexcept + ACtxPrefabs const& rPrefabs, + Resources const& rResources, + ACtxPhysics& rCtxPhys) noexcept { - auto itPfEnts = std::begin(rPrefabInit.m_ents); + auto itPfEnts = rPrefabs.spawnedEntsOffset.begin(); - for (TmpPrefabInitBasic const& rPfBasic : rPrefabInit.m_basicIn) + for (TmpPrefabRequest const& rPfBasic : rPrefabs.spawnRequest) { auto const &rImportData = rResources.data_get( gc_importer, rPfBasic.m_importerRes); diff --git a/src/osp/activescene/prefab_fn.h b/src/osp/activescene/prefab_fn.h index 6dfdb1b5..7e407b6a 100644 --- a/src/osp/activescene/prefab_fn.h +++ b/src/osp/activescene/prefab_fn.h @@ -27,19 +27,13 @@ #include "basic_fn.h" #include "physics.h" +#include "../core/array_view.h" #include "../core/resourcetypes.h" #include "../vehicles/prefabs.h" -#include - #include #include -namespace osp -{ - using Corrade::Containers::ArrayView; -} - namespace osp::active { @@ -52,44 +46,60 @@ namespace osp::active * Intended to be created and quickly destroyed once the prefab is created. * This is often the span of a single frame. */ -struct TmpPrefabInitBasic +struct TmpPrefabRequest { ResId m_importerRes{lgrn::id_null()}; PrefabId m_prefabId; Matrix4 const* m_pTransform{nullptr}; }; +struct PrefabInstanceInfo +{ + ResId importer { lgrn::id_null() }; + PrefabId prefab { lgrn::id_null() }; + ObjId obj { lgrn::id_null() }; +}; -struct ACtxPrefabInit +struct ACtxPrefabs { - std::vector m_basicIn; - std::vector< ArrayView > m_ents; + std::vector spawnRequest; + std::vector< ArrayView > spawnedEntsOffset; + std::vector newEnts; - std::vector m_newEnts; + osp::active::ActiveEntSet_t roots; + KeyedVec instanceInfo; }; - class SysPrefabInit { public: + static void create_activeents( + ACtxPrefabs& rPrefabs, + ACtxBasic& rBasic, + Resources& rResources); + static void add_to_subtree( - TmpPrefabInitBasic const& basic, - ArrayView ents, - Resources const& rResources, - SubtreeBuilder& rSubtree) noexcept; + TmpPrefabRequest const& basic, + ArrayView ents, + Resources const& rResources, + SubtreeBuilder& rSubtree) noexcept; static void init_transforms( - ACtxPrefabInit const& rPrefabInit, - Resources const& rResources, - ACompTransformStorage_t& rTransform) noexcept; + ACtxPrefabs const& rPrefabs, + Resources const& rResources, + ACompTransformStorage_t& rTransform) noexcept; + + static void init_info( + ACtxPrefabs& rPrefabs, + Resources const& rResources) noexcept; static void init_physics( - ACtxPrefabInit const& rPrefabInit, - Resources const& rResources, - ACtxPhysics& rCtxPhys) noexcept; -}; + ACtxPrefabs const& rPrefabs, + Resources const& rResources, + ACtxPhysics& rCtxPhys) noexcept; +}; } // namespace osp::active diff --git a/src/osp/activescene/vehicles.h b/src/osp/activescene/vehicles.h index 784d93f5..46747d9f 100644 --- a/src/osp/activescene/vehicles.h +++ b/src/osp/activescene/vehicles.h @@ -27,6 +27,7 @@ #include "active_ent.h" #include "../core/array_view.h" +#include "../core/keyed_vector.h" #include "../core/math_types.h" #include "../link/machines.h" #include "../vehicles/prefabs.h" @@ -34,70 +35,75 @@ #include #include +#include + namespace osp::active { using PartId = uint32_t; using WeldId = uint32_t; -struct Parts +struct ACtxParts { using MapPartToMachines_t = lgrn::IntArrayMultiMap; - lgrn::IdRegistryStl m_partIds; - std::vector m_partPrefabs; - std::vector m_partToWeld; - std::vector m_partTransformWeld; - MapPartToMachines_t m_partToMachines; - std::vector m_partDirty; + lgrn::IdRegistryStl partIds; + KeyedVec partPrefabs; + KeyedVec partTransformWeld; ///< Part's transform relative to the weld it's part of + std::vector partDirty; - lgrn::IdRegistryStl m_weldIds; - lgrn::IntArrayMultiMap m_weldToParts; - std::vector m_weldDirty; + lgrn::IdRegistryStl weldIds; + std::vector weldDirty; - link::Machines m_machines; - std::vector m_machineToPart; - std::vector m_nodePerType; -}; + link::Machines machines; + KeyedVec nodePerType; -struct ACtxParts : Parts -{ - std::vector m_partToActive; - std::vector m_activeToPart; - std::vector m_weldToEnt; + lgrn::IntArrayMultiMap weldToParts; + KeyedVec partToWeld; + + MapPartToMachines_t partToMachines; + KeyedVec machineToPart; + + KeyedVec partToActive; + KeyedVec activeToPart; + + KeyedVec weldToActive; }; -using NewVehicleId = uint32_t; -using NewPartId = uint32_t; -using NewWeldId = uint32_t; + +using SpVehicleId = StrongId; +using SpPartId = StrongId; +using SpWeldId = StrongId; +using SpMachAnyId = StrongId; struct ACtxVehicleSpawn { struct TmpToInit { - Vector3 m_position; - Vector3 m_velocity; - Quaternion m_rotation; + Vector3 position; + Vector3 velocity; + Quaternion rotation; }; std::size_t new_vehicle_count() const noexcept { - return m_newVhBasicIn.size(); + return spawnRequest.size(); } - std::vector m_newVhBasicIn; - std::vector m_newVhPartOffsets; - std::vector m_newVhWeldOffsets; + KeyedVec spawnRequest; + + KeyedVec spawnedParts; + KeyedVec spawnedPartOffsets; - std::vector m_newPartToPart; - std::vector m_newPartPrefabs; + KeyedVec partToSpawned; + KeyedVec spawnedPrefabs; - std::vector m_partToNewPart; + KeyedVec spawnedWelds; + KeyedVec spawnedWeldOffsets; - std::vector m_newWeldToWeld; - std::vector m_newWeldToEnt; + KeyedVec rootEnts; - std::vector m_newMachToMach; + KeyedVec spawnedMachs; }; } // namespace osp::active diff --git a/src/osp/core/bitvector.h b/src/osp/core/bitvector.h index 81fb33d2..2f399df8 100644 --- a/src/osp/core/bitvector.h +++ b/src/osp/core/bitvector.h @@ -37,7 +37,7 @@ using BitVector_t = lgrn::BitView< std::vector >; inline void bitvector_resize(BitVector_t &rBitVector, std::size_t size) { - rBitVector.ints().resize(size / 64 + (size % 64 != 0)); + rBitVector.ints().resize(size / 64 + (size % 64 != 0), 0); } } // namespace osp diff --git a/src/osp/core/strong_id.h b/src/osp/core/strong_id.h index 954ee4a1..b9d2cbc7 100644 --- a/src/osp/core/strong_id.h +++ b/src/osp/core/strong_id.h @@ -24,8 +24,6 @@ */ #pragma once -#include "copymove_macros.h" - #include #include // light-ish header that happens to include std::hash @@ -48,26 +46,31 @@ struct StrongId using entity_type = INT_T; // Name used for entt compatibility constexpr StrongId() noexcept = default; - constexpr StrongId(StrongId const& copy) noexcept - : m_value{copy.m_value} - { }; + constexpr StrongId(StrongId const& copy) noexcept = default; + constexpr StrongId& operator=(StrongId const& copy) noexcept = default; + + static StrongId from_index(std::size_t const index) + { + return StrongId{static_cast(index)}; + } + constexpr explicit StrongId(INT_T const value) noexcept - : m_value{value} + : value{value} { }; constexpr explicit operator std::size_t() const noexcept { - return m_value; + return value; } constexpr explicit operator INT_T() const noexcept { - return m_value; + return value; } constexpr auto operator<=>(StrongId const&) const = default; - INT_T m_value{ lgrn::id_null() }; + INT_T value{ lgrn::id_null() }; }; } // namespace osp @@ -77,7 +80,7 @@ template struct std::hash> { constexpr auto operator() (osp::StrongId const& key) const { - return key.m_value; + return key.value; } }; diff --git a/src/osp/drawing/drawing.h b/src/osp/drawing/drawing.h index bc70702b..60e7800a 100644 --- a/src/osp/drawing/drawing.h +++ b/src/osp/drawing/drawing.h @@ -140,7 +140,8 @@ struct ACtxSceneRender void resize_active(std::size_t const size) { bitvector_resize(m_needDrawTf, size); - m_activeToDraw.resize(size, lgrn::id_null()); + m_activeToDraw .resize(size, lgrn::id_null()); + drawTfObserverEnable.resize(size, 0); } lgrn::IdRegistryStl m_drawIds; @@ -153,6 +154,7 @@ struct ACtxSceneRender DrawEntSet_t m_needDrawTf; KeyedVec m_activeToDraw; + KeyedVec drawTfObserverEnable; DrawTransforms_t m_drawTransform; // Meshes and textures assigned to DrawEnts diff --git a/src/osp/drawing/drawing_fn.cpp b/src/osp/drawing/drawing_fn.cpp index 74c2d4a4..9dc197d3 100644 --- a/src/osp/drawing/drawing_fn.cpp +++ b/src/osp/drawing/drawing_fn.cpp @@ -25,7 +25,6 @@ #include "drawing_fn.h" #include "own_restypes.h" -#include "../activescene/basic_fn.h" #include "../core/Resources.h" using namespace osp; @@ -88,35 +87,6 @@ void SysRender::clear_resource_owners(ACtxDrawingRes& rCtxDrawingRes, Resources rCtxDrawingRes.m_resToMesh.clear(); } -void SysRender::update_draw_transforms_recurse( - ACtxSceneGraph const& rScnGraph, - KeyedVec const& activeToDraw, - active::ACompTransformStorage_t const& rTf, - DrawTransforms_t& rDrawTf, - ActiveEntSet_t const& needDrawTf, - ActiveEnt ent, - Matrix4 const& parentTf, - bool root) -{ - Matrix4 const& entTf = rTf.get(ent).m_transform; - Matrix4 const& entDrawTf = root ? (entTf) : (parentTf * entTf); - - if (DrawEnt const drawEnt = activeToDraw[ent]; - drawEnt != lgrn::id_null()) - { - rDrawTf[drawEnt] = entDrawTf; - } - - for (ActiveEnt entChild : SysSceneGraph::children(rScnGraph, ent)) - { - if (needDrawTf.test(std::size_t(entChild))) - { - update_draw_transforms_recurse(rScnGraph, activeToDraw, rTf, rDrawTf, needDrawTf, entChild, entDrawTf, false); - } - } -} - - MeshIdOwner_t SysRender::add_drawable_mesh(ACtxDrawing& rDrawing, ACtxDrawingRes& rDrawingRes, Resources& rResources, PkgId const pkg, std::string_view const name) { ResId const res = rResources.find(restypes::gc_mesh, pkg, name); diff --git a/src/osp/drawing/drawing_fn.h b/src/osp/drawing/drawing_fn.h index a52217b3..6ba54561 100644 --- a/src/osp/drawing/drawing_fn.h +++ b/src/osp/drawing/drawing_fn.h @@ -27,12 +27,41 @@ #include "drawing.h" #include "../activescene/basic.h" - -#include +#include "../activescene/basic_fn.h" namespace osp::draw { +/** + * @brief Function pointers called when new draw transforms are calculated + * + * Draw transforms (Matrix4) are calculated by traversing the Scene graph (tree of ActiveEnts). + * These matrices are not always stored in memory since they're slightly expensive. By default, + * they are only saved for DrawEnts associated with an ActiveEnt in ACtxSceneRender::activeToDraw. + * + * Draw transforms can be calculated by SysRender::update_draw_transforms, or potentially by a + * future system that takes physics engine interpolation or animations into account. + * DrawTfObservers provides a way to tap into this procedure to call custom functions for other + * systems. + * + * To use, write into DrawTfObservers::observers[i] + * Enable per-DrawEnt by setting ACtxSceneRender::drawTfObserverEnable[drawEnt] bit [i] + * + */ +struct DrawTfObservers +{ + using UserData_t = std::array; + using Func_t = void(*)(ACtxSceneRender& rCtxScnRdr, Matrix4 const&, active::ActiveEnt, int, UserData_t) noexcept; + + struct Observer + { + Func_t func{nullptr}; + UserData_t data{}; + }; + + std::array observers; +}; + /** * @brief View and Projection matrix */ @@ -66,17 +95,10 @@ struct EntityToDraw using ShaderDrawFnc_t = void (*)( DrawEnt, ViewProjMatrix const&, UserData_t) noexcept; - constexpr void operator()( - DrawEnt ent, - ViewProjMatrix const& viewProj) const noexcept - { - m_draw(ent, viewProj, m_data); - } - - ShaderDrawFnc_t m_draw; + ShaderDrawFnc_t draw; // Non-owning user data passed to draw function, such as the shader - UserData_t m_data; + UserData_t data; }; // struct EntityToDraw @@ -92,28 +114,17 @@ struct RenderGroup { using DrawEnts_t = Storage_t; - /** - * @return Iterable view for stored entities - */ - decltype(auto) view() - { - return entt::basic_view{m_entities}; - } - - /** - * @return Iterable view for stored entities - */ - decltype(auto) view() const - { - return entt::basic_view{m_entities}; - } - - DrawEnts_t m_entities; + DrawEnts_t entities; }; // struct RenderGroup class SysRender { + struct UpdDrawTransformNoOp + { + constexpr void operator()(Matrix4 const&, active::ActiveEnt, int) const noexcept {} + }; + public: /** @@ -150,25 +161,29 @@ class SysRender * @param rResources [ref] Application Resources */ static void clear_resource_owners( - ACtxDrawingRes& rCtxDrawingRes, - Resources& rResources); + ACtxDrawingRes& rCtxDrawingRes, + Resources& rResources); - template - static void update_draw_transforms( - active::ACtxSceneGraph const& rScnGraph, - KeyedVec const& activeToDraw, - active::ACompTransformStorage_t const& transform, - DrawTransforms_t& rDrawTf, - active::ActiveEntSet_t const& useDrawTf, - IT_T first, - ITB_T const& last); + static inline void needs_draw_transforms( + active::ACtxSceneGraph const& scnGraph, + active::ActiveEntSet_t& rNeedDrawTf, + active::ActiveEnt ent); - /** - * @brief Set all dirty flags/vectors - * - * @param rCtxDrawing [ref] Drawing data - */ - static void set_dirty_all(ACtxDrawing& rCtxDrawing); + struct ArgsForUpdDrawTransform + { + active::ACtxSceneGraph const& scnGraph; + active::ACompTransformStorage_t const& transforms; + KeyedVec const& activeToDraw; + active::ActiveEntSet_t const& needDrawTf; + DrawTransforms_t& rDrawTf; + }; + + template + static void update_draw_transforms( + ArgsForUpdDrawTransform args, + IT_T first, + ITB_T const& last, + FUNC_T func = {}); template static void update_delete_drawing( @@ -179,38 +194,85 @@ class SysRender static constexpr decltype(auto) gen_drawable_mesh_adder(ACtxDrawing& rDrawing, ACtxDrawingRes& rDrawingRes, Resources& rResources, PkgId const pkg); private: + + template static void update_draw_transforms_recurse( - active::ACtxSceneGraph const& rScnGraph, - KeyedVec const& activeToDraw, - active::ACompTransformStorage_t const& rTf, - DrawTransforms_t& rDrawTf, - active::ActiveEntSet_t const& useDrawTf, - active::ActiveEnt ent, - Matrix4 const& parentTf, - bool root); + ArgsForUpdDrawTransform args, + active::ActiveEnt ent, + Matrix4 const& parentTf, + int depth, + FUNC_T& func); }; // class SysRender -template +void SysRender::needs_draw_transforms( + active::ACtxSceneGraph const& scnGraph, + active::ActiveEntSet_t& rNeedDrawTf, + active::ActiveEnt ent) +{ + rNeedDrawTf.set(ent.value); + + active::ActiveEnt const parentEnt = scnGraph.m_entParent[ent]; + + if ( parentEnt != lgrn::id_null() + && ! rNeedDrawTf.test(std::size_t(parentEnt)) ) + { + SysRender::needs_draw_transforms(scnGraph, rNeedDrawTf, parentEnt); + } +} + +template void SysRender::update_draw_transforms( - active::ACtxSceneGraph const& rScnGraph, - KeyedVec const& activeToDraw, - active::ACompTransformStorage_t const& rTf, - DrawTransforms_t& rDrawTf, - active::ActiveEntSet_t const& needDrawTf, - IT_T first, - ITB_T const& last) + ArgsForUpdDrawTransform args, + IT_T first, + ITB_T const& last, + FUNC_T func) { static constexpr Matrix4 const identity{}; while (first != last) { - update_draw_transforms_recurse(rScnGraph, activeToDraw, rTf, rDrawTf, needDrawTf, *first, identity, true); + active::ActiveEnt const ent = *first; + + if (args.needDrawTf.test(ent.value)) + { + update_draw_transforms_recurse(args, ent, identity, true, func); + } std::advance(first, 1); } } +template +void SysRender::update_draw_transforms_recurse( + ArgsForUpdDrawTransform args, + active::ActiveEnt ent, + Matrix4 const& parentTf, + int depth, + FUNC_T& func) +{ + using namespace osp::active; + + Matrix4 const& entTf = args.transforms.get(ent).m_transform; + Matrix4 const& entDrawTf = (depth == 0) ? (entTf) : (parentTf * entTf); + + func(entDrawTf, ent, depth); + + DrawEnt const drawEnt = args.activeToDraw[ent]; + if (drawEnt != lgrn::id_null()) + { + args.rDrawTf[drawEnt] = entDrawTf; + } + + for (ActiveEnt entChild : SysSceneGraph::children(args.scnGraph, ent)) + { + if (args.needDrawTf.test(std::size_t(entChild))) + { + update_draw_transforms_recurse(args, entChild, entDrawTf, depth + 1, func); + } + } +} + template void remove_refcounted( diff --git a/src/osp/drawing/prefab_draw.cpp b/src/osp/drawing/prefab_draw.cpp new file mode 100644 index 00000000..085429fd --- /dev/null +++ b/src/osp/drawing/prefab_draw.cpp @@ -0,0 +1,261 @@ +/** + * Open Space Program + * Copyright © 2019-2023 Open Space Program Project + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHEfR DEALINGS IN THE + * SOFTWARE. + */ + +#include "prefab_draw.h" +#include "drawing_fn.h" + +#include +#include + +#include +#include + +using namespace osp; +using namespace osp::active; +using namespace osp::draw; + +using osp::restypes::gc_importer; + +void SysPrefabDraw::init_drawents( + ACtxPrefabs& rPrefabs, + Resources& rResources, + ACtxBasic const& rBasic, + ACtxDrawing& rDrawing, + ACtxSceneRender& rScnRender) +{ + auto itPfEnts = rPrefabs.spawnedEntsOffset.begin(); + + for (TmpPrefabRequest const& request : rPrefabs.spawnRequest) + { + auto const &rImportData = rResources.data_get(gc_importer, request.m_importerRes); + auto const &rPrefabData = rResources.data_get (gc_importer, request.m_importerRes); + auto const objects = rPrefabData.m_prefabs[request.m_prefabId]; + auto const ents = ArrayView{*itPfEnts}; + + for (std::size_t i = 0; i < objects.size(); ++i) + { + int const meshImportId = rImportData.m_objMeshes[objects[i]]; + if (meshImportId == -1) + { + continue; + } + + ActiveEnt const ent = ents[i]; + rScnRender.m_activeToDraw[ent] = rScnRender.m_drawIds.create(); + } + + ++itPfEnts; + } +} + +void SysPrefabDraw::resync_drawents( + ACtxPrefabs& rPrefabs, + Resources& rResources, + ACtxBasic const& rBasic, + ACtxDrawing& rDrawing, + ACtxSceneRender& rScnRender) +{ + for (std::size_t const rootInt : rPrefabs.roots.ones()) + { + auto const root = ActiveEnt::from_index(rootInt); + + PrefabInstanceInfo const &rRootInfo = rPrefabs.instanceInfo[root]; + + LGRN_ASSERT(rRootInfo.prefab != lgrn::id_null()); + LGRN_ASSERT(rRootInfo.importer != lgrn::id_null()); + + auto const &rImportData = rResources.data_get(gc_importer, rRootInfo.importer); + auto const &rPrefabData = rResources.data_get (gc_importer, rRootInfo.importer); + auto const objects = rPrefabData.m_prefabs[rRootInfo.prefab]; + + for (ActiveEnt const ent : SysSceneGraph::descendants(rBasic.m_scnGraph, root)) + { + PrefabInstanceInfo const &rInfo = rPrefabs.instanceInfo[ent]; + + int const meshImportId = rImportData.m_objMeshes[objects[rInfo.obj]]; + if (meshImportId == -1) + { + continue; + } + + LGRN_ASSERT(rScnRender.m_activeToDraw[ent] == lgrn::id_null()); + rScnRender.m_activeToDraw[ent] = rScnRender.m_drawIds.create(); + } + } +} + +void SysPrefabDraw::init_mesh_and_material( + ACtxPrefabs& rPrefabs, + Resources& rResources, + ACtxBasic const& rBasic, + ACtxDrawing& rDrawing, + ACtxDrawingRes& rDrawingRes, + ACtxSceneRender& rScnRender, + MaterialId material) +{ + auto itPfEnts = rPrefabs.spawnedEntsOffset.begin(); + + for (TmpPrefabRequest const& request : rPrefabs.spawnRequest) + { + auto const &rImportData = rResources.data_get(gc_importer, request.m_importerRes); + auto const &rPrefabData = rResources.data_get (gc_importer, request.m_importerRes); + auto const objects = lgrn::Span{rPrefabData.m_prefabs[request.m_prefabId]}; + auto const parents = lgrn::Span{rPrefabData.m_prefabParents[request.m_prefabId]}; + auto const ents = ArrayView{*itPfEnts}; + + // All ancestors of each entity that has a mesh + auto const needs_draw_transform + = [&parents, &ents, &rDrawing, &needDrawTf = rScnRender.m_needDrawTf] + (auto&& self, int const object, ActiveEnt const ent) noexcept -> void + { + needDrawTf.set(ent.value); + + int const parentObj = parents[object]; + + if (parentObj != -1) + { + self(self, parentObj, ents[parentObj]); + } + }; + + for (std::size_t i = 0; i < objects.size(); ++i) + { + ActiveEnt const ent = ents[i]; + + // Check if object has mesh + int const meshImportId = rImportData.m_objMeshes[objects[i]]; + if (meshImportId == -1) + { + continue; + } + + needs_draw_transform(needs_draw_transform, objects[i], ent); + + DrawEnt const drawEnt = rScnRender.m_activeToDraw[ent]; + + osp::ResId const meshRes = rImportData.m_meshes[meshImportId]; + MeshId const meshId = SysRender::own_mesh_resource(rDrawing, rDrawingRes, rResources, meshRes); + rScnRender.m_mesh[drawEnt] = rDrawing.m_meshRefCounts.ref_add(meshId); + rScnRender.m_meshDirty.push_back(drawEnt); + + int const matImportId = rImportData.m_objMaterials[objects[i]]; + + if (Magnum::Trade::MaterialData const &mat = *rImportData.m_materials.at(matImportId); + mat.types() & Magnum::Trade::MaterialType::PbrMetallicRoughness) + { + auto const& matPbr = mat.as(); + if (int const baseColor = matPbr.baseColorTexture(); + baseColor != -1) + { + osp::ResId const texRes = rImportData.m_textures[baseColor]; + TexId const texId = SysRender::own_texture_resource(rDrawing, rDrawingRes, rResources, texRes); + rScnRender.m_diffuseTex[drawEnt] = rDrawing.m_texRefCounts.ref_add(texId); + rScnRender.m_diffuseDirty.push_back(drawEnt); + } + } + + rScnRender.m_opaque.set(drawEnt.value); + rScnRender.m_visible.set(drawEnt.value); + + if (material != lgrn::id_null()) + { + rScnRender.m_materials[material].m_dirty.push_back(drawEnt); + rScnRender.m_materials[material].m_ents.set(drawEnt.value); + } + } + + ++itPfEnts; + } +} + + +void SysPrefabDraw::resync_mesh_and_material( + ACtxPrefabs& rPrefabs, + Resources& rResources, + ACtxBasic const& rBasic, + ACtxDrawing& rDrawing, + ACtxDrawingRes& rDrawingRes, + ACtxSceneRender& rScnRender, + MaterialId material) +{ + for (std::size_t const rootInt : rPrefabs.roots.ones()) + { + auto const root = ActiveEnt::from_index(rootInt); + + PrefabInstanceInfo const &rRootInfo = rPrefabs.instanceInfo[root]; + + LGRN_ASSERT(rRootInfo.prefab != lgrn::id_null()); + LGRN_ASSERT(rRootInfo.importer != lgrn::id_null()); + + auto const &rImportData = rResources.data_get(gc_importer, rRootInfo.importer); + auto const &rPrefabData = rResources.data_get (gc_importer, rRootInfo.importer); + auto const objects = lgrn::Span{rPrefabData.m_prefabs[rRootInfo.prefab]}; + + for (ActiveEnt const ent : SysSceneGraph::descendants(rBasic.m_scnGraph, root)) + { + PrefabInstanceInfo const &rInfo = rPrefabs.instanceInfo[ent]; + + int const meshImportId = rImportData.m_objMeshes[objects[rInfo.obj]]; + if (meshImportId == -1) + { + continue; + } + + SysRender::needs_draw_transforms(rBasic.m_scnGraph, rScnRender.m_needDrawTf, ent); + + DrawEnt const drawEnt = rScnRender.m_activeToDraw[ent]; + + osp::ResId const meshRes = rImportData.m_meshes[meshImportId]; + MeshId const meshId = SysRender::own_mesh_resource(rDrawing, rDrawingRes, rResources, meshRes); + rScnRender.m_mesh[drawEnt] = rDrawing.m_meshRefCounts.ref_add(meshId); + rScnRender.m_meshDirty.push_back(drawEnt); + + int const matImportId = rImportData.m_objMaterials[objects[rPrefabs.instanceInfo[ent].obj]]; + + if (Magnum::Trade::MaterialData const &mat = *rImportData.m_materials.at(matImportId); + mat.types() & Magnum::Trade::MaterialType::PbrMetallicRoughness) + { + auto const& matPbr = mat.as(); + if (int const baseColor = matPbr.baseColorTexture(); + baseColor != -1) + { + osp::ResId const texRes = rImportData.m_textures[baseColor]; + TexId const texId = SysRender::own_texture_resource(rDrawing, rDrawingRes, rResources, texRes); + rScnRender.m_diffuseTex[drawEnt] = rDrawing.m_texRefCounts.ref_add(texId); + rScnRender.m_diffuseDirty.push_back(drawEnt); + } + } + + rScnRender.m_opaque.set(drawEnt.value); + rScnRender.m_visible.set(drawEnt.value); + + if (material != lgrn::id_null()) + { + rScnRender.m_materials[material].m_dirty.push_back(drawEnt); + rScnRender.m_materials[material].m_ents.set(drawEnt.value); + } + } + } +} diff --git a/src/osp/drawing/prefab_draw.h b/src/osp/drawing/prefab_draw.h new file mode 100644 index 00000000..b3020f16 --- /dev/null +++ b/src/osp/drawing/prefab_draw.h @@ -0,0 +1,75 @@ +/** + * Open Space Program + * Copyright © 2019-2023 Open Space Program Project + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHEfR DEALINGS IN THE + * SOFTWARE. + */ +#pragma once + +#include "drawing.h" +#include "../activescene/prefab_fn.h" +#include "../core/array_view.h" + +namespace osp::draw +{ + +class SysPrefabDraw +{ + using ACtxBasic = osp::active::ACtxBasic; + using ACtxPrefabs = osp::active::ACtxPrefabs; + using ActiveEnt = osp::active::ActiveEnt; + using TmpPrefabRequest = osp::active::TmpPrefabRequest; +public: + + static void init_drawents( + ACtxPrefabs& rPrefabs, + Resources& rResources, + ACtxBasic const& rBasic, + ACtxDrawing& rDrawing, + ACtxSceneRender& rScnRender); + + static void resync_drawents( + ACtxPrefabs& rPrefabs, + Resources& rResources, + ACtxBasic const& rBasic, + ACtxDrawing& rDrawing, + ACtxSceneRender& rScnRender); + + static void init_mesh_and_material( + ACtxPrefabs& rPrefabs, + Resources& rResources, + ACtxBasic const& rBasic, + ACtxDrawing& rDrawing, + ACtxDrawingRes& rDrawingRes, + ACtxSceneRender& rScnRender, + MaterialId material = lgrn::id_null()); + + static void resync_mesh_and_material( + ACtxPrefabs& rPrefabs, + Resources& rResources, + ACtxBasic const& rBasic, + ACtxDrawing& rDrawing, + ACtxDrawingRes& rDrawingRes, + ACtxSceneRender& rScnRender, + MaterialId material = lgrn::id_null()); +}; + +} // namespace osp::active diff --git a/src/osp/drawing_gl/rendergl.cpp b/src/osp/drawing_gl/rendergl.cpp index bba5fa90..b1cd6c2d 100644 --- a/src/osp/drawing_gl/rendergl.cpp +++ b/src/osp/drawing_gl/rendergl.cpp @@ -240,7 +240,7 @@ void SysRenderGL::sync_drawent_mesh( else { OSP_LOG_WARN("No mesh data found for Mesh {} from Entity {}", - std::size_t(entMeshScnId), std::size_t(ent)); + std::size_t(entMeshScnId.value()), std::size_t(ent)); } } else @@ -290,7 +290,7 @@ void SysRenderGL::sync_drawent_texture( else { OSP_LOG_WARN("No mesh data found for Mesh {} from Entity {}", - std::size_t(entMeshScnId), std::size_t(ent)); + std::size_t(entTexScnId.value()), std::size_t(ent)); } } else @@ -383,11 +383,11 @@ void SysRenderGL::draw_group( DrawEntSet_t const& visible, ViewProjMatrix const& viewProj) { - for (auto const& [ent, toDraw] : group.view().each()) + for (auto const& [ent, toDraw] : entt::basic_view{group.entities}.each()) { if (visible.test(std::size_t(ent))) { - toDraw(ent, viewProj); + toDraw.draw(ent, viewProj, toDraw.data); } } } diff --git a/src/osp/link/machines.cpp b/src/osp/link/machines.cpp index 5524b20c..bca25a0f 100644 --- a/src/osp/link/machines.cpp +++ b/src/osp/link/machines.cpp @@ -38,51 +38,51 @@ void copy_nodes( using lgrn::Span; // Create new node IDs - for (NodeId const srcNode : rSrcNodes.m_nodeIds.bitview().zeros()) + for (NodeId const srcNode : rSrcNodes.nodeIds.bitview().zeros()) { - NodeId const dstNode = rDstNodes.m_nodeIds.create(); + NodeId const dstNode = rDstNodes.nodeIds.create(); remapNode[srcNode] = dstNode; } // Copy node-to-machine connections - rDstNodes.m_nodeToMach.ids_reserve(rDstNodes.m_nodeIds.capacity()); - rDstNodes.m_nodeToMach.data_reserve(rDstNodes.m_nodeToMach.data_size() - + rSrcNodes.m_nodeToMach.data_size()); - for (NodeId const srcNode : rSrcNodes.m_nodeIds.bitview().zeros()) + rDstNodes.nodeToMach.ids_reserve(rDstNodes.nodeIds.capacity()); + rDstNodes.nodeToMach.data_reserve(rDstNodes.nodeToMach.data_size() + + rSrcNodes.nodeToMach.data_size()); + for (NodeId const srcNode : rSrcNodes.nodeIds.bitview().zeros()) { NodeId const dstNode = remapNode[srcNode]; - Span srcJunction = rSrcNodes.m_nodeToMach[srcNode]; - rDstNodes.m_nodeToMach.emplace(dstNode, srcJunction.size()); - Span dstJuncton = rDstNodes.m_nodeToMach[dstNode]; + Span srcJunction = rSrcNodes.nodeToMach[srcNode]; + rDstNodes.nodeToMach.emplace(dstNode, srcJunction.size()); + Span dstJuncton = rDstNodes.nodeToMach[dstNode]; auto dstJuncIt = std::begin(dstJuncton); for (Junction const& srcJunc : srcJunction) { - MachTypeId const machType = srcJunc.m_type; - MachAnyId const srcMach = rSrcMach.m_perType[machType].m_localToAny[srcJunc.m_local]; + MachTypeId const machType = srcJunc.type; + MachAnyId const srcMach = rSrcMach.perType[machType].localToAny[srcJunc.local]; MachAnyId const dstMach = remapMach[srcMach]; - MachLocalId const dstLocal = rDstMach.m_machToLocal[dstMach]; + MachLocalId const dstLocal = rDstMach.machToLocal[dstMach]; - dstJuncIt->m_local = dstLocal; - dstJuncIt->m_type = machType; - dstJuncIt->m_custom = srcJunc.m_custom; + dstJuncIt->local = dstLocal; + dstJuncIt->type = machType; + dstJuncIt->custom = srcJunc.custom; std::advance(dstJuncIt, 1); } } // copy mach-to-node connections - rDstNodes.m_machToNode.ids_reserve(rDstMach.m_ids.capacity()); - rDstNodes.m_machToNode.data_reserve(rDstNodes.m_machToNode.data_size() - + rSrcNodes.m_machToNode.data_size()); - for (MachAnyId const srcMach : rSrcMach.m_ids.bitview().zeros()) + rDstNodes.machToNode.ids_reserve(rDstMach.ids.capacity()); + rDstNodes.machToNode.data_reserve(rDstNodes.machToNode.data_size() + + rSrcNodes.machToNode.data_size()); + for (MachAnyId const srcMach : rSrcMach.ids.bitview().zeros()) { - if (rSrcNodes.m_machToNode.contains(srcMach)) + if (rSrcNodes.machToNode.contains(srcMach)) { - Span srcPorts = rSrcNodes.m_machToNode[srcMach]; + Span srcPorts = rSrcNodes.machToNode[srcMach]; MachAnyId const dstMach = remapMach[srcMach]; - rDstNodes.m_machToNode.emplace(dstMach, srcPorts.size()); - Span dstPorts = rDstNodes.m_machToNode[dstMach]; + rDstNodes.machToNode.emplace(dstMach, srcPorts.size()); + Span dstPorts = rDstNodes.machToNode[dstMach]; auto dstPortIt = std::begin(dstPorts); for (NodeId const srcNode : srcPorts) diff --git a/src/osp/link/machines.h b/src/osp/link/machines.h index dc19111b..c2a744a0 100644 --- a/src/osp/link/machines.h +++ b/src/osp/link/machines.h @@ -24,14 +24,16 @@ */ #pragma once -#include "../core/global_id.h" -#include "../core/bitvector.h" #include "../core/array_view.h" +#include "../core/bitvector.h" +#include "../core/global_id.h" +#include "../core/keyed_vector.h" #include #include #include +#include #include namespace osp::link @@ -58,8 +60,8 @@ inline NodeTypeId const gc_ntSigFloat = NodeTypeReg_t::create(); */ struct PerMachType { - lgrn::IdRegistryStl m_localIds; - std::vector m_localToAny; + lgrn::IdRegistryStl localIds; + std::vector localToAny; }; /** @@ -67,33 +69,35 @@ struct PerMachType */ struct Machines { - lgrn::IdRegistryStl m_ids; + lgrn::IdRegistryStl ids; - std::vector m_machTypes; - std::vector m_machToLocal; + std::vector machTypes; + std::vector machToLocal; - std::vector m_perType; + std::vector perType; }; -struct UpdMachPerType +struct MachineUpdater { - BitVector_t m_machTypesDirty; + alignas(64) std::atomic requestMachineUpdateLoop {false}; + + BitVector_t machTypesDirty; // [MachTypeId][MachLocalId] - std::vector m_localDirty; + osp::KeyedVec localDirty; }; struct MachinePair { - MachLocalId m_local{lgrn::id_null()}; - MachTypeId m_type{lgrn::id_null()}; + MachLocalId local {lgrn::id_null()}; + MachTypeId type {lgrn::id_null()}; }; struct Junction { - MachLocalId m_local{lgrn::id_null()}; - MachTypeId m_type{lgrn::id_null()}; - JuncCustom m_custom{0}; + MachLocalId local {lgrn::id_null()}; + MachTypeId type {lgrn::id_null()}; + JuncCustom custom {0}; }; /** @@ -106,22 +110,22 @@ struct Nodes using NodeToMach_t = lgrn::IntArrayMultiMap; using MachToNode_t = lgrn::IntArrayMultiMap; - lgrn::IdRegistryStl m_nodeIds; + lgrn::IdRegistryStl nodeIds; // Node-to-Machine connections // [NodeId][JunctionIndex] -> Junction (type, MachLocalId, custom int) - NodeToMach_t m_nodeToMach; + NodeToMach_t nodeToMach; // Corresponding Machine-to-Node connections // [MachAnyId][PortIndex] -> NodeId - MachToNode_t m_machToNode; + MachToNode_t machToNode; }; struct PortEntry { - NodeTypeId m_type; - PortId m_port; - JuncCustom m_custom; + NodeTypeId type; + PortId port; + JuncCustom custom; }; inline NodeId connected_node(lgrn::Span portSpan, PortId port) noexcept diff --git a/src/osp/link/signal.h b/src/osp/link/signal.h index e60d96f2..cb4e0fd5 100644 --- a/src/osp/link/signal.h +++ b/src/osp/link/signal.h @@ -38,16 +38,16 @@ using SignalValues_t = std::vector; template struct UpdateNodes { - BitVector_t m_nodeDirty; - SignalValues_t m_nodeNewValues; + BitVector_t nodeDirty; + SignalValues_t nodeNewValues; - bool m_dirty{false}; + bool dirty{false}; void assign(NodeId node, VALUE_T value) { - m_dirty = true; - m_nodeDirty.set(node); - m_nodeNewValues[node] = std::forward(value); + dirty = true; + nodeDirty.set(node); + nodeNewValues[node] = std::forward(value); } }; @@ -58,7 +58,7 @@ bool update_signal_nodes( Machines const& machines, ArrayView newValues, ArrayView currentValues, - UpdMachPerType& rUpdMach) + MachineUpdater& rUpdMach) { bool somethingNotified = false; @@ -70,15 +70,15 @@ bool update_signal_nodes( // Notify connected inputs for (Junction junc : nodeToMach[node]) { - if (junc.m_custom == gc_sigIn) + if (junc.custom == gc_sigIn) { somethingNotified = true; // A machine of type "junc.m_type" has new values to read - rUpdMach.m_machTypesDirty.set(junc.m_type); + rUpdMach.machTypesDirty.set(junc.type); // Specify using local Id on which machine needs to update - rUpdMach.m_localDirty[junc.m_type].set(junc.m_local); + rUpdMach.localDirty[junc.type].set(junc.local); } } } diff --git a/src/osp/tasks/execute.cpp b/src/osp/tasks/execute.cpp index 04411746..2b5fadb9 100644 --- a/src/osp/tasks/execute.cpp +++ b/src/osp/tasks/execute.cpp @@ -289,6 +289,7 @@ static int pipeline_run(Tasks const& tasks, TaskGraph const& graph, ExecContext if (isLoopScope) { + LGRN_ASSERT(rExecPl.loopChildrenLeft == 0); rExecPl.loopChildrenLeft = scopeChildCount; return 0; @@ -322,39 +323,43 @@ static void loop_scope_done(Tasks const& tasks, TaskGraph const& graph, ExecCont { ExecPipeline &rLoopExecPl = rExec.plData[loopPipeline]; - LGRN_ASSERT(rLoopExecPl.running == true); LGRN_ASSERT(rLoopExecPl.stage == lgrn::id_null()); + if (rLoopExecPl.running) + { + LGRN_ASSERT(rExec.pipelinesRunning != 0); + -- rExec.pipelinesRunning; + } + rLoopExecPl.running = false; rLoopExecPl.canceled = false; rLoopExecPl.loop = false; - LGRN_ASSERT(rExec.pipelinesRunning != 0); - -- rExec.pipelinesRunning; exec_log(rExec, ExecContext::PipelineLoopFinish{loopPipeline}); }); // If this is a nested loop, decrement parent loop scope's loopChildrenLeft - PipelineId const parentScopePl = tasks.m_pipelineParents[pipeline]; - if (parentScopePl == lgrn::id_null()) + PipelineId const parentPl = tasks.m_pipelineParents[pipeline]; + if (parentPl == lgrn::id_null()) { return; // Loop is in the root } - PipelineTreePos_t const parentScopeTreePos = graph.pipelineToLoopScope[parentScopePl]; + PipelineTreePos_t const parentScopeTreePos = graph.pipelineToLoopScope[parentPl]; if (parentScopeTreePos == lgrn::id_null()) { return; // Parent does not loop } + PipelineId const parentScopePl = graph.pltreeToPipeline[parentScopeTreePos]; ExecPipeline &rParentScopeExecPl = rExec.plData[parentScopePl]; LGRN_ASSERT(rParentScopeExecPl.loopChildrenLeft != 0); -- rParentScopeExecPl.loopChildrenLeft; - if (rParentScopeExecPl.loopChildrenLeft) + if (rParentScopeExecPl.loopChildrenLeft == 0) { loop_scope_done(tasks, graph, rExec, rParentScopeExecPl, parentScopePl, parentScopeTreePos); } diff --git a/src/osp/vehicles/load_tinygltf.cpp b/src/osp/vehicles/load_tinygltf.cpp index b8074214..17dcc2e3 100644 --- a/src/osp/vehicles/load_tinygltf.cpp +++ b/src/osp/vehicles/load_tinygltf.cpp @@ -355,7 +355,7 @@ static EShape shape_from_name(std::string_view name) noexcept if (name == "cube") { return EShape::Box; } else if (name == "cylinder") { return EShape::Cylinder; } - OSP_LOG_WARN("Unknown shape: {}" name); + OSP_LOG_WARN("Unknown shape: {}", name); return EShape::None; } diff --git a/src/testapp/enginetest.cpp b/src/testapp/enginetest.cpp index 8cb7664a..aaecceca 100644 --- a/src/testapp/enginetest.cpp +++ b/src/testapp/enginetest.cpp @@ -184,8 +184,7 @@ void update_test_scene(EngineTestScene& rScene, float const delta) rScene.m_matPhongDirty.clear(); // Rotate the cube - osp::Matrix4 &rCubeTf - = rScene.m_basic.m_transform.get(rScene.m_cube).m_transform; + osp::Matrix4 &rCubeTf = rScene.m_basic.m_transform.get(rScene.m_cube).m_transform; rCubeTf = Magnum::Matrix4::rotationZ(90.0_degf * delta) * rCubeTf; } @@ -244,7 +243,7 @@ void sync_test_scene( sync_drawent_phong(rScene.m_matPhongDirty.cbegin(), rScene.m_matPhongDirty.cend(), { .hasMaterial = rScene.m_matPhong, - .pStorageOpaque = &rRenderer.m_groupFwdOpaque.m_entities, + .pStorageOpaque = &rRenderer.m_groupFwdOpaque.entities, .opaque = rScene.m_scnRdr.m_opaque, .transparent = rScene.m_scnRdr.m_transparent, .diffuse = rRenderer.m_sceneRenderGL.m_diffuseTexId, @@ -278,11 +277,13 @@ void sync_test_scene( auto drawTfDirty = {rScene.m_cube}; SysRender::update_draw_transforms( - rScene.m_basic.m_scnGraph, - rScene.m_scnRdr.m_activeToDraw, - rScene.m_basic.m_transform, - rScene.m_scnRdr.m_drawTransform, - rScene.m_scnRdr.m_needDrawTf, + { + .scnGraph = rScene.m_basic .m_scnGraph, + .transforms = rScene.m_basic .m_transform, + .activeToDraw = rScene.m_scnRdr.m_activeToDraw, + .needDrawTf = rScene.m_scnRdr.m_needDrawTf, + .rDrawTf = rScene.m_scnRdr.m_drawTransform + }, drawTfDirty.begin(), drawTfDirty.end()); } @@ -363,7 +364,7 @@ class EngineTestApp : public IOspApplication RenderGL &m_rRenderGl; }; -MagnumApplication::AppPtr_t generate_draw_func(EngineTestScene& rScene, MagnumApplication &rApp, RenderGL& rRenderGl, UserInputHandler& rUserInput) +MagnumApplication::AppPtr_t generate_osp_magnum_app(EngineTestScene& rScene, MagnumApplication &rApp, RenderGL& rRenderGl, UserInputHandler& rUserInput) { using namespace osp::active; using namespace osp::draw; diff --git a/src/testapp/enginetest.h b/src/testapp/enginetest.h index c1a29461..38438315 100644 --- a/src/testapp/enginetest.h +++ b/src/testapp/enginetest.h @@ -44,17 +44,9 @@ struct EngineTestScene; entt::any setup_scene(osp::Resources& rResources, osp::PkgId pkg); /** - * @brief Generate MagnumApplication draw function - * - * This draw function stores renderer data, and is responsible for updating - * and drawing the engine test scene. - * - * @param rScene [ref] Engine test scene. Must be in stable memory. - * @param rApp [ref] Existing MagnumApplication to use GL resources of - * - * @return MagnumApplication draw function + * @brief Generate IOspApplication for MagnumApplication */ -MagnumApplication::AppPtr_t generate_draw_func(EngineTestScene& rScene, MagnumApplication& rApp, osp::draw::RenderGL& rRenderGl, osp::input::UserInputHandler& rUserInput); +MagnumApplication::AppPtr_t generate_osp_magnum_app(EngineTestScene& rScene, MagnumApplication& rApp, osp::draw::RenderGL& rRenderGl, osp::input::UserInputHandler& rUserInput); } // namespace testapp::enginetest diff --git a/src/testapp/identifiers.h b/src/testapp/identifiers.h index 0069e98a..88fc2d1e 100644 --- a/src/testapp/identifiers.h +++ b/src/testapp/identifiers.h @@ -114,6 +114,16 @@ enum class EStgFBO OSP_DECLARE_STAGE_NAMES(EStgFBO, "Bind", "Draw", "Unbind"); OSP_DECLARE_STAGE_NO_SCHEDULE(EStgFBO); + +enum class EStgLink +{ + ScheduleLink, + NodeUpd, + MachUpd +}; +OSP_DECLARE_STAGE_NAMES(EStgLink, "Schedule", "NodeUpd", "MachUpd"); +OSP_DECLARE_STAGE_SCHEDULE(EStgLink, EStgLink::ScheduleLink); + //----------------------------------------------------------------------------- inline void register_stage_enums() @@ -125,6 +135,7 @@ inline void register_stage_enums() osp::PipelineInfo::register_stage_enum(); osp::PipelineInfo::register_stage_enum(); osp::PipelineInfo::register_stage_enum(); + osp::PipelineInfo::register_stage_enum(); } using osp::PipelineDef; @@ -144,7 +155,7 @@ struct PlApplication idDeltaTimeIn struct PlScene { - PipelineDef cleanup {"cleanup - Scene cleanup before destruction"}; + PipelineDef cleanup {"cleanup - Scene cleanup before destruction"}; PipelineDef update {"update"}; }; @@ -152,12 +163,12 @@ struct PlScene idBasic, idDrawing, idDrawingRes, idActiveEntDel, idDrawEntDel, idNMesh struct PlCommonScene { - PipelineDef activeEnt {"activeEnt - ActiveEnt ID Registry"}; - PipelineDef activeEntResized {"activeEntResized"}; - PipelineDef activeEntDelete {"activeEntDelete - Vector of ActiveEnts that need to be deleted"}; + PipelineDef activeEnt {"activeEnt - ACtxBasic::m_activeIds"}; + PipelineDef activeEntResized {"activeEntResized - ACtxBasic::m_activeIds option to resize"}; + PipelineDef activeEntDelete {"activeEntDelete - idActiveEntDel, vector of ActiveEnts that need to be deleted"}; - PipelineDef transform {"transform"}; - PipelineDef hierarchy {"hierarchy"}; + PipelineDef transform {"transform - ACtxBasic::m_transform"}; + PipelineDef hierarchy {"hierarchy - ACtxBasic::m_scnGraph"}; }; @@ -171,11 +182,11 @@ struct PlPhysics -#define TESTAPP_DATA_SHAPE_SPAWN 1, \ - idSpawner -struct PlShapeSpawn +#define TESTAPP_DATA_PHYS_SHAPES 1, \ + idPhysShapes +struct PlPhysShapes { - PipelineDef spawnRequest {"spawnRequest"}; + PipelineDef spawnRequest {"spawnRequest - Spawned shapes"}; PipelineDef spawnedEnts {"spawnedEnts"}; PipelineDef ownedEnts {"ownedEnts"}; }; @@ -183,11 +194,17 @@ struct PlShapeSpawn #define TESTAPP_DATA_PREFABS 1, \ - idPrefabInit -#define OSP_TAGS_TESTAPP_PREFABS 7, \ - tgPrefabMod, tgPrefabReq, tgPrefabClr, \ - tgPrefabEntMod, tgPrefabEntReq, \ - tgPfParentHierMod, tgPfParentHierReq + idPrefabs +struct PlPrefabs +{ + PipelineDef spawnRequest {"spawnRequest"}; + PipelineDef spawnedEnts {"spawnedEnts"}; + PipelineDef ownedEnts {"ownedEnts"}; + + PipelineDef instanceInfo {"instanceInfo"}; + + PipelineDef inSubtree {"inSubtree"}; +}; @@ -201,60 +218,75 @@ struct PlBounds -#define TESTAPP_DATA_PARTS 6, \ - idScnParts, idPartInit, idUpdMach, idMachEvtTags, idMachUpdEnqueue, idtgNodeUpdEvt -#define OSP_TAGS_TESTAPP_PARTS 17, \ - tgPartMod, tgPartReq, tgPartClr, \ - tgMapPartEntMod, tgMapPartEntReq, \ - tgWeldMod, tgWeldReq, tgWeldClr, \ - tgLinkMod, tgLinkReq, \ - tgLinkMhUpdMod, tgLinkMhUpdReq, \ - tgNodeAnyUpdMod, tgNodeAnyUpdReq, \ - tgMachUpdEnqMod, tgMachUpdEnqReq, tgNodeUpdEvt +#define TESTAPP_DATA_PARTS 2, \ + idScnParts, idUpdMach +struct PlParts +{ + PipelineDef partIds {"partIds - ACtxParts::partIds"}; + PipelineDef partPrefabs {"partPrefabs - ACtxParts::partPrefabs"}; + PipelineDef partTransformWeld {"partTransformWeld - ACtxParts::partTransformWeld"}; + PipelineDef partDirty {"partDirty - ACtxParts::partDirty"}; + + PipelineDef weldIds {"weldIds - ACtxParts::weldIds"}; + PipelineDef weldDirty {"weldDirty - ACtxParts::weldDirty"}; + + PipelineDef machIds {"machIds - ACtxParts::machines.ids"}; + PipelineDef nodeIds {"nodeIds - ACtxParts::nodePerType[*].nodeIds"}; + PipelineDef connect {"connect - ACtxParts::nodePerType[*].nodeToMach/machToNode"}; + + PipelineDef mapWeldPart {"mapPartWeld - ACtxParts::weldToParts/partToWeld"}; + PipelineDef mapPartMach {"mapPartMach - ACtxParts::partToMachines/machineToPart"}; + PipelineDef mapPartActive {"mapPartActive - ACtxParts::partToActive/activeToPart"}; + PipelineDef mapWeldActive {"mapWeldActive - ACtxParts::weldToActive"}; + + PipelineDef machUpdExtIn {"machUpdExtIn -"}; + + PipelineDef linkLoop {"linkLoop - Link update loop"}; +}; #define TESTAPP_DATA_VEHICLE_SPAWN 1, \ idVehicleSpawn -#define OSP_TAGS_TESTAPP_VEHICLE_SPAWN 11, \ - tgVsBasicInMod, tgVsBasicInReq, tgVsBasicInClr, \ - tgVsPartMod, tgVsPartReq, \ - tgVsMapPartMachMod, tgVsMapPartMachReq, \ - tgVsWeldMod, tgVsWeldReq, \ - tgVsPartPfMod, tgVsPartPfReq +struct PlVehicleSpawn +{ + PipelineDef spawnRequest {"spawnRequest - ACtxVehicleSpawn::spawnRequest"}; + PipelineDef spawnedParts {"spawnedParts - ACtxVehicleSpawn::spawnedPart*"}; + PipelineDef spawnedWelds {"spawnedWelds - ACtxVehicleSpawn::spawnedWeld*"}; + PipelineDef rootEnts {"rootEnts - ACtxVehicleSpawn::rootEnts"}; + PipelineDef spawnedMachs {"spawnedMachs - ACtxVehicleSpawn::spawnedMachs"}; +}; #define TESTAPP_DATA_VEHICLE_SPAWN_VB 1, \ idVehicleSpawnVB -#define OSP_TAGS_TESTAPP_VEHICLE_SPAWN_VB 10, \ - tgVbSpBasicInMod, tgVbSpBasicInReq, \ - tgVbPartMod, tgVbPartReq, \ - tgVbWeldMod, tgVbWeldReq, \ - tgVbMachMod, tgVbMachReq, \ - tgVbNodeMod, tgVbNodeReq +struct PlVehicleSpawnVB +{ + PipelineDef dataVB {"dataVB - ACtxVehicleSpawnVB::dataVB"}; + PipelineDef remapParts {"remapParts - ACtxVehicleSpawnVB::remapPart*"}; + PipelineDef remapWelds {"remapWelds - ACtxVehicleSpawnVB::remapWeld*"}; + PipelineDef remapMachs {"remapMachs - ACtxVehicleSpawnVB::remapMach*"}; + PipelineDef remapNodes {"remapNodes - ACtxVehicleSpawnVB::remapNode*"}; +}; + #define TESTAPP_DATA_TEST_VEHICLES 1, \ - idTVPartVehicle + idPrebuiltVehicles #define TESTAPP_DATA_SIGNALS_FLOAT 2, \ idSigValFloat, idSigUpdFloat -#define OSP_TAGS_TESTAPP_SIGNALS_FLOAT 5, \ - tgSigFloatLinkMod, tgSigFloatLinkReq, \ - tgSigFloatUpdMod, tgSigFloatUpdReq, tgSigFloatUpdEvt \ - - +struct PlSignalsFloat +{ + PipelineDef sigFloatValues {"sigFloatValues -"}; + PipelineDef sigFloatUpdExtIn {"sigFloatUpdExtIn -"}; + PipelineDef sigFloatUpdLoop {"sigFloatUpdLoop -"}; +}; -#define TESTAPP_DATA_MACH_ROCKET 1, \ - idDummy -#define OSP_TAGS_TESTAPP_MACH_ROCKET 1, \ - tgMhRocketEvt -#define OSP_TAGS_TESTAPP_MACH_RCSDRIVER 1, \ - tgMhRcsDriverEvt #define TESTAPP_DATA_NEWTON 1, \ idNwt @@ -273,12 +305,6 @@ struct PlNewton -#define OSP_TAGS_TESTAPP_VEHICLE_SPAWN_NWT 4, \ - tgNwtVhWeldEntMod, tgNwtVhWeldEntReq, \ - tgNwtVhHierMod, tgNwtVhHierReq - - - #define TESTAPP_DATA_ROCKETS_NWT 1, \ idRocketsNwt @@ -291,7 +317,7 @@ struct PlNewton idUniverse, tgUniDeltaTimeIn struct PlUniCore { - PipelineDef update {"update - Universe update"}; + PipelineDef update {"update - Universe update"}; PipelineDef transfer {"transfer"}; }; @@ -317,21 +343,21 @@ struct PlWindowApp PipelineDef sync {"sync"}; PipelineDef resync {"resync"}; - PipelineDef cleanup {"cleanup - Cleanup renderer resources before destruction"}; + PipelineDef cleanup {"cleanup - Cleanup renderer resources before destruction"}; }; -#define TESTAPP_DATA_SCENE_RENDERER 1, \ - idScnRender +#define TESTAPP_DATA_SCENE_RENDERER 2, \ + idScnRender, idDrawTfObservers struct PlSceneRenderer { - PipelineDef render {"render"}; + PipelineDef render {"render - "}; - PipelineDef drawEnt {"drawEnt"}; - PipelineDef drawEntResized {"drawEntResized"}; - PipelineDef drawEntDelete {"drawEntDelete - Vector of DrawEnts that need to be deleted"}; + PipelineDef drawEnt {"drawEnt - "}; + PipelineDef drawEntResized {"drawEntResized - "}; + PipelineDef drawEntDelete {"drawEntDelete - Vector of DrawEnts that need to be deleted"}; PipelineDef entTextureDirty {"entTextureDirty"}; PipelineDef entMeshDirty {"entMeshDirty"}; @@ -410,7 +436,9 @@ struct PlCameraCtrl #define TESTAPP_DATA_VEHICLE_CONTROL 1, \ idVhControls -#define OSP_TAGS_TESTAPP_VEHICLE_CONTROL 2, \ - tgSelUsrCtrlMod, tgSelUsrCtrlReq +struct PlVehicleCtrl +{ + PipelineDef selectedVehicle {"selectedVehicle"}; +}; } // namespace testapp diff --git a/src/testapp/scenarios.cpp b/src/testapp/scenarios.cpp index ae0034fb..14afef6b 100644 --- a/src/testapp/scenarios.cpp +++ b/src/testapp/scenarios.cpp @@ -28,15 +28,19 @@ #include "identifiers.h" #include "sessions/common.h" -#include "sessions/physics.h" +#include "sessions/magnum.h" #include "sessions/misc.h" #include "sessions/newton.h" -#include "sessions/magnum.h" +#include "sessions/physics.h" +#include "sessions/shapes.h" #include "sessions/universe.h" -//#include "sessions/vehicles.h" +#include "sessions/vehicles.h" +#include "sessions/vehicles_machines.h" +#include "sessions/vehicles_prebuilt.h" #include "MagnumApplication.h" -//#include "../VehicleBuilder.h" + +#include #include #include @@ -47,137 +51,22 @@ #include +using namespace adera; using namespace osp; +using namespace osp::active; namespace testapp { +static void setup_magnum_draw(TestApp& rTestApp, Session const& scene, Session const& sceneRenderer, Session const& magnumScene); + +// MaterialIds hints which shaders should be used to draw a DrawEnt +// DrawEnts can be assigned to multiple materials static constexpr auto sc_matVisualizer = draw::MaterialId(0); static constexpr auto sc_matFlat = draw::MaterialId(1); static constexpr auto sc_matPhong = draw::MaterialId(2); static constexpr int sc_materialCount = 4; -struct MainLoopSignals -{ - PipelineId mainLoop; - PipelineId inputs; - PipelineId renderSync; - PipelineId renderResync; - PipelineId sceneUpdate; - PipelineId sceneRender; -}; - -class CommonMagnumApp : public IOspApplication -{ -public: - CommonMagnumApp(TestApp &rTestApp, MainLoopControl &rMainLoopCtrl, MainLoopSignals signals) noexcept - : m_rTestApp { rTestApp } - , m_rMainLoopCtrl { rMainLoopCtrl } - , m_signals { signals } - { } - - void run(MagnumApplication& rApp) override - { - // Start the main loop - - PipelineId const mainLoop = m_rTestApp.m_application.get_pipelines().mainLoop; - m_rTestApp.m_pExecutor->run(m_rTestApp, mainLoop); - - // Resyncronize renderer - - m_rMainLoopCtrl = MainLoopControl{ - .doUpdate = false, - .doSync = true, - .doResync = true, - .doRender = false, - }; - - signal_all(); - - m_rTestApp.m_pExecutor->wait(m_rTestApp); - } - - void draw(MagnumApplication& rApp, float delta) override - { - // Magnum Application's main loop calls this - - m_rMainLoopCtrl = MainLoopControl{ - .doUpdate = true, - .doSync = true, - .doResync = false, - .doRender = true, - }; - - signal_all(); - - m_rTestApp.m_pExecutor->wait(m_rTestApp); - } - - void exit(MagnumApplication& rApp) override - { - m_rMainLoopCtrl = MainLoopControl{ - .doUpdate = false, - .doSync = false, - .doResync = false, - .doRender = false, - }; - - signal_all(); - - m_rTestApp.m_pExecutor->wait(m_rTestApp); - - if (m_rTestApp.m_pExecutor->is_running(m_rTestApp)) - { - // Main loop must have stopped, but didn't! - m_rTestApp.m_pExecutor->wait(m_rTestApp); - std::abort(); - } - } - -private: - - void signal_all() - { - m_rTestApp.m_pExecutor->signal(m_rTestApp, m_signals.mainLoop); - m_rTestApp.m_pExecutor->signal(m_rTestApp, m_signals.inputs); - m_rTestApp.m_pExecutor->signal(m_rTestApp, m_signals.renderSync); - m_rTestApp.m_pExecutor->signal(m_rTestApp, m_signals.renderResync); - m_rTestApp.m_pExecutor->signal(m_rTestApp, m_signals.sceneUpdate); - m_rTestApp.m_pExecutor->signal(m_rTestApp, m_signals.sceneRender); - } - - TestApp &m_rTestApp; - MainLoopControl &m_rMainLoopCtrl; - - MainLoopSignals m_signals; -}; - -static void setup_magnum_draw(TestApp& rTestApp, Session const& scene, Session const& sceneRenderer, Session const& magnumScene) -{ - OSP_DECLARE_GET_DATA_IDS(rTestApp.m_application, TESTAPP_DATA_APPLICATION); - OSP_DECLARE_GET_DATA_IDS(sceneRenderer, TESTAPP_DATA_SCENE_RENDERER); - OSP_DECLARE_GET_DATA_IDS(rTestApp.m_magnum, TESTAPP_DATA_MAGNUM); - OSP_DECLARE_GET_DATA_IDS(magnumScene, TESTAPP_DATA_MAGNUM_SCENE); - - auto &rMainLoopCtrl = top_get (rTestApp.m_topData, idMainLoopCtrl); - auto &rActiveApp = top_get(rTestApp.m_topData, idActiveApp); - auto &rCamera = top_get (rTestApp.m_topData, idCamera); - - rCamera.set_aspect_ratio(Vector2{Magnum::GL::defaultFramebuffer.viewport().size()}); - - MainLoopSignals const signals - { - .mainLoop = rTestApp.m_application .get_pipelines() .mainLoop, - .inputs = rTestApp.m_windowApp .get_pipelines() .inputs, - .renderSync = rTestApp.m_windowApp .get_pipelines() .sync, - .renderResync = rTestApp.m_windowApp .get_pipelines() .resync, - .sceneUpdate = scene .get_pipelines() .update, - .sceneRender = sceneRenderer .get_pipelines() .render, - }; - - rActiveApp.set_osp_app( std::make_unique(rTestApp, rMainLoopCtrl, signals) ); -} - static ScenarioMap_t make_scenarios() { ScenarioMap_t scenarioMap; @@ -189,22 +78,35 @@ static ScenarioMap_t make_scenarios() scenarioMap.emplace(name, ScenarioOption{desc, run}); }; - add_scenario("enginetest", "Basic game engine and drawing scenario (without using TopTasks)", + add_scenario("enginetest", "Simple rotating cube scenario without using Pipelines/Tasks", [] (TestApp& rTestApp) -> RendererSetupFunc_t { - SessionGroup &rOut = rTestApp.m_scene; - rOut.m_sessions.resize(1); - TopDataId const idSceneData = rOut.m_sessions[0].acquire_data<1>(rTestApp.m_topData)[0]; - + // Declares idResources TopDataId variable, obtained from Session m_application.m_data[0]. + // + // This macro expands to: + // + // auto const [idResources, idMainLoopCtrl] = osp::unpack<2>(rTestApp.m_application.m_data); + // + // TopDataIds can be used to access rTestApp.m_topData. The purpose of TopData is to store + // all data in a safe, type-erased, and addressable manner that can be easily accessed by + // Tasks. This also allow reserving IDs before instantiation (which cannot be achieved + // with (smart) pointers alone) OSP_DECLARE_GET_DATA_IDS(rTestApp.m_application, TESTAPP_DATA_APPLICATION); - auto &rResources = top_get(rTestApp.m_topData, idResources); - // enginetest::setup_scene returns an entt::any containing one big - // struct containing all the scene data. + // Create 1 unnamed session as m_sessions[0], reserving 1 TopDataId as idSceneData + rTestApp.m_scene.m_sessions.resize(1); + TopDataId const idSceneData = rTestApp.m_scene.m_sessions[0].acquire_data<1>(rTestApp.m_topData)[0]; + + // Create scene, store it in rTestApp.m_topData[idSceneData]. + // enginetest::setup_scene returns an entt::any containing one big EngineTestScene struct + // containing all the scene data: a spinning cube. top_assign(rTestApp.m_topData, idSceneData, enginetest::setup_scene(rResources, rTestApp.m_defaultPkg)); - return [] (TestApp& rTestApp) + // Called when the MagnumApplication / window is opened, called again if the window is + // re-opened after its closed. Closing the window completely destructs MagnumApplication + // and all GPU resources. EngineTestScene will remain untouched in the background. + RendererSetupFunc_t const setup_renderer = [] (TestApp& rTestApp) -> void { TopDataId const idSceneData = rTestApp.m_scene.m_sessions[0].m_data[0]; auto &rScene = top_get(rTestApp.m_topData, idSceneData); @@ -215,15 +117,19 @@ static ScenarioMap_t make_scenarios() auto &rRenderGl = top_get< draw::RenderGL > (rTestApp.m_topData, idRenderGl); auto &rUserInput = top_get< input::UserInputHandler >(rTestApp.m_topData, idUserInput); - // Renderer state is stored as lambda capture - rActiveApp.set_osp_app(enginetest::generate_draw_func(rScene, rActiveApp, rRenderGl, rUserInput)); + // This creates the renderer actually updates and draws the scene. + rActiveApp.set_osp_app(enginetest::generate_osp_magnum_app(rScene, rActiveApp, rRenderGl, rUserInput)); }; + + return setup_renderer; }); + static constexpr auto sc_gravityForce = Vector3{0.0f, 0.0f, -9.81f}; + add_scenario("physics", "Newton Dynamics integration test scenario", [] (TestApp& rTestApp) -> RendererSetupFunc_t { - #define SCENE_SESSIONS scene, commonScene, physics, shapeSpawn, droppers, bounds, newton, nwtGravSet, nwtGrav, shapeSpawnNwt + #define SCENE_SESSIONS scene, commonScene, physics, physShapes, droppers, bounds, newton, nwtGravSet, nwtGrav, physShapesNwt #define RENDERER_SESSIONS sceneRenderer, magnumScene, cameraCtrl, cameraFree, shVisual, shFlat, shPhong, camThrow, shapeDraw, cursor using namespace testapp::scenes; @@ -240,18 +146,18 @@ static ScenarioMap_t make_scenarios() scene = setup_scene (builder, rTopData, application); commonScene = setup_common_scene (builder, rTopData, scene, application, defaultPkg); physics = setup_physics (builder, rTopData, scene, commonScene); - shapeSpawn = setup_shape_spawn (builder, rTopData, scene, commonScene, physics, sc_matPhong); - droppers = setup_droppers (builder, rTopData, scene, commonScene, shapeSpawn); - bounds = setup_bounds (builder, rTopData, scene, commonScene, shapeSpawn); + physShapes = setup_phys_shapes (builder, rTopData, scene, commonScene, physics, sc_matPhong); + droppers = setup_droppers (builder, rTopData, scene, commonScene, physShapes); + bounds = setup_bounds (builder, rTopData, scene, commonScene, physShapes); newton = setup_newton (builder, rTopData, scene, commonScene, physics); nwtGravSet = setup_newton_factors (builder, rTopData); - nwtGrav = setup_newton_force_accel (builder, rTopData, newton, nwtGravSet, Vector3{0.0f, 0.0f, -9.81f}); - shapeSpawnNwt = setup_shape_spawn_newton (builder, rTopData, commonScene, physics, shapeSpawn, newton, nwtGravSet); + nwtGrav = setup_newton_force_accel (builder, rTopData, newton, nwtGravSet, sc_gravityForce); + physShapesNwt = setup_phys_shapes_newton (builder, rTopData, commonScene, physics, physShapes, newton, nwtGravSet); - add_floor(rTopData, shapeSpawn, sc_matVisualizer, defaultPkg, 4); + add_floor(rTopData, physShapes, sc_matVisualizer, defaultPkg, 4); - return [] (TestApp& rTestApp) + RendererSetupFunc_t const setup_renderer = [] (TestApp& rTestApp) -> void { auto const application = rTestApp.m_application; auto const windowApp = rTestApp.m_windowApp; @@ -273,8 +179,8 @@ static ScenarioMap_t make_scenarios() shVisual = setup_shader_visualizer (builder, rTopData, windowApp, sceneRenderer, magnum, magnumScene, sc_matVisualizer); shFlat = setup_shader_flat (builder, rTopData, windowApp, sceneRenderer, magnum, magnumScene, sc_matFlat); shPhong = setup_shader_phong (builder, rTopData, windowApp, sceneRenderer, magnum, magnumScene, sc_matPhong); - camThrow = setup_thrower (builder, rTopData, windowApp, cameraCtrl, shapeSpawn); - shapeDraw = setup_shape_spawn_draw (builder, rTopData, windowApp, sceneRenderer, commonScene, physics, shapeSpawn); + camThrow = setup_thrower (builder, rTopData, windowApp, cameraCtrl, physShapes); + shapeDraw = setup_phys_shapes_draw (builder, rTopData, windowApp, sceneRenderer, commonScene, physics, physShapes); cursor = setup_cursor (builder, rTopData, application, sceneRenderer, cameraCtrl, commonScene, sc_matFlat, rTestApp.m_defaultPkg); setup_magnum_draw(rTestApp, scene, sceneRenderer, magnumScene); @@ -282,114 +188,119 @@ static ScenarioMap_t make_scenarios() #undef SCENE_SESSIONS #undef RENDERER_SESSIONS - }); - -#if 0 + return setup_renderer; + }); add_scenario("vehicles", "Physics scenario but with Vehicles", - [] (MainView mainView, Sessions_t& sceneOut) -> RendererSetup_t + [] (TestApp& rTestApp) -> RendererSetupFunc_t { + #define SCENE_SESSIONS scene, commonScene, physics, physShapes, droppers, bounds, newton, nwtGravSet, nwtGrav, physShapesNwt, \ + prefabs, parts, vehicleSpawn, signalsFloat, \ + vehicleSpawnVB, vehicleSpawnRgd, vehicleSpawnNwt, \ + testVehicles, machRocket, machRcsDriver, nwtRocketSet, rocketsNwt + #define RENDERER_SESSIONS sceneRenderer, magnumScene, cameraCtrl, shVisual, shFlat, shPhong, camThrow, shapeDraw, cursor, \ + prefabDraw, vehicleDraw, vehicleCtrl, cameraVehicle, thrustIndicator + using namespace testapp::scenes; - using namespace osp::active; - - auto const idResources = mainView.m_idResources; - auto &rTopData = mainView.m_topData; - auto &rTags = mainView.m_rTags; - Builder_t builder{rTags, mainView.m_rTasks, mainView.m_rTaskData}; - - auto & - [ - commonScene, matVisual, physics, shapeSpawn, - prefabs, parts, - vehicleSpawn, vehicleSpawnVB, vehicleSpawnRgd, - signalsFloat, machRocket, machRcsDriver, - testVehicles, droppers, gravity, bounds, thrower, - newton, nwtGravSet, nwtGrav, shapeSpawnNwt, vehicleSpawnNwt, nwtRocketSet, rocketsNwt - ] = resize_then_unpack<24>(sceneOut); - - commonScene = setup_common_scene (builder, rTopData, rTags, idResources, mainView.m_defaultPkg); - matVisual = setup_material (builder, rTopData, rTags, commonScene); - physics = setup_physics (builder, rTopData, rTags, commonScene); - shapeSpawn = setup_shape_spawn (builder, rTopData, rTags, commonScene, physics, matVisual); - prefabs = setup_prefabs (builder, rTopData, rTags, commonScene, physics, matVisual, idResources); - parts = setup_parts (builder, rTopData, rTags, commonScene, idResources); - signalsFloat = setup_signals_float (builder, rTopData, rTags, commonScene, parts); - vehicleSpawn = setup_vehicle_spawn (builder, rTopData, rTags, commonScene); - vehicleSpawnVB = setup_vehicle_spawn_vb (builder, rTopData, rTags, commonScene, prefabs, parts, vehicleSpawn, signalsFloat, idResources); - machRocket = setup_mach_rocket (builder, rTopData, rTags, commonScene, parts, signalsFloat); - machRcsDriver = setup_mach_rcsdriver (builder, rTopData, rTags, commonScene, parts, signalsFloat); - testVehicles = setup_test_vehicles (builder, rTopData, rTags, commonScene, idResources); - droppers = setup_droppers (builder, rTopData, rTags, commonScene, shapeSpawn); - bounds = setup_bounds (builder, rTopData, rTags, commonScene, physics, shapeSpawn); - - newton = setup_newton (builder, rTopData, rTags, commonScene, physics); - nwtGravSet = setup_newton_factors (builder, rTopData, rTags); - nwtGrav = setup_newton_force_accel (builder, rTopData, rTags, newton, nwtGravSet, Vector3{0.0f, 0.0f, -9.81f}); - shapeSpawnNwt = setup_shape_spawn_newton (builder, rTopData, rTags, commonScene, physics, shapeSpawn, newton, nwtGravSet); - vehicleSpawnNwt = setup_vehicle_spawn_newton(builder, rTopData, rTags, commonScene, physics, prefabs, parts, vehicleSpawn, newton, idResources); - nwtRocketSet = setup_newton_factors (builder, rTopData, rTags); - rocketsNwt = setup_rocket_thrust_newton(builder, rTopData, rTags, commonScene, physics, prefabs, parts, signalsFloat, newton, nwtRocketSet); - - OSP_SESSION_UNPACK_DATA(commonScene, TESTAPP_COMMON_SCENE); - OSP_SESSION_UNPACK_DATA(vehicleSpawn, TESTAPP_VEHICLE_SPAWN); - OSP_SESSION_UNPACK_DATA(vehicleSpawnVB, TESTAPP_VEHICLE_SPAWN_VB); - OSP_SESSION_UNPACK_DATA(testVehicles, TESTAPP_TEST_VEHICLES); - - add_floor(rTopData, commonScene, matVisual, shapeSpawn, idResources, mainView.m_defaultPkg); - - auto &rActiveIds = top_get (rTopData, idActiveIds); - auto &rTVPartVehicle = top_get (rTopData, idTVPartVehicle); + + auto const defaultPkg = rTestApp.m_defaultPkg; + auto const application = rTestApp.m_application; + auto & rTopData = rTestApp.m_topData; + + TopTaskBuilder builder{rTestApp.m_tasks, rTestApp.m_scene.m_edges, rTestApp.m_taskData}; + + auto & [SCENE_SESSIONS] = resize_then_unpack<22>(rTestApp.m_scene.m_sessions); + + scene = setup_scene (builder, rTopData, application); + commonScene = setup_common_scene (builder, rTopData, scene, application, defaultPkg); + physics = setup_physics (builder, rTopData, scene, commonScene); + physShapes = setup_phys_shapes (builder, rTopData, scene, commonScene, physics, sc_matPhong); + droppers = setup_droppers (builder, rTopData, scene, commonScene, physShapes); + bounds = setup_bounds (builder, rTopData, scene, commonScene, physShapes); + + prefabs = setup_prefabs (builder, rTopData, application, scene, commonScene, physics); + parts = setup_parts (builder, rTopData, application, scene); + signalsFloat = setup_signals_float (builder, rTopData, scene, parts); + vehicleSpawn = setup_vehicle_spawn (builder, rTopData, scene); + vehicleSpawnVB = setup_vehicle_spawn_vb (builder, rTopData, application, scene, commonScene, prefabs, parts, vehicleSpawn, signalsFloat); + testVehicles = setup_prebuilt_vehicles (builder, rTopData, application, scene); + + machRocket = setup_mach_rocket (builder, rTopData, scene, parts, signalsFloat); + machRcsDriver = setup_mach_rcsdriver (builder, rTopData, scene, parts, signalsFloat); + + newton = setup_newton (builder, rTopData, scene, commonScene, physics); + nwtGravSet = setup_newton_factors (builder, rTopData); + nwtGrav = setup_newton_force_accel (builder, rTopData, newton, nwtGravSet, sc_gravityForce); + physShapesNwt = setup_phys_shapes_newton (builder, rTopData, commonScene, physics, physShapes, newton, nwtGravSet); + vehicleSpawnNwt = setup_vehicle_spawn_newton(builder, rTopData, application, commonScene, physics, prefabs, parts, vehicleSpawn, newton); + nwtRocketSet = setup_newton_factors (builder, rTopData); + rocketsNwt = setup_rocket_thrust_newton(builder, rTopData, scene, commonScene, physics, prefabs, parts, signalsFloat, newton, nwtRocketSet); + + OSP_DECLARE_GET_DATA_IDS(vehicleSpawn, TESTAPP_DATA_VEHICLE_SPAWN); + OSP_DECLARE_GET_DATA_IDS(vehicleSpawnVB, TESTAPP_DATA_VEHICLE_SPAWN_VB); + OSP_DECLARE_GET_DATA_IDS(testVehicles, TESTAPP_DATA_TEST_VEHICLES); + auto &rVehicleSpawn = top_get (rTopData, idVehicleSpawn); auto &rVehicleSpawnVB = top_get (rTopData, idVehicleSpawnVB); + auto &rPrebuiltVehicles = top_get (rTopData, idPrebuiltVehicles); for (int i = 0; i < 10; ++i) { - rVehicleSpawn.m_newVhBasicIn.push_back( + rVehicleSpawn.spawnRequest.push_back( { - .m_position = {float(i - 2) * 8.0f, 30.0f, 10.0f}, - .m_velocity = {0.0, 0.0f, 0.0f}, - .m_rotation = {} + .position = {float(i - 2) * 8.0f, 30.0f, 10.0f}, + .velocity = {0.0, 0.0f, 50.0f * float(i)}, + .rotation = {} }); - rVehicleSpawnVB.m_dataVB.push_back(&rTVPartVehicle); + rVehicleSpawnVB.dataVB.push_back(rPrebuiltVehicles[gc_pbvSimpleCommandServiceModule].get()); } - return [] (MainView mainView, Session const& magnum, Sessions_t const& scene, [[maybe_unused]] Sessions_t& rendererOut) + add_floor(rTopData, physShapes, sc_matVisualizer, defaultPkg, 4); + + RendererSetupFunc_t const setup_renderer = [] (TestApp& rTestApp) { - auto &rTopData = mainView.m_topData; - auto &rTags = mainView.m_rTags; - Builder_t builder{mainView.m_rTags, mainView.m_rTasks, mainView.m_rTaskData}; - - auto const& - [ - commonScene, matVisual, physics, shapeSpawn, - prefabs, parts, - vehicleSpawn, vehicleSpawnVB, vehicleSpawnRgd, - signalsFloat, machRocket, machRcsDriver, - testVehicles, droppers, gravity, bounds, thrower, - newton, nwtGravSet, nwtGrav, shapeSpawnNwt, vehicleSpawnNwt, nwtRocketSet, rocketsNwt - ] = unpack<24>(scene); - - auto & [scnRender, cameraCtrl, shPhong, shFlat, camThrow, vehicleCtrl, cameraVehicle, thrustIndicator] = resize_then_unpack<8>(rendererOut); - scnRender = setup_scene_renderer (builder, rTopData, rTags, magnum, commonScene, mainView.m_idResources); - cameraCtrl = setup_camera_ctrl (builder, rTopData, rTags, magnum, scnRender); - shPhong = setup_shader_phong (builder, rTopData, rTags, magnum, commonScene, scnRender, matVisual); - shFlat = setup_shader_flat (builder, rTopData, rTags, magnum, commonScene, scnRender, {}); - camThrow = setup_thrower (builder, rTopData, rTags, magnum, scnRender, cameraCtrl, shapeSpawn); - vehicleCtrl = setup_vehicle_control (builder, rTopData, rTags, commonScene, parts, signalsFloat, magnum); - cameraVehicle = setup_camera_vehicle (builder, rTopData, rTags, magnum, commonScene, parts, physics, cameraCtrl, vehicleCtrl); - thrustIndicator = setup_thrust_indicators (builder, rTopData, rTags, magnum, commonScene, parts, signalsFloat, scnRender, cameraCtrl, shFlat, mainView.m_idResources, mainView.m_defaultPkg); - - setup_magnum_draw(mainView, magnum, commonScene, scnRender); + auto const application = rTestApp.m_application; + auto const windowApp = rTestApp.m_windowApp; + auto const magnum = rTestApp.m_magnum; + auto const defaultPkg = rTestApp.m_defaultPkg; + auto & rTopData = rTestApp.m_topData; + + TopTaskBuilder builder{rTestApp.m_tasks, rTestApp.m_renderer.m_edges, rTestApp.m_taskData}; + + auto & [SCENE_SESSIONS] = unpack<22>(rTestApp.m_scene.m_sessions); + auto & [RENDERER_SESSIONS] = resize_then_unpack<14>(rTestApp.m_renderer.m_sessions); + + sceneRenderer = setup_scene_renderer (builder, rTopData, application, windowApp, commonScene); + create_materials(rTopData, sceneRenderer, sc_materialCount); + + magnumScene = setup_magnum_scene (builder, rTopData, application, windowApp, sceneRenderer, magnum, scene, commonScene); + cameraCtrl = setup_camera_ctrl (builder, rTopData, windowApp, sceneRenderer, magnumScene); + shVisual = setup_shader_visualizer (builder, rTopData, windowApp, sceneRenderer, magnum, magnumScene, sc_matVisualizer); + shFlat = setup_shader_flat (builder, rTopData, windowApp, sceneRenderer, magnum, magnumScene, sc_matFlat); + shPhong = setup_shader_phong (builder, rTopData, windowApp, sceneRenderer, magnum, magnumScene, sc_matPhong); + camThrow = setup_thrower (builder, rTopData, windowApp, cameraCtrl, physShapes); + shapeDraw = setup_phys_shapes_draw (builder, rTopData, windowApp, sceneRenderer, commonScene, physics, physShapes); + cursor = setup_cursor (builder, rTopData, application, sceneRenderer, cameraCtrl, commonScene, sc_matFlat, rTestApp.m_defaultPkg); + prefabDraw = setup_prefab_draw (builder, rTopData, application, windowApp, sceneRenderer, commonScene, prefabs, sc_matPhong); + vehicleDraw = setup_vehicle_spawn_draw (builder, rTopData, sceneRenderer, vehicleSpawn); + vehicleCtrl = setup_vehicle_control (builder, rTopData, windowApp, scene, parts, signalsFloat); + cameraVehicle = setup_camera_vehicle (builder, rTopData, windowApp, scene, sceneRenderer, commonScene, physics, parts, cameraCtrl, vehicleCtrl); + thrustIndicator = setup_thrust_indicators (builder, rTopData, application, windowApp, commonScene, parts, signalsFloat, sceneRenderer, defaultPkg, sc_matFlat); + + setup_magnum_draw(rTestApp, scene, sceneRenderer, magnumScene); }; - }); -#endif + #undef SCENE_SESSIONS + #undef RENDERER_SESSIONS + + return setup_renderer; + }); add_scenario("universe", "Universe test scenario with very unrealistic planets", [] (TestApp& rTestApp) -> RendererSetupFunc_t { - #define SCENE_SESSIONS scene, commonScene, physics, shapeSpawn, droppers, bounds, newton, nwtGravSet, nwtGrav, shapeSpawnNwt, uniCore, uniScnFrame, uniTestPlanets + #define SCENE_SESSIONS scene, commonScene, physics, physShapes, droppers, bounds, newton, nwtGravSet, nwtGrav, physShapesNwt, uniCore, uniScnFrame, uniTestPlanets #define RENDERER_SESSIONS sceneRenderer, magnumScene, cameraCtrl, cameraFree, shVisual, shFlat, shPhong, camThrow, shapeDraw, cursor, planetsDraw using namespace testapp::scenes; @@ -406,14 +317,14 @@ static ScenarioMap_t make_scenarios() scene = setup_scene (builder, rTopData, application); commonScene = setup_common_scene (builder, rTopData, scene, application, defaultPkg); physics = setup_physics (builder, rTopData, scene, commonScene); - shapeSpawn = setup_shape_spawn (builder, rTopData, scene, commonScene, physics, sc_matPhong); - droppers = setup_droppers (builder, rTopData, scene, commonScene, shapeSpawn); - bounds = setup_bounds (builder, rTopData, scene, commonScene, shapeSpawn); + physShapes = setup_phys_shapes (builder, rTopData, scene, commonScene, physics, sc_matPhong); + droppers = setup_droppers (builder, rTopData, scene, commonScene, physShapes); + bounds = setup_bounds (builder, rTopData, scene, commonScene, physShapes); newton = setup_newton (builder, rTopData, scene, commonScene, physics); nwtGravSet = setup_newton_factors (builder, rTopData); nwtGrav = setup_newton_force_accel (builder, rTopData, newton, nwtGravSet, Vector3{0.0f, 0.0f, -9.81f}); - shapeSpawnNwt = setup_shape_spawn_newton (builder, rTopData, commonScene, physics, shapeSpawn, newton, nwtGravSet); + physShapesNwt = setup_phys_shapes_newton (builder, rTopData, commonScene, physics, physShapes, newton, nwtGravSet); auto const tgApp = application.get_pipelines< PlApplication >(); @@ -421,9 +332,9 @@ static ScenarioMap_t make_scenarios() uniScnFrame = setup_uni_sceneframe (builder, rTopData, uniCore); uniTestPlanets = setup_uni_testplanets (builder, rTopData, uniCore, uniScnFrame); - add_floor(rTopData, shapeSpawn, sc_matVisualizer, defaultPkg, 0); + add_floor(rTopData, physShapes, sc_matVisualizer, defaultPkg, 0); - return [] (TestApp& rTestApp) + RendererSetupFunc_t const setup_renderer = [] (TestApp& rTestApp) { auto const application = rTestApp.m_application; auto const windowApp = rTestApp.m_windowApp; @@ -445,8 +356,8 @@ static ScenarioMap_t make_scenarios() shVisual = setup_shader_visualizer (builder, rTopData, windowApp, sceneRenderer, magnum, magnumScene, sc_matVisualizer); shFlat = setup_shader_flat (builder, rTopData, windowApp, sceneRenderer, magnum, magnumScene, sc_matFlat); shPhong = setup_shader_phong (builder, rTopData, windowApp, sceneRenderer, magnum, magnumScene, sc_matPhong); - camThrow = setup_thrower (builder, rTopData, windowApp, cameraCtrl, shapeSpawn); - shapeDraw = setup_shape_spawn_draw (builder, rTopData, windowApp, sceneRenderer, commonScene, physics, shapeSpawn); + camThrow = setup_thrower (builder, rTopData, windowApp, cameraCtrl, physShapes); + shapeDraw = setup_phys_shapes_draw (builder, rTopData, windowApp, sceneRenderer, commonScene, physics, physShapes); cursor = setup_cursor (builder, rTopData, application, sceneRenderer, cameraCtrl, commonScene, sc_matFlat, rTestApp.m_defaultPkg); planetsDraw = setup_testplanets_draw (builder, rTopData, windowApp, sceneRenderer, cameraCtrl, commonScene, uniCore, uniScnFrame, uniTestPlanets, sc_matVisualizer, sc_matFlat); @@ -455,6 +366,8 @@ static ScenarioMap_t make_scenarios() #undef SCENE_SESSIONS #undef RENDERER_SESSIONS + + return setup_renderer; }); return scenarioMap; @@ -466,6 +379,134 @@ ScenarioMap_t const& scenarios() return s_scenarioMap; } + +//----------------------------------------------------------------------------- + + +struct MainLoopSignals +{ + PipelineId mainLoop; + PipelineId inputs; + PipelineId renderSync; + PipelineId renderResync; + PipelineId sceneUpdate; + PipelineId sceneRender; +}; + +/** + * @brief Runs Task/Pipeline main loop within MagnumApplication + */ +class CommonMagnumApp : public IOspApplication +{ +public: + CommonMagnumApp(TestApp &rTestApp, MainLoopControl &rMainLoopCtrl, MainLoopSignals signals) noexcept + : m_rTestApp { rTestApp } + , m_rMainLoopCtrl { rMainLoopCtrl } + , m_signals { signals } + { } + + void run(MagnumApplication& rApp) override + { + // Start the main loop + + PipelineId const mainLoop = m_rTestApp.m_application.get_pipelines().mainLoop; + m_rTestApp.m_pExecutor->run(m_rTestApp, mainLoop); + + // Resyncronize renderer + + m_rMainLoopCtrl = MainLoopControl{ + .doUpdate = false, + .doSync = true, + .doResync = true, + .doRender = false, + }; + + signal_all(); + + m_rTestApp.m_pExecutor->wait(m_rTestApp); + } + + void draw(MagnumApplication& rApp, float delta) override + { + // Magnum Application's main loop calls this + + m_rMainLoopCtrl = MainLoopControl{ + .doUpdate = true, + .doSync = true, + .doResync = false, + .doRender = true, + }; + + signal_all(); + + m_rTestApp.m_pExecutor->wait(m_rTestApp); + } + + void exit(MagnumApplication& rApp) override + { + m_rMainLoopCtrl = MainLoopControl{ + .doUpdate = false, + .doSync = false, + .doResync = false, + .doRender = false, + }; + + signal_all(); + + m_rTestApp.m_pExecutor->wait(m_rTestApp); + + if (m_rTestApp.m_pExecutor->is_running(m_rTestApp)) + { + // Main loop must have stopped, but didn't! + m_rTestApp.m_pExecutor->wait(m_rTestApp); + std::abort(); + } + } + +private: + + void signal_all() + { + m_rTestApp.m_pExecutor->signal(m_rTestApp, m_signals.mainLoop); + m_rTestApp.m_pExecutor->signal(m_rTestApp, m_signals.inputs); + m_rTestApp.m_pExecutor->signal(m_rTestApp, m_signals.renderSync); + m_rTestApp.m_pExecutor->signal(m_rTestApp, m_signals.renderResync); + m_rTestApp.m_pExecutor->signal(m_rTestApp, m_signals.sceneUpdate); + m_rTestApp.m_pExecutor->signal(m_rTestApp, m_signals.sceneRender); + } + + TestApp &m_rTestApp; + MainLoopControl &m_rMainLoopCtrl; + + MainLoopSignals m_signals; +}; + +void setup_magnum_draw(TestApp& rTestApp, Session const& scene, Session const& sceneRenderer, Session const& magnumScene) +{ + OSP_DECLARE_GET_DATA_IDS(rTestApp.m_application, TESTAPP_DATA_APPLICATION); + OSP_DECLARE_GET_DATA_IDS(sceneRenderer, TESTAPP_DATA_SCENE_RENDERER); + OSP_DECLARE_GET_DATA_IDS(rTestApp.m_magnum, TESTAPP_DATA_MAGNUM); + OSP_DECLARE_GET_DATA_IDS(magnumScene, TESTAPP_DATA_MAGNUM_SCENE); + + auto &rMainLoopCtrl = top_get (rTestApp.m_topData, idMainLoopCtrl); + auto &rActiveApp = top_get(rTestApp.m_topData, idActiveApp); + auto &rCamera = top_get (rTestApp.m_topData, idCamera); + + rCamera.set_aspect_ratio(Vector2{Magnum::GL::defaultFramebuffer.viewport().size()}); + + MainLoopSignals const signals + { + .mainLoop = rTestApp.m_application .get_pipelines() .mainLoop, + .inputs = rTestApp.m_windowApp .get_pipelines() .inputs, + .renderSync = rTestApp.m_windowApp .get_pipelines() .sync, + .renderResync = rTestApp.m_windowApp .get_pipelines() .resync, + .sceneUpdate = scene .get_pipelines() .update, + .sceneRender = sceneRenderer .get_pipelines() .render, + }; + + rActiveApp.set_osp_app( std::make_unique(rTestApp, rMainLoopCtrl, signals) ); +} + } // namespace testapp diff --git a/src/testapp/scenarios.h b/src/testapp/scenarios.h index 932f77a8..edb02e52 100644 --- a/src/testapp/scenarios.h +++ b/src/testapp/scenarios.h @@ -44,6 +44,7 @@ namespace scenes using enum EStgRevd; using enum EStgEvnt; using enum EStgFBO; + using enum EStgLink; } struct MainLoopControl diff --git a/src/testapp/sessions/common.cpp b/src/testapp/sessions/common.cpp index 5ec59025..db944c05 100644 --- a/src/testapp/sessions/common.cpp +++ b/src/testapp/sessions/common.cpp @@ -221,7 +221,17 @@ Session setup_window_app( out.m_cleanup = tgWin.cleanup; rBuilder.task() - .name ("Schedule GL Resync") + .name ("Schedule Renderer Sync") + .schedules ({tgWin.sync(Schedule)}) + .push_to (out.m_tasks) + .args ({ idMainLoopCtrl}) + .func([] (MainLoopControl const& rMainLoopCtrl) noexcept -> osp::TaskActions + { + return rMainLoopCtrl.doSync ? osp::TaskActions{} : osp::TaskAction::Cancel; + }); + + rBuilder.task() + .name ("Schedule Renderer Resync") .schedules ({tgWin.resync(Schedule)}) .push_to (out.m_tasks) .args ({ idMainLoopCtrl}) @@ -276,6 +286,7 @@ Session setup_scene_renderer( rBuilder.pipeline(tgScnRdr.textureResDirty) .parent(tgWin.sync); auto &rScnRender = osp::top_emplace(topData, idScnRender); + /* unused */ osp::top_emplace(topData, idDrawTfObservers); rBuilder.task() .name ("Resize ACtxSceneRender containers to fit all DrawEnts") @@ -333,6 +344,38 @@ Session setup_scene_renderer( return rScnRender.m_meshDirty.empty() ? TaskAction::Cancel : TaskActions{}; }); + rBuilder.task() + .name ("Calculate draw transforms") + .run_on ({tgScnRdr.render(Run)}) + .sync_with ({tgCS.hierarchy(Ready), tgCS.transform(Ready), tgCS.activeEnt(Ready), tgScnRdr.drawTransforms(Modify_), tgScnRdr.drawEnt(Ready), tgScnRdr.drawEntResized(Done), tgCS.activeEntResized(Done)}) + .push_to (out.m_tasks) + .args ({ idBasic, idDrawing, idScnRender, idDrawTfObservers }) + .func([] (ACtxBasic const& rBasic, ACtxDrawing const& rDrawing, ACtxSceneRender& rScnRender, DrawTfObservers &rDrawTfObservers) noexcept + { + auto rootChildren = SysSceneGraph::children(rBasic.m_scnGraph); + SysRender::update_draw_transforms( + { + .scnGraph = rBasic .m_scnGraph, + .transforms = rBasic .m_transform, + .activeToDraw = rScnRender.m_activeToDraw, + .needDrawTf = rScnRender.m_needDrawTf, + .rDrawTf = rScnRender.m_drawTransform + }, + rootChildren.begin(), + rootChildren.end(), + [&rDrawTfObservers, &rScnRender] (Matrix4 const& transform, active::ActiveEnt ent, int depth) + { + auto const enableInt = std::array{rScnRender.drawTfObserverEnable[ent]}; + auto const enableBits = lgrn::bit_view(enableInt); + + for (std::size_t idx : enableBits.ones()) + { + DrawTfObservers::Observer const &rObserver = rDrawTfObservers.observers[idx]; + rObserver.func(rScnRender, transform, ent, depth, rObserver.data); + } + }); + }); + rBuilder.task() .name ("Delete DrawEntity of deleted ActiveEnts") .run_on ({tgCS.activeEntDelete(UseOrRun)}) diff --git a/src/testapp/sessions/magnum.cpp b/src/testapp/sessions/magnum.cpp index 111ffc35..bac839d1 100644 --- a/src/testapp/sessions/magnum.cpp +++ b/src/testapp/sessions/magnum.cpp @@ -101,16 +101,6 @@ Session setup_magnum( rRenderGl = {}; // Needs the OpenGL thread for destruction }); - rBuilder.task() - .name ("Schedule GL Sync") - .schedules ({tgWin.sync(Schedule)}) - .push_to (out.m_tasks) - .args ({ idMainLoopCtrl}) - .func([] (MainLoopControl const& rMainLoopCtrl) noexcept -> osp::TaskActions - { - return rMainLoopCtrl.doSync ? osp::TaskActions{} : osp::TaskAction::Cancel; - }); - return out; } // setup_magnum @@ -134,7 +124,6 @@ Session setup_magnum_scene( OSP_DECLARE_GET_DATA_IDS(magnum, TESTAPP_DATA_MAGNUM); auto const tgWin = windowApp .get_pipelines< PlWindowApp >(); - auto const tgCS = commonScene .get_pipelines< PlCommonScene >(); auto const tgMgn = magnum .get_pipelines< PlMagnum >(); auto const tgScnRdr = sceneRenderer .get_pipelines< PlSceneRenderer >(); @@ -282,25 +271,6 @@ Session setup_magnum_scene( | FramebufferClear::Stencil); }); - rBuilder.task() - .name ("Calculate draw transforms") - .run_on ({tgScnRdr.render(Run)}) - .sync_with ({tgCS.hierarchy(Ready), tgCS.transform(Ready), tgCS.activeEnt(Ready), tgScnRdr.drawTransforms(Modify_), tgScnRdr.drawEnt(Ready), tgScnRdr.drawEntResized(Done), tgCS.activeEntResized(Done)}) - .push_to (out.m_tasks) - .args ({ idBasic, idDrawing, idScnRender, idScnRenderGl }) - .func([] (ACtxBasic const& rBasic, ACtxDrawing const& rDrawing, ACtxSceneRender& rScnRender, ACtxSceneRenderGL& rScnRenderGl) noexcept - { - auto rootChildren = SysSceneGraph::children(rBasic.m_scnGraph); - SysRender::update_draw_transforms( - rBasic .m_scnGraph, - rScnRender .m_activeToDraw, - rBasic .m_transform, - rScnRender .m_drawTransform, - rScnRender .m_needDrawTf, - rootChildren.begin(), - rootChildren.end()); - }); - rBuilder.task() .name ("Render Entities") .run_on ({tgScnRdr.render(Run)}) @@ -327,7 +297,7 @@ Session setup_magnum_scene( { for (DrawEnt const drawEnt : rDrawEntDel) { - rGroup.m_entities.remove(drawEnt); + rGroup.entities.remove(drawEnt); } }); @@ -383,7 +353,7 @@ Session setup_shader_visualizer( .func([] (ACtxSceneRender& rScnRender, RenderGroup& rGroupFwd, ACtxDrawMeshVisualizer& rDrawShVisual) noexcept { Material const &rMat = rScnRender.m_materials[rDrawShVisual.m_materialId]; - sync_drawent_visualizer(rMat.m_dirty.begin(), rMat.m_dirty.end(), rMat.m_ents, rGroupFwd.m_entities, rDrawShVisual); + sync_drawent_visualizer(rMat.m_dirty.begin(), rMat.m_dirty.end(), rMat.m_ents, rGroupFwd.entities, rDrawShVisual); }); rBuilder.task() @@ -397,7 +367,7 @@ Session setup_shader_visualizer( Material const &rMat = rScnRender.m_materials[rDrawShVisual.m_materialId]; for (auto const drawEntInt : rMat.m_ents.ones()) { - sync_drawent_visualizer(DrawEnt(drawEntInt), rMat.m_ents, rGroupFwd.m_entities, rDrawShVisual); + sync_drawent_visualizer(DrawEnt(drawEntInt), rMat.m_ents, rGroupFwd.entities, rDrawShVisual); } }); @@ -452,7 +422,7 @@ Session setup_shader_flat( sync_drawent_flat(rMat.m_dirty.begin(), rMat.m_dirty.end(), { .hasMaterial = rMat.m_ents, - .pStorageOpaque = &rGroupFwd.m_entities, + .pStorageOpaque = &rGroupFwd.entities, /* TODO: set .pStorageTransparent */ .opaque = rScnRender.m_opaque, .transparent = rScnRender.m_transparent, @@ -475,7 +445,7 @@ Session setup_shader_flat( sync_drawent_flat(DrawEnt(drawEntInt), { .hasMaterial = rMat.m_ents, - .pStorageOpaque = &rGroupFwd.m_entities, + .pStorageOpaque = &rGroupFwd.entities, /* TODO: set .pStorageTransparent */ .opaque = rScnRender.m_opaque, .transparent = rScnRender.m_transparent, @@ -537,7 +507,7 @@ Session setup_shader_phong( sync_drawent_phong(rMat.m_dirty.begin(), rMat.m_dirty.end(), { .hasMaterial = rMat.m_ents, - .pStorageOpaque = &rGroupFwd.m_entities, + .pStorageOpaque = &rGroupFwd.entities, /* TODO: set .pStorageTransparent */ .opaque = rScnRender.m_opaque, .transparent = rScnRender.m_transparent, @@ -560,7 +530,7 @@ Session setup_shader_phong( sync_drawent_phong(DrawEnt(drawEntInt), { .hasMaterial = rMat.m_ents, - .pStorageOpaque = &rGroupFwd.m_entities, + .pStorageOpaque = &rGroupFwd.entities, .opaque = rScnRender.m_opaque, .transparent = rScnRender.m_transparent, .diffuse = rScnRenderGl.m_diffuseTexId, @@ -573,165 +543,4 @@ Session setup_shader_phong( } // setup_shader_phong - -#if 0 - -struct IndicatorMesh -{ - Magnum::Color4 m_color; - MeshIdOwner_t m_mesh; -}; - - -Session setup_thrust_indicators( - TopTaskBuilder& rBuilder, - ArrayView const topData, - Session const& magnum, - Session const& commonScene, - Session const& parts, - Session const& signalsFloat, - Session const& scnRender, - Session const& cameraCtrl, - Session const& shFlat, - TopDataId const idResources, - PkgId const pkg) -{ - - - using namespace osp::link; - using adera::gc_mtMagicRocket; - using adera::ports_magicrocket::gc_throttleIn; - using adera::ports_magicrocket::gc_multiplierIn; - - static constexpr float indicatorScale = 0.0001f; - - OSP_SESSION_UNPACK_TAGS(magnum, TESTAPP_APP_MAGNUM); - OSP_SESSION_UNPACK_DATA(magnum, TESTAPP_APP_MAGNUM); - OSP_SESSION_UNPACK_TAGS(commonScene, TESTAPP_COMMON_SCENE); - OSP_SESSION_UNPACK_DATA(commonScene, TESTAPP_COMMON_SCENE); - OSP_SESSION_UNPACK_TAGS(parts, TESTAPP_PARTS); - OSP_SESSION_UNPACK_DATA(parts, TESTAPP_PARTS); - OSP_SESSION_UNPACK_DATA(signalsFloat, TESTAPP_SIGNALS_FLOAT) - OSP_SESSION_UNPACK_TAGS(signalsFloat, TESTAPP_SIGNALS_FLOAT); - OSP_SESSION_UNPACK_TAGS(scnRender, TESTAPP_COMMON_RENDERER); - OSP_SESSION_UNPACK_DATA(scnRender, TESTAPP_COMMON_RENDERER); - - OSP_SESSION_UNPACK_DATA(cameraCtrl, TESTAPP_CAMERA_CTRL); - OSP_SESSION_UNPACK_TAGS(cameraCtrl, TESTAPP_CAMERA_CTRL); - OSP_SESSION_UNPACK_DATA(shFlat, TESTAPP_SHADER_FLAT); - - auto &rResources = top_get< Resources > (topData, idResources); - auto &rBasic = top_get< ACtxBasic > (topData, idBasic); - auto &rActiveIds = top_get< ActiveReg_t > (topData, idActiveIds); - auto &rDrawing = top_get< ACtxDrawing > (topData, idDrawing); - auto &rDrawingRes = top_get< ACtxDrawingRes > (topData, idDrawingRes); - - Session thrustIndicator; -#if 0 // RENDERENT - OSP_SESSION_ACQUIRE_DATA(thrustIndicator, topData, TESTAPP_INDICATOR); - thrustIndicator.m_tgCleanupEvt = tgCleanupMagnumEvt; - - auto &rIndicator = top_emplace(topData, idIndicator); - rIndicator.m_color = { 1.0f, 0.0f, 0.0f, 1.0f }; - rIndicator.m_mesh = SysRender::add_drawable_mesh(rDrawing, rDrawingRes, rResources, pkg, "cone"); - - thrustIndicator.task() = rBuilder.task().assign({tgRenderEvt, tgGlUse, tgDrawTransformReq, tgBindFboReq, tgFwdRenderMod, tgCamCtrlReq}).data( - "Render cursor", - TopDataIds_t{ idRenderGl, idCamera, idDrawingRes, idScnRender, idScnParts, idSigValFloat, idDrawShFlat, idCamCtrl, idIndicator}, - wrap_args([] (RenderGL& rRenderGl, Camera const& rCamera, ACtxDrawingRes const& rDrawingRes, ACtxSceneRenderGL const& rScnRender, ACtxParts const& rScnParts, SignalValues_t const& rSigValFloat, ACtxDrawFlat& rDrawShFlat, ACtxCameraController const& rCamCtrl, IndicatorMesh& rIndicator) noexcept - { - - ResId const indicatorResId = rDrawingRes.m_meshToRes.at(rIndicator.m_mesh.value()); - MeshGlId const indicatorMeshGlId = rRenderGl.m_resToMesh.at(indicatorResId); - Mesh& rIndicatorMeshGl = rRenderGl.m_meshGl.get(indicatorMeshGlId); - - ViewProjMatrix viewProj{rCamera.m_transform.inverted(), rCamera.perspective()}; - - //auto const matrix = viewProj.m_viewProj * Matrix4::translation(rCamCtrl.m_target.value()); - - PerMachType const& rockets = rScnParts.m_machines.m_perType[gc_mtMagicRocket]; - Nodes const& floats = rScnParts.m_nodePerType[gc_ntSigFloat]; - - for (MachLocalId const localId : rockets.m_localIds.bitview().zeros()) - { - MachAnyId const anyId = rockets.m_localToAny[localId]; - PartId const part = rScnParts.m_machineToPart[anyId]; - ActiveEnt const partEnt = rScnParts.m_partToActive[part]; - - auto const& portSpan = floats.m_machToNode[anyId]; - NodeId const throttleIn = connected_node(portSpan, gc_throttleIn.m_port); - NodeId const multiplierIn = connected_node(portSpan, gc_multiplierIn.m_port); - - float const throttle = std::clamp(rSigValFloat[throttleIn], 0.0f, 1.0f); - float const multiplier = rSigValFloat[multiplierIn]; - float const thrustMag = throttle * multiplier; - - if (thrustMag == 0.0f) - { - continue; - } - - Matrix4 const& rocketDrawTf = rScnRender.m_drawTransform.get(partEnt); - - auto const& matrix = Matrix4::from(rocketDrawTf.rotationNormalized(), rocketDrawTf.translation()) - * Matrix4::scaling({1.0f, 1.0f, thrustMag * indicatorScale}) - * Matrix4::translation({0.0f, 0.0f, -1.0f}) - * Matrix4::scaling({0.2f, 0.2f, 1.0f}); - - rDrawShFlat.m_shaderUntextured - .setColor(rIndicator.m_color) - .setTransformationProjectionMatrix(viewProj.m_viewProj * matrix) - .draw(rIndicatorMeshGl); - } - })); - - thrustIndicator.task() = rBuilder.task().assign({tgCleanupMagnumEvt}).data( - "Clean up thrust indicator resource owners", - TopDataIds_t{ idDrawing, idIndicator}, - wrap_args([] (ACtxDrawing& rDrawing, IndicatorMesh& rIndicator) noexcept - { - rDrawing.m_meshRefCounts.ref_release(std::move(rIndicator.m_mesh)); - })); - - // Draw transforms in part entities are required for drawing indicators. - // This solution of adding them here is a bit janky but it works. - - thrustIndicator.task() = rBuilder.task().assign({tgSyncEvt, tgPartReq}).data( - "Add draw transforms to rocket entities", - TopDataIds_t{ idScnParts, idScnRender}, - wrap_args([] (ACtxParts const& rScnParts, ACtxSceneRenderGL& rScnRender) noexcept - { - for (PartId const part : rScnParts.m_partDirty) - { - // TODO: maybe first check if the part contains any rocket machines - - ActiveEnt const ent = rScnParts.m_partToActive[part]; - if ( ! rScnRender.m_drawTransform.contains(ent) ) - { - rScnRender.m_drawTransform.emplace(ent); - } - } - })); - - // Called when scene is reopened - thrustIndicator.task() = rBuilder.task().assign({tgResyncEvt, tgPartReq}).data( - "Add draw transforms to all rocket entities", - TopDataIds_t{ idScnParts, idScnRender}, - wrap_args([] (ACtxParts const& rScnParts, ACtxSceneRenderGL& rScnRender) noexcept - { - for (PartId const part : rScnParts.m_partIds.bitview().zeros()) - { - ActiveEnt const ent = rScnParts.m_partToActive[part]; - if ( ! rScnRender.m_drawTransform.contains(ent) ) - { - rScnRender.m_drawTransform.emplace(ent); - } - } - })); -#endif - return thrustIndicator; -} - -#endif - } // namespace testapp::scenes diff --git a/src/testapp/sessions/misc.cpp b/src/testapp/sessions/misc.cpp index 2de5aeb9..554abef3 100644 --- a/src/testapp/sessions/misc.cpp +++ b/src/testapp/sessions/misc.cpp @@ -31,7 +31,6 @@ #include #include -#include using namespace adera; using namespace osp; @@ -62,39 +61,8 @@ void create_materials( rScnRender.m_materials.resize(count); } -void add_floor( - ArrayView const topData, - Session const& shapeSpawn, - MaterialId const materialId, - PkgId const pkg, - int const size) -{ - OSP_DECLARE_GET_DATA_IDS(shapeSpawn, TESTAPP_DATA_SHAPE_SPAWN); - - auto &rSpawner = top_get(topData, idSpawner); - - std::mt19937 randGen(69); - auto distSizeX = std::uniform_real_distribution{20.0, 80.0}; - auto distSizeY = std::uniform_real_distribution{20.0, 80.0}; - auto distHeight = std::uniform_real_distribution{1.0, 10.0}; - constexpr float spread = 128.0f; - for (int x = -size; x < size+1; ++x) - { - for (int y = -size; y < size+1; ++y) - { - float const heightZ = distHeight(randGen); - rSpawner.m_spawnRequest.emplace_back(SpawnShape{ - .m_position = Vector3{x*spread, y*spread, heightZ}, - .m_velocity = {0.0f, 0.0f, 0.0f}, - .m_size = Vector3{distSizeX(randGen), distSizeY(randGen), heightZ}, - .m_mass = 0.0f, - .m_shape = EShape::Box - }); - } - } -} Session setup_camera_ctrl( TopTaskBuilder& rBuilder, @@ -107,6 +75,7 @@ Session setup_camera_ctrl( OSP_DECLARE_GET_DATA_IDS(magnumScene, TESTAPP_DATA_MAGNUM_SCENE); auto const tgScnRdr = sceneRenderer .get_pipelines(); auto const tgSR = magnumScene .get_pipelines(); + auto const tgWin = windowApp .get_pipelines(); auto &rUserInput = top_get< osp::input::UserInputHandler >(topData, idUserInput); @@ -116,7 +85,7 @@ Session setup_camera_ctrl( top_emplace< ACtxCameraController > (topData, idCamCtrl, rUserInput); - rBuilder.pipeline(tgCmCt.camCtrl).parent(tgScnRdr.render); + rBuilder.pipeline(tgCmCt.camCtrl).parent(tgWin.sync); rBuilder.task() .name ("Position Rendering Camera according to Camera Controller") @@ -168,242 +137,6 @@ Session setup_camera_free( -Session setup_thrower( - TopTaskBuilder& rBuilder, - ArrayView const topData, - Session const& windowApp, - Session const& cameraCtrl, - Session const& shapeSpawn) -{ - OSP_DECLARE_GET_DATA_IDS(shapeSpawn, TESTAPP_DATA_SHAPE_SPAWN); - OSP_DECLARE_GET_DATA_IDS(cameraCtrl, TESTAPP_DATA_CAMERA_CTRL); - auto &rCamCtrl = top_get< ACtxCameraController > (topData, idCamCtrl); - - auto const tgWin = windowApp .get_pipelines(); - auto const tgCmCt = cameraCtrl.get_pipelines(); - auto const tgShSp = shapeSpawn.get_pipelines(); - - Session out; - auto const [idBtnThrow] = out.acquire_data<1>(topData); - - top_emplace< EButtonControlIndex > (topData, idBtnThrow, rCamCtrl.m_controls.button_subscribe("debug_throw")); - - rBuilder.task() - .name ("Throw spheres when pressing space") - .run_on ({tgWin.inputs(Run)}) - .sync_with ({tgCmCt.camCtrl(Ready), tgShSp.spawnRequest(Modify_)}) - .push_to (out.m_tasks) - .args ({ idCamCtrl, idSpawner, idBtnThrow }) - .func([] (ACtxCameraController& rCamCtrl, ACtxShapeSpawner& rSpawner, EButtonControlIndex btnThrow) noexcept - { - // Throw a sphere when the throw button is pressed - if (rCamCtrl.m_controls.button_held(btnThrow)) - { - Matrix4 const &camTf = rCamCtrl.m_transform; - float const speed = 120; - float const dist = 8.0f; - - for (int x = -2; x < 3; ++x) - { - for (int y = -2; y < 3; ++y) - { - rSpawner.m_spawnRequest.push_back({ - .m_position = camTf.translation() - camTf.backward()*dist + camTf.up()*y*5.5f + camTf.right()*x*5.5f, - .m_velocity = -camTf.backward()*speed, - .m_size = Vector3{1.0f}, - .m_mass = 1.0f, - .m_shape = EShape::Sphere - }); - } - } - } - }); - - return out; -} // setup_thrower - - - - -Session setup_droppers( - TopTaskBuilder& rBuilder, - ArrayView const topData, - Session const& scene, - Session const& commonScene, - Session const& shapeSpawn) -{ - OSP_DECLARE_GET_DATA_IDS(scene, TESTAPP_DATA_SCENE); - OSP_DECLARE_GET_DATA_IDS(commonScene, TESTAPP_DATA_COMMON_SCENE); - OSP_DECLARE_GET_DATA_IDS(shapeSpawn, TESTAPP_DATA_SHAPE_SPAWN); - - auto const tgScn = scene .get_pipelines(); - auto const tgShSp = shapeSpawn .get_pipelines(); - - Session out; - auto const [idSpawnTimerA, idSpawnTimerB] = out.acquire_data<2>(topData); - - top_emplace< float > (topData, idSpawnTimerA, 0.0f); - top_emplace< float > (topData, idSpawnTimerB, 0.0f); - - rBuilder.task() - .name ("Spawn blocks every 2 seconds") - .run_on ({tgScn.update(Run)}) - .sync_with ({tgShSp.spawnRequest(Modify_)}) - .push_to (out.m_tasks) - .args({ idSpawner, idSpawnTimerA, idDeltaTimeIn }) - .func([] (ACtxShapeSpawner& rSpawner, float& rSpawnTimer, float const deltaTimeIn) noexcept - - { - rSpawnTimer += deltaTimeIn; - if (rSpawnTimer >= 2.0f) - { - rSpawnTimer -= 2.0f; - - rSpawner.m_spawnRequest.push_back({ - .m_position = {10.0f, 0.0f, 30.0f}, - .m_velocity = {0.0f, 0.0f, 0.0f}, - .m_size = {2.0f, 2.0f, 1.0f}, - .m_mass = 1.0f, - .m_shape = EShape::Box - }); - } - }); - - rBuilder.task() - .name ("Spawn cylinders every 1 second") - .run_on ({tgScn.update(Run)}) - .sync_with ({tgShSp.spawnRequest(Modify_)}) - .push_to (out.m_tasks) - .args({ idSpawner, idSpawnTimerB, idDeltaTimeIn }) - .func([] (ACtxShapeSpawner& rSpawner, float& rSpawnTimer, float const deltaTimeIn) noexcept - { - rSpawnTimer += deltaTimeIn; - if (rSpawnTimer >= 1.0f) - { - rSpawnTimer -= 1.0f; - - rSpawner.m_spawnRequest.push_back({ - .m_position = {-10.0f, 0.0, 30.0f}, - .m_velocity = {0.0f, 0.0f, 0.0f}, - .m_size = {2.0f, 2.0f, 1.0f}, - .m_mass = 1.0f, - .m_shape = EShape::Cylinder - }); - } - }); - - return out; -} // setup_droppers - - - - -Session setup_bounds( - TopTaskBuilder& rBuilder, - ArrayView const topData, - Session const& scene, - Session const& commonScene, - Session const& shapeSpawn) -{ - OSP_DECLARE_GET_DATA_IDS(commonScene, TESTAPP_DATA_COMMON_SCENE); - OSP_DECLARE_GET_DATA_IDS(shapeSpawn, TESTAPP_DATA_SHAPE_SPAWN); - auto const tgScn = scene .get_pipelines(); - auto const tgCS = commonScene .get_pipelines(); - auto const tgShSp = shapeSpawn .get_pipelines(); - - Session out; - OSP_DECLARE_CREATE_DATA_IDS(out, topData, TESTAPP_DATA_BOUNDS); - auto const tgBnds = out.create_pipelines(rBuilder); - - rBuilder.pipeline(tgBnds.boundsSet) .parent(tgScn.update); - rBuilder.pipeline(tgBnds.outOfBounds) .parent(tgScn.update); - - top_emplace< ActiveEntSet_t > (topData, idBounds); - top_emplace< ActiveEntVec_t > (topData, idOutOfBounds); - - rBuilder.task() - .name ("Check for out-of-bounds entities") - .run_on ({tgScn.update(Run)}) - .sync_with ({tgCS.transform(Ready), tgBnds.boundsSet(Ready), tgBnds.outOfBounds(Modify__)}) - .push_to (out.m_tasks) - .args ({ idBasic, idBounds, idOutOfBounds }) - .func([] (ACtxBasic const& rBasic, ActiveEntSet_t const& rBounds, ActiveEntVec_t& rOutOfBounds) noexcept - { - for (std::size_t const ent : rBounds.ones()) - { - ACompTransform const &entTf = rBasic.m_transform.get(ActiveEnt(ent)); - if (entTf.m_transform.translation().z() < -10) - { - rOutOfBounds.push_back(ActiveEnt(ent)); - } - } - }); - - rBuilder.task() - .name ("Queue-Delete out-of-bounds entities") - .run_on ({tgBnds.outOfBounds(UseOrRun_)}) - .sync_with ({tgCS.activeEntDelete(Modify_), tgCS.hierarchy(Delete)}) - .push_to (out.m_tasks) - .args ({ idBasic, idActiveEntDel, idOutOfBounds }) - .func([] (ACtxBasic& rBasic, ActiveEntVec_t& rActiveEntDel, ActiveEntVec_t& rOutOfBounds) noexcept - { - SysSceneGraph::queue_delete_entities(rBasic.m_scnGraph, rActiveEntDel, rOutOfBounds.begin(), rOutOfBounds.end()); - }); - - rBuilder.task() - .name ("Clear out-of-bounds vector once we're done with it") - .run_on ({tgBnds.outOfBounds(Clear_)}) - .push_to (out.m_tasks) - .args ({ idOutOfBounds }) - .func([] (ActiveEntVec_t& rOutOfBounds) noexcept - { - rOutOfBounds.clear(); - }); - - rBuilder.task() - .name ("Add bounds to spawned shapes") - .run_on ({tgShSp.spawnRequest(UseOrRun)}) - .sync_with ({tgShSp.spawnedEnts(UseOrRun), tgBnds.boundsSet(Modify)}) - .push_to (out.m_tasks) - .args ({ idBasic, idSpawner, idBounds }) - .func([] (ACtxBasic& rBasic, ACtxShapeSpawner& rSpawner, ActiveEntSet_t& rBounds) noexcept - { - rBounds.ints().resize(rBasic.m_activeIds.vec().capacity()); - - for (std::size_t i = 0; i < rSpawner.m_spawnRequest.size(); ++i) - { - SpawnShape const &spawn = rSpawner.m_spawnRequest[i]; - if (spawn.m_mass == 0) - { - continue; - } - - ActiveEnt const root = rSpawner.m_ents[i * 2]; - - rBounds.set(std::size_t(root)); - } - }); - - rBuilder.task() - .name ("Delete bounds components") - .run_on ({tgCS.activeEntDelete(UseOrRun)}) - .sync_with ({tgBnds.boundsSet(Delete)}) - .push_to (out.m_tasks) - .args ({ idActiveEntDel, idBounds }) - .func([] (ActiveEntVec_t const& rActiveEntDel, ActiveEntSet_t& rBounds) noexcept - { - for (osp::active::ActiveEnt const ent : rActiveEntDel) - { - rBounds.reset(std::size_t(ent)); - } - }); - - return out; -} // setup_bounds - - - - Session setup_cursor( TopTaskBuilder& rBuilder, ArrayView const topData, @@ -414,7 +147,7 @@ Session setup_cursor( MaterialId const material, PkgId const pkg) { - OSP_DECLARE_GET_DATA_IDS(application, TESTAPP_DATA_APPLICATION); + OSP_DECLARE_GET_DATA_IDS(application, TESTAPP_DATA_APPLICATION); OSP_DECLARE_GET_DATA_IDS(sceneRenderer, TESTAPP_DATA_SCENE_RENDERER); OSP_DECLARE_GET_DATA_IDS(commonScene, TESTAPP_DATA_COMMON_SCENE); OSP_DECLARE_GET_DATA_IDS(cameraCtrl, TESTAPP_DATA_CAMERA_CTRL); diff --git a/src/testapp/sessions/misc.h b/src/testapp/sessions/misc.h index b3318d81..012b9b2a 100644 --- a/src/testapp/sessions/misc.h +++ b/src/testapp/sessions/misc.h @@ -37,13 +37,6 @@ void create_materials( osp::Session const& sceneRenderer, int count); -void add_floor( - osp::ArrayView topData, - osp::Session const& shapeSpawn, - osp::draw::MaterialId material, - osp::PkgId pkg, - int size); - /** * @brief Create CameraController connected to an app's UserInputHandler */ @@ -64,36 +57,6 @@ osp::Session setup_camera_free( osp::Session const& scene, osp::Session const& camera); -/** - * @brief Throws spheres when pressing space - */ -osp::Session setup_thrower( - osp::TopTaskBuilder& rBuilder, - osp::ArrayView topData, - osp::Session const& windowApp, - osp::Session const& cameraCtrl, - osp::Session const& shapeSpawn); - -/** - * @brief Spawn blocks every 2 seconds and spheres every 1 second - */ -osp::Session setup_droppers( - osp::TopTaskBuilder& rBuilder, - osp::ArrayView topData, - osp::Session const& scene, - osp::Session const& commonScene, - osp::Session const& shapeSpawn); - -/** - * @brief Entity set to delete entities under Z = -10, added to spawned shapes - */ -osp::Session setup_bounds( - osp::TopTaskBuilder& rBuilder, - osp::ArrayView topData, - osp::Session const& scene, - osp::Session const& commonScene, - osp::Session const& shapeSpawn); - /** * @brief Wireframe cube over the camera controller's target */ diff --git a/src/testapp/sessions/newton.cpp b/src/testapp/sessions/newton.cpp index 4848baeb..a6f62cd9 100644 --- a/src/testapp/sessions/newton.cpp +++ b/src/testapp/sessions/newton.cpp @@ -24,9 +24,12 @@ */ #include "newton.h" #include "physics.h" +#include "shapes.h" #include #include +#include +#include #include #include #include @@ -165,24 +168,24 @@ osp::Session setup_newton_force_accel( -Session setup_shape_spawn_newton( +Session setup_phys_shapes_newton( TopTaskBuilder& rBuilder, ArrayView const topData, Session const& commonScene, Session const& physics, - Session const& shapeSpawn, + Session const& physShapes, Session const& newton, Session const& nwtFactors) { OSP_DECLARE_GET_DATA_IDS(commonScene, TESTAPP_DATA_COMMON_SCENE); OSP_DECLARE_GET_DATA_IDS(physics, TESTAPP_DATA_PHYSICS); - OSP_DECLARE_GET_DATA_IDS(shapeSpawn, TESTAPP_DATA_SHAPE_SPAWN); + OSP_DECLARE_GET_DATA_IDS(physShapes, TESTAPP_DATA_PHYS_SHAPES); OSP_DECLARE_GET_DATA_IDS(newton, TESTAPP_DATA_NEWTON); OSP_DECLARE_GET_DATA_IDS(nwtFactors, TESTAPP_DATA_NEWTON_FORCES); auto const tgPhy = physics .get_pipelines(); - auto const tgShSp = shapeSpawn .get_pipelines(); + auto const tgShSp = physShapes .get_pipelines(); auto const tgNwt = newton .get_pipelines(); Session out; @@ -192,14 +195,14 @@ Session setup_shape_spawn_newton( .run_on ({tgShSp.spawnRequest(UseOrRun)}) .sync_with ({tgShSp.spawnedEnts(UseOrRun), tgNwt.nwtBody(New), tgPhy.physUpdate(Done)}) .push_to (out.m_tasks) - .args({ idBasic, idSpawner, idPhys, idNwt, idNwtFactors }) - .func([] (ACtxBasic const &rBasic, ACtxShapeSpawner& rSpawner, ACtxPhysics& rPhys, ACtxNwtWorld& rNwt, ForceFactors_t nwtFactors) noexcept + .args({ idBasic, idPhysShapes, idPhys, idNwt, idNwtFactors }) + .func([] (ACtxBasic const &rBasic, ACtxPhysShapes& rPhysShapes, ACtxPhysics& rPhys, ACtxNwtWorld& rNwt, ForceFactors_t nwtFactors) noexcept { - for (std::size_t i = 0; i < rSpawner.m_spawnRequest.size(); ++i) + for (std::size_t i = 0; i < rPhysShapes.m_spawnRequest.size(); ++i) { - SpawnShape const &spawn = rSpawner.m_spawnRequest[i]; - ActiveEnt const root = rSpawner.m_ents[i * 2]; - ActiveEnt const child = rSpawner.m_ents[i * 2 + 1]; + SpawnShape const &spawn = rPhysShapes.m_spawnRequest[i]; + ActiveEnt const root = rPhysShapes.m_ents[i * 2]; + ActiveEnt const child = rPhysShapes.m_ents[i * 2 + 1]; NwtColliderPtr_t pCollision{ SysNewton::create_primative(rNwt, spawn.m_shape) }; SysNewton::orient_collision(pCollision.get(), spawn.m_shape, {0.0f, 0.0f, 0.0f}, Matrix3{}, spawn.m_size); @@ -226,11 +229,10 @@ Session setup_shape_spawn_newton( }); return out; -} // setup_shape_spawn_newton +} // setup_phys_shapes_newton -#if 0 void compound_collect_recurse( @@ -241,11 +243,10 @@ void compound_collect_recurse( Matrix4 const& transform, NewtonCollision* pCompound) { - EShape const shape = rCtxPhys.m_shape[std::size_t(ent)]; + EShape const shape = rCtxPhys.m_shape[ent]; if (shape != EShape::None) { - NwtColliderPtr_t &rPtr = rCtxWorld.m_colliders.contains(ent) ? rCtxWorld.m_colliders.get(ent) : rCtxWorld.m_colliders.emplace(ent); @@ -277,7 +278,6 @@ void compound_collect_recurse( rCtxPhys, rCtxWorld, rBasic, child, childMatrix, pCompound); } } - } @@ -285,113 +285,104 @@ void compound_collect_recurse( Session setup_vehicle_spawn_newton( TopTaskBuilder& rBuilder, ArrayView const topData, + Session const& application, Session const& commonScene, Session const& physics, Session const& prefabs, Session const& parts, Session const& vehicleSpawn, - Session const& newton, - TopDataId const idResources) + Session const& newton) { - OSP_SESSION_UNPACK_DATA(commonScene, TESTAPP_COMMON_SCENE); - OSP_SESSION_UNPACK_TAGS(commonScene, TESTAPP_COMMON_SCENE); - OSP_SESSION_UNPACK_DATA(physics, TESTAPP_PHYSICS); - OSP_SESSION_UNPACK_TAGS(physics, TESTAPP_PHYSICS); - OSP_SESSION_UNPACK_DATA(prefabs, TESTAPP_PREFABS); - OSP_SESSION_UNPACK_TAGS(prefabs, TESTAPP_PREFABS); - OSP_SESSION_UNPACK_DATA(vehicleSpawn, TESTAPP_VEHICLE_SPAWN); - OSP_SESSION_UNPACK_TAGS(vehicleSpawn, TESTAPP_VEHICLE_SPAWN); - OSP_SESSION_UNPACK_DATA(newton, TESTAPP_NEWTON); - OSP_SESSION_UNPACK_TAGS(newton, TESTAPP_NEWTON); - OSP_SESSION_UNPACK_DATA(parts, TESTAPP_PARTS); - OSP_SESSION_UNPACK_TAGS(parts, TESTAPP_PARTS); - - Session vehicleSpawnNwt; - //OSP_SESSION_ACQUIRE_DATA(vehicleSpawnNwt, topData, TESTAPP_VEHICLE_SPAWN_NWT); - OSP_SESSION_ACQUIRE_TAGS(vehicleSpawnNwt, rTags, TESTAPP_VEHICLE_SPAWN_NWT); - - rBuilder.tag(tgNwtVhWeldEntReq) .depend_on({tgNwtVhWeldEntMod}); - rBuilder.tag(tgNwtVhHierReq) .depend_on({tgNwtVhHierMod}); + OSP_DECLARE_GET_DATA_IDS(application, TESTAPP_DATA_APPLICATION); + OSP_DECLARE_GET_DATA_IDS(commonScene, TESTAPP_DATA_COMMON_SCENE); + OSP_DECLARE_GET_DATA_IDS(physics, TESTAPP_DATA_PHYSICS); + OSP_DECLARE_GET_DATA_IDS(prefabs, TESTAPP_DATA_PREFABS); + OSP_DECLARE_GET_DATA_IDS(vehicleSpawn, TESTAPP_DATA_VEHICLE_SPAWN); + OSP_DECLARE_GET_DATA_IDS(newton, TESTAPP_DATA_NEWTON); + OSP_DECLARE_GET_DATA_IDS(parts, TESTAPP_DATA_PARTS); + auto const tgCS = commonScene .get_pipelines(); + auto const tgPhy = physics .get_pipelines(); + auto const tgPf = prefabs .get_pipelines(); + auto const tgParts = parts .get_pipelines(); + auto const tgVhSp = vehicleSpawn .get_pipelines(); + auto const tgNwt = newton .get_pipelines(); + + Session out; rBuilder.task() - .name ("aaa") - .depends_on ({}) - .fulfills ({}) + .name ("Create root ActiveEnts for each Weld") + .run_on ({tgVhSp.spawnRequest(UseOrRun)}) + .sync_with ({tgCS.activeEnt(New), tgCS.activeEntResized(Schedule), tgParts.mapWeldActive(Modify), tgVhSp.rootEnts(Resize)}) .push_to (out.m_tasks) - vehicleSpawnNwt.task() = rBuilder.task().assign({tgSceneEvt, tgEntNew, tgNwtVhWeldEntMod}).data( - "Create entity for each rigid group", - .args({ idActiveIds, idVehicleSpawn, idScnParts }) - .func([] (ActiveReg_t& rActiveIds, ACtxVehicleSpawn& rVehicleSpawn, ACtxParts& rScnParts) noexcept + .args ({ idBasic, idVehicleSpawn, idScnParts}) + .func([] (ACtxBasic& rBasic, ACtxVehicleSpawn& rVehicleSpawn, ACtxParts& rScnParts) noexcept { - if (rVehicleSpawn.new_vehicle_count() == 0) - { - return; - } + LGRN_ASSERT(rVehicleSpawn.new_vehicle_count() != 0); - rVehicleSpawn.m_newWeldToEnt.resize(rVehicleSpawn.m_newWeldToWeld.size()); - rActiveIds.create(std::begin(rVehicleSpawn.m_newWeldToEnt), std::end(rVehicleSpawn.m_newWeldToEnt)); + rVehicleSpawn.rootEnts.resize(rVehicleSpawn.spawnedWelds.size()); + rBasic.m_activeIds.create(rVehicleSpawn.rootEnts.begin(), rVehicleSpawn.rootEnts.end()); // update WeldId->ActiveEnt mapping - auto itWeldEnt = std::begin(rVehicleSpawn.m_newWeldToEnt); - for (WeldId const weld : rVehicleSpawn.m_newWeldToWeld) + auto itWeldEnt = rVehicleSpawn.rootEnts.begin(); + for (WeldId const weld : rVehicleSpawn.spawnedWelds) { - rScnParts.m_weldToEnt[weld] = *itWeldEnt; - std::advance(itWeldEnt, 1); + rScnParts.weldToActive[weld] = *itWeldEnt; + ++itWeldEnt; } - })); + }); rBuilder.task() - .name ("aaa") - .depends_on ({}) - .fulfills ({}) + .name ("Add vehicle entities to Scene Graph") + .run_on ({tgVhSp.spawnRequest(UseOrRun)}) + .sync_with ({tgVhSp.rootEnts(UseOrRun), tgParts.mapWeldActive(Ready), tgPf.spawnedEnts(UseOrRun), tgPf.spawnRequest(UseOrRun), tgPf.inSubtree(Run), tgCS.transform(Ready), tgCS.hierarchy(Modify)}) .push_to (out.m_tasks) - vehicleSpawnNwt.task() = rBuilder.task().assign({tgSceneEvt, tgVsBasicInReq, tgVsWeldReq, tgNwtVhWeldEntReq, tgPrefabEntReq, tgNwtVhHierMod, tgPfParentHierMod, tgHierMod, tgTransformNew}).data( - "Add vehicle entities to Scene Graph", - .args({ idBasic, idActiveIds, idVehicleSpawn, idScnParts, idPrefabInit, idResources }) - .func([] (ACtxBasic& rBasic, ActiveReg_t const& rActiveIds, ACtxVehicleSpawn const& rVehicleSpawn, ACtxParts& rScnParts, ACtxPrefabInit& rPrefabInit, Resources& rResources) noexcept + .args ({ idBasic, idVehicleSpawn, idScnParts, idPrefabs, idResources}) + .func([] (ACtxBasic& rBasic, ACtxVehicleSpawn const& rVehicleSpawn, ACtxParts& rScnParts, ACtxPrefabs& rPrefabs, Resources& rResources) noexcept { + LGRN_ASSERT(rVehicleSpawn.new_vehicle_count() != 0); + // ActiveEnts created for welds + ActiveEnts created for vehicle prefabs - std::size_t const totalEnts = rVehicleSpawn.m_newWeldToEnt.size() + rPrefabInit.m_newEnts.size(); + std::size_t const totalEnts = rVehicleSpawn.rootEnts.size() + rPrefabs.newEnts.size(); - auto const& itWeldsFirst = std::begin(rVehicleSpawn.m_newWeldToWeld); - auto const& itWeldOffsetsLast = std::end(rVehicleSpawn.m_newVhWeldOffsets); - auto itWeldOffsets = std::begin(rVehicleSpawn.m_newVhWeldOffsets); + auto const& itWeldsFirst = rVehicleSpawn.spawnedWelds.begin(); + auto const& itWeldOffsetsLast = rVehicleSpawn.spawnedWeldOffsets.end(); + auto itWeldOffsets = rVehicleSpawn.spawnedWeldOffsets.begin(); - rBasic.m_scnGraph.resize(rActiveIds.capacity()); + rBasic.m_scnGraph.resize(rBasic.m_activeIds.capacity()); - for (ACtxVehicleSpawn::TmpToInit const& toInit : rVehicleSpawn.m_newVhBasicIn) + for (ACtxVehicleSpawn::TmpToInit const& toInit : rVehicleSpawn.spawnRequest) { auto const itWeldOffsetsNext = std::next(itWeldOffsets); - NewWeldId const weldOffsetNext = (itWeldOffsetsNext != itWeldOffsetsLast) - ? (*itWeldOffsetsNext) - : rVehicleSpawn.m_newWeldToWeld.size(); + SpWeldId const weldOffsetNext = (itWeldOffsetsNext != itWeldOffsetsLast) + ? (*itWeldOffsetsNext) + : SpWeldId(uint32_t(rVehicleSpawn.spawnedWelds.size())); - std::for_each(itWeldsFirst + (*itWeldOffsets), - itWeldsFirst + weldOffsetNext, - [&rBasic, &rScnParts, &rVehicleSpawn, &rPrefabInit, &rResources, &toInit] (WeldId const weld) + std::for_each(itWeldsFirst + std::size_t{*itWeldOffsets}, + itWeldsFirst + std::size_t{weldOffsetNext}, + [&rBasic, &rScnParts, &rVehicleSpawn, &rPrefabs, &rResources, &toInit] (WeldId const weld) { // Count parts in this weld first std::size_t entCount = 0; - for (PartId const part : rScnParts.m_weldToParts[weld]) + for (PartId const part : rScnParts.weldToParts[weld]) { - NewPartId const newPart = rVehicleSpawn.m_partToNewPart[part]; - uint32_t const prefabInit = rVehicleSpawn.m_newPartPrefabs[newPart]; - entCount += rPrefabInit.m_ents[prefabInit].size(); + SpPartId const newPart = rVehicleSpawn.partToSpawned[part]; + uint32_t const prefabInit = rVehicleSpawn.spawnedPrefabs[newPart]; + entCount += rPrefabs.spawnedEntsOffset[prefabInit].size(); } - ActiveEnt const weldEnt = rScnParts.m_weldToEnt[weld]; + ActiveEnt const weldEnt = rScnParts.weldToActive[weld]; - rBasic.m_transform.emplace(weldEnt, Matrix4::from(toInit.m_rotation.toMatrix(), toInit.m_position)); + rBasic.m_transform.emplace(weldEnt, Matrix4::from(toInit.rotation.toMatrix(), toInit.position)); SubtreeBuilder bldRoot = SysSceneGraph::add_descendants(rBasic.m_scnGraph, entCount + 1); SubtreeBuilder bldWeld = bldRoot.add_child(weldEnt, entCount); - for (PartId const part : rScnParts.m_weldToParts[weld]) + for (PartId const part : rScnParts.weldToParts[weld]) { - NewPartId const newPart = rVehicleSpawn.m_partToNewPart[part]; - uint32_t const prefabInit = rVehicleSpawn.m_newPartPrefabs[newPart]; - auto const& basic = rPrefabInit.m_basicIn[prefabInit]; - auto const& ents = rPrefabInit.m_ents[prefabInit]; + SpPartId const newPart = rVehicleSpawn.partToSpawned[part]; + uint32_t const prefabInit = rVehicleSpawn.spawnedPrefabs[newPart]; + auto const& basic = rPrefabs.spawnRequest[prefabInit]; + auto const& ents = rPrefabs.spawnedEntsOffset[prefabInit]; SysPrefabInit::add_to_subtree(basic, ents, rResources, bldWeld); } @@ -399,43 +390,38 @@ Session setup_vehicle_spawn_newton( itWeldOffsets = itWeldOffsetsNext; } - })); + }); rBuilder.task() - .name ("aaa") - .depends_on ({}) - .fulfills ({}) + .name ("Add Newton physics to Weld entities") + .run_on ({tgVhSp.spawnRequest(UseOrRun)}) + .sync_with ({tgVhSp.rootEnts(UseOrRun), tgPf.spawnedEnts(UseOrRun), tgCS.transform(Ready), tgPhy.physBody(Ready), tgNwt.nwtBody(New), tgPhy.physUpdate(Done), tgCS.hierarchy(Ready)}) .push_to (out.m_tasks) - vehicleSpawnNwt.task() = rBuilder.task().assign({tgSceneEvt, tgVsBasicInReq, tgVsWeldReq, tgNwtVhWeldEntReq, tgNwtVhHierReq, tgPfParentHierReq, tgNwtBodyMod}).data( - "Add Newton physics to rigid group entities", - .args({ idActiveIds, idBasic, idPhys, idNwt, idVehicleSpawn, idScnParts }) - .func([] (ActiveReg_t const &rActiveIds, ACtxBasic& rBasic, ACtxPhysics& rPhys, ACtxNwtWorld& rNwt, ACtxVehicleSpawn const& rVehicleSpawn, ACtxParts const& rScnParts) noexcept + .args ({ idBasic, idPhys, idNwt, idVehicleSpawn, idScnParts}) + .func([] (ACtxBasic& rBasic, ACtxPhysics& rPhys, ACtxNwtWorld& rNwt, ACtxVehicleSpawn const& rVehicleSpawn, ACtxParts const& rScnParts) noexcept { - if (rVehicleSpawn.new_vehicle_count() == 0) - { - return; - } + LGRN_ASSERT(rVehicleSpawn.new_vehicle_count() != 0); - rPhys.m_hasColliders.ints().resize(rActiveIds.vec().capacity()); + rPhys.m_hasColliders.ints().resize(rBasic.m_activeIds.vec().capacity()); - auto const& itWeldsFirst = std::begin(rVehicleSpawn.m_newWeldToWeld); - auto const& itWeldOffsetsLast = std::end(rVehicleSpawn.m_newVhWeldOffsets); - auto itWeldOffsets = std::begin(rVehicleSpawn.m_newVhWeldOffsets); + auto const& itWeldsFirst = std::begin(rVehicleSpawn.spawnedWelds); + auto const& itWeldOffsetsLast = std::end(rVehicleSpawn.spawnedWeldOffsets); + auto itWeldOffsets = std::begin(rVehicleSpawn.spawnedWeldOffsets); - for (ACtxVehicleSpawn::TmpToInit const& toInit : rVehicleSpawn.m_newVhBasicIn) + for (ACtxVehicleSpawn::TmpToInit const& toInit : rVehicleSpawn.spawnRequest) { auto const itWeldOffsetsNext = std::next(itWeldOffsets); - NewWeldId const weldOffsetNext = (itWeldOffsetsNext != itWeldOffsetsLast) + SpWeldId const weldOffsetNext = (itWeldOffsetsNext != itWeldOffsetsLast) ? (*itWeldOffsetsNext) - : rVehicleSpawn.m_newWeldToWeld.size(); + : SpWeldId(uint32_t(rVehicleSpawn.spawnedWelds.size())); - std::for_each(itWeldsFirst + (*itWeldOffsets), - itWeldsFirst + weldOffsetNext, + std::for_each(itWeldsFirst + std::size_t{*itWeldOffsets}, + itWeldsFirst + std::size_t{weldOffsetNext}, [&rBasic, &rScnParts, &rVehicleSpawn, &toInit, &rPhys, &rNwt] (WeldId const weld) { - ActiveEnt const weldEnt = rScnParts.m_weldToEnt[weld]; + ActiveEnt const weldEnt = rScnParts.weldToActive[weld]; - auto const transform = Matrix4::from(toInit.m_rotation.toMatrix(), toInit.m_position); + auto const transform = Matrix4::from(toInit.rotation.toMatrix(), toInit.position); NwtColliderPtr_t pCompound{ NewtonCreateCompoundCollision(rNwt.m_world.get(), 0) }; rPhys.m_hasColliders.set(std::size_t(weldEnt)); @@ -467,27 +453,27 @@ Session setup_vehicle_spawn_newton( SysPhysics::calculate_subtree_mass_inertia(rBasic.m_transform, rPhys, rBasic.m_scnGraph, weldEnt, inertiaTensor, comToOrigin); Matrix4 const inertiaTensorMat4{inertiaTensor}; - NewtonBodySetFullMassMatrix(pBody, totalMass, inertiaTensorMat4.data()); - - NewtonBodySetCentreOfMass(pBody, com.data()); - - NewtonBodySetGyroscopicTorque(pBody, 1); - NewtonBodySetMatrix(pBody, transform.data()); - NewtonBodySetLinearDamping(pBody, 0.0f); - NewtonBodySetAngularDamping(pBody, Vector3{0.0f}.data()); - NewtonBodySetForceAndTorqueCallback(pBody, &SysNewton::cb_force_torque); - NewtonBodySetTransformCallback(pBody, &SysNewton::cb_set_transform); - SysNewton::set_userdata_bodyid(pBody, bodyId); - - rPhys.m_setVelocity.emplace_back(weldEnt, toInit.m_velocity); + //NewtonBodySetMassMatrix(pBody, 0.0f, 1.0f, 1.0f, 1.0f); + NewtonBodySetFullMassMatrix (pBody, totalMass, inertiaTensorMat4.data()); + NewtonBodySetCentreOfMass (pBody, com.data()); + NewtonBodySetGyroscopicTorque (pBody, 1); + NewtonBodySetMatrix (pBody, transform.data()); + NewtonBodySetLinearDamping (pBody, 0.0f); + NewtonBodySetAngularDamping (pBody, Vector3{0.0f}.data()); + NewtonBodySetForceAndTorqueCallback (pBody, &SysNewton::cb_force_torque); + NewtonBodySetTransformCallback (pBody, &SysNewton::cb_set_transform); + SysNewton::set_userdata_bodyid (pBody, bodyId); + + rPhys.m_setVelocity.emplace_back(weldEnt, toInit.velocity); }); itWeldOffsets = itWeldOffsetsNext; } - })); + }); + + return out; +} // setup_vehicle_spawn_newton - return vehicleSpawnNwt; -} struct BodyRocket { @@ -521,29 +507,29 @@ static void assign_rockets( using adera::ports_magicrocket::gc_throttleIn; using adera::ports_magicrocket::gc_multiplierIn; - ActiveEnt const weldEnt = rScnParts.m_weldToEnt[weld]; - BodyId const body = rNwt.m_entToBody.at(weldEnt); + ActiveEnt const weldEnt = rScnParts.weldToActive[weld]; + BodyId const body = rNwt.m_entToBody.at(weldEnt); if (rRocketsNwt.m_bodyRockets.contains(body)) { rRocketsNwt.m_bodyRockets.erase(body); } - for (PartId const part : rScnParts.m_weldToParts[weld]) + for (PartId const part : rScnParts.weldToParts[weld]) { auto const sizeBefore = rTemp.size(); - for (MachinePair const pair : rScnParts.m_partToMachines[part]) + for (MachinePair const pair : rScnParts.partToMachines[part]) { - if (pair.m_type != gc_mtMagicRocket) + if (pair.type != gc_mtMagicRocket) { continue; // This machine is not a rocket } - MachAnyId const mach = machtypeRocket.m_localToAny[pair.m_local]; - auto const& portSpan = rFloatNodes.m_machToNode[mach]; - NodeId const throttleIn = connected_node(portSpan, gc_throttleIn.m_port); - NodeId const multiplierIn = connected_node(portSpan, gc_multiplierIn.m_port); + MachAnyId const mach = machtypeRocket.localToAny[pair.local]; + auto const& portSpan = rFloatNodes.machToNode[mach]; + NodeId const throttleIn = connected_node(portSpan, gc_throttleIn.port); + NodeId const multiplierIn = connected_node(portSpan, gc_multiplierIn.port); if ( (throttleIn == lgrn::id_null()) || (multiplierIn == lgrn::id_null()) ) @@ -552,7 +538,7 @@ static void assign_rockets( } BodyRocket &rBodyRocket = rTemp.emplace_back(); - rBodyRocket.m_local = pair.m_local; + rBodyRocket.m_local = pair.local; rBodyRocket.m_throttleIn = throttleIn; rBodyRocket.m_multiplierIn = multiplierIn; } @@ -564,16 +550,16 @@ static void assign_rockets( // calculate transform relative to body root // start from part, then walk parents up - ActiveEnt const partEnt = rScnParts.m_partToActive[part]; + ActiveEnt const partEnt = rScnParts.partToActive[part]; Matrix4 transform = rBasic.m_transform.get(partEnt).m_transform; - ActiveEnt parent = rBasic.m_scnGraph.m_entParent[std::size_t(partEnt)]; + ActiveEnt parent = rBasic.m_scnGraph.m_entParent[partEnt]; while (parent != weldEnt) { Matrix4 const& parentTransform = rBasic.m_transform.get(parent).m_transform; transform = parentTransform * transform; - parent = rBasic.m_scnGraph.m_entParent[std::size_t(parent)]; + parent = rBasic.m_scnGraph.m_entParent[parent]; } auto const rotation = Quaternion::fromMatrix(transform.rotation()); @@ -652,6 +638,7 @@ static void rocket_thrust_force(NewtonBody const* pBody, BodyId const body, ACtx Session setup_rocket_thrust_newton( TopTaskBuilder& rBuilder, ArrayView const topData, + Session const& scene, Session const& commonScene, Session const& physics, Session const& prefabs, @@ -660,55 +647,49 @@ Session setup_rocket_thrust_newton( Session const& newton, Session const& nwtFactors) { - OSP_SESSION_UNPACK_DATA(commonScene, TESTAPP_COMMON_SCENE); - OSP_SESSION_UNPACK_TAGS(commonScene, TESTAPP_COMMON_SCENE); - OSP_SESSION_UNPACK_DATA(physics, TESTAPP_PHYSICS); - OSP_SESSION_UNPACK_TAGS(physics, TESTAPP_PHYSICS); - OSP_SESSION_UNPACK_DATA(prefabs, TESTAPP_PREFABS); - OSP_SESSION_UNPACK_TAGS(prefabs, TESTAPP_PREFABS); - OSP_SESSION_UNPACK_DATA(parts, TESTAPP_PARTS); - OSP_SESSION_UNPACK_TAGS(parts, TESTAPP_PARTS); - OSP_SESSION_UNPACK_DATA(signalsFloat, TESTAPP_SIGNALS_FLOAT) - OSP_SESSION_UNPACK_TAGS(signalsFloat, TESTAPP_SIGNALS_FLOAT); - OSP_SESSION_UNPACK_DATA(newton, TESTAPP_NEWTON); - OSP_SESSION_UNPACK_TAGS(newton, TESTAPP_NEWTON); - OSP_SESSION_UNPACK_DATA(nwtFactors, TESTAPP_NEWTON_FORCES); - - - Session rocketNwt; - OSP_SESSION_ACQUIRE_DATA(rocketNwt, topData, TESTAPP_ROCKETS_NWT); + OSP_DECLARE_GET_DATA_IDS(scene, TESTAPP_DATA_SCENE); + OSP_DECLARE_GET_DATA_IDS(commonScene, TESTAPP_DATA_COMMON_SCENE); + OSP_DECLARE_GET_DATA_IDS(physics, TESTAPP_DATA_PHYSICS); + OSP_DECLARE_GET_DATA_IDS(parts, TESTAPP_DATA_PARTS); + OSP_DECLARE_GET_DATA_IDS(signalsFloat, TESTAPP_DATA_SIGNALS_FLOAT) + OSP_DECLARE_GET_DATA_IDS(newton, TESTAPP_DATA_NEWTON); + OSP_DECLARE_GET_DATA_IDS(nwtFactors, TESTAPP_DATA_NEWTON_FORCES); + auto const tgScn = scene .get_pipelines(); + auto const tgParts = parts .get_pipelines(); + auto const tgNwt = newton .get_pipelines(); + + Session out; + OSP_DECLARE_CREATE_DATA_IDS(out, topData, TESTAPP_DATA_ROCKETS_NWT); auto &rRocketsNwt = top_emplace< ACtxRocketsNwt >(topData, idRocketsNwt); rBuilder.task() - .name ("aaa") - .depends_on ({}) - .fulfills ({}) + .name ("Assign rockets to Newton bodies") + .run_on ({tgScn.update(Run)}) + .sync_with ({tgParts.weldIds(Ready), tgNwt.nwtBody(Ready), tgParts.connect(Ready)}) .push_to (out.m_tasks) - rocketNwt.task() = rBuilder.task().assign({tgSceneEvt, tgLinkReq, tgWeldReq, tgNwtBodyReq}).data( - "Assign rockets to Newton bodies", - .args({ idActiveIds, idBasic, idPhys, idNwt, idScnParts, idRocketsNwt, idNwtFactors }) - .func([] (ActiveReg_t const &rActiveIds, ACtxBasic& rBasic, ACtxPhysics& rPhys, ACtxNwtWorld& rNwt, ACtxParts const& rScnParts, ACtxRocketsNwt& rRocketsNwt, ForceFactors_t const& rNwtFactors) noexcept + .args ({ idBasic, idPhys, idNwt, idScnParts, idRocketsNwt, idNwtFactors}) + .func([] (ACtxBasic& rBasic, ACtxPhysics& rPhys, ACtxNwtWorld& rNwt, ACtxParts const& rScnParts, ACtxRocketsNwt& rRocketsNwt, ForceFactors_t const& rNwtFactors) noexcept { using adera::gc_mtMagicRocket; - Nodes const &rFloatNodes = rScnParts.m_nodePerType[gc_ntSigFloat]; - PerMachType const& machtypeRocket = rScnParts.m_machines.m_perType[gc_mtMagicRocket]; + Nodes const &rFloatNodes = rScnParts.nodePerType[gc_ntSigFloat]; + PerMachType const& machtypeRocket = rScnParts.machines.perType[gc_mtMagicRocket]; rRocketsNwt.m_bodyRockets.ids_reserve(rNwt.m_bodyIds.size()); - rRocketsNwt.m_bodyRockets.data_reserve(rScnParts.m_machines.m_perType[gc_mtMagicRocket].m_localIds.capacity()); + rRocketsNwt.m_bodyRockets.data_reserve(rScnParts.machines.perType[gc_mtMagicRocket].localIds.capacity()); std::vector temp; - for (WeldId const weld : rScnParts.m_weldDirty) + for (WeldId const weld : rScnParts.weldDirty) { assign_rockets(rBasic, rScnParts, rNwt, rRocketsNwt, rFloatNodes, machtypeRocket, rNwtFactors, weld, temp); } - })); + }); auto &rScnParts = top_get< ACtxParts > (topData, idScnParts); auto &rSigValFloat = top_get< SignalValues_t > (topData, idSigValFloat); - Machines &rMachines = rScnParts.m_machines; + Machines &rMachines = rScnParts.machines; ACtxNwtWorld::ForceFactorFunc const factor @@ -725,9 +706,9 @@ Session setup_rocket_thrust_newton( auto factorBits = lgrn::bit_view(top_get(topData, idNwtFactors)); factorBits.set(index); - return rocketNwt; -} -#endif + return out; +} // setup_rocket_thrust_newton + } // namespace testapp::scenes diff --git a/src/testapp/sessions/newton.h b/src/testapp/sessions/newton.h index 18964e03..18a659ee 100644 --- a/src/testapp/sessions/newton.h +++ b/src/testapp/sessions/newton.h @@ -72,12 +72,12 @@ osp::Session setup_newton_force_accel( /** * @brief Support for Shape Spawner physics using Newton Dynamics */ -osp::Session setup_shape_spawn_newton( +osp::Session setup_phys_shapes_newton( osp::TopTaskBuilder& rBuilder, osp::ArrayView topData, osp::Session const& commonScene, osp::Session const& physics, - osp::Session const& shapeSpawn, + osp::Session const& physShapes, osp::Session const& newton, osp::Session const& nwtFactors); @@ -87,13 +87,13 @@ osp::Session setup_shape_spawn_newton( osp::Session setup_vehicle_spawn_newton( osp::TopTaskBuilder& rBuilder, osp::ArrayView topData, + osp::Session const& application, osp::Session const& commonScene, osp::Session const& physics, osp::Session const& prefabs, osp::Session const& parts, osp::Session const& vehicleSpawn, - osp::Session const& newton, - osp::TopDataId const idResources); + osp::Session const& newton); /** * @brief Add thrust forces to Magic Rockets from setup_mach_rocket @@ -101,6 +101,7 @@ osp::Session setup_vehicle_spawn_newton( osp::Session setup_rocket_thrust_newton( osp::TopTaskBuilder& rBuilder, osp::ArrayView topData, + osp::Session const& scene, osp::Session const& commonScene, osp::Session const& physics, osp::Session const& prefabs, diff --git a/src/testapp/sessions/physics.cpp b/src/testapp/sessions/physics.cpp index 293c4606..e44f82be 100644 --- a/src/testapp/sessions/physics.cpp +++ b/src/testapp/sessions/physics.cpp @@ -29,10 +29,14 @@ #include #include #include -#include #include +#include #include +#include +#include + + using namespace osp; using namespace osp::active; using namespace osp::draw; @@ -78,16 +82,18 @@ Session setup_physics( } // setup_physics +//----------------------------------------------------------------------------- -Session setup_shape_spawn( +Session setup_prefabs( TopTaskBuilder& rBuilder, ArrayView const topData, + Session const& application, Session const& scene, Session const& commonScene, - Session const& physics, - MaterialId const materialId) + Session const& physics) { + OSP_DECLARE_GET_DATA_IDS(application, TESTAPP_DATA_APPLICATION); OSP_DECLARE_GET_DATA_IDS(commonScene, TESTAPP_DATA_COMMON_SCENE); OSP_DECLARE_GET_DATA_IDS(physics, TESTAPP_DATA_PHYSICS); auto const tgScn = scene .get_pipelines(); @@ -95,395 +101,168 @@ Session setup_shape_spawn( auto const tgPhy = physics .get_pipelines(); Session out; - OSP_DECLARE_CREATE_DATA_IDS(out, topData, TESTAPP_DATA_SHAPE_SPAWN); - auto const tgShSp = out.create_pipelines(rBuilder); + OSP_DECLARE_CREATE_DATA_IDS(out, topData, TESTAPP_DATA_PREFABS); + auto const tgPf = out.create_pipelines(rBuilder); - rBuilder.pipeline(tgShSp.spawnRequest) .parent(tgScn.update); - rBuilder.pipeline(tgShSp.spawnedEnts) .parent(tgScn.update); + rBuilder.pipeline(tgPf.spawnRequest).parent(tgScn.update); + rBuilder.pipeline(tgPf.spawnedEnts) .parent(tgScn.update); + rBuilder.pipeline(tgPf.ownedEnts) .parent(tgScn.update); + rBuilder.pipeline(tgPf.instanceInfo).parent(tgScn.update); + rBuilder.pipeline(tgPf.inSubtree) .parent(tgScn.update); + + top_emplace< ACtxPrefabs > (topData, idPrefabs); rBuilder.task() - .name ("Schedule Shape spawn") - .schedules ({tgShSp.spawnRequest(Schedule_)}) + .name ("Schedule Prefab spawn") + .schedules ({tgPf.spawnRequest(Schedule_)}) + .sync_with ({tgScn.update(Run)}) .push_to (out.m_tasks) - .args ({ idSpawner }) - .func([] (ACtxShapeSpawner& rSpawner) noexcept -> TaskActions + .args ({ idPrefabs }) + .func([] (ACtxPrefabs const& rPrefabs) noexcept -> TaskActions { - return rSpawner.m_spawnRequest.empty() ? TaskAction::Cancel : TaskActions{}; + return rPrefabs.spawnRequest.empty() ? TaskAction::Cancel : TaskActions{}; }); - rBuilder.pipeline(tgShSp.ownedEnts) .parent(tgScn.update); - top_emplace< ACtxShapeSpawner > (topData, idSpawner, ACtxShapeSpawner{ .m_materialId = materialId }); rBuilder.task() - .name ("Create ActiveEnts for requested shapes to spawn") - .run_on ({tgShSp.spawnRequest(UseOrRun)}) - .sync_with ({tgCS.activeEnt(New), tgCS.activeEntResized(Schedule), tgShSp.spawnedEnts(Resize)}) + .name ("Create Prefab entities") + .run_on ({tgPf.spawnRequest(UseOrRun)}) + .sync_with ({tgCS.activeEnt(New), tgCS.activeEntResized(Schedule), tgPf.spawnedEnts(Resize)}) .push_to (out.m_tasks) - .args ({ idBasic, idSpawner}) - .func([] (ACtxBasic& rBasic, ACtxShapeSpawner& rSpawner) noexcept + .args ({ idPrefabs, idBasic, idResources}) + .func([] (ACtxPrefabs& rPrefabs, ACtxBasic& rBasic, Resources& rResources) noexcept { - LGRN_ASSERTM(!rSpawner.m_spawnRequest.empty(), "spawnRequest Use_ shouldn't run if rSpawner.m_spawnRequest is empty!"); - - rSpawner.m_ents.resize(rSpawner.m_spawnRequest.size() * 2); - rBasic.m_activeIds.create(rSpawner.m_ents.begin(), rSpawner.m_ents.end()); + SysPrefabInit::create_activeents(rPrefabs, rBasic, rResources); }); rBuilder.task() - .name ("Add hierarchy and transform to spawned shapes") - .run_on ({tgShSp.spawnRequest(UseOrRun)}) - .sync_with ({tgShSp.spawnedEnts(UseOrRun), tgShSp.ownedEnts(Modify__), tgCS.hierarchy(New), tgCS.transform(New)}) + .name ("Init Prefab transforms") + .run_on ({tgPf.spawnRequest(UseOrRun)}) + .sync_with ({tgPf.spawnedEnts(UseOrRun), tgCS.transform(New)}) .push_to (out.m_tasks) - .args ({ idBasic, idSpawner }) - .func([] (ACtxBasic& rBasic, ACtxShapeSpawner& rSpawner) noexcept + .args ({ idBasic, idResources, idPrefabs}) + .func([] (ACtxBasic& rBasic, Resources& rResources, ACtxPrefabs& rPrefabs) noexcept { - osp::bitvector_resize(rSpawner.m_ownedEnts, rBasic.m_activeIds.capacity()); - rBasic.m_scnGraph.resize(rBasic.m_activeIds.capacity()); - - SubtreeBuilder bldScnRoot = SysSceneGraph::add_descendants(rBasic.m_scnGraph, rSpawner.m_spawnRequest.size() * 2); - - for (std::size_t i = 0; i < rSpawner.m_spawnRequest.size(); ++i) - { - SpawnShape const &spawn = rSpawner.m_spawnRequest[i]; - ActiveEnt const root = rSpawner.m_ents[i * 2]; - ActiveEnt const child = rSpawner.m_ents[i * 2 + 1]; - - rSpawner.m_ownedEnts.set(std::size_t(root)); - - rBasic.m_transform.emplace(root, ACompTransform{osp::Matrix4::translation(spawn.m_position)}); - rBasic.m_transform.emplace(child, ACompTransform{Matrix4::scaling(spawn.m_size)}); - SubtreeBuilder bldRoot = bldScnRoot.add_child(root, 1); - bldRoot.add_child(child); - } + SysPrefabInit::init_transforms(rPrefabs, rResources, rBasic.m_transform); }); rBuilder.task() - .name ("Add physics to spawned shapes") - .run_on ({tgShSp.spawnRequest(UseOrRun)}) - .sync_with ({tgShSp.spawnedEnts(UseOrRun), tgPhy.physBody(Modify), tgPhy.physUpdate(Done)}) + .name ("Init Prefab instance info") + .run_on ({tgPf.spawnRequest(UseOrRun)}) + .sync_with ({tgPf.spawnedEnts(UseOrRun), tgPf.instanceInfo(Modify)}) .push_to (out.m_tasks) - .args ({ idBasic, idSpawner, idPhys }) - .func([] (ACtxBasic const& rBasic, ACtxShapeSpawner& rSpawner, ACtxPhysics& rPhys) noexcept + .args ({ idBasic, idResources, idPrefabs}) + .func([] (ACtxBasic& rBasic, Resources& rResources, ACtxPrefabs& rPrefabs) noexcept { - rPhys.m_hasColliders.ints().resize(rBasic.m_activeIds.vec().capacity()); - rPhys.m_shape.resize(rBasic.m_activeIds.capacity()); - - for (std::size_t i = 0; i < rSpawner.m_spawnRequest.size(); ++i) - { - SpawnShape const &spawn = rSpawner.m_spawnRequest[i]; - ActiveEnt const root = rSpawner.m_ents[i * 2]; - ActiveEnt const child = rSpawner.m_ents[i * 2 + 1]; - - rPhys.m_hasColliders.set(std::size_t(root)); - if (spawn.m_mass != 0.0f) - { - rPhys.m_setVelocity.emplace_back(root, spawn.m_velocity); - Vector3 const inertia = collider_inertia_tensor(spawn.m_shape, spawn.m_size, spawn.m_mass); - Vector3 const offset{0.0f, 0.0f, 0.0f}; - rPhys.m_mass.emplace( child, ACompMass{ inertia, offset, spawn.m_mass } ); - } - - rPhys.m_shape[child] = spawn.m_shape; - rPhys.m_colliderDirty.push_back(child); - } + rPrefabs.instanceInfo.resize(rBasic.m_activeIds.capacity(), PrefabInstanceInfo{.prefab = lgrn::id_null()}); + osp::bitvector_resize(rPrefabs.roots, rBasic.m_activeIds.capacity()); + SysPrefabInit::init_info(rPrefabs, rResources); }); - //TODO rBuilder.task() - .name ("Delete basic components") - .run_on ({tgCS.activeEntDelete(UseOrRun)}) - .sync_with ({tgShSp.ownedEnts(Modify__)}) + .name ("Init Prefab physics") + .run_on ({tgPf.spawnRequest(UseOrRun)}) + .sync_with ({tgPf.spawnedEnts(UseOrRun), tgPhy.physBody(Modify), tgPhy.physUpdate(Done)}) .push_to (out.m_tasks) - .args ({ idBasic, idActiveEntDel }) - .func([] (ACtxBasic& rBasic, ActiveEntVec_t const& rActiveEntDel) noexcept + .args ({ idBasic, idResources, idPhys, idPrefabs}) + .func([] (ACtxBasic& rBasic, Resources& rResources, ACtxPhysics& rPhys, ACtxPrefabs& rPrefabs) noexcept { - update_delete_basic(rBasic, rActiveEntDel.cbegin(), rActiveEntDel.cend()); + rPhys.m_hasColliders.ints().resize(rBasic.m_activeIds.vec().capacity()); + //rPhys.m_massDirty.ints().resize(rActiveIds.vec().capacity()); + rPhys.m_shape.resize(rBasic.m_activeIds.capacity()); + SysPrefabInit::init_physics(rPrefabs, rResources, rPhys); }); rBuilder.task() - .name ("Clear Shape Spawning vector after use") - .run_on ({tgShSp.spawnRequest(Clear)}) + .name ("Clear Prefab vector") + .run_on ({tgPf.spawnRequest(Clear)}) .push_to (out.m_tasks) - .args ({ idSpawner }) - .func([] (ACtxShapeSpawner& rSpawner) noexcept + .args ({ idPrefabs}) + .func([] (ACtxPrefabs& rPrefabs) noexcept { - rSpawner.m_spawnRequest.clear(); + rPrefabs.spawnRequest.clear(); }); - return out; -} // setup_shape_spawn +} // setup_prefabs -Session setup_shape_spawn_draw( +Session setup_prefab_draw( TopTaskBuilder& rBuilder, ArrayView const topData, + Session const& application, Session const& windowApp, Session const& sceneRenderer, Session const& commonScene, - Session const& physics, - Session const& shapeSpawn) + Session const& prefabs, + MaterialId material) { + OSP_DECLARE_GET_DATA_IDS(application, TESTAPP_DATA_APPLICATION); OSP_DECLARE_GET_DATA_IDS(sceneRenderer, TESTAPP_DATA_SCENE_RENDERER); OSP_DECLARE_GET_DATA_IDS(commonScene, TESTAPP_DATA_COMMON_SCENE); - OSP_DECLARE_GET_DATA_IDS(physics, TESTAPP_DATA_PHYSICS); - OSP_DECLARE_GET_DATA_IDS(shapeSpawn, TESTAPP_DATA_SHAPE_SPAWN); + OSP_DECLARE_GET_DATA_IDS(prefabs, TESTAPP_DATA_PREFABS); auto const tgWin = windowApp .get_pipelines< PlWindowApp >(); auto const tgScnRdr = sceneRenderer .get_pipelines< PlSceneRenderer >(); auto const tgCS = commonScene .get_pipelines< PlCommonScene >(); - auto const tgShSp = shapeSpawn .get_pipelines< PlShapeSpawn >(); + auto const tgPf = prefabs .get_pipelines< PlPrefabs >(); Session out; + auto const [idMaterial] = out.acquire_data<1>(topData); + + top_emplace(topData, idMaterial, material); rBuilder.task() - .name ("Create DrawEnts for spawned shapes") - .run_on ({tgShSp.spawnRequest(UseOrRun)}) - .sync_with ({tgShSp.spawnedEnts(UseOrRun), tgCS.activeEntResized(Done), tgScnRdr.drawEntResized(ModifyOrSignal)}) + .name ("Create DrawEnts for prefabs") + .run_on ({tgPf.spawnRequest(UseOrRun)}) + .sync_with ({tgPf.spawnedEnts(UseOrRun), tgCS.activeEntResized(Done), tgScnRdr.drawEntResized(ModifyOrSignal)}) .push_to (out.m_tasks) - .args ({ idBasic, idDrawing, idScnRender, idSpawner, idNMesh }) - .func([] (ACtxBasic const& rBasic, ACtxDrawing& rDrawing, ACtxSceneRender& rScnRender, ACtxShapeSpawner& rSpawner, NamedMeshes& rNMesh) noexcept + .args ({ idPrefabs, idResources, idBasic, idDrawing, idScnRender}) + .func([] (ACtxPrefabs& rPrefabs, Resources& rResources, ACtxBasic const& rBasic, ACtxDrawing& rDrawing, ACtxSceneRender& rScnRender) noexcept { - for (std::size_t i = 0; i < rSpawner.m_spawnRequest.size(); ++i) - { - ActiveEnt const child = rSpawner.m_ents[i * 2 + 1]; - rScnRender.m_activeToDraw[child] = rScnRender.m_drawIds.create(); - } + SysPrefabDraw::init_drawents(rPrefabs, rResources, rBasic, rDrawing, rScnRender); }); rBuilder.task() - .name ("Add mesh and material to spawned shapes") - .run_on ({tgShSp.spawnRequest(UseOrRun)}) - .sync_with ({tgShSp.spawnedEnts(UseOrRun), + .name ("Add mesh and material to prefabs") + .run_on ({tgPf.spawnRequest(UseOrRun)}) + .sync_with ({tgPf.spawnedEnts(UseOrRun), tgScnRdr.entMesh(New), tgScnRdr.material(New), tgScnRdr.drawEnt(New), tgScnRdr.drawEntResized(Done), tgScnRdr.materialDirty(Modify_), tgScnRdr.entMeshDirty(Modify_)}) .push_to (out.m_tasks) - .args ({ idBasic, idDrawing, idScnRender, idSpawner, idNMesh }) - .func([] (ACtxBasic const& rBasic, ACtxDrawing& rDrawing, ACtxSceneRender& rScnRender, ACtxShapeSpawner& rSpawner, NamedMeshes& rNMesh) noexcept + .args ({ idPrefabs, idResources, idBasic, idDrawing, idDrawingRes, idScnRender, idMaterial}) + .func([] (ACtxPrefabs& rPrefabs, Resources& rResources, ACtxBasic const& rBasic, ACtxDrawing& rDrawing, ACtxDrawingRes& rDrawingRes, ACtxSceneRender& rScnRender, MaterialId material) noexcept { - Material &rMat = rScnRender.m_materials[rSpawner.m_materialId]; - - for (std::size_t i = 0; i < rSpawner.m_spawnRequest.size(); ++i) - { - SpawnShape const &spawn = rSpawner.m_spawnRequest[i]; - ActiveEnt const root = rSpawner.m_ents[i * 2]; - ActiveEnt const child = rSpawner.m_ents[i * 2 + 1]; - DrawEnt const drawEnt = rScnRender.m_activeToDraw[child]; - - rScnRender.m_needDrawTf.set(std::size_t(root)); - rScnRender.m_needDrawTf.set(std::size_t(child)); - - rScnRender.m_mesh[drawEnt] = rDrawing.m_meshRefCounts.ref_add(rNMesh.m_shapeToMesh.at(spawn.m_shape)); - rScnRender.m_meshDirty.push_back(drawEnt); - - rMat.m_ents.set(std::size_t(drawEnt)); - rMat.m_dirty.push_back(drawEnt); - - rScnRender.m_visible.set(std::size_t(drawEnt)); - rScnRender.m_opaque.set(std::size_t(drawEnt)); - } + SysPrefabDraw::init_mesh_and_material(rPrefabs, rResources, rBasic, rDrawing, rDrawingRes, rScnRender, material); }); - // When does resync run relative to deletes? rBuilder.task() .name ("Resync spawned shapes DrawEnts") .run_on ({tgWin.resync(Run)}) - .sync_with ({tgShSp.ownedEnts(UseOrRun_), tgCS.hierarchy(Ready), tgCS.activeEntResized(Done), tgScnRdr.drawEntResized(ModifyOrSignal)}) + .sync_with ({tgPf.ownedEnts(UseOrRun_), tgCS.hierarchy(Ready), tgCS.activeEntResized(Done), tgScnRdr.drawEntResized(ModifyOrSignal)}) .push_to (out.m_tasks) - .args ({ idBasic, idDrawing, idScnRender, idSpawner, idNMesh }) - .func([] (ACtxBasic const& rBasic, ACtxDrawing& rDrawing, ACtxSceneRender& rScnRender, ACtxShapeSpawner& rSpawner, NamedMeshes& rNMesh) noexcept + .args ({ idPrefabs, idResources, idBasic, idDrawing, idScnRender}) + .func([] (ACtxPrefabs& rPrefabs, Resources& rResources, ACtxBasic const& rBasic, ACtxDrawing& rDrawing, ACtxSceneRender& rScnRender) noexcept { - for (std::size_t entInt : rSpawner.m_ownedEnts.ones()) - { - ActiveEnt const root = ActiveEnt(entInt); - ActiveEnt const child = *SysSceneGraph::children(rBasic.m_scnGraph, root).begin(); - - rScnRender.m_activeToDraw[child] = rScnRender.m_drawIds.create(); - } + SysPrefabDraw::resync_drawents(rPrefabs, rResources, rBasic, rDrawing, rScnRender); }); rBuilder.task() .name ("Resync spawned shapes mesh and material") .run_on ({tgWin.resync(Run)}) - .sync_with ({tgShSp.ownedEnts(UseOrRun_), tgScnRdr.entMesh(New), tgScnRdr.material(New), tgScnRdr.drawEnt(New), tgScnRdr.drawEntResized(Done), + .sync_with ({tgPf.ownedEnts(UseOrRun_), tgScnRdr.entMesh(New), tgScnRdr.material(New), tgScnRdr.drawEnt(New), tgScnRdr.drawEntResized(Done), tgScnRdr.materialDirty(Modify_), tgScnRdr.entMeshDirty(Modify_)}) .push_to (out.m_tasks) - .args ({ idBasic, idDrawing, idPhys, idSpawner, idScnRender, idNMesh }) - .func([] (ACtxBasic const& rBasic, ACtxDrawing& rDrawing, ACtxPhysics& rPhys, ACtxShapeSpawner& rSpawner, ACtxSceneRender& rScnRender, NamedMeshes& rNMesh) noexcept + .args ({ idPrefabs, idResources, idBasic, idDrawing, idDrawingRes, idScnRender, idMaterial}) + .func([] (ACtxPrefabs& rPrefabs, Resources& rResources, ACtxBasic const& rBasic, ACtxDrawing& rDrawing, ACtxDrawingRes& rDrawingRes, ACtxSceneRender& rScnRender, MaterialId material) noexcept { - Material &rMat = rScnRender.m_materials[rSpawner.m_materialId]; - - for (std::size_t entInt : rSpawner.m_ownedEnts.ones()) - { - ActiveEnt const root = ActiveEnt(entInt); - ActiveEnt const child = *SysSceneGraph::children(rBasic.m_scnGraph, root).begin(); - - //SpawnShape const &spawn = rSpawner.m_spawnRequest[i]; - DrawEnt const drawEnt = rScnRender.m_activeToDraw[child]; - - rScnRender.m_needDrawTf.set(std::size_t(root)); - rScnRender.m_needDrawTf.set(std::size_t(child)); - - EShape const shape = rPhys.m_shape.at(child); - rScnRender.m_mesh[drawEnt] = rDrawing.m_meshRefCounts.ref_add(rNMesh.m_shapeToMesh.at(shape)); - rScnRender.m_meshDirty.push_back(drawEnt); - - rMat.m_ents.set(std::size_t(drawEnt)); - rMat.m_dirty.push_back(drawEnt); - - rScnRender.m_visible.set(std::size_t(drawEnt)); - rScnRender.m_opaque.set(std::size_t(drawEnt)); - } + SysPrefabDraw::resync_mesh_and_material(rPrefabs, rResources, rBasic, rDrawing, rDrawingRes, rScnRender, material); }); - rBuilder.task() - .name ("Remove deleted ActiveEnts from ACtxShapeSpawner") - .run_on ({tgCS.activeEntDelete(UseOrRun)}) - .sync_with ({tgShSp.ownedEnts(Modify__)}) - .push_to (out.m_tasks) - .args ({ idSpawner, idActiveEntDel }) - .func([] (ACtxShapeSpawner& rSpawner, ActiveEntVec_t const& rActiveEntDel) noexcept - { - for (ActiveEnt const deleted : rActiveEntDel) - { - rSpawner.m_ownedEnts.reset(std::size_t(deleted)); - } - }); return out; -} // setup_shape_spawn_draw - - -#if 0 - -Session setup_prefabs( - TopTaskBuilder& rBuilder, - ArrayView const topData, - Tags& rTags, - Session const& commonScene, - Session const& physics, - Session const& material, - TopDataId const idResources) -{ - OSP_SESSION_UNPACK_DATA(commonScene, TESTAPP_COMMON_SCENE); - OSP_SESSION_UNPACK_TAGS(commonScene, TESTAPP_COMMON_SCENE); - OSP_SESSION_UNPACK_DATA(physics, TESTAPP_PHYSICS); - OSP_SESSION_UNPACK_TAGS(physics, TESTAPP_PHYSICS); - - Session prefabs; - OSP_SESSION_ACQUIRE_DATA(prefabs, topData, TESTAPP_PREFABS); - OSP_SESSION_ACQUIRE_TAGS(prefabs, rTags, TESTAPP_PREFABS); - - rBuilder.tag(tgPrefabReq) .depend_on({tgPrefabMod}); - rBuilder.tag(tgPrefabClr) .depend_on({tgPrefabMod, tgPrefabReq}); - rBuilder.tag(tgPfParentHierReq) .depend_on({tgPfParentHierMod}); - rBuilder.tag(tgPrefabEntReq) .depend_on({tgPrefabEntMod}); - - top_emplace< ACtxPrefabInit > (topData, idPrefabInit); - - prefabs.task() = rBuilder.task().assign({tgSceneEvt, tgPrefabReq, tgPrefabEntMod, tgEntNew}).data( - "Create Prefab entities", - TopDataIds_t{ idActiveIds, idPrefabInit, idResources }, - wrap_args([] (ActiveReg_t& rActiveIds, ACtxPrefabInit& rPrefabInit, Resources& rResources) noexcept - { - // Count number of entities needed to be created - std::size_t totalEnts = 0; - for (TmpPrefabInitBasic const& rPfBasic : rPrefabInit.m_basicIn) - { - auto const& rPrefabData = rResources.data_get(gc_importer, rPfBasic.m_importerRes); - auto const& objects = rPrefabData.m_prefabs[rPfBasic.m_prefabId]; - - totalEnts += objects.size(); - } - - // Create entities - rPrefabInit.m_newEnts.resize(totalEnts); - rActiveIds.create(std::begin(rPrefabInit.m_newEnts), std::end(rPrefabInit.m_newEnts)); - - // Assign new entities to each prefab to create - rPrefabInit.m_ents.resize(rPrefabInit.m_basicIn.size()); - auto itEntAvailable = std::begin(rPrefabInit.m_newEnts); - auto itPfEntSpanOut = std::begin(rPrefabInit.m_ents); - for (TmpPrefabInitBasic& rPfBasic : rPrefabInit.m_basicIn) - { - auto const& rPrefabData = rResources.data_get(gc_importer, rPfBasic.m_importerRes); - auto const& objects = rPrefabData.m_prefabs[rPfBasic.m_prefabId]; - - (*itPfEntSpanOut) = { &(*itEntAvailable), objects.size() }; - - std::advance(itEntAvailable, objects.size()); - std::advance(itPfEntSpanOut, 1); - } - - assert(itEntAvailable == std::end(rPrefabInit.m_newEnts)); - })); - - prefabs.task() = rBuilder.task().assign({tgSceneEvt, tgPrefabReq, tgPrefabEntReq, tgHierNew, tgPfParentHierReq}).data( - "Init Prefab hierarchy", - TopDataIds_t{ idPrefabInit, idResources, idBasic }, - wrap_args([] (ACtxPrefabInit& rPrefabInit, Resources& rResources, ACtxBasic& rBasic) noexcept - { - //SysPrefabInit::init_hierarchy(rPrefabInit, rResources, rBasic.m_hierarchy); - })); - - prefabs.task() = rBuilder.task().assign({tgSceneEvt, tgPrefabReq, tgPrefabEntReq, PlransformNew}).data( - "Init Prefab transforms", - TopDataIds_t{ idPrefabInit, idResources, idBasic }, - wrap_args([] (ACtxPrefabInit& rPrefabInit, Resources& rResources, ACtxBasic& rBasic) noexcept - { - SysPrefabInit::init_transforms(rPrefabInit, rResources, rBasic.m_transform); - })); - - if (material.m_dataIds.empty()) - { - prefabs.task() = rBuilder.task().assign({tgSceneEvt, tgPrefabReq, tgPrefabEntReq, tgDrawMod, tgMeshMod}).data( - "Init Prefab drawables (no material)", - TopDataIds_t{ idPrefabInit, idResources, idDrawing, idDrawingRes }, - wrap_args([] (ACtxPrefabInit& rPrefabInit, Resources& rResources, ACtxDrawing& rDrawing, ACtxDrawingRes& rDrawingRes) noexcept - { - SysPrefabInit::init_drawing(rPrefabInit, rResources, rDrawing, rDrawingRes, {}); - })); - } - else - { - OSP_SESSION_UNPACK_DATA(material, TESTAPP_MATERIAL); - OSP_SESSION_UNPACK_TAGS(material, TESTAPP_MATERIAL); - - prefabs.task() = rBuilder.task().assign({tgSceneEvt, tgPrefabReq, tgPrefabEntReq, tgDrawMod, tgMeshMod, tgMatMod}).data( - "Init Prefab drawables (single material)", - TopDataIds_t{ idPrefabInit, idResources, idDrawing, idDrawingRes, idMatEnts, idMatDirty, idActiveIds }, - wrap_args([] (ACtxPrefabInit& rPrefabInit, Resources& rResources, ACtxDrawing& rDrawing, ACtxDrawingRes& rDrawingRes, EntSet_t& rMatEnts, std::vector& rMatDirty, ActiveReg_t const& rActiveIds) noexcept - { - rDrawing.resize_active(rActiveIds.capacity()); - rMatEnts.ints().resize(rActiveIds.vec().capacity()); - SysPrefabInit::init_drawing(rPrefabInit, rResources, rDrawing, rDrawingRes, {{rMatEnts, rMatDirty}}); - })); - } - - prefabs.task() = rBuilder.task().assign({tgSceneEvt, tgPrefabReq, tgPrefabEntReq, tgPhysMod}).data( - "Init Prefab physics", - TopDataIds_t{ idActiveIds, idPrefabInit, idResources, idPhys}, - wrap_args([] (ActiveReg_t const& rActiveIds, ACtxPrefabInit& rPrefabInit, Resources& rResources, ACtxPhysics& rPhys) noexcept - { - rPhys.m_hasColliders.ints().resize(rActiveIds.vec().capacity()); - //rPhys.m_massDirty.ints().resize(rActiveIds.vec().capacity()); - rPhys.m_shape.resize(rActiveIds.capacity()); - SysPrefabInit::init_physics(rPrefabInit, rResources, rPhys); - })); - - - prefabs.task() = rBuilder.task().assign({tgSceneEvt, tgPrefabClr}).data( - "Clear Prefab vector", - TopDataIds_t{ idPrefabInit }, - wrap_args([] (ACtxPrefabInit& rPrefabInit) noexcept - { - rPrefabInit.m_basicIn.clear(); - })); - - - return prefabs; -} +} // setup_phys_shapes_draw -#endif } // namespace testapp::scenes diff --git a/src/testapp/sessions/physics.h b/src/testapp/sessions/physics.h index 07256bc4..f8a72854 100644 --- a/src/testapp/sessions/physics.h +++ b/src/testapp/sessions/physics.h @@ -37,23 +37,6 @@ namespace testapp::scenes { -struct SpawnShape -{ - osp::Vector3 m_position; - osp::Vector3 m_velocity; - osp::Vector3 m_size; - float m_mass; - osp::EShape m_shape; -}; - -struct ACtxShapeSpawner -{ - osp::active::ActiveEntSet_t m_ownedEnts; - - std::vector m_spawnRequest; - osp::active::ActiveEntVec_t m_ents; - osp::draw::MaterialId m_materialId; -}; /** * @brief Physical properties for entities and generic Physics interface @@ -67,34 +50,24 @@ osp::Session setup_physics( osp::Session const& commonScene); /** - * @brief Queues and logic for spawning physics shapes + * @brief Queues and logic for spawning Prefab resources */ -osp::Session setup_shape_spawn( +osp::Session setup_prefabs( osp::TopTaskBuilder& rBuilder, osp::ArrayView topData, + osp::Session const& application, osp::Session const& scene, osp::Session const& commonScene, - osp::Session const& physics, - osp::draw::MaterialId materialId); + osp::Session const& physics); -osp::Session setup_shape_spawn_draw( +osp::Session setup_prefab_draw( osp::TopTaskBuilder& rBuilder, osp::ArrayView topData, + osp::Session const& application, osp::Session const& windowApp, osp::Session const& sceneRenderer, osp::Session const& commonScene, - osp::Session const& physics, - osp::Session const& shapeSpawn); - -/** - * @brief Queues and logic for spawning Prefab resources - */ -osp::Session setup_prefabs( - osp::TopTaskBuilder& rBuilder, - osp::ArrayView topData, - osp::Session const& commonScene, - osp::Session const& physics, - osp::Session const& material, - osp::TopDataId idResources); + osp::Session const& prefabs, + osp::draw::MaterialId material); } // namespace testapp::scenes diff --git a/src/testapp/sessions/shapes.cpp b/src/testapp/sessions/shapes.cpp new file mode 100644 index 00000000..e12a0d67 --- /dev/null +++ b/src/testapp/sessions/shapes.cpp @@ -0,0 +1,595 @@ +/** + * Open Space Program + * Copyright © 2019-2023 Open Space Program Project + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "shapes.h" +#include "common.h" + +#include + +#include +#include +#include +#include + +#include + +using namespace adera; +using namespace osp; +using namespace osp::active; +using namespace osp::draw; + +using osp::input::EButtonControlIndex; + +namespace testapp::scenes +{ + +void add_floor( + ArrayView const topData, + Session const& physShapes, + MaterialId const materialId, + PkgId const pkg, + int const size) +{ + OSP_DECLARE_GET_DATA_IDS(physShapes, TESTAPP_DATA_PHYS_SHAPES); + + auto &rPhysShapes = top_get(topData, idPhysShapes); + + std::mt19937 randGen(69); + auto distSizeX = std::uniform_real_distribution{20.0, 80.0}; + auto distSizeY = std::uniform_real_distribution{20.0, 80.0}; + auto distHeight = std::uniform_real_distribution{1.0, 10.0}; + + constexpr float spread = 128.0f; + + for (int x = -size; x < size+1; ++x) + { + for (int y = -size; y < size+1; ++y) + { + float const heightZ = distHeight(randGen); + rPhysShapes.m_spawnRequest.emplace_back(SpawnShape{ + .m_position = Vector3{x*spread, y*spread, heightZ}, + .m_velocity = {0.0f, 0.0f, 0.0f}, + .m_size = Vector3{distSizeX(randGen), distSizeY(randGen), heightZ}, + .m_mass = 0.0f, + .m_shape = EShape::Box + }); + } + } +} + + + +Session setup_phys_shapes( + TopTaskBuilder& rBuilder, + ArrayView const topData, + Session const& scene, + Session const& commonScene, + Session const& physics, + MaterialId const materialId) +{ + OSP_DECLARE_GET_DATA_IDS(commonScene, TESTAPP_DATA_COMMON_SCENE); + OSP_DECLARE_GET_DATA_IDS(physics, TESTAPP_DATA_PHYSICS); + auto const tgScn = scene .get_pipelines(); + auto const tgCS = commonScene .get_pipelines(); + auto const tgPhy = physics .get_pipelines(); + + Session out; + OSP_DECLARE_CREATE_DATA_IDS(out, topData, TESTAPP_DATA_PHYS_SHAPES); + auto const tgShSp = out.create_pipelines(rBuilder); + + rBuilder.pipeline(tgShSp.spawnRequest) .parent(tgScn.update); + rBuilder.pipeline(tgShSp.spawnedEnts) .parent(tgScn.update); + rBuilder.pipeline(tgShSp.ownedEnts) .parent(tgScn.update); + + top_emplace< ACtxPhysShapes > (topData, idPhysShapes, ACtxPhysShapes{ .m_materialId = materialId }); + + rBuilder.task() + .name ("Schedule Shape spawn") + .schedules ({tgShSp.spawnRequest(Schedule_)}) + .sync_with ({tgScn.update(Run)}) + .push_to (out.m_tasks) + .args ({ idPhysShapes }) + .func([] (ACtxPhysShapes& rPhysShapes) noexcept -> TaskActions + { + return rPhysShapes.m_spawnRequest.empty() ? TaskAction::Cancel : TaskActions{}; + }); + + rBuilder.task() + .name ("Create ActiveEnts for requested shapes to spawn") + .run_on ({tgShSp.spawnRequest(UseOrRun)}) + .sync_with ({tgCS.activeEnt(New), tgCS.activeEntResized(Schedule), tgShSp.spawnedEnts(Resize)}) + .push_to (out.m_tasks) + .args ({ idBasic, idPhysShapes}) + .func([] (ACtxBasic& rBasic, ACtxPhysShapes& rPhysShapes) noexcept + { + LGRN_ASSERTM(!rPhysShapes.m_spawnRequest.empty(), "spawnRequest Use_ shouldn't run if rPhysShapes.m_spawnRequest is empty!"); + + rPhysShapes.m_ents.resize(rPhysShapes.m_spawnRequest.size() * 2); + rBasic.m_activeIds.create(rPhysShapes.m_ents.begin(), rPhysShapes.m_ents.end()); + }); + + rBuilder.task() + .name ("Add hierarchy and transform to spawned shapes") + .run_on ({tgShSp.spawnRequest(UseOrRun)}) + .sync_with ({tgShSp.spawnedEnts(UseOrRun), tgShSp.ownedEnts(Modify__), tgCS.hierarchy(New), tgCS.transform(New)}) + .push_to (out.m_tasks) + .args ({ idBasic, idPhysShapes }) + .func([] (ACtxBasic& rBasic, ACtxPhysShapes& rPhysShapes) noexcept + { + osp::bitvector_resize(rPhysShapes.ownedEnts, rBasic.m_activeIds.capacity()); + rBasic.m_scnGraph.resize(rBasic.m_activeIds.capacity()); + + SubtreeBuilder bldScnRoot = SysSceneGraph::add_descendants(rBasic.m_scnGraph, rPhysShapes.m_spawnRequest.size() * 2); + + for (std::size_t i = 0; i < rPhysShapes.m_spawnRequest.size(); ++i) + { + SpawnShape const &spawn = rPhysShapes.m_spawnRequest[i]; + ActiveEnt const root = rPhysShapes.m_ents[i * 2]; + ActiveEnt const child = rPhysShapes.m_ents[i * 2 + 1]; + + rPhysShapes.ownedEnts.set(std::size_t(root)); + + rBasic.m_transform.emplace(root, ACompTransform{osp::Matrix4::translation(spawn.m_position)}); + rBasic.m_transform.emplace(child, ACompTransform{Matrix4::scaling(spawn.m_size)}); + SubtreeBuilder bldRoot = bldScnRoot.add_child(root, 1); + bldRoot.add_child(child); + } + }); + + rBuilder.task() + .name ("Add physics to spawned shapes") + .run_on ({tgShSp.spawnRequest(UseOrRun)}) + .sync_with ({tgShSp.spawnedEnts(UseOrRun), tgPhy.physBody(Modify), tgPhy.physUpdate(Done)}) + .push_to (out.m_tasks) + .args ({ idBasic, idPhysShapes, idPhys }) + .func([] (ACtxBasic const& rBasic, ACtxPhysShapes& rPhysShapes, ACtxPhysics& rPhys) noexcept + { + rPhys.m_hasColliders.ints().resize(rBasic.m_activeIds.vec().capacity()); + rPhys.m_shape.resize(rBasic.m_activeIds.capacity()); + + for (std::size_t i = 0; i < rPhysShapes.m_spawnRequest.size(); ++i) + { + SpawnShape const &spawn = rPhysShapes.m_spawnRequest[i]; + ActiveEnt const root = rPhysShapes.m_ents[i * 2]; + ActiveEnt const child = rPhysShapes.m_ents[i * 2 + 1]; + + rPhys.m_hasColliders.set(std::size_t(root)); + if (spawn.m_mass != 0.0f) + { + rPhys.m_setVelocity.emplace_back(root, spawn.m_velocity); + Vector3 const inertia = collider_inertia_tensor(spawn.m_shape, spawn.m_size, spawn.m_mass); + Vector3 const offset{0.0f, 0.0f, 0.0f}; + rPhys.m_mass.emplace( child, ACompMass{ inertia, offset, spawn.m_mass } ); + } + + rPhys.m_shape[child] = spawn.m_shape; + rPhys.m_colliderDirty.push_back(child); + } + }); + + //TODO + rBuilder.task() + .name ("Delete basic components") + .run_on ({tgCS.activeEntDelete(UseOrRun)}) + .sync_with ({tgShSp.ownedEnts(Modify__)}) + .push_to (out.m_tasks) + .args ({ idBasic, idActiveEntDel }) + .func([] (ACtxBasic& rBasic, ActiveEntVec_t const& rActiveEntDel) noexcept + { + update_delete_basic(rBasic, rActiveEntDel.cbegin(), rActiveEntDel.cend()); + }); + + rBuilder.task() + .name ("Clear Shape Spawning vector after use") + .run_on ({tgShSp.spawnRequest(Clear)}) + .push_to (out.m_tasks) + .args ({ idPhysShapes }) + .func([] (ACtxPhysShapes& rPhysShapes) noexcept + { + rPhysShapes.m_spawnRequest.clear(); + }); + + + return out; +} // setup_phys_shapes + + + + +Session setup_phys_shapes_draw( + TopTaskBuilder& rBuilder, + ArrayView const topData, + Session const& windowApp, + Session const& sceneRenderer, + Session const& commonScene, + Session const& physics, + Session const& physShapes) +{ + OSP_DECLARE_GET_DATA_IDS(sceneRenderer, TESTAPP_DATA_SCENE_RENDERER); + OSP_DECLARE_GET_DATA_IDS(commonScene, TESTAPP_DATA_COMMON_SCENE); + OSP_DECLARE_GET_DATA_IDS(physics, TESTAPP_DATA_PHYSICS); + OSP_DECLARE_GET_DATA_IDS(physShapes, TESTAPP_DATA_PHYS_SHAPES); + auto const tgWin = windowApp .get_pipelines< PlWindowApp >(); + auto const tgScnRdr = sceneRenderer .get_pipelines< PlSceneRenderer >(); + auto const tgCS = commonScene .get_pipelines< PlCommonScene >(); + auto const tgShSp = physShapes .get_pipelines< PlPhysShapes >(); + + Session out; + + rBuilder.task() + .name ("Create DrawEnts for spawned shapes") + .run_on ({tgShSp.spawnRequest(UseOrRun)}) + .sync_with ({tgShSp.spawnedEnts(UseOrRun), tgCS.activeEntResized(Done), tgScnRdr.drawEntResized(ModifyOrSignal), tgScnRdr.drawEnt(New)}) + .push_to (out.m_tasks) + .args ({ idBasic, idDrawing, idScnRender, idPhysShapes, idNMesh }) + .func([] (ACtxBasic const& rBasic, ACtxDrawing& rDrawing, ACtxSceneRender& rScnRender, ACtxPhysShapes& rPhysShapes, NamedMeshes& rNMesh) noexcept + { + for (std::size_t i = 0; i < rPhysShapes.m_spawnRequest.size(); ++i) + { + ActiveEnt const child = rPhysShapes.m_ents[i * 2 + 1]; + rScnRender.m_activeToDraw[child] = rScnRender.m_drawIds.create(); + } + }); + + rBuilder.task() + .name ("Add mesh and material to spawned shapes") + .run_on ({tgShSp.spawnRequest(UseOrRun)}) + .sync_with ({tgShSp.spawnedEnts(UseOrRun), + tgScnRdr.entMesh(New), tgScnRdr.material(New), tgScnRdr.drawEnt(New), tgScnRdr.drawEntResized(Done), + tgScnRdr.materialDirty(Modify_), tgScnRdr.entMeshDirty(Modify_)}) + .push_to (out.m_tasks) + .args ({ idBasic, idDrawing, idScnRender, idPhysShapes, idNMesh }) + .func([] (ACtxBasic const& rBasic, ACtxDrawing& rDrawing, ACtxSceneRender& rScnRender, ACtxPhysShapes& rPhysShapes, NamedMeshes& rNMesh) noexcept + { + Material &rMat = rScnRender.m_materials[rPhysShapes.m_materialId]; + + for (std::size_t i = 0; i < rPhysShapes.m_spawnRequest.size(); ++i) + { + SpawnShape const &spawn = rPhysShapes.m_spawnRequest[i]; + ActiveEnt const root = rPhysShapes.m_ents[i * 2]; + ActiveEnt const child = rPhysShapes.m_ents[i * 2 + 1]; + DrawEnt const drawEnt = rScnRender.m_activeToDraw[child]; + + rScnRender.m_needDrawTf.set(std::size_t(root)); + rScnRender.m_needDrawTf.set(std::size_t(child)); + + rScnRender.m_mesh[drawEnt] = rDrawing.m_meshRefCounts.ref_add(rNMesh.m_shapeToMesh.at(spawn.m_shape)); + rScnRender.m_meshDirty.push_back(drawEnt); + + rMat.m_ents.set(std::size_t(drawEnt)); + rMat.m_dirty.push_back(drawEnt); + + rScnRender.m_visible.set(std::size_t(drawEnt)); + rScnRender.m_opaque.set(std::size_t(drawEnt)); + } + }); + + // When does resync run relative to deletes? + + rBuilder.task() + .name ("Resync spawned shapes DrawEnts") + .run_on ({tgWin.resync(Run)}) + .sync_with ({tgShSp.ownedEnts(UseOrRun_), tgCS.hierarchy(Ready), tgCS.activeEntResized(Done), tgScnRdr.drawEntResized(ModifyOrSignal)}) + .push_to (out.m_tasks) + .args ({ idBasic, idDrawing, idScnRender, idPhysShapes, idNMesh }) + .func([] (ACtxBasic const& rBasic, ACtxDrawing& rDrawing, ACtxSceneRender& rScnRender, ACtxPhysShapes& rPhysShapes, NamedMeshes& rNMesh) noexcept + { + for (std::size_t entInt : rPhysShapes.ownedEnts.ones()) + { + ActiveEnt const root = ActiveEnt(entInt); + ActiveEnt const child = *SysSceneGraph::children(rBasic.m_scnGraph, root).begin(); + + rScnRender.m_activeToDraw[child] = rScnRender.m_drawIds.create(); + } + }); + + rBuilder.task() + .name ("Resync spawned shapes mesh and material") + .run_on ({tgWin.resync(Run)}) + .sync_with ({tgShSp.ownedEnts(UseOrRun_), tgScnRdr.entMesh(New), tgScnRdr.material(New), tgScnRdr.drawEnt(New), tgScnRdr.drawEntResized(Done), + tgScnRdr.materialDirty(Modify_), tgScnRdr.entMeshDirty(Modify_)}) + .push_to (out.m_tasks) + .args ({ idBasic, idDrawing, idPhys, idPhysShapes, idScnRender, idNMesh }) + .func([] (ACtxBasic const& rBasic, ACtxDrawing& rDrawing, ACtxPhysics& rPhys, ACtxPhysShapes& rPhysShapes, ACtxSceneRender& rScnRender, NamedMeshes& rNMesh) noexcept + { + Material &rMat = rScnRender.m_materials[rPhysShapes.m_materialId]; + + for (std::size_t entInt : rPhysShapes.ownedEnts.ones()) + { + ActiveEnt const root = ActiveEnt(entInt); + ActiveEnt const child = *SysSceneGraph::children(rBasic.m_scnGraph, root).begin(); + + //SpawnShape const &spawn = rPhysShapes.m_spawnRequest[i]; + DrawEnt const drawEnt = rScnRender.m_activeToDraw[child]; + + rScnRender.m_needDrawTf.set(std::size_t(root)); + rScnRender.m_needDrawTf.set(std::size_t(child)); + + EShape const shape = rPhys.m_shape.at(child); + rScnRender.m_mesh[drawEnt] = rDrawing.m_meshRefCounts.ref_add(rNMesh.m_shapeToMesh.at(shape)); + rScnRender.m_meshDirty.push_back(drawEnt); + + rMat.m_ents.set(std::size_t(drawEnt)); + rMat.m_dirty.push_back(drawEnt); + + rScnRender.m_visible.set(std::size_t(drawEnt)); + rScnRender.m_opaque.set(std::size_t(drawEnt)); + } + }); + + rBuilder.task() + .name ("Remove deleted ActiveEnts from ACtxPhysShapeser") + .run_on ({tgCS.activeEntDelete(UseOrRun)}) + .sync_with ({tgShSp.ownedEnts(Modify__)}) + .push_to (out.m_tasks) + .args ({ idPhysShapes, idActiveEntDel }) + .func([] (ACtxPhysShapes& rPhysShapes, ActiveEntVec_t const& rActiveEntDel) noexcept + { + for (ActiveEnt const deleted : rActiveEntDel) + { + rPhysShapes.ownedEnts.reset(std::size_t(deleted)); + } + }); + + return out; +} // setup_phys_shapes_draw + + + + +Session setup_thrower( + TopTaskBuilder& rBuilder, + ArrayView const topData, + Session const& windowApp, + Session const& cameraCtrl, + Session const& physShapes) +{ + OSP_DECLARE_GET_DATA_IDS(physShapes, TESTAPP_DATA_PHYS_SHAPES); + OSP_DECLARE_GET_DATA_IDS(cameraCtrl, TESTAPP_DATA_CAMERA_CTRL); + auto &rCamCtrl = top_get< ACtxCameraController > (topData, idCamCtrl); + + auto const tgWin = windowApp .get_pipelines(); + auto const tgCmCt = cameraCtrl.get_pipelines(); + auto const tgShSp = physShapes.get_pipelines(); + + Session out; + auto const [idBtnThrow] = out.acquire_data<1>(topData); + + top_emplace< EButtonControlIndex > (topData, idBtnThrow, rCamCtrl.m_controls.button_subscribe("debug_throw")); + + rBuilder.task() + .name ("Throw spheres when pressing space") + .run_on ({tgWin.inputs(Run)}) + .sync_with ({tgCmCt.camCtrl(Ready), tgShSp.spawnRequest(Modify_)}) + .push_to (out.m_tasks) + .args ({ idCamCtrl, idPhysShapes, idBtnThrow }) + .func([] (ACtxCameraController& rCamCtrl, ACtxPhysShapes& rPhysShapes, EButtonControlIndex btnThrow) noexcept + { + // Throw a sphere when the throw button is pressed + if (rCamCtrl.m_controls.button_held(btnThrow)) + { + Matrix4 const &camTf = rCamCtrl.m_transform; + float const speed = 120; + float const dist = 8.0f; + + for (int x = -2; x < 3; ++x) + { + for (int y = -2; y < 3; ++y) + { + rPhysShapes.m_spawnRequest.push_back({ + .m_position = camTf.translation() - camTf.backward()*dist + camTf.up()*y*5.5f + camTf.right()*x*5.5f, + .m_velocity = -camTf.backward()*speed, + .m_size = Vector3{1.0f}, + .m_mass = 1.0f, + .m_shape = EShape::Sphere + }); + } + } + } + }); + + return out; +} // setup_thrower + + + + +Session setup_droppers( + TopTaskBuilder& rBuilder, + ArrayView const topData, + Session const& scene, + Session const& commonScene, + Session const& physShapes) +{ + OSP_DECLARE_GET_DATA_IDS(scene, TESTAPP_DATA_SCENE); + OSP_DECLARE_GET_DATA_IDS(commonScene, TESTAPP_DATA_COMMON_SCENE); + OSP_DECLARE_GET_DATA_IDS(physShapes, TESTAPP_DATA_PHYS_SHAPES); + + auto const tgScn = scene .get_pipelines(); + auto const tgShSp = physShapes .get_pipelines(); + + Session out; + auto const [idSpawnTimerA, idSpawnTimerB] = out.acquire_data<2>(topData); + + top_emplace< float > (topData, idSpawnTimerA, 0.0f); + top_emplace< float > (topData, idSpawnTimerB, 0.0f); + + rBuilder.task() + .name ("Spawn blocks every 2 seconds") + .run_on ({tgScn.update(Run)}) + .sync_with ({tgShSp.spawnRequest(Modify_)}) + .push_to (out.m_tasks) + .args({ idPhysShapes, idSpawnTimerA, idDeltaTimeIn }) + .func([] (ACtxPhysShapes& rPhysShapes, float& rSpawnTimer, float const deltaTimeIn) noexcept + + { + rSpawnTimer += deltaTimeIn; + if (rSpawnTimer >= 2.0f) + { + rSpawnTimer -= 2.0f; + + rPhysShapes.m_spawnRequest.push_back({ + .m_position = {10.0f, 0.0f, 30.0f}, + .m_velocity = {0.0f, 0.0f, 0.0f}, + .m_size = {2.0f, 2.0f, 1.0f}, + .m_mass = 1.0f, + .m_shape = EShape::Box + }); + } + }); + + rBuilder.task() + .name ("Spawn cylinders every 1 second") + .run_on ({tgScn.update(Run)}) + .sync_with ({tgShSp.spawnRequest(Modify_)}) + .push_to (out.m_tasks) + .args({ idPhysShapes, idSpawnTimerB, idDeltaTimeIn }) + .func([] (ACtxPhysShapes& rPhysShapes, float& rSpawnTimer, float const deltaTimeIn) noexcept + { + rSpawnTimer += deltaTimeIn; + if (rSpawnTimer >= 1.0f) + { + rSpawnTimer -= 1.0f; + + rPhysShapes.m_spawnRequest.push_back({ + .m_position = {-10.0f, 0.0, 30.0f}, + .m_velocity = {0.0f, 0.0f, 0.0f}, + .m_size = {2.0f, 2.0f, 1.0f}, + .m_mass = 1.0f, + .m_shape = EShape::Cylinder + }); + } + }); + + return out; +} // setup_droppers + + + + +Session setup_bounds( + TopTaskBuilder& rBuilder, + ArrayView const topData, + Session const& scene, + Session const& commonScene, + Session const& physShapes) +{ + OSP_DECLARE_GET_DATA_IDS(commonScene, TESTAPP_DATA_COMMON_SCENE); + OSP_DECLARE_GET_DATA_IDS(physShapes, TESTAPP_DATA_PHYS_SHAPES); + auto const tgScn = scene .get_pipelines(); + auto const tgCS = commonScene .get_pipelines(); + auto const tgShSp = physShapes .get_pipelines(); + + Session out; + OSP_DECLARE_CREATE_DATA_IDS(out, topData, TESTAPP_DATA_BOUNDS); + auto const tgBnds = out.create_pipelines(rBuilder); + + rBuilder.pipeline(tgBnds.boundsSet) .parent(tgScn.update); + rBuilder.pipeline(tgBnds.outOfBounds) .parent(tgScn.update); + + top_emplace< ActiveEntSet_t > (topData, idBounds); + top_emplace< ActiveEntVec_t > (topData, idOutOfBounds); + + rBuilder.task() + .name ("Check for out-of-bounds entities") + .run_on ({tgScn.update(Run)}) + .sync_with ({tgCS.transform(Ready), tgBnds.boundsSet(Ready), tgBnds.outOfBounds(Modify__)}) + .push_to (out.m_tasks) + .args ({ idBasic, idBounds, idOutOfBounds }) + .func([] (ACtxBasic const& rBasic, ActiveEntSet_t const& rBounds, ActiveEntVec_t& rOutOfBounds) noexcept + { + for (std::size_t const ent : rBounds.ones()) + { + ACompTransform const &entTf = rBasic.m_transform.get(ActiveEnt(ent)); + if (entTf.m_transform.translation().z() < -10) + { + rOutOfBounds.push_back(ActiveEnt(ent)); + } + } + }); + + rBuilder.task() + .name ("Queue-Delete out-of-bounds entities") + .run_on ({tgBnds.outOfBounds(UseOrRun_)}) + .sync_with ({tgCS.activeEntDelete(Modify_), tgCS.hierarchy(Delete)}) + .push_to (out.m_tasks) + .args ({ idBasic, idActiveEntDel, idOutOfBounds }) + .func([] (ACtxBasic& rBasic, ActiveEntVec_t& rActiveEntDel, ActiveEntVec_t& rOutOfBounds) noexcept + { + SysSceneGraph::queue_delete_entities(rBasic.m_scnGraph, rActiveEntDel, rOutOfBounds.begin(), rOutOfBounds.end()); + }); + + rBuilder.task() + .name ("Clear out-of-bounds vector once we're done with it") + .run_on ({tgBnds.outOfBounds(Clear_)}) + .push_to (out.m_tasks) + .args ({ idOutOfBounds }) + .func([] (ActiveEntVec_t& rOutOfBounds) noexcept + { + rOutOfBounds.clear(); + }); + + rBuilder.task() + .name ("Add bounds to spawned shapes") + .run_on ({tgShSp.spawnRequest(UseOrRun)}) + .sync_with ({tgShSp.spawnedEnts(UseOrRun), tgBnds.boundsSet(Modify)}) + .push_to (out.m_tasks) + .args ({ idBasic, idPhysShapes, idBounds }) + .func([] (ACtxBasic& rBasic, ACtxPhysShapes& rPhysShapes, ActiveEntSet_t& rBounds) noexcept + { + rBounds.ints().resize(rBasic.m_activeIds.vec().capacity()); + + for (std::size_t i = 0; i < rPhysShapes.m_spawnRequest.size(); ++i) + { + SpawnShape const &spawn = rPhysShapes.m_spawnRequest[i]; + if (spawn.m_mass == 0) + { + continue; + } + + ActiveEnt const root = rPhysShapes.m_ents[i * 2]; + + rBounds.set(std::size_t(root)); + } + }); + + rBuilder.task() + .name ("Delete bounds components") + .run_on ({tgCS.activeEntDelete(UseOrRun)}) + .sync_with ({tgBnds.boundsSet(Delete)}) + .push_to (out.m_tasks) + .args ({ idActiveEntDel, idBounds }) + .func([] (ActiveEntVec_t const& rActiveEntDel, ActiveEntSet_t& rBounds) noexcept + { + for (osp::active::ActiveEnt const ent : rActiveEntDel) + { + rBounds.reset(std::size_t(ent)); + } + }); + + return out; +} // setup_bounds + + +} // namespace testapp::scenes diff --git a/src/testapp/sessions/shapes.h b/src/testapp/sessions/shapes.h new file mode 100644 index 00000000..c763d872 --- /dev/null +++ b/src/testapp/sessions/shapes.h @@ -0,0 +1,114 @@ +/** + * Open Space Program + * Copyright © 2019-2023 Open Space Program Project + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#pragma once + +#include "../scenarios.h" + +#include +#include +#include + +namespace testapp::scenes +{ + +struct SpawnShape +{ + osp::Vector3 m_position; + osp::Vector3 m_velocity; + osp::Vector3 m_size; + float m_mass; + osp::EShape m_shape; +}; + +struct ACtxPhysShapes +{ + osp::active::ActiveEntSet_t ownedEnts; + + std::vector m_spawnRequest; + osp::active::ActiveEntVec_t m_ents; + osp::draw::MaterialId m_materialId; +}; + +void add_floor( + osp::ArrayView topData, + osp::Session const& physShapes, + osp::draw::MaterialId material, + osp::PkgId pkg, + int size); + + +/** + * @brief Queues and logic for spawning physics shapes + */ +osp::Session setup_phys_shapes( + osp::TopTaskBuilder& rBuilder, + osp::ArrayView topData, + osp::Session const& scene, + osp::Session const& commonScene, + osp::Session const& physics, + osp::draw::MaterialId materialId); + +osp::Session setup_phys_shapes_draw( + osp::TopTaskBuilder& rBuilder, + osp::ArrayView topData, + osp::Session const& windowApp, + osp::Session const& sceneRenderer, + osp::Session const& commonScene, + osp::Session const& physics, + osp::Session const& physShapes); + +/** + * @brief Throws spheres when pressing space + */ +osp::Session setup_thrower( + osp::TopTaskBuilder& rBuilder, + osp::ArrayView topData, + osp::Session const& windowApp, + osp::Session const& cameraCtrl, + osp::Session const& physShapes); + +/** + * @brief Spawn blocks every 2 seconds and spheres every 1 second + */ +osp::Session setup_droppers( + osp::TopTaskBuilder& rBuilder, + osp::ArrayView topData, + osp::Session const& scene, + osp::Session const& commonScene, + osp::Session const& physShapes); + +/** + * @brief Entity set to delete entities under Z = -10, added to spawned shapes + */ +osp::Session setup_bounds( + osp::TopTaskBuilder& rBuilder, + osp::ArrayView topData, + osp::Session const& scene, + osp::Session const& commonScene, + osp::Session const& physShapes); + + +} + diff --git a/src/testapp/sessions/vehicles.cpp b/src/testapp/sessions/vehicles.cpp index e04cc2b6..13419f7c 100644 --- a/src/testapp/sessions/vehicles.cpp +++ b/src/testapp/sessions/vehicles.cpp @@ -24,16 +24,23 @@ */ #include "vehicles.h" -#include +#include +#include #include #include #include +#include +#include #include +#include + +using namespace adera; -using namespace osp; using namespace osp::active; +using namespace osp::draw; using namespace osp::link; +using namespace osp; using osp::restypes::gc_importer; @@ -42,617 +49,262 @@ using namespace Magnum::Math::Literals; namespace testapp::scenes { -#if 0 - Session setup_parts( TopTaskBuilder& rBuilder, ArrayView const topData, - Session const& commonScene, - TopDataId const idResources) + Session const& application, + Session const& scene) { - OSP_SESSION_UNPACK_TAGS(commonScene, TESTAPP_COMMON_SCENE); - - Session parts; - OSP_SESSION_ACQUIRE_DATA(parts, topData, TESTAPP_PARTS); - OSP_SESSION_ACQUIRE_TAGS(parts, rTags, TESTAPP_PARTS); - parts.m_tgCleanupEvt = tgCleanupEvt; - - rBuilder.tag(tgPartReq) .depend_on({tgPartMod}); - rBuilder.tag(tgPartClr) .depend_on({tgPartMod, tgPartReq}); - rBuilder.tag(tgMapPartEntMod) .depend_on({tgMapPartEntReq}); - rBuilder.tag(tgWeldReq) .depend_on({tgWeldMod}); - rBuilder.tag(tgWeldClr) .depend_on({tgWeldMod, tgWeldReq}); - rBuilder.tag(tgLinkReq) .depend_on({tgLinkMod}); - rBuilder.tag(tgLinkMhUpdReq) .depend_on({tgLinkMhUpdMod}); - rBuilder.tag(tgNodeAnyUpdReq) .depend_on({tgNodeAnyUpdMod}); - rBuilder.tag(tgMachUpdEnqReq) .depend_on({tgMachUpdEnqMod}); + OSP_DECLARE_GET_DATA_IDS(application, TESTAPP_DATA_APPLICATION); + + auto const tgScn = scene.get_pipelines(); + + Session out; + OSP_DECLARE_CREATE_DATA_IDS(out, topData, TESTAPP_DATA_PARTS); + auto const tgParts = out.create_pipelines(rBuilder); + + out.m_cleanup = tgScn.cleanup; + + rBuilder.pipeline(tgParts.partIds) .parent(tgScn.update); + rBuilder.pipeline(tgParts.partPrefabs) .parent(tgScn.update); + rBuilder.pipeline(tgParts.partTransformWeld).parent(tgScn.update); + rBuilder.pipeline(tgParts.partDirty) .parent(tgScn.update); + rBuilder.pipeline(tgParts.weldIds) .parent(tgScn.update); + rBuilder.pipeline(tgParts.weldDirty) .parent(tgScn.update); + rBuilder.pipeline(tgParts.machIds) .parent(tgScn.update); + rBuilder.pipeline(tgParts.nodeIds) .parent(tgScn.update); + rBuilder.pipeline(tgParts.connect) .parent(tgScn.update); + rBuilder.pipeline(tgParts.mapWeldPart) .parent(tgScn.update); + rBuilder.pipeline(tgParts.mapPartMach) .parent(tgScn.update); + rBuilder.pipeline(tgParts.mapPartActive) .parent(tgScn.update); + rBuilder.pipeline(tgParts.mapWeldActive) .parent(tgScn.update); + rBuilder.pipeline(tgParts.machUpdExtIn) .parent(tgScn.update); + rBuilder.pipeline(tgParts.linkLoop) .parent(tgScn.update).loops(true); auto &rScnParts = top_emplace< ACtxParts > (topData, idScnParts); - auto &rUpdMach = top_emplace< UpdMachPerType > (topData, idUpdMach); - top_emplace< TagId > (topData, idtgNodeUpdEvt, tgNodeUpdEvt); - top_emplace< MachTypeToEvt_t > (topData, idMachEvtTags, MachTypeReg_t::size()); - top_emplace< std::vector > (topData, idMachUpdEnqueue); + auto &rUpdMach = top_emplace< MachineUpdater > (topData, idUpdMach); // Resize containers to fit all existing MachTypeIds and NodeTypeIds // These Global IDs are dynamically initialized just as the program starts - bitvector_resize(rUpdMach.m_machTypesDirty, MachTypeReg_t::size()); - rUpdMach.m_localDirty .resize(MachTypeReg_t::size()); - rScnParts.m_machines.m_perType .resize(MachTypeReg_t::size()); - rScnParts.m_nodePerType .resize(NodeTypeReg_t::size()); - - auto const idNull = lgrn::id_null(); - - parts.task() = rBuilder.task().assign({tgCleanupEvt}).data( - "Clean up Part prefab owners", - TopDataIds_t{ idScnParts, idResources }, - wrap_args([] (ACtxParts& rScnParts, Resources& rResources) noexcept + bitvector_resize(rUpdMach.machTypesDirty, MachTypeReg_t::size()); + rUpdMach.localDirty .resize(MachTypeReg_t::size()); + rScnParts.machines.perType.resize(MachTypeReg_t::size()); + rScnParts.nodePerType .resize(NodeTypeReg_t::size()); + + rBuilder.task() + .name ("Clear Resource owners") + .run_on ({tgScn.cleanup(Run_)}) + .push_to (out.m_tasks) + .args ({ idScnParts, idResources}) + .func([] (ACtxParts& rScnParts, Resources& rResources) noexcept { - for (osp::PrefabPair &rPrefabPair : rScnParts.m_partPrefabs) + for (osp::PrefabPair &rPrefabPair : rScnParts.partPrefabs) { rResources.owner_destroy(gc_importer, std::move(rPrefabPair.m_importer)); } - })); + }); - parts.task() = rBuilder.task().assign({tgSceneEvt, tgPartClr, tgWeldClr}).data( - "Clear Part and Weld dirty vectors after use", - TopDataIds_t{ idScnParts}, - wrap_args([] (ACtxParts& rScnParts) noexcept - { - rScnParts.m_partDirty.clear(); - rScnParts.m_weldDirty.clear(); - })); - - parts.task() = rBuilder.task().assign({tgSceneEvt, tgNodeUpdEvt, tgMachUpdEnqReq}).data( - "Enqueue Machine & Node update tasks", - TopDataIds_t{ idNull, idMachUpdEnqueue, idtgNodeUpdEvt }, - wrap_args([] (WorkerContext ctx, std::vector& rMachUpdEnqueue, TagId const tgNodeUpdEvt ) noexcept + rBuilder.task() + .name ("Clear Part dirty vectors after use") + .run_on ({tgParts.partDirty(Clear)}) + .push_to (out.m_tasks) + .args ({ idScnParts}) + .func([] (ACtxParts& rScnParts) noexcept { - if (rMachUpdEnqueue.empty()) - { - return; // Nothing to enqueue - } - - ctx.m_rEnqueueHappened = true; - - - auto enqueueBits = lgrn::bit_view(ctx.m_enqueue); - - // Enqueue machine tags, eg: tgMhRcsDriverEvt, tgMhRocketEvt, ... - for (TagId const tag : rMachUpdEnqueue) - { - enqueueBits.set(std::size_t(tag)); - } - rMachUpdEnqueue.clear(); - - // Enqueue self and all other machine update - enqueueBits.set(std::size_t(tgNodeUpdEvt)); - })); - - return parts; -} - -Session setup_signals_float( - TopTaskBuilder& rBuilder, - ArrayView const topData, - Session const& commonScene, - Session const& parts) -{ - OSP_SESSION_UNPACK_TAGS(commonScene, TESTAPP_COMMON_SCENE); - OSP_SESSION_UNPACK_DATA(parts, TESTAPP_PARTS); - OSP_SESSION_UNPACK_TAGS(parts, TESTAPP_PARTS); - - Session signalsFloat; - OSP_SESSION_ACQUIRE_DATA(signalsFloat, topData, TESTAPP_SIGNALS_FLOAT); - OSP_SESSION_ACQUIRE_TAGS(signalsFloat, rTags, TESTAPP_SIGNALS_FLOAT); - - rBuilder.tag(tgSigFloatLinkReq) .depend_on({tgSigFloatLinkMod}); - rBuilder.tag(tgSigFloatUpdReq) .depend_on({tgSigFloatLinkMod, tgSigFloatUpdMod}); - - top_emplace< SignalValues_t > (topData, idSigValFloat); - top_emplace< UpdateNodes > (topData, idSigUpdFloat); - - // NOTE: Eventually have an array of UpdateNodes to allow multiple threads to update nodes in - // parallel, noting the use of "Reduce". Tag limits are intended select which UpdateNodes - // are passed to each thread, once they're properly implemented. - - auto const idNull = lgrn::id_null(); - + rScnParts.partDirty.clear(); + }); - signalsFloat.task() = rBuilder.task().assign({tgSceneEvt, tgNodeUpdEvt, tgSigFloatUpdEvt, tgSigFloatUpdReq, tgMachUpdEnqMod}).data( - "Reduce Signal-Float Nodes", - TopDataIds_t{ idNull, idSigUpdFloat, idSigValFloat, idUpdMach, idMachUpdEnqueue, idScnParts, idMachEvtTags }, - wrap_args([] (WorkerContext const ctx, UpdateNodes& rSigUpdFloat, SignalValues_t& rSigValFloat, UpdMachPerType& rUpdMach, std::vector& rMachUpdEnqueue, ACtxParts const& rScnParts, MachTypeToEvt_t const& rMachEvtTags ) noexcept + rBuilder.task() + .name ("Clear Weld dirty vectors after use") + .run_on ({tgParts.weldDirty(Clear)}) + .push_to (out.m_tasks) + .args ({ idScnParts}) + .func([] (ACtxParts& rScnParts) noexcept { - if ( ! rSigUpdFloat.m_dirty ) - { - return; // Not dirty, nothing to do - } - - Nodes const &rFloatNodes = rScnParts.m_nodePerType[gc_ntSigFloat]; - - // NOTE: The various use of reset() clear entire bit arrays, which may or may - // not be expensive. They likely use memset + rScnParts.weldDirty.clear(); + }); - for (std::size_t const machTypeDirty : rUpdMach.m_machTypesDirty.ones()) + rBuilder.task() + .name ("Schedule Link update") + .schedules ({tgParts.linkLoop(ScheduleLink)}) + .sync_with ({tgScn.update(Run)}) + .push_to (out.m_tasks) + .args ({ idUpdMach}) + .func([] (MachineUpdater& rUpdMach) noexcept -> TaskActions + { + if (rUpdMach.requestMachineUpdateLoop) { - rUpdMach.m_localDirty[machTypeDirty].reset(); + rUpdMach.requestMachineUpdateLoop = false; + return TaskActions{}; } - rUpdMach.m_machTypesDirty.reset(); - - // Sees which nodes changed, and writes into rUpdMach set dirty which MACHINES - // must be updated next - update_signal_nodes( - rSigUpdFloat.m_nodeDirty.ones(), - rFloatNodes.m_nodeToMach, - rScnParts.m_machines, - arrayView(rSigUpdFloat.m_nodeNewValues), - rSigValFloat, - rUpdMach); - rSigUpdFloat.m_nodeDirty.reset(); - rSigUpdFloat.m_dirty = false; - - // Tasks cannot be enqueued here directly, since that will interfere with other node reduce - // tasks. All machine tasks must be enqueued at the same time. rMachUpdEnqueue here is - // passed to a task in setup_parts - - // Run tasks needed to update machine types that are dirty - for (MachTypeId const type : rUpdMach.m_machTypesDirty.ones()) + else { - rMachUpdEnqueue.push_back(rMachEvtTags[type]); + return TaskAction::Cancel; } - })); - - signalsFloat.task() = rBuilder.task().assign({tgSceneEvt, tgLinkReq, tgSigFloatLinkMod}).data( - "Allocate Signal-Float Node Values", - TopDataIds_t{ idSigUpdFloat, idSigValFloat, idScnParts }, - wrap_args([] (UpdateNodes& rSigUpdFloat, SignalValues_t& rSigValFloat, ACtxParts const& rScnParts) noexcept - { - Nodes const &rFloatNodes = rScnParts.m_nodePerType[gc_ntSigFloat]; - rSigUpdFloat.m_nodeNewValues.resize(rFloatNodes.m_nodeIds.capacity()); - rSigUpdFloat.m_nodeDirty.ints().resize(rFloatNodes.m_nodeIds.vec().capacity()); - rSigValFloat.resize(rFloatNodes.m_nodeIds.capacity()); - })); - - return signalsFloat; -} - -template -TopTaskFunc_t gen_allocate_mach_bitsets() -{ - static TopTaskFunc_t const func = wrap_args([] (ACtxParts& rScnParts, UpdMachPerType& rUpdMach) noexcept - { - rUpdMach.m_localDirty[MachType_T].ints().resize(rScnParts.m_machines.m_perType[MachType_T].m_localIds.vec().capacity()); }); - return func; -} - -Session setup_mach_rocket( - TopTaskBuilder& rBuilder, - ArrayView const topData, - Session const& commonScene, - Session const& parts, - Session const& signalsFloat) -{ - OSP_SESSION_UNPACK_TAGS(commonScene, TESTAPP_COMMON_SCENE); - OSP_SESSION_UNPACK_DATA(signalsFloat, TESTAPP_SIGNALS_FLOAT) - OSP_SESSION_UNPACK_TAGS(signalsFloat, TESTAPP_SIGNALS_FLOAT); - OSP_SESSION_UNPACK_DATA(parts, TESTAPP_PARTS); - OSP_SESSION_UNPACK_TAGS(parts, TESTAPP_PARTS); - - using namespace adera; - - Session machRocket; - OSP_SESSION_ACQUIRE_TAGS(machRocket, rTags, TESTAPP_MACH_ROCKET); - - top_get< MachTypeToEvt_t >(topData, idMachEvtTags).at(gc_mtMagicRocket) = tgMhRocketEvt; - - machRocket.task() = rBuilder.task().assign({tgSceneEvt, tgLinkReq, tgLinkMhUpdMod}).data( - "Allocate Machine update bitset for MagicRocket", - TopDataIds_t{idScnParts, idUpdMach}, - gen_allocate_mach_bitsets()); - - return machRocket; -} - -Session setup_mach_rcsdriver( - TopTaskBuilder& rBuilder, - ArrayView const topData, - Session const& commonScene, - Session const& parts, - Session const& signalsFloat) -{ - OSP_SESSION_UNPACK_TAGS(commonScene, TESTAPP_COMMON_SCENE); - OSP_SESSION_UNPACK_DATA(signalsFloat, TESTAPP_SIGNALS_FLOAT) - OSP_SESSION_UNPACK_TAGS(signalsFloat, TESTAPP_SIGNALS_FLOAT); - OSP_SESSION_UNPACK_DATA(parts, TESTAPP_PARTS); - OSP_SESSION_UNPACK_TAGS(parts, TESTAPP_PARTS); - - using namespace adera; - - Session machRcsDriver; - OSP_SESSION_ACQUIRE_TAGS(machRcsDriver, rTags, TESTAPP_MACH_RCSDRIVER); - - top_get< MachTypeToEvt_t >(topData, idMachEvtTags).at(gc_mtRcsDriver) = tgMhRcsDriverEvt; - - machRcsDriver.task() = rBuilder.task().assign({tgMhRcsDriverEvt, tgSigFloatUpdMod}).data( - "RCS Drivers calculate new values", - TopDataIds_t{ idScnParts, idUpdMach, idSigValFloat, idSigUpdFloat }, - wrap_args([] (ACtxParts& rScnParts, UpdMachPerType const& rUpdMach, SignalValues_t& rSigValFloat, UpdateNodes& rSigUpdFloat) noexcept - { - Nodes const &rFloatNodes = rScnParts.m_nodePerType[gc_ntSigFloat]; - PerMachType &rRockets = rScnParts.m_machines.m_perType[gc_mtRcsDriver]; - - for (MachLocalId const local : rUpdMach.m_localDirty[gc_mtRcsDriver].ones()) - { - MachAnyId const mach = rRockets.m_localToAny[local]; - lgrn::Span const portSpan = rFloatNodes.m_machToNode[mach]; - - NodeId const thrNode = connected_node(portSpan, ports_rcsdriver::gc_throttleOut.m_port); - if (thrNode == lgrn::id_null()) - { - continue; // Throttle Output not connected, calculations below are useless - } - - auto const rcs_read = [&rSigValFloat, portSpan] (float& rDstVar, PortEntry const& entry) - { - NodeId const node = connected_node(portSpan, entry.m_port); - - if (node != lgrn::id_null()) - { - rDstVar = rSigValFloat[node]; - } - }; - - Vector3 pos{0.0f}; - Vector3 dir{0.0f}; - Vector3 cmdLin{0.0f}; - Vector3 cmdAng{0.0f}; - - rcs_read( pos.x(), ports_rcsdriver::gc_posXIn ); - rcs_read( pos.y(), ports_rcsdriver::gc_posYIn ); - rcs_read( pos.z(), ports_rcsdriver::gc_posZIn ); - rcs_read( dir.x(), ports_rcsdriver::gc_dirXIn ); - rcs_read( dir.y(), ports_rcsdriver::gc_dirYIn ); - rcs_read( dir.z(), ports_rcsdriver::gc_dirZIn ); - rcs_read( cmdLin.x(), ports_rcsdriver::gc_cmdLinXIn ); - rcs_read( cmdLin.y(), ports_rcsdriver::gc_cmdLinYIn ); - rcs_read( cmdLin.z(), ports_rcsdriver::gc_cmdLinZIn ); - rcs_read( cmdAng.x(), ports_rcsdriver::gc_cmdAngXIn ); - rcs_read( cmdAng.y(), ports_rcsdriver::gc_cmdAngYIn ); - rcs_read( cmdAng.z(), ports_rcsdriver::gc_cmdAngZIn ); - - OSP_LOG_TRACE("RCS controller {} pitch = {}", local, cmdAng.x()); - OSP_LOG_TRACE("RCS controller {} yaw = {}", local, cmdAng.y()); - OSP_LOG_TRACE("RCS controller {} roll = {}", local, cmdAng.z()); - - float const thrCurr = rSigValFloat[thrNode]; - float const thrNew = thruster_influence(pos, dir, cmdLin, cmdAng); - - if (thrCurr != thrNew) - { - rSigUpdFloat.assign(thrNode, thrNew); - } - } - })); - - machRcsDriver.task() = rBuilder.task().assign({tgSceneEvt, tgLinkReq, tgLinkMhUpdMod}).data( - "Allocate Machine update bitset for RCS Drivers", - TopDataIds_t{idScnParts, idUpdMach}, - gen_allocate_mach_bitsets()); - - return machRcsDriver; -} + return out; +} // setup_parts Session setup_vehicle_spawn( TopTaskBuilder& rBuilder, ArrayView const topData, - Session const& commonScene) + Session const& scene) { - OSP_SESSION_UNPACK_TAGS(commonScene, TESTAPP_COMMON_SCENE); + auto const tgScn = scene.get_pipelines(); - Session vehicleSpawn; - OSP_SESSION_ACQUIRE_DATA(vehicleSpawn, topData, TESTAPP_VEHICLE_SPAWN); - OSP_SESSION_ACQUIRE_TAGS(vehicleSpawn, rTags, TESTAPP_VEHICLE_SPAWN); + Session out; + OSP_DECLARE_CREATE_DATA_IDS(out, topData, TESTAPP_DATA_VEHICLE_SPAWN); + auto const tgVhSp = out.create_pipelines(rBuilder); - rBuilder.tag(tgVsBasicInReq) .depend_on({tgVsBasicInMod}); - rBuilder.tag(tgVsBasicInClr) .depend_on({tgVsBasicInMod, tgVsBasicInReq}); - rBuilder.tag(tgVsPartReq) .depend_on({tgVsPartMod}); - rBuilder.tag(tgVsMapPartMachReq) .depend_on({tgVsMapPartMachMod}); - rBuilder.tag(tgVsPartPfReq) .depend_on({tgVsPartPfMod}); - rBuilder.tag(tgVsWeldReq) .depend_on({tgVsWeldMod}); + rBuilder.pipeline(tgVhSp.spawnRequest) .parent(tgScn.update); + rBuilder.pipeline(tgVhSp.spawnedParts) .parent(tgScn.update); + rBuilder.pipeline(tgVhSp.spawnedWelds) .parent(tgScn.update); + rBuilder.pipeline(tgVhSp.rootEnts) .parent(tgScn.update); + rBuilder.pipeline(tgVhSp.spawnedMachs) .parent(tgScn.update); top_emplace< ACtxVehicleSpawn > (topData, idVehicleSpawn); - vehicleSpawn.task() = rBuilder.task().assign({tgSceneEvt, tgVsBasicInClr}).data( - "Clear Vehicle Spawning vector after use", - TopDataIds_t{ idVehicleSpawn}, - wrap_args([] (ACtxVehicleSpawn& rVehicleSpawn) noexcept + rBuilder.task() + .name ("Schedule Vehicle spawn") + .schedules ({tgVhSp.spawnRequest(Schedule_)}) + .sync_with ({tgScn.update(Run)}) + .push_to (out.m_tasks) + .args ({ idVehicleSpawn }) + .func([] (ACtxVehicleSpawn const& rVehicleSpawn) noexcept -> TaskActions + { + return rVehicleSpawn.spawnRequest.empty() ? TaskAction::Cancel : TaskActions{}; + }); + + rBuilder.task() + .name ("Clear Vehicle Spawning vector after use") + .run_on ({tgVhSp.spawnRequest(Clear)}) + .push_to (out.m_tasks) + .args ({ idVehicleSpawn}) + .func([] (ACtxVehicleSpawn& rVehicleSpawn) noexcept { - rVehicleSpawn.m_newVhBasicIn.clear(); - })); + rVehicleSpawn.spawnRequest.clear(); + }); - return vehicleSpawn; -} + return out; +} // setup_vehicle_spawn Session setup_vehicle_spawn_vb( TopTaskBuilder& rBuilder, ArrayView const topData, + Session const& application, + Session const& scene, Session const& commonScene, Session const& prefabs, Session const& parts, Session const& vehicleSpawn, - Session const& signalsFloat, - TopDataId const idResources) + Session const& signalsFloat) { - OSP_SESSION_UNPACK_DATA(commonScene, TESTAPP_COMMON_SCENE); - OSP_SESSION_UNPACK_TAGS(commonScene, TESTAPP_COMMON_SCENE); - OSP_SESSION_UNPACK_DATA(prefabs, TESTAPP_PREFABS); - OSP_SESSION_UNPACK_TAGS(prefabs, TESTAPP_PREFABS); - OSP_SESSION_UNPACK_DATA(vehicleSpawn, TESTAPP_VEHICLE_SPAWN); - OSP_SESSION_UNPACK_TAGS(vehicleSpawn, TESTAPP_VEHICLE_SPAWN); - OSP_SESSION_UNPACK_DATA(parts, TESTAPP_PARTS); - OSP_SESSION_UNPACK_TAGS(parts, TESTAPP_PARTS); - OSP_SESSION_UNPACK_DATA(signalsFloat, TESTAPP_SIGNALS_FLOAT); - OSP_SESSION_UNPACK_TAGS(signalsFloat, TESTAPP_SIGNALS_FLOAT); - - Session vehicleSpawnVB; - OSP_SESSION_ACQUIRE_DATA(vehicleSpawnVB, topData, TESTAPP_VEHICLE_SPAWN_VB); - OSP_SESSION_ACQUIRE_TAGS(vehicleSpawnVB, rTags, TESTAPP_VEHICLE_SPAWN_VB); - - rBuilder.tag(tgVbSpBasicInReq) .depend_on({tgVbSpBasicInMod}); - rBuilder.tag(tgVbPartReq) .depend_on({tgVbPartMod}); - rBuilder.tag(tgVbWeldReq) .depend_on({tgVbWeldMod}); - rBuilder.tag(tgVbMachReq) .depend_on({tgVbMachMod}); + OSP_DECLARE_GET_DATA_IDS(application, TESTAPP_DATA_APPLICATION); + OSP_DECLARE_GET_DATA_IDS(commonScene, TESTAPP_DATA_COMMON_SCENE); + OSP_DECLARE_GET_DATA_IDS(parts, TESTAPP_DATA_PARTS); + OSP_DECLARE_GET_DATA_IDS(prefabs, TESTAPP_DATA_PREFABS); + OSP_DECLARE_GET_DATA_IDS(signalsFloat, TESTAPP_DATA_SIGNALS_FLOAT); + OSP_DECLARE_GET_DATA_IDS(vehicleSpawn, TESTAPP_DATA_VEHICLE_SPAWN); + auto const tgPf = prefabs .get_pipelines(); + auto const tgScn = scene .get_pipelines(); + auto const tgParts = parts .get_pipelines(); + auto const tgSgFlt = signalsFloat .get_pipelines(); + auto const tgVhSp = vehicleSpawn .get_pipelines(); + + Session out; + OSP_DECLARE_CREATE_DATA_IDS(out, topData, TESTAPP_DATA_VEHICLE_SPAWN_VB); + auto const tgVhSpVB = out.create_pipelines(rBuilder); + + rBuilder.pipeline(tgVhSpVB.dataVB) .parent(tgScn.update); + rBuilder.pipeline(tgVhSpVB.remapParts) .parent(tgScn.update); + rBuilder.pipeline(tgVhSpVB.remapWelds) .parent(tgScn.update); + rBuilder.pipeline(tgVhSpVB.remapMachs) .parent(tgScn.update); + rBuilder.pipeline(tgVhSpVB.remapNodes) .parent(tgScn.update); top_emplace< ACtxVehicleSpawnVB >(topData, idVehicleSpawnVB); - vehicleSpawnVB.task() = rBuilder.task().assign({tgSceneEvt, tgVsBasicInReq, tgVbSpBasicInReq, tgEntNew, tgPartMod, tgWeldMod, tgVsPartMod, tgVsWeldMod, tgVbPartMod, tgVbWeldMod}).data( - "Create part IDs for vehicles from VehicleData", - TopDataIds_t{ idVehicleSpawn, idVehicleSpawnVB, idScnParts}, - wrap_args([] (ACtxVehicleSpawn& rVehicleSpawn, ACtxVehicleSpawnVB& rVehicleSpawnVB, ACtxParts& rScnParts) noexcept + rBuilder.task() + .name ("Create PartIds and WeldIds for vehicles to spawn from VehicleData") + .run_on ({tgVhSp.spawnRequest(UseOrRun)}) + .sync_with ({tgVhSp.spawnedParts(Resize), tgVhSpVB.remapParts(Modify_), tgVhSpVB.remapWelds(Modify_), tgParts.partIds(New), tgParts.weldIds(New), tgParts.mapWeldActive(New)}) + .push_to (out.m_tasks) + .args ({ idVehicleSpawn, idVehicleSpawnVB, idScnParts}) + .func([] (ACtxVehicleSpawn& rVehicleSpawn, ACtxVehicleSpawnVB& rVehicleSpawnVB, ACtxParts& rScnParts) noexcept { - std::size_t const newVehicleCount = rVehicleSpawn.new_vehicle_count(); - ACtxVehicleSpawnVB &rVSVB = rVehicleSpawnVB; - - if (newVehicleCount == 0) - { - return; - } - - rVSVB.m_remapPartOffsets .resize(newVehicleCount); - rVSVB.m_remapWeldOffsets .resize(newVehicleCount); - rVehicleSpawn.m_newVhPartOffsets .resize(newVehicleCount); - rVehicleSpawn.m_newVhWeldOffsets .resize(newVehicleCount); - - // Count total parts and welds, and calculate offsets for remaps - - std::size_t partTotal = 0; - std::size_t remapPartTotal = 0; - - std::size_t weldTotal = 0; - std::size_t remapWeldTotal = 0; - - for (NewVehicleId vhId = 0; vhId < newVehicleCount; ++vhId) - { - VehicleData const* pVData = rVSVB.m_dataVB[vhId]; - if (pVData == nullptr) - { - continue; - } - - rVehicleSpawn.m_newVhPartOffsets[vhId] = partTotal; - partTotal += pVData->m_partIds.size(); - - rVSVB.m_remapPartOffsets[vhId] = remapPartTotal; - remapPartTotal += pVData->m_partIds.capacity(); - - rVehicleSpawn.m_newVhWeldOffsets[vhId] = weldTotal; - weldTotal += pVData->m_weldIds.size(); - - rVSVB.m_remapWeldOffsets[vhId] = remapWeldTotal; - remapWeldTotal += pVData->m_weldIds.capacity(); - } - - // Resize containers for new IDs - - rVehicleSpawn.m_newPartToPart .resize(partTotal); - rVehicleSpawn.m_newWeldToWeld .resize(weldTotal); - rVehicleSpawn.m_newPartPrefabs .resize(partTotal); - rVSVB.m_remapParts .resize(remapPartTotal, lgrn::id_null()); - rVSVB.m_remapWelds .resize(remapPartTotal, lgrn::id_null()); - - // Create new Scene PartIds and WeldIds - - rScnParts.m_partIds.create(std::begin(rVehicleSpawn.m_newPartToPart), std::end(rVehicleSpawn.m_newPartToPart)); - rScnParts.m_weldIds.create(std::begin(rVehicleSpawn.m_newWeldToWeld), std::end(rVehicleSpawn.m_newWeldToWeld)); - - rScnParts.m_partDirty.insert(std::begin(rScnParts.m_partDirty), std::begin(rVehicleSpawn.m_newPartToPart), std::end(rVehicleSpawn.m_newPartToPart)); - rScnParts.m_weldDirty.insert(std::begin(rScnParts.m_weldDirty), std::begin(rVehicleSpawn.m_newWeldToWeld), std::end(rVehicleSpawn.m_newWeldToWeld)); - - // Resize scene containers to account for new IDs - - std::size_t const maxParts = rScnParts.m_partIds.capacity(); - std::size_t const maxWelds = rScnParts.m_weldIds.capacity(); - rScnParts.m_partPrefabs .resize(maxParts); - rScnParts.m_partTransformWeld .resize(maxParts); - rScnParts.m_partToWeld .resize(maxParts); - rScnParts.m_weldToParts .data_reserve(maxParts); - rScnParts.m_weldToParts .ids_reserve(maxWelds); - rScnParts.m_weldToEnt .resize(maxWelds); - rVehicleSpawn.m_partToNewPart .resize(maxParts); - - // Populate "Scene PartId -> NewPartId" map - - auto itPart = std::begin(rVehicleSpawn.m_newPartToPart); - for (PartId newPart = 0; newPart < rVehicleSpawn.m_newPartToPart.size(); ++newPart) - { - rVehicleSpawn.m_partToNewPart[*itPart] = newPart; - std::advance(itPart, 1); - } - - // Populate remap vectors and set weld connections - - auto itDstPartIds = std::cbegin(rVehicleSpawn.m_newPartToPart); - auto itDstWeldIds = std::cbegin(rVehicleSpawn.m_newWeldToWeld); - - for (NewVehicleId vhId = 0; vhId < newVehicleCount; ++vhId) - { - VehicleData const* pVData = rVSVB.m_dataVB[vhId]; - if (pVData == nullptr) - { - continue; - } - - std::size_t const remapPartOffset = rVSVB.m_remapPartOffsets[vhId]; - std::size_t const remapWeldOffset = rVSVB.m_remapWeldOffsets[vhId]; - - for (PartId const srcPart : pVData->m_partIds.bitview().zeros()) - { - PartId const dstPart = *itDstPartIds; - ++itDstPartIds; - - // Populate map for "VehicleBuilder PartId -> ACtxParts PartId" - rVSVB.m_remapParts[remapPartOffset + srcPart] = dstPart; - } - - for (WeldId const srcWeld : pVData->m_weldIds.bitview().zeros()) - { - WeldId const dstWeld = *itDstWeldIds; - ++itDstWeldIds; - - // Populate map for "VehicleBuilder WeldId -> ACtxParts WeldId" - // rVehicleSpawnVB.m_remapWelds - rVSVB.m_remapWelds[remapWeldOffset + srcWeld] = dstWeld; - - // Use remaps to connect ACtxParts WeldIds and PartIds - // rScnParts.m_partToWeld and rScnParts.m_weldToParts - - auto const srcWeldPartSpan = pVData->m_weldToParts[srcWeld]; - WeldId *pDstWeldPartOut = rScnParts.m_weldToParts.emplace(dstWeld, srcWeldPartSpan.size()); - - for (PartId const srcPart : srcWeldPartSpan) - { - PartId const dstPart = rVSVB.m_remapParts[remapPartOffset + srcPart]; - - (*pDstWeldPartOut) = dstPart; - std::advance(pDstWeldPartOut, 1); - - rScnParts.m_partToWeld[dstPart] = dstWeld; - } - } - } - })); + SysVehicleSpawnVB::create_parts_and_welds(rVehicleSpawn, rVehicleSpawnVB, rScnParts); + }); - vehicleSpawnVB.task() = rBuilder.task().assign({tgSceneEvt, tgVsBasicInReq, tgVsPartReq, tgVbPartReq, tgPrefabMod}).data( - "Request prefabs for vehicle parts from VehicleBuilder", - TopDataIds_t{ idVehicleSpawn, idVehicleSpawnVB, idScnParts, idPrefabInit, idResources}, - wrap_args([] (ACtxVehicleSpawn& rVehicleSpawn, ACtxVehicleSpawnVB const& rVehicleSpawnVB, ACtxParts& rScnParts, ACtxPrefabInit& rPrefabInit, Resources& rResources) noexcept + rBuilder.task() + .name ("Request prefabs for vehicle parts from VehicleBuilder") + .run_on ({tgVhSp.spawnRequest(UseOrRun)}) + .sync_with ({tgPf.spawnRequest(Modify_), tgVhSp.spawnedParts(UseOrRun)}) + .push_to (out.m_tasks) + .args ({ idVehicleSpawn, idVehicleSpawnVB, idScnParts, idPrefabs, idResources}) + .func([] (ACtxVehicleSpawn& rVehicleSpawn, ACtxVehicleSpawnVB const& rVehicleSpawnVB, ACtxParts& rScnParts, ACtxPrefabs& rPrefabs, Resources& rResources) noexcept { - if (rVehicleSpawn.new_vehicle_count() == 0) - { - return; - } - - auto itDstPartIds = std::cbegin(rVehicleSpawn.m_newPartToPart); - auto itPrefabOut = std::begin(rVehicleSpawn.m_newPartPrefabs); - - for (VehicleData const* pVData : rVehicleSpawnVB.m_dataVB) - { - if (pVData == nullptr) - { - continue; - } - - // Copy Part data from VehicleBuilder to scene - for (uint32_t srcPart : pVData->m_partIds.bitview().zeros()) - { - PartId const dstPart = *itDstPartIds; - std::advance(itDstPartIds, 1); - - PrefabPair const& prefabPairSrc = pVData->m_partPrefabs[srcPart]; - PrefabPair prefabPairDst{ - rResources.owner_create(gc_importer, prefabPairSrc.m_importer), - prefabPairSrc.m_prefabId - }; - rScnParts.m_partPrefabs[dstPart] = std::move(prefabPairDst); - rScnParts.m_partTransformWeld[dstPart] = pVData->m_partTransformWeld[srcPart]; - - // Add Prefab and Part init events - (*itPrefabOut) = rPrefabInit.m_basicIn.size(); - std::advance(itPrefabOut, 1); - - rPrefabInit.m_basicIn.push_back(TmpPrefabInitBasic{ - .m_importerRes = prefabPairSrc.m_importer, - .m_prefabId = prefabPairSrc.m_prefabId, - .m_pTransform = &pVData->m_partTransformWeld[srcPart] - }); - } - } - - })); + SysVehicleSpawnVB::request_prefabs(rVehicleSpawn, rVehicleSpawnVB, rScnParts, rPrefabs, rResources); + }); - vehicleSpawnVB.task() = rBuilder.task().assign({tgSceneEvt, tgVsBasicInReq, tgVbMachMod, tgLinkMod}).data( - "Copy Machine IDs from VehicleData to ACtxParts", - TopDataIds_t{ idVehicleSpawn, idVehicleSpawnVB, idScnParts}, - wrap_args([] (ACtxVehicleSpawn& rVehicleSpawn, ACtxVehicleSpawnVB& rVehicleSpawnVB, ACtxParts& rScnParts) noexcept + rBuilder.task() + .name ("Create Machine IDs copied from VehicleData") + .run_on ({tgVhSp.spawnRequest(UseOrRun)}) + .sync_with ({tgVhSpVB.dataVB(UseOrRun), tgVhSpVB.remapMachs(Modify_), tgVhSp.spawnedMachs(Resize), tgParts.machIds(New)}) + .push_to (out.m_tasks) + .args ({ idVehicleSpawn, idVehicleSpawnVB, idScnParts}) + .func([] (ACtxVehicleSpawn& rVehicleSpawn, ACtxVehicleSpawnVB& rVehicleSpawnVB, ACtxParts& rScnParts) noexcept { std::size_t const newVehicleCount = rVehicleSpawn.new_vehicle_count(); ACtxVehicleSpawnVB &rVSVB = rVehicleSpawnVB; - if (newVehicleCount == 0) - { - return; - } - // Count total machines, and calculate offsets for remaps. std::size_t machTotal = 0; std::size_t remapMachTotal = 0; - rVSVB.m_machtypeCount.clear(); - rVSVB.m_machtypeCount.resize(MachTypeReg_t::size(), 0); + rVSVB.machtypeCount.clear(); + rVSVB.machtypeCount.resize(MachTypeReg_t::size(), 0); - rVSVB.m_remapMachOffsets.resize(newVehicleCount); + rVSVB.remapMachOffsets.resize(newVehicleCount); - for (NewVehicleId vhId = 0; vhId < newVehicleCount; ++vhId) + for (SpVehicleId vhId{0}; vhId.value < newVehicleCount; ++vhId.value) { - VehicleData const* pVData = rVSVB.m_dataVB[vhId]; + VehicleData const* pVData = rVSVB.dataVB[vhId]; if (pVData == nullptr) { continue; } Machines const &srcMachines = pVData->m_machines; - std::size_t const bounds = srcMachines.m_ids.capacity(); + std::size_t const bounds = srcMachines.ids.capacity(); - rVSVB.m_remapMachOffsets[vhId] = remapMachTotal; + rVSVB.remapMachOffsets[vhId] = remapMachTotal; remapMachTotal += bounds; - machTotal += srcMachines.m_ids.size(); + machTotal += srcMachines.ids.size(); for (MachTypeId type = 0; type < MachTypeReg_t::size(); ++type) { - rVSVB.m_machtypeCount[type] += srcMachines.m_perType[type].m_localIds.size(); + rVSVB.machtypeCount[type] += srcMachines.perType[type].localIds.size(); } } - rVehicleSpawn.m_newMachToMach.resize(machTotal); - rVSVB.m_remapMachs.resize(remapMachTotal); + rVehicleSpawn.spawnedMachs.resize(machTotal); + rVSVB.remapMachs.resize(remapMachTotal); // Create ACtxParts MachAny/LocalIDs and populate remaps // MachAnyIDs created here - rScnParts.m_machines.m_ids.create(rVehicleSpawn.m_newMachToMach.begin(), - rVehicleSpawn.m_newMachToMach.end()); + rScnParts.machines.ids.create(rVehicleSpawn.spawnedMachs.begin(), + rVehicleSpawn.spawnedMachs.end()); - rScnParts.m_machines.m_machToLocal.resize(rScnParts.m_machines.m_ids.capacity()); + rScnParts.machines.machToLocal.resize(rScnParts.machines.ids.capacity()); - auto itDstMachIds = std::cbegin(rVehicleSpawn.m_newMachToMach); + auto itDstMachIds = rVehicleSpawn.spawnedMachs.cbegin(); - for (NewVehicleId vhId = 0; vhId < newVehicleCount; ++vhId) + for (SpVehicleId vhId{0}; vhId.value < newVehicleCount; ++vhId.value) { - VehicleData const* pVData = rVSVB.m_dataVB[vhId]; + VehicleData const* pVData = rVSVB.dataVB[vhId]; if (pVData == nullptr) { continue; @@ -660,15 +312,15 @@ Session setup_vehicle_spawn_vb( Machines const &srcMachines = pVData->m_machines; - std::size_t const remapMachOffset = rVSVB.m_remapMachOffsets[vhId]; + std::size_t const remapMachOffset = rVSVB.remapMachOffsets[vhId]; - for (MachAnyId const srcMach : srcMachines.m_ids.bitview().zeros()) + for (MachAnyId const srcMach : srcMachines.ids.bitview().zeros()) { MachAnyId const dstMach = *itDstMachIds; ++itDstMachIds; // Populate map for "VehicleBuilder MachAnyId -> ACtxParts MachAnyId" - rVSVB.m_remapMachs[remapMachOffset + srcMach] = dstMach; + rVSVB.remapMachs[remapMachOffset + srcMach] = dstMach; // Create ACtxParts MachLocalIds // MachLocalIds don't need a remap, since they can be obtained @@ -676,100 +328,96 @@ Session setup_vehicle_spawn_vb( // TODO: This can be optimized later, where all local IDs are // created at once with ids.create(first, last), and make // resize(..) called once per type too - MachTypeId const type = srcMachines.m_machTypes[srcMach]; - PerMachType& rDstPerType = rScnParts.m_machines.m_perType[type]; + MachTypeId const type = srcMachines.machTypes[srcMach]; + PerMachType& rDstPerType = rScnParts.machines.perType[type]; - MachLocalId const dstLocal = rDstPerType.m_localIds.create(); - rDstPerType.m_localToAny.resize(rDstPerType.m_localIds.capacity()); + MachLocalId const dstLocal = rDstPerType.localIds.create(); + rDstPerType.localToAny.resize(rDstPerType.localIds.capacity()); - rDstPerType.m_localToAny[dstLocal] = dstMach; - rScnParts.m_machines.m_machToLocal[dstMach] = dstLocal; + rDstPerType.localToAny[dstLocal] = dstMach; + rScnParts.machines.machToLocal[dstMach] = dstLocal; } } - })); + }); - vehicleSpawnVB.task() = rBuilder.task().assign({tgSceneEvt, tgVsBasicInReq, tgVsPartReq, tgVbPartReq, tgVbMachReq, tgVsMapPartMachMod}).data( - "Update Part<->Machine maps", - TopDataIds_t{ idVehicleSpawn, idVehicleSpawnVB, idScnParts, idPrefabInit, idResources}, - wrap_args([] (ACtxVehicleSpawn& rVehicleSpawn, ACtxVehicleSpawnVB const& rVehicleSpawnVB, ACtxParts& rScnParts, ACtxPrefabInit& rPrefabInit, Resources& rResources) noexcept + rBuilder.task() + .name ("Update Part<->Machine maps") + .run_on ({tgVhSp.spawnRequest(UseOrRun)}) + .sync_with ({tgVhSpVB.dataVB(UseOrRun), tgVhSpVB.remapMachs(UseOrRun), tgVhSpVB.remapParts(UseOrRun), tgParts.mapPartMach(New)}) + .push_to (out.m_tasks) + .args ({ idVehicleSpawn, idVehicleSpawnVB, idScnParts, idPrefabs, idResources}) + .func([] (ACtxVehicleSpawn& rVehicleSpawn, ACtxVehicleSpawnVB const& rVehicleSpawnVB, ACtxParts& rScnParts, ACtxPrefabs& rPrefabs, Resources& rResources) noexcept { std::size_t const newVehicleCount = rVehicleSpawn.new_vehicle_count(); ACtxVehicleSpawnVB const& rVSVB = rVehicleSpawnVB; - if (newVehicleCount == 0) + for (SpVehicleId vhId{0}; vhId.value < newVehicleCount; ++vhId.value) { - return; - } - - for (NewVehicleId vhId = 0; vhId < newVehicleCount; ++vhId) - { - VehicleData const* pVData = rVSVB.m_dataVB[vhId]; + VehicleData const* pVData = rVSVB.dataVB[vhId]; if (pVData == nullptr) { continue; } - rScnParts.m_machineToPart.resize(rScnParts.m_machines.m_ids.capacity()); - rScnParts.m_partToMachines.ids_reserve(rScnParts.m_partIds.capacity()); - rScnParts.m_partToMachines.data_reserve(rScnParts.m_machines.m_ids.capacity()); + rScnParts.machineToPart.resize(rScnParts.machines.ids.capacity()); + rScnParts.partToMachines.ids_reserve(rScnParts.partIds.capacity()); + rScnParts.partToMachines.data_reserve(rScnParts.machines.ids.capacity()); - std::size_t const remapMachOffset = rVSVB.m_remapMachOffsets[vhId]; - std::size_t const remapPartOffset = rVSVB.m_remapPartOffsets[vhId]; + std::size_t const remapMachOffset = rVSVB.remapMachOffsets[vhId]; + std::size_t const remapPartOffset = rVSVB.remapPartOffsets[vhId]; // Update rScnParts machine->part map - for (MachAnyId const srcMach : pVData->m_machines.m_ids.bitview().zeros()) + for (MachAnyId const srcMach : pVData->m_machines.ids.bitview().zeros()) { - MachAnyId const dstMach = rVSVB.m_remapMachs[remapMachOffset + srcMach]; + MachAnyId const dstMach = rVSVB.remapMachs[remapMachOffset + srcMach]; PartId const srcPart = pVData->m_machToPart[srcMach]; - PartId const dstPart = rVSVB.m_remapParts[remapPartOffset + srcPart]; + PartId const dstPart = rVSVB.remapParts[remapPartOffset + srcPart]; - rScnParts.m_machineToPart[dstMach] = dstPart; + rScnParts.machineToPart[dstMach] = dstPart; } // Update rScnParts part->machine multimap for (PartId const srcPart : pVData->m_partIds.bitview().zeros()) { - PartId const dstPart = rVSVB.m_remapParts[remapPartOffset + srcPart]; + PartId const dstPart = rVSVB.remapParts[remapPartOffset + srcPart]; auto const& srcPairs = pVData->m_partToMachines[srcPart]; - rScnParts.m_partToMachines.emplace(dstPart, srcPairs.size()); - auto dstPairs = rScnParts.m_partToMachines[dstPart]; + rScnParts.partToMachines.emplace(dstPart, srcPairs.size()); + auto dstPairs = rScnParts.partToMachines[dstPart]; for (int i = 0; i < srcPairs.size(); ++i) { MachinePair const& srcPair = srcPairs[i]; MachinePair& rDstPair = dstPairs[i]; - MachAnyId const srcMach = pVData->m_machines.m_perType[srcPair.m_type].m_localToAny[srcPair.m_local]; - MachAnyId const dstMach = rVSVB.m_remapMachs[remapMachOffset + srcMach]; - MachTypeId const dstType = srcPair.m_type; - MachLocalId const dstLocal = rScnParts.m_machines.m_machToLocal[dstMach]; + MachAnyId const srcMach = pVData->m_machines.perType[srcPair.type].localToAny[srcPair.local]; + MachAnyId const dstMach = rVSVB.remapMachs[remapMachOffset + srcMach]; + MachTypeId const dstType = srcPair.type; + MachLocalId const dstLocal = rScnParts.machines.machToLocal[dstMach]; - rDstPair = { .m_local = dstLocal, .m_type = dstType }; + rDstPair = { .local = dstLocal, .type = dstType }; } } } - })); + }); - vehicleSpawnVB.task() = rBuilder.task().assign({tgSceneEvt, tgVsPartReq, tgVbPartReq, tgVbMachReq, tgLinkMod, tgVbNodeMod}).data( - "Copy Node IDs from VehicleBuilder to ACtxParts", - TopDataIds_t{ idVehicleSpawn, idVehicleSpawnVB, idScnParts}, - wrap_args([] (ACtxVehicleSpawn& rVehicleSpawn, ACtxVehicleSpawnVB& rVehicleSpawnVB, ACtxParts& rScnParts) noexcept + rBuilder.task() + .name ("Create (and connect) Node IDs copied from VehicleBuilder") + .run_on ({tgVhSp.spawnRequest(UseOrRun)}) + .sync_with ({tgVhSpVB.dataVB(UseOrRun), tgVhSpVB.remapMachs(UseOrRun), tgVhSpVB.remapNodes(Modify_), tgParts.nodeIds(New), tgParts.connect(New)}) + .push_to (out.m_tasks) + .args ({ idVehicleSpawn, idVehicleSpawnVB, idScnParts}) + .func([] (ACtxVehicleSpawn& rVehicleSpawn, ACtxVehicleSpawnVB& rVehicleSpawnVB, ACtxParts& rScnParts) noexcept { std::size_t const newVehicleCount = rVehicleSpawn.new_vehicle_count(); ACtxVehicleSpawnVB &rVSVB = rVehicleSpawnVB; - if (newVehicleCount == 0) - { - return; - } - - rVSVB.m_remapNodeOffsets.resize(newVehicleCount * NodeTypeReg_t::size()); + rVSVB.remapNodeOffsets.resize(newVehicleCount * NodeTypeReg_t::size()); auto remapNodeOffsets2d = rVSVB.remap_node_offsets_2d(); // Add up bounds needed for all nodes of every type for remaps std::size_t remapNodeTotal = 0; - for (VehicleData const* pVData : rVSVB.m_dataVB) + for (VehicleData const* pVData : rVSVB.dataVB) { if (pVData == nullptr) { @@ -777,82 +425,83 @@ Session setup_vehicle_spawn_vb( } for (PerNodeType const &rSrcNodeType : pVData->m_nodePerType) { - remapNodeTotal += rSrcNodeType.m_nodeIds.capacity(); + remapNodeTotal += rSrcNodeType.nodeIds.capacity(); } } - rVSVB.m_remapNodes.resize(remapNodeTotal); + rVSVB.remapNodes.resize(remapNodeTotal); std::size_t nodeRemapUsed = 0; - for (NewVehicleId vhId = 0; vhId < newVehicleCount; ++vhId) + for (SpVehicleId vhId{0}; vhId.value < newVehicleCount; ++vhId.value) { - VehicleData const* pVData = rVSVB.m_dataVB[vhId]; + VehicleData const* pVData = rVSVB.dataVB[vhId]; if (pVData == nullptr) { continue; } - auto machRemap = arrayView(std::as_const(rVSVB.m_remapMachs)).exceptPrefix(rVSVB.m_remapMachOffsets[vhId]); + auto machRemap = arrayView(std::as_const(rVSVB.remapMachs)).exceptPrefix(rVSVB.remapMachOffsets[vhId]); for (NodeTypeId nodeType = 0; nodeType < NodeTypeReg_t::size(); ++nodeType) { PerNodeType const &rSrcNodeType = pVData->m_nodePerType[nodeType]; - std::size_t const remapSize = rSrcNodeType.m_nodeIds.capacity(); - auto nodeRemapOut = arrayView(rVSVB.m_remapNodes).sliceSize(nodeRemapUsed, remapSize); - remapNodeOffsets2d[vhId][nodeType] = nodeRemapUsed; + std::size_t const remapSize = rSrcNodeType.nodeIds.capacity(); + auto nodeRemapOut = arrayView(rVSVB.remapNodes).sliceSize(nodeRemapUsed, remapSize); + remapNodeOffsets2d[vhId.value][nodeType] = nodeRemapUsed; nodeRemapUsed += remapSize; copy_nodes(rSrcNodeType, pVData->m_machines, machRemap, - rScnParts.m_nodePerType[nodeType], rScnParts.m_machines, nodeRemapOut); + rScnParts.nodePerType[nodeType], rScnParts.machines, nodeRemapOut); } } - })); + }); - vehicleSpawnVB.task() = rBuilder.task().assign({tgSceneEvt, tgVsPartReq, tgPrefabEntReq, tgMapPartEntMod}).data( - "Update PartId<->ActiveEnt mapping", - TopDataIds_t{ idVehicleSpawn, idScnParts, idActiveIds, idPrefabInit }, - wrap_args([] (ACtxVehicleSpawn& rVehicleSpawn, ACtxParts& rScnParts, ActiveReg_t const& rActiveIds, ACtxPrefabInit& rPrefabInit) noexcept + rBuilder.task() + .name ("Update PartId<->ActiveEnt mapping") + .run_on ({tgVhSp.spawnRequest(UseOrRun)}) + .sync_with ({tgVhSp.spawnedParts(UseOrRun), tgPf.spawnedEnts(UseOrRun), tgParts.mapPartActive(Modify)}) + .push_to (out.m_tasks) + .args ({ idVehicleSpawn, idBasic, idScnParts, idPrefabs}) + .func([] (ACtxVehicleSpawn& rVehicleSpawn, ACtxBasic const& rBasic, ACtxParts& rScnParts, ACtxPrefabs& rPrefabs) noexcept { - if (rVehicleSpawn.new_vehicle_count() == 0) - { - return; - } - - rScnParts.m_partToActive.resize(rScnParts.m_partIds.capacity()); - rScnParts.m_activeToPart.resize(rActiveIds.capacity()); + rScnParts.partToActive.resize(rScnParts.partIds.capacity()); + rScnParts.activeToPart.resize(rBasic.m_activeIds.capacity()); // Populate PartId<->ActiveEnt mapping, now that the prefabs exist - auto itPrefab = std::begin(rVehicleSpawn.m_newPartPrefabs); + auto itPrefab = rVehicleSpawn.spawnedPrefabs.begin(); - for (PartId const partId : rVehicleSpawn.m_newPartToPart) + for (PartId const partId : rVehicleSpawn.spawnedParts) { - ActiveEnt const root = rPrefabInit.m_ents[*itPrefab].front(); - std::advance(itPrefab, 1); + ActiveEnt const root = rPrefabs.spawnedEntsOffset[*itPrefab].front(); + ++itPrefab; - rScnParts.m_partToActive[partId] = root; - rScnParts.m_activeToPart[std::size_t(root)] = partId; + rScnParts.partToActive[partId] = root; + rScnParts.activeToPart[root] = partId; } - })); + }); - vehicleSpawnVB.task() = rBuilder.task().assign({tgSceneEvt, tgVbNodeReq, tgSigFloatLinkReq}).data( - "Copy float signal values from VehicleBuilder", - TopDataIds_t{ idVehicleSpawn, idVehicleSpawnVB, idScnParts, idSigValFloat}, - wrap_args([] (ACtxVehicleSpawn& rVehicleSpawn, ACtxVehicleSpawnVB const& rVehicleSpawnVB, ACtxParts& rScnParts, SignalValues_t& rSigValFloat) noexcept + rBuilder.task() + .name ("Copy float signal values from VehicleBuilder") + .run_on ({tgVhSp.spawnRequest(UseOrRun)}) + .sync_with ({tgVhSp.spawnedParts(UseOrRun), tgVhSpVB.remapNodes(UseOrRun), tgSgFlt.sigFloatValues(New), tgSgFlt.sigFloatUpdExtIn(New)}) + .push_to (out.m_tasks) + .args ({ idVehicleSpawn, idVehicleSpawnVB, idScnParts, idSigValFloat, idSigUpdFloat}) + .func([] (ACtxVehicleSpawn& rVehicleSpawn, ACtxVehicleSpawnVB const& rVehicleSpawnVB, ACtxParts& rScnParts, SignalValues_t& rSigValFloat, UpdateNodes& rSigUpdFloat) noexcept { - std::size_t const newVehicleCount = rVehicleSpawn.new_vehicle_count(); - ACtxVehicleSpawnVB const &rVSVB = rVehicleSpawnVB; + Nodes const &rFloatNodes = rScnParts.nodePerType[gc_ntSigFloat]; + std::size_t const maxNodes = rFloatNodes.nodeIds.capacity(); + rSigUpdFloat.nodeNewValues.resize(maxNodes); + bitvector_resize(rSigUpdFloat.nodeDirty, maxNodes); + rSigValFloat.resize(maxNodes); - if (newVehicleCount == 0) - { - return; - } + std::size_t const newVehicleCount = rVehicleSpawn.new_vehicle_count(); + ACtxVehicleSpawnVB const &rVSVB = rVehicleSpawnVB; auto const remapNodeOffsets2d = rVSVB.remap_node_offsets_2d(); - - for (NewVehicleId vhId = 0; vhId < newVehicleCount; ++vhId) + for (SpVehicleId vhId{0}; vhId.value < newVehicleCount; ++vhId.value) { - VehicleData const* pVData = rVSVB.m_dataVB[vhId]; + VehicleData const* pVData = rVSVB.dataVB[vhId]; if (pVData == nullptr) { continue; @@ -861,397 +510,125 @@ Session setup_vehicle_spawn_vb( PerNodeType const& srcFloatNodes = pVData->m_nodePerType[gc_ntSigFloat]; entt::any const& srcFloatValuesAny = srcFloatNodes.m_nodeValues; auto const& srcFloatValues = entt::any_cast< SignalValues_t >(srcFloatValuesAny); - std::size_t const nodeRemapOffset = remapNodeOffsets2d[vhId][gc_ntSigFloat]; - auto const nodeRemap = arrayView(rVSVB.m_remapNodes).exceptPrefix(nodeRemapOffset); + std::size_t const nodeRemapOffset = remapNodeOffsets2d[vhId.value][gc_ntSigFloat]; + auto const nodeRemap = arrayView(rVSVB.remapNodes).exceptPrefix(nodeRemapOffset); - for (NodeId const srcNode : srcFloatNodes.m_nodeIds.bitview().zeros()) + for (NodeId const srcNode : srcFloatNodes.nodeIds.bitview().zeros()) { NodeId const dstNode = nodeRemap[srcNode]; rSigValFloat[dstNode] = srcFloatValues[srcNode]; } } - })); - - return vehicleSpawnVB; -} - -Matrix4 quick_transform(Vector3 const pos, Quaternion const rot) noexcept -{ - return Matrix4::from(rot.toMatrix(), pos); -} - -struct RCSInputs -{ - NodeId m_pitch {lgrn::id_null()}; - NodeId m_yaw {lgrn::id_null()}; - NodeId m_roll {lgrn::id_null()}; -}; - -void add_rcs_machines(VehicleBuilder& rBuilder, RCSInputs const& inputs, PartId part, float thrustMul, Matrix4 const& tf) -{ - using namespace adera; - namespace ports_rcsdriver = adera::ports_rcsdriver; - namespace ports_magicrocket = adera::ports_magicrocket; - - auto const [posX, posY, posZ, dirX, dirY, dirZ, driverOut, thrMul] = rBuilder.create_nodes<8>(gc_ntSigFloat); - - rBuilder.create_machine(part, gc_mtRcsDriver, { - { ports_rcsdriver::gc_posXIn, posX }, - { ports_rcsdriver::gc_posYIn, posY }, - { ports_rcsdriver::gc_posZIn, posZ }, - { ports_rcsdriver::gc_dirXIn, dirX }, - { ports_rcsdriver::gc_dirYIn, dirY }, - { ports_rcsdriver::gc_dirZIn, dirZ }, - { ports_rcsdriver::gc_cmdAngXIn, inputs.m_pitch }, - { ports_rcsdriver::gc_cmdAngYIn, inputs.m_yaw }, - { ports_rcsdriver::gc_cmdAngZIn, inputs.m_roll }, - { ports_rcsdriver::gc_throttleOut, driverOut } - } ); - - rBuilder.create_machine(part, gc_mtMagicRocket, { - { ports_magicrocket::gc_throttleIn, driverOut }, - { ports_magicrocket::gc_multiplierIn, thrMul } - } ); - - Vector3 const dir = tf.rotation() * gc_rocketForward; - - auto &rFloatValues = rBuilder.node_values< SignalValues_t >(gc_ntSigFloat); - - rFloatValues[posX] = tf.translation().x(); - rFloatValues[posY] = tf.translation().y(); - rFloatValues[posZ] = tf.translation().z(); - rFloatValues[dirX] = dir.x(); - rFloatValues[dirY] = dir.y(); - rFloatValues[dirZ] = dir.z(); - rFloatValues[thrMul] = thrustMul; -} - -void add_rcs_block(VehicleBuilder& rBuilder, VehicleBuilder::WeldVec_t& rWeldTo, RCSInputs const& inputs, float thrustMul, Vector3 pos, Quaternion rot) -{ - constexpr Vector3 xAxis{1.0f, 0.0f, 0.0f}; - - auto const [ nozzleA, nozzleB ] = rBuilder.create_parts<2>(); - rBuilder.set_prefabs({ - { nozzleA, "phLinRCS" }, - { nozzleB, "phLinRCS" } }); - Matrix4 const nozzleTfA = quick_transform(pos, rot * Quaternion::rotation( 90.0_degf, xAxis)); - Matrix4 const nozzleTfB = quick_transform(pos, rot * Quaternion::rotation( -90.0_degf, xAxis)); - - add_rcs_machines(rBuilder, inputs, nozzleA, thrustMul, nozzleTfA); - add_rcs_machines(rBuilder, inputs, nozzleB, thrustMul, nozzleTfB); - - rWeldTo.push_back({ nozzleA, nozzleTfA }); - rWeldTo.push_back({ nozzleB, nozzleTfB }); -} + return out; +} // setup_vehicle_spawn_vb -Session setup_test_vehicles( +Session setup_vehicle_spawn_draw( TopTaskBuilder& rBuilder, ArrayView const topData, - Session const& commonScene, - TopDataId const idResources) + Session const& sceneRenderer, + Session const& vehicleSpawn) { - OSP_SESSION_UNPACK_TAGS(commonScene, TESTAPP_COMMON_SCENE); - - Session testVehicles; - OSP_SESSION_ACQUIRE_DATA(testVehicles, topData, TESTAPP_TEST_VEHICLES); - testVehicles.m_tgCleanupEvt = tgCleanupEvt; - - auto &rResources = top_get(topData, idResources); - - using namespace adera; - - // Build "PartVehicle" - { - VehicleBuilder vbuilder{&rResources}; - VehicleBuilder::WeldVec_t toWeld; - - auto const [ capsule, fueltank, engineA, engineB ] = vbuilder.create_parts<4>(); - vbuilder.set_prefabs({ - { capsule, "phCapsule" }, - { fueltank, "phFuselage" }, - { engineA, "phEngine" }, - { engineB, "phEngine" }, - }); - - toWeld.push_back( {capsule, quick_transform({ 0.0f, 0.0f, 3.0f}, {})} ); - toWeld.push_back( {fueltank, quick_transform({ 0.0f, 0.0f, 0.0f}, {})} ); - toWeld.push_back( {engineA, quick_transform({ 0.7f, 0.0f, -2.9f}, {})} ); - toWeld.push_back( {engineB, quick_transform({-0.7f, 0.0f, -2.9f}, {})} ); - - namespace ports_magicrocket = adera::ports_magicrocket; - namespace ports_userctrl = adera::ports_userctrl; - - auto const [ pitch, yaw, roll, throttle, thrustMul ] = vbuilder.create_nodes<5>(gc_ntSigFloat); - - auto &rFloatValues = vbuilder.node_values< SignalValues_t >(gc_ntSigFloat); - rFloatValues[thrustMul] = 50000.0f; - - vbuilder.create_machine(capsule, gc_mtUserCtrl, { - { ports_userctrl::gc_throttleOut, throttle }, - { ports_userctrl::gc_pitchOut, pitch }, - { ports_userctrl::gc_yawOut, yaw }, - { ports_userctrl::gc_rollOut, roll } - } ); - - vbuilder.create_machine(engineA, gc_mtMagicRocket, { - { ports_magicrocket::gc_throttleIn, throttle }, - { ports_magicrocket::gc_multiplierIn, thrustMul } - } ); - - vbuilder.create_machine(engineB, gc_mtMagicRocket, { - { ports_magicrocket::gc_throttleIn, throttle }, - { ports_magicrocket::gc_multiplierIn, thrustMul } - } ); - - RCSInputs rcsInputs{pitch, yaw, roll}; - - int const rcsRingBlocks = 4; - int const rcsRingCount = 2; - float const rcsRingZ = -2.0f; - float const rcsZStep = 4.0f; - float const rcsRadius = 1.1f; - float const rcsThrust = 3000.0f; - - for (int ring = 0; ring < rcsRingCount; ++ring) - { - Vector3 const rcsOset{rcsRadius, 0.0f, rcsRingZ + ring*rcsZStep }; - - for (Rad ang = 0.0_degf; ang < Rad(360.0_degf); ang += Rad(360.0_degf)/rcsRingBlocks) - { - Quaternion const rotZ = Quaternion::rotation(ang, {0.0f, 0.0f, 1.0f}); - add_rcs_block(vbuilder, toWeld, rcsInputs, rcsThrust, rotZ.transformVector(rcsOset), rotZ); - } - } - - vbuilder.weld(toWeld); - - top_emplace(topData, idTVPartVehicle, vbuilder.finalize_release()); - } - - auto const cleanup_prefab_owners = wrap_args([] (Resources& rResources, VehicleData &rTVData) noexcept + OSP_DECLARE_GET_DATA_IDS(sceneRenderer, TESTAPP_DATA_SCENE_RENDERER); + OSP_DECLARE_GET_DATA_IDS(vehicleSpawn, TESTAPP_DATA_VEHICLE_SPAWN); + auto const tgScnRdr = sceneRenderer .get_pipelines< PlSceneRenderer >(); + auto const tgVhSp = vehicleSpawn .get_pipelines< PlVehicleSpawn >(); + + Session out; + + rBuilder.task() + .name ("Enable Draw Transforms for spawned vehicle root entities") + .run_on ({tgVhSp.spawnRequest(UseOrRun)}) + .sync_with ({tgVhSp.rootEnts(UseOrRun), tgScnRdr.drawEntResized(Done)}) + .push_to (out.m_tasks) + .args ({ idScnRender, idVehicleSpawn}) + .func([] (ACtxSceneRender& rScnRender, ACtxVehicleSpawn const& rVehicleSpawn) noexcept { - for (osp::PrefabPair &rPrefabPair : rTVData.m_partPrefabs) + for (ActiveEnt const ent : rVehicleSpawn.rootEnts) { - rResources.owner_destroy(gc_importer, std::move(rPrefabPair.m_importer)); + rScnRender.m_needDrawTf.set(ent.value); } }); - testVehicles.task() = rBuilder.task().assign({tgCleanupEvt}).data( - "Clean up test vehicle's (idTVPartVehicle) owners", - TopDataIds_t{ idResources, idTVPartVehicle }, cleanup_prefab_owners); + return out; +} // setup_vehicle_spawn_draw - return testVehicles; -} -struct VehicleTestControls -{ - MachLocalId m_selectedUsrCtrl{lgrn::id_null()}; - - input::EButtonControlIndex m_btnSwitch; - input::EButtonControlIndex m_btnThrMax; - input::EButtonControlIndex m_btnThrMin; - input::EButtonControlIndex m_btnThrMore; - input::EButtonControlIndex m_btnThrLess; - input::EButtonControlIndex m_btnPitchUp; - input::EButtonControlIndex m_btnPitchDn; - input::EButtonControlIndex m_btnYawLf; - input::EButtonControlIndex m_btnYawRt; - input::EButtonControlIndex m_btnRollLf; - input::EButtonControlIndex m_btnRollRt; -}; - -Session setup_vehicle_control( +Session setup_signals_float( TopTaskBuilder& rBuilder, ArrayView const topData, - Session const& commonScene, - Session const& parts, - Session const& signalsFloat, - Session const& app) + Session const& scene, + Session const& parts) { - OSP_SESSION_UNPACK_DATA(commonScene, TESTAPP_COMMON_SCENE); - OSP_SESSION_UNPACK_DATA(signalsFloat, TESTAPP_SIGNALS_FLOAT) - OSP_SESSION_UNPACK_TAGS(signalsFloat, TESTAPP_SIGNALS_FLOAT); - OSP_SESSION_UNPACK_DATA(parts, TESTAPP_PARTS); - OSP_SESSION_UNPACK_DATA(app, TESTAPP_APP); - OSP_SESSION_UNPACK_TAGS(app, TESTAPP_APP); - - using namespace adera; - - Session vehicleCtrl; - OSP_SESSION_ACQUIRE_DATA(vehicleCtrl, topData, TESTAPP_VEHICLE_CONTROL); - OSP_SESSION_ACQUIRE_TAGS(vehicleCtrl, rTags, TESTAPP_VEHICLE_CONTROL); - - rBuilder.tag(tgSelUsrCtrlReq).depend_on({tgSelUsrCtrlMod}); - - auto &rUserInput = top_get< input::UserInputHandler >(topData, idUserInput); - - // TODO: add cleanup task - top_emplace(topData, idVhControls, VehicleTestControls{ - .m_btnSwitch = rUserInput.button_subscribe("game_switch"), - .m_btnThrMax = rUserInput.button_subscribe("vehicle_thr_max"), - .m_btnThrMin = rUserInput.button_subscribe("vehicle_thr_min"), - .m_btnThrMore = rUserInput.button_subscribe("vehicle_thr_more"), - .m_btnThrLess = rUserInput.button_subscribe("vehicle_thr_less"), - .m_btnPitchUp = rUserInput.button_subscribe("vehicle_pitch_up"), - .m_btnPitchDn = rUserInput.button_subscribe("vehicle_pitch_dn"), - .m_btnYawLf = rUserInput.button_subscribe("vehicle_yaw_lf"), - .m_btnYawRt = rUserInput.button_subscribe("vehicle_yaw_rt"), - .m_btnRollLf = rUserInput.button_subscribe("vehicle_roll_lf"), - .m_btnRollRt = rUserInput.button_subscribe("vehicle_roll_rt") - }); + OSP_DECLARE_GET_DATA_IDS(parts, TESTAPP_DATA_PARTS); + auto const tgScn = scene.get_pipelines(); + auto const tgParts = parts.get_pipelines(); - auto const idNull = lgrn::id_null(); + Session out; + OSP_DECLARE_CREATE_DATA_IDS(out, topData, TESTAPP_DATA_SIGNALS_FLOAT); + auto const tgSgFlt = out.create_pipelines(rBuilder); - vehicleCtrl.task() = rBuilder.task().assign({tgInputEvt, tgSigFloatUpdMod}).data( - "Write inputs to UserControl Machines", - TopDataIds_t{ idScnParts, idSigValFloat, idSigUpdFloat, idUserInput, idVhControls, idDeltaTimeIn }, - wrap_args([] (ACtxParts& rScnParts, SignalValues_t& rSigValFloat, UpdateNodes& rSigUpdFloat, input::UserInputHandler const &rUserInput, VehicleTestControls &rVhControls, float const deltaTimeIn) noexcept - { - Nodes const &rFloatNodes = rScnParts.m_nodePerType[gc_ntSigFloat]; - PerMachType &rUsrCtrl = rScnParts.m_machines.m_perType[gc_mtUserCtrl]; + rBuilder.pipeline(tgSgFlt.sigFloatValues) .parent(tgScn.update); + rBuilder.pipeline(tgSgFlt.sigFloatUpdExtIn) .parent(tgScn.update); + rBuilder.pipeline(tgSgFlt.sigFloatUpdLoop) .parent(tgParts.linkLoop); - // Select a UsrCtrl machine when pressing the switch button - if (rUserInput.button_state(rVhControls.m_btnSwitch).m_triggered) - { - ++rVhControls.m_selectedUsrCtrl; - bool found = false; - for (MachLocalId local = rVhControls.m_selectedUsrCtrl; local < rUsrCtrl.m_localIds.capacity(); ++local) - { - if (rUsrCtrl.m_localIds.exists(local)) - { - found = true; - rVhControls.m_selectedUsrCtrl = local; - break; - } - } + top_emplace< SignalValues_t > (topData, idSigValFloat); + top_emplace< UpdateNodes > (topData, idSigUpdFloat); - if ( ! found ) - { - rVhControls.m_selectedUsrCtrl = lgrn::id_null(); - OSP_LOG_INFO("Unselected vehicles"); - } - else - { - OSP_LOG_INFO("Selected User Control: {}", rVhControls.m_selectedUsrCtrl); - } - } + // NOTE: Consider supporting per-thread UpdateNodes to allow multiple threads to write + // new float values in parallel. - if (rVhControls.m_selectedUsrCtrl == lgrn::id_null()) + rBuilder.task() + .name ("Update Signal Nodes") + .run_on ({tgParts.linkLoop(EStgLink::NodeUpd)}) + .sync_with ({tgSgFlt.sigFloatUpdExtIn(Ready), tgParts.machUpdExtIn(Ready), tgSgFlt.sigFloatUpdLoop(Modify), tgSgFlt.sigFloatValues(Modify)}) + .push_to (out.m_tasks) + .args ({ idSigUpdFloat, idSigValFloat, idUpdMach, idScnParts}) + .func([] (UpdateNodes& rSigUpdFloat, SignalValues_t& rSigValFloat, MachineUpdater& rUpdMach, ACtxParts const& rScnParts) noexcept + { + if ( ! rSigUpdFloat.dirty ) { - return; // No vehicle selected + return; // Not dirty, nothing to do } - // Control selected UsrCtrl machine - - float const thrRate = deltaTimeIn; - float const thrChange = - float(rUserInput.button_state(rVhControls.m_btnThrMore).m_held) * thrRate - - float(rUserInput.button_state(rVhControls.m_btnThrLess).m_held) * thrRate - + float(rUserInput.button_state(rVhControls.m_btnThrMax).m_triggered) - - float(rUserInput.button_state(rVhControls.m_btnThrMin).m_triggered); - - Vector3 const attitude - { - float(rUserInput.button_state(rVhControls.m_btnPitchDn).m_held) - - float(rUserInput.button_state(rVhControls.m_btnPitchUp).m_held), - float(rUserInput.button_state(rVhControls.m_btnYawLf).m_held) - - float(rUserInput.button_state(rVhControls.m_btnYawRt).m_held), - float(rUserInput.button_state(rVhControls.m_btnRollRt).m_held) - - float(rUserInput.button_state(rVhControls.m_btnRollLf).m_held) - }; - + Nodes const &rFloatNodes = rScnParts.nodePerType[gc_ntSigFloat]; + // NOTE: The various use of reset() clear entire bit arrays, which may or may + // not be expensive. They likely optimize to memset - MachAnyId const mach = rUsrCtrl.m_localToAny[rVhControls.m_selectedUsrCtrl]; - lgrn::Span const portSpan = rFloatNodes.m_machToNode[mach]; - - auto const write_control = [&rSigValFloat, &rSigUpdFloat, portSpan] (PortEntry const& entry, float write, bool replace = true, float min = 0.0f, float max = 1.0f) + for (std::size_t const machTypeDirty : rUpdMach.machTypesDirty.ones()) { - NodeId const node = connected_node(portSpan, entry.m_port); - if (node == lgrn::id_null()) - { - return; // not connected - } - - float const oldVal = rSigValFloat[node]; - float const newVal = replace ? write : Magnum::Math::clamp(oldVal + write, min, max); - - if (oldVal != newVal) - { - rSigUpdFloat.assign(node, newVal); - } - }; - - write_control(ports_userctrl::gc_throttleOut, thrChange, false); - write_control(ports_userctrl::gc_pitchOut, attitude.x()); - write_control(ports_userctrl::gc_yawOut, attitude.y()); - write_control(ports_userctrl::gc_rollOut, attitude.z()); - - })); + rUpdMach.localDirty[machTypeDirty].reset(); + } + rUpdMach.machTypesDirty.reset(); - return vehicleCtrl; -} + // Sees which nodes changed, and writes into rUpdMach set dirty which MACHINES + // must be updated next + update_signal_nodes( + rSigUpdFloat.nodeDirty.ones(), + rFloatNodes.nodeToMach, + rScnParts.machines, + arrayView(rSigUpdFloat.nodeNewValues), + rSigValFloat, + rUpdMach); + rSigUpdFloat.nodeDirty.reset(); + rSigUpdFloat.dirty = false; -Session setup_camera_vehicle( - TopTaskBuilder& rBuilder, - [[maybe_unused]] ArrayView const topData, - Session const& app, - Session const& commonScene, - Session const& parts, - Session const& physics, - Session const& camera, - Session const& vehicleControl) -{ - OSP_SESSION_UNPACK_DATA(commonScene, TESTAPP_COMMON_SCENE); - OSP_SESSION_UNPACK_DATA(parts, TESTAPP_PARTS); - OSP_SESSION_UNPACK_TAGS(physics, TESTAPP_PHYSICS); - OSP_SESSION_UNPACK_TAGS(app, TESTAPP_APP); - OSP_SESSION_UNPACK_DATA(camera, TESTAPP_CAMERA_CTRL); - OSP_SESSION_UNPACK_TAGS(camera, TESTAPP_CAMERA_CTRL); - OSP_SESSION_UNPACK_DATA(vehicleControl, TESTAPP_VEHICLE_CONTROL); - OSP_SESSION_UNPACK_TAGS(vehicleControl, TESTAPP_VEHICLE_CONTROL); - - Session cameraFree; - - cameraFree.task() = rBuilder.task().assign({tgInputEvt, tgSelUsrCtrlReq, tgPhysTransformReq, tgCamCtrlMod}).data( - "Update vehicle camera", - TopDataIds_t{ idCamCtrl, idDeltaTimeIn, idBasic, idVhControls, idScnParts }, - wrap_args([] (ACtxCameraController& rCamCtrl, float const deltaTimeIn, ACtxBasic const& rBasic, VehicleTestControls& rVhControls, ACtxParts const& rScnParts) noexcept - { - if (MachLocalId const selectedLocal = rVhControls.m_selectedUsrCtrl; - selectedLocal != lgrn::id_null()) + // Run tasks needed to update machine types that are dirty + bool anyMachineNotified = false; + for (MachTypeId const type : rUpdMach.machTypesDirty.ones()) { - // Follow selected UserControl machine - - // Obtain associated ActiveEnt - // MachLocalId -> MachAnyId -> PartId -> RigidGroup -> ActiveEnt - PerMachType const& rUsrCtrls = rScnParts.m_machines.m_perType.at(adera::gc_mtUserCtrl); - MachAnyId const selectedMach = rUsrCtrls.m_localToAny .at(selectedLocal); - PartId const selectedPart = rScnParts.m_machineToPart .at(selectedMach); - WeldId const weld = rScnParts.m_partToWeld .at(selectedPart); - ActiveEnt const selectedEnt = rScnParts.m_weldToEnt .at(weld); - - if (rBasic.m_transform.contains(selectedEnt)) - { - rCamCtrl.m_target = rBasic.m_transform.get(selectedEnt).m_transform.translation(); - } + anyMachineNotified = true; } - else + if (anyMachineNotified && rUpdMach.requestMachineUpdateLoop.load()) { - // Free cam when no vehicle selected - SysCameraController::update_move( - rCamCtrl, - deltaTimeIn, true); + rUpdMach.requestMachineUpdateLoop.store(true); } + }); - SysCameraController::update_view(rCamCtrl, deltaTimeIn); - })); - - return cameraFree; -} + return out; +} // setup_signals_float -#endif } // namespace testapp::scenes diff --git a/src/testapp/sessions/vehicles.h b/src/testapp/sessions/vehicles.h index aa3b8967..89caa031 100644 --- a/src/testapp/sessions/vehicles.h +++ b/src/testapp/sessions/vehicles.h @@ -26,8 +26,6 @@ #include "../scenarios.h" -namespace testapp { struct VehicleData; } - namespace testapp::scenes { @@ -37,8 +35,8 @@ namespace testapp::scenes osp::Session setup_parts( osp::TopTaskBuilder& rBuilder, osp::ArrayView topData, - osp::Session const& commonScene, - osp::TopDataId const idResources); + osp::Session const& application, + osp::Session const& scene); /** * @brief Float Signal Links, allowing Machines to pass floats to each other @@ -72,7 +70,7 @@ osp::Session setup_parts( osp::Session setup_signals_float( osp::TopTaskBuilder& rBuilder, osp::ArrayView topData, - osp::Session const& commonScene, + osp::Session const& scene, osp::Session const& parts); /** @@ -83,7 +81,7 @@ osp::Session setup_signals_float( osp::Session setup_mach_rocket( osp::TopTaskBuilder& rBuilder, osp::ArrayView topData, - osp::Session const& commonScene, + osp::Session const& scene, osp::Session const& parts, osp::Session const& signalsFloat); @@ -93,7 +91,7 @@ osp::Session setup_mach_rocket( osp::Session setup_mach_rcsdriver( osp::TopTaskBuilder& rBuilder, osp::ArrayView topData, - osp::Session const& commonScene, + osp::Session const& scene, osp::Session const& parts, osp::Session const& signalsFloat); @@ -106,7 +104,13 @@ osp::Session setup_mach_rcsdriver( osp::Session setup_vehicle_spawn( osp::TopTaskBuilder& rBuilder, osp::ArrayView topData, - osp::Session const& commonScene); + osp::Session const& scene); + +osp::Session setup_vehicle_spawn_draw( + osp::TopTaskBuilder& rBuilder, + osp::ArrayView topData, + osp::Session const& sceneRenderer, + osp::Session const& vehicleSpawn); /** * @brief Support VehicleBuilder data to be used to spawn vehicles @@ -114,61 +118,14 @@ osp::Session setup_vehicle_spawn( osp::Session setup_vehicle_spawn_vb( osp::TopTaskBuilder& rBuilder, osp::ArrayView topData, + osp::Session const& application, + osp::Session const& scene, osp::Session const& commonScene, osp::Session const& prefabs, osp::Session const& parts, osp::Session const& vehicleSpawn, - osp::Session const& signalsFloat, - osp::TopDataId const idResources); - -/** - * @brief Build "Test Vehicle" data, so they can be spawned - */ -osp::Session setup_test_vehicles( - osp::TopTaskBuilder& rBuilder, - osp::ArrayView topData, - osp::Session const& commonScene, - osp::TopDataId const idResources); - -/** - * @brief Controls to select and control a UserControl Machine - */ -osp::Session setup_vehicle_control( - osp::TopTaskBuilder& rBuilder, - osp::ArrayView topData, - osp::Session const& commonScene, - osp::Session const& parts, - osp::Session const& signalsFloat, - osp::Session const& app); - -/** - * @brief Camera which can free cam or follow a selected vehicle - */ -osp::Session setup_camera_vehicle( - osp::TopTaskBuilder& rBuilder, - osp::ArrayView topData, - osp::Session const& app, - osp::Session const& commonScene, - osp::Session const& parts, - osp::Session const& physics, - osp::Session const& camera, - osp::Session const& vehicleControl); + osp::Session const& signalsFloat); -/** - * @brief Red indicators over Magic Rockets - */ -osp::Session setup_thrust_indicators( - osp::TopTaskBuilder& rBuilder, - osp::ArrayView topData, - osp::Session const& magnum, - osp::Session const& commonScene, - osp::Session const& parts, - osp::Session const& signalsFloat, - osp::Session const& scnRender, - osp::Session const& cameraCtrl, - osp::Session const& shFlat, - osp::TopDataId const idResources, - osp::PkgId const pkg); } diff --git a/src/testapp/sessions/vehicles_machines.cpp b/src/testapp/sessions/vehicles_machines.cpp new file mode 100644 index 00000000..f378f29a --- /dev/null +++ b/src/testapp/sessions/vehicles_machines.cpp @@ -0,0 +1,601 @@ +/** + * Open Space Program + * Copyright © 2019-2022 Open Space Program Project + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "vehicles.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +using namespace adera; + +using namespace osp::active; +using namespace osp::draw; +using namespace osp::link; +using namespace osp; + +namespace testapp::scenes +{ + +template +TopTaskFunc_t gen_allocate_mach() +{ + static TopTaskFunc_t const func = wrap_args([] (ACtxParts& rScnParts, MachineUpdater& rUpdMach) noexcept + { + rUpdMach.localDirty[MachType_T].ints().resize(rScnParts.machines.perType[MachType_T].localIds.vec().capacity()); + }); + + return func; +} + +Session setup_mach_rocket( + TopTaskBuilder& rBuilder, + ArrayView const topData, + Session const& scene, + Session const& parts, + Session const& signalsFloat) +{ + OSP_DECLARE_GET_DATA_IDS(signalsFloat, TESTAPP_DATA_SIGNALS_FLOAT) + OSP_DECLARE_GET_DATA_IDS(parts, TESTAPP_DATA_PARTS); + auto const tgScn = scene .get_pipelines(); + auto const tgParts = parts .get_pipelines(); + + Session out; + + rBuilder.task() + .name ("Allocate Machine update bitset for MagicRocket") + .run_on ({tgScn.update(Run)}) + .sync_with ({tgParts.machIds(Ready), tgParts.machUpdExtIn(New)}) + .push_to (out.m_tasks) + .args ({idScnParts, idUpdMach}) + .func_raw (gen_allocate_mach()); + + return out; +} // setup_mach_rocket + + + + +struct ThrustIndicator +{ + MaterialId material; + Magnum::Color4 color; + MeshIdOwner_t mesh; + + KeyedVec rktToDrawEnt; + + float indicatorScale {0.0001f}; +}; + +Session setup_thrust_indicators( + TopTaskBuilder& rBuilder, + ArrayView const topData, + Session const& application, + Session const& windowApp, + Session const& commonScene, + Session const& parts, + Session const& signalsFloat, + Session const& sceneRenderer, + PkgId const pkg, + MaterialId const material) +{ + OSP_DECLARE_GET_DATA_IDS(application, TESTAPP_DATA_APPLICATION); + OSP_DECLARE_GET_DATA_IDS(commonScene, TESTAPP_DATA_COMMON_SCENE); + OSP_DECLARE_GET_DATA_IDS(parts, TESTAPP_DATA_PARTS); + OSP_DECLARE_GET_DATA_IDS(signalsFloat, TESTAPP_DATA_SIGNALS_FLOAT) + OSP_DECLARE_GET_DATA_IDS(sceneRenderer, TESTAPP_DATA_SCENE_RENDERER); + auto const tgWin = windowApp .get_pipelines(); + auto const tgScnRdr = sceneRenderer .get_pipelines(); + auto const tgParts = parts .get_pipelines(); + + auto &rResources = top_get< Resources > (topData, idResources); + auto &rBasic = top_get< ACtxBasic > (topData, idBasic); + auto &rDrawing = top_get< ACtxDrawing > (topData, idDrawing); + auto &rDrawingRes = top_get< ACtxDrawingRes > (topData, idDrawingRes); + auto &rScnRender = top_get< ACtxSceneRender >(topData, idScnRender); + auto &rDrawTfObservers = top_get< DrawTfObservers >(topData, idDrawTfObservers); + auto &rScnParts = top_get< ACtxParts > (topData, idScnParts); + auto &rSigValFloat = top_get< SignalValues_t > (topData, idSigValFloat); + + Session out; + auto const [idThrustIndicator] = out.acquire_data<1>(topData); + auto &rThrustIndicator = top_emplace(topData, idThrustIndicator); + + rThrustIndicator.material = material; + rThrustIndicator.color = { 1.0f, 0.2f, 0.8f, 1.0f }; + rThrustIndicator.mesh = SysRender::add_drawable_mesh(rDrawing, rDrawingRes, rResources, pkg, "cone"); + + rBuilder.task() + .name ("Create DrawEnts for Thrust indicators") + .run_on ({tgWin.sync(Run)}) + .sync_with ({tgScnRdr.drawEntResized(ModifyOrSignal), tgScnRdr.drawEnt(New), tgParts.machIds(Ready)}) + .push_to (out.m_tasks) + .args ({ idScnRender, idScnParts, idThrustIndicator}) + .func([] (ACtxSceneRender &rScnRender, ACtxParts const& rScnParts, ThrustIndicator& rThrustIndicator) noexcept + { + PerMachType const& rockets = rScnParts.machines.perType[gc_mtMagicRocket]; + + rThrustIndicator.rktToDrawEnt.resize(rockets.localIds.capacity()); + + for (MachLocalId const localId : rockets.localIds.bitview().zeros()) + { + DrawEnt& rDrawEnt = rThrustIndicator.rktToDrawEnt[localId]; + if (rDrawEnt == lgrn::id_null()) + { + rDrawEnt = rScnRender.m_drawIds.create(); + } + } + }); + + rBuilder.task() + .name ("Add mesh and materials to Thrust indicators") + .run_on ({tgWin.sync(Run)}) + .sync_with ({tgScnRdr.drawEntResized(Done), tgScnRdr.drawEnt(Ready), tgScnRdr.entMesh(New), tgScnRdr.material(New), tgScnRdr.materialDirty(Modify_), tgScnRdr.entMeshDirty(Modify_)}) + .push_to (out.m_tasks) + .args ({ idBasic, idScnRender, idDrawing, idDrawingRes, idScnParts, idSigValFloat, idThrustIndicator}) + .func([] (ACtxBasic& rBasic, ACtxSceneRender &rScnRender, ACtxDrawing& rDrawing, ACtxDrawingRes const& rDrawingRes, ACtxParts const& rScnParts, SignalValues_t const& rSigValFloat, ThrustIndicator& rThrustIndicator) noexcept + { + Material &rMat = rScnRender.m_materials[rThrustIndicator.material]; + PerMachType const &rockets = rScnParts.machines.perType[gc_mtMagicRocket]; + Nodes const &floats = rScnParts.nodePerType[gc_ntSigFloat]; + + for (MachLocalId const localId : rockets.localIds.bitview().zeros()) + { + DrawEnt const drawEnt = rThrustIndicator.rktToDrawEnt[localId]; + + MachAnyId const anyId = rockets.localToAny[localId]; + PartId const part = rScnParts.machineToPart[anyId]; + ActiveEnt const partEnt = rScnParts.partToActive[part]; + + auto const& portSpan = floats.machToNode[anyId]; + NodeId const throttleIn = connected_node(portSpan, ports_magicrocket::gc_throttleIn.port); + NodeId const multiplierIn = connected_node(portSpan, ports_magicrocket::gc_multiplierIn.port); + + float const throttle = std::clamp(rSigValFloat[throttleIn], 0.0f, 1.0f); + float const multiplier = rSigValFloat[multiplierIn]; + float const thrustMag = throttle * multiplier; + + if (thrustMag == 0.0f) + { + rScnRender.m_visible.reset(drawEnt.value); + continue; + } + + if (!rMat.m_ents.test(drawEnt.value)) + { + rMat.m_ents.set(drawEnt.value); + rMat.m_dirty.push_back(drawEnt); + } + + MeshIdOwner_t &rMeshOwner = rScnRender.m_mesh[drawEnt]; + if ( ! rMeshOwner.has_value() ) + { + rScnRender.m_mesh[drawEnt] = rDrawing.m_meshRefCounts.ref_add(rThrustIndicator.mesh.value()); + rScnRender.m_meshDirty.push_back(drawEnt); + } + + rScnRender.m_visible.set(drawEnt.value); + rScnRender.m_opaque .set(drawEnt.value); + + rScnRender.m_color [drawEnt] = rThrustIndicator.color; + rScnRender.drawTfObserverEnable [partEnt] = 1; + + SysRender::needs_draw_transforms(rBasic.m_scnGraph, rScnRender.m_needDrawTf, partEnt); + } + }); + + using UserData_t = DrawTfObservers::UserData_t; + + DrawTfObservers::Observer &rObserver = rDrawTfObservers.observers[0]; + + rObserver.data = { &rThrustIndicator, &rScnParts, &rSigValFloat }; + rObserver.func = [] (ACtxSceneRender& rCtxScnRdr, Matrix4 const& drawTf, active::ActiveEnt ent, int depth, UserData_t data) noexcept + { + auto &rThrustIndicator = *static_cast< ThrustIndicator* > (data[0]); + auto &rScnParts = *static_cast< ACtxParts* > (data[1]); + auto &rSigValFloat = *static_cast< SignalValues_t* > (data[2]); + + PerMachType const &rockets = rScnParts.machines.perType[gc_mtMagicRocket]; + Nodes const &floats = rScnParts.nodePerType[gc_ntSigFloat]; + + PartId const part = rScnParts.activeToPart[ent]; + ActiveEnt const partEnt = rScnParts.partToActive[part]; + + for (MachinePair const pair : rScnParts.partToMachines[part]) + if (pair.type == gc_mtMagicRocket) + { + DrawEnt const drawEnt = rThrustIndicator.rktToDrawEnt[pair.local]; + MachAnyId const anyId = rockets.localToAny[pair.local]; + + auto const& portSpan = floats.machToNode[anyId]; + NodeId const throttleIn = connected_node(portSpan, ports_magicrocket::gc_throttleIn.port); + NodeId const multiplierIn = connected_node(portSpan, ports_magicrocket::gc_multiplierIn.port); + + float const throttle = std::clamp(rSigValFloat[throttleIn], 0.0f, 1.0f); + float const multiplier = rSigValFloat[multiplierIn]; + float const thrustMag = throttle * multiplier; + + rCtxScnRdr.m_drawTransform[drawEnt] + = drawTf + * Matrix4::scaling({1.0f, 1.0f, thrustMag * rThrustIndicator.indicatorScale}) + * Matrix4::translation({0.0f, 0.0f, -1.0f}) + * Matrix4::scaling({0.2f, 0.2f, 1.0f}); + } + }; + + rBuilder.task() + .name ("Clean up ThrustIndicator") + .run_on ({tgWin.cleanup(Run_)}) + .push_to (out.m_tasks) + .args ({ idResources, idDrawing, idThrustIndicator}) + .func([] (Resources& rResources, ACtxDrawing& rDrawing, ThrustIndicator& rThrustIndicator) noexcept + { + rDrawing.m_meshRefCounts.ref_release(std::move(rThrustIndicator.mesh)); + }); + + return out; +} // setup_thrust_indicators + + + + +Session setup_mach_rcsdriver( + TopTaskBuilder& rBuilder, + ArrayView const topData, + Session const& scene, + Session const& parts, + Session const& signalsFloat) +{ + OSP_DECLARE_GET_DATA_IDS(signalsFloat, TESTAPP_DATA_SIGNALS_FLOAT) + OSP_DECLARE_GET_DATA_IDS(parts, TESTAPP_DATA_PARTS); + auto const tgScn = scene .get_pipelines(); + auto const tgParts = parts .get_pipelines(); + + Session out; + + rBuilder.task() + .name ("Allocate Machine update bitset for RcsDriver") + .run_on ({tgScn.update(Run)}) + .sync_with ({tgParts.machIds(Ready), tgParts.machUpdExtIn(New)}) + .push_to (out.m_tasks) + .args ({idScnParts, idUpdMach}) + .func_raw (gen_allocate_mach()); + + rBuilder.task() + .name ("RCS Drivers calculate new values") + .run_on ({tgParts.linkLoop(MachUpd)}) + .sync_with ({tgParts.machUpdExtIn(Ready)}) + .push_to (out.m_tasks) + .args ({ idScnParts, idUpdMach, idSigValFloat, idSigUpdFloat}) + .func([] (ACtxParts& rScnParts, MachineUpdater& rUpdMach, SignalValues_t& rSigValFloat, UpdateNodes& rSigUpdFloat) noexcept + { + Nodes const &rFloatNodes = rScnParts.nodePerType[gc_ntSigFloat]; + PerMachType &rRockets = rScnParts.machines.perType[gc_mtRcsDriver]; + + for (MachLocalId const local : rUpdMach.localDirty[gc_mtRcsDriver].ones()) + { + MachAnyId const mach = rRockets.localToAny[local]; + auto const portSpan = lgrn::Span{rFloatNodes.machToNode[mach]}; + + NodeId const thrNode = connected_node(portSpan, ports_rcsdriver::gc_throttleOut.port); + if (thrNode == lgrn::id_null()) + { + continue; // Throttle Output not connected, calculations below are useless + } + + auto const rcs_read = [&rSigValFloat, portSpan] (float& rDstVar, PortEntry const& entry) + { + NodeId const node = connected_node(portSpan, entry.port); + + if (node != lgrn::id_null()) + { + rDstVar = rSigValFloat[node]; + } + }; + + Vector3 pos {0.0f}; + Vector3 dir {0.0f}; + Vector3 cmdLin {0.0f}; + Vector3 cmdAng {0.0f}; + + rcs_read( pos.x(), ports_rcsdriver::gc_posXIn ); + rcs_read( pos.y(), ports_rcsdriver::gc_posYIn ); + rcs_read( pos.z(), ports_rcsdriver::gc_posZIn ); + rcs_read( dir.x(), ports_rcsdriver::gc_dirXIn ); + rcs_read( dir.y(), ports_rcsdriver::gc_dirYIn ); + rcs_read( dir.z(), ports_rcsdriver::gc_dirZIn ); + rcs_read( cmdLin.x(), ports_rcsdriver::gc_cmdLinXIn ); + rcs_read( cmdLin.y(), ports_rcsdriver::gc_cmdLinYIn ); + rcs_read( cmdLin.z(), ports_rcsdriver::gc_cmdLinZIn ); + rcs_read( cmdAng.x(), ports_rcsdriver::gc_cmdAngXIn ); + rcs_read( cmdAng.y(), ports_rcsdriver::gc_cmdAngYIn ); + rcs_read( cmdAng.z(), ports_rcsdriver::gc_cmdAngZIn ); + + OSP_LOG_TRACE("RCS controller {} pitch = {}", local, cmdAng.x()); + OSP_LOG_TRACE("RCS controller {} yaw = {}", local, cmdAng.y()); + OSP_LOG_TRACE("RCS controller {} roll = {}", local, cmdAng.z()); + + float const thrCurr = rSigValFloat[thrNode]; + float const thrNew = thruster_influence(pos, dir, cmdLin, cmdAng); + + if (thrCurr != thrNew) + { + rSigUpdFloat.assign(thrNode, thrNew); + rUpdMach.requestMachineUpdateLoop = true; + } + } + }); + + return out; +} // setup_mach_rcsdriver + + + + +struct VehicleControls +{ + MachLocalId selectedUsrCtrl{lgrn::id_null()}; + + input::EButtonControlIndex btnSwitch; + input::EButtonControlIndex btnThrMax; + input::EButtonControlIndex btnThrMin; + input::EButtonControlIndex btnThrMore; + input::EButtonControlIndex btnThrLess; + input::EButtonControlIndex btnPitchUp; + input::EButtonControlIndex btnPitchDn; + input::EButtonControlIndex btnYawLf; + input::EButtonControlIndex btnYawRt; + input::EButtonControlIndex btnRollLf; + input::EButtonControlIndex btnRollRt; +}; + +Session setup_vehicle_control( + TopTaskBuilder& rBuilder, + ArrayView const topData, + Session const& windowApp, + Session const& scene, + Session const& parts, + Session const& signalsFloat) +{ + OSP_DECLARE_GET_DATA_IDS(scene, TESTAPP_DATA_SCENE); + OSP_DECLARE_GET_DATA_IDS(parts, TESTAPP_DATA_PARTS); + OSP_DECLARE_GET_DATA_IDS(windowApp, TESTAPP_DATA_WINDOW_APP); + OSP_DECLARE_GET_DATA_IDS(signalsFloat, TESTAPP_DATA_SIGNALS_FLOAT); + auto const tgWin = windowApp .get_pipelines(); + auto const tgScn = scene .get_pipelines(); + auto const tgSgFlt = signalsFloat .get_pipelines(); + + Session out; + OSP_DECLARE_CREATE_DATA_IDS(out, topData, TESTAPP_DATA_VEHICLE_CONTROL); + auto const tgVhCtrl = out.create_pipelines(rBuilder); + + rBuilder.pipeline(tgVhCtrl.selectedVehicle).parent(tgScn.update); + + auto &rUserInput = top_get< input::UserInputHandler >(topData, idUserInput); + + // TODO: add cleanup task + top_emplace(topData, idVhControls, VehicleControls{ + .btnSwitch = rUserInput.button_subscribe("game_switch"), + .btnThrMax = rUserInput.button_subscribe("vehicle_thr_max"), + .btnThrMin = rUserInput.button_subscribe("vehicle_thr_min"), + .btnThrMore = rUserInput.button_subscribe("vehicle_thr_more"), + .btnThrLess = rUserInput.button_subscribe("vehicle_thr_less"), + .btnPitchUp = rUserInput.button_subscribe("vehicle_pitch_up"), + .btnPitchDn = rUserInput.button_subscribe("vehicle_pitch_dn"), + .btnYawLf = rUserInput.button_subscribe("vehicle_yaw_lf"), + .btnYawRt = rUserInput.button_subscribe("vehicle_yaw_rt"), + .btnRollLf = rUserInput.button_subscribe("vehicle_roll_lf"), + .btnRollRt = rUserInput.button_subscribe("vehicle_roll_rt") + }); + + rBuilder.task() + .name ("Select vehicle") + .run_on ({tgWin.inputs(Run)}) + .sync_with ({tgVhCtrl.selectedVehicle(Modify)}) + .push_to (out.m_tasks) + .args ({ idScnParts, idUserInput, idVhControls}) + .func([] (ACtxParts& rScnParts, input::UserInputHandler const &rUserInput, VehicleControls &rVhControls) noexcept + { + PerMachType &rUsrCtrl = rScnParts.machines.perType[gc_mtUserCtrl]; + + // Select a UsrCtrl machine when pressing the switch button + if (rUserInput.button_state(rVhControls.btnSwitch).m_triggered) + { + ++rVhControls.selectedUsrCtrl; + bool found = false; + for (MachLocalId local = rVhControls.selectedUsrCtrl; local < rUsrCtrl.localIds.capacity(); ++local) + { + if (rUsrCtrl.localIds.exists(local)) + { + found = true; + rVhControls.selectedUsrCtrl = local; + break; + } + } + + if ( ! found ) + { + rVhControls.selectedUsrCtrl = lgrn::id_null(); + OSP_LOG_INFO("Unselected vehicles"); + } + else + { + OSP_LOG_INFO("Selected User Control: {}", rVhControls.selectedUsrCtrl); + } + } + }); + + rBuilder.task() + .name ("Write inputs to UserControl Machines") + .run_on ({tgScn.update(Run)}) + .sync_with ({tgWin.inputs(Run), tgSgFlt.sigFloatUpdExtIn(Modify)}) + .push_to (out.m_tasks) + .args ({ idScnParts, idUpdMach, idSigValFloat, idSigUpdFloat, idUserInput, idVhControls, idDeltaTimeIn}) + .func([] (ACtxParts& rScnParts, MachineUpdater& rUpdMach, SignalValues_t& rSigValFloat, UpdateNodes& rSigUpdFloat, input::UserInputHandler const& rUserInput, VehicleControls& rVhControls, float const deltaTimeIn) noexcept + { + VehicleControls& rVC = rVhControls; + auto const held = [&rUserInput] (input::EButtonControlIndex idx, float val) -> float + { + return rUserInput.button_state(idx).m_held ? val : 0.0f; + }; + + if (rVC.selectedUsrCtrl == lgrn::id_null()) + { + return; // No vehicle selected + } + + Nodes const &rFloatNodes = rScnParts.nodePerType[gc_ntSigFloat]; + float const thrRate = deltaTimeIn; + + float const thrChange + = held(rVC.btnThrMore, thrRate) - held(rVC.btnThrLess, thrRate) + + held(rVC.btnThrMax, 1.0f) - held(rVC.btnThrMin, 1.0f); + + Vector3 const attitude + { + held(rVC.btnPitchDn, 1.0f) - held(rVC.btnPitchUp, 1.0f), + held(rVC.btnYawLf, 1.0f) - held(rVC.btnYawRt, 1.0f), + held(rVC.btnRollRt, 1.0f) - held(rVC.btnRollLf, 1.0f) + }; + + PerMachType &rUsrCtrl = rScnParts.machines.perType[gc_mtUserCtrl]; + MachAnyId const mach = rUsrCtrl.localToAny[rVC.selectedUsrCtrl]; + auto const portSpan = lgrn::Span{rFloatNodes.machToNode[mach]}; + + bool changed = false; + auto const write_control = [&rSigValFloat, &rSigUpdFloat, &changed, portSpan] (PortEntry const& entry, float write, bool replace = true, float min = 0.0f, float max = 1.0f) + { + NodeId const node = connected_node(portSpan, entry.port); + if (node == lgrn::id_null()) + { + return; // not connected + } + + float const oldVal = rSigValFloat[node]; + float const newVal = replace ? write : Magnum::Math::clamp(oldVal + write, min, max); + + if (oldVal != newVal) + { + rSigUpdFloat.assign(node, newVal); + changed = true; + } + }; + + write_control(ports_userctrl::gc_throttleOut, thrChange, false); + write_control(ports_userctrl::gc_pitchOut, attitude.x()); + write_control(ports_userctrl::gc_yawOut, attitude.y()); + write_control(ports_userctrl::gc_rollOut, attitude.z()); + + if (changed) + { + rUpdMach.requestMachineUpdateLoop = true; + } + }); + + return out; +} // setup_vehicle_control + + + +Session setup_camera_vehicle( + TopTaskBuilder& rBuilder, + [[maybe_unused]] ArrayView const topData, + Session const& windowApp, + Session const& scene, + Session const& sceneRenderer, + Session const& commonScene, + Session const& physics, + Session const& parts, + Session const& cameraCtrl, + Session const& vehicleCtrl) +{ + OSP_DECLARE_GET_DATA_IDS(scene, TESTAPP_DATA_SCENE); + OSP_DECLARE_GET_DATA_IDS(commonScene, TESTAPP_DATA_COMMON_SCENE); + OSP_DECLARE_GET_DATA_IDS(parts, TESTAPP_DATA_PARTS); + OSP_DECLARE_GET_DATA_IDS(cameraCtrl, TESTAPP_DATA_CAMERA_CTRL); + OSP_DECLARE_GET_DATA_IDS(vehicleCtrl, TESTAPP_DATA_VEHICLE_CONTROL); + + auto const tgWin = windowApp .get_pipelines(); + auto const tgCmCt = cameraCtrl .get_pipelines(); + auto const tgPhys = physics .get_pipelines(); + auto const tgParts = parts .get_pipelines(); + + Session out; + + // Don't add tgCS.transform(Modify) to sync_with, even though this uses transforms. + // tgPhys.physUpdate(Done) assures physics transforms are done. + // + // tgCmCt.camCtrl(Ready) is needed by the shape thrower, which needs tgCS.transform(New), + // causing a circular dependency. The transform pipeline probably needs to be split into + // a few separate ones. + rBuilder.task() + .name ("Update vehicle camera") + .run_on ({tgWin.sync(Run)}) + .sync_with ({tgCmCt.camCtrl(Modify), tgPhys.physUpdate(Done), tgParts.mapWeldActive(Ready)}) + .push_to (out.m_tasks) + .args ({ idCamCtrl, idDeltaTimeIn, idBasic, idVhControls, idScnParts}) + .func([] (ACtxCameraController& rCamCtrl, float const deltaTimeIn, ACtxBasic const& rBasic, VehicleControls& rVhControls, ACtxParts const& rScnParts) noexcept + { + if (rVhControls.selectedUsrCtrl != lgrn::id_null()) + { + // Follow selected UserControl machine + + // Obtain associated ActiveEnt + // MachLocalId -> MachAnyId -> PartId -> RigidGroup -> ActiveEnt + PerMachType const& rUsrCtrls = rScnParts.machines.perType.at(adera::gc_mtUserCtrl); + MachAnyId const selectedMach = rUsrCtrls.localToAny .at(rVhControls.selectedUsrCtrl); + PartId const selectedPart = rScnParts.machineToPart .at(selectedMach); + WeldId const weld = rScnParts.partToWeld .at(selectedPart); + ActiveEnt const selectedEnt = rScnParts.weldToActive .at(weld); + + if (rBasic.m_transform.contains(selectedEnt)) + { + rCamCtrl.m_target = rBasic.m_transform.get(selectedEnt).m_transform.translation(); + } + } + else + { + // Free cam when no vehicle selected + SysCameraController::update_move( + rCamCtrl, + deltaTimeIn, true); + } + + SysCameraController::update_view(rCamCtrl, deltaTimeIn); + }); + + return out; +} // setup_camera_vehicle + + + +} // namespace testapp::scenes diff --git a/src/testapp/sessions/vehicles_machines.h b/src/testapp/sessions/vehicles_machines.h new file mode 100644 index 00000000..95139245 --- /dev/null +++ b/src/testapp/sessions/vehicles_machines.h @@ -0,0 +1,75 @@ +/** + * Open Space Program + * Copyright © 2019-2023 Open Space Program Project + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#pragma once + +#include "../scenarios.h" + +#include + +namespace testapp::scenes +{ + +/** + * @brief Controls to select and control a UserControl Machine + */ +osp::Session setup_vehicle_control( + osp::TopTaskBuilder& rBuilder, + osp::ArrayView topData, + osp::Session const& windowApp, + osp::Session const& scene, + osp::Session const& parts, + osp::Session const& signalsFloat); + +/** + * @brief Camera which can free cam or follow a selected vehicle + */ +osp::Session setup_camera_vehicle( + osp::TopTaskBuilder& rBuilder, + osp::ArrayView topData, + osp::Session const& windowApp, + osp::Session const& scene, + osp::Session const& sceneRenderer, + osp::Session const& commonScene, + osp::Session const& physics, + osp::Session const& parts, + osp::Session const& cameraCtrl, + osp::Session const& vehicleCtrl); + +/** + * @brief Red indicators over Magic Rockets + */ +osp::Session setup_thrust_indicators( + osp::TopTaskBuilder& rBuilder, + osp::ArrayView topData, + osp::Session const& application, + osp::Session const& windowApp, + osp::Session const& commonSecne, + osp::Session const& parts, + osp::Session const& signalsFloat, + osp::Session const& sceneRenderer, + osp::PkgId const pkg, + osp::draw::MaterialId material); + +} // namespace testapp::scenes diff --git a/src/testapp/sessions/vehicles_prebuilt.cpp b/src/testapp/sessions/vehicles_prebuilt.cpp new file mode 100644 index 00000000..1d8ecc57 --- /dev/null +++ b/src/testapp/sessions/vehicles_prebuilt.cpp @@ -0,0 +1,230 @@ +/** + * Open Space Program + * Copyright © 2019-2023 Open Space Program Project + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "vehicles_prebuilt.h" + +#include + +#include + +using namespace adera; + +using namespace osp::link; +using namespace osp; + +using osp::restypes::gc_importer; + +using namespace Magnum::Math::Literals; + +namespace testapp::scenes +{ + + +Matrix4 quick_transform(Vector3 const pos, Quaternion const rot) noexcept +{ + return Matrix4::from(rot.toMatrix(), pos); +} + +struct RCSInputs +{ + NodeId m_pitch {lgrn::id_null()}; + NodeId m_yaw {lgrn::id_null()}; + NodeId m_roll {lgrn::id_null()}; +}; + +static void add_rcs_machines(VehicleBuilder& rBuilder, RCSInputs const& inputs, PartId part, float thrustMul, Matrix4 const& tf) +{ + namespace ports_rcsdriver = adera::ports_rcsdriver; + namespace ports_magicrocket = adera::ports_magicrocket; + + auto const [posX, posY, posZ, dirX, dirY, dirZ, driverOut, thrMul] = rBuilder.create_nodes<8>(gc_ntSigFloat); + + rBuilder.create_machine(part, gc_mtRcsDriver, { + { ports_rcsdriver::gc_posXIn, posX }, + { ports_rcsdriver::gc_posYIn, posY }, + { ports_rcsdriver::gc_posZIn, posZ }, + { ports_rcsdriver::gc_dirXIn, dirX }, + { ports_rcsdriver::gc_dirYIn, dirY }, + { ports_rcsdriver::gc_dirZIn, dirZ }, + { ports_rcsdriver::gc_cmdAngXIn, inputs.m_pitch }, + { ports_rcsdriver::gc_cmdAngYIn, inputs.m_yaw }, + { ports_rcsdriver::gc_cmdAngZIn, inputs.m_roll }, + { ports_rcsdriver::gc_throttleOut, driverOut } + } ); + + rBuilder.create_machine(part, gc_mtMagicRocket, { + { ports_magicrocket::gc_throttleIn, driverOut }, + { ports_magicrocket::gc_multiplierIn, thrMul } + } ); + + Vector3 const dir = tf.rotation() * gc_rocketForward; + + auto &rFloatValues = rBuilder.node_values< SignalValues_t >(gc_ntSigFloat); + + rFloatValues[posX] = tf.translation().x(); + rFloatValues[posY] = tf.translation().y(); + rFloatValues[posZ] = tf.translation().z(); + rFloatValues[dirX] = dir.x(); + rFloatValues[dirY] = dir.y(); + rFloatValues[dirZ] = dir.z(); + rFloatValues[thrMul] = thrustMul; +} + +static void add_rcs_block(VehicleBuilder& rBuilder, VehicleBuilder::WeldVec_t& rWeldTo, RCSInputs const& inputs, float thrustMul, Vector3 pos, Quaternion rot) +{ + constexpr Vector3 xAxis{1.0f, 0.0f, 0.0f}; + + auto const [ nozzleA, nozzleB ] = rBuilder.create_parts<2>(); + rBuilder.set_prefabs({ + { nozzleA, "phLinRCS" }, + { nozzleB, "phLinRCS" } + }); + + Matrix4 const nozzleTfA = quick_transform(pos, rot * Quaternion::rotation(90.0_degf, xAxis)); + Matrix4 const nozzleTfB = quick_transform(pos, rot * Quaternion::rotation(-90.0_degf, xAxis)); + + add_rcs_machines(rBuilder, inputs, nozzleA, thrustMul, nozzleTfA); + add_rcs_machines(rBuilder, inputs, nozzleB, thrustMul, nozzleTfB); + + rWeldTo.push_back({ nozzleA, nozzleTfA }); + rWeldTo.push_back({ nozzleB, nozzleTfB }); +} + +Session setup_prebuilt_vehicles( + TopTaskBuilder& rBuilder, + ArrayView const topData, + Session const& application, + Session const& scene) +{ + OSP_DECLARE_GET_DATA_IDS(application, TESTAPP_DATA_APPLICATION); + auto const tgScn = scene.get_pipelines(); + + auto &rResources = top_get(topData, idResources); + + Session out; + OSP_DECLARE_CREATE_DATA_IDS(out, topData, TESTAPP_DATA_TEST_VEHICLES); + out.m_cleanup = tgScn.cleanup; + + auto &rPrebuiltVehicles = top_emplace(topData, idPrebuiltVehicles); + rPrebuiltVehicles.resize(PrebuiltVhIdReg_t::size()); + + using namespace adera; + + // Build "PartVehicle" + { + VehicleBuilder vbuilder{&rResources}; + VehicleBuilder::WeldVec_t toWeld; + + auto const [ capsule, fueltank, engineA, engineB ] = vbuilder.create_parts<4>(); + vbuilder.set_prefabs({ + { capsule, "phCapsule" }, + { fueltank, "phFuselage" }, + { engineA, "phEngine" }, + { engineB, "phEngine" }, + }); + + toWeld.push_back( {capsule, quick_transform({ 0.0f, 0.0f, 3.0f}, {})} ); + toWeld.push_back( {fueltank, quick_transform({ 0.0f, 0.0f, 0.0f}, {})} ); + toWeld.push_back( {engineA, quick_transform({ 0.7f, 0.0f, -2.9f}, {})} ); + toWeld.push_back( {engineB, quick_transform({-0.7f, 0.0f, -2.9f}, {})} ); + + namespace ports_magicrocket = adera::ports_magicrocket; + namespace ports_userctrl = adera::ports_userctrl; + + auto const [ pitch, yaw, roll, throttle, thrustMul ] = vbuilder.create_nodes<5>(gc_ntSigFloat); + + auto &rFloatValues = vbuilder.node_values< SignalValues_t >(gc_ntSigFloat); + rFloatValues[thrustMul] = 50000.0f; + + vbuilder.create_machine(capsule, gc_mtUserCtrl, { + { ports_userctrl::gc_throttleOut, throttle }, + { ports_userctrl::gc_pitchOut, pitch }, + { ports_userctrl::gc_yawOut, yaw }, + { ports_userctrl::gc_rollOut, roll } + } ); + + vbuilder.create_machine(engineA, gc_mtMagicRocket, { + { ports_magicrocket::gc_throttleIn, throttle }, + { ports_magicrocket::gc_multiplierIn, thrustMul } + } ); + + vbuilder.create_machine(engineB, gc_mtMagicRocket, { + { ports_magicrocket::gc_throttleIn, throttle }, + { ports_magicrocket::gc_multiplierIn, thrustMul } + } ); + + RCSInputs rcsInputs{pitch, yaw, roll}; + + int const rcsRingBlocks = 4; + int const rcsRingCount = 2; + float const rcsRingZ = -2.0f; + float const rcsZStep = 4.0f; + float const rcsRadius = 1.1f; + float const rcsThrust = 3000.0f; + + for (int ring = 0; ring < rcsRingCount; ++ring) + { + Vector3 const rcsOset{rcsRadius, 0.0f, rcsRingZ + ring*rcsZStep }; + + for (Rad ang = 0.0_degf; ang < Rad(360.0_degf); ang += Rad(360.0_degf)/rcsRingBlocks) + { + Quaternion const rotZ = Quaternion::rotation(ang, {0.0f, 0.0f, 1.0f}); + add_rcs_block(vbuilder, toWeld, rcsInputs, rcsThrust, rotZ.transformVector(rcsOset), rotZ); + } + } + + vbuilder.weld(toWeld); + + rPrebuiltVehicles[gc_pbvSimpleCommandServiceModule] = std::make_unique(std::move(vbuilder.finalize_release())); + } + + + // Put more prebuilt vehicles here! + + + rBuilder.task() + .name ("Clean up prebuilt vehicles") + .run_on ({tgScn.cleanup(Run_)}) + .push_to (out.m_tasks) + .args ({ idPrebuiltVehicles, idResources}) + .func([] (PrebuiltVehicles &rPrebuildVehicles, Resources& rResources) noexcept + { + for (std::unique_ptr &rpData : rPrebuildVehicles) + { + if (rpData != nullptr) + { + for (PrefabPair &rPrefabPair : rpData->m_partPrefabs) + { + rResources.owner_destroy(gc_importer, std::move(rPrefabPair.m_importer)); + } + } + } + rPrebuildVehicles.clear(); + }); + + return out; +} // setup_prebuilt_vehicles + + +} // namespace testapp::scenes diff --git a/src/testapp/sessions/vehicles_prebuilt.h b/src/testapp/sessions/vehicles_prebuilt.h new file mode 100644 index 00000000..4f2dabea --- /dev/null +++ b/src/testapp/sessions/vehicles_prebuilt.h @@ -0,0 +1,59 @@ +/** + * Open Space Program + * Copyright © 2019-2023 Open Space Program Project + * + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#pragma once + +#include "../scenarios.h" + +#include + +#include +#include +#include +#include + +#include + +namespace testapp::scenes +{ + +using PrebuiltVhId = osp::StrongId; +using PrebuiltVhIdReg_t = osp::GlobalIdReg; + +struct PrebuiltVehicles : osp::KeyedVec< PrebuiltVhId, std::unique_ptr > +{ + PrebuiltVehicles() = default; + OSP_MOVE_ONLY_CTOR_ASSIGN(PrebuiltVehicles); +}; + +inline PrebuiltVhId const gc_pbvSimpleCommandServiceModule = PrebuiltVhIdReg_t::create(); + +osp::Session setup_prebuilt_vehicles( + osp::TopTaskBuilder& rBuilder, + osp::ArrayView topData, + osp::Session const& application, + osp::Session const& scene); + + +} // namespace testapp::scenes