From 4149a7fe8e7bc7e96378ed7d54057d5359373f24 Mon Sep 17 00:00:00 2001 From: Josef Date: Fri, 11 Oct 2024 12:38:13 +0200 Subject: [PATCH 01/31] Bundle Brick in build time [ci skip] --- .../AGXDynamicsLibrary.Build.cs | 114 ++++++------------ 1 file changed, 39 insertions(+), 75 deletions(-) diff --git a/Source/ThirdParty/AGXDynamicsLibrary/AGXDynamicsLibrary.Build.cs b/Source/ThirdParty/AGXDynamicsLibrary/AGXDynamicsLibrary.Build.cs index 407ce4e53..b689cc569 100644 --- a/Source/ThirdParty/AGXDynamicsLibrary/AGXDynamicsLibrary.Build.cs +++ b/Source/ThirdParty/AGXDynamicsLibrary/AGXDynamicsLibrary.Build.cs @@ -56,6 +56,9 @@ private enum LibSource /// libraries. AGX, + /// Brick libraries location. + Brick, + /// The configuration part of AGX Dynamics. Contains generated header /// files for things such as version and CMake settings. Does not /// contain any libraries or runtime files. @@ -229,11 +232,11 @@ public AGXDynamicsLibrary(ReadOnlyTargetRules Target) : base(Target) RuntimeLibFiles.Add("tbb", LibSource.TerrainDependencies); } - // List of link-time libraries from AGX Dynamics and its dependencies - // that we need. These will be added to the Unreal Engine - // PublicAdditionalLibraries list. See - // https://docs.unrealengine.com/en-US/ProductionPipelines/BuildTools/UnrealBuildTool/ModuleFiles/index.html - Dictionary LinkLibFiles = new Dictionary(); + // List of link-time libraries from AGX Dynamics and its dependencies + // that we need. These will be added to the Unreal Engine + // PublicAdditionalLibraries list. See + // https://docs.unrealengine.com/en-US/ProductionPipelines/BuildTools/UnrealBuildTool/ModuleFiles/index.html + Dictionary LinkLibFiles = new Dictionary(); LinkLibFiles.Add("agxPhysics", LibSource.AGX); LinkLibFiles.Add("agxCore", LibSource.AGX); LinkLibFiles.Add("agxHydraulics", LibSource.AGX); @@ -245,11 +248,15 @@ public AGXDynamicsLibrary(ReadOnlyTargetRules Target) : base(Target) LinkLibFiles.Add("agxROS2", LibSource.AGX); LinkLibFiles.Add("agx-nt-ros2", LibSource.AGX); - // List of the include directories from aGX Dynamics and its - // dependenciesthat we need. These will be added to the Unreal Engine - // PublicIncludePaths. - List IncludePaths = new List(); + // Brick + LinkLibFiles.Add("brick.agx", LibSource.Brick); + + // List of the include directories from aGX Dynamics and its + // dependenciesthat we need. These will be added to the Unreal Engine + // PublicIncludePaths. + List IncludePaths = new List(); IncludePaths.Add(LibSource.AGX); + IncludePaths.Add(LibSource.Brick); // OS specific dependencies. if (Target.Platform == UnrealTargetPlatform.Linux) @@ -273,7 +280,7 @@ public AGXDynamicsLibrary(ReadOnlyTargetRules Target) : base(Target) } RuntimeLibFiles.Add("zlib", LibSource.Dependencies); - RuntimeLibFiles.Add("libpng", LibSource.Dependencies); + RuntimeLibFiles.Add("libpng*", LibSource.Dependencies); if (TargetAGXVersion.IsOlderThan(2, 31, 0, 0)) { RuntimeLibFiles.Add("glew", LibSource.Dependencies); @@ -333,6 +340,7 @@ public AGXDynamicsLibrary(ReadOnlyTargetRules Target) : base(Target) RuntimeDependencies.Add(Path.Combine(BundledAGXResourcesPath, "plugins", "*")); RuntimeDependencies.Add(Path.Combine(BundledAGXResourcesPath, "include", "*")); RuntimeDependencies.Add(Path.Combine(BundledAGXResourcesPath, "lib", "*")); + RuntimeDependencies.Add(Path.Combine(BundledAGXResourcesPath, "brickbundles", "*")); SetLicenseForCopySafe(Target); // This is a work-around for Linux which ensures that the .so files are @@ -505,7 +513,6 @@ private bool IsAGXResourcesBundled() return Directory.Exists(GetBundledAGXResourcesPath()); } - private void BundleAGXResources(ReadOnlyTargetRules Target, Dictionary RuntimeLibFiles, Dictionary LinkLibFiles, List IncludePaths) { @@ -578,71 +585,17 @@ private void BundleAGXResources(ReadOnlyTargetRules Target, Dictionary HeaderFileDirs = new List - { - "agx", - "agxCable", - "agxCollide", - "agxControl", - "agxData", - "agxDriveTrain", - "agxHydraulics", - "agxIO", - "agxModel", - "agxNet", - "agxPlot", - "agxPowerLine", - "agxRender", - "agxSabre", - "agxSDK", - "agxStream", - "agxTerrain", - "agxUtil", - "agxVehicle", - "agxWire", - "agxROS2", - "agx-nt-ros2", - Path.Combine("external", "hedley"), - Path.Combine("external", "json"), - Path.Combine("external", "pystring") - }; - - if (InstalledAGXResources.GetAGXVersion().IsNewerOrEqualTo(2, 32, 0, 0)) - { - HeaderFileDirs.Add(Path.Combine("external", "GIMPACT")); - } - - // Single header files to include. - List HeaderFiles = new List - { - "HashImplementationSwitcher.h" - }; - - foreach (var Dir in HeaderFileDirs) - { - if (!CopyDirectoryRecursively(Path.Combine(Source, Dir), Path.Combine(Dest, Dir))) - { - CleanBundledAGXDynamicsResources(); - return; - } - } - - foreach (var File in HeaderFiles) - { - if (!CopyFile(Path.Combine(Source, File), Path.Combine(Dest, File))) - { - CleanBundledAGXDynamicsResources(); - return; - } - } - } + if (!CopyDirectoryRecursively(Source, Dest)) + { + CleanBundledAGXDynamicsResources(); + return; + } + } // Copy AGX Dynamics cfg directory. { @@ -1482,6 +1435,7 @@ private void InitializeLinuxBundledAGX(string BundledAGXResourcesPath) private void InitializeWindowsInstalledAGX() { string BaseDir = Environment.GetEnvironmentVariable("AGX_DIR"); + string BrickDir = Path.GetFullPath(Path.Combine(BaseDir, "..", "rebrick")); string PluginDir = Environment.GetEnvironmentVariable("AGX_PLUGIN_PATH"); string DataDir = Environment.GetEnvironmentVariable("AGX_DATA_DIR"); @@ -1492,7 +1446,12 @@ private void InitializeWindowsInstalledAGX() Path.Combine(BaseDir, "lib", "x64"), Path.Combine(BaseDir, "bin", "x64") )); - LibSources.Add(LibSource.Config, new LibSourceInfo( + LibSources.Add(LibSource.Brick, new LibSourceInfo( + Path.Combine(BrickDir, "include"), + Path.Combine(BrickDir, "lib"), + Path.Combine(BrickDir, "brickbundles") + )); + LibSources.Add(LibSource.Config, new LibSourceInfo( null, null, null )); LibSources.Add(LibSource.Components, new LibSourceInfo( @@ -1535,14 +1494,19 @@ private void InitializeWindowsBundledAGX(string BundledAGXResourcesPath) { string BaseDir = BundledAGXResourcesPath; - LicenseTextPath = Path.Combine(BaseDir, "LICENSE.TXT"); + LicenseTextPath = Path.Combine(BaseDir, "LICENSE.TXT"); LibSources.Add(LibSource.AGX, new LibSourceInfo( Path.Combine(BaseDir, "include"), Path.Combine(BaseDir, "lib", "Win64"), Path.Combine(BaseDir, "bin", "Win64") )); - LibSources.Add(LibSource.Config, new LibSourceInfo( + LibSources.Add(LibSource.Brick, new LibSourceInfo( + Path.Combine(BaseDir, "include"), + Path.Combine(BaseDir, "lib", "Win64"), + Path.Combine(BaseDir, "brickbundles") + )); + LibSources.Add(LibSource.Config, new LibSourceInfo( null, null, null )); LibSources.Add(LibSource.Components, new LibSourceInfo( From 1fe4fed0fa24ce8381b3c253b364cc920a8d75e8 Mon Sep 17 00:00:00 2001 From: Josef Date: Thu, 17 Oct 2024 15:27:10 +0200 Subject: [PATCH 02/31] Add test-code for importing brick file in barrier --- .../AGXUnrealBarrier/Private/Brick/Brick.cpp | 110 ++++++++++++++++++ Source/AGXUnrealBarrier/Public/Brick/Brick.h | 13 +++ .../AGXDynamicsLibrary.Build.cs | 108 +++++++++++------ 3 files changed, 197 insertions(+), 34 deletions(-) create mode 100644 Source/AGXUnrealBarrier/Private/Brick/Brick.cpp create mode 100644 Source/AGXUnrealBarrier/Public/Brick/Brick.h diff --git a/Source/AGXUnrealBarrier/Private/Brick/Brick.cpp b/Source/AGXUnrealBarrier/Private/Brick/Brick.cpp new file mode 100644 index 000000000..cc60aa703 --- /dev/null +++ b/Source/AGXUnrealBarrier/Private/Brick/Brick.cpp @@ -0,0 +1,110 @@ +// Copyright 2024, Algoryx Simulation AB. + +#include "Brick/Brick.h" + +// AGX Dynamics for Unreal includes. +#include "TypeConversions.h" + +// Brick includes. +#include "Brick/brick/BrickContext.h" +#include "Brick/Brick/BrickCoreApi.h" +#include "Brick/brickagx/AgxCache.h" +#include "Brick/brickagx/BrickAgxApi.h" +#include "Brick/Math/Math_all.h" +#include "Brick/Physics/Physics_all.h" +#include "Brick/Physics1D/Physics1D_all.h" +#include "Brick/Physics3D/Physics3D_all.h" +#include "Brick/DriveTrain/DriveTrain_all.h" +#include "Brick/Robotics/Robotics_all.h" +#include "Brick/Simulation/Simulation_all.h" +#include "Brick/Vehicles/Vehicles_all.h" +#include "Brick/Terrain/Terrain_all.h" +#include "Brick/Visuals/Visuals_all.h" +#include "Brick/Urdf/Urdf_all.h" + + +// AGX Dynamics includes. +#include "BeginAGXIncludes.h" +#include "EndAGXIncludes.h" + +#include +#include +#include + +namespace +{ + class BrickUnrealMapper + { + public: + std::shared_ptr AGXCache; + + void MapModel(Brick::Core::ObjectPtr Model) + { + // Todo. + UE_LOG(LogTemp, Warning, TEXT("MapModel called!")); + } + + private: + }; + + std::shared_ptr CreateBrickContext(BrickUnrealMapper& Mapper) + { + auto BrickCtx = std::make_shared(std::vector( + {"C:/Users/Admin/git/agxunreal/AGXUnrealDev/Plugins/AGXUnreal/Source/ThirdParty/agx/brickbundles"})); + + Math_register_factories(BrickCtx.get()); + Physics_register_factories(BrickCtx.get()); + Physics1D_register_factories(BrickCtx.get()); + Physics3D_register_factories(BrickCtx.get()); + DriveTrain_register_factories(BrickCtx.get()); + Robotics_register_factories(BrickCtx.get()); + Simulation_register_factories(BrickCtx.get()); + Vehicles_register_factories(BrickCtx.get()); + Terrain_register_factories(BrickCtx.get()); + Visuals_register_factories(BrickCtx.get()); + Urdf_register_factories(BrickCtx.get()); + + BrickAgx::register_plugins(*BrickCtx, Mapper.AGXCache); + return BrickCtx; + } + + Brick::Core::ObjectPtr ParseBrickSource( + const std::filesystem::path& BrickFile, BrickUnrealMapper& Mapper) + { + auto Context = CreateBrickContext(Mapper); + if (Context == nullptr) + { + UE_LOG(LogTemp, Error, TEXT("Error Creating Brick Context")); + return nullptr; + } + + auto LoadedModel = Brick::Core::Api::loadModelFromFile(BrickFile, {}, *Context); + + if (Context->hasErrors()) + { + LoadedModel = nullptr; + for (auto Error : Context->getErrors()) + UE_LOG(LogTemp, Error, TEXT("Error in Brick Context : %d"), Error->getErrorCode()); + } + + return LoadedModel; + } + + void ImportBrickFile(const std::filesystem::path& BrickFile) + { + BrickUnrealMapper Mapper; + auto LoadedModel = ParseBrickSource(BrickFile, Mapper); + if (LoadedModel == nullptr) + { + UE_LOG(LogTemp, Error, TEXT("Unable to load model %s"), *Convert(BrickFile.string())); + return; + } + + Mapper.MapModel(LoadedModel); + } +} + +void FBrick::Test() +{ + +} diff --git a/Source/AGXUnrealBarrier/Public/Brick/Brick.h b/Source/AGXUnrealBarrier/Public/Brick/Brick.h new file mode 100644 index 000000000..d43a0bf93 --- /dev/null +++ b/Source/AGXUnrealBarrier/Public/Brick/Brick.h @@ -0,0 +1,13 @@ +// Copyright 2024, Algoryx Simulation AB. + +#pragma once + +// Unreal Engine includes. +#include "CoreMinimal.h" + +// Experimental only, donīt merge! +class AGXUNREALBARRIER_API FBrick +{ +public: + void Test(); +}; diff --git a/Source/ThirdParty/AGXDynamicsLibrary/AGXDynamicsLibrary.Build.cs b/Source/ThirdParty/AGXDynamicsLibrary/AGXDynamicsLibrary.Build.cs index b689cc569..767463b5e 100644 --- a/Source/ThirdParty/AGXDynamicsLibrary/AGXDynamicsLibrary.Build.cs +++ b/Source/ThirdParty/AGXDynamicsLibrary/AGXDynamicsLibrary.Build.cs @@ -232,11 +232,11 @@ public AGXDynamicsLibrary(ReadOnlyTargetRules Target) : base(Target) RuntimeLibFiles.Add("tbb", LibSource.TerrainDependencies); } - // List of link-time libraries from AGX Dynamics and its dependencies - // that we need. These will be added to the Unreal Engine - // PublicAdditionalLibraries list. See - // https://docs.unrealengine.com/en-US/ProductionPipelines/BuildTools/UnrealBuildTool/ModuleFiles/index.html - Dictionary LinkLibFiles = new Dictionary(); + // List of link-time libraries from AGX Dynamics and its dependencies + // that we need. These will be added to the Unreal Engine + // PublicAdditionalLibraries list. See + // https://docs.unrealengine.com/en-US/ProductionPipelines/BuildTools/UnrealBuildTool/ModuleFiles/index.html + Dictionary LinkLibFiles = new Dictionary(); LinkLibFiles.Add("agxPhysics", LibSource.AGX); LinkLibFiles.Add("agxCore", LibSource.AGX); LinkLibFiles.Add("agxHydraulics", LibSource.AGX); @@ -248,13 +248,38 @@ public AGXDynamicsLibrary(ReadOnlyTargetRules Target) : base(Target) LinkLibFiles.Add("agxROS2", LibSource.AGX); LinkLibFiles.Add("agx-nt-ros2", LibSource.AGX); - // Brick - LinkLibFiles.Add("brick.agx", LibSource.Brick); - - // List of the include directories from aGX Dynamics and its - // dependenciesthat we need. These will be added to the Unreal Engine - // PublicIncludePaths. - List IncludePaths = new List(); + // Brick + LinkLibFiles.Add("brick.agx", LibSource.Brick); + LinkLibFiles.Add("brick.analysis", LibSource.Brick); + LinkLibFiles.Add("brick.bundle", LibSource.Brick); + LinkLibFiles.Add("brick.core.api", LibSource.Brick); + LinkLibFiles.Add("brick.error", LibSource.Brick); + //LinkLibFiles.Add("brick.eval", LibSource.Brick); + //LinkLibFiles.Add("brick.generate", LibSource.Brick); + LinkLibFiles.Add("brick.internal", LibSource.Brick); + LinkLibFiles.Add("brick.nodes", LibSource.Brick); + //LinkLibFiles.Add("brick.osg", LibSource.Brick); + LinkLibFiles.Add("brick.parser", LibSource.Brick); + //LinkLibFiles.Add("brick.runtime", LibSource.Brick); + //LinkLibFiles.Add("DriveTrain", LibSource.Brick); + //LinkLibFiles.Add("Math", LibSource.Brick); + //LinkLibFiles.Add("Physics", LibSource.Brick); + //LinkLibFiles.Add("Physics1D", LibSource.Brick); + //LinkLibFiles.Add("Physics3D", LibSource.Brick); + //LinkLibFiles.Add("Robotics", LibSource.Brick); + //LinkLibFiles.Add("Simulation", LibSource.Brick); + //LinkLibFiles.Add("Terrain", LibSource.Brick); + //LinkLibFiles.Add("Urdf", LibSource.Brick); + //LinkLibFiles.Add("Vehicles", LibSource.Brick); + //LinkLibFiles.Add("Visuals", LibSource.Brick); + + LinkLibFiles.Add("fmt", LibSource.Brick); + LinkLibFiles.Add("spdlog", LibSource.Brick); + + // List of the include directories from aGX Dynamics and its + // dependenciesthat we need. These will be added to the Unreal Engine + // PublicIncludePaths. + List IncludePaths = new List(); IncludePaths.Add(LibSource.AGX); IncludePaths.Add(LibSource.Brick); @@ -340,7 +365,7 @@ public AGXDynamicsLibrary(ReadOnlyTargetRules Target) : base(Target) RuntimeDependencies.Add(Path.Combine(BundledAGXResourcesPath, "plugins", "*")); RuntimeDependencies.Add(Path.Combine(BundledAGXResourcesPath, "include", "*")); RuntimeDependencies.Add(Path.Combine(BundledAGXResourcesPath, "lib", "*")); - RuntimeDependencies.Add(Path.Combine(BundledAGXResourcesPath, "brickbundles", "*")); + RuntimeDependencies.Add(Path.Combine(BundledAGXResourcesPath, "brick", "*")); SetLicenseForCopySafe(Target); // This is a work-around for Linux which ensures that the .so files are @@ -394,6 +419,7 @@ private void AddLinkLibrary(string Name, LibSource Src) foreach (string FilePath in FilesToAdd) { + Console.WriteLine(FilePath); PublicAdditionalLibraries.Add(FilePath); } } @@ -590,12 +616,12 @@ private void BundleAGXResources(ReadOnlyTargetRules Target, Dictionary Date: Tue, 22 Oct 2024 11:43:48 +0200 Subject: [PATCH 03/31] Add missing libraries and import using hardcoded paths --- .../AGXUnrealBarrier/Private/Brick/Brick.cpp | 2 +- Source/AGXUnrealBarrier/Public/Brick/Brick.h | 2 +- .../AGXDynamicsLibrary.Build.cs | 42 +++++++++++-------- 3 files changed, 26 insertions(+), 20 deletions(-) diff --git a/Source/AGXUnrealBarrier/Private/Brick/Brick.cpp b/Source/AGXUnrealBarrier/Private/Brick/Brick.cpp index cc60aa703..ab3fefaa1 100644 --- a/Source/AGXUnrealBarrier/Private/Brick/Brick.cpp +++ b/Source/AGXUnrealBarrier/Private/Brick/Brick.cpp @@ -106,5 +106,5 @@ namespace void FBrick::Test() { - + ::ImportBrickFile("C:/Users/Admin/Desktop/double_pendulum.brick"); } diff --git a/Source/AGXUnrealBarrier/Public/Brick/Brick.h b/Source/AGXUnrealBarrier/Public/Brick/Brick.h index d43a0bf93..1f4cfa4fa 100644 --- a/Source/AGXUnrealBarrier/Public/Brick/Brick.h +++ b/Source/AGXUnrealBarrier/Public/Brick/Brick.h @@ -9,5 +9,5 @@ class AGXUNREALBARRIER_API FBrick { public: - void Test(); + static void Test(); }; diff --git a/Source/ThirdParty/AGXDynamicsLibrary/AGXDynamicsLibrary.Build.cs b/Source/ThirdParty/AGXDynamicsLibrary/AGXDynamicsLibrary.Build.cs index 767463b5e..43615fabe 100644 --- a/Source/ThirdParty/AGXDynamicsLibrary/AGXDynamicsLibrary.Build.cs +++ b/Source/ThirdParty/AGXDynamicsLibrary/AGXDynamicsLibrary.Build.cs @@ -249,32 +249,38 @@ public AGXDynamicsLibrary(ReadOnlyTargetRules Target) : base(Target) LinkLibFiles.Add("agx-nt-ros2", LibSource.AGX); // Brick + LinkLibFiles.Add("fmt", LibSource.Brick); + LinkLibFiles.Add("spdlog", LibSource.Brick); LinkLibFiles.Add("brick.agx", LibSource.Brick); LinkLibFiles.Add("brick.analysis", LibSource.Brick); LinkLibFiles.Add("brick.bundle", LibSource.Brick); LinkLibFiles.Add("brick.core.api", LibSource.Brick); LinkLibFiles.Add("brick.error", LibSource.Brick); - //LinkLibFiles.Add("brick.eval", LibSource.Brick); - //LinkLibFiles.Add("brick.generate", LibSource.Brick); + LinkLibFiles.Add("brick.eval", LibSource.Brick); + LinkLibFiles.Add("brick.generate", LibSource.Brick); LinkLibFiles.Add("brick.internal", LibSource.Brick); LinkLibFiles.Add("brick.nodes", LibSource.Brick); - //LinkLibFiles.Add("brick.osg", LibSource.Brick); + LinkLibFiles.Add("brick.osg", LibSource.Brick); LinkLibFiles.Add("brick.parser", LibSource.Brick); - //LinkLibFiles.Add("brick.runtime", LibSource.Brick); - //LinkLibFiles.Add("DriveTrain", LibSource.Brick); - //LinkLibFiles.Add("Math", LibSource.Brick); - //LinkLibFiles.Add("Physics", LibSource.Brick); - //LinkLibFiles.Add("Physics1D", LibSource.Brick); - //LinkLibFiles.Add("Physics3D", LibSource.Brick); - //LinkLibFiles.Add("Robotics", LibSource.Brick); - //LinkLibFiles.Add("Simulation", LibSource.Brick); - //LinkLibFiles.Add("Terrain", LibSource.Brick); - //LinkLibFiles.Add("Urdf", LibSource.Brick); - //LinkLibFiles.Add("Vehicles", LibSource.Brick); - //LinkLibFiles.Add("Visuals", LibSource.Brick); - - LinkLibFiles.Add("fmt", LibSource.Brick); - LinkLibFiles.Add("spdlog", LibSource.Brick); + LinkLibFiles.Add("brick.runtime", LibSource.Brick); + LinkLibFiles.Add("DriveTrain", LibSource.Brick); + LinkLibFiles.Add("Math", LibSource.Brick); + LinkLibFiles.Add("Physics", LibSource.Brick); + LinkLibFiles.Add("Physics1D", LibSource.Brick); + LinkLibFiles.Add("Physics3D", LibSource.Brick); + LinkLibFiles.Add("Robotics", LibSource.Brick); + LinkLibFiles.Add("Simulation", LibSource.Brick); + LinkLibFiles.Add("Terrain", LibSource.Brick); + LinkLibFiles.Add("Urdf", LibSource.Brick); + LinkLibFiles.Add("Vehicles", LibSource.Brick); + LinkLibFiles.Add("Visuals", LibSource.Brick); + LinkLibFiles.Add("brickurdfplugin", LibSource.Brick); + LinkLibFiles.Add("urdfdom_model", LibSource.Brick); + LinkLibFiles.Add("urdfdom_model_state", LibSource.Brick); + LinkLibFiles.Add("urdfdom_world", LibSource.Brick); + LinkLibFiles.Add("urdfdom_sensor", LibSource.Brick); + LinkLibFiles.Add("console_bridge", LibSource.Brick); + LinkLibFiles.Add("tinyxml", LibSource.Brick); // List of the include directories from aGX Dynamics and its // dependenciesthat we need. These will be added to the Unreal Engine From 4f8e264b4dcd6d9786d975997b062103645597d7 Mon Sep 17 00:00:00 2001 From: Josef Date: Tue, 22 Oct 2024 15:11:49 +0200 Subject: [PATCH 04/31] Fix bad call to register_factories --- .../AGXUnrealBarrier/Private/Brick/Brick.cpp | 26 +++++++++++-------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/Source/AGXUnrealBarrier/Private/Brick/Brick.cpp b/Source/AGXUnrealBarrier/Private/Brick/Brick.cpp index ab3fefaa1..b91cbfe1b 100644 --- a/Source/AGXUnrealBarrier/Private/Brick/Brick.cpp +++ b/Source/AGXUnrealBarrier/Private/Brick/Brick.cpp @@ -7,6 +7,7 @@ // Brick includes. #include "Brick/brick/BrickContext.h" +#include "Brick/brick/BrickContextInternal.h" #include "Brick/Brick/BrickCoreApi.h" #include "Brick/brickagx/AgxCache.h" #include "Brick/brickagx/BrickAgxApi.h" @@ -52,17 +53,20 @@ namespace auto BrickCtx = std::make_shared(std::vector( {"C:/Users/Admin/git/agxunreal/AGXUnrealDev/Plugins/AGXUnreal/Source/ThirdParty/agx/brickbundles"})); - Math_register_factories(BrickCtx.get()); - Physics_register_factories(BrickCtx.get()); - Physics1D_register_factories(BrickCtx.get()); - Physics3D_register_factories(BrickCtx.get()); - DriveTrain_register_factories(BrickCtx.get()); - Robotics_register_factories(BrickCtx.get()); - Simulation_register_factories(BrickCtx.get()); - Vehicles_register_factories(BrickCtx.get()); - Terrain_register_factories(BrickCtx.get()); - Visuals_register_factories(BrickCtx.get()); - Urdf_register_factories(BrickCtx.get()); + auto InternalContext = Brick::Core::Api::BrickContextInternal::fromContext(*BrickCtx); + auto EvalCtx = InternalContext->evaluatorContext().get(); + + Math_register_factories(EvalCtx); + Physics_register_factories(EvalCtx); + Physics1D_register_factories(EvalCtx); + Physics3D_register_factories(EvalCtx); + DriveTrain_register_factories(EvalCtx); + Robotics_register_factories(EvalCtx); + Simulation_register_factories(EvalCtx); + Vehicles_register_factories(EvalCtx); + Terrain_register_factories(EvalCtx); + Visuals_register_factories(EvalCtx); + Urdf_register_factories(EvalCtx); BrickAgx::register_plugins(*BrickCtx, Mapper.AGXCache); return BrickCtx; From e4ceb08a0b07fe07c1d54338f719d17ef47324fa Mon Sep 17 00:00:00 2001 From: Josef Date: Thu, 24 Oct 2024 14:27:27 +0200 Subject: [PATCH 05/31] Add factory handling automatic import with empty implementation --- .../AGXUnreal/Public/Brick/AGX_BrickAsset.h | 21 +++++++++++ .../AGXUnrealBarrier/Private/Brick/Brick.cpp | 22 +++++++++--- .../Private/Brick/AGX_BrickAssetFactory.cpp | 35 +++++++++++++++++++ .../Public/Brick/AGX_BrickAssetFactory.h | 31 ++++++++++++++++ 4 files changed, 105 insertions(+), 4 deletions(-) create mode 100644 Source/AGXUnreal/Public/Brick/AGX_BrickAsset.h create mode 100644 Source/AGXUnrealEditor/Private/Brick/AGX_BrickAssetFactory.cpp create mode 100644 Source/AGXUnrealEditor/Public/Brick/AGX_BrickAssetFactory.h diff --git a/Source/AGXUnreal/Public/Brick/AGX_BrickAsset.h b/Source/AGXUnreal/Public/Brick/AGX_BrickAsset.h new file mode 100644 index 000000000..9e4d0224f --- /dev/null +++ b/Source/AGXUnreal/Public/Brick/AGX_BrickAsset.h @@ -0,0 +1,21 @@ +// Copyright 2024, Algoryx Simulation AB. + +#pragma once + +// Unreal Engine includes. +#include "CoreMinimal.h" + +#include "AGX_BrickAsset.generated.h" + + +UCLASS(ClassGroup = "AGX", Category = "AGX", BlueprintType, Blueprintable) +class AGXUNREAL_API UAGX_BrickAsset : public UObject +{ + GENERATED_BODY() + + public: + + UPROPERTY(EditAnywhere, Category = "AGX Brick") + int Dummy; +}; + diff --git a/Source/AGXUnrealBarrier/Private/Brick/Brick.cpp b/Source/AGXUnrealBarrier/Private/Brick/Brick.cpp index b91cbfe1b..0c14ff035 100644 --- a/Source/AGXUnrealBarrier/Private/Brick/Brick.cpp +++ b/Source/AGXUnrealBarrier/Private/Brick/Brick.cpp @@ -23,7 +23,6 @@ #include "Brick/Visuals/Visuals_all.h" #include "Brick/Urdf/Urdf_all.h" - // AGX Dynamics includes. #include "BeginAGXIncludes.h" #include "EndAGXIncludes.h" @@ -39,10 +38,24 @@ namespace public: std::shared_ptr AGXCache; - void MapModel(Brick::Core::ObjectPtr Model) + void MapModel(const Brick::Core::ObjectPtr Model) { // Todo. UE_LOG(LogTemp, Warning, TEXT("MapModel called!")); + if (Model->is()) + { + auto System = std::dynamic_pointer_cast(Model); + UE_LOG( + LogTemp, Warning, TEXT("MapModel was given a System %s."), + *Convert(System->getName())); + } + else if (Model->is()) + { + auto Body = Model->as>(); + UE_LOG( + LogTemp, Warning, TEXT("MapModel was given a Brick Body %s"), + *Convert(Body->getName())); + } } private: @@ -50,8 +63,9 @@ namespace std::shared_ptr CreateBrickContext(BrickUnrealMapper& Mapper) { - auto BrickCtx = std::make_shared(std::vector( - {"C:/Users/Admin/git/agxunreal/AGXUnrealDev/Plugins/AGXUnreal/Source/ThirdParty/agx/brickbundles"})); + auto BrickCtx = std::make_shared( + std::vector({"C:/Users/Admin/git/agxunreal/AGXUnrealDev/Plugins/AGXUnreal/" + "Source/ThirdParty/agx/brickbundles"})); auto InternalContext = Brick::Core::Api::BrickContextInternal::fromContext(*BrickCtx); auto EvalCtx = InternalContext->evaluatorContext().get(); diff --git a/Source/AGXUnrealEditor/Private/Brick/AGX_BrickAssetFactory.cpp b/Source/AGXUnrealEditor/Private/Brick/AGX_BrickAssetFactory.cpp new file mode 100644 index 000000000..98a34595c --- /dev/null +++ b/Source/AGXUnrealEditor/Private/Brick/AGX_BrickAssetFactory.cpp @@ -0,0 +1,35 @@ +// Copyright 2024, Algoryx Simulation AB. + +#include "Brick/AGX_BrickAssetFactory.h" + +// AGX Dynamics for Unreal includes. +#include "Brick/AGX_BrickAsset.h" + +UAGX_BrickAssetFactory::UAGX_BrickAssetFactory(const class FObjectInitializer& OBJ) + : Super(OBJ) +{ + SupportedClass = UAGX_BrickAsset::StaticClass(); + bEditAfterNew = true; // Todo: what does this do? + bCreateNew = false; // Todo: what does this do? + bEditorImport = true; + + Formats.Add(TEXT("brick;Brick file")); +} + +bool UAGX_BrickAssetFactory::DoesSupportClass(UClass* Class) +{ + return Class == UAGX_BrickAsset::StaticClass(); +} + +UObject* UAGX_BrickAssetFactory::FactoryCreateBinary( + UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, + const TCHAR* Type, const uint8*& Buffer, const uint8* BufferEnd, FFeedbackContext* Warn) +{ + return nullptr; // Todo +} + +bool UAGX_BrickAssetFactory::FactoryCanImport(const FString& Filename) +{ + return FPaths::GetExtension(Filename).Equals("brick"); +} + diff --git a/Source/AGXUnrealEditor/Public/Brick/AGX_BrickAssetFactory.h b/Source/AGXUnrealEditor/Public/Brick/AGX_BrickAssetFactory.h new file mode 100644 index 000000000..4efb67827 --- /dev/null +++ b/Source/AGXUnrealEditor/Public/Brick/AGX_BrickAssetFactory.h @@ -0,0 +1,31 @@ +// Copyright 2024, Algoryx Simulation AB. + +#pragma once + +// Unreal Engine includes. +#include "CoreMinimal.h" +#include "Factories/Factory.h" +#include "Factories/ImportSettings.h" + +#include "AGX_BrickAssetFactory.generated.h" + +UCLASS() +class AGXUNREALEDITOR_API UAGX_BrickAssetFactory : public UFactory +{ + GENERATED_BODY() + +public: + UAGX_BrickAssetFactory(const class FObjectInitializer& OBJ); + + //~ Begin UFactory Interface + virtual bool DoesSupportClass(UClass* Class) override; + virtual UObject* FactoryCreateBinary( + UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, + const TCHAR* Type, const uint8*& Buffer, const uint8* BufferEnd, + FFeedbackContext* Warn) override; + virtual bool FactoryCanImport(const FString& Filename) override; + //~ End UFactory Interface + + +protected: +}; From 24c6c562b39587ba243c9f7a30715240b7f0f219 Mon Sep 17 00:00:00 2001 From: Josef Date: Thu, 24 Oct 2024 14:27:45 +0200 Subject: [PATCH 06/31] Use FactoryCreateFile instead in factory --- .../Private/Brick/AGX_BrickAssetFactory.cpp | 8 ++++---- .../AGXUnrealEditor/Public/Brick/AGX_BrickAssetFactory.h | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Source/AGXUnrealEditor/Private/Brick/AGX_BrickAssetFactory.cpp b/Source/AGXUnrealEditor/Private/Brick/AGX_BrickAssetFactory.cpp index 98a34595c..a96f29013 100644 --- a/Source/AGXUnrealEditor/Private/Brick/AGX_BrickAssetFactory.cpp +++ b/Source/AGXUnrealEditor/Private/Brick/AGX_BrickAssetFactory.cpp @@ -21,11 +21,11 @@ bool UAGX_BrickAssetFactory::DoesSupportClass(UClass* Class) return Class == UAGX_BrickAsset::StaticClass(); } -UObject* UAGX_BrickAssetFactory::FactoryCreateBinary( - UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, - const TCHAR* Type, const uint8*& Buffer, const uint8* BufferEnd, FFeedbackContext* Warn) +UObject* UAGX_BrickAssetFactory::FactoryCreateFile( + UClass* InClass, UObject* InParent, FName InName, EObjectFlags Flags, const FString& Filename, + const TCHAR* Parms, FFeedbackContext* Warn, bool& bOutOperationCanceled) { - return nullptr; // Todo + return nullptr; // todo } bool UAGX_BrickAssetFactory::FactoryCanImport(const FString& Filename) diff --git a/Source/AGXUnrealEditor/Public/Brick/AGX_BrickAssetFactory.h b/Source/AGXUnrealEditor/Public/Brick/AGX_BrickAssetFactory.h index 4efb67827..ba04b450a 100644 --- a/Source/AGXUnrealEditor/Public/Brick/AGX_BrickAssetFactory.h +++ b/Source/AGXUnrealEditor/Public/Brick/AGX_BrickAssetFactory.h @@ -19,10 +19,10 @@ class AGXUNREALEDITOR_API UAGX_BrickAssetFactory : public UFactory //~ Begin UFactory Interface virtual bool DoesSupportClass(UClass* Class) override; - virtual UObject* FactoryCreateBinary( - UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, - const TCHAR* Type, const uint8*& Buffer, const uint8* BufferEnd, - FFeedbackContext* Warn) override; + virtual UObject* FactoryCreateFile( + UClass* InClass, UObject* InParent, FName InName, EObjectFlags Flags, + const FString& Filename, const TCHAR* Parms, FFeedbackContext* Warn, + bool& bOutOperationCanceled) override; virtual bool FactoryCanImport(const FString& Filename) override; //~ End UFactory Interface From 1eb7c76ef67d83340703cf146740e436370cc905 Mon Sep 17 00:00:00 2001 From: Josef Date: Fri, 25 Oct 2024 14:27:27 +0200 Subject: [PATCH 07/31] Get callback for both import and reimport automatically [ci skip] --- .../Private/Brick/AGX_BrickAsset.cpp | 21 ++++++ .../AGXUnreal/Public/Brick/AGX_BrickAsset.h | 14 ++-- .../Private/AGXUnrealEditor.cpp | 3 + .../Private/Brick/AGX_BrickAssetFactory.cpp | 54 +++++++++++++- .../Brick/AGX_BrickAssetTypeActions.cpp | 71 +++++++++++++++++++ .../Public/Brick/AGX_BrickAssetFactory.h | 12 +++- .../Public/Brick/AGX_BrickAssetTypeActions.h | 34 +++++++++ 7 files changed, 200 insertions(+), 9 deletions(-) create mode 100644 Source/AGXUnreal/Private/Brick/AGX_BrickAsset.cpp create mode 100644 Source/AGXUnrealEditor/Private/Brick/AGX_BrickAssetTypeActions.cpp create mode 100644 Source/AGXUnrealEditor/Public/Brick/AGX_BrickAssetTypeActions.h diff --git a/Source/AGXUnreal/Private/Brick/AGX_BrickAsset.cpp b/Source/AGXUnreal/Private/Brick/AGX_BrickAsset.cpp new file mode 100644 index 000000000..9c6542c06 --- /dev/null +++ b/Source/AGXUnreal/Private/Brick/AGX_BrickAsset.cpp @@ -0,0 +1,21 @@ +// Copyright 2024, Algoryx Simulation AB. + +#include "Brick/AGX_BrickAsset.h" + +// Unreal Engine includes. +#include "EditorFramework/AssetImportData.h" +#include "UObject/AssetRegistryTagsContext.h" + +void UAGX_BrickAsset::GetAssetRegistryTags(FAssetRegistryTagsContext Context) const +{ + // Not sure exactly what it does. But removing it stops reimport triggering the factory from + // working. + if (AssetImportData) + { + Context.AddTag(FAssetRegistryTag( + SourceFileTagName(), AssetImportData->GetSourceData().ToJson(), + FAssetRegistryTag::TT_Hidden)); + } + + Super::GetAssetRegistryTags(Context); +} diff --git a/Source/AGXUnreal/Public/Brick/AGX_BrickAsset.h b/Source/AGXUnreal/Public/Brick/AGX_BrickAsset.h index 9e4d0224f..17bb39c13 100644 --- a/Source/AGXUnreal/Public/Brick/AGX_BrickAsset.h +++ b/Source/AGXUnreal/Public/Brick/AGX_BrickAsset.h @@ -7,15 +7,21 @@ #include "AGX_BrickAsset.generated.h" - UCLASS(ClassGroup = "AGX", Category = "AGX", BlueprintType, Blueprintable) class AGXUNREAL_API UAGX_BrickAsset : public UObject { GENERATED_BODY() - public: - +public: UPROPERTY(EditAnywhere, Category = "AGX Brick") int Dummy; -}; +#if WITH_EDITORONLY_DATA + + /** The file this data table was imported from, may be empty */ + UPROPERTY(VisibleAnywhere, Instanced, Category = ImportSource) + TObjectPtr AssetImportData; + + virtual void GetAssetRegistryTags(FAssetRegistryTagsContext Context) const override; +#endif +}; diff --git a/Source/AGXUnrealEditor/Private/AGXUnrealEditor.cpp b/Source/AGXUnrealEditor/Private/AGXUnrealEditor.cpp index 604a80ce1..b61e7dd04 100644 --- a/Source/AGXUnrealEditor/Private/AGXUnrealEditor.cpp +++ b/Source/AGXUnrealEditor/Private/AGXUnrealEditor.cpp @@ -44,6 +44,7 @@ #include "AMOR/AGX_ConstraintMergeSplitThresholdsTypeActions.h" #include "AMOR/AGX_ShapeContactMergeSplitThresholdsTypeActions.h" #include "AMOR/AGX_WireMergeSplitThresholdsTypeActions.h" +#include "Brick/AGX_BrickAssetTypeActions.h" #include "CollisionGroups/AGX_CollisionGroupDisablerActor.h" #include "CollisionGroups/AGX_CollisionGroupDisablerComponent.h" #include "CollisionGroups/AGX_CollisionGroupDisablerComponentCustomization.h" @@ -193,6 +194,8 @@ void FAGXUnrealEditorModule::RegisterAssetTypeActions() EAssetTypeCategories::Type AgxAssetCategoryBit = AssetTools.RegisterAdvancedAssetCategory( FName(TEXT("AgxUnreal")), LOCTEXT("AgxAssetCategory", "AGX Dynamics")); + RegisterAssetTypeAction( + AssetTools, MakeShareable(new FAGX_BrickAssetTypeActions(AgxAssetCategoryBit))); RegisterAssetTypeAction( AssetTools, MakeShareable(new FAGX_ContactMaterialAssetTypeActions(AgxAssetCategoryBit))); RegisterAssetTypeAction( diff --git a/Source/AGXUnrealEditor/Private/Brick/AGX_BrickAssetFactory.cpp b/Source/AGXUnrealEditor/Private/Brick/AGX_BrickAssetFactory.cpp index a96f29013..69d0bd199 100644 --- a/Source/AGXUnrealEditor/Private/Brick/AGX_BrickAssetFactory.cpp +++ b/Source/AGXUnrealEditor/Private/Brick/AGX_BrickAssetFactory.cpp @@ -5,11 +5,14 @@ // AGX Dynamics for Unreal includes. #include "Brick/AGX_BrickAsset.h" +// Unreal Engine includes. +#include "EditorFramework/AssetImportData.h" + + UAGX_BrickAssetFactory::UAGX_BrickAssetFactory(const class FObjectInitializer& OBJ) : Super(OBJ) { SupportedClass = UAGX_BrickAsset::StaticClass(); - bEditAfterNew = true; // Todo: what does this do? bCreateNew = false; // Todo: what does this do? bEditorImport = true; @@ -25,7 +28,24 @@ UObject* UAGX_BrickAssetFactory::FactoryCreateFile( UClass* InClass, UObject* InParent, FName InName, EObjectFlags Flags, const FString& Filename, const TCHAR* Parms, FFeedbackContext* Warn, bool& bOutOperationCanceled) { - return nullptr; // todo + GEditor->GetEditorSubsystem()->BroadcastAssetPreImport( + this, InClass, InParent, InName, TEXT("brick")); + + bOutOperationCanceled = false; + + // Reminder: + // if (IsAutomatedImport()) -> don't show any dialogues, or ask for user input! See Factory.h + + auto BrickAsset = NewObject(InParent, InClass, InName, Flags); + BrickAsset->AssetImportData = NewObject(this, TEXT("AssetImportData")); + if (!CurrentFilename.IsEmpty()) + { + BrickAsset->AssetImportData->Update(CurrentFilename); + } + + GEditor->GetEditorSubsystem()->BroadcastAssetPostImport(this, BrickAsset); + + return BrickAsset; } bool UAGX_BrickAssetFactory::FactoryCanImport(const FString& Filename) @@ -33,3 +53,33 @@ bool UAGX_BrickAssetFactory::FactoryCanImport(const FString& Filename) return FPaths::GetExtension(Filename).Equals("brick"); } +bool UAGX_BrickAssetFactory::CanReimport(UObject* Obj, TArray& OutFilenames) +{ + if (auto BrickAsset = Cast(Obj)) + { + BrickAsset->AssetImportData->ExtractFilenames(OutFilenames); + return true; + } + + return false; +} + +void UAGX_BrickAssetFactory::SetReimportPaths(UObject* Obj, const TArray& NewReimportPaths) +{ + auto BrickAsset = Cast(Obj); + if (BrickAsset && BrickAsset->AssetImportData && ensure(NewReimportPaths.Num() == 1)) + { + BrickAsset->AssetImportData->UpdateFilenameOnly(NewReimportPaths[0]); + } +} + +EReimportResult::Type UAGX_BrickAssetFactory::Reimport(UObject* Obj) +{ + return EReimportResult::Succeeded; +} + +int32 UAGX_BrickAssetFactory::GetPriority() const +{ + return ImportPriority; +} + diff --git a/Source/AGXUnrealEditor/Private/Brick/AGX_BrickAssetTypeActions.cpp b/Source/AGXUnrealEditor/Private/Brick/AGX_BrickAssetTypeActions.cpp new file mode 100644 index 000000000..8b8c4ac8b --- /dev/null +++ b/Source/AGXUnrealEditor/Private/Brick/AGX_BrickAssetTypeActions.cpp @@ -0,0 +1,71 @@ +// Copyright 2024, Algoryx Simulation AB. + +#include "Brick/AGX_BrickAssetTypeActions.h" + +// AGX Dynamics for Unreal includes. +#include "Brick/AGX_BrickAsset.h" +#include "Utilities/AGX_SlateUtilities.h" + + +#define LOCTEXT_NAMESPACE "FAGX_BrickAssetTypeActions" + +FAGX_BrickAssetTypeActions::FAGX_BrickAssetTypeActions( + EAssetTypeCategories::Type InAssetCategory) + : AssetCategory(InAssetCategory) +{ +} + +FText FAGX_BrickAssetTypeActions::GetName() const +{ + return LOCTEXT("AssetName", "AGX Brick Asset"); +} + +const TArray& FAGX_BrickAssetTypeActions::GetSubMenus() const +{ + static const TArray SubMenus { + LOCTEXT("BrickSubMenu", "Brick"), + }; + + return SubMenus; +} + +uint32 FAGX_BrickAssetTypeActions::GetCategories() +{ + return AssetCategory; +} + +FColor FAGX_BrickAssetTypeActions::GetTypeColor() const +{ + return FAGX_SlateUtilities::GetAGXColorOrange(); +} + +FText FAGX_BrickAssetTypeActions::GetAssetDescription(const FAssetData& AssetData) const +{ + return LOCTEXT( + "AssetDescription", "Brick Asset representing the source Brick file on disk."); +} + +UClass* FAGX_BrickAssetTypeActions::GetSupportedClass() const +{ + return UAGX_BrickAsset::StaticClass(); +} + +bool FAGX_BrickAssetTypeActions::IsImportedAsset() const +{ + return true; +} + +void FAGX_BrickAssetTypeActions::GetResolvedSourceFilePaths( + const TArray& TypeAssets, TArray& OutSourceFilePaths) const +{ + for (auto& Asset : TypeAssets) + { + const auto BrickAsset = CastChecked(Asset); + if (BrickAsset->AssetImportData != nullptr) + { + BrickAsset->AssetImportData->ExtractFilenames(OutSourceFilePaths); + } + } +} + +#undef LOCTEXT_NAMESPACE diff --git a/Source/AGXUnrealEditor/Public/Brick/AGX_BrickAssetFactory.h b/Source/AGXUnrealEditor/Public/Brick/AGX_BrickAssetFactory.h index ba04b450a..4f2808d93 100644 --- a/Source/AGXUnrealEditor/Public/Brick/AGX_BrickAssetFactory.h +++ b/Source/AGXUnrealEditor/Public/Brick/AGX_BrickAssetFactory.h @@ -4,13 +4,14 @@ // Unreal Engine includes. #include "CoreMinimal.h" +#include "EditorReimportHandler.h" #include "Factories/Factory.h" #include "Factories/ImportSettings.h" #include "AGX_BrickAssetFactory.generated.h" UCLASS() -class AGXUNREALEDITOR_API UAGX_BrickAssetFactory : public UFactory +class AGXUNREALEDITOR_API UAGX_BrickAssetFactory : public UFactory, public FReimportHandler { GENERATED_BODY() @@ -26,6 +27,11 @@ class AGXUNREALEDITOR_API UAGX_BrickAssetFactory : public UFactory virtual bool FactoryCanImport(const FString& Filename) override; //~ End UFactory Interface - -protected: + //~ Begin FReimportHandler Interface + virtual bool CanReimport(UObject* Obj, TArray& OutFilenames) override; + virtual void SetReimportPaths( + UObject* Obj, const TArray& NewReimportPaths) override; + virtual EReimportResult::Type Reimport(UObject* Obj) override; + virtual int32 GetPriority() const override; + //~ End FReimportHandler Interface }; diff --git a/Source/AGXUnrealEditor/Public/Brick/AGX_BrickAssetTypeActions.h b/Source/AGXUnrealEditor/Public/Brick/AGX_BrickAssetTypeActions.h new file mode 100644 index 000000000..24bd0e99b --- /dev/null +++ b/Source/AGXUnrealEditor/Public/Brick/AGX_BrickAssetTypeActions.h @@ -0,0 +1,34 @@ +// Copyright 2024, Algoryx Simulation AB. + +#pragma once + +#include "CoreMinimal.h" +#include "AssetTypeCategories.h" +#include "AssetTypeActions_Base.h" + + +class AGXUNREALEDITOR_API FAGX_BrickAssetTypeActions : public FAssetTypeActions_Base +{ +public: + FAGX_BrickAssetTypeActions(EAssetTypeCategories::Type InAssetCategory); + + virtual FText GetName() const override; + + virtual const TArray& GetSubMenus() const override; + + virtual uint32 GetCategories() override; + + virtual FColor GetTypeColor() const override; + + virtual FText GetAssetDescription(const FAssetData& AssetData) const override; + + virtual UClass* GetSupportedClass() const override; + + virtual bool IsImportedAsset() const override; + + virtual void GetResolvedSourceFilePaths( + const TArray& TypeAssets, TArray& OutSourceFilePaths) const override; + +private: + EAssetTypeCategories::Type AssetCategory; +}; From 3badf9f1532ba967b452beaae5812f8b79429e39 Mon Sep 17 00:00:00 2001 From: Josef Date: Mon, 28 Oct 2024 10:09:46 +0100 Subject: [PATCH 08/31] Add temporary button hooks in BrickAsset view for experimenting --- .../Private/AGXUnrealEditor.cpp | 8 ++ .../Brick/AGX_BrickAssetCustomization.cpp | 128 ++++++++++++++++++ .../Brick/AGX_BrickAssetCustomization.h | 28 ++++ 3 files changed, 164 insertions(+) create mode 100644 Source/AGXUnrealEditor/Private/Brick/AGX_BrickAssetCustomization.cpp create mode 100644 Source/AGXUnrealEditor/Public/Brick/AGX_BrickAssetCustomization.h diff --git a/Source/AGXUnrealEditor/Private/AGXUnrealEditor.cpp b/Source/AGXUnrealEditor/Private/AGXUnrealEditor.cpp index b61e7dd04..b872f8f78 100644 --- a/Source/AGXUnrealEditor/Private/AGXUnrealEditor.cpp +++ b/Source/AGXUnrealEditor/Private/AGXUnrealEditor.cpp @@ -44,6 +44,8 @@ #include "AMOR/AGX_ConstraintMergeSplitThresholdsTypeActions.h" #include "AMOR/AGX_ShapeContactMergeSplitThresholdsTypeActions.h" #include "AMOR/AGX_WireMergeSplitThresholdsTypeActions.h" +#include "Brick/AGX_BrickAsset.h" +#include "Brick/AGX_BrickAssetCustomization.h" #include "Brick/AGX_BrickAssetTypeActions.h" #include "CollisionGroups/AGX_CollisionGroupDisablerActor.h" #include "CollisionGroups/AGX_CollisionGroupDisablerComponent.h" @@ -294,6 +296,10 @@ void FAGXUnrealEditorModule::RegisterCustomizations() * Class customizations. */ + PropertyModule.RegisterCustomClassLayout( + UAGX_BrickAsset::StaticClass()->GetFName(), + FOnGetDetailCustomizationInstance::CreateStatic(&FAGX_BrickAssetCustomization::MakeInstance)); + /// \todo I don't know if this should be AAGX_ConstraintActor or /// UAGX_ConstraintComponent. Should we have one for each? Which should be /// the new one and what should it contain/do? @@ -438,6 +444,8 @@ void FAGXUnrealEditorModule::UnregisterCustomizations() * Class Customizations. */ + PropertyModule.UnregisterCustomClassLayout(UAGX_BrickAsset::StaticClass()->GetFName()); + /// \todo Not sure if this should be AAGX_ConstraintActor, /// UAGX_ConstraintComponent, or both. PropertyModule.UnregisterCustomClassLayout(AAGX_ConstraintActor::StaticClass()->GetFName()); diff --git a/Source/AGXUnrealEditor/Private/Brick/AGX_BrickAssetCustomization.cpp b/Source/AGXUnrealEditor/Private/Brick/AGX_BrickAssetCustomization.cpp new file mode 100644 index 000000000..28c20d834 --- /dev/null +++ b/Source/AGXUnrealEditor/Private/Brick/AGX_BrickAssetCustomization.cpp @@ -0,0 +1,128 @@ +// Copyright 2024, Algoryx Simulation AB. + +#include "Brick/AGX_BrickAssetCustomization.h" + +// AGX Dynamics for Unreal includes. +#include "Brick/AGX_BrickAsset.h" +#include "Brick/Brick.h" +#include "Utilities/AGX_EditorUtilities.h" +#include "Utilities/AGX_ImportUtilities.h" + +// Unreal Engine includes. +#include "DetailCategoryBuilder.h" +#include "DetailLayoutBuilder.h" +#include "DetailWidgetRow.h" +#include "Engine/Level.h" +#include "Engine/World.h" +#include "Kismet/GameplayStatics.h" +#include "Widgets/Input/SButton.h" +#include "Widgets/Text/STextBlock.h" + +#define LOCTEXT_NAMESPACE "FAGX_BrickAssetCustomization" + +TSharedRef FAGX_BrickAssetCustomization::MakeInstance() +{ + return MakeShareable(new FAGX_BrickAssetCustomization); +} + +void FAGX_BrickAssetCustomization::CustomizeDetails(IDetailLayoutBuilder& InDetailBuilder) +{ + DetailBuilder = &InDetailBuilder; + + const UAGX_BrickAsset* BrickAsset = + FAGX_EditorUtilities::GetSingleObjectBeingCustomized(InDetailBuilder); + if (BrickAsset == nullptr) + { + return; + } + + IDetailCategoryBuilder& CategoryBuilder = InDetailBuilder.EditCategory("Experiments"); + + // clang-format off + CategoryBuilder.AddCustomRow(FText::GetEmpty()) + [ + SNew(SBorder) + .BorderBackgroundColor(FLinearColor(1.0f, 1.0f, 1.0f)) + .Padding(FMargin(5.0f, 5.0f)) + .Content() + [ + SNew(SVerticalBox) + + SVerticalBox::Slot() + .Padding(FMargin(5.0f, 5.0f)) + .AutoHeight() + [ + SNew(SBorder) + .BorderBackgroundColor(FLinearColor(1.0f, 1.0f, 1.0f)) + .BorderImage(FAGX_EditorUtilities::GetBrush("ToolPanel.GroupBorder")) + .Padding(FMargin(5.0f, 5.0f)) + .Content() + [ + SNew(SHorizontalBox) + + SHorizontalBox::Slot() + .AutoWidth() + .Padding(FMargin(10.0f, 0.0f)) + [ + SNew(SButton) + .Text(LOCTEXT("Import", "Import")) + .ToolTipText(LOCTEXT("ImportTooltip", + "Import using hard-coded path.")) + .OnClicked(this, &FAGX_BrickAssetCustomization::OnImportButtonClicked) + ] + + SHorizontalBox::Slot() + .AutoWidth() + .Padding(FMargin(10.0f, 0.0f)) + [ + SNew(SButton) + .Text(LOCTEXT("Experiment", "Experiment")) + .ToolTipText(LOCTEXT("ExperimentTooltip", + "Experiment.")) + .OnClicked(this, &FAGX_BrickAssetCustomization::OnExperimentButtonClicked) + ] + ] + ] + ] + ]; + // clang-format on +} + +FReply FAGX_BrickAssetCustomization::OnImportButtonClicked() +{ + AGX_CHECK(DetailBuilder); + FBrick::Test(); + return FReply::Handled(); +} + +namespace +{ + UPackage* CreateUPackage(const FString& BlueprintPackagePath) + { + UPackage* Package = CreatePackage(*BlueprintPackagePath); + Package->FullyLoad(); + return Package; + } + + FString CreatePackagePath(const FString& SubDir) + { + const FString BasePath = FString(TEXT("/Game/")); + return FAGX_ImportUtilities::CreatePackagePath(BasePath, SubDir); + } + + UBlueprint* CreateBlueprint(const FString& Name) + { + GEditor->SelectNone(false, false); + UPackage* Package = CreateUPackage("Blueprint"); + + } +} + +FReply FAGX_BrickAssetCustomization::OnExperimentButtonClicked() +{ + AGX_CHECK(DetailBuilder); + UE_LOG(LogTemp, Warning, TEXT("Experiment!")); + UBlueprint* BaseBlueprint = ::CreateBlueprint("BaseBlueprint"); + + + return FReply::Handled(); +} + +#undef LOCTEXT_NAMESPACE diff --git a/Source/AGXUnrealEditor/Public/Brick/AGX_BrickAssetCustomization.h b/Source/AGXUnrealEditor/Public/Brick/AGX_BrickAssetCustomization.h new file mode 100644 index 000000000..6d3403fd4 --- /dev/null +++ b/Source/AGXUnrealEditor/Public/Brick/AGX_BrickAssetCustomization.h @@ -0,0 +1,28 @@ +// Copyright 2024, Algoryx Simulation AB. + +#pragma once + +// Unreal Engine includes. +#include "CoreMinimal.h" +#include "IDetailCustomization.h" +#include "Input/Reply.h" + +class IDetailLayoutBuilder; +class IDetailCategoryBuilder; + +/** + * Only for internal testing, do not merge! + */ +class AGXUNREALEDITOR_API FAGX_BrickAssetCustomization : public IDetailCustomization +{ +public: + static TSharedRef MakeInstance(); + + virtual void CustomizeDetails(IDetailLayoutBuilder& InDetailBuilder) override; + +private: + FReply OnImportButtonClicked(); + FReply OnExperimentButtonClicked(); + + IDetailLayoutBuilder* DetailBuilder; +}; From bcbf8f3dca1ca890a87e7f4bdb4a711c419c7106 Mon Sep 17 00:00:00 2001 From: Josef Date: Tue, 29 Oct 2024 14:33:49 +0100 Subject: [PATCH 09/31] Add more experiment hooks --- .../Brick/AGX_BrickAssetCustomization.cpp | 206 +++++++++++++++++- .../Brick/AGX_BrickAssetCustomization.h | 8 +- 2 files changed, 200 insertions(+), 14 deletions(-) diff --git a/Source/AGXUnrealEditor/Private/Brick/AGX_BrickAssetCustomization.cpp b/Source/AGXUnrealEditor/Private/Brick/AGX_BrickAssetCustomization.cpp index 28c20d834..411e31f38 100644 --- a/Source/AGXUnrealEditor/Private/Brick/AGX_BrickAssetCustomization.cpp +++ b/Source/AGXUnrealEditor/Private/Brick/AGX_BrickAssetCustomization.cpp @@ -3,18 +3,27 @@ #include "Brick/AGX_BrickAssetCustomization.h" // AGX Dynamics for Unreal includes. +#include "AGX_RigidBodyComponent.h" #include "Brick/AGX_BrickAsset.h" #include "Brick/Brick.h" #include "Utilities/AGX_EditorUtilities.h" #include "Utilities/AGX_ImportUtilities.h" // Unreal Engine includes. +#include "ActorFactories/ActorFactoryEmptyActor.h" +#include "AssetSelection.h" #include "DetailCategoryBuilder.h" #include "DetailLayoutBuilder.h" #include "DetailWidgetRow.h" +#include "Engine/Blueprint.h" #include "Engine/Level.h" +#include "Engine/SCS_Node.h" +#include "Engine/SimpleConstructionScript.h" #include "Engine/World.h" #include "Kismet/GameplayStatics.h" +#include "Kismet2/BlueprintEditorUtils.h" +#include "Kismet2/KismetEditorUtilities.h" +#include "PackageTools.h" #include "Widgets/Input/SButton.h" #include "Widgets/Text/STextBlock.h" @@ -73,10 +82,30 @@ void FAGX_BrickAssetCustomization::CustomizeDetails(IDetailLayoutBuilder& InDeta .Padding(FMargin(10.0f, 0.0f)) [ SNew(SButton) - .Text(LOCTEXT("Experiment", "Experiment")) - .ToolTipText(LOCTEXT("ExperimentTooltip", - "Experiment.")) - .OnClicked(this, &FAGX_BrickAssetCustomization::OnExperimentButtonClicked) + .Text(LOCTEXT("CreateBlueprints", "CreateBlueprints")) + .ToolTipText(LOCTEXT("CreateBpsToolTip", + "CreateBlueprints.")) + .OnClicked(this, &FAGX_BrickAssetCustomization::OnCreateBlueprintsButtonClicked) + ] + + SHorizontalBox::Slot() + .AutoWidth() + .Padding(FMargin(10.0f, 0.0f)) + [ + SNew(SButton) + .Text(LOCTEXT("UpdateBlueprint", "UpdateBlueprint")) + .ToolTipText(LOCTEXT("UpdateBlueprintToolTip", + "UpdateBlueprint.")) + .OnClicked(this, &FAGX_BrickAssetCustomization::OnUpdateBlueprintButtonClicked) + ] + + SHorizontalBox::Slot() + .AutoWidth() + .Padding(FMargin(10.0f, 0.0f)) + [ + SNew(SButton) + .Text(LOCTEXT("CopyBlueprint", "CopyBlueprint")) + .ToolTipText(LOCTEXT("CopyBlueprintToolTip", + "CopyBlueprint.")) + .OnClicked(this, &FAGX_BrickAssetCustomization::OnCopyFromOtherBlueprintButtonClicked) ] ] ] @@ -101,26 +130,179 @@ namespace return Package; } - FString CreatePackagePath(const FString& SubDir) + FString CreatePackagePath(const FString& SubDir, FString Name) { const FString BasePath = FString(TEXT("/Game/")); - return FAGX_ImportUtilities::CreatePackagePath(BasePath, SubDir); + FString PackagePath = FAGX_ImportUtilities::CreatePackagePath(BasePath, SubDir); + FAGX_ImportUtilities::MakePackageAndAssetNameUnique(PackagePath, Name); + UPackage* ParentPackage = CreatePackage(*PackagePath); + FString Path = FPaths::GetPath(ParentPackage->GetName()); + return UPackageTools::SanitizePackageName(Path + "/" + Name); } - UBlueprint* CreateBlueprint(const FString& Name) + UBlueprint* CreateBlueprint(const FString& Name, AActor*& OutTemplate, bool SetNonDefaultMass = false) { GEditor->SelectNone(false, false); - UPackage* Package = CreateUPackage("Blueprint"); - + FString PackagePath = CreatePackagePath("Blueprint", Name); + UPackage* Package = CreateUPackage(PackagePath); + + UActorFactory* Factory = + GEditor->FindActorFactoryByClass(UActorFactoryEmptyActor::StaticClass()); + FAssetData EmptyActorAssetData = FAssetData(Factory->GetDefaultActorClass(FAssetData())); + UObject* EmptyActorAsset = EmptyActorAssetData.GetAsset(); + OutTemplate = + FActorFactoryAssetProxy::AddActorForAsset(EmptyActorAsset, false); + check(OutTemplate != nullptr); /// \todo Test and return false instead of check? + OutTemplate->SetFlags(RF_Transactional); + OutTemplate->SetActorLabel(Name); + + UAGX_RigidBodyComponent* RigidBody = + NewObject(OutTemplate, TEXT("Body")); + RigidBody->bAutoGenerateMass = false; + if (SetNonDefaultMass) + RigidBody->Mass = 6.f; + + RigidBody->SetFlags(RF_Transactional); + OutTemplate->AddInstanceComponent(RigidBody); + RigidBody->RegisterComponent(); + RigidBody->PostEditChange(); + + FKismetEditorUtilities::FCreateBlueprintFromActorParams Params; + Params.bReplaceActor = false; + Params.bKeepMobility = true; + Params.bOpenBlueprint = false; + + UBlueprint* Blueprint = FKismetEditorUtilities::CreateBlueprintFromActor( + Package->GetName(), OutTemplate, Params); + + //RootActorContainer->Destroy(); + FAGX_ObjectUtilities::SaveAsset(*Blueprint); + + // Child Blueprint + FString PackagePathChild = CreatePackagePath("", FString::Printf(TEXT("%s_child"), *Name)); + UPackage* PackageChild = CreateUPackage(PackagePathChild); + const FString AssetNameChild = FPaths::GetBaseFilename(PackageChild->GetName()); + + UBlueprint* BlueprintChild = FKismetEditorUtilities::CreateBlueprint( + Blueprint->GeneratedClass, PackageChild, FName(AssetNameChild), + EBlueprintType::BPTYPE_Normal, + UBlueprint::StaticClass(), UBlueprintGeneratedClass::StaticClass(), + FName("HackyCodeCO")); + + GEngine->BroadcastLevelActorListChanged(); + + FAGX_ObjectUtilities::SaveAsset(*BlueprintChild); + + return Blueprint; } } -FReply FAGX_BrickAssetCustomization::OnExperimentButtonClicked() +FReply FAGX_BrickAssetCustomization::OnCreateBlueprintsButtonClicked() { AGX_CHECK(DetailBuilder); - UE_LOG(LogTemp, Warning, TEXT("Experiment!")); - UBlueprint* BaseBlueprint = ::CreateBlueprint("BaseBlueprint"); + UE_LOG(LogTemp, Warning, TEXT("CreateBlueprints!")); + UBlueprint* BaseBlueprint = ::CreateBlueprint("BaseBlueprint", TemplateActor); + BlueprintBase = BaseBlueprint; + return FReply::Handled(); +} + +FReply FAGX_BrickAssetCustomization::OnUpdateBlueprintButtonClicked() +{ + AGX_CHECK(DetailBuilder); + AGX_CHECK(BlueprintBase); + UE_LOG(LogTemp, Warning, TEXT("UpdateBlueprints!")); + + for (USCS_Node* Node : BlueprintBase->SimpleConstructionScript->GetAllNodes()) + { + if (Node == nullptr) + continue; + + UActorComponent* Component = Node->ComponentTemplate; + + if (auto Ri = Cast(Component)) + { + Ri->Modify(); + Ri->Mass = 3.f; + UE_LOG(LogTemp, Warning, TEXT("Updated rigidbody with new mass")); + FBlueprintEditorUtils::MarkBlueprintAsModified(BlueprintBase); + Ri->MarkPackageDirty(); + Ri->PostEditChange(); + FPropertyChangedEvent PropertyChangedEvent( + Ri->GetClass()->FindPropertyByName( + GET_MEMBER_NAME_CHECKED(UAGX_RigidBodyComponent, Mass))); + Ri->PostEditChangeProperty(PropertyChangedEvent); + } + } + + FKismetEditorUtilities::CompileBlueprint(BlueprintBase); + FAGX_ObjectUtilities::SaveAsset(*BlueprintBase); + + return FReply::Handled(); +} + +FReply FAGX_BrickAssetCustomization::OnCopyFromOtherBlueprintButtonClicked() +{ +#if 0 // didnt work + AGX_CHECK(TemplateActor); + AGX_CHECK(BlueprintBase); + + AActor* CDO = CastChecked(BlueprintBase->GeneratedClass->GetDefaultObject()); + const EditorUtilities::ECopyOptions::Type CopyOptions = (EditorUtilities::ECopyOptions::Type)( + EditorUtilities::ECopyOptions::PropagateChangesToArchetypeInstances); + EditorUtilities::CopyActorProperties(TemplateActor, CDO, CopyOptions); +#endif + + + UBlueprint* BlueprintA = LoadObject(nullptr, TEXT("/Game/BlueprintA")); + UBlueprint* BlueprintB = LoadObject(nullptr, TEXT("/Game/BlueprintB")); + AGX_CHECK(BlueprintA != nullptr); + AGX_CHECK(BlueprintB != nullptr); + +#if 0 // Almost, kinda works if ReplaceStaleRefs is skipped with debugger + //, but child blueprints will die, due to no parent anymore. + UBlueprint* Replacement = FKismetEditorUtilities::ReplaceBlueprint(BlueprintB, BlueprintA); + FKismetEditorUtilities::CompileBlueprint(Replacement); + FAGX_ObjectUtilities::SaveAsset(*Replacement); +#endif + +#if 0 //Crash when trying to open Blueprint afterwords. + UEngine::CopyPropertiesForUnrelatedObjects(BlueprintA, BlueprintB); +#endif + +#if 0 // does nothing + AActor* CDOA = CastChecked(BlueprintA->GeneratedClass->GetDefaultObject()); + AActor* CDOB = CastChecked(BlueprintA->GeneratedClass->GetDefaultObject()); + UEngine::CopyPropertiesForUnrelatedObjects(CDOA, CDOB); +#endif + +#if 0 // does nothing + AActor* CDOA = CastChecked(BlueprintA->GeneratedClass->GetDefaultObject()); + AActor* CDOB = CastChecked(BlueprintA->GeneratedClass->GetDefaultObject()); + EditorUtilities::CopyActorProperties(CDOA, CDOB); +#endif + + + + + +#if 0 // This will crash after CopyPropertiesForUnrelatedObjects. + UBlueprint* NewBlueprint = ::CreateBlueprint("BaseBlueprint_new"); + // Now we have BlueprintBase and NewBlueprint which are almost identical. + // NewBlueprint has another mass for the RigidBody. + // Mission: can we somehow update BlueprintBase to match NewBlueprint? + + UEngine::CopyPropertiesForUnrelatedObjects( + NewBlueprint->ParentClass, BlueprintBase->ParentClass); + FKismetEditorUtilities::CompileBlueprint(BlueprintBase); + FAGX_ObjectUtilities::SaveAsset(*BlueprintBase); + FKismetEditorUtilities::CompileBlueprint(NewBlueprint); + FAGX_ObjectUtilities::SaveAsset(*NewBlueprint); +#endif +#if 0 // Also crashes + UEngine::CopyPropertiesForUnrelatedObjects( + NewBlueprint->GeneratedClass, BlueprintBase->GeneratedClass); +#endif return FReply::Handled(); } diff --git a/Source/AGXUnrealEditor/Public/Brick/AGX_BrickAssetCustomization.h b/Source/AGXUnrealEditor/Public/Brick/AGX_BrickAssetCustomization.h index 6d3403fd4..0a039c937 100644 --- a/Source/AGXUnrealEditor/Public/Brick/AGX_BrickAssetCustomization.h +++ b/Source/AGXUnrealEditor/Public/Brick/AGX_BrickAssetCustomization.h @@ -22,7 +22,11 @@ class AGXUNREALEDITOR_API FAGX_BrickAssetCustomization : public IDetailCustomiza private: FReply OnImportButtonClicked(); - FReply OnExperimentButtonClicked(); + FReply OnCreateBlueprintsButtonClicked(); + FReply OnUpdateBlueprintButtonClicked(); + FReply OnCopyFromOtherBlueprintButtonClicked(); - IDetailLayoutBuilder* DetailBuilder; + IDetailLayoutBuilder* DetailBuilder {nullptr}; + UBlueprint* BlueprintBase {nullptr}; + AActor* TemplateActor {nullptr}; }; From b2629c0ee10b7dfb29dfd010bb44a86612e16f53 Mon Sep 17 00:00:00 2001 From: Josef Date: Tue, 5 Nov 2024 15:49:24 +0100 Subject: [PATCH 10/31] Add support for brick import using regular import pipeline --- .../Private/Brick/AGX_BrickAsset.cpp | 3 + .../Private/AGXSimObjectsReader.cpp | 105 +++++++++++++- .../AGXUnrealBarrier/Private/Brick/Brick.cpp | 128 ------------------ .../Public/AGXSimObjectsReader.h | 3 + Source/AGXUnrealBarrier/Public/Brick/Brick.h | 13 -- .../Private/AGX_ImporterToBlueprint.cpp | 35 ++++- .../Brick/AGX_BrickAssetCustomization.cpp | 32 ++--- .../Private/Utilities/AGX_ImportUtilities.cpp | 4 + .../Private/Widgets/AGX_ImportDialog.cpp | 2 +- .../AGXUnrealEditor/Public/AGX_ImportEnums.h | 7 +- 10 files changed, 163 insertions(+), 169 deletions(-) delete mode 100644 Source/AGXUnrealBarrier/Private/Brick/Brick.cpp delete mode 100644 Source/AGXUnrealBarrier/Public/Brick/Brick.h diff --git a/Source/AGXUnreal/Private/Brick/AGX_BrickAsset.cpp b/Source/AGXUnreal/Private/Brick/AGX_BrickAsset.cpp index 9c6542c06..774d1fc62 100644 --- a/Source/AGXUnreal/Private/Brick/AGX_BrickAsset.cpp +++ b/Source/AGXUnreal/Private/Brick/AGX_BrickAsset.cpp @@ -6,6 +6,8 @@ #include "EditorFramework/AssetImportData.h" #include "UObject/AssetRegistryTagsContext.h" + +#if WITH_EDITORONLY_DATA void UAGX_BrickAsset::GetAssetRegistryTags(FAssetRegistryTagsContext Context) const { // Not sure exactly what it does. But removing it stops reimport triggering the factory from @@ -19,3 +21,4 @@ void UAGX_BrickAsset::GetAssetRegistryTags(FAssetRegistryTagsContext Context) co Super::GetAssetRegistryTags(Context); } +#endif diff --git a/Source/AGXUnrealBarrier/Private/AGXSimObjectsReader.cpp b/Source/AGXUnrealBarrier/Private/AGXSimObjectsReader.cpp index d67adccd5..9f5b20b8e 100644 --- a/Source/AGXUnrealBarrier/Private/AGXSimObjectsReader.cpp +++ b/Source/AGXUnrealBarrier/Private/AGXSimObjectsReader.cpp @@ -5,6 +5,7 @@ // AGX Dynamics for Unreal includes. #include "AGXBarrierFactories.h" #include "AGX_Check.h" +#include "AGX_Environment.h" #include "AGX_LogCategory.h" #include "BarrierOnly/AGXRefs.h" #include "RigidBodyBarrier.h" @@ -29,6 +30,25 @@ #include #include +// Brick includes. +#include "Brick/brick/BrickContext.h" +#include "Brick/brick/BrickContextInternal.h" +#include "Brick/Brick/BrickCoreApi.h" +#include "Brick/brickagx/AgxCache.h" +#include "Brick/brickagx/BrickAgxApi.h" +#include "Brick/brickagx/BrickToAgxMapper.h" +#include "Brick/Math/Math_all.h" +#include "Brick/Physics/Physics_all.h" +#include "Brick/Physics1D/Physics1D_all.h" +#include "Brick/Physics3D/Physics3D_all.h" +#include "Brick/DriveTrain/DriveTrain_all.h" +#include "Brick/Robotics/Robotics_all.h" +#include "Brick/Simulation/Simulation_all.h" +#include "Brick/Vehicles/Vehicles_all.h" +#include "Brick/Terrain/Terrain_all.h" +#include "Brick/Visuals/Visuals_all.h" +#include "Brick/Urdf/Urdf_all.h" + // In 2.28 including Cable.h causes a preprocessor macro named DEPRECATED to be defined. This // conflicts with a macro with the same name in Unreal. Undeffing the Unreal one. /// \todo Remove this #undef once the macro has been removed from AGX Dynamics. @@ -591,9 +611,9 @@ bool FAGXSimObjectsReader::ReadAGXArchive( return true; } -AGXUNREALBARRIER_API bool FAGXSimObjectsReader::ReadUrdf( - const FString& UrdfFilePath, const FString& UrdfPackagePath, - const TArray& InInitJoints, FSimulationObjectCollection& OutSimObjects) +bool FAGXSimObjectsReader::ReadUrdf( + const FString& UrdfFilePath, const FString& UrdfPackagePath, const TArray& InInitJoints, + FSimulationObjectCollection& OutSimObjects) { agx::RealVector* InitJointsPtr = nullptr; agx::RealVector InitJoints; @@ -625,3 +645,82 @@ AGXUNREALBARRIER_API bool FAGXSimObjectsReader::ReadUrdf( return true; } + +namespace AGXSimObjectsReader_helpers +{ + std::shared_ptr CreateBrickContext( + std::shared_ptr AGXCache) + { + const FString BrickBundlesPath = + FPaths::Combine(FAGX_Environment::GetPluginSourcePath(), "Thirdparty", "agx", "brickbundles"); + auto BrickCtx = std::make_shared( + std::vector({Convert(BrickBundlesPath)})); + + auto InternalContext = Brick::Core::Api::BrickContextInternal::fromContext(*BrickCtx); + auto EvalCtx = InternalContext->evaluatorContext().get(); + + Math_register_factories(EvalCtx); + Physics_register_factories(EvalCtx); + Physics1D_register_factories(EvalCtx); + Physics3D_register_factories(EvalCtx); + DriveTrain_register_factories(EvalCtx); + Robotics_register_factories(EvalCtx); + Simulation_register_factories(EvalCtx); + Vehicles_register_factories(EvalCtx); + Terrain_register_factories(EvalCtx); + Visuals_register_factories(EvalCtx); + Urdf_register_factories(EvalCtx); + + BrickAgx::register_plugins(*BrickCtx, AGXCache); + return BrickCtx; + } + + Brick::Core::ObjectPtr LoadModelFromFile( + const std::string& BrickFile, std::shared_ptr AGXCache) + { + auto Context = CreateBrickContext(AGXCache); + if (Context == nullptr) + { + UE_LOG(LogAGX, Error, TEXT("Error Creating Brick Context")); + return nullptr; + } + + auto LoadedModel = Brick::Core::Api::loadModelFromFile(BrickFile, {}, *Context); + + if (Context->hasErrors()) + { + LoadedModel = nullptr; + for (auto Error : Context->getErrors()) + UE_LOG(LogAGX, Error, TEXT("Error in Brick Context : %d"), Error->getErrorCode()); + + return nullptr; + } + + return LoadedModel; + } +} + +bool FAGXSimObjectsReader::ReadBrickFile( + const FString& Filename, FSimulationObjectCollection& OutSimObjects) +{ + using namespace AGXSimObjectsReader_helpers; + std::shared_ptr AGXCache; + Brick::Core::ObjectPtr BrickModel = LoadModelFromFile(Convert(Filename), AGXCache); + if (BrickModel == nullptr) + { + UE_LOG( + LogAGX, Error, + TEXT("Could not read Brick file '%s'. The Log category LogAGXDynamics may include more " + "details."), + *Filename); + return false; + } + + agxSDK::SimulationRef Simulation {new agxSDK::Simulation()}; + BrickAgx::BrickToAgxMapper Mapper(Simulation, Convert(Filename), AGXCache); + agxSDK::AssemblyRef AssemblyAGX = Mapper.mapObject(BrickModel); + + Simulation->add(AssemblyAGX); + ::ReadAll(*Simulation, Filename, OutSimObjects); + return true; +} diff --git a/Source/AGXUnrealBarrier/Private/Brick/Brick.cpp b/Source/AGXUnrealBarrier/Private/Brick/Brick.cpp deleted file mode 100644 index 0c14ff035..000000000 --- a/Source/AGXUnrealBarrier/Private/Brick/Brick.cpp +++ /dev/null @@ -1,128 +0,0 @@ -// Copyright 2024, Algoryx Simulation AB. - -#include "Brick/Brick.h" - -// AGX Dynamics for Unreal includes. -#include "TypeConversions.h" - -// Brick includes. -#include "Brick/brick/BrickContext.h" -#include "Brick/brick/BrickContextInternal.h" -#include "Brick/Brick/BrickCoreApi.h" -#include "Brick/brickagx/AgxCache.h" -#include "Brick/brickagx/BrickAgxApi.h" -#include "Brick/Math/Math_all.h" -#include "Brick/Physics/Physics_all.h" -#include "Brick/Physics1D/Physics1D_all.h" -#include "Brick/Physics3D/Physics3D_all.h" -#include "Brick/DriveTrain/DriveTrain_all.h" -#include "Brick/Robotics/Robotics_all.h" -#include "Brick/Simulation/Simulation_all.h" -#include "Brick/Vehicles/Vehicles_all.h" -#include "Brick/Terrain/Terrain_all.h" -#include "Brick/Visuals/Visuals_all.h" -#include "Brick/Urdf/Urdf_all.h" - -// AGX Dynamics includes. -#include "BeginAGXIncludes.h" -#include "EndAGXIncludes.h" - -#include -#include -#include - -namespace -{ - class BrickUnrealMapper - { - public: - std::shared_ptr AGXCache; - - void MapModel(const Brick::Core::ObjectPtr Model) - { - // Todo. - UE_LOG(LogTemp, Warning, TEXT("MapModel called!")); - if (Model->is()) - { - auto System = std::dynamic_pointer_cast(Model); - UE_LOG( - LogTemp, Warning, TEXT("MapModel was given a System %s."), - *Convert(System->getName())); - } - else if (Model->is()) - { - auto Body = Model->as>(); - UE_LOG( - LogTemp, Warning, TEXT("MapModel was given a Brick Body %s"), - *Convert(Body->getName())); - } - } - - private: - }; - - std::shared_ptr CreateBrickContext(BrickUnrealMapper& Mapper) - { - auto BrickCtx = std::make_shared( - std::vector({"C:/Users/Admin/git/agxunreal/AGXUnrealDev/Plugins/AGXUnreal/" - "Source/ThirdParty/agx/brickbundles"})); - - auto InternalContext = Brick::Core::Api::BrickContextInternal::fromContext(*BrickCtx); - auto EvalCtx = InternalContext->evaluatorContext().get(); - - Math_register_factories(EvalCtx); - Physics_register_factories(EvalCtx); - Physics1D_register_factories(EvalCtx); - Physics3D_register_factories(EvalCtx); - DriveTrain_register_factories(EvalCtx); - Robotics_register_factories(EvalCtx); - Simulation_register_factories(EvalCtx); - Vehicles_register_factories(EvalCtx); - Terrain_register_factories(EvalCtx); - Visuals_register_factories(EvalCtx); - Urdf_register_factories(EvalCtx); - - BrickAgx::register_plugins(*BrickCtx, Mapper.AGXCache); - return BrickCtx; - } - - Brick::Core::ObjectPtr ParseBrickSource( - const std::filesystem::path& BrickFile, BrickUnrealMapper& Mapper) - { - auto Context = CreateBrickContext(Mapper); - if (Context == nullptr) - { - UE_LOG(LogTemp, Error, TEXT("Error Creating Brick Context")); - return nullptr; - } - - auto LoadedModel = Brick::Core::Api::loadModelFromFile(BrickFile, {}, *Context); - - if (Context->hasErrors()) - { - LoadedModel = nullptr; - for (auto Error : Context->getErrors()) - UE_LOG(LogTemp, Error, TEXT("Error in Brick Context : %d"), Error->getErrorCode()); - } - - return LoadedModel; - } - - void ImportBrickFile(const std::filesystem::path& BrickFile) - { - BrickUnrealMapper Mapper; - auto LoadedModel = ParseBrickSource(BrickFile, Mapper); - if (LoadedModel == nullptr) - { - UE_LOG(LogTemp, Error, TEXT("Unable to load model %s"), *Convert(BrickFile.string())); - return; - } - - Mapper.MapModel(LoadedModel); - } -} - -void FBrick::Test() -{ - ::ImportBrickFile("C:/Users/Admin/Desktop/double_pendulum.brick"); -} diff --git a/Source/AGXUnrealBarrier/Public/AGXSimObjectsReader.h b/Source/AGXUnrealBarrier/Public/AGXSimObjectsReader.h index 30e295043..909d50cc5 100644 --- a/Source/AGXUnrealBarrier/Public/AGXSimObjectsReader.h +++ b/Source/AGXUnrealBarrier/Public/AGXSimObjectsReader.h @@ -34,4 +34,7 @@ namespace FAGXSimObjectsReader AGXUNREALBARRIER_API bool ReadUrdf( const FString& UrdfFilePath, const FString& UrdfPackagePath, const TArray& InitJoints, FSimulationObjectCollection& OutSimObjects); + + AGXUNREALBARRIER_API bool ReadBrickFile( + const FString& BrickFilePath, FSimulationObjectCollection& OutSimObjects); }; diff --git a/Source/AGXUnrealBarrier/Public/Brick/Brick.h b/Source/AGXUnrealBarrier/Public/Brick/Brick.h deleted file mode 100644 index 1f4cfa4fa..000000000 --- a/Source/AGXUnrealBarrier/Public/Brick/Brick.h +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright 2024, Algoryx Simulation AB. - -#pragma once - -// Unreal Engine includes. -#include "CoreMinimal.h" - -// Experimental only, donīt merge! -class AGXUNREALBARRIER_API FBrick -{ -public: - static void Test(); -}; diff --git a/Source/AGXUnrealEditor/Private/AGX_ImporterToBlueprint.cpp b/Source/AGXUnrealEditor/Private/AGX_ImporterToBlueprint.cpp index 9d6aed6a8..69ceddeff 100644 --- a/Source/AGXUnrealEditor/Private/AGX_ImporterToBlueprint.cpp +++ b/Source/AGXUnrealEditor/Private/AGX_ImporterToBlueprint.cpp @@ -568,6 +568,23 @@ namespace return EImportResult::Success; } + EImportResult AddComponentsFromBrick( + AActor& ImportedActor, FAGX_SimObjectsImporterHelper& Helper) + { + FSimulationObjectCollection SimObjects; + if (!FAGXSimObjectsReader::ReadBrickFile(Helper.SourceFilePath, SimObjects)) + { + return EImportResult::ErrorReadingSourceFile; + } + + if (!AddAllComponents(ImportedActor, SimObjects, Helper)) + { + return EImportResult::ErrorDuringInstantiations; + } + + return EImportResult::Success; + } + EImportResult AddComponentsFromUrdf( AActor& ImportedActor, FAGX_SimObjectsImporterHelper& Helper, const FAGX_ImportSettings& ImportSettings) @@ -623,10 +640,20 @@ namespace RootActorContainer->SetRootComponent(ActorRootComponent); #endif - EImportResult Result = - ImportSettings.ImportType == EAGX_ImportType::Urdf - ? AddComponentsFromUrdf(*RootActorContainer, Helper, ImportSettings) - : AddComponentsFromAGXArchive(*RootActorContainer, Helper); + + EImportResult Result {EImportResult::UnknownFailure}; + switch (ImportSettings.ImportType) + { + case EAGX_ImportType::Agx: + Result = AddComponentsFromAGXArchive(*RootActorContainer, Helper); + break; + case EAGX_ImportType::Brick: + Result = AddComponentsFromBrick(*RootActorContainer, Helper); + break; + case EAGX_ImportType::Urdf: + Result = AddComponentsFromUrdf(*RootActorContainer, Helper, ImportSettings); + break; + } return {Result, RootActorContainer}; } diff --git a/Source/AGXUnrealEditor/Private/Brick/AGX_BrickAssetCustomization.cpp b/Source/AGXUnrealEditor/Private/Brick/AGX_BrickAssetCustomization.cpp index 411e31f38..accdfcb1a 100644 --- a/Source/AGXUnrealEditor/Private/Brick/AGX_BrickAssetCustomization.cpp +++ b/Source/AGXUnrealEditor/Private/Brick/AGX_BrickAssetCustomization.cpp @@ -29,6 +29,9 @@ #define LOCTEXT_NAMESPACE "FAGX_BrickAssetCustomization" +// IMPORTANT: This is all experimental test code used durint development. Remove before merge to +// master. + TSharedRef FAGX_BrickAssetCustomization::MakeInstance() { return MakeShareable(new FAGX_BrickAssetCustomization); @@ -117,7 +120,7 @@ void FAGX_BrickAssetCustomization::CustomizeDetails(IDetailLayoutBuilder& InDeta FReply FAGX_BrickAssetCustomization::OnImportButtonClicked() { AGX_CHECK(DetailBuilder); - FBrick::Test(); + // FBrick::Test(); return FReply::Handled(); } @@ -140,7 +143,8 @@ namespace return UPackageTools::SanitizePackageName(Path + "/" + Name); } - UBlueprint* CreateBlueprint(const FString& Name, AActor*& OutTemplate, bool SetNonDefaultMass = false) + UBlueprint* CreateBlueprint( + const FString& Name, AActor*& OutTemplate, bool SetNonDefaultMass = false) { GEditor->SelectNone(false, false); FString PackagePath = CreatePackagePath("Blueprint", Name); @@ -150,8 +154,7 @@ namespace GEditor->FindActorFactoryByClass(UActorFactoryEmptyActor::StaticClass()); FAssetData EmptyActorAssetData = FAssetData(Factory->GetDefaultActorClass(FAssetData())); UObject* EmptyActorAsset = EmptyActorAssetData.GetAsset(); - OutTemplate = - FActorFactoryAssetProxy::AddActorForAsset(EmptyActorAsset, false); + OutTemplate = FActorFactoryAssetProxy::AddActorForAsset(EmptyActorAsset, false); check(OutTemplate != nullptr); /// \todo Test and return false instead of check? OutTemplate->SetFlags(RF_Transactional); OutTemplate->SetActorLabel(Name); @@ -175,7 +178,7 @@ namespace UBlueprint* Blueprint = FKismetEditorUtilities::CreateBlueprintFromActor( Package->GetName(), OutTemplate, Params); - //RootActorContainer->Destroy(); + // RootActorContainer->Destroy(); FAGX_ObjectUtilities::SaveAsset(*Blueprint); // Child Blueprint @@ -185,9 +188,8 @@ namespace UBlueprint* BlueprintChild = FKismetEditorUtilities::CreateBlueprint( Blueprint->GeneratedClass, PackageChild, FName(AssetNameChild), - EBlueprintType::BPTYPE_Normal, - UBlueprint::StaticClass(), UBlueprintGeneratedClass::StaticClass(), - FName("HackyCodeCO")); + EBlueprintType::BPTYPE_Normal, UBlueprint::StaticClass(), + UBlueprintGeneratedClass::StaticClass(), FName("HackyCodeCO")); GEngine->BroadcastLevelActorListChanged(); @@ -227,8 +229,7 @@ FReply FAGX_BrickAssetCustomization::OnUpdateBlueprintButtonClicked() FBlueprintEditorUtils::MarkBlueprintAsModified(BlueprintBase); Ri->MarkPackageDirty(); Ri->PostEditChange(); - FPropertyChangedEvent PropertyChangedEvent( - Ri->GetClass()->FindPropertyByName( + FPropertyChangedEvent PropertyChangedEvent(Ri->GetClass()->FindPropertyByName( GET_MEMBER_NAME_CHECKED(UAGX_RigidBodyComponent, Mass))); Ri->PostEditChangeProperty(PropertyChangedEvent); } @@ -250,8 +251,7 @@ FReply FAGX_BrickAssetCustomization::OnCopyFromOtherBlueprintButtonClicked() const EditorUtilities::ECopyOptions::Type CopyOptions = (EditorUtilities::ECopyOptions::Type)( EditorUtilities::ECopyOptions::PropagateChangesToArchetypeInstances); EditorUtilities::CopyActorProperties(TemplateActor, CDO, CopyOptions); -#endif - +#endif UBlueprint* BlueprintA = LoadObject(nullptr, TEXT("/Game/BlueprintA")); UBlueprint* BlueprintB = LoadObject(nullptr, TEXT("/Game/BlueprintB")); @@ -265,7 +265,7 @@ FReply FAGX_BrickAssetCustomization::OnCopyFromOtherBlueprintButtonClicked() FAGX_ObjectUtilities::SaveAsset(*Replacement); #endif -#if 0 //Crash when trying to open Blueprint afterwords. +#if 0 // Crash when trying to open Blueprint afterwords. UEngine::CopyPropertiesForUnrelatedObjects(BlueprintA, BlueprintB); #endif @@ -280,12 +280,8 @@ FReply FAGX_BrickAssetCustomization::OnCopyFromOtherBlueprintButtonClicked() AActor* CDOB = CastChecked(BlueprintA->GeneratedClass->GetDefaultObject()); EditorUtilities::CopyActorProperties(CDOA, CDOB); #endif - - - - -#if 0 // This will crash after CopyPropertiesForUnrelatedObjects. +#if 0 // This will crash after CopyPropertiesForUnrelatedObjects. UBlueprint* NewBlueprint = ::CreateBlueprint("BaseBlueprint_new"); // Now we have BlueprintBase and NewBlueprint which are almost identical. // NewBlueprint has another mass for the RigidBody. diff --git a/Source/AGXUnrealEditor/Private/Utilities/AGX_ImportUtilities.cpp b/Source/AGXUnrealEditor/Private/Utilities/AGX_ImportUtilities.cpp index effa105c4..abddb0a1c 100644 --- a/Source/AGXUnrealEditor/Private/Utilities/AGX_ImportUtilities.cpp +++ b/Source/AGXUnrealEditor/Private/Utilities/AGX_ImportUtilities.cpp @@ -600,6 +600,10 @@ EAGX_ImportType FAGX_ImportUtilities::GetFrom(const FString& FilePath) { return EAGX_ImportType::Agx; } + else if (FileExtension.Equals("brick")) + { + return EAGX_ImportType::Brick; + } else if (FileExtension.Equals("urdf")) { return EAGX_ImportType::Urdf; diff --git a/Source/AGXUnrealEditor/Private/Widgets/AGX_ImportDialog.cpp b/Source/AGXUnrealEditor/Private/Widgets/AGX_ImportDialog.cpp index c45c4d056..93b1b69b1 100644 --- a/Source/AGXUnrealEditor/Private/Widgets/AGX_ImportDialog.cpp +++ b/Source/AGXUnrealEditor/Private/Widgets/AGX_ImportDialog.cpp @@ -51,7 +51,7 @@ namespace AGX_ImportDialog_helpers void SAGX_ImportDialog::Construct(const FArguments& InArgs) { - FileTypes = ".agx;*.urdf"; + FileTypes = ".agx;*.brick;*.urdf"; // clang-format off ChildSlot diff --git a/Source/AGXUnrealEditor/Public/AGX_ImportEnums.h b/Source/AGXUnrealEditor/Public/AGX_ImportEnums.h index 7e4b300a2..fda06429a 100644 --- a/Source/AGXUnrealEditor/Public/AGX_ImportEnums.h +++ b/Source/AGXUnrealEditor/Public/AGX_ImportEnums.h @@ -13,9 +13,12 @@ enum class EAGX_ImportType : uint8 /** Imported type is Invalid. */ Invalid, - /** Imported type is AGX Dynamics Archive. */ + /** Imported type is an AGX Dynamics Archive. */ Agx, - /** Imported type is URDF (Unified Robotic Description Format) model. */ + /** Imported type is a Brick model. */ + Brick, + + /** Imported type is a URDF (Unified Robotic Description Format) model. */ Urdf }; From 33d9b08014c4d888206c0c907fd3007363089d5a Mon Sep 17 00:00:00 2001 From: Josef Date: Wed, 6 Nov 2024 08:02:59 +0100 Subject: [PATCH 11/31] Remove deleted header include [ci skip] --- .../Private/Brick/AGX_BrickAssetCustomization.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/Source/AGXUnrealEditor/Private/Brick/AGX_BrickAssetCustomization.cpp b/Source/AGXUnrealEditor/Private/Brick/AGX_BrickAssetCustomization.cpp index accdfcb1a..d7a5745b7 100644 --- a/Source/AGXUnrealEditor/Private/Brick/AGX_BrickAssetCustomization.cpp +++ b/Source/AGXUnrealEditor/Private/Brick/AGX_BrickAssetCustomization.cpp @@ -5,7 +5,6 @@ // AGX Dynamics for Unreal includes. #include "AGX_RigidBodyComponent.h" #include "Brick/AGX_BrickAsset.h" -#include "Brick/Brick.h" #include "Utilities/AGX_EditorUtilities.h" #include "Utilities/AGX_ImportUtilities.h" From bf022002ec026e39de1ce8e541d67f48a069f1db Mon Sep 17 00:00:00 2001 From: Josef Date: Wed, 6 Nov 2024 14:45:42 +0100 Subject: [PATCH 12/31] Add missing include --- Source/AGXUnrealBarrier/Private/AGXSimObjectsReader.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Source/AGXUnrealBarrier/Private/AGXSimObjectsReader.cpp b/Source/AGXUnrealBarrier/Private/AGXSimObjectsReader.cpp index 9f5b20b8e..e8d66081c 100644 --- a/Source/AGXUnrealBarrier/Private/AGXSimObjectsReader.cpp +++ b/Source/AGXUnrealBarrier/Private/AGXSimObjectsReader.cpp @@ -67,6 +67,9 @@ #include #include "EndAGXIncludes.h" +// Unreal Engine inludes. +#include "Misc/Paths.h" + namespace { void ReadShapes( From 35b49114b08fa4d023943145984eb72590e4ea84 Mon Sep 17 00:00:00 2001 From: Josef Date: Thu, 7 Nov 2024 12:44:20 +0100 Subject: [PATCH 13/31] Add internal brick_experiments header --- .../Private/Brick/Brick_Experiments.h | 178 ++++++++++++++++++ 1 file changed, 178 insertions(+) create mode 100644 Source/AGXUnrealBarrier/Private/Brick/Brick_Experiments.h diff --git a/Source/AGXUnrealBarrier/Private/Brick/Brick_Experiments.h b/Source/AGXUnrealBarrier/Private/Brick/Brick_Experiments.h new file mode 100644 index 000000000..880a87624 --- /dev/null +++ b/Source/AGXUnrealBarrier/Private/Brick/Brick_Experiments.h @@ -0,0 +1,178 @@ +#pragma once + +// Never merge this to master. Only for development experiments/tests. +// TODO @todo (just to make this file be found) + +// Brick includes. +#include "Brick/brick/BrickContext.h" +#include "Brick/brick/BrickContextInternal.h" +#include "Brick/Brick/BrickCoreApi.h" +#include "Brick/brickagx/AgxCache.h" +#include "Brick/brickagx/BrickAgxApi.h" +#include "Brick/brickagx/BrickToAgxMapper.h" +#include "Brick/brickagx/InputSignalListener.h" +#include "Brick/brickagx/Signals.h" +#include "Brick/Math/Math_all.h" +#include "Brick/Physics/Physics_all.h" +#include "Brick/Physics1D/Physics1D_all.h" +#include "Brick/Physics3D/Physics3D_all.h" +#include "Brick/DriveTrain/DriveTrain_all.h" +#include "Brick/Robotics/Robotics_all.h" +#include "Brick/Simulation/Simulation_all.h" +#include "Brick/Vehicles/Vehicles_all.h" +#include "Brick/Terrain/Terrain_all.h" +#include "Brick/Visuals/Visuals_all.h" +#include "Brick/Urdf/Urdf_all.h" + + +std::shared_ptr CreateBrickContext( + std::shared_ptr AGXCache) +{ + const FString BrickBundlesPath = FPaths::Combine( + FAGX_Environment::GetPluginSourcePath(), "Thirdparty", "agx", "brickbundles"); + auto BrickCtx = std::make_shared( + std::vector({Convert(BrickBundlesPath)})); + + auto InternalContext = Brick::Core::Api::BrickContextInternal::fromContext(*BrickCtx); + auto EvalCtx = InternalContext->evaluatorContext().get(); + + Math_register_factories(EvalCtx); + Physics_register_factories(EvalCtx); + Physics1D_register_factories(EvalCtx); + Physics3D_register_factories(EvalCtx); + DriveTrain_register_factories(EvalCtx); + Robotics_register_factories(EvalCtx); + Simulation_register_factories(EvalCtx); + Vehicles_register_factories(EvalCtx); + Terrain_register_factories(EvalCtx); + Visuals_register_factories(EvalCtx); + Urdf_register_factories(EvalCtx); + + BrickAgx::register_plugins(*BrickCtx, AGXCache); + return BrickCtx; +} + +Brick::Core::ObjectPtr LoadModelFromFile( + const std::string& BrickFile, std::shared_ptr AGXCache) +{ + auto Context = CreateBrickContext(AGXCache); + if (Context == nullptr) + { + UE_LOG(LogAGX, Error, TEXT("Error Creating Brick Context")); + return nullptr; + } + + auto LoadedModel = Brick::Core::Api::loadModelFromFile(BrickFile, {}, *Context); + + if (Context->hasErrors()) + { + LoadedModel = nullptr; + for (auto Error : Context->getErrors()) + UE_LOG(LogAGX, Error, TEXT("Error in Brick Context : %d"), Error->getErrorCode()); + + return nullptr; + } + + return LoadedModel; +} + +static Brick::Core::ObjectPtr BrickModel = nullptr; +static std::shared_ptr AGXCache = nullptr; +static std::vector> SignalInputs; +static agxSDK::AssemblyRef Assembly; +static std::shared_ptr InputSignalListener; + +void findAllSignalInputs( + Brick::Physics3D::System* System, + std::vector>& OutSignalInputs) +{ + for (auto& Subsystem : System->getValues()) + { + findAllSignalInputs(System, OutSignalInputs); + } + + for (auto& SignalInput : System->getValues()) + { + SignalInputs.push_back(SignalInput); + } +} + +void BrickExperimentInit(agxSDK::Simulation* Simulation) +{ + const FString Filename = "C:/Users/Admin/Desktop/inverted_pendulum.brick"; + BrickModel = LoadModelFromFile(Convert(Filename), AGXCache); + if (BrickModel == nullptr) + { + UE_LOG( + LogAGX, Error, + TEXT("Could not read Brick file '%s'. The Log category LogAGXDynamics may include more " + "details."), + *Filename); + return; + } + UE_LOG(LogTemp, Warning, TEXT("BrickExperimentInit %d"), Simulation != nullptr); + + auto System = std::dynamic_pointer_cast(BrickModel); + findAllSignalInputs(System.get(), SignalInputs); + + for (auto& Constraint : Simulation->getConstraints()) + { + for (size_t i = 0; i < Constraint->getNumSecondaryConstraints(); i++) + { + agx::ElementaryConstraint* ElemC = Constraint->getSecondaryConstraint(i); + agx::TargetSpeedController* TargetSC = dynamic_cast(ElemC); + if (TargetSC == nullptr) + continue; + + TargetSC->setName(agx::Name("PendulumScene.cart_motor")); + UE_LOG( + LogTemp, Warning, TEXT("Elem Constraint %s is targetspeed: %d"), + *Convert(ElemC->getName()), TargetSC != nullptr); + + Assembly = new agxSDK::Assembly(); + Assembly->add(Constraint); + } + } + + if (Assembly->getConstraints().size() == 0) + return; + + // Hacky hacky hack-hack start + agxPowerLine::PowerLineRef BrickPowerLineHack = new agxPowerLine::PowerLine(); + BrickPowerLineHack->setName(agx::Name("BrickPowerLine")); + Assembly->add(BrickPowerLineHack); + // Hacky hacky hack-hack end + + InputSignalListener = std::make_shared(Assembly); + Simulation->add(InputSignalListener.get()); +} + +void BrickExperimentCheck(agxSDK::Simulation* Simulation) +{ + UE_LOG(LogTemp, Warning, TEXT("BrickExperimentCheck %d"), Simulation != nullptr); + if (SignalInputs.size() == 0) + { + UE_LOG(LogTemp, Error, TEXT("Num signal inputs found in check: %d"), SignalInputs.size()); + return; + } + + std::shared_ptr MyCoolInputSignal; + + for (std::shared_ptr& SignalInput : SignalInputs) + { + auto LinearVelInput = + std::dynamic_pointer_cast( + SignalInput); + if (LinearVelInput == nullptr) + continue; + + UE_LOG( + LogTemp, Warning, TEXT("Found LinearVelocityMotorVelocityInput! %s"), + *Convert(LinearVelInput->getName())); + UE_LOG( + LogTemp, Warning, TEXT("Motor name: %s"), *Convert(LinearVelInput->motor()->getName())); + + MyCoolInputSignal = Brick::Physics::Signals::RealInputSignal::create(-1.0, LinearVelInput); + BrickAgx::Signals::sendInputSignal(MyCoolInputSignal); + } +} From c027d8e49dcca1339790ba53e29e79f8a0945579 Mon Sep 17 00:00:00 2001 From: Josef Date: Thu, 7 Nov 2024 13:05:45 +0100 Subject: [PATCH 14/31] Import and assign name to secondary constraints --- .../Private/Constraints/AGX_ConstraintController.cpp | 6 ++++++ .../Public/Constraints/AGX_ConstraintController.h | 3 +++ .../Constraints/ControllerConstraintBarriers.cpp | 12 ++++++++++++ .../Constraints/ControllerConstraintBarriers.h | 3 +++ 4 files changed, 24 insertions(+) diff --git a/Source/AGXUnreal/Private/Constraints/AGX_ConstraintController.cpp b/Source/AGXUnreal/Private/Constraints/AGX_ConstraintController.cpp index 3585f4d0e..007902f1d 100644 --- a/Source/AGXUnreal/Private/Constraints/AGX_ConstraintController.cpp +++ b/Source/AGXUnreal/Private/Constraints/AGX_ConstraintController.cpp @@ -39,6 +39,7 @@ FAGX_ConstraintController& FAGX_ConstraintController::operator=( SpookDamping = Other.SpookDamping; ForceRange = Other.ForceRange; bRotational = Other.bRotational; + Name = Other.Name; return *this; } @@ -201,6 +202,7 @@ void FAGX_ConstraintController::UpdateNativeProperties() NativeBarrier->SetCompliance(Compliance); NativeBarrier->SetSpookDamping(SpookDamping); NativeBarrier->SetForceRange(ForceRange); + NativeBarrier->SetName(Name); UpdateNativePropertiesImpl(); } @@ -224,10 +226,14 @@ void FAGX_ConstraintController::CopyFrom( if (ForceOverwriteInstances || Instance->ForceRange == ForceRange) Instance->ForceRange = Source.GetForceRange(); + + if (ForceOverwriteInstances || Instance->Name == Name) + Instance->Name = Source.GetName(); } bEnable = Source.GetEnable(); Compliance = Source.GetCompliance(); SpookDamping = Source.GetSpookDamping(); ForceRange = Source.GetForceRange(); + Name = Source.GetName(); } diff --git a/Source/AGXUnreal/Public/Constraints/AGX_ConstraintController.h b/Source/AGXUnreal/Public/Constraints/AGX_ConstraintController.h index 5f8b58639..2473bd000 100644 --- a/Source/AGXUnreal/Public/Constraints/AGX_ConstraintController.h +++ b/Source/AGXUnreal/Public/Constraints/AGX_ConstraintController.h @@ -90,6 +90,9 @@ struct AGXUNREAL_API FAGX_ConstraintController double GetForce(); + UPROPERTY(EditAnywhere, Category = "AGX Constraint Controller") + FString Name; + /** * Handle serialization backwards compatibility. May be overridden by subclasses as long as they * call Super::Serialize. diff --git a/Source/AGXUnrealBarrier/Private/Constraints/ControllerConstraintBarriers.cpp b/Source/AGXUnrealBarrier/Private/Constraints/ControllerConstraintBarriers.cpp index 247aaef2f..f9d25256e 100644 --- a/Source/AGXUnrealBarrier/Private/Constraints/ControllerConstraintBarriers.cpp +++ b/Source/AGXUnrealBarrier/Private/Constraints/ControllerConstraintBarriers.cpp @@ -111,6 +111,18 @@ double FConstraintControllerBarrier::GetForce() const return NativeRef->Native->getCurrentForce(); } +void FConstraintControllerBarrier::SetName(const FString& Name) +{ + check(HasNative()); + NativeRef->Native->setName(Convert(Name)); +} + +FString FConstraintControllerBarrier::GetName() const +{ + check(HasNative()); + return Convert(NativeRef->Native->getName()); +} + namespace { template diff --git a/Source/AGXUnrealBarrier/Public/Constraints/ControllerConstraintBarriers.h b/Source/AGXUnrealBarrier/Public/Constraints/ControllerConstraintBarriers.h index d2f757ef2..daa5bc490 100644 --- a/Source/AGXUnrealBarrier/Public/Constraints/ControllerConstraintBarriers.h +++ b/Source/AGXUnrealBarrier/Public/Constraints/ControllerConstraintBarriers.h @@ -39,6 +39,9 @@ class AGXUNREALBARRIER_API FConstraintControllerBarrier double GetForce() const; + void SetName(const FString& Name); + FString GetName() const; + protected: FConstraintControllerBarrier() = default; FConstraintControllerBarrier(std::unique_ptr Native); From 1bb97dde0d4c9e1c9ba21ea6190175e6fd38fe16 Mon Sep 17 00:00:00 2001 From: Josef Date: Thu, 7 Nov 2024 16:40:50 +0100 Subject: [PATCH 15/31] Add brick inputs to SimObjectCollection --- .../AGXCommon/Public/Brick/AGX_BrickInputs.h | 36 ++++++++++++++++ .../Private/AGXSimObjectsReader.cpp | 6 +++ .../Private/SimulationObjectCollection.cpp | 10 +++++ .../Private/Utilities/BrickUtilities.cpp | 43 +++++++++++++++++++ .../Private/Utilities/BrickUtilities.h | 21 +++++++++ .../Private/Utilities/ROS2Utilities.h | 2 + .../Public/SimulationObjectCollection.h | 8 +++- 7 files changed, 125 insertions(+), 1 deletion(-) create mode 100644 Source/AGXCommon/Public/Brick/AGX_BrickInputs.h create mode 100644 Source/AGXUnrealBarrier/Private/Utilities/BrickUtilities.cpp create mode 100644 Source/AGXUnrealBarrier/Private/Utilities/BrickUtilities.h diff --git a/Source/AGXCommon/Public/Brick/AGX_BrickInputs.h b/Source/AGXCommon/Public/Brick/AGX_BrickInputs.h new file mode 100644 index 000000000..af59450d0 --- /dev/null +++ b/Source/AGXCommon/Public/Brick/AGX_BrickInputs.h @@ -0,0 +1,36 @@ +// Copyright 2024, Algoryx Simulation AB. + +#pragma once + +// Unreal Engine includes. +#include "CoreMinimal.h" +#include "UObject/Class.h" + +#include "AGX_BrickInputs.generated.h" + +USTRUCT(BlueprintType) +struct AGXCOMMON_API FPLX_Input +{ + GENERATED_BODY() + + FPLX_Input() = default; + explicit FPLX_Input(const FString& InName) + : Name(InName) + { + } + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "PLX") + FString Name; +}; + +USTRUCT(BlueprintType) +struct AGXCOMMON_API FPLX_LinearVelocityMotorVelocityInput : public FPLX_Input +{ + GENERATED_BODY() + + FPLX_LinearVelocityMotorVelocityInput() = default; + explicit FPLX_LinearVelocityMotorVelocityInput(const FString& InName) + : FPLX_Input(InName) + { + } +}; diff --git a/Source/AGXUnrealBarrier/Private/AGXSimObjectsReader.cpp b/Source/AGXUnrealBarrier/Private/AGXSimObjectsReader.cpp index e8d66081c..027187507 100644 --- a/Source/AGXUnrealBarrier/Private/AGXSimObjectsReader.cpp +++ b/Source/AGXUnrealBarrier/Private/AGXSimObjectsReader.cpp @@ -14,6 +14,7 @@ #include "Shapes/SphereShapeBarrier.h" #include "SimulationObjectCollection.h" #include "TypeConversions.h" +#include "Utilities/BrickUtilities.h" // AGX Dynamics includes. #include "BeginAGXIncludes.h" @@ -725,5 +726,10 @@ bool FAGXSimObjectsReader::ReadBrickFile( Simulation->add(AssemblyAGX); ::ReadAll(*Simulation, Filename, OutSimObjects); + + // Read Brick inputs. + auto System = std::dynamic_pointer_cast(BrickModel); + OutSimObjects.GetPLXInputs() = FBrickUtilities::GetInputs(System.get()); + return true; } diff --git a/Source/AGXUnrealBarrier/Private/SimulationObjectCollection.cpp b/Source/AGXUnrealBarrier/Private/SimulationObjectCollection.cpp index e44f7832b..b53a287a8 100644 --- a/Source/AGXUnrealBarrier/Private/SimulationObjectCollection.cpp +++ b/Source/AGXUnrealBarrier/Private/SimulationObjectCollection.cpp @@ -288,3 +288,13 @@ const TArray& FSimulationObjectCollection::GetTracks() const { return Tracks; } + +TArray& FSimulationObjectCollection::GetPLXInputs() +{ + return PLXInputs; +} + +const TArray& FSimulationObjectCollection::GetPLXInputs() const +{ + return PLXInputs; +} diff --git a/Source/AGXUnrealBarrier/Private/Utilities/BrickUtilities.cpp b/Source/AGXUnrealBarrier/Private/Utilities/BrickUtilities.cpp new file mode 100644 index 000000000..bcae52755 --- /dev/null +++ b/Source/AGXUnrealBarrier/Private/Utilities/BrickUtilities.cpp @@ -0,0 +1,43 @@ +// Copyright 2024, Algoryx Simulation AB. + +#include "Utilities/BrickUtilities.h" + +// Brick includes. +#include "Brick/Physics3D/Physics3D_all.h" +#include + +// Standard library includes. +#include + +namespace BrickUtilities_helpers +{ + void GetInputs(Brick::Physics3D::System* System, TArray& OutInputs) + { + if (System == nullptr) + return; + + for (auto& Subsystem : System->getValues()) + { + GetInputs(System, OutInputs); + } + + for (auto& Input : System->getValues()) + { + if (auto LvmvI = std::dynamic_pointer_cast< + Brick::Physics3D::Signals::LinearVelocityMotorVelocityInput>(Input)) + { + OutInputs.Add(FPLX_LinearVelocityMotorVelocityInput(Convert(LvmvI->motor()->getName()))); + continue; + } + + UE_LOG(LogAGX, Warning, TEXT("Unhandled PLX Input: %s"), *Convert(Input->getName())); + } + } +} + +TArray FBrickUtilities::GetInputs(Brick::Physics3D::System* System) +{ + TArray Inputs; + BrickUtilities_helpers::GetInputs(System, Inputs); + return Inputs; +} diff --git a/Source/AGXUnrealBarrier/Private/Utilities/BrickUtilities.h b/Source/AGXUnrealBarrier/Private/Utilities/BrickUtilities.h new file mode 100644 index 000000000..0410675fb --- /dev/null +++ b/Source/AGXUnrealBarrier/Private/Utilities/BrickUtilities.h @@ -0,0 +1,21 @@ +// Copyright 2024, Algoryx Simulation AB. + +#pragma once + +// AGX Dynamics for Unreal includes. +#include "AGX_LogCategory.h" +#include "Brick/AGX_BrickInputs.h" + +namespace Brick +{ + namespace Physics3D + { + class System; + } +} + +class FBrickUtilities +{ +public: + static TArray GetInputs(Brick::Physics3D::System* System); +}; diff --git a/Source/AGXUnrealBarrier/Private/Utilities/ROS2Utilities.h b/Source/AGXUnrealBarrier/Private/Utilities/ROS2Utilities.h index 9a55aa20e..897930311 100644 --- a/Source/AGXUnrealBarrier/Private/Utilities/ROS2Utilities.h +++ b/Source/AGXUnrealBarrier/Private/Utilities/ROS2Utilities.h @@ -1,3 +1,5 @@ +// Copyright 2024, Algoryx Simulation AB. + #pragma once namespace AGX_ROS2Utilities diff --git a/Source/AGXUnrealBarrier/Public/SimulationObjectCollection.h b/Source/AGXUnrealBarrier/Public/SimulationObjectCollection.h index 0619dfe57..b9f8ff136 100644 --- a/Source/AGXUnrealBarrier/Public/SimulationObjectCollection.h +++ b/Source/AGXUnrealBarrier/Public/SimulationObjectCollection.h @@ -3,7 +3,8 @@ #pragma once // AGX Dynamics for Unreal includes. -// For some reason, these Shapes could not be forward declared without compiler error. +// For some reason, these could not be forward declared without compiler error. +#include "Brick/AGX_BrickInputs.h" #include "Shapes/BoxShapeBarrier.h" #include "Shapes/CylinderShapeBarrier.h" #include "Shapes/CapsuleShapeBarrier.h" @@ -111,6 +112,9 @@ struct AGXUNREALBARRIER_API FSimulationObjectCollection TArray& GetTracks(); const TArray& GetTracks() const; + TArray& GetPLXInputs(); + const TArray& GetPLXInputs() const; + private: FSimulationObjectCollection(const FSimulationObjectCollection&) = delete; void operator=(const FSimulationObjectCollection&) = delete; @@ -142,4 +146,6 @@ struct AGXUNREALBARRIER_API FSimulationObjectCollection TArray Wires; TArray Shovels; TArray Tracks; + + TArray PLXInputs; }; From df5035e674f82b200240bd41343b2513f73e239b Mon Sep 17 00:00:00 2001 From: Josef Date: Fri, 8 Nov 2024 13:23:36 +0100 Subject: [PATCH 16/31] Rename Brick->OpenPLX and import signal as blueprint variable --- .../PLX_Inputs.h} | 16 +- .../Private/Brick/AGX_BrickAsset.cpp | 24 -- .../AGXUnreal/Public/Brick/AGX_BrickAsset.h | 27 -- .../Private/AGXSimObjectsReader.cpp | 42 +-- .../PLX_Experiments.h} | 0 .../Private/SimulationObjectCollection.cpp | 4 +- .../Private/Utilities/BrickUtilities.cpp | 43 --- .../Private/Utilities/PLXUtilities.cpp | 50 +++ .../{BrickUtilities.h => PLXUtilities.h} | 4 +- .../Public/AGXSimObjectsReader.h | 2 +- .../Public/SimulationObjectCollection.h | 8 +- .../AGXUnrealEditor/AGXUnrealEditor.Build.cs | 2 +- .../Private/AGXUnrealEditor.cpp | 11 - .../Private/AGX_ImporterToBlueprint.cpp | 119 +++---- .../Brick/AGX_BrickAssetCustomization.cpp | 305 ------------------ .../Private/Brick/AGX_BrickAssetFactory.cpp | 85 ----- .../Brick/AGX_BrickAssetTypeActions.cpp | 71 ---- .../Private/Utilities/AGX_ImportUtilities.cpp | 2 +- .../AGXUnrealEditor/Public/AGX_ImportEnums.h | 4 +- .../Brick/AGX_BrickAssetCustomization.h | 32 -- .../Public/Brick/AGX_BrickAssetFactory.h | 37 --- .../Public/Brick/AGX_BrickAssetTypeActions.h | 34 -- 22 files changed, 160 insertions(+), 762 deletions(-) rename Source/AGXCommon/Public/{Brick/AGX_BrickInputs.h => OpenPLX/PLX_Inputs.h} (67%) delete mode 100644 Source/AGXUnreal/Private/Brick/AGX_BrickAsset.cpp delete mode 100644 Source/AGXUnreal/Public/Brick/AGX_BrickAsset.h rename Source/AGXUnrealBarrier/Private/{Brick/Brick_Experiments.h => OpenPLX/PLX_Experiments.h} (100%) delete mode 100644 Source/AGXUnrealBarrier/Private/Utilities/BrickUtilities.cpp create mode 100644 Source/AGXUnrealBarrier/Private/Utilities/PLXUtilities.cpp rename Source/AGXUnrealBarrier/Private/Utilities/{BrickUtilities.h => PLXUtilities.h} (65%) delete mode 100644 Source/AGXUnrealEditor/Private/Brick/AGX_BrickAssetCustomization.cpp delete mode 100644 Source/AGXUnrealEditor/Private/Brick/AGX_BrickAssetFactory.cpp delete mode 100644 Source/AGXUnrealEditor/Private/Brick/AGX_BrickAssetTypeActions.cpp delete mode 100644 Source/AGXUnrealEditor/Public/Brick/AGX_BrickAssetCustomization.h delete mode 100644 Source/AGXUnrealEditor/Public/Brick/AGX_BrickAssetFactory.h delete mode 100644 Source/AGXUnrealEditor/Public/Brick/AGX_BrickAssetTypeActions.h diff --git a/Source/AGXCommon/Public/Brick/AGX_BrickInputs.h b/Source/AGXCommon/Public/OpenPLX/PLX_Inputs.h similarity index 67% rename from Source/AGXCommon/Public/Brick/AGX_BrickInputs.h rename to Source/AGXCommon/Public/OpenPLX/PLX_Inputs.h index af59450d0..92179ee12 100644 --- a/Source/AGXCommon/Public/Brick/AGX_BrickInputs.h +++ b/Source/AGXCommon/Public/OpenPLX/PLX_Inputs.h @@ -6,7 +6,7 @@ #include "CoreMinimal.h" #include "UObject/Class.h" -#include "AGX_BrickInputs.generated.h" +#include "PLX_Inputs.generated.h" USTRUCT(BlueprintType) struct AGXCOMMON_API FPLX_Input @@ -19,8 +19,15 @@ struct AGXCOMMON_API FPLX_Input { } - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "PLX") + virtual ~FPLX_Input() = default; + + UPROPERTY() FString Name; + + virtual UScriptStruct* GetType() const + { + return FPLX_Input::StaticStruct(); + } }; USTRUCT(BlueprintType) @@ -33,4 +40,9 @@ struct AGXCOMMON_API FPLX_LinearVelocityMotorVelocityInput : public FPLX_Input : FPLX_Input(InName) { } + + virtual UScriptStruct* GetType() const override + { + return FPLX_LinearVelocityMotorVelocityInput::StaticStruct(); + } }; diff --git a/Source/AGXUnreal/Private/Brick/AGX_BrickAsset.cpp b/Source/AGXUnreal/Private/Brick/AGX_BrickAsset.cpp deleted file mode 100644 index 774d1fc62..000000000 --- a/Source/AGXUnreal/Private/Brick/AGX_BrickAsset.cpp +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2024, Algoryx Simulation AB. - -#include "Brick/AGX_BrickAsset.h" - -// Unreal Engine includes. -#include "EditorFramework/AssetImportData.h" -#include "UObject/AssetRegistryTagsContext.h" - - -#if WITH_EDITORONLY_DATA -void UAGX_BrickAsset::GetAssetRegistryTags(FAssetRegistryTagsContext Context) const -{ - // Not sure exactly what it does. But removing it stops reimport triggering the factory from - // working. - if (AssetImportData) - { - Context.AddTag(FAssetRegistryTag( - SourceFileTagName(), AssetImportData->GetSourceData().ToJson(), - FAssetRegistryTag::TT_Hidden)); - } - - Super::GetAssetRegistryTags(Context); -} -#endif diff --git a/Source/AGXUnreal/Public/Brick/AGX_BrickAsset.h b/Source/AGXUnreal/Public/Brick/AGX_BrickAsset.h deleted file mode 100644 index 17bb39c13..000000000 --- a/Source/AGXUnreal/Public/Brick/AGX_BrickAsset.h +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2024, Algoryx Simulation AB. - -#pragma once - -// Unreal Engine includes. -#include "CoreMinimal.h" - -#include "AGX_BrickAsset.generated.h" - -UCLASS(ClassGroup = "AGX", Category = "AGX", BlueprintType, Blueprintable) -class AGXUNREAL_API UAGX_BrickAsset : public UObject -{ - GENERATED_BODY() - -public: - UPROPERTY(EditAnywhere, Category = "AGX Brick") - int Dummy; - -#if WITH_EDITORONLY_DATA - - /** The file this data table was imported from, may be empty */ - UPROPERTY(VisibleAnywhere, Instanced, Category = ImportSource) - TObjectPtr AssetImportData; - - virtual void GetAssetRegistryTags(FAssetRegistryTagsContext Context) const override; -#endif -}; diff --git a/Source/AGXUnrealBarrier/Private/AGXSimObjectsReader.cpp b/Source/AGXUnrealBarrier/Private/AGXSimObjectsReader.cpp index 027187507..d77951d6a 100644 --- a/Source/AGXUnrealBarrier/Private/AGXSimObjectsReader.cpp +++ b/Source/AGXUnrealBarrier/Private/AGXSimObjectsReader.cpp @@ -14,7 +14,7 @@ #include "Shapes/SphereShapeBarrier.h" #include "SimulationObjectCollection.h" #include "TypeConversions.h" -#include "Utilities/BrickUtilities.h" +#include "Utilities/PLXUtilities.h" // AGX Dynamics includes. #include "BeginAGXIncludes.h" @@ -31,7 +31,7 @@ #include #include -// Brick includes. +// OpenPLX includes. #include "Brick/brick/BrickContext.h" #include "Brick/brick/BrickContextInternal.h" #include "Brick/Brick/BrickCoreApi.h" @@ -652,15 +652,15 @@ bool FAGXSimObjectsReader::ReadUrdf( namespace AGXSimObjectsReader_helpers { - std::shared_ptr CreateBrickContext( + std::shared_ptr CreatePLXContext( std::shared_ptr AGXCache) { - const FString BrickBundlesPath = + const FString PLXBundlesPath = FPaths::Combine(FAGX_Environment::GetPluginSourcePath(), "Thirdparty", "agx", "brickbundles"); - auto BrickCtx = std::make_shared( - std::vector({Convert(BrickBundlesPath)})); + auto PLXCtx = std::make_shared( + std::vector({Convert(PLXBundlesPath)})); - auto InternalContext = Brick::Core::Api::BrickContextInternal::fromContext(*BrickCtx); + auto InternalContext = Brick::Core::Api::BrickContextInternal::fromContext(*PLXCtx); auto EvalCtx = InternalContext->evaluatorContext().get(); Math_register_factories(EvalCtx); @@ -675,27 +675,27 @@ namespace AGXSimObjectsReader_helpers Visuals_register_factories(EvalCtx); Urdf_register_factories(EvalCtx); - BrickAgx::register_plugins(*BrickCtx, AGXCache); - return BrickCtx; + BrickAgx::register_plugins(*PLXCtx, AGXCache); + return PLXCtx; } Brick::Core::ObjectPtr LoadModelFromFile( - const std::string& BrickFile, std::shared_ptr AGXCache) + const std::string& OpenPLXFile, std::shared_ptr AGXCache) { - auto Context = CreateBrickContext(AGXCache); + auto Context = CreatePLXContext(AGXCache); if (Context == nullptr) { - UE_LOG(LogAGX, Error, TEXT("Error Creating Brick Context")); + UE_LOG(LogAGX, Error, TEXT("Error Creating OpenPLX Context")); return nullptr; } - auto LoadedModel = Brick::Core::Api::loadModelFromFile(BrickFile, {}, *Context); + auto LoadedModel = Brick::Core::Api::loadModelFromFile(OpenPLXFile, {}, *Context); if (Context->hasErrors()) { LoadedModel = nullptr; for (auto Error : Context->getErrors()) - UE_LOG(LogAGX, Error, TEXT("Error in Brick Context : %d"), Error->getErrorCode()); + UE_LOG(LogAGX, Error, TEXT("Error in OpenPLX Context : %d"), Error->getErrorCode()); return nullptr; } @@ -704,17 +704,17 @@ namespace AGXSimObjectsReader_helpers } } -bool FAGXSimObjectsReader::ReadBrickFile( +bool FAGXSimObjectsReader::ReadOpenPLXFile( const FString& Filename, FSimulationObjectCollection& OutSimObjects) { using namespace AGXSimObjectsReader_helpers; std::shared_ptr AGXCache; - Brick::Core::ObjectPtr BrickModel = LoadModelFromFile(Convert(Filename), AGXCache); - if (BrickModel == nullptr) + Brick::Core::ObjectPtr PLXModel = LoadModelFromFile(Convert(Filename), AGXCache); + if (PLXModel == nullptr) { UE_LOG( LogAGX, Error, - TEXT("Could not read Brick file '%s'. The Log category LogAGXDynamics may include more " + TEXT("Could not read OpenPLX file '%s'. The Log category LogAGXDynamics may include more " "details."), *Filename); return false; @@ -722,13 +722,13 @@ bool FAGXSimObjectsReader::ReadBrickFile( agxSDK::SimulationRef Simulation {new agxSDK::Simulation()}; BrickAgx::BrickToAgxMapper Mapper(Simulation, Convert(Filename), AGXCache); - agxSDK::AssemblyRef AssemblyAGX = Mapper.mapObject(BrickModel); + agxSDK::AssemblyRef AssemblyAGX = Mapper.mapObject(PLXModel); Simulation->add(AssemblyAGX); ::ReadAll(*Simulation, Filename, OutSimObjects); - // Read Brick inputs. - auto System = std::dynamic_pointer_cast(BrickModel); + // Read PLX inputs. + auto System = std::dynamic_pointer_cast(PLXModel); OutSimObjects.GetPLXInputs() = FBrickUtilities::GetInputs(System.get()); return true; diff --git a/Source/AGXUnrealBarrier/Private/Brick/Brick_Experiments.h b/Source/AGXUnrealBarrier/Private/OpenPLX/PLX_Experiments.h similarity index 100% rename from Source/AGXUnrealBarrier/Private/Brick/Brick_Experiments.h rename to Source/AGXUnrealBarrier/Private/OpenPLX/PLX_Experiments.h diff --git a/Source/AGXUnrealBarrier/Private/SimulationObjectCollection.cpp b/Source/AGXUnrealBarrier/Private/SimulationObjectCollection.cpp index b53a287a8..54017f3e5 100644 --- a/Source/AGXUnrealBarrier/Private/SimulationObjectCollection.cpp +++ b/Source/AGXUnrealBarrier/Private/SimulationObjectCollection.cpp @@ -289,12 +289,12 @@ const TArray& FSimulationObjectCollection::GetTracks() const return Tracks; } -TArray& FSimulationObjectCollection::GetPLXInputs() +TArray>& FSimulationObjectCollection::GetPLXInputs() { return PLXInputs; } -const TArray& FSimulationObjectCollection::GetPLXInputs() const +const TArray>& FSimulationObjectCollection::GetPLXInputs() const { return PLXInputs; } diff --git a/Source/AGXUnrealBarrier/Private/Utilities/BrickUtilities.cpp b/Source/AGXUnrealBarrier/Private/Utilities/BrickUtilities.cpp deleted file mode 100644 index bcae52755..000000000 --- a/Source/AGXUnrealBarrier/Private/Utilities/BrickUtilities.cpp +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2024, Algoryx Simulation AB. - -#include "Utilities/BrickUtilities.h" - -// Brick includes. -#include "Brick/Physics3D/Physics3D_all.h" -#include - -// Standard library includes. -#include - -namespace BrickUtilities_helpers -{ - void GetInputs(Brick::Physics3D::System* System, TArray& OutInputs) - { - if (System == nullptr) - return; - - for (auto& Subsystem : System->getValues()) - { - GetInputs(System, OutInputs); - } - - for (auto& Input : System->getValues()) - { - if (auto LvmvI = std::dynamic_pointer_cast< - Brick::Physics3D::Signals::LinearVelocityMotorVelocityInput>(Input)) - { - OutInputs.Add(FPLX_LinearVelocityMotorVelocityInput(Convert(LvmvI->motor()->getName()))); - continue; - } - - UE_LOG(LogAGX, Warning, TEXT("Unhandled PLX Input: %s"), *Convert(Input->getName())); - } - } -} - -TArray FBrickUtilities::GetInputs(Brick::Physics3D::System* System) -{ - TArray Inputs; - BrickUtilities_helpers::GetInputs(System, Inputs); - return Inputs; -} diff --git a/Source/AGXUnrealBarrier/Private/Utilities/PLXUtilities.cpp b/Source/AGXUnrealBarrier/Private/Utilities/PLXUtilities.cpp new file mode 100644 index 000000000..7da2f51c5 --- /dev/null +++ b/Source/AGXUnrealBarrier/Private/Utilities/PLXUtilities.cpp @@ -0,0 +1,50 @@ +// Copyright 2024, Algoryx Simulation AB. + +#include "Utilities/PLXUtilities.h" + +// AGX Dynamics for Unreal includes. +#include "TypeConversions.h" + +// OpenPLX includes. +#include "Brick/Physics3D/Physics3D_all.h" +#include "Physics3D/Signals/LinearVelocityMotorVelocityInput.h" + +// Unreal Engine includes. +#include "Templates/UniquePtr.h" + +// Standard library includes. +#include + +namespace PLXUtilities_helpers +{ + void GetInputs(Brick::Physics3D::System* System, TArray>& OutInputs) + { + if (System == nullptr) + return; + + for (auto& Subsystem : System->getValues()) + { + GetInputs(System, OutInputs); + } + + for (auto& Input : System->getValues()) + { + if (auto LvmvI = std::dynamic_pointer_cast< + Brick::Physics3D::Signals::LinearVelocityMotorVelocityInput>(Input)) + { + OutInputs.Emplace(MakeUnique( + Convert(LvmvI->motor()->getName()))); + continue; + } + + UE_LOG(LogAGX, Warning, TEXT("Unhandled PLX Input: %s"), *Convert(Input->getName())); + } + } +} + +TArray> FBrickUtilities::GetInputs(Brick::Physics3D::System* System) +{ + TArray> Inputs; + PLXUtilities_helpers::GetInputs(System, Inputs); + return Inputs; +} diff --git a/Source/AGXUnrealBarrier/Private/Utilities/BrickUtilities.h b/Source/AGXUnrealBarrier/Private/Utilities/PLXUtilities.h similarity index 65% rename from Source/AGXUnrealBarrier/Private/Utilities/BrickUtilities.h rename to Source/AGXUnrealBarrier/Private/Utilities/PLXUtilities.h index 0410675fb..3018cf172 100644 --- a/Source/AGXUnrealBarrier/Private/Utilities/BrickUtilities.h +++ b/Source/AGXUnrealBarrier/Private/Utilities/PLXUtilities.h @@ -4,7 +4,7 @@ // AGX Dynamics for Unreal includes. #include "AGX_LogCategory.h" -#include "Brick/AGX_BrickInputs.h" +#include "OpenPLX/PLX_Inputs.h" namespace Brick { @@ -17,5 +17,5 @@ namespace Brick class FBrickUtilities { public: - static TArray GetInputs(Brick::Physics3D::System* System); + static TArray> GetInputs(Brick::Physics3D::System* System); }; diff --git a/Source/AGXUnrealBarrier/Public/AGXSimObjectsReader.h b/Source/AGXUnrealBarrier/Public/AGXSimObjectsReader.h index 909d50cc5..ec412ac6b 100644 --- a/Source/AGXUnrealBarrier/Public/AGXSimObjectsReader.h +++ b/Source/AGXUnrealBarrier/Public/AGXSimObjectsReader.h @@ -35,6 +35,6 @@ namespace FAGXSimObjectsReader const FString& UrdfFilePath, const FString& UrdfPackagePath, const TArray& InitJoints, FSimulationObjectCollection& OutSimObjects); - AGXUNREALBARRIER_API bool ReadBrickFile( + AGXUNREALBARRIER_API bool ReadOpenPLXFile( const FString& BrickFilePath, FSimulationObjectCollection& OutSimObjects); }; diff --git a/Source/AGXUnrealBarrier/Public/SimulationObjectCollection.h b/Source/AGXUnrealBarrier/Public/SimulationObjectCollection.h index b9f8ff136..343475511 100644 --- a/Source/AGXUnrealBarrier/Public/SimulationObjectCollection.h +++ b/Source/AGXUnrealBarrier/Public/SimulationObjectCollection.h @@ -4,7 +4,7 @@ // AGX Dynamics for Unreal includes. // For some reason, these could not be forward declared without compiler error. -#include "Brick/AGX_BrickInputs.h" +#include "OpenPLX/PLX_Inputs.h" #include "Shapes/BoxShapeBarrier.h" #include "Shapes/CylinderShapeBarrier.h" #include "Shapes/CapsuleShapeBarrier.h" @@ -112,8 +112,8 @@ struct AGXUNREALBARRIER_API FSimulationObjectCollection TArray& GetTracks(); const TArray& GetTracks() const; - TArray& GetPLXInputs(); - const TArray& GetPLXInputs() const; + TArray>& GetPLXInputs(); + const TArray>& GetPLXInputs() const; private: FSimulationObjectCollection(const FSimulationObjectCollection&) = delete; @@ -147,5 +147,5 @@ struct AGXUNREALBARRIER_API FSimulationObjectCollection TArray Shovels; TArray Tracks; - TArray PLXInputs; + TArray> PLXInputs; }; diff --git a/Source/AGXUnrealEditor/AGXUnrealEditor.Build.cs b/Source/AGXUnrealEditor/AGXUnrealEditor.Build.cs index ce4b43fba..29b624c09 100644 --- a/Source/AGXUnrealEditor/AGXUnrealEditor.Build.cs +++ b/Source/AGXUnrealEditor/AGXUnrealEditor.Build.cs @@ -25,7 +25,7 @@ public AGXUnrealEditor(ReadOnlyTargetRules Target) : base(Target) /// \todo Copied from the prototype plugin. Not sure if all of these are /// required. PublicDependencyModuleNames.AddRange(new string[]{ - "AGXUnrealBarrier", "AGXUnreal", "ComponentVisualizers", "Core", "CoreUObject", "Engine", + "AGXCommon", "AGXUnrealBarrier", "AGXUnreal", "ComponentVisualizers", "Core", "CoreUObject", "Engine", "InputCore", "RawMesh", "RHI", "RenderCore" }); diff --git a/Source/AGXUnrealEditor/Private/AGXUnrealEditor.cpp b/Source/AGXUnrealEditor/Private/AGXUnrealEditor.cpp index b872f8f78..604a80ce1 100644 --- a/Source/AGXUnrealEditor/Private/AGXUnrealEditor.cpp +++ b/Source/AGXUnrealEditor/Private/AGXUnrealEditor.cpp @@ -44,9 +44,6 @@ #include "AMOR/AGX_ConstraintMergeSplitThresholdsTypeActions.h" #include "AMOR/AGX_ShapeContactMergeSplitThresholdsTypeActions.h" #include "AMOR/AGX_WireMergeSplitThresholdsTypeActions.h" -#include "Brick/AGX_BrickAsset.h" -#include "Brick/AGX_BrickAssetCustomization.h" -#include "Brick/AGX_BrickAssetTypeActions.h" #include "CollisionGroups/AGX_CollisionGroupDisablerActor.h" #include "CollisionGroups/AGX_CollisionGroupDisablerComponent.h" #include "CollisionGroups/AGX_CollisionGroupDisablerComponentCustomization.h" @@ -196,8 +193,6 @@ void FAGXUnrealEditorModule::RegisterAssetTypeActions() EAssetTypeCategories::Type AgxAssetCategoryBit = AssetTools.RegisterAdvancedAssetCategory( FName(TEXT("AgxUnreal")), LOCTEXT("AgxAssetCategory", "AGX Dynamics")); - RegisterAssetTypeAction( - AssetTools, MakeShareable(new FAGX_BrickAssetTypeActions(AgxAssetCategoryBit))); RegisterAssetTypeAction( AssetTools, MakeShareable(new FAGX_ContactMaterialAssetTypeActions(AgxAssetCategoryBit))); RegisterAssetTypeAction( @@ -296,10 +291,6 @@ void FAGXUnrealEditorModule::RegisterCustomizations() * Class customizations. */ - PropertyModule.RegisterCustomClassLayout( - UAGX_BrickAsset::StaticClass()->GetFName(), - FOnGetDetailCustomizationInstance::CreateStatic(&FAGX_BrickAssetCustomization::MakeInstance)); - /// \todo I don't know if this should be AAGX_ConstraintActor or /// UAGX_ConstraintComponent. Should we have one for each? Which should be /// the new one and what should it contain/do? @@ -444,8 +435,6 @@ void FAGXUnrealEditorModule::UnregisterCustomizations() * Class Customizations. */ - PropertyModule.UnregisterCustomClassLayout(UAGX_BrickAsset::StaticClass()->GetFName()); - /// \todo Not sure if this should be AAGX_ConstraintActor, /// UAGX_ConstraintComponent, or both. PropertyModule.UnregisterCustomClassLayout(AAGX_ConstraintActor::StaticClass()->GetFName()); diff --git a/Source/AGXUnrealEditor/Private/AGX_ImporterToBlueprint.cpp b/Source/AGXUnrealEditor/Private/AGX_ImporterToBlueprint.cpp index 69ceddeff..638e910ea 100644 --- a/Source/AGXUnrealEditor/Private/AGX_ImporterToBlueprint.cpp +++ b/Source/AGXUnrealEditor/Private/AGX_ImporterToBlueprint.cpp @@ -33,6 +33,7 @@ #include "Materials/ShapeMaterialBarrier.h" #include "Materials/ContactMaterialBarrier.h" #include "Materials/AGX_ContactMaterialRegistrarComponent.h" +#include "OpenPLX/PLX_Inputs.h" #include "Shapes/AGX_BoxShapeComponent.h" #include "Shapes/AGX_SphereShapeComponent.h" #include "Shapes/AGX_CapsuleShapeComponent.h" @@ -62,6 +63,7 @@ #include "Editor.h" #include "FileHelpers.h" #include "GameFramework/Actor.h" +#include "Kismet2/BlueprintEditorUtils.h" #include "Kismet2/KismetEditorUtilities.h" #include "Materials/MaterialInstanceConstant.h" #include "Misc/EngineVersionComparison.h" @@ -552,15 +554,15 @@ namespace } EImportResult AddComponentsFromAGXArchive( - AActor& ImportedActor, FAGX_SimObjectsImporterHelper& Helper) + AActor& ImportedActor, FAGX_SimObjectsImporterHelper& Helper, + FSimulationObjectCollection& OutSimObjects) { - FSimulationObjectCollection SimObjects; - if (!FAGXSimObjectsReader::ReadAGXArchive(Helper.SourceFilePath, SimObjects)) + if (!FAGXSimObjectsReader::ReadAGXArchive(Helper.SourceFilePath, OutSimObjects)) { return EImportResult::ErrorReadingSourceFile; } - if (!AddAllComponents(ImportedActor, SimObjects, Helper)) + if (!AddAllComponents(ImportedActor, OutSimObjects, Helper)) { return EImportResult::ErrorDuringInstantiations; } @@ -568,16 +570,40 @@ namespace return EImportResult::Success; } - EImportResult AddComponentsFromBrick( - AActor& ImportedActor, FAGX_SimObjectsImporterHelper& Helper) + bool AddPLXSignals(const FSimulationObjectCollection& SimObjects, UBlueprint& OutBlueprint) { - FSimulationObjectCollection SimObjects; - if (!FAGXSimObjectsReader::ReadBrickFile(Helper.SourceFilePath, SimObjects)) + bool SignalsAdded = false; + for (const TUniquePtr& Input : SimObjects.GetPLXInputs()) + { + FEdGraphPinType PinType; + PinType.PinCategory = FName(TEXT("struct")); + PinType.PinSubCategoryObject = Input->GetType(); + FBlueprintEditorUtils::AddMemberVariable(&OutBlueprint, FName(Input->Name), PinType); + SignalsAdded = true; + } + + for (auto& NewVariable : OutBlueprint.NewVariables) // Todo: is there a safer way to get these? + { + NewVariable.Category = FText::FromString("OpenPLX Inputs"); + NewVariable.PropertyFlags |= CPF_BlueprintReadOnly; + } + + if (SignalsAdded) + FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(&OutBlueprint); + + return SignalsAdded; + } + + EImportResult AddComponentsFromPLX( + AActor& ImportedActor, FAGX_SimObjectsImporterHelper& Helper, + FSimulationObjectCollection& OutSimObjects) + { + if (!FAGXSimObjectsReader::ReadOpenPLXFile(Helper.SourceFilePath, OutSimObjects)) { return EImportResult::ErrorReadingSourceFile; } - if (!AddAllComponents(ImportedActor, SimObjects, Helper)) + if (!AddAllComponents(ImportedActor, OutSimObjects, Helper)) { return EImportResult::ErrorDuringInstantiations; } @@ -587,17 +613,16 @@ namespace EImportResult AddComponentsFromUrdf( AActor& ImportedActor, FAGX_SimObjectsImporterHelper& Helper, - const FAGX_ImportSettings& ImportSettings) + const FAGX_ImportSettings& ImportSettings, FSimulationObjectCollection& OutSimObjects) { - FSimulationObjectCollection SimObjects; if (!FAGXSimObjectsReader::ReadUrdf( ImportSettings.FilePath, ImportSettings.UrdfPackagePath, - ImportSettings.UrdfInitialJoints, SimObjects)) + ImportSettings.UrdfInitialJoints, OutSimObjects)) { return EImportResult::ErrorReadingSourceFile; } - if (!AddAllComponents(ImportedActor, SimObjects, Helper)) + if (!AddAllComponents(ImportedActor, OutSimObjects, Helper)) { return EImportResult::ErrorDuringInstantiations; } @@ -605,7 +630,7 @@ namespace return EImportResult::Success; } - ImportActorResult CreateTemplate( + AActor* CreateTemplate( FAGX_SimObjectsImporterHelper& Helper, const FAGX_ImportSettings& ImportSettings) { UActorFactory* Factory = @@ -617,45 +642,7 @@ namespace check(RootActorContainer != nullptr); /// \todo Test and return false instead of check? RootActorContainer->SetFlags(RF_Transactional); RootActorContainer->SetActorLabel(Helper.RootDirectoryName); - -// I would like to be able to create and configure the RootComponent here, but -// the way Blueprint creation has been done in Unreal Engine makes this -// impossible. A new RootComponent is always created and the DefaultSceneRoot I -// create here is made a child of that new SceneComponent. Not what I want. My -// work-around for now is to rely on the implicitly created RootComponent and -// hoping it does what we want in all cases. I leave SceneComponents that should -// be attached to the RootComponent unconnected, they are implicitly connected -// to the implicit RootComponent by the Blueprint creator code. This produces a -// weird/invalid template actor so I'm worried that the it-happens-to-work state -// we now have won't survive for long. -#if 0 - USceneComponent* ActorRootComponent = NewObject( - RootActorContainer, USceneComponent::GetDefaultSceneRootVariableName()); - check(ActorRootComponent != nullptr); - ActorRootComponent->Mobility = EComponentMobility::Movable; - ActorRootComponent->bVisualizeComponent = true; - ActorRootComponent->SetFlags(RF_Transactional); - ActorRootComponent->RegisterComponent(); - RootActorContainer->AddInstanceComponent(ActorRootComponent); - RootActorContainer->SetRootComponent(ActorRootComponent); -#endif - - - EImportResult Result {EImportResult::UnknownFailure}; - switch (ImportSettings.ImportType) - { - case EAGX_ImportType::Agx: - Result = AddComponentsFromAGXArchive(*RootActorContainer, Helper); - break; - case EAGX_ImportType::Brick: - Result = AddComponentsFromBrick(*RootActorContainer, Helper); - break; - case EAGX_ImportType::Urdf: - Result = AddComponentsFromUrdf(*RootActorContainer, Helper, ImportSettings); - break; - } - - return {Result, RootActorContainer}; + return RootActorContainer; } UBlueprint* CreateBaseBlueprint(UPackage* Package, AActor* Template) @@ -710,16 +697,34 @@ namespace PreCreationSetup(); FString BlueprintPackagePath = CreateBlueprintPackagePath(Helper, true); UPackage* Package = GetPackage(BlueprintPackagePath); - ImportActorResult TemplateResult = CreateTemplate(Helper, ImportSettings); - AActor* Template = TemplateResult.Actor; + AActor* Template = CreateTemplate(Helper, ImportSettings); if (Template == nullptr) + return {EImportResult::UnknownFailure, nullptr}; + + FSimulationObjectCollection SimObjects; + EImportResult Result {EImportResult::UnknownFailure}; + switch (ImportSettings.ImportType) { - return {TemplateResult.Result, nullptr}; + case EAGX_ImportType::Agx: + Result = AddComponentsFromAGXArchive(*Template, Helper, SimObjects); + break; + case EAGX_ImportType::Plx: + Result = AddComponentsFromPLX(*Template, Helper, SimObjects); + break; + case EAGX_ImportType::Urdf: + Result = AddComponentsFromUrdf(*Template, Helper, ImportSettings, SimObjects); + break; } UBlueprint* Blueprint = CreateBaseBlueprint(Package, Template); + if (ImportSettings.ImportType == EAGX_ImportType::Plx && Blueprint != nullptr) + { + if (AddPLXSignals(SimObjects, *Blueprint)) + FKismetEditorUtilities::CompileBlueprint(Blueprint); + } + PostCreationTeardown(Template, Package, Blueprint, BlueprintPackagePath); - return {TemplateResult.Result, Blueprint}; + return {Result, Blueprint}; } UBlueprint* CreateChildBlueprint( diff --git a/Source/AGXUnrealEditor/Private/Brick/AGX_BrickAssetCustomization.cpp b/Source/AGXUnrealEditor/Private/Brick/AGX_BrickAssetCustomization.cpp deleted file mode 100644 index d7a5745b7..000000000 --- a/Source/AGXUnrealEditor/Private/Brick/AGX_BrickAssetCustomization.cpp +++ /dev/null @@ -1,305 +0,0 @@ -// Copyright 2024, Algoryx Simulation AB. - -#include "Brick/AGX_BrickAssetCustomization.h" - -// AGX Dynamics for Unreal includes. -#include "AGX_RigidBodyComponent.h" -#include "Brick/AGX_BrickAsset.h" -#include "Utilities/AGX_EditorUtilities.h" -#include "Utilities/AGX_ImportUtilities.h" - -// Unreal Engine includes. -#include "ActorFactories/ActorFactoryEmptyActor.h" -#include "AssetSelection.h" -#include "DetailCategoryBuilder.h" -#include "DetailLayoutBuilder.h" -#include "DetailWidgetRow.h" -#include "Engine/Blueprint.h" -#include "Engine/Level.h" -#include "Engine/SCS_Node.h" -#include "Engine/SimpleConstructionScript.h" -#include "Engine/World.h" -#include "Kismet/GameplayStatics.h" -#include "Kismet2/BlueprintEditorUtils.h" -#include "Kismet2/KismetEditorUtilities.h" -#include "PackageTools.h" -#include "Widgets/Input/SButton.h" -#include "Widgets/Text/STextBlock.h" - -#define LOCTEXT_NAMESPACE "FAGX_BrickAssetCustomization" - -// IMPORTANT: This is all experimental test code used durint development. Remove before merge to -// master. - -TSharedRef FAGX_BrickAssetCustomization::MakeInstance() -{ - return MakeShareable(new FAGX_BrickAssetCustomization); -} - -void FAGX_BrickAssetCustomization::CustomizeDetails(IDetailLayoutBuilder& InDetailBuilder) -{ - DetailBuilder = &InDetailBuilder; - - const UAGX_BrickAsset* BrickAsset = - FAGX_EditorUtilities::GetSingleObjectBeingCustomized(InDetailBuilder); - if (BrickAsset == nullptr) - { - return; - } - - IDetailCategoryBuilder& CategoryBuilder = InDetailBuilder.EditCategory("Experiments"); - - // clang-format off - CategoryBuilder.AddCustomRow(FText::GetEmpty()) - [ - SNew(SBorder) - .BorderBackgroundColor(FLinearColor(1.0f, 1.0f, 1.0f)) - .Padding(FMargin(5.0f, 5.0f)) - .Content() - [ - SNew(SVerticalBox) - + SVerticalBox::Slot() - .Padding(FMargin(5.0f, 5.0f)) - .AutoHeight() - [ - SNew(SBorder) - .BorderBackgroundColor(FLinearColor(1.0f, 1.0f, 1.0f)) - .BorderImage(FAGX_EditorUtilities::GetBrush("ToolPanel.GroupBorder")) - .Padding(FMargin(5.0f, 5.0f)) - .Content() - [ - SNew(SHorizontalBox) - + SHorizontalBox::Slot() - .AutoWidth() - .Padding(FMargin(10.0f, 0.0f)) - [ - SNew(SButton) - .Text(LOCTEXT("Import", "Import")) - .ToolTipText(LOCTEXT("ImportTooltip", - "Import using hard-coded path.")) - .OnClicked(this, &FAGX_BrickAssetCustomization::OnImportButtonClicked) - ] - + SHorizontalBox::Slot() - .AutoWidth() - .Padding(FMargin(10.0f, 0.0f)) - [ - SNew(SButton) - .Text(LOCTEXT("CreateBlueprints", "CreateBlueprints")) - .ToolTipText(LOCTEXT("CreateBpsToolTip", - "CreateBlueprints.")) - .OnClicked(this, &FAGX_BrickAssetCustomization::OnCreateBlueprintsButtonClicked) - ] - + SHorizontalBox::Slot() - .AutoWidth() - .Padding(FMargin(10.0f, 0.0f)) - [ - SNew(SButton) - .Text(LOCTEXT("UpdateBlueprint", "UpdateBlueprint")) - .ToolTipText(LOCTEXT("UpdateBlueprintToolTip", - "UpdateBlueprint.")) - .OnClicked(this, &FAGX_BrickAssetCustomization::OnUpdateBlueprintButtonClicked) - ] - + SHorizontalBox::Slot() - .AutoWidth() - .Padding(FMargin(10.0f, 0.0f)) - [ - SNew(SButton) - .Text(LOCTEXT("CopyBlueprint", "CopyBlueprint")) - .ToolTipText(LOCTEXT("CopyBlueprintToolTip", - "CopyBlueprint.")) - .OnClicked(this, &FAGX_BrickAssetCustomization::OnCopyFromOtherBlueprintButtonClicked) - ] - ] - ] - ] - ]; - // clang-format on -} - -FReply FAGX_BrickAssetCustomization::OnImportButtonClicked() -{ - AGX_CHECK(DetailBuilder); - // FBrick::Test(); - return FReply::Handled(); -} - -namespace -{ - UPackage* CreateUPackage(const FString& BlueprintPackagePath) - { - UPackage* Package = CreatePackage(*BlueprintPackagePath); - Package->FullyLoad(); - return Package; - } - - FString CreatePackagePath(const FString& SubDir, FString Name) - { - const FString BasePath = FString(TEXT("/Game/")); - FString PackagePath = FAGX_ImportUtilities::CreatePackagePath(BasePath, SubDir); - FAGX_ImportUtilities::MakePackageAndAssetNameUnique(PackagePath, Name); - UPackage* ParentPackage = CreatePackage(*PackagePath); - FString Path = FPaths::GetPath(ParentPackage->GetName()); - return UPackageTools::SanitizePackageName(Path + "/" + Name); - } - - UBlueprint* CreateBlueprint( - const FString& Name, AActor*& OutTemplate, bool SetNonDefaultMass = false) - { - GEditor->SelectNone(false, false); - FString PackagePath = CreatePackagePath("Blueprint", Name); - UPackage* Package = CreateUPackage(PackagePath); - - UActorFactory* Factory = - GEditor->FindActorFactoryByClass(UActorFactoryEmptyActor::StaticClass()); - FAssetData EmptyActorAssetData = FAssetData(Factory->GetDefaultActorClass(FAssetData())); - UObject* EmptyActorAsset = EmptyActorAssetData.GetAsset(); - OutTemplate = FActorFactoryAssetProxy::AddActorForAsset(EmptyActorAsset, false); - check(OutTemplate != nullptr); /// \todo Test and return false instead of check? - OutTemplate->SetFlags(RF_Transactional); - OutTemplate->SetActorLabel(Name); - - UAGX_RigidBodyComponent* RigidBody = - NewObject(OutTemplate, TEXT("Body")); - RigidBody->bAutoGenerateMass = false; - if (SetNonDefaultMass) - RigidBody->Mass = 6.f; - - RigidBody->SetFlags(RF_Transactional); - OutTemplate->AddInstanceComponent(RigidBody); - RigidBody->RegisterComponent(); - RigidBody->PostEditChange(); - - FKismetEditorUtilities::FCreateBlueprintFromActorParams Params; - Params.bReplaceActor = false; - Params.bKeepMobility = true; - Params.bOpenBlueprint = false; - - UBlueprint* Blueprint = FKismetEditorUtilities::CreateBlueprintFromActor( - Package->GetName(), OutTemplate, Params); - - // RootActorContainer->Destroy(); - FAGX_ObjectUtilities::SaveAsset(*Blueprint); - - // Child Blueprint - FString PackagePathChild = CreatePackagePath("", FString::Printf(TEXT("%s_child"), *Name)); - UPackage* PackageChild = CreateUPackage(PackagePathChild); - const FString AssetNameChild = FPaths::GetBaseFilename(PackageChild->GetName()); - - UBlueprint* BlueprintChild = FKismetEditorUtilities::CreateBlueprint( - Blueprint->GeneratedClass, PackageChild, FName(AssetNameChild), - EBlueprintType::BPTYPE_Normal, UBlueprint::StaticClass(), - UBlueprintGeneratedClass::StaticClass(), FName("HackyCodeCO")); - - GEngine->BroadcastLevelActorListChanged(); - - FAGX_ObjectUtilities::SaveAsset(*BlueprintChild); - - return Blueprint; - } -} - -FReply FAGX_BrickAssetCustomization::OnCreateBlueprintsButtonClicked() -{ - AGX_CHECK(DetailBuilder); - UE_LOG(LogTemp, Warning, TEXT("CreateBlueprints!")); - UBlueprint* BaseBlueprint = ::CreateBlueprint("BaseBlueprint", TemplateActor); - BlueprintBase = BaseBlueprint; - return FReply::Handled(); -} - -FReply FAGX_BrickAssetCustomization::OnUpdateBlueprintButtonClicked() -{ - AGX_CHECK(DetailBuilder); - AGX_CHECK(BlueprintBase); - UE_LOG(LogTemp, Warning, TEXT("UpdateBlueprints!")); - - for (USCS_Node* Node : BlueprintBase->SimpleConstructionScript->GetAllNodes()) - { - if (Node == nullptr) - continue; - - UActorComponent* Component = Node->ComponentTemplate; - - if (auto Ri = Cast(Component)) - { - Ri->Modify(); - Ri->Mass = 3.f; - UE_LOG(LogTemp, Warning, TEXT("Updated rigidbody with new mass")); - FBlueprintEditorUtils::MarkBlueprintAsModified(BlueprintBase); - Ri->MarkPackageDirty(); - Ri->PostEditChange(); - FPropertyChangedEvent PropertyChangedEvent(Ri->GetClass()->FindPropertyByName( - GET_MEMBER_NAME_CHECKED(UAGX_RigidBodyComponent, Mass))); - Ri->PostEditChangeProperty(PropertyChangedEvent); - } - } - - FKismetEditorUtilities::CompileBlueprint(BlueprintBase); - FAGX_ObjectUtilities::SaveAsset(*BlueprintBase); - - return FReply::Handled(); -} - -FReply FAGX_BrickAssetCustomization::OnCopyFromOtherBlueprintButtonClicked() -{ -#if 0 // didnt work - AGX_CHECK(TemplateActor); - AGX_CHECK(BlueprintBase); - - AActor* CDO = CastChecked(BlueprintBase->GeneratedClass->GetDefaultObject()); - const EditorUtilities::ECopyOptions::Type CopyOptions = (EditorUtilities::ECopyOptions::Type)( - EditorUtilities::ECopyOptions::PropagateChangesToArchetypeInstances); - EditorUtilities::CopyActorProperties(TemplateActor, CDO, CopyOptions); -#endif - - UBlueprint* BlueprintA = LoadObject(nullptr, TEXT("/Game/BlueprintA")); - UBlueprint* BlueprintB = LoadObject(nullptr, TEXT("/Game/BlueprintB")); - AGX_CHECK(BlueprintA != nullptr); - AGX_CHECK(BlueprintB != nullptr); - -#if 0 // Almost, kinda works if ReplaceStaleRefs is skipped with debugger - //, but child blueprints will die, due to no parent anymore. - UBlueprint* Replacement = FKismetEditorUtilities::ReplaceBlueprint(BlueprintB, BlueprintA); - FKismetEditorUtilities::CompileBlueprint(Replacement); - FAGX_ObjectUtilities::SaveAsset(*Replacement); -#endif - -#if 0 // Crash when trying to open Blueprint afterwords. - UEngine::CopyPropertiesForUnrelatedObjects(BlueprintA, BlueprintB); -#endif - -#if 0 // does nothing - AActor* CDOA = CastChecked(BlueprintA->GeneratedClass->GetDefaultObject()); - AActor* CDOB = CastChecked(BlueprintA->GeneratedClass->GetDefaultObject()); - UEngine::CopyPropertiesForUnrelatedObjects(CDOA, CDOB); -#endif - -#if 0 // does nothing - AActor* CDOA = CastChecked(BlueprintA->GeneratedClass->GetDefaultObject()); - AActor* CDOB = CastChecked(BlueprintA->GeneratedClass->GetDefaultObject()); - EditorUtilities::CopyActorProperties(CDOA, CDOB); -#endif - -#if 0 // This will crash after CopyPropertiesForUnrelatedObjects. - UBlueprint* NewBlueprint = ::CreateBlueprint("BaseBlueprint_new"); - // Now we have BlueprintBase and NewBlueprint which are almost identical. - // NewBlueprint has another mass for the RigidBody. - // Mission: can we somehow update BlueprintBase to match NewBlueprint? - - UEngine::CopyPropertiesForUnrelatedObjects( - NewBlueprint->ParentClass, BlueprintBase->ParentClass); - FKismetEditorUtilities::CompileBlueprint(BlueprintBase); - FAGX_ObjectUtilities::SaveAsset(*BlueprintBase); - FKismetEditorUtilities::CompileBlueprint(NewBlueprint); - FAGX_ObjectUtilities::SaveAsset(*NewBlueprint); -#endif - -#if 0 // Also crashes - UEngine::CopyPropertiesForUnrelatedObjects( - NewBlueprint->GeneratedClass, BlueprintBase->GeneratedClass); -#endif - - return FReply::Handled(); -} - -#undef LOCTEXT_NAMESPACE diff --git a/Source/AGXUnrealEditor/Private/Brick/AGX_BrickAssetFactory.cpp b/Source/AGXUnrealEditor/Private/Brick/AGX_BrickAssetFactory.cpp deleted file mode 100644 index 69d0bd199..000000000 --- a/Source/AGXUnrealEditor/Private/Brick/AGX_BrickAssetFactory.cpp +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright 2024, Algoryx Simulation AB. - -#include "Brick/AGX_BrickAssetFactory.h" - -// AGX Dynamics for Unreal includes. -#include "Brick/AGX_BrickAsset.h" - -// Unreal Engine includes. -#include "EditorFramework/AssetImportData.h" - - -UAGX_BrickAssetFactory::UAGX_BrickAssetFactory(const class FObjectInitializer& OBJ) - : Super(OBJ) -{ - SupportedClass = UAGX_BrickAsset::StaticClass(); - bCreateNew = false; // Todo: what does this do? - bEditorImport = true; - - Formats.Add(TEXT("brick;Brick file")); -} - -bool UAGX_BrickAssetFactory::DoesSupportClass(UClass* Class) -{ - return Class == UAGX_BrickAsset::StaticClass(); -} - -UObject* UAGX_BrickAssetFactory::FactoryCreateFile( - UClass* InClass, UObject* InParent, FName InName, EObjectFlags Flags, const FString& Filename, - const TCHAR* Parms, FFeedbackContext* Warn, bool& bOutOperationCanceled) -{ - GEditor->GetEditorSubsystem()->BroadcastAssetPreImport( - this, InClass, InParent, InName, TEXT("brick")); - - bOutOperationCanceled = false; - - // Reminder: - // if (IsAutomatedImport()) -> don't show any dialogues, or ask for user input! See Factory.h - - auto BrickAsset = NewObject(InParent, InClass, InName, Flags); - BrickAsset->AssetImportData = NewObject(this, TEXT("AssetImportData")); - if (!CurrentFilename.IsEmpty()) - { - BrickAsset->AssetImportData->Update(CurrentFilename); - } - - GEditor->GetEditorSubsystem()->BroadcastAssetPostImport(this, BrickAsset); - - return BrickAsset; -} - -bool UAGX_BrickAssetFactory::FactoryCanImport(const FString& Filename) -{ - return FPaths::GetExtension(Filename).Equals("brick"); -} - -bool UAGX_BrickAssetFactory::CanReimport(UObject* Obj, TArray& OutFilenames) -{ - if (auto BrickAsset = Cast(Obj)) - { - BrickAsset->AssetImportData->ExtractFilenames(OutFilenames); - return true; - } - - return false; -} - -void UAGX_BrickAssetFactory::SetReimportPaths(UObject* Obj, const TArray& NewReimportPaths) -{ - auto BrickAsset = Cast(Obj); - if (BrickAsset && BrickAsset->AssetImportData && ensure(NewReimportPaths.Num() == 1)) - { - BrickAsset->AssetImportData->UpdateFilenameOnly(NewReimportPaths[0]); - } -} - -EReimportResult::Type UAGX_BrickAssetFactory::Reimport(UObject* Obj) -{ - return EReimportResult::Succeeded; -} - -int32 UAGX_BrickAssetFactory::GetPriority() const -{ - return ImportPriority; -} - diff --git a/Source/AGXUnrealEditor/Private/Brick/AGX_BrickAssetTypeActions.cpp b/Source/AGXUnrealEditor/Private/Brick/AGX_BrickAssetTypeActions.cpp deleted file mode 100644 index 8b8c4ac8b..000000000 --- a/Source/AGXUnrealEditor/Private/Brick/AGX_BrickAssetTypeActions.cpp +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright 2024, Algoryx Simulation AB. - -#include "Brick/AGX_BrickAssetTypeActions.h" - -// AGX Dynamics for Unreal includes. -#include "Brick/AGX_BrickAsset.h" -#include "Utilities/AGX_SlateUtilities.h" - - -#define LOCTEXT_NAMESPACE "FAGX_BrickAssetTypeActions" - -FAGX_BrickAssetTypeActions::FAGX_BrickAssetTypeActions( - EAssetTypeCategories::Type InAssetCategory) - : AssetCategory(InAssetCategory) -{ -} - -FText FAGX_BrickAssetTypeActions::GetName() const -{ - return LOCTEXT("AssetName", "AGX Brick Asset"); -} - -const TArray& FAGX_BrickAssetTypeActions::GetSubMenus() const -{ - static const TArray SubMenus { - LOCTEXT("BrickSubMenu", "Brick"), - }; - - return SubMenus; -} - -uint32 FAGX_BrickAssetTypeActions::GetCategories() -{ - return AssetCategory; -} - -FColor FAGX_BrickAssetTypeActions::GetTypeColor() const -{ - return FAGX_SlateUtilities::GetAGXColorOrange(); -} - -FText FAGX_BrickAssetTypeActions::GetAssetDescription(const FAssetData& AssetData) const -{ - return LOCTEXT( - "AssetDescription", "Brick Asset representing the source Brick file on disk."); -} - -UClass* FAGX_BrickAssetTypeActions::GetSupportedClass() const -{ - return UAGX_BrickAsset::StaticClass(); -} - -bool FAGX_BrickAssetTypeActions::IsImportedAsset() const -{ - return true; -} - -void FAGX_BrickAssetTypeActions::GetResolvedSourceFilePaths( - const TArray& TypeAssets, TArray& OutSourceFilePaths) const -{ - for (auto& Asset : TypeAssets) - { - const auto BrickAsset = CastChecked(Asset); - if (BrickAsset->AssetImportData != nullptr) - { - BrickAsset->AssetImportData->ExtractFilenames(OutSourceFilePaths); - } - } -} - -#undef LOCTEXT_NAMESPACE diff --git a/Source/AGXUnrealEditor/Private/Utilities/AGX_ImportUtilities.cpp b/Source/AGXUnrealEditor/Private/Utilities/AGX_ImportUtilities.cpp index abddb0a1c..c2ff7cd5f 100644 --- a/Source/AGXUnrealEditor/Private/Utilities/AGX_ImportUtilities.cpp +++ b/Source/AGXUnrealEditor/Private/Utilities/AGX_ImportUtilities.cpp @@ -602,7 +602,7 @@ EAGX_ImportType FAGX_ImportUtilities::GetFrom(const FString& FilePath) } else if (FileExtension.Equals("brick")) { - return EAGX_ImportType::Brick; + return EAGX_ImportType::Plx; } else if (FileExtension.Equals("urdf")) { diff --git a/Source/AGXUnrealEditor/Public/AGX_ImportEnums.h b/Source/AGXUnrealEditor/Public/AGX_ImportEnums.h index fda06429a..f5279af11 100644 --- a/Source/AGXUnrealEditor/Public/AGX_ImportEnums.h +++ b/Source/AGXUnrealEditor/Public/AGX_ImportEnums.h @@ -16,8 +16,8 @@ enum class EAGX_ImportType : uint8 /** Imported type is an AGX Dynamics Archive. */ Agx, - /** Imported type is a Brick model. */ - Brick, + /** Imported type is a OpenPLX model. */ + Plx, /** Imported type is a URDF (Unified Robotic Description Format) model. */ Urdf diff --git a/Source/AGXUnrealEditor/Public/Brick/AGX_BrickAssetCustomization.h b/Source/AGXUnrealEditor/Public/Brick/AGX_BrickAssetCustomization.h deleted file mode 100644 index 0a039c937..000000000 --- a/Source/AGXUnrealEditor/Public/Brick/AGX_BrickAssetCustomization.h +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2024, Algoryx Simulation AB. - -#pragma once - -// Unreal Engine includes. -#include "CoreMinimal.h" -#include "IDetailCustomization.h" -#include "Input/Reply.h" - -class IDetailLayoutBuilder; -class IDetailCategoryBuilder; - -/** - * Only for internal testing, do not merge! - */ -class AGXUNREALEDITOR_API FAGX_BrickAssetCustomization : public IDetailCustomization -{ -public: - static TSharedRef MakeInstance(); - - virtual void CustomizeDetails(IDetailLayoutBuilder& InDetailBuilder) override; - -private: - FReply OnImportButtonClicked(); - FReply OnCreateBlueprintsButtonClicked(); - FReply OnUpdateBlueprintButtonClicked(); - FReply OnCopyFromOtherBlueprintButtonClicked(); - - IDetailLayoutBuilder* DetailBuilder {nullptr}; - UBlueprint* BlueprintBase {nullptr}; - AActor* TemplateActor {nullptr}; -}; diff --git a/Source/AGXUnrealEditor/Public/Brick/AGX_BrickAssetFactory.h b/Source/AGXUnrealEditor/Public/Brick/AGX_BrickAssetFactory.h deleted file mode 100644 index 4f2808d93..000000000 --- a/Source/AGXUnrealEditor/Public/Brick/AGX_BrickAssetFactory.h +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2024, Algoryx Simulation AB. - -#pragma once - -// Unreal Engine includes. -#include "CoreMinimal.h" -#include "EditorReimportHandler.h" -#include "Factories/Factory.h" -#include "Factories/ImportSettings.h" - -#include "AGX_BrickAssetFactory.generated.h" - -UCLASS() -class AGXUNREALEDITOR_API UAGX_BrickAssetFactory : public UFactory, public FReimportHandler -{ - GENERATED_BODY() - -public: - UAGX_BrickAssetFactory(const class FObjectInitializer& OBJ); - - //~ Begin UFactory Interface - virtual bool DoesSupportClass(UClass* Class) override; - virtual UObject* FactoryCreateFile( - UClass* InClass, UObject* InParent, FName InName, EObjectFlags Flags, - const FString& Filename, const TCHAR* Parms, FFeedbackContext* Warn, - bool& bOutOperationCanceled) override; - virtual bool FactoryCanImport(const FString& Filename) override; - //~ End UFactory Interface - - //~ Begin FReimportHandler Interface - virtual bool CanReimport(UObject* Obj, TArray& OutFilenames) override; - virtual void SetReimportPaths( - UObject* Obj, const TArray& NewReimportPaths) override; - virtual EReimportResult::Type Reimport(UObject* Obj) override; - virtual int32 GetPriority() const override; - //~ End FReimportHandler Interface -}; diff --git a/Source/AGXUnrealEditor/Public/Brick/AGX_BrickAssetTypeActions.h b/Source/AGXUnrealEditor/Public/Brick/AGX_BrickAssetTypeActions.h deleted file mode 100644 index 24bd0e99b..000000000 --- a/Source/AGXUnrealEditor/Public/Brick/AGX_BrickAssetTypeActions.h +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2024, Algoryx Simulation AB. - -#pragma once - -#include "CoreMinimal.h" -#include "AssetTypeCategories.h" -#include "AssetTypeActions_Base.h" - - -class AGXUNREALEDITOR_API FAGX_BrickAssetTypeActions : public FAssetTypeActions_Base -{ -public: - FAGX_BrickAssetTypeActions(EAssetTypeCategories::Type InAssetCategory); - - virtual FText GetName() const override; - - virtual const TArray& GetSubMenus() const override; - - virtual uint32 GetCategories() override; - - virtual FColor GetTypeColor() const override; - - virtual FText GetAssetDescription(const FAssetData& AssetData) const override; - - virtual UClass* GetSupportedClass() const override; - - virtual bool IsImportedAsset() const override; - - virtual void GetResolvedSourceFilePaths( - const TArray& TypeAssets, TArray& OutSourceFilePaths) const override; - -private: - EAssetTypeCategories::Type AssetCategory; -}; From e0ab0c0eec688c1a31f8fe3e207d8ee4d0091c8f Mon Sep 17 00:00:00 2001 From: Josef Date: Mon, 11 Nov 2024 07:27:36 +0100 Subject: [PATCH 17/31] Start implementing a SignalHandler who will propagate signals [ci skip] --- .../OpenPLX/PLX_SignalHandlerComponent.cpp | 54 +++++++++++++++++++ .../OpenPLX/PLX_SignalHandlerComponent.h | 33 ++++++++++++ .../Private/OpenPLX/PLXSignalHandler.cpp | 12 +++++ .../Public/OpenPLX/PLXSignalHandler.h | 12 +++++ 4 files changed, 111 insertions(+) create mode 100644 Source/AGXUnreal/Private/OpenPLX/PLX_SignalHandlerComponent.cpp create mode 100644 Source/AGXUnreal/Public/OpenPLX/PLX_SignalHandlerComponent.h create mode 100644 Source/AGXUnrealBarrier/Private/OpenPLX/PLXSignalHandler.cpp create mode 100644 Source/AGXUnrealBarrier/Public/OpenPLX/PLXSignalHandler.h diff --git a/Source/AGXUnreal/Private/OpenPLX/PLX_SignalHandlerComponent.cpp b/Source/AGXUnreal/Private/OpenPLX/PLX_SignalHandlerComponent.cpp new file mode 100644 index 000000000..61d98a87f --- /dev/null +++ b/Source/AGXUnreal/Private/OpenPLX/PLX_SignalHandlerComponent.cpp @@ -0,0 +1,54 @@ +// Copyright 2024, Algoryx Simulation AB. + +#include "OpenPLX/PLX_SignalHandlerComponent.h" + +// AGX Dynamics for Unreal includes. +#include "AGX_Simulation.h" +#include "Constraints/AGX_ConstraintComponent.h" +#include "Utilities/AGX_ObjectUtilities.h" + +UPLX_SignalHandlerComponent::UPLX_SignalHandlerComponent() +{ + PrimaryComponentTick.bCanEverTick = false; +} + +namespace PLX_SignalHandlerComponent_helpers +{ + TArray CollectConstraintBarriers(AActor* Owner) + { + if (Owner == nullptr) + return TArray(); + + TArray ConstraintsInThisActor = + FAGX_ObjectUtilities::Filter(Owner->GetComponents()); + TArray ConstraintBarriers; + for (UAGX_ConstraintComponent* Constraint : ConstraintsInThisActor) + { + if (auto CBarrier = Constraint->GetOrCreateNative()) + ConstraintBarriers.Add(CBarrier); + } + + return ConstraintBarriers; + } +} + +void UPLX_SignalHandlerComponent::BeginPlay() +{ + using namespace PLX_SignalHandlerComponent_helpers; + Super::BeginPlay(); + + auto Sim = UAGX_Simulation::GetFrom(this); + auto SimulationBarrier = Sim != nullptr ? Sim->GetNative() : nullptr; + if (SimulationBarrier == nullptr) + { + // todo: log warning. + return; + } + + // Collect all Constraints in the same AActor as us. + TArray ConstraintBarriers = CollectConstraintBarriers(GetOwner()); + + // Initialize SignalHandler in Barrier module. + SignalHandler.Init(*SimulationBarrier, ConstraintBarriers); +} + diff --git a/Source/AGXUnreal/Public/OpenPLX/PLX_SignalHandlerComponent.h b/Source/AGXUnreal/Public/OpenPLX/PLX_SignalHandlerComponent.h new file mode 100644 index 000000000..e6280484a --- /dev/null +++ b/Source/AGXUnreal/Public/OpenPLX/PLX_SignalHandlerComponent.h @@ -0,0 +1,33 @@ +// Copyright 2024, Algoryx Simulation AB. + +#pragma once + +// AGX Dynamics for Unreal includes. +#include "OpenPLX/PLXSignalHandler.h" + +// Unreal Engine includes. +#include "Components/ActorComponent.h" +#include "CoreMinimal.h" + +#include "PLX_SignalHandlerComponent.generated.h" + +/** + * Todo: Add description. + */ +UCLASS( + ClassGroup = "OpenPLX", Category = "OpenPLX", Meta = (BlueprintSpawnableComponent), + Hidecategories = (Cooking, Collision, LOD, Physics, Rendering, Replication)) +class AGXUNREAL_API UPLX_SignalHandlerComponent : public UActorComponent +{ + GENERATED_BODY() + +public: + UPLX_SignalHandlerComponent(); + + //~ Begin UActorComponent Interface + virtual void BeginPlay() override; + //~ End UActorComponent Interface + +private: + FPLXSignalHandler SignalHandler; +}; diff --git a/Source/AGXUnrealBarrier/Private/OpenPLX/PLXSignalHandler.cpp b/Source/AGXUnrealBarrier/Private/OpenPLX/PLXSignalHandler.cpp new file mode 100644 index 000000000..3ac43a539 --- /dev/null +++ b/Source/AGXUnrealBarrier/Private/OpenPLX/PLXSignalHandler.cpp @@ -0,0 +1,12 @@ +// Copyright 2024, Algoryx Simulation AB. + +#include "OpenPLX/PLXSignalHandler.h" + +// AGX Dynamics for Unreal includes. +#include "Constraints/ConstraintBarrier.h" +#include "SimulationBarrier.h" + +void FPLXSignalHandler::Init(FSimulationBarrier& Simulation, TArray& Constraints) +{ + +} diff --git a/Source/AGXUnrealBarrier/Public/OpenPLX/PLXSignalHandler.h b/Source/AGXUnrealBarrier/Public/OpenPLX/PLXSignalHandler.h new file mode 100644 index 000000000..2ded41bfc --- /dev/null +++ b/Source/AGXUnrealBarrier/Public/OpenPLX/PLXSignalHandler.h @@ -0,0 +1,12 @@ +// Copyright 2024, Algoryx Simulation AB. + +#pragma once + +class FConstraintBarrier; +class FSimulationBarrier; + +class AGXUNREALBARRIER_API FPLXSignalHandler +{ +public: + void Init(FSimulationBarrier& Simulation, TArray& Constraints); +}; From b058090782422ca3ea7d9fa4104a38f0f968a442 Mon Sep 17 00:00:00 2001 From: Josef Date: Tue, 12 Nov 2024 11:29:02 +0100 Subject: [PATCH 18/31] Add SignalHandler and refactor some locations of import classes --- Source/AGXUnreal/Private/AGX_Simulation.cpp | 8 +- .../OpenPLX/PLX_SignalHandlerComponent.cpp | 41 ++++++- .../Public/Import}/AGX_ImportEnums.h | 0 .../{ => Import}/AGX_ModelSourceComponent.h | 1 + .../OpenPLX/PLX_SignalHandlerComponent.h | 5 + .../Private/AGXSimObjectsReader.cpp | 82 +------------ .../Private/OpenPLX/PLXSignalHandler.cpp | 111 +++++++++++++++++- .../Private/Utilities/PLXUtilities.cpp | 86 +++++++++++++- .../Private/Utilities/PLXUtilities.h | 9 +- .../Public/BarrierOnly/AGXRefs.h | 12 ++ .../Public/BarrierOnly/OpenPLX/OpenPLXRefs.h | 24 ++++ .../Public/OpenPLX/PLXSignalHandler.h | 21 +++- .../Private/AGXUnrealEditor.cpp | 2 +- .../Private/AGX_ImporterToBlueprint.cpp | 4 +- .../AGX_ModelSourceComponentCustomization.cpp | 2 +- .../Private/AGX_SimObjectsImporterHelper.cpp | 2 +- .../Private/AGX_SimObjectsImporterHelper.h | 2 +- .../Private/Utilities/AGX_EditorUtilities.cpp | 2 +- .../Public/AGX_ImportSettings.h | 2 +- .../Public/Utilities/AGX_ImportUtilities.h | 2 +- .../Public/Widgets/AGX_ImportDialogBase.h | 2 +- 21 files changed, 322 insertions(+), 98 deletions(-) rename Source/{AGXUnrealEditor/Public => AGXUnreal/Public/Import}/AGX_ImportEnums.h (100%) rename Source/AGXUnreal/Public/{ => Import}/AGX_ModelSourceComponent.h (98%) create mode 100644 Source/AGXUnrealBarrier/Public/BarrierOnly/OpenPLX/OpenPLXRefs.h diff --git a/Source/AGXUnreal/Private/AGX_Simulation.cpp b/Source/AGXUnreal/Private/AGX_Simulation.cpp index e2f24ef1d..1dd801a90 100644 --- a/Source/AGXUnreal/Private/AGX_Simulation.cpp +++ b/Source/AGXUnreal/Private/AGX_Simulation.cpp @@ -664,12 +664,8 @@ void UAGX_Simulation::Deinitialize() #endif Super::Deinitialize(); - if (!HasNative()) - { - return; - } - - ReleaseNative(); + if (HasNative()) + ReleaseNative(); } #if WITH_EDITOR diff --git a/Source/AGXUnreal/Private/OpenPLX/PLX_SignalHandlerComponent.cpp b/Source/AGXUnreal/Private/OpenPLX/PLX_SignalHandlerComponent.cpp index 61d98a87f..4794f8016 100644 --- a/Source/AGXUnreal/Private/OpenPLX/PLX_SignalHandlerComponent.cpp +++ b/Source/AGXUnreal/Private/OpenPLX/PLX_SignalHandlerComponent.cpp @@ -5,13 +5,23 @@ // AGX Dynamics for Unreal includes. #include "AGX_Simulation.h" #include "Constraints/AGX_ConstraintComponent.h" +#include "Import/AGX_ModelSourceComponent.h" #include "Utilities/AGX_ObjectUtilities.h" +#include "Utilities/AGX_StringUtilities.h" UPLX_SignalHandlerComponent::UPLX_SignalHandlerComponent() { PrimaryComponentTick.bCanEverTick = false; } +bool UPLX_SignalHandlerComponent::Send(FPLX_LinearVelocityMotorVelocityInput Input, double Value) +{ + if (!SignalHandler.IsInitialized()) + return false; + + return SignalHandler.Send(Input, Value); +} + namespace PLX_SignalHandlerComponent_helpers { TArray CollectConstraintBarriers(AActor* Owner) @@ -30,6 +40,20 @@ namespace PLX_SignalHandlerComponent_helpers return ConstraintBarriers; } + + TOptional GetPLXFilePath(AActor* Owner) + { + if (Owner == nullptr) + return {}; + + auto ModelSource = Owner->GetComponentByClass(); + if (ModelSource == nullptr) + return {}; + + // Todo: this must point to the corresponding PLX file in the project files, copied from + // import, not the source file itself. This is to ensure portability of standalone-projects. + return ModelSource->FilePath; + } } void UPLX_SignalHandlerComponent::BeginPlay() @@ -37,6 +61,17 @@ void UPLX_SignalHandlerComponent::BeginPlay() using namespace PLX_SignalHandlerComponent_helpers; Super::BeginPlay(); + auto PLXFile = GetPLXFilePath(GetOwner()); + if (!PLXFile.IsSet()) + { + UE_LOG( + LogAGX, Warning, + TEXT("UPLX_SignalHandlerComponent '%s' in '%s' was unable to get OpenPLX file path " + "from UAGX_ModelSourceComponent. OpenPLX Signals will not work properly."), + *GetName(), *GetLabelSafe(GetOwner())); + return; + } + auto Sim = UAGX_Simulation::GetFrom(this); auto SimulationBarrier = Sim != nullptr ? Sim->GetNative() : nullptr; if (SimulationBarrier == nullptr) @@ -49,6 +84,10 @@ void UPLX_SignalHandlerComponent::BeginPlay() TArray ConstraintBarriers = CollectConstraintBarriers(GetOwner()); // Initialize SignalHandler in Barrier module. - SignalHandler.Init(*SimulationBarrier, ConstraintBarriers); + SignalHandler.Init(*PLXFile, *SimulationBarrier, ConstraintBarriers); } +void UPLX_SignalHandlerComponent::EndPlay(const EEndPlayReason::Type Reason) +{ + SignalHandler.ReleaseNatives(); +} diff --git a/Source/AGXUnrealEditor/Public/AGX_ImportEnums.h b/Source/AGXUnreal/Public/Import/AGX_ImportEnums.h similarity index 100% rename from Source/AGXUnrealEditor/Public/AGX_ImportEnums.h rename to Source/AGXUnreal/Public/Import/AGX_ImportEnums.h diff --git a/Source/AGXUnreal/Public/AGX_ModelSourceComponent.h b/Source/AGXUnreal/Public/Import/AGX_ModelSourceComponent.h similarity index 98% rename from Source/AGXUnreal/Public/AGX_ModelSourceComponent.h rename to Source/AGXUnreal/Public/Import/AGX_ModelSourceComponent.h index 0bc7d9f4d..9b0591e9a 100644 --- a/Source/AGXUnreal/Public/AGX_ModelSourceComponent.h +++ b/Source/AGXUnreal/Public/Import/AGX_ModelSourceComponent.h @@ -5,6 +5,7 @@ // Unreal Engine includes. #include "Components/ActorComponent.h" #include "CoreMinimal.h" +#include "Import/AGX_ImportEnums.h" #include "AGX_ModelSourceComponent.generated.h" diff --git a/Source/AGXUnreal/Public/OpenPLX/PLX_SignalHandlerComponent.h b/Source/AGXUnreal/Public/OpenPLX/PLX_SignalHandlerComponent.h index e6280484a..275e08101 100644 --- a/Source/AGXUnreal/Public/OpenPLX/PLX_SignalHandlerComponent.h +++ b/Source/AGXUnreal/Public/OpenPLX/PLX_SignalHandlerComponent.h @@ -8,6 +8,7 @@ // Unreal Engine includes. #include "Components/ActorComponent.h" #include "CoreMinimal.h" +#include "OpenPLX/PLX_Inputs.h" #include "PLX_SignalHandlerComponent.generated.h" @@ -24,8 +25,12 @@ class AGXUNREAL_API UPLX_SignalHandlerComponent : public UActorComponent public: UPLX_SignalHandlerComponent(); + UFUNCTION(BlueprintCallable, Category = "OpenPLX") + bool Send(FPLX_LinearVelocityMotorVelocityInput Input, double Value); + //~ Begin UActorComponent Interface virtual void BeginPlay() override; + virtual void EndPlay(const EEndPlayReason::Type Reason) override; //~ End UActorComponent Interface private: diff --git a/Source/AGXUnrealBarrier/Private/AGXSimObjectsReader.cpp b/Source/AGXUnrealBarrier/Private/AGXSimObjectsReader.cpp index d77951d6a..b4aa8ceff 100644 --- a/Source/AGXUnrealBarrier/Private/AGXSimObjectsReader.cpp +++ b/Source/AGXUnrealBarrier/Private/AGXSimObjectsReader.cpp @@ -5,7 +5,6 @@ // AGX Dynamics for Unreal includes. #include "AGXBarrierFactories.h" #include "AGX_Check.h" -#include "AGX_Environment.h" #include "AGX_LogCategory.h" #include "BarrierOnly/AGXRefs.h" #include "RigidBodyBarrier.h" @@ -16,6 +15,9 @@ #include "TypeConversions.h" #include "Utilities/PLXUtilities.h" +// OpenPLX includes. +#include "Brick/brickagx/BrickToAgxMapper.h" + // AGX Dynamics includes. #include "BeginAGXIncludes.h" #include @@ -31,24 +33,7 @@ #include #include -// OpenPLX includes. -#include "Brick/brick/BrickContext.h" -#include "Brick/brick/BrickContextInternal.h" -#include "Brick/Brick/BrickCoreApi.h" -#include "Brick/brickagx/AgxCache.h" -#include "Brick/brickagx/BrickAgxApi.h" -#include "Brick/brickagx/BrickToAgxMapper.h" -#include "Brick/Math/Math_all.h" -#include "Brick/Physics/Physics_all.h" -#include "Brick/Physics1D/Physics1D_all.h" -#include "Brick/Physics3D/Physics3D_all.h" -#include "Brick/DriveTrain/DriveTrain_all.h" -#include "Brick/Robotics/Robotics_all.h" -#include "Brick/Simulation/Simulation_all.h" -#include "Brick/Vehicles/Vehicles_all.h" -#include "Brick/Terrain/Terrain_all.h" -#include "Brick/Visuals/Visuals_all.h" -#include "Brick/Urdf/Urdf_all.h" + // In 2.28 including Cable.h causes a preprocessor macro named DEPRECATED to be defined. This // conflicts with a macro with the same name in Unreal. Undeffing the Unreal one. @@ -650,66 +635,11 @@ bool FAGXSimObjectsReader::ReadUrdf( return true; } -namespace AGXSimObjectsReader_helpers -{ - std::shared_ptr CreatePLXContext( - std::shared_ptr AGXCache) - { - const FString PLXBundlesPath = - FPaths::Combine(FAGX_Environment::GetPluginSourcePath(), "Thirdparty", "agx", "brickbundles"); - auto PLXCtx = std::make_shared( - std::vector({Convert(PLXBundlesPath)})); - - auto InternalContext = Brick::Core::Api::BrickContextInternal::fromContext(*PLXCtx); - auto EvalCtx = InternalContext->evaluatorContext().get(); - - Math_register_factories(EvalCtx); - Physics_register_factories(EvalCtx); - Physics1D_register_factories(EvalCtx); - Physics3D_register_factories(EvalCtx); - DriveTrain_register_factories(EvalCtx); - Robotics_register_factories(EvalCtx); - Simulation_register_factories(EvalCtx); - Vehicles_register_factories(EvalCtx); - Terrain_register_factories(EvalCtx); - Visuals_register_factories(EvalCtx); - Urdf_register_factories(EvalCtx); - - BrickAgx::register_plugins(*PLXCtx, AGXCache); - return PLXCtx; - } - - Brick::Core::ObjectPtr LoadModelFromFile( - const std::string& OpenPLXFile, std::shared_ptr AGXCache) - { - auto Context = CreatePLXContext(AGXCache); - if (Context == nullptr) - { - UE_LOG(LogAGX, Error, TEXT("Error Creating OpenPLX Context")); - return nullptr; - } - - auto LoadedModel = Brick::Core::Api::loadModelFromFile(OpenPLXFile, {}, *Context); - - if (Context->hasErrors()) - { - LoadedModel = nullptr; - for (auto Error : Context->getErrors()) - UE_LOG(LogAGX, Error, TEXT("Error in OpenPLX Context : %d"), Error->getErrorCode()); - - return nullptr; - } - - return LoadedModel; - } -} - bool FAGXSimObjectsReader::ReadOpenPLXFile( const FString& Filename, FSimulationObjectCollection& OutSimObjects) { - using namespace AGXSimObjectsReader_helpers; std::shared_ptr AGXCache; - Brick::Core::ObjectPtr PLXModel = LoadModelFromFile(Convert(Filename), AGXCache); + Brick::Core::ObjectPtr PLXModel = FPLXUtilities::LoadModel(Filename, AGXCache); if (PLXModel == nullptr) { UE_LOG( @@ -729,7 +659,7 @@ bool FAGXSimObjectsReader::ReadOpenPLXFile( // Read PLX inputs. auto System = std::dynamic_pointer_cast(PLXModel); - OutSimObjects.GetPLXInputs() = FBrickUtilities::GetInputs(System.get()); + OutSimObjects.GetPLXInputs() = FPLXUtilities::GetInputs(System.get()); return true; } diff --git a/Source/AGXUnrealBarrier/Private/OpenPLX/PLXSignalHandler.cpp b/Source/AGXUnrealBarrier/Private/OpenPLX/PLXSignalHandler.cpp index 3ac43a539..39a15b213 100644 --- a/Source/AGXUnrealBarrier/Private/OpenPLX/PLXSignalHandler.cpp +++ b/Source/AGXUnrealBarrier/Private/OpenPLX/PLXSignalHandler.cpp @@ -3,10 +3,119 @@ #include "OpenPLX/PLXSignalHandler.h" // AGX Dynamics for Unreal includes. +#include "AGX_Check.h" +#include "AGX_LogCategory.h" +#include "BarrierOnly/AGXRefs.h" +#include "BarrierOnly/OpenPLX/OpenPLXRefs.h" #include "Constraints/ConstraintBarrier.h" #include "SimulationBarrier.h" +#include "TypeConversions.h" +#include "Utilities/PLXUtilities.h" -void FPLXSignalHandler::Init(FSimulationBarrier& Simulation, TArray& Constraints) +// OpenPLX includes. +#include "Brick/brickagx/InputSignalListener.h" +#include "Brick/brickagx/Signals.h" +#include "Brick/Physics/Signals/RealInputSignal.h" +#include "Brick/Physics3D/Signals/LinearVelocityMotorVelocityInput.h" + +static Brick::Core::ObjectPtr PLXModel = nullptr; // Todo: store somewhere to be re-used! + +void FPLXSignalHandler::Init( + const FString& PLXFile, FSimulationBarrier& Simulation, + TArray& Constraints) +{ + check(Simulation.HasNative()); + + // Todo: this PLXModel (tree) should be cached somewhere and re-used for other instances of the + // same PLX model in the same world, so that LoadModel only has to be done once for any givel + // PLX model. + std::shared_ptr AGXCache; + PLXModel = FPLXUtilities::LoadModel(PLXFile, AGXCache); + if (PLXModel == nullptr) + { + UE_LOG( + LogAGX, Error, + TEXT("Could not read OpenPLX file '%s'. The Log category LogAGXDynamics may include " + "more details."), + *PLXFile); + return; + } + + // Here, we re-use the InputSignalListener in OpenPLX to propagate signals. + // It expects an assembly with the relevant contraints (and powerlines). + // If no powerline exists in the OpenPLX model, one must be added anyway because the + // InputSignalListener assumes one. + NativeAssemblyRef = std::make_shared(new agxSDK::Assembly()); + for (FConstraintBarrier* Constraint : Constraints) + { + AGX_CHECK(Constraint->HasNative()); + NativeAssemblyRef->Native->add(Constraint->GetNative()->Native); + } + + agxPowerLine::PowerLineRef RequiredDummyPowerLine = new agxPowerLine::PowerLine(); + RequiredDummyPowerLine->setName(agx::Name("BrickPowerLine")); + NativeAssemblyRef->Native->add(RequiredDummyPowerLine); + NativeInputSignalHandlerRef = + std::make_shared(NativeAssemblyRef->Native); + Simulation.GetNative()->Native->add(NativeInputSignalHandlerRef->Native.get()); + + // Todo: build up signals map for fast lookup later. +} + +bool FPLXSignalHandler::IsInitialized() const +{ + return NativeAssemblyRef != nullptr && NativeInputSignalHandlerRef != nullptr; +} + +void FPLXSignalHandler::ReleaseNatives() { + NativeInputSignalHandlerRef = nullptr; + PLXModel = nullptr; + NativeAssemblyRef = nullptr; +} + +namespace PLXSignalHandler_helpers +{ + void findAllSignalInputs( + Brick::Physics3D::System* System, + std::vector>& OutSignalInputs) + { + for (auto& Subsystem : System->getValues()) + { + findAllSignalInputs(System, OutSignalInputs); + } + + for (auto& SignalInput : System->getValues()) + { + OutSignalInputs.push_back(SignalInput); + } + } +} + +bool FPLXSignalHandler::Send(FPLX_LinearVelocityMotorVelocityInput Input, double Value) +{ + AGX_CHECK(IsInitialized()); + + std::vector> SignalInputs; + auto System = std::dynamic_pointer_cast(PLXModel); + if (System == nullptr) + return false; + + PLXSignalHandler_helpers::findAllSignalInputs(System.get(), SignalInputs); + + // Todo - map these in advance. + for (std::shared_ptr& SignalInput : SignalInputs) + { + auto LinearVelInput = + std::dynamic_pointer_cast( + SignalInput); + if (LinearVelInput == nullptr) + continue; + + auto Signal = Brick::Physics::Signals::RealInputSignal::create(ConvertDistanceToAGX(Value), LinearVelInput); + BrickAgx::Signals::sendInputSignal(Signal); + return true; + } + return false; } diff --git a/Source/AGXUnrealBarrier/Private/Utilities/PLXUtilities.cpp b/Source/AGXUnrealBarrier/Private/Utilities/PLXUtilities.cpp index 7da2f51c5..d23d641a6 100644 --- a/Source/AGXUnrealBarrier/Private/Utilities/PLXUtilities.cpp +++ b/Source/AGXUnrealBarrier/Private/Utilities/PLXUtilities.cpp @@ -3,11 +3,27 @@ #include "Utilities/PLXUtilities.h" // AGX Dynamics for Unreal includes. +#include "AGX_Environment.h" #include "TypeConversions.h" // OpenPLX includes. +#include "Brick/brick/BrickContext.h" +#include "Brick/brick/BrickContextInternal.h" +#include "Brick/Brick/BrickCoreApi.h" +#include "Brick/brickagx/BrickAgxApi.h" +#include "Brick/Math/Math_all.h" +#include "Brick/Physics/Physics_all.h" +#include "Brick/Physics1D/Physics1D_all.h" #include "Brick/Physics3D/Physics3D_all.h" -#include "Physics3D/Signals/LinearVelocityMotorVelocityInput.h" +#include "Brick/Physics3D/Signals/LinearVelocityMotorVelocityInput.h" +#include "Brick/DriveTrain/DriveTrain_all.h" +#include "Brick/Robotics/Robotics_all.h" +#include "Brick/Simulation/Simulation_all.h" +#include "Brick/Vehicles/Vehicles_all.h" +#include "Brick/Terrain/Terrain_all.h" +#include "Brick/Visuals/Visuals_all.h" +#include "Brick/Urdf/Urdf_all.h" + // Unreal Engine includes. #include "Templates/UniquePtr.h" @@ -40,9 +56,75 @@ namespace PLXUtilities_helpers UE_LOG(LogAGX, Warning, TEXT("Unhandled PLX Input: %s"), *Convert(Input->getName())); } } + + std::shared_ptr CreatePLXContext( + std::shared_ptr AGXCache) + { + const FString PLXBundlesPath = FPaths::Combine( + FAGX_Environment::GetPluginSourcePath(), "Thirdparty", "agx", "brickbundles"); + auto PLXCtx = std::make_shared( + std::vector({Convert(PLXBundlesPath)})); + + auto InternalContext = Brick::Core::Api::BrickContextInternal::fromContext(*PLXCtx); + auto EvalCtx = InternalContext->evaluatorContext().get(); + + Math_register_factories(EvalCtx); + Physics_register_factories(EvalCtx); + Physics1D_register_factories(EvalCtx); + Physics3D_register_factories(EvalCtx); + DriveTrain_register_factories(EvalCtx); + Robotics_register_factories(EvalCtx); + Simulation_register_factories(EvalCtx); + Vehicles_register_factories(EvalCtx); + Terrain_register_factories(EvalCtx); + Visuals_register_factories(EvalCtx); + Urdf_register_factories(EvalCtx); + + BrickAgx::register_plugins(*PLXCtx, AGXCache); + return PLXCtx; + } + + Brick::Core::ObjectPtr LoadModelFromFile( + const std::string& OpenPLXFile, std::shared_ptr AGXCache) + { + auto Context = CreatePLXContext(AGXCache); + if (Context == nullptr) + { + UE_LOG(LogAGX, Error, TEXT("Error Creating OpenPLX Context")); + return nullptr; + } + + auto LoadedModel = Brick::Core::Api::loadModelFromFile(OpenPLXFile, {}, *Context); + + if (Context->hasErrors()) + { + LoadedModel = nullptr; + for (auto Error : Context->getErrors()) + UE_LOG(LogAGX, Error, TEXT("Error in OpenPLX Context : %d"), Error->getErrorCode()); + + return nullptr; + } + + return LoadedModel; + } +} + +Brick::Core::ObjectPtr FPLXUtilities::LoadModel( + const FString& Filename, std::shared_ptr AGXCache) +{ + if (!FPaths::FileExists(Filename)) + { + UE_LOG( + LogAGX, Warning, + TEXT("Could not read OpenPLX file '%s'. The file does not exist."), + *Filename); + return nullptr; + } + + return PLXUtilities_helpers::LoadModelFromFile(Convert(Filename), AGXCache); } -TArray> FBrickUtilities::GetInputs(Brick::Physics3D::System* System) +TArray> FPLXUtilities::GetInputs(Brick::Physics3D::System* System) { TArray> Inputs; PLXUtilities_helpers::GetInputs(System, Inputs); diff --git a/Source/AGXUnrealBarrier/Private/Utilities/PLXUtilities.h b/Source/AGXUnrealBarrier/Private/Utilities/PLXUtilities.h index 3018cf172..26545dce9 100644 --- a/Source/AGXUnrealBarrier/Private/Utilities/PLXUtilities.h +++ b/Source/AGXUnrealBarrier/Private/Utilities/PLXUtilities.h @@ -6,6 +6,10 @@ #include "AGX_LogCategory.h" #include "OpenPLX/PLX_Inputs.h" +// OpenPLX includes. +#include "Brick/brick/Object.h" +#include "Brick/brickagx/AgxCache.h" + namespace Brick { namespace Physics3D @@ -14,8 +18,11 @@ namespace Brick } } -class FBrickUtilities +class FPLXUtilities { public: + static Brick::Core::ObjectPtr LoadModel( + const FString& Filename, std::shared_ptr AGXCache); + static TArray> GetInputs(Brick::Physics3D::System* System); }; diff --git a/Source/AGXUnrealBarrier/Public/BarrierOnly/AGXRefs.h b/Source/AGXUnrealBarrier/Public/BarrierOnly/AGXRefs.h index b36e2450d..ee8be05b1 100644 --- a/Source/AGXUnrealBarrier/Public/BarrierOnly/AGXRefs.h +++ b/Source/AGXUnrealBarrier/Public/BarrierOnly/AGXRefs.h @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -190,6 +191,17 @@ struct FSimulationRef } }; +struct FAssemblyRef +{ + agxSDK::AssemblyRef Native; + + FAssemblyRef() = default; + FAssemblyRef(agxSDK::Assembly* InNative) + : Native(InNative) + { + } +}; + struct FShovelRef { agxTerrain::ShovelRef Native; diff --git a/Source/AGXUnrealBarrier/Public/BarrierOnly/OpenPLX/OpenPLXRefs.h b/Source/AGXUnrealBarrier/Public/BarrierOnly/OpenPLX/OpenPLXRefs.h new file mode 100644 index 000000000..739fe140a --- /dev/null +++ b/Source/AGXUnrealBarrier/Public/BarrierOnly/OpenPLX/OpenPLXRefs.h @@ -0,0 +1,24 @@ +#pragma once + +// OpenPLX includes. +#include "Brick/brickagx/InputSignalListener.h" + +// AGX Dynamics includes. +#include "BeginAGXIncludes.h" +#include +#include +#include "EndAGXIncludes.h" + +// Standard library includes. +#include + +struct FInputSignalHandlerRef +{ + agx::ref_ptr Native; + + FInputSignalHandlerRef() = default; + FInputSignalHandlerRef(agxSDK::Assembly* Assembly) + : Native(new BrickAgx::InputSignalListener(Assembly)) + { + } +}; diff --git a/Source/AGXUnrealBarrier/Public/OpenPLX/PLXSignalHandler.h b/Source/AGXUnrealBarrier/Public/OpenPLX/PLXSignalHandler.h index 2ded41bfc..64b0a95ca 100644 --- a/Source/AGXUnrealBarrier/Public/OpenPLX/PLXSignalHandler.h +++ b/Source/AGXUnrealBarrier/Public/OpenPLX/PLXSignalHandler.h @@ -2,11 +2,30 @@ #pragma once + +// Standard library includes. +#include + class FConstraintBarrier; class FSimulationBarrier; +struct FAssemblyRef; +struct FInputSignalHandlerRef; +struct FPLX_LinearVelocityMotorVelocityInput; + class AGXUNREALBARRIER_API FPLXSignalHandler { public: - void Init(FSimulationBarrier& Simulation, TArray& Constraints); + void Init(const FString& PLXFile, FSimulationBarrier& Simulation, TArray& Constraints); + + bool IsInitialized() const; + + void ReleaseNatives(); + + // todo: match the base class RealInput on the PLX side for less overloads. + bool Send(FPLX_LinearVelocityMotorVelocityInput Input, double Value); + +private: + std::shared_ptr NativeAssemblyRef; + std::shared_ptr NativeInputSignalHandlerRef; }; diff --git a/Source/AGXUnrealEditor/Private/AGXUnrealEditor.cpp b/Source/AGXUnrealEditor/Private/AGXUnrealEditor.cpp index 604a80ce1..24b033126 100644 --- a/Source/AGXUnrealEditor/Private/AGXUnrealEditor.cpp +++ b/Source/AGXUnrealEditor/Private/AGXUnrealEditor.cpp @@ -27,7 +27,7 @@ #include "AGX_RigidBodyReference.h" #include "AGX_Real.h" #include "AGX_RealDetails.h" -#include "AGX_ModelSourceComponent.h" +#include "Import/AGX_ModelSourceComponent.h" #include "AGX_ModelSourceComponentCustomization.h" #include "AGX_Simulation.h" #include "AGX_SimulationCustomization.h" diff --git a/Source/AGXUnrealEditor/Private/AGX_ImporterToBlueprint.cpp b/Source/AGXUnrealEditor/Private/AGX_ImporterToBlueprint.cpp index 638e910ea..052d2f60f 100644 --- a/Source/AGXUnrealEditor/Private/AGX_ImporterToBlueprint.cpp +++ b/Source/AGXUnrealEditor/Private/AGX_ImporterToBlueprint.cpp @@ -4,11 +4,11 @@ // AGX Dynamics for Unreal includes. #include "AGX_Check.h" -#include "AGX_ImportEnums.h" +#include "Import/AGX_ImportEnums.h" #include "AGX_ImportSettings.h" #include "AGX_LogCategory.h" #include "AGX_ObserverFrameComponent.h" -#include "AGX_ModelSourceComponent.h" +#include "Import/AGX_ModelSourceComponent.h" #include "AGX_RigidBodyComponent.h" #include "AGX_SimObjectsImporterHelper.h" #include "AGXSimObjectsReader.h" diff --git a/Source/AGXUnrealEditor/Private/AGX_ModelSourceComponentCustomization.cpp b/Source/AGXUnrealEditor/Private/AGX_ModelSourceComponentCustomization.cpp index e76d4556e..b512a62b2 100644 --- a/Source/AGXUnrealEditor/Private/AGX_ModelSourceComponentCustomization.cpp +++ b/Source/AGXUnrealEditor/Private/AGX_ModelSourceComponentCustomization.cpp @@ -6,7 +6,7 @@ #include "AGX_Check.h" #include "AGX_ImporterToBlueprint.h" #include "AGX_ImportSettings.h" -#include "AGX_ModelSourceComponent.h" +#include "Import/AGX_ModelSourceComponent.h" #include "Utilities/AGX_BlueprintUtilities.h" #include "Utilities/AGX_EditorUtilities.h" #include "Utilities/AGX_NotificationUtilities.h" diff --git a/Source/AGXUnrealEditor/Private/AGX_SimObjectsImporterHelper.cpp b/Source/AGXUnrealEditor/Private/AGX_SimObjectsImporterHelper.cpp index d1ac3c559..1a2a9e391 100644 --- a/Source/AGXUnrealEditor/Private/AGX_SimObjectsImporterHelper.cpp +++ b/Source/AGXUnrealEditor/Private/AGX_SimObjectsImporterHelper.cpp @@ -5,7 +5,7 @@ // AGX Dynamics for Unreal includes. #include "AGX_Check.h" #include "AGX_LogCategory.h" -#include "AGX_ModelSourceComponent.h" +#include "Import/AGX_ModelSourceComponent.h" #include "AGX_ObserverFrameComponent.h" #include "AGX_RigidBodyComponent.h" #include "AMOR/AGX_AmorEnums.h" diff --git a/Source/AGXUnrealEditor/Private/AGX_SimObjectsImporterHelper.h b/Source/AGXUnrealEditor/Private/AGX_SimObjectsImporterHelper.h index 1c9b14d98..9c7845ce1 100644 --- a/Source/AGXUnrealEditor/Private/AGX_SimObjectsImporterHelper.h +++ b/Source/AGXUnrealEditor/Private/AGX_SimObjectsImporterHelper.h @@ -3,7 +3,7 @@ #pragma once // AGX Dynamics for Unreal includes. -#include "AGX_ImportEnums.h" +#include "Import/AGX_ImportEnums.h" #include "Utilities/AGX_ImportUtilities.h" // Unreal Engine includes. diff --git a/Source/AGXUnrealEditor/Private/Utilities/AGX_EditorUtilities.cpp b/Source/AGXUnrealEditor/Private/Utilities/AGX_EditorUtilities.cpp index a082455b2..3c84a4795 100644 --- a/Source/AGXUnrealEditor/Private/Utilities/AGX_EditorUtilities.cpp +++ b/Source/AGXUnrealEditor/Private/Utilities/AGX_EditorUtilities.cpp @@ -5,7 +5,7 @@ // AGX Dynamics for Unreal includes. #include "AGX_ImporterToBlueprint.h" #include "AGX_LogCategory.h" -#include "AGX_ModelSourceComponent.h" +#include "Import/AGX_ModelSourceComponent.h" #include "AGX_RigidBodyComponent.h" #include "Shapes/AGX_ShapeComponent.h" #include "Shapes/AGX_SphereShapeComponent.h" diff --git a/Source/AGXUnrealEditor/Public/AGX_ImportSettings.h b/Source/AGXUnrealEditor/Public/AGX_ImportSettings.h index b3361d2ef..384447295 100644 --- a/Source/AGXUnrealEditor/Public/AGX_ImportSettings.h +++ b/Source/AGXUnrealEditor/Public/AGX_ImportSettings.h @@ -3,7 +3,7 @@ #pragma once // AGX Dynamics for Unreal includes. -#include "AGX_ImportEnums.h" +#include "Import/AGX_ImportEnums.h" struct FAGX_ImportSettings { diff --git a/Source/AGXUnrealEditor/Public/Utilities/AGX_ImportUtilities.h b/Source/AGXUnrealEditor/Public/Utilities/AGX_ImportUtilities.h index 9393f3fb1..a3554b4ac 100644 --- a/Source/AGXUnrealEditor/Public/Utilities/AGX_ImportUtilities.h +++ b/Source/AGXUnrealEditor/Public/Utilities/AGX_ImportUtilities.h @@ -3,7 +3,7 @@ #pragma once // AGX Dynamics for Unreal includes. -#include "AGX_ImportEnums.h" +#include "Import/AGX_ImportEnums.h" #include "AGX_LogCategory.h" // Unreal Engine includes. diff --git a/Source/AGXUnrealEditor/Public/Widgets/AGX_ImportDialogBase.h b/Source/AGXUnrealEditor/Public/Widgets/AGX_ImportDialogBase.h index 85acdf681..4823027f5 100644 --- a/Source/AGXUnrealEditor/Public/Widgets/AGX_ImportDialogBase.h +++ b/Source/AGXUnrealEditor/Public/Widgets/AGX_ImportDialogBase.h @@ -3,7 +3,7 @@ #pragma once // AGX Dynamics for Unreal includes. -#include "AGX_ImportEnums.h" +#include "Import/AGX_ImportEnums.h" #include "AGX_ImportSettings.h" // Unreal Engine includes. From e8d56d763eb4f2fa35e4f883f4eac0c0ecc838ad Mon Sep 17 00:00:00 2001 From: Josef Date: Tue, 12 Nov 2024 13:36:45 +0100 Subject: [PATCH 19/31] Add SignalHandlerComponent during import --- .../Private/AGX_ImporterToBlueprint.cpp | 35 +++++++++++++++++++ .../Private/AGX_SimObjectsImporterHelper.cpp | 23 ++++++++++++ .../Private/AGX_SimObjectsImporterHelper.h | 5 +++ .../Widgets/AGX_SynchronizeModelDialog.cpp | 4 ++- .../Public/AGX_ImportSettings.h | 1 + .../AGXDynamicsLibrary.Build.cs | 2 +- 6 files changed, 68 insertions(+), 2 deletions(-) diff --git a/Source/AGXUnrealEditor/Private/AGX_ImporterToBlueprint.cpp b/Source/AGXUnrealEditor/Private/AGX_ImporterToBlueprint.cpp index 052d2f60f..40a350e0f 100644 --- a/Source/AGXUnrealEditor/Private/AGX_ImporterToBlueprint.cpp +++ b/Source/AGXUnrealEditor/Private/AGX_ImporterToBlueprint.cpp @@ -34,6 +34,7 @@ #include "Materials/ContactMaterialBarrier.h" #include "Materials/AGX_ContactMaterialRegistrarComponent.h" #include "OpenPLX/PLX_Inputs.h" +#include "OpenPLX/PLX_SignalHandlerComponent.h" #include "Shapes/AGX_BoxShapeComponent.h" #include "Shapes/AGX_SphereShapeComponent.h" #include "Shapes/AGX_CapsuleShapeComponent.h" @@ -608,6 +609,9 @@ namespace return EImportResult::ErrorDuringInstantiations; } + // OpenPLX imports should always get a PLX_SignalHandlerComponent. + Helper.InstantiateModelSourceComponent(ImportedActor); + return EImportResult::Success; } @@ -1071,6 +1075,11 @@ namespace AGX_ImporterToBlueprint_SynchronizeModel_helpers } } } + else if (auto Sh = Cast(Component)) + { + AGX_CHECK(SignalHandlerComponent == nullptr); + SignalHandlerComponent = Node; + } else if (auto St = Cast(Component)) { // Handled by gathering information from the ModelSourceComponent since a Static @@ -1155,6 +1164,7 @@ namespace AGX_ImporterToBlueprint_SynchronizeModel_helpers USCS_Node* CollisionGroupDisablerComponent = nullptr; USCS_Node* ContactMaterialRegistrarComponent = nullptr; USCS_Node* ModelSourceComponent = nullptr; + USCS_Node* SignalHandlerComponent = nullptr; // OpenPLX USCS_Node* RootComponent = nullptr; }; @@ -1898,6 +1908,28 @@ namespace AGX_ImporterToBlueprint_SynchronizeModel_helpers *Cast(ModelSourceComponent->ComponentTemplate)); } + void AddOrUpdateSignalHandlerComponent( + UBlueprint& BaseBP, SCSNodeCollection& SCSNodes, FAGX_SimObjectsImporterHelper& Helper) + { + USCS_Node* SignalHandlerComponent = nullptr; + if (SCSNodes.SignalHandlerComponent == nullptr) + { + SignalHandlerComponent = BaseBP.SimpleConstructionScript->CreateNode( + UPLX_SignalHandlerComponent::StaticClass(), + FName(FAGX_ImportUtilities::GetUnsetUniqueImportName())); + BaseBP.SimpleConstructionScript->GetDefaultSceneRootNode()->AddChildNode( + SignalHandlerComponent); + SCSNodes.SignalHandlerComponent = SignalHandlerComponent; + } + else + { + SignalHandlerComponent = SCSNodes.ModelSourceComponent; + } + + Helper.UpdateSignalHandlerComponent( + *Cast(SignalHandlerComponent->ComponentTemplate)); + } + FString GetModelDirectoryPathFromBaseBlueprint(UBlueprint& BaseBP) { const FString ParentDir = FPaths::GetPath(BaseBP.GetPathName()); @@ -1968,6 +2000,9 @@ namespace AGX_ImporterToBlueprint_SynchronizeModel_helpers 5.f, FText::FromString("Synchronizing Model Source Component")); AddOrUpdateModelSourceComponent(BaseBP, SCSNodes, Helper); + if (Settings.ImportType == EAGX_ImportType::Plx) + AddOrUpdateSignalHandlerComponent(BaseBP, SCSNodes, Helper); // OpenPLX only. + ImportTask.EnterProgressFrame(30.f, FText::FromString("Finalizing Synchronization")); Helper.FinalizeImport(); } diff --git a/Source/AGXUnrealEditor/Private/AGX_SimObjectsImporterHelper.cpp b/Source/AGXUnrealEditor/Private/AGX_SimObjectsImporterHelper.cpp index 1a2a9e391..7ce6a82ee 100644 --- a/Source/AGXUnrealEditor/Private/AGX_SimObjectsImporterHelper.cpp +++ b/Source/AGXUnrealEditor/Private/AGX_SimObjectsImporterHelper.cpp @@ -35,6 +35,7 @@ #include "Materials/AGX_ShapeMaterial.h" #include "Materials/ContactMaterialBarrier.h" #include "Materials/ShapeMaterialBarrier.h" +#include "OpenPLX/PLX_SignalHandlerComponent.h" #include "RigidBodyBarrier.h" #include "Shapes/AGX_BoxShapeComponent.h" #include "Shapes/AGX_CapsuleShapeComponent.h" @@ -2120,6 +2121,28 @@ void FAGX_SimObjectsImporterHelper::UpdateObserverFrameComponent( Component.ImportGuid = ObserverGuid; } +UPLX_SignalHandlerComponent* FAGX_SimObjectsImporterHelper::InstantiateSignalHandlerComponent( + AActor& Owner) +{ + UPLX_SignalHandlerComponent* Component = NewObject(&Owner); + if (Component == nullptr) + return nullptr; + + UpdateSignalHandlerComponent(*Component); + Component->SetFlags(RF_Transactional); + Owner.AddInstanceComponent(Component); + Component->RegisterComponent(); + Component->PostEditChange(); + + return Component; +} + +void FAGX_SimObjectsImporterHelper::UpdateSignalHandlerComponent( + UPLX_SignalHandlerComponent& Component) +{ + FAGX_ImportUtilities::Rename(Component, "PLX_SignalHandler"); +} + UAGX_RigidBodyComponent* FAGX_SimObjectsImporterHelper::GetBody( const FRigidBodyBarrier& Barrier, bool LogErrorIfNotFound) { diff --git a/Source/AGXUnrealEditor/Private/AGX_SimObjectsImporterHelper.h b/Source/AGXUnrealEditor/Private/AGX_SimObjectsImporterHelper.h index 9c7845ce1..b64d96134 100644 --- a/Source/AGXUnrealEditor/Private/AGX_SimObjectsImporterHelper.h +++ b/Source/AGXUnrealEditor/Private/AGX_SimObjectsImporterHelper.h @@ -57,6 +57,7 @@ class UAGX_TrackProperties; class UAGX_TrimeshShapeComponent; class UAGX_TwoBodyTireComponent; class UAGX_WireComponent; +class UPLX_SignalHandlerComponent; // Unreal Engine classes. class AActor; @@ -227,6 +228,10 @@ struct FAGX_SimObjectsImporterHelper const FString& Name, const FGuid& ObserverGuid, const FTransform& Transform, UAGX_ObserverFrameComponent& Component, bool ForceOverwriteInstances); + UPLX_SignalHandlerComponent* InstantiateSignalHandlerComponent(AActor& Owner); + + void UpdateSignalHandlerComponent(UPLX_SignalHandlerComponent& Component); + UAGX_RigidBodyComponent* GetBody( const FRigidBodyBarrier& Barrier, bool LogErrorIfNotFound = true); diff --git a/Source/AGXUnrealEditor/Private/Widgets/AGX_SynchronizeModelDialog.cpp b/Source/AGXUnrealEditor/Private/Widgets/AGX_SynchronizeModelDialog.cpp index 8aa966b6e..40e239144 100644 --- a/Source/AGXUnrealEditor/Private/Widgets/AGX_SynchronizeModelDialog.cpp +++ b/Source/AGXUnrealEditor/Private/Widgets/AGX_SynchronizeModelDialog.cpp @@ -4,6 +4,7 @@ // AGX Dynamics for Unreal includes. #include "Utilities/AGX_EditorUtilities.h" +#include "Utilities/AGX_ImportUtilities.h" #include "Utilities/AGX_NotificationUtilities.h" #include "Utilities/AGX_SlateUtilities.h" @@ -16,7 +17,7 @@ void SAGX_SynchronizeModelDialog::Construct(const FArguments& InArgs) { - FileTypes = ".agx"; + FileTypes = ".agx;*.brick"; ImportType = EAGX_ImportType::Agx; // clang-format off @@ -84,6 +85,7 @@ TOptional SAGX_SynchronizeModelDialog::ToSynchron Settings.bIgnoreDisabledTrimeshes = bIgnoreDisabledTrimesh; Settings.bForceOverwriteProperties = bForceOverwriteProperties; Settings.bForceReassignRenderMaterials = bForceReassignRenderMaterials; + Settings.ImportType = FAGX_ImportUtilities::GetFrom(FilePath); return Settings; } diff --git a/Source/AGXUnrealEditor/Public/AGX_ImportSettings.h b/Source/AGXUnrealEditor/Public/AGX_ImportSettings.h index 384447295..9ca364e92 100644 --- a/Source/AGXUnrealEditor/Public/AGX_ImportSettings.h +++ b/Source/AGXUnrealEditor/Public/AGX_ImportSettings.h @@ -22,6 +22,7 @@ struct FAGX_ImportSettings struct FAGX_SynchronizeModelSettings { + EAGX_ImportType ImportType = EAGX_ImportType::Invalid; FString FilePath; bool bIgnoreDisabledTrimeshes = true; bool bForceOverwriteProperties = false; diff --git a/Source/ThirdParty/AGXDynamicsLibrary/AGXDynamicsLibrary.Build.cs b/Source/ThirdParty/AGXDynamicsLibrary/AGXDynamicsLibrary.Build.cs index 43615fabe..7413c9fbb 100644 --- a/Source/ThirdParty/AGXDynamicsLibrary/AGXDynamicsLibrary.Build.cs +++ b/Source/ThirdParty/AGXDynamicsLibrary/AGXDynamicsLibrary.Build.cs @@ -248,7 +248,7 @@ public AGXDynamicsLibrary(ReadOnlyTargetRules Target) : base(Target) LinkLibFiles.Add("agxROS2", LibSource.AGX); LinkLibFiles.Add("agx-nt-ros2", LibSource.AGX); - // Brick + // OpenPLX LinkLibFiles.Add("fmt", LibSource.Brick); LinkLibFiles.Add("spdlog", LibSource.Brick); LinkLibFiles.Add("brick.agx", LibSource.Brick); From 38d755168010ebb0891f9fcdd9443250b5aa5356 Mon Sep 17 00:00:00 2001 From: Josef Date: Tue, 12 Nov 2024 15:27:38 +0100 Subject: [PATCH 20/31] Fix typo --- Source/AGXUnrealEditor/Private/AGX_ImporterToBlueprint.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/AGXUnrealEditor/Private/AGX_ImporterToBlueprint.cpp b/Source/AGXUnrealEditor/Private/AGX_ImporterToBlueprint.cpp index 40a350e0f..f5f8809d2 100644 --- a/Source/AGXUnrealEditor/Private/AGX_ImporterToBlueprint.cpp +++ b/Source/AGXUnrealEditor/Private/AGX_ImporterToBlueprint.cpp @@ -610,7 +610,7 @@ namespace } // OpenPLX imports should always get a PLX_SignalHandlerComponent. - Helper.InstantiateModelSourceComponent(ImportedActor); + Helper.InstantiateSignalHandlerComponent(ImportedActor); return EImportResult::Success; } From cfebbfbf72a3d76a225e166b20e3af151b7d87d5 Mon Sep 17 00:00:00 2001 From: Josef Date: Thu, 14 Nov 2024 07:39:26 +0100 Subject: [PATCH 21/31] Prepare a PLX_ModelInfo subsystem to keep track of all native PLX trees and signals --- .../Private/OpenPLX/PLX_ModelInfo.cpp | 45 +++++++++++++++++++ .../AGXUnreal/Public/OpenPLX/PLX_ModelInfo.h | 38 ++++++++++++++++ .../Private/OpenPLX/PLXModelInfo.cpp | 23 ++++++++++ .../Private/OpenPLX/PLXSignalHandler.cpp | 10 ++--- .../Public/BarrierOnly/OpenPLX/OpenPLXRefs.h | 25 +++++++++++ .../Public/OpenPLX/PLXModelInfo.h | 23 ++++++++++ .../Public/OpenPLX/PLXSignalHandler.h | 8 ++-- 7 files changed, 162 insertions(+), 10 deletions(-) create mode 100644 Source/AGXUnreal/Private/OpenPLX/PLX_ModelInfo.cpp create mode 100644 Source/AGXUnreal/Public/OpenPLX/PLX_ModelInfo.h create mode 100644 Source/AGXUnrealBarrier/Private/OpenPLX/PLXModelInfo.cpp create mode 100644 Source/AGXUnrealBarrier/Public/OpenPLX/PLXModelInfo.h diff --git a/Source/AGXUnreal/Private/OpenPLX/PLX_ModelInfo.cpp b/Source/AGXUnreal/Private/OpenPLX/PLX_ModelInfo.cpp new file mode 100644 index 000000000..f19be23c7 --- /dev/null +++ b/Source/AGXUnreal/Private/OpenPLX/PLX_ModelInfo.cpp @@ -0,0 +1,45 @@ +// Copyright 2024, Algoryx Simulation AB. + +#pragma once + +#include "OpenPLX/PLX_ModelInfo.h" + + +UPLX_ModelInfo* UPLX_ModelInfo::GetFrom(UWorld* World) +{ + if (World == nullptr || !World->IsGameWorld()) + return nullptr; + + return World->GetSubsystem(); +} + +bool UPLX_ModelInfo::HasNative() const +{ + return Native.HasNative(); +} + +FPLXModelInfo* UPLX_ModelInfo::GetNative() +{ + if (!HasNative()) + return nullptr; + + return &Native; +} + +const FPLXModelInfo* UPLX_ModelInfo::GetNative() const +{ + if (!HasNative()) + return nullptr; + + return &Native; +} + +void UPLX_ModelInfo::Initialize(FSubsystemCollectionBase& Collection) +{ + Super::Initialize(Collection); +} + +void UPLX_ModelInfo::Deinitialize() +{ + Super::Deinitialize(); +} diff --git a/Source/AGXUnreal/Public/OpenPLX/PLX_ModelInfo.h b/Source/AGXUnreal/Public/OpenPLX/PLX_ModelInfo.h new file mode 100644 index 000000000..240ea9bcd --- /dev/null +++ b/Source/AGXUnreal/Public/OpenPLX/PLX_ModelInfo.h @@ -0,0 +1,38 @@ +// Copyright 2024, Algoryx Simulation AB. + +#pragma once + +// AGX Dynamics for Unreal includes. +#include "OpenPLX/PLXModelInfo.h" + +// Unreal Engine includes. +#include "CoreMinimal.h" +#include "LevelInstance/LevelInstanceSubsystem.h" +#include "Subsystems/GameInstanceSubsystem.h" + +#include "PLX_ModelInfo.generated.h" + + +/** + * Todo: add description. + */ +UCLASS(ClassGroup = "PLX", Category = "PLX") +class AGXUNREAL_API UPLX_ModelInfo : public ULevelInstanceSubsystem +{ + GENERATED_BODY() + +public: + static UPLX_ModelInfo* GetFrom(UWorld* World); + + bool HasNative() const; + FPLXModelInfo* GetNative(); + const FPLXModelInfo* GetNative() const; + +private: + // ~Begin USubsystem interface. + virtual void Initialize(FSubsystemCollectionBase& Collection) override; + virtual void Deinitialize() override; + // ~End USubsystem interface. + + FPLXModelInfo Native; +}; diff --git a/Source/AGXUnrealBarrier/Private/OpenPLX/PLXModelInfo.cpp b/Source/AGXUnrealBarrier/Private/OpenPLX/PLXModelInfo.cpp new file mode 100644 index 000000000..4d8ad50d1 --- /dev/null +++ b/Source/AGXUnrealBarrier/Private/OpenPLX/PLXModelInfo.cpp @@ -0,0 +1,23 @@ +// Copyright 2024, Algoryx Simulation AB. + +#include "OpenPLX/PLXModelInfo.h" + +// AGX Dynamics for Unreal includes. +#include "BarrierOnly/OpenPLX/OpenPLXRefs.h" + + +FPLXModelInfo::FPLXModelInfo() + : Native(std::make_unique()) +{ +} + +FPLXModelInfo::~FPLXModelInfo() +{ +} + +bool FPLXModelInfo::HasNative() const +{ + return Native != nullptr; +} + + diff --git a/Source/AGXUnrealBarrier/Private/OpenPLX/PLXSignalHandler.cpp b/Source/AGXUnrealBarrier/Private/OpenPLX/PLXSignalHandler.cpp index 39a15b213..feeca066d 100644 --- a/Source/AGXUnrealBarrier/Private/OpenPLX/PLXSignalHandler.cpp +++ b/Source/AGXUnrealBarrier/Private/OpenPLX/PLXSignalHandler.cpp @@ -26,6 +26,7 @@ void FPLXSignalHandler::Init( { check(Simulation.HasNative()); +#if 0 // Todo: this PLXModel (tree) should be cached somewhere and re-used for other instances of the // same PLX model in the same world, so that LoadModel only has to be done once for any givel // PLX model. @@ -60,18 +61,17 @@ void FPLXSignalHandler::Init( Simulation.GetNative()->Native->add(NativeInputSignalHandlerRef->Native.get()); // Todo: build up signals map for fast lookup later. +#endif } bool FPLXSignalHandler::IsInitialized() const { - return NativeAssemblyRef != nullptr && NativeInputSignalHandlerRef != nullptr; + return NativeOutputSignalHandlerRef != nullptr; } void FPLXSignalHandler::ReleaseNatives() { - NativeInputSignalHandlerRef = nullptr; - PLXModel = nullptr; - NativeAssemblyRef = nullptr; + NativeOutputSignalHandlerRef = nullptr; } namespace PLXSignalHandler_helpers @@ -92,7 +92,7 @@ namespace PLXSignalHandler_helpers } } -bool FPLXSignalHandler::Send(FPLX_LinearVelocityMotorVelocityInput Input, double Value) +bool FPLXSignalHandler::Send(const FPLX_LinearVelocityMotorVelocityInput& Input, double Value) { AGX_CHECK(IsInitialized()); diff --git a/Source/AGXUnrealBarrier/Public/BarrierOnly/OpenPLX/OpenPLXRefs.h b/Source/AGXUnrealBarrier/Public/BarrierOnly/OpenPLX/OpenPLXRefs.h index 739fe140a..280fcc871 100644 --- a/Source/AGXUnrealBarrier/Public/BarrierOnly/OpenPLX/OpenPLXRefs.h +++ b/Source/AGXUnrealBarrier/Public/BarrierOnly/OpenPLX/OpenPLXRefs.h @@ -2,6 +2,7 @@ // OpenPLX includes. #include "Brick/brickagx/InputSignalListener.h" +#include "Brick/brickagx/OutputSignalListener.h" // AGX Dynamics includes. #include "BeginAGXIncludes.h" @@ -11,6 +12,7 @@ // Standard library includes. #include +#include struct FInputSignalHandlerRef { @@ -22,3 +24,26 @@ struct FInputSignalHandlerRef { } }; + +struct FOutputSignalHandlerRef +{ + agx::ref_ptr Native; + + FOutputSignalHandlerRef() = default; + FOutputSignalHandlerRef( + agxSDK::Assembly* Assembly, const std::shared_ptr& PlxModel) + : Native(new BrickAgx::OutputSignalListener(Assembly, PlxModel)) + { + } +}; + +struct FPLXModelDatum +{ + agx::ref_ptr InputSignalHandler; + agxSDK::AssemblyRef Assembly; +}; + +struct FPLXModelData +{ + std::vector ModelData; +}; diff --git a/Source/AGXUnrealBarrier/Public/OpenPLX/PLXModelInfo.h b/Source/AGXUnrealBarrier/Public/OpenPLX/PLXModelInfo.h new file mode 100644 index 000000000..51ab75a37 --- /dev/null +++ b/Source/AGXUnrealBarrier/Public/OpenPLX/PLXModelInfo.h @@ -0,0 +1,23 @@ +// Copyright 2024, Algoryx Simulation AB. + +#pragma once + +// Standard library includes. +#include + +struct FPLXModelData; + +class AGXUNREALBARRIER_API FPLXModelInfo +{ +public: + FPLXModelInfo(); + ~FPLXModelInfo(); + + bool HasNative() const; + +private: + FPLXModelInfo(const FPLXModelInfo&) = delete; + void operator=(const FPLXModelInfo&) = delete; + + std::unique_ptr Native; +}; diff --git a/Source/AGXUnrealBarrier/Public/OpenPLX/PLXSignalHandler.h b/Source/AGXUnrealBarrier/Public/OpenPLX/PLXSignalHandler.h index 64b0a95ca..dd9751f31 100644 --- a/Source/AGXUnrealBarrier/Public/OpenPLX/PLXSignalHandler.h +++ b/Source/AGXUnrealBarrier/Public/OpenPLX/PLXSignalHandler.h @@ -9,9 +9,8 @@ class FConstraintBarrier; class FSimulationBarrier; -struct FAssemblyRef; -struct FInputSignalHandlerRef; struct FPLX_LinearVelocityMotorVelocityInput; +struct FOutputSignalHandlerRef; class AGXUNREALBARRIER_API FPLXSignalHandler { @@ -23,9 +22,8 @@ class AGXUNREALBARRIER_API FPLXSignalHandler void ReleaseNatives(); // todo: match the base class RealInput on the PLX side for less overloads. - bool Send(FPLX_LinearVelocityMotorVelocityInput Input, double Value); + bool Send(const FPLX_LinearVelocityMotorVelocityInput& Input, double Value); private: - std::shared_ptr NativeAssemblyRef; - std::shared_ptr NativeInputSignalHandlerRef; + std::shared_ptr NativeOutputSignalHandlerRef; }; From 05778a6c66857600385780461cb06cd5d7e0bfe5 Mon Sep 17 00:00:00 2001 From: Josef Date: Thu, 14 Nov 2024 13:28:35 +0100 Subject: [PATCH 22/31] PLXModelRegistry now holds all loaded PLX models --- .../Private/OpenPLX/PLX_ModelInfo.cpp | 45 ----- .../Private/OpenPLX/PLX_ModelRegistry.cpp | 46 ++++++ .../OpenPLX/PLX_SignalHandlerComponent.cpp | 16 +- .../{PLX_ModelInfo.h => PLX_ModelRegistry.h} | 14 +- .../Private/OpenPLX/PLXModelInfo.cpp | 23 --- .../Private/OpenPLX/PLXModelRegistry.cpp | 156 ++++++++++++++++++ .../Private/OpenPLX/PLXSignalHandler.cpp | 57 +++---- .../Public/BarrierOnly/OpenPLX/OpenPLXRefs.h | 5 +- .../Public/OpenPLX/PLXModelInfo.h | 23 --- .../Public/OpenPLX/PLXModelRegistry.h | 52 ++++++ .../Public/OpenPLX/PLXSignalHandler.h | 11 +- 11 files changed, 309 insertions(+), 139 deletions(-) delete mode 100644 Source/AGXUnreal/Private/OpenPLX/PLX_ModelInfo.cpp create mode 100644 Source/AGXUnreal/Private/OpenPLX/PLX_ModelRegistry.cpp rename Source/AGXUnreal/Public/OpenPLX/{PLX_ModelInfo.h => PLX_ModelRegistry.h} (64%) delete mode 100644 Source/AGXUnrealBarrier/Private/OpenPLX/PLXModelInfo.cpp create mode 100644 Source/AGXUnrealBarrier/Private/OpenPLX/PLXModelRegistry.cpp delete mode 100644 Source/AGXUnrealBarrier/Public/OpenPLX/PLXModelInfo.h create mode 100644 Source/AGXUnrealBarrier/Public/OpenPLX/PLXModelRegistry.h diff --git a/Source/AGXUnreal/Private/OpenPLX/PLX_ModelInfo.cpp b/Source/AGXUnreal/Private/OpenPLX/PLX_ModelInfo.cpp deleted file mode 100644 index f19be23c7..000000000 --- a/Source/AGXUnreal/Private/OpenPLX/PLX_ModelInfo.cpp +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2024, Algoryx Simulation AB. - -#pragma once - -#include "OpenPLX/PLX_ModelInfo.h" - - -UPLX_ModelInfo* UPLX_ModelInfo::GetFrom(UWorld* World) -{ - if (World == nullptr || !World->IsGameWorld()) - return nullptr; - - return World->GetSubsystem(); -} - -bool UPLX_ModelInfo::HasNative() const -{ - return Native.HasNative(); -} - -FPLXModelInfo* UPLX_ModelInfo::GetNative() -{ - if (!HasNative()) - return nullptr; - - return &Native; -} - -const FPLXModelInfo* UPLX_ModelInfo::GetNative() const -{ - if (!HasNative()) - return nullptr; - - return &Native; -} - -void UPLX_ModelInfo::Initialize(FSubsystemCollectionBase& Collection) -{ - Super::Initialize(Collection); -} - -void UPLX_ModelInfo::Deinitialize() -{ - Super::Deinitialize(); -} diff --git a/Source/AGXUnreal/Private/OpenPLX/PLX_ModelRegistry.cpp b/Source/AGXUnreal/Private/OpenPLX/PLX_ModelRegistry.cpp new file mode 100644 index 000000000..4cf7f0a6e --- /dev/null +++ b/Source/AGXUnreal/Private/OpenPLX/PLX_ModelRegistry.cpp @@ -0,0 +1,46 @@ +// Copyright 2024, Algoryx Simulation AB. + +#pragma once + +#include "OpenPLX/PLX_ModelRegistry.h" + + +UPLX_ModelRegistry* UPLX_ModelRegistry::GetFrom(UWorld* World) +{ + if (World == nullptr || !World->IsGameWorld()) + return nullptr; + + return World->GetSubsystem(); +} + +bool UPLX_ModelRegistry::HasNative() const +{ + return Native.HasNative(); +} + +FPLXModelRegistry* UPLX_ModelRegistry::GetNative() +{ + if (!HasNative()) + return nullptr; + + return &Native; +} + +const FPLXModelRegistry* UPLX_ModelRegistry::GetNative() const +{ + if (!HasNative()) + return nullptr; + + return &Native; +} + +void UPLX_ModelRegistry::Initialize(FSubsystemCollectionBase& Collection) +{ + Super::Initialize(Collection); +} + +void UPLX_ModelRegistry::Deinitialize() +{ + Native.ReleaseNative(); + Super::Deinitialize(); +} diff --git a/Source/AGXUnreal/Private/OpenPLX/PLX_SignalHandlerComponent.cpp b/Source/AGXUnreal/Private/OpenPLX/PLX_SignalHandlerComponent.cpp index 4794f8016..dfe549baa 100644 --- a/Source/AGXUnreal/Private/OpenPLX/PLX_SignalHandlerComponent.cpp +++ b/Source/AGXUnreal/Private/OpenPLX/PLX_SignalHandlerComponent.cpp @@ -6,6 +6,7 @@ #include "AGX_Simulation.h" #include "Constraints/AGX_ConstraintComponent.h" #include "Import/AGX_ModelSourceComponent.h" +#include "OpenPLX/PLX_ModelRegistry.h" #include "Utilities/AGX_ObjectUtilities.h" #include "Utilities/AGX_StringUtilities.h" @@ -35,7 +36,10 @@ namespace PLX_SignalHandlerComponent_helpers for (UAGX_ConstraintComponent* Constraint : ConstraintsInThisActor) { if (auto CBarrier = Constraint->GetOrCreateNative()) - ConstraintBarriers.Add(CBarrier); + { + if (CBarrier->HasNative()) + ConstraintBarriers.Add(CBarrier); + } } return ConstraintBarriers; @@ -80,11 +84,19 @@ void UPLX_SignalHandlerComponent::BeginPlay() return; } + auto PLXModelRegistry = UPLX_ModelRegistry::GetFrom(GetWorld()); + auto PLXModelRegistryBarrier = PLXModelRegistry != nullptr ? PLXModelRegistry->GetNative() : nullptr; + if (PLXModelRegistryBarrier == nullptr) + { + // Todo: log warning. + return; + } + // Collect all Constraints in the same AActor as us. TArray ConstraintBarriers = CollectConstraintBarriers(GetOwner()); // Initialize SignalHandler in Barrier module. - SignalHandler.Init(*PLXFile, *SimulationBarrier, ConstraintBarriers); + SignalHandler.Init(*PLXFile, *SimulationBarrier, *PLXModelRegistryBarrier, ConstraintBarriers); } void UPLX_SignalHandlerComponent::EndPlay(const EEndPlayReason::Type Reason) diff --git a/Source/AGXUnreal/Public/OpenPLX/PLX_ModelInfo.h b/Source/AGXUnreal/Public/OpenPLX/PLX_ModelRegistry.h similarity index 64% rename from Source/AGXUnreal/Public/OpenPLX/PLX_ModelInfo.h rename to Source/AGXUnreal/Public/OpenPLX/PLX_ModelRegistry.h index 240ea9bcd..8b13ec8c2 100644 --- a/Source/AGXUnreal/Public/OpenPLX/PLX_ModelInfo.h +++ b/Source/AGXUnreal/Public/OpenPLX/PLX_ModelRegistry.h @@ -3,30 +3,30 @@ #pragma once // AGX Dynamics for Unreal includes. -#include "OpenPLX/PLXModelInfo.h" +#include "OpenPLX/PLXModelRegistry.h" // Unreal Engine includes. #include "CoreMinimal.h" #include "LevelInstance/LevelInstanceSubsystem.h" #include "Subsystems/GameInstanceSubsystem.h" -#include "PLX_ModelInfo.generated.h" +#include "PLX_ModelRegistry.generated.h" /** * Todo: add description. */ UCLASS(ClassGroup = "PLX", Category = "PLX") -class AGXUNREAL_API UPLX_ModelInfo : public ULevelInstanceSubsystem +class AGXUNREAL_API UPLX_ModelRegistry : public ULevelInstanceSubsystem { GENERATED_BODY() public: - static UPLX_ModelInfo* GetFrom(UWorld* World); + static UPLX_ModelRegistry* GetFrom(UWorld* World); bool HasNative() const; - FPLXModelInfo* GetNative(); - const FPLXModelInfo* GetNative() const; + FPLXModelRegistry* GetNative(); + const FPLXModelRegistry* GetNative() const; private: // ~Begin USubsystem interface. @@ -34,5 +34,5 @@ class AGXUNREAL_API UPLX_ModelInfo : public ULevelInstanceSubsystem virtual void Deinitialize() override; // ~End USubsystem interface. - FPLXModelInfo Native; + FPLXModelRegistry Native; }; diff --git a/Source/AGXUnrealBarrier/Private/OpenPLX/PLXModelInfo.cpp b/Source/AGXUnrealBarrier/Private/OpenPLX/PLXModelInfo.cpp deleted file mode 100644 index 4d8ad50d1..000000000 --- a/Source/AGXUnrealBarrier/Private/OpenPLX/PLXModelInfo.cpp +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2024, Algoryx Simulation AB. - -#include "OpenPLX/PLXModelInfo.h" - -// AGX Dynamics for Unreal includes. -#include "BarrierOnly/OpenPLX/OpenPLXRefs.h" - - -FPLXModelInfo::FPLXModelInfo() - : Native(std::make_unique()) -{ -} - -FPLXModelInfo::~FPLXModelInfo() -{ -} - -bool FPLXModelInfo::HasNative() const -{ - return Native != nullptr; -} - - diff --git a/Source/AGXUnrealBarrier/Private/OpenPLX/PLXModelRegistry.cpp b/Source/AGXUnrealBarrier/Private/OpenPLX/PLXModelRegistry.cpp new file mode 100644 index 000000000..cb1b9b641 --- /dev/null +++ b/Source/AGXUnrealBarrier/Private/OpenPLX/PLXModelRegistry.cpp @@ -0,0 +1,156 @@ +// Copyright 2024, Algoryx Simulation AB. + +#include "OpenPLX/PLXModelRegistry.h" + +// AGX Dynamics for Unreal includes. +#include "AGX_Check.h" +#include "BarrierOnly/AGXRefs.h" +#include "BarrierOnly/OpenPLX/OpenPLXRefs.h" +#include "SimulationBarrier.h" +#include "TypeConversions.h" +#include "Utilities/PLXUtilities.h" + +// Standard library includes. +#include + +FPLXModelRegistry::FPLXModelRegistry() + : Native(std::make_unique()) +{ +} + +FPLXModelRegistry::~FPLXModelRegistry() +{ +} + +bool FPLXModelRegistry::HasNative() const +{ + return Native != nullptr; +} + +void FPLXModelRegistry::ReleaseNative() +{ + Native = nullptr; +} + +namespace FPLXModelRegistry_helpers +{ + FPLXModelRegistry::Handle Convert(size_t Val) + { + if (Val > std::numeric_limits::max()) + { + // This should never ever happen. It means we have more than + // int32::max number of models in the world. + AGX_CHECK(false); + return std::numeric_limits::max(); + } + + return static_cast(Val); + } + + size_t Convert(FPLXModelRegistry::Handle Val) + { + check(Val >= 0); + return static_cast(Val); + } +} + +FPLXModelRegistry::Handle FPLXModelRegistry::Register( + const FString& PLXFile, FAssemblyRef& Assembly, FSimulationBarrier& Simulation) +{ + check(HasNative()); + check(Simulation.HasNative()); + + Handle Handle = GetFrom(PLXFile); + + if (Handle == InvalidHandle) // We have never seen this PLX Model before. + Handle = PrepareNewModel(PLXFile); + + if (Handle == InvalidHandle) + return InvalidHandle; // Something went wrong. + + // At this point, we know there exists valid PLXModelData for this PLXModel. + // We need to add the given Assembly to the base Assembly and (re)-create the + // InputSignalListener which is shared by all instances of this PLX Model. + + FPLXModelDatum* ModelDatum = GetModelDatum(Handle); + if (ModelDatum == nullptr) + return InvalidHandle; + + // Add the given assebly as a sub-assembly to the existing root assembly. + // This way, the InputSignalListener will correctly find all relevant AGX objects. + ModelDatum->Assembly->add(Assembly.Native); + + if (ModelDatum->InputSignalListener != nullptr) // Has been added before. + Simulation.GetNative()->Native->remove(ModelDatum->InputSignalListener); + + // The InputSignalListener always needs to be re-created even if it existed before, since now + // we have added another sub-assembly containing agx-objects it needs to know about. + ModelDatum->InputSignalListener = new BrickAgx::InputSignalListener(ModelDatum->Assembly); + Simulation.GetNative()->Native->add(ModelDatum->InputSignalListener); + + return Handle; +} + +const FPLXModelDatum* FPLXModelRegistry::GetModelDatum(Handle Handle) const +{ + check(HasNative()); + if (Handle == InvalidHandle) + return nullptr; + + const size_t Index = FPLXModelRegistry_helpers::Convert(Handle); + if (Index >= Native->ModelData.size()) + return nullptr; + + return &Native->ModelData[Index]; +} + +FPLXModelDatum* FPLXModelRegistry::GetModelDatum(Handle Handle) +{ + check(HasNative()); + if (Handle == InvalidHandle) + return nullptr; + + const size_t Index = FPLXModelRegistry_helpers::Convert(Handle); + if (Index >= Native->ModelData.size()) + return nullptr; + + return &Native->ModelData[Index]; +} + +FPLXModelRegistry::Handle FPLXModelRegistry::GetFrom(const FString& PLXFile) const +{ + auto It = KnownModels.find(Convert(PLXFile)); + return It != KnownModels.end() ? It->second : InvalidHandle; +} + +FPLXModelRegistry::Handle FPLXModelRegistry::PrepareNewModel(const FString& PLXFile) +{ + // Here we create a new slot in the PLXModelData array with the PLX model tree as well + // as some other required objects line an agxSDK::Assembly that the InputSignalListener needs. + AGX_CHECK(GetFrom(PLXFile) == InvalidHandle); + + FPLXModelDatum NewModel; + NewModel.PLXModel = FPLXUtilities::LoadModel(PLXFile, NewModel.AGXCache); + if (NewModel.PLXModel == nullptr) + { + UE_LOG( + LogAGX, Error, + TEXT("Could not read OpenPLX file '%s'. The Log category LogAGXDynamics may include " + "more details."), + *PLXFile); + return InvalidHandle; + } + + // OpenPLX's InputSignalListener expects an Assembly with a PowerLine always. + // Therefore we add one here, even though it's a bit of a hack. + NewModel.Assembly = new agxSDK::Assembly(); + agxPowerLine::PowerLineRef RequiredDummyPowerLine = new agxPowerLine::PowerLine(); + RequiredDummyPowerLine->setName(agx::Name("BrickPowerLine")); + NewModel.Assembly->add(RequiredDummyPowerLine); + + const Handle NewHande = FPLXModelRegistry_helpers::Convert(Native->ModelData.size()); + Native->ModelData.emplace_back(std::move(NewModel)); + KnownModels.insert({Convert(PLXFile), NewHande}); + + return NewHande; +} diff --git a/Source/AGXUnrealBarrier/Private/OpenPLX/PLXSignalHandler.cpp b/Source/AGXUnrealBarrier/Private/OpenPLX/PLXSignalHandler.cpp index feeca066d..ae95a2ea0 100644 --- a/Source/AGXUnrealBarrier/Private/OpenPLX/PLXSignalHandler.cpp +++ b/Source/AGXUnrealBarrier/Private/OpenPLX/PLXSignalHandler.cpp @@ -13,60 +13,39 @@ #include "Utilities/PLXUtilities.h" // OpenPLX includes. -#include "Brick/brickagx/InputSignalListener.h" #include "Brick/brickagx/Signals.h" #include "Brick/Physics/Signals/RealInputSignal.h" #include "Brick/Physics3D/Signals/LinearVelocityMotorVelocityInput.h" -static Brick::Core::ObjectPtr PLXModel = nullptr; // Todo: store somewhere to be re-used! void FPLXSignalHandler::Init( const FString& PLXFile, FSimulationBarrier& Simulation, - TArray& Constraints) + FPLXModelRegistry& InModelInfo, TArray& Constraints) { check(Simulation.HasNative()); + check(InModelInfo.HasNative()); -#if 0 - // Todo: this PLXModel (tree) should be cached somewhere and re-used for other instances of the - // same PLX model in the same world, so that LoadModel only has to be done once for any givel - // PLX model. - std::shared_ptr AGXCache; - PLXModel = FPLXUtilities::LoadModel(PLXFile, AGXCache); - if (PLXModel == nullptr) - { - UE_LOG( - LogAGX, Error, - TEXT("Could not read OpenPLX file '%s'. The Log category LogAGXDynamics may include " - "more details."), - *PLXFile); - return; - } - - // Here, we re-use the InputSignalListener in OpenPLX to propagate signals. - // It expects an assembly with the relevant contraints (and powerlines). - // If no powerline exists in the OpenPLX model, one must be added anyway because the - // InputSignalListener assumes one. - NativeAssemblyRef = std::make_shared(new agxSDK::Assembly()); + agxSDK::AssemblyRef Assembly = new agxSDK::Assembly(); for (FConstraintBarrier* Constraint : Constraints) { AGX_CHECK(Constraint->HasNative()); - NativeAssemblyRef->Native->add(Constraint->GetNative()->Native); + Assembly->add(Constraint->GetNative()->Native); } - agxPowerLine::PowerLineRef RequiredDummyPowerLine = new agxPowerLine::PowerLine(); - RequiredDummyPowerLine->setName(agx::Name("BrickPowerLine")); - NativeAssemblyRef->Native->add(RequiredDummyPowerLine); - NativeInputSignalHandlerRef = - std::make_shared(NativeAssemblyRef->Native); - Simulation.GetNative()->Native->add(NativeInputSignalHandlerRef->Native.get()); - - // Todo: build up signals map for fast lookup later. -#endif + FAssemblyRef AssemblyRef(Assembly); + ModelInfo = &InModelInfo; + ModelHandle = ModelInfo->Register(PLXFile, AssemblyRef, Simulation); + if (ModelHandle == FPLXModelRegistry::InvalidHandle) + { + // Todo: log error + return; + } } bool FPLXSignalHandler::IsInitialized() const { - return NativeOutputSignalHandlerRef != nullptr; + return true;// Todo, uncomment below once implemented. + //return NativeOutputSignalHandlerRef != nullptr; } void FPLXSignalHandler::ReleaseNatives() @@ -95,9 +74,15 @@ namespace PLXSignalHandler_helpers bool FPLXSignalHandler::Send(const FPLX_LinearVelocityMotorVelocityInput& Input, double Value) { AGX_CHECK(IsInitialized()); + if (ModelInfo == nullptr) + return false; + + const FPLXModelDatum* ModelDatum = ModelInfo->GetModelDatum(ModelHandle); + if (ModelDatum == nullptr) + return false; std::vector> SignalInputs; - auto System = std::dynamic_pointer_cast(PLXModel); + auto System = std::dynamic_pointer_cast(ModelDatum->PLXModel); if (System == nullptr) return false; diff --git a/Source/AGXUnrealBarrier/Public/BarrierOnly/OpenPLX/OpenPLXRefs.h b/Source/AGXUnrealBarrier/Public/BarrierOnly/OpenPLX/OpenPLXRefs.h index 280fcc871..466bc34ca 100644 --- a/Source/AGXUnrealBarrier/Public/BarrierOnly/OpenPLX/OpenPLXRefs.h +++ b/Source/AGXUnrealBarrier/Public/BarrierOnly/OpenPLX/OpenPLXRefs.h @@ -1,6 +1,7 @@ #pragma once // OpenPLX includes. +#include "Brick/brickagx/AgxCache.h" #include "Brick/brickagx/InputSignalListener.h" #include "Brick/brickagx/OutputSignalListener.h" @@ -39,7 +40,9 @@ struct FOutputSignalHandlerRef struct FPLXModelDatum { - agx::ref_ptr InputSignalHandler; + std::shared_ptr AGXCache; + Brick::Core::ObjectPtr PLXModel; + agx::ref_ptr InputSignalListener; agxSDK::AssemblyRef Assembly; }; diff --git a/Source/AGXUnrealBarrier/Public/OpenPLX/PLXModelInfo.h b/Source/AGXUnrealBarrier/Public/OpenPLX/PLXModelInfo.h deleted file mode 100644 index 51ab75a37..000000000 --- a/Source/AGXUnrealBarrier/Public/OpenPLX/PLXModelInfo.h +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2024, Algoryx Simulation AB. - -#pragma once - -// Standard library includes. -#include - -struct FPLXModelData; - -class AGXUNREALBARRIER_API FPLXModelInfo -{ -public: - FPLXModelInfo(); - ~FPLXModelInfo(); - - bool HasNative() const; - -private: - FPLXModelInfo(const FPLXModelInfo&) = delete; - void operator=(const FPLXModelInfo&) = delete; - - std::unique_ptr Native; -}; diff --git a/Source/AGXUnrealBarrier/Public/OpenPLX/PLXModelRegistry.h b/Source/AGXUnrealBarrier/Public/OpenPLX/PLXModelRegistry.h new file mode 100644 index 000000000..565b6e3f5 --- /dev/null +++ b/Source/AGXUnrealBarrier/Public/OpenPLX/PLXModelRegistry.h @@ -0,0 +1,52 @@ +// Copyright 2024, Algoryx Simulation AB. + +#pragma once + +// Standard library includes. +#include +#include +#include + +class FSimulationBarrier; + +struct FAssemblyRef; +struct FPLXModelData; +struct FPLXModelDatum; + +class AGXUNREALBARRIER_API FPLXModelRegistry +{ +public: + using Handle = int32; + inline static constexpr Handle InvalidHandle = -1; + + FPLXModelRegistry(); + ~FPLXModelRegistry(); + + bool HasNative() const; + void ReleaseNative(); + + /** + * The PLXFile is the absolute path of a OpenPLX model to be loaded. + * The Assembly must contain the AGX objects relevant for the specific instance of this OpenPLX + * model (there can exist many). + * The Simulation is the Simulation object used by AGXUnreal during Play. + */ + Handle Register(const FString& PLXFile, FAssemblyRef& Assembly, FSimulationBarrier& Simulation); + + /** + * Important note: the lifetime of the returned FPLXModelDatum is only guaranteed during direct + * usage in local scope. Do not store this pointer for later use. + */ + const FPLXModelDatum* GetModelDatum(Handle Handle) const; + FPLXModelDatum* GetModelDatum(Handle Handle); + +private: + FPLXModelRegistry(const FPLXModelRegistry&) = delete; + void operator=(const FPLXModelRegistry&) = delete; + + Handle GetFrom(const FString& PLXFile) const; + Handle PrepareNewModel(const FString& PLXFile); + + std::unique_ptr Native; + std::unordered_map KnownModels; +}; diff --git a/Source/AGXUnrealBarrier/Public/OpenPLX/PLXSignalHandler.h b/Source/AGXUnrealBarrier/Public/OpenPLX/PLXSignalHandler.h index dd9751f31..396bc5596 100644 --- a/Source/AGXUnrealBarrier/Public/OpenPLX/PLXSignalHandler.h +++ b/Source/AGXUnrealBarrier/Public/OpenPLX/PLXSignalHandler.h @@ -2,6 +2,8 @@ #pragma once +// AGX Dynamics for Unreal includes. +#include "OpenPLX/PLXModelRegistry.h" // Standard library includes. #include @@ -15,7 +17,9 @@ struct FOutputSignalHandlerRef; class AGXUNREALBARRIER_API FPLXSignalHandler { public: - void Init(const FString& PLXFile, FSimulationBarrier& Simulation, TArray& Constraints); + void Init( + const FString& PLXFile, FSimulationBarrier& Simulation, FPLXModelRegistry& InModelInfo, + TArray& Constraints); bool IsInitialized() const; @@ -24,6 +28,9 @@ class AGXUNREALBARRIER_API FPLXSignalHandler // todo: match the base class RealInput on the PLX side for less overloads. bool Send(const FPLX_LinearVelocityMotorVelocityInput& Input, double Value); -private: +private: std::shared_ptr NativeOutputSignalHandlerRef; + FPLXModelRegistry* ModelInfo {nullptr}; + + FPLXModelRegistry::Handle ModelHandle {FPLXModelRegistry::InvalidHandle}; }; From 10fa0ac1debdd26bb6f9809b1384d8879ee21a1c Mon Sep 17 00:00:00 2001 From: Josef Date: Fri, 15 Nov 2024 08:09:28 +0100 Subject: [PATCH 23/31] Set correct default value of input variables --- Source/AGXCommon/Public/OpenPLX/PLX_Inputs.h | 2 +- .../Public/Utilities/AGX_ROS2Utilities.h | 6 +++--- .../Private/AGX_ImporterToBlueprint.cpp | 21 +++++++++++++------ 3 files changed, 19 insertions(+), 10 deletions(-) diff --git a/Source/AGXCommon/Public/OpenPLX/PLX_Inputs.h b/Source/AGXCommon/Public/OpenPLX/PLX_Inputs.h index 92179ee12..857be5bbc 100644 --- a/Source/AGXCommon/Public/OpenPLX/PLX_Inputs.h +++ b/Source/AGXCommon/Public/OpenPLX/PLX_Inputs.h @@ -21,7 +21,7 @@ struct AGXCOMMON_API FPLX_Input virtual ~FPLX_Input() = default; - UPROPERTY() + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "OpenPXL") FString Name; virtual UScriptStruct* GetType() const diff --git a/Source/AGXUnreal/Public/Utilities/AGX_ROS2Utilities.h b/Source/AGXUnreal/Public/Utilities/AGX_ROS2Utilities.h index 79652a9ea..5038bc9c1 100644 --- a/Source/AGXUnreal/Public/Utilities/AGX_ROS2Utilities.h +++ b/Source/AGXUnreal/Public/Utilities/AGX_ROS2Utilities.h @@ -32,7 +32,7 @@ class AGXUNREAL_API UAGX_ROS2Utilities : public UBlueprintFunctionLibrary /** * Takes a TimeStamp in seconds and converts it into a ROS2 builtin_interfaces::Time message. */ - UFUNCTION(BlueprintCallable, Category = "AGX ROS2") + UFUNCTION(BlueprintCallable, BlueprintPure, Category = "AGX ROS2") static FAGX_BuiltinInterfacesTime ConvertTime(double TimeStamp); /** @@ -58,7 +58,7 @@ class AGXUNREAL_API UAGX_ROS2Utilities : public UBlueprintFunctionLibrary * the timestamp of the first valid Point in the given array, even if other points have been * generated at later timestamps. */ - UFUNCTION(BlueprintCallable, Category = "AGX ROS2") + UFUNCTION(BlueprintCallable, BlueprintPure, Category = "AGX ROS2") static FAGX_SensorMsgsPointCloud2 ConvertXYZ( const TArray& Points, bool DoublePrecision = false, bool ROSCoordinates = true, const FString& FrameId = ""); @@ -82,7 +82,7 @@ class AGXUNREAL_API UAGX_ROS2Utilities : public UBlueprintFunctionLibrary * the timestamp of the first valid Point in the given array, even if other points have been * generated at later timestamps. */ - UFUNCTION(BlueprintCallable, Category = "AGX Lidar") + UFUNCTION(BlueprintCallable, BlueprintPure, Category = "AGX Lidar") static FAGX_SensorMsgsPointCloud2 ConvertAnglesTOF( const TArray& Points, const FString& FrameId = ""); }; diff --git a/Source/AGXUnrealEditor/Private/AGX_ImporterToBlueprint.cpp b/Source/AGXUnrealEditor/Private/AGX_ImporterToBlueprint.cpp index f5f8809d2..31767502d 100644 --- a/Source/AGXUnrealEditor/Private/AGX_ImporterToBlueprint.cpp +++ b/Source/AGXUnrealEditor/Private/AGX_ImporterToBlueprint.cpp @@ -579,14 +579,23 @@ namespace FEdGraphPinType PinType; PinType.PinCategory = FName(TEXT("struct")); PinType.PinSubCategoryObject = Input->GetType(); - FBlueprintEditorUtils::AddMemberVariable(&OutBlueprint, FName(Input->Name), PinType); + FBlueprintEditorUtils::AddMemberVariable( + &OutBlueprint, FName(Input->Name), PinType, FString::Printf(TEXT("(Name=\"%s\")"), *Input->Name)); SignalsAdded = true; - } - for (auto& NewVariable : OutBlueprint.NewVariables) // Todo: is there a safer way to get these? - { - NewVariable.Category = FText::FromString("OpenPLX Inputs"); - NewVariable.PropertyFlags |= CPF_BlueprintReadOnly; + const int32 VarIndex = + FBlueprintEditorUtils::FindNewVariableIndex(&OutBlueprint, FName(Input->Name)); + if (VarIndex == INDEX_NONE) + { + UE_LOG( + LogAGX, Warning, + TEXT("Unable to properly setup OpenPLX Input '%s' as a variable in Blueprint " + "'%s'. The OpenPLX input might not work as expected."), *Input->Name, *OutBlueprint.GetName()); + continue; + } + + OutBlueprint.NewVariables[VarIndex].Category = FText::FromString("OpenPLX Inputs"); + OutBlueprint.NewVariables[VarIndex].PropertyFlags |= CPF_BlueprintReadOnly; } if (SignalsAdded) From 761899a5fe0f44095886fcb840b54136e1fd8112 Mon Sep 17 00:00:00 2001 From: Josef Date: Fri, 15 Nov 2024 12:37:53 +0100 Subject: [PATCH 24/31] Read in Outputs during import (only single output type is supported) --- Source/AGXCommon/Public/OpenPLX/PLX_Outputs.h | 48 +++++++++++++++++++ .../OpenPLX/PLX_SignalHandlerComponent.cpp | 13 +++-- .../OpenPLX/PLX_SignalHandlerComponent.h | 1 - .../Private/AGXSimObjectsReader.cpp | 1 + .../Private/OpenPLX/PLXModelRegistry.cpp | 39 ++++++++++++++- .../Private/OpenPLX/PLXSignalHandler.cpp | 28 +++++------ .../Private/SimulationObjectCollection.cpp | 10 ++++ .../Private/Utilities/PLXUtilities.cpp | 34 ++++++++++++- .../Private/Utilities/PLXUtilities.h | 2 + .../Public/BarrierOnly/OpenPLX/OpenPLXRefs.h | 2 + .../Public/OpenPLX/PLXModelRegistry.h | 8 +++- .../Public/OpenPLX/PLXSignalHandler.h | 8 ++-- .../Public/SimulationObjectCollection.h | 5 ++ .../Private/AGX_ImporterToBlueprint.cpp | 42 +++++++++++++++- 14 files changed, 205 insertions(+), 36 deletions(-) create mode 100644 Source/AGXCommon/Public/OpenPLX/PLX_Outputs.h diff --git a/Source/AGXCommon/Public/OpenPLX/PLX_Outputs.h b/Source/AGXCommon/Public/OpenPLX/PLX_Outputs.h new file mode 100644 index 000000000..57a6b3f2f --- /dev/null +++ b/Source/AGXCommon/Public/OpenPLX/PLX_Outputs.h @@ -0,0 +1,48 @@ +// Copyright 2024, Algoryx Simulation AB. + +#pragma once + +// Unreal Engine includes. +#include "CoreMinimal.h" +#include "UObject/Class.h" + +#include "PLX_Outputs.generated.h" + +USTRUCT(BlueprintType) +struct AGXCOMMON_API FPLX_Output +{ + GENERATED_BODY() + + FPLX_Output() = default; + explicit FPLX_Output(const FString& InName) + : Name(InName) + { + } + + virtual ~FPLX_Output() = default; + + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "OpenPXL") + FString Name; + + virtual UScriptStruct* GetType() const + { + return FPLX_Output::StaticStruct(); + } +}; + +USTRUCT(BlueprintType) +struct AGXCOMMON_API FPLX_HingeAngleOutput : public FPLX_Output +{ + GENERATED_BODY() + + FPLX_HingeAngleOutput() = default; + explicit FPLX_HingeAngleOutput(const FString& InName) + : FPLX_Output(InName) + { + } + + virtual UScriptStruct* GetType() const override + { + return FPLX_HingeAngleOutput::StaticStruct(); + } +}; diff --git a/Source/AGXUnreal/Private/OpenPLX/PLX_SignalHandlerComponent.cpp b/Source/AGXUnreal/Private/OpenPLX/PLX_SignalHandlerComponent.cpp index dfe549baa..e8964da34 100644 --- a/Source/AGXUnreal/Private/OpenPLX/PLX_SignalHandlerComponent.cpp +++ b/Source/AGXUnreal/Private/OpenPLX/PLX_SignalHandlerComponent.cpp @@ -85,7 +85,8 @@ void UPLX_SignalHandlerComponent::BeginPlay() } auto PLXModelRegistry = UPLX_ModelRegistry::GetFrom(GetWorld()); - auto PLXModelRegistryBarrier = PLXModelRegistry != nullptr ? PLXModelRegistry->GetNative() : nullptr; + auto PLXModelRegistryBarrier = + PLXModelRegistry != nullptr ? PLXModelRegistry->GetNative() : nullptr; if (PLXModelRegistryBarrier == nullptr) { // Todo: log warning. @@ -96,10 +97,8 @@ void UPLX_SignalHandlerComponent::BeginPlay() TArray ConstraintBarriers = CollectConstraintBarriers(GetOwner()); // Initialize SignalHandler in Barrier module. - SignalHandler.Init(*PLXFile, *SimulationBarrier, *PLXModelRegistryBarrier, ConstraintBarriers); -} - -void UPLX_SignalHandlerComponent::EndPlay(const EEndPlayReason::Type Reason) -{ - SignalHandler.ReleaseNatives(); + const FString UniqueInstancePrefix = GetOwner()->GetName(); + SignalHandler.Init( + *PLXFile, UniqueInstancePrefix, *SimulationBarrier, *PLXModelRegistryBarrier, + ConstraintBarriers); } diff --git a/Source/AGXUnreal/Public/OpenPLX/PLX_SignalHandlerComponent.h b/Source/AGXUnreal/Public/OpenPLX/PLX_SignalHandlerComponent.h index 275e08101..849cdf25b 100644 --- a/Source/AGXUnreal/Public/OpenPLX/PLX_SignalHandlerComponent.h +++ b/Source/AGXUnreal/Public/OpenPLX/PLX_SignalHandlerComponent.h @@ -30,7 +30,6 @@ class AGXUNREAL_API UPLX_SignalHandlerComponent : public UActorComponent //~ Begin UActorComponent Interface virtual void BeginPlay() override; - virtual void EndPlay(const EEndPlayReason::Type Reason) override; //~ End UActorComponent Interface private: diff --git a/Source/AGXUnrealBarrier/Private/AGXSimObjectsReader.cpp b/Source/AGXUnrealBarrier/Private/AGXSimObjectsReader.cpp index b4aa8ceff..d4e2153e9 100644 --- a/Source/AGXUnrealBarrier/Private/AGXSimObjectsReader.cpp +++ b/Source/AGXUnrealBarrier/Private/AGXSimObjectsReader.cpp @@ -660,6 +660,7 @@ bool FAGXSimObjectsReader::ReadOpenPLXFile( // Read PLX inputs. auto System = std::dynamic_pointer_cast(PLXModel); OutSimObjects.GetPLXInputs() = FPLXUtilities::GetInputs(System.get()); + OutSimObjects.GetPLXOutputs() = FPLXUtilities::GetOutputs(System.get()); return true; } diff --git a/Source/AGXUnrealBarrier/Private/OpenPLX/PLXModelRegistry.cpp b/Source/AGXUnrealBarrier/Private/OpenPLX/PLXModelRegistry.cpp index cb1b9b641..e9835b27f 100644 --- a/Source/AGXUnrealBarrier/Private/OpenPLX/PLXModelRegistry.cpp +++ b/Source/AGXUnrealBarrier/Private/OpenPLX/PLXModelRegistry.cpp @@ -55,13 +55,42 @@ namespace FPLXModelRegistry_helpers } FPLXModelRegistry::Handle FPLXModelRegistry::Register( - const FString& PLXFile, FAssemblyRef& Assembly, FSimulationBarrier& Simulation) + const FString& PLXFile, const FString& Prefix, FAssemblyRef& Assembly, + FSimulationBarrier& Simulation) { check(HasNative()); check(Simulation.HasNative()); + if (Prefix.IsEmpty()) + { + UE_LOG( + LogAGX, Warning, + TEXT("FPLXModelRegistry::Register got an empty UniqueModelInstancePrefix when " + "registering OpenPLX file '%s'. The OpenPLX model instance will not be " + "registered."), + *PLXFile); + return InvalidHandle; + } + Handle Handle = GetFrom(PLXFile); + // Check that we got a unique Model Instance Prefix. + if (Handle != InvalidHandle) + { + const size_t Index = FPLXModelRegistry_helpers::Convert(Handle); + if (Native->ModelData[Index].OutputSignalListeners.contains(Convert(Prefix))) + { + UE_LOG( + LogAGX, Warning, + TEXT("FPLXModelRegistry::Register got UniqueModelInstancePrefix '%s' that has " + "already been used when registering an instance using OpenPLX file '%s'. This " + "registration will fail and the behaviour of this instance may not work as " + "expected."), + *Prefix, *PLXFile); + return InvalidHandle; + } + } + if (Handle == InvalidHandle) // We have never seen this PLX Model before. Handle = PrepareNewModel(PLXFile); @@ -79,7 +108,7 @@ FPLXModelRegistry::Handle FPLXModelRegistry::Register( // Add the given assebly as a sub-assembly to the existing root assembly. // This way, the InputSignalListener will correctly find all relevant AGX objects. ModelDatum->Assembly->add(Assembly.Native); - + if (ModelDatum->InputSignalListener != nullptr) // Has been added before. Simulation.GetNative()->Native->remove(ModelDatum->InputSignalListener); @@ -88,6 +117,12 @@ FPLXModelRegistry::Handle FPLXModelRegistry::Register( ModelDatum->InputSignalListener = new BrickAgx::InputSignalListener(ModelDatum->Assembly); Simulation.GetNative()->Native->add(ModelDatum->InputSignalListener); + // Finally add an OutputSignalListener that is used for this PLX model instance only, producing + // output signals. It has a unique name prefix that is used to keep signals from getting name + // collisions when having mutliple instances of the same PLX model in the world. + ModelDatum->OutputSignalListeners.insert( + {Convert(Prefix), + new BrickAgx::OutputSignalListener(Assembly.Native, ModelDatum->PLXModel)}); return Handle; } diff --git a/Source/AGXUnrealBarrier/Private/OpenPLX/PLXSignalHandler.cpp b/Source/AGXUnrealBarrier/Private/OpenPLX/PLXSignalHandler.cpp index ae95a2ea0..f7a37d162 100644 --- a/Source/AGXUnrealBarrier/Private/OpenPLX/PLXSignalHandler.cpp +++ b/Source/AGXUnrealBarrier/Private/OpenPLX/PLXSignalHandler.cpp @@ -17,10 +17,10 @@ #include "Brick/Physics/Signals/RealInputSignal.h" #include "Brick/Physics3D/Signals/LinearVelocityMotorVelocityInput.h" - void FPLXSignalHandler::Init( - const FString& PLXFile, FSimulationBarrier& Simulation, - FPLXModelRegistry& InModelInfo, TArray& Constraints) + const FString& PLXFile, const FString& UniqueModelInstancePrefix, + FSimulationBarrier& Simulation, FPLXModelRegistry& InModelInfo, + TArray& Constraints) { check(Simulation.HasNative()); check(InModelInfo.HasNative()); @@ -34,23 +34,19 @@ void FPLXSignalHandler::Init( FAssemblyRef AssemblyRef(Assembly); ModelInfo = &InModelInfo; - ModelHandle = ModelInfo->Register(PLXFile, AssemblyRef, Simulation); + ModelHandle = ModelInfo->Register(PLXFile, UniqueModelInstancePrefix, AssemblyRef, Simulation); if (ModelHandle == FPLXModelRegistry::InvalidHandle) { // Todo: log error return; } -} -bool FPLXSignalHandler::IsInitialized() const -{ - return true;// Todo, uncomment below once implemented. - //return NativeOutputSignalHandlerRef != nullptr; + bIsInitialized = true; } -void FPLXSignalHandler::ReleaseNatives() +bool FPLXSignalHandler::IsInitialized() const { - NativeOutputSignalHandlerRef = nullptr; + return bIsInitialized; } namespace PLXSignalHandler_helpers @@ -73,7 +69,7 @@ namespace PLXSignalHandler_helpers bool FPLXSignalHandler::Send(const FPLX_LinearVelocityMotorVelocityInput& Input, double Value) { - AGX_CHECK(IsInitialized()); + check(IsInitialized()); if (ModelInfo == nullptr) return false; @@ -91,13 +87,11 @@ bool FPLXSignalHandler::Send(const FPLX_LinearVelocityMotorVelocityInput& Input, // Todo - map these in advance. for (std::shared_ptr& SignalInput : SignalInputs) { - auto LinearVelInput = - std::dynamic_pointer_cast( - SignalInput); - if (LinearVelInput == nullptr) + if (Convert(SignalInput->getName()) != Input.Name) continue; - auto Signal = Brick::Physics::Signals::RealInputSignal::create(ConvertDistanceToAGX(Value), LinearVelInput); + auto Signal = Brick::Physics::Signals::RealInputSignal::create( + ConvertDistanceToAGX(Value), SignalInput); BrickAgx::Signals::sendInputSignal(Signal); return true; } diff --git a/Source/AGXUnrealBarrier/Private/SimulationObjectCollection.cpp b/Source/AGXUnrealBarrier/Private/SimulationObjectCollection.cpp index 54017f3e5..58edde4f3 100644 --- a/Source/AGXUnrealBarrier/Private/SimulationObjectCollection.cpp +++ b/Source/AGXUnrealBarrier/Private/SimulationObjectCollection.cpp @@ -298,3 +298,13 @@ const TArray>& FSimulationObjectCollection::GetPLXInputs( { return PLXInputs; } + +TArray>& FSimulationObjectCollection::GetPLXOutputs() +{ + return PLXOutputs; +} + +const TArray>& FSimulationObjectCollection::GetPLXOutputs() const +{ + return PLXOutputs; +} diff --git a/Source/AGXUnrealBarrier/Private/Utilities/PLXUtilities.cpp b/Source/AGXUnrealBarrier/Private/Utilities/PLXUtilities.cpp index d23d641a6..16f391870 100644 --- a/Source/AGXUnrealBarrier/Private/Utilities/PLXUtilities.cpp +++ b/Source/AGXUnrealBarrier/Private/Utilities/PLXUtilities.cpp @@ -45,11 +45,11 @@ namespace PLXUtilities_helpers for (auto& Input : System->getValues()) { - if (auto LvmvI = std::dynamic_pointer_cast< + if (auto Lvmvi = std::dynamic_pointer_cast< Brick::Physics3D::Signals::LinearVelocityMotorVelocityInput>(Input)) { OutInputs.Emplace(MakeUnique( - Convert(LvmvI->motor()->getName()))); + Convert(Input->getName()))); continue; } @@ -57,6 +57,29 @@ namespace PLXUtilities_helpers } } + void GetOutputs(Brick::Physics3D::System* System, TArray>& OutOutputs) + { + if (System == nullptr) + return; + + for (auto& Subsystem : System->getValues()) + { + GetOutputs(System, OutOutputs); + } + + for (auto& Output : System->getValues()) + { + if (auto Hao = std::dynamic_pointer_cast(Output)) + { + OutOutputs.Emplace( + MakeUnique(Convert(Output->getName()))); + continue; + } + + UE_LOG(LogAGX, Warning, TEXT("Unhandled PLX Output: %s"), *Convert(Output->getName())); + } + } + std::shared_ptr CreatePLXContext( std::shared_ptr AGXCache) { @@ -130,3 +153,10 @@ TArray> FPLXUtilities::GetInputs(Brick::Physics3D::System PLXUtilities_helpers::GetInputs(System, Inputs); return Inputs; } + +TArray> FPLXUtilities::GetOutputs(Brick::Physics3D::System* System) +{ + TArray> Outputs; + PLXUtilities_helpers::GetOutputs(System, Outputs); + return Outputs; +} diff --git a/Source/AGXUnrealBarrier/Private/Utilities/PLXUtilities.h b/Source/AGXUnrealBarrier/Private/Utilities/PLXUtilities.h index 26545dce9..205dbf1a3 100644 --- a/Source/AGXUnrealBarrier/Private/Utilities/PLXUtilities.h +++ b/Source/AGXUnrealBarrier/Private/Utilities/PLXUtilities.h @@ -5,6 +5,7 @@ // AGX Dynamics for Unreal includes. #include "AGX_LogCategory.h" #include "OpenPLX/PLX_Inputs.h" +#include "OpenPLX/PLX_Outputs.h" // OpenPLX includes. #include "Brick/brick/Object.h" @@ -25,4 +26,5 @@ class FPLXUtilities const FString& Filename, std::shared_ptr AGXCache); static TArray> GetInputs(Brick::Physics3D::System* System); + static TArray> GetOutputs(Brick::Physics3D::System* System); }; diff --git a/Source/AGXUnrealBarrier/Public/BarrierOnly/OpenPLX/OpenPLXRefs.h b/Source/AGXUnrealBarrier/Public/BarrierOnly/OpenPLX/OpenPLXRefs.h index 466bc34ca..8e666114f 100644 --- a/Source/AGXUnrealBarrier/Public/BarrierOnly/OpenPLX/OpenPLXRefs.h +++ b/Source/AGXUnrealBarrier/Public/BarrierOnly/OpenPLX/OpenPLXRefs.h @@ -14,6 +14,7 @@ // Standard library includes. #include #include +#include struct FInputSignalHandlerRef { @@ -43,6 +44,7 @@ struct FPLXModelDatum std::shared_ptr AGXCache; Brick::Core::ObjectPtr PLXModel; agx::ref_ptr InputSignalListener; + std::unordered_map> OutputSignalListeners; agxSDK::AssemblyRef Assembly; }; diff --git a/Source/AGXUnrealBarrier/Public/OpenPLX/PLXModelRegistry.h b/Source/AGXUnrealBarrier/Public/OpenPLX/PLXModelRegistry.h index 565b6e3f5..eb759a76b 100644 --- a/Source/AGXUnrealBarrier/Public/OpenPLX/PLXModelRegistry.h +++ b/Source/AGXUnrealBarrier/Public/OpenPLX/PLXModelRegistry.h @@ -30,8 +30,14 @@ class AGXUNREALBARRIER_API FPLXModelRegistry * The Assembly must contain the AGX objects relevant for the specific instance of this OpenPLX * model (there can exist many). * The Simulation is the Simulation object used by AGXUnreal during Play. + * + * The Handle returned can be used to later access the loaded OpenPLX model along with related + * data. This Handle will be shared by all who register the same PLX file, for example when the + * same PLX model is instanced many times in the same world. */ - Handle Register(const FString& PLXFile, FAssemblyRef& Assembly, FSimulationBarrier& Simulation); + Handle Register( + const FString& PLXFile, const FString& UniqueModelInstancePrefix, FAssemblyRef& Assembly, + FSimulationBarrier& Simulation); /** * Important note: the lifetime of the returned FPLXModelDatum is only guaranteed during direct diff --git a/Source/AGXUnrealBarrier/Public/OpenPLX/PLXSignalHandler.h b/Source/AGXUnrealBarrier/Public/OpenPLX/PLXSignalHandler.h index 396bc5596..eb0b814d2 100644 --- a/Source/AGXUnrealBarrier/Public/OpenPLX/PLXSignalHandler.h +++ b/Source/AGXUnrealBarrier/Public/OpenPLX/PLXSignalHandler.h @@ -18,18 +18,18 @@ class AGXUNREALBARRIER_API FPLXSignalHandler { public: void Init( - const FString& PLXFile, FSimulationBarrier& Simulation, FPLXModelRegistry& InModelInfo, + const FString& PLXFile, const FString& UniqueModelInstancePrefix, + FSimulationBarrier& Simulation, + FPLXModelRegistry& InModelInfo, TArray& Constraints); bool IsInitialized() const; - void ReleaseNatives(); - // todo: match the base class RealInput on the PLX side for less overloads. bool Send(const FPLX_LinearVelocityMotorVelocityInput& Input, double Value); private: - std::shared_ptr NativeOutputSignalHandlerRef; + bool bIsInitialized {false}; FPLXModelRegistry* ModelInfo {nullptr}; FPLXModelRegistry::Handle ModelHandle {FPLXModelRegistry::InvalidHandle}; diff --git a/Source/AGXUnrealBarrier/Public/SimulationObjectCollection.h b/Source/AGXUnrealBarrier/Public/SimulationObjectCollection.h index 343475511..d2baf2f88 100644 --- a/Source/AGXUnrealBarrier/Public/SimulationObjectCollection.h +++ b/Source/AGXUnrealBarrier/Public/SimulationObjectCollection.h @@ -5,6 +5,7 @@ // AGX Dynamics for Unreal includes. // For some reason, these could not be forward declared without compiler error. #include "OpenPLX/PLX_Inputs.h" +#include "OpenPLX/PLX_Outputs.h" #include "Shapes/BoxShapeBarrier.h" #include "Shapes/CylinderShapeBarrier.h" #include "Shapes/CapsuleShapeBarrier.h" @@ -115,6 +116,9 @@ struct AGXUNREALBARRIER_API FSimulationObjectCollection TArray>& GetPLXInputs(); const TArray>& GetPLXInputs() const; + TArray>& GetPLXOutputs(); + const TArray>& GetPLXOutputs() const; + private: FSimulationObjectCollection(const FSimulationObjectCollection&) = delete; void operator=(const FSimulationObjectCollection&) = delete; @@ -148,4 +152,5 @@ struct AGXUNREALBARRIER_API FSimulationObjectCollection TArray Tracks; TArray> PLXInputs; + TArray> PLXOutputs; }; diff --git a/Source/AGXUnrealEditor/Private/AGX_ImporterToBlueprint.cpp b/Source/AGXUnrealEditor/Private/AGX_ImporterToBlueprint.cpp index 31767502d..855f32c78 100644 --- a/Source/AGXUnrealEditor/Private/AGX_ImporterToBlueprint.cpp +++ b/Source/AGXUnrealEditor/Private/AGX_ImporterToBlueprint.cpp @@ -34,6 +34,7 @@ #include "Materials/ContactMaterialBarrier.h" #include "Materials/AGX_ContactMaterialRegistrarComponent.h" #include "OpenPLX/PLX_Inputs.h" +#include "OpenPLX/PLX_Outputs.h" #include "OpenPLX/PLX_SignalHandlerComponent.h" #include "Shapes/AGX_BoxShapeComponent.h" #include "Shapes/AGX_SphereShapeComponent.h" @@ -571,7 +572,7 @@ namespace return EImportResult::Success; } - bool AddPLXSignals(const FSimulationObjectCollection& SimObjects, UBlueprint& OutBlueprint) + bool AddPLXInputs(const FSimulationObjectCollection& SimObjects, UBlueprint& OutBlueprint) { bool SignalsAdded = false; for (const TUniquePtr& Input : SimObjects.GetPLXInputs()) @@ -604,6 +605,41 @@ namespace return SignalsAdded; } + bool AddPLXOutputs(const FSimulationObjectCollection& SimObjects, UBlueprint& OutBlueprint) + { + bool SignalsAdded = false; + for (const TUniquePtr& Output : SimObjects.GetPLXOutputs()) + { + FEdGraphPinType PinType; + PinType.PinCategory = FName(TEXT("struct")); + PinType.PinSubCategoryObject = Output->GetType(); + FBlueprintEditorUtils::AddMemberVariable( + &OutBlueprint, FName(Output->Name), PinType, + FString::Printf(TEXT("(Name=\"%s\")"), *Output->Name)); + SignalsAdded = true; + + const int32 VarIndex = + FBlueprintEditorUtils::FindNewVariableIndex(&OutBlueprint, FName(Output->Name)); + if (VarIndex == INDEX_NONE) + { + UE_LOG( + LogAGX, Warning, + TEXT("Unable to properly setup OpenPLX Output '%s' as a variable in Blueprint " + "'%s'. The OpenPLX Output might not work as expected."), + *Output->Name, *OutBlueprint.GetName()); + continue; + } + + OutBlueprint.NewVariables[VarIndex].Category = FText::FromString("OpenPLX Outputs"); + OutBlueprint.NewVariables[VarIndex].PropertyFlags |= CPF_BlueprintReadOnly; + } + + if (SignalsAdded) + FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(&OutBlueprint); + + return SignalsAdded; + } + EImportResult AddComponentsFromPLX( AActor& ImportedActor, FAGX_SimObjectsImporterHelper& Helper, FSimulationObjectCollection& OutSimObjects) @@ -732,7 +768,9 @@ namespace UBlueprint* Blueprint = CreateBaseBlueprint(Package, Template); if (ImportSettings.ImportType == EAGX_ImportType::Plx && Blueprint != nullptr) { - if (AddPLXSignals(SimObjects, *Blueprint)) + const bool InputsAdded = AddPLXInputs(SimObjects, *Blueprint); + const bool OutputsAdded = AddPLXOutputs(SimObjects, *Blueprint); + if (InputsAdded || OutputsAdded) FKismetEditorUtilities::CompileBlueprint(Blueprint); } From ce48f90de3b1af44db167bf4bee276b7b1572626 Mon Sep 17 00:00:00 2001 From: Josef Date: Fri, 15 Nov 2024 13:15:39 +0100 Subject: [PATCH 25/31] Cache inputs for fast lookup in ModelDatum --- .../Private/OpenPLX/PLXModelRegistry.cpp | 35 +++++++++++++++- .../Private/OpenPLX/PLXSignalHandler.cpp | 42 ++++--------------- .../Private/Utilities/PLXUtilities.cpp | 6 +++ .../Public/BarrierOnly/OpenPLX/OpenPLXRefs.h | 3 ++ 4 files changed, 50 insertions(+), 36 deletions(-) diff --git a/Source/AGXUnrealBarrier/Private/OpenPLX/PLXModelRegistry.cpp b/Source/AGXUnrealBarrier/Private/OpenPLX/PLXModelRegistry.cpp index e9835b27f..e183ff1a3 100644 --- a/Source/AGXUnrealBarrier/Private/OpenPLX/PLXModelRegistry.cpp +++ b/Source/AGXUnrealBarrier/Private/OpenPLX/PLXModelRegistry.cpp @@ -10,6 +10,7 @@ #include "TypeConversions.h" #include "Utilities/PLXUtilities.h" + // Standard library includes. #include @@ -52,6 +53,29 @@ namespace FPLXModelRegistry_helpers check(Val >= 0); return static_cast(Val); } + + void MapAllInputs( + Brick::Physics3D::System* System, + std::unordered_map>& + OutInputs) + { + if (System == nullptr) + return; + + for (auto& Subsystem : System->getValues()) + { + MapAllInputs(System, OutInputs); + } + + for (auto& Input : System->getValues()) + { + if (Input == nullptr) + continue; + + AGX_CHECK(!OutInputs.contains(Input->getName())); + OutInputs.insert({Input->getName(), Input}); + } + } } FPLXModelRegistry::Handle FPLXModelRegistry::Register( @@ -120,9 +144,13 @@ FPLXModelRegistry::Handle FPLXModelRegistry::Register( // Finally add an OutputSignalListener that is used for this PLX model instance only, producing // output signals. It has a unique name prefix that is used to keep signals from getting name // collisions when having mutliple instances of the same PLX model in the world. + agx::ref_ptr OutputSignalListener = + new BrickAgx::OutputSignalListener(Assembly.Native, ModelDatum->PLXModel); ModelDatum->OutputSignalListeners.insert( - {Convert(Prefix), - new BrickAgx::OutputSignalListener(Assembly.Native, ModelDatum->PLXModel)}); + {Convert(Prefix), OutputSignalListener}); + AGX_CHECK(!Simulation.GetNative()->Native->remove(OutputSignalListener)); + Simulation.GetNative()->Native->add(OutputSignalListener); + return Handle; } @@ -176,6 +204,9 @@ FPLXModelRegistry::Handle FPLXModelRegistry::PrepareNewModel(const FString& PLXF return InvalidHandle; } + auto System = std::dynamic_pointer_cast(NewModel.PLXModel); + FPLXModelRegistry_helpers::MapAllInputs(System.get(), NewModel.Inputs); + // OpenPLX's InputSignalListener expects an Assembly with a PowerLine always. // Therefore we add one here, even though it's a bit of a hack. NewModel.Assembly = new agxSDK::Assembly(); diff --git a/Source/AGXUnrealBarrier/Private/OpenPLX/PLXSignalHandler.cpp b/Source/AGXUnrealBarrier/Private/OpenPLX/PLXSignalHandler.cpp index f7a37d162..557172fca 100644 --- a/Source/AGXUnrealBarrier/Private/OpenPLX/PLXSignalHandler.cpp +++ b/Source/AGXUnrealBarrier/Private/OpenPLX/PLXSignalHandler.cpp @@ -49,52 +49,26 @@ bool FPLXSignalHandler::IsInitialized() const return bIsInitialized; } -namespace PLXSignalHandler_helpers -{ - void findAllSignalInputs( - Brick::Physics3D::System* System, - std::vector>& OutSignalInputs) - { - for (auto& Subsystem : System->getValues()) - { - findAllSignalInputs(System, OutSignalInputs); - } - - for (auto& SignalInput : System->getValues()) - { - OutSignalInputs.push_back(SignalInput); - } - } -} - bool FPLXSignalHandler::Send(const FPLX_LinearVelocityMotorVelocityInput& Input, double Value) { check(IsInitialized()); if (ModelInfo == nullptr) return false; - const FPLXModelDatum* ModelDatum = ModelInfo->GetModelDatum(ModelHandle); + FPLXModelDatum* ModelDatum = ModelInfo->GetModelDatum(ModelHandle); if (ModelDatum == nullptr) return false; - std::vector> SignalInputs; - auto System = std::dynamic_pointer_cast(ModelDatum->PLXModel); - if (System == nullptr) + auto PLXInput = ModelDatum->Inputs.find(Convert(Input.Name)); + if (PLXInput == ModelDatum->Inputs.end()) return false; - PLXSignalHandler_helpers::findAllSignalInputs(System.get(), SignalInputs); - - // Todo - map these in advance. - for (std::shared_ptr& SignalInput : SignalInputs) - { - if (Convert(SignalInput->getName()) != Input.Name) - continue; + auto Signal = + Brick::Physics::Signals::RealInputSignal::create(ConvertDistanceToAGX(Value), PLXInput->second); - auto Signal = Brick::Physics::Signals::RealInputSignal::create( - ConvertDistanceToAGX(Value), SignalInput); - BrickAgx::Signals::sendInputSignal(Signal); - return true; - } + // Todo: prepend unique instance name prefix to signal once supported. + BrickAgx::Signals::sendInputSignal(Signal); + return true; return false; } diff --git a/Source/AGXUnrealBarrier/Private/Utilities/PLXUtilities.cpp b/Source/AGXUnrealBarrier/Private/Utilities/PLXUtilities.cpp index 16f391870..6a36ee10b 100644 --- a/Source/AGXUnrealBarrier/Private/Utilities/PLXUtilities.cpp +++ b/Source/AGXUnrealBarrier/Private/Utilities/PLXUtilities.cpp @@ -150,6 +150,9 @@ Brick::Core::ObjectPtr FPLXUtilities::LoadModel( TArray> FPLXUtilities::GetInputs(Brick::Physics3D::System* System) { TArray> Inputs; + if (System == nullptr) + return Inputs; + PLXUtilities_helpers::GetInputs(System, Inputs); return Inputs; } @@ -157,6 +160,9 @@ TArray> FPLXUtilities::GetInputs(Brick::Physics3D::System TArray> FPLXUtilities::GetOutputs(Brick::Physics3D::System* System) { TArray> Outputs; + if (System == nullptr) + return Outputs; + PLXUtilities_helpers::GetOutputs(System, Outputs); return Outputs; } diff --git a/Source/AGXUnrealBarrier/Public/BarrierOnly/OpenPLX/OpenPLXRefs.h b/Source/AGXUnrealBarrier/Public/BarrierOnly/OpenPLX/OpenPLXRefs.h index 8e666114f..83ee7f1e3 100644 --- a/Source/AGXUnrealBarrier/Public/BarrierOnly/OpenPLX/OpenPLXRefs.h +++ b/Source/AGXUnrealBarrier/Public/BarrierOnly/OpenPLX/OpenPLXRefs.h @@ -2,8 +2,10 @@ // OpenPLX includes. #include "Brick/brickagx/AgxCache.h" +#include "Brick/brickagx/Signals.h" #include "Brick/brickagx/InputSignalListener.h" #include "Brick/brickagx/OutputSignalListener.h" +#include "Brick/Physics3D/System.h" // AGX Dynamics includes. #include "BeginAGXIncludes.h" @@ -46,6 +48,7 @@ struct FPLXModelDatum agx::ref_ptr InputSignalListener; std::unordered_map> OutputSignalListeners; agxSDK::AssemblyRef Assembly; + std::unordered_map> Inputs; }; struct FPLXModelData From 7ea7ee93a405a48a3abf81bd7bd5900075028c93 Mon Sep 17 00:00:00 2001 From: Josef Date: Fri, 15 Nov 2024 15:01:14 +0100 Subject: [PATCH 26/31] Implement receive for one output type --- .../OpenPLX/PLX_SignalHandlerComponent.cpp | 7 +++- .../OpenPLX/PLX_SignalHandlerComponent.h | 6 ++- .../Private/OpenPLX/PLXModelRegistry.cpp | 3 +- .../Private/OpenPLX/PLXSignalHandler.cpp | 40 ++++++++++++++++--- .../Public/OpenPLX/PLXSignalHandler.h | 4 +- 5 files changed, 50 insertions(+), 10 deletions(-) diff --git a/Source/AGXUnreal/Private/OpenPLX/PLX_SignalHandlerComponent.cpp b/Source/AGXUnreal/Private/OpenPLX/PLX_SignalHandlerComponent.cpp index e8964da34..6d80c9544 100644 --- a/Source/AGXUnreal/Private/OpenPLX/PLX_SignalHandlerComponent.cpp +++ b/Source/AGXUnreal/Private/OpenPLX/PLX_SignalHandlerComponent.cpp @@ -15,7 +15,7 @@ UPLX_SignalHandlerComponent::UPLX_SignalHandlerComponent() PrimaryComponentTick.bCanEverTick = false; } -bool UPLX_SignalHandlerComponent::Send(FPLX_LinearVelocityMotorVelocityInput Input, double Value) +bool UPLX_SignalHandlerComponent::Send(const FPLX_LinearVelocityMotorVelocityInput& Input, double Value) { if (!SignalHandler.IsInitialized()) return false; @@ -60,6 +60,11 @@ namespace PLX_SignalHandlerComponent_helpers } } +bool UPLX_SignalHandlerComponent::Receive(const FPLX_HingeAngleOutput& Output, double& OutValue) +{ + return SignalHandler.Receive(Output, OutValue); +} + void UPLX_SignalHandlerComponent::BeginPlay() { using namespace PLX_SignalHandlerComponent_helpers; diff --git a/Source/AGXUnreal/Public/OpenPLX/PLX_SignalHandlerComponent.h b/Source/AGXUnreal/Public/OpenPLX/PLX_SignalHandlerComponent.h index 849cdf25b..cb23a438d 100644 --- a/Source/AGXUnreal/Public/OpenPLX/PLX_SignalHandlerComponent.h +++ b/Source/AGXUnreal/Public/OpenPLX/PLX_SignalHandlerComponent.h @@ -9,6 +9,7 @@ #include "Components/ActorComponent.h" #include "CoreMinimal.h" #include "OpenPLX/PLX_Inputs.h" +#include "OpenPLX/PLX_Outputs.h" #include "PLX_SignalHandlerComponent.generated.h" @@ -26,7 +27,10 @@ class AGXUNREAL_API UPLX_SignalHandlerComponent : public UActorComponent UPLX_SignalHandlerComponent(); UFUNCTION(BlueprintCallable, Category = "OpenPLX") - bool Send(FPLX_LinearVelocityMotorVelocityInput Input, double Value); + bool Send(const FPLX_LinearVelocityMotorVelocityInput& Input, double Value); + + UFUNCTION(BlueprintCallable, Category = "OpenPLX") + bool Receive(const FPLX_HingeAngleOutput& Output, double& OutValue); //~ Begin UActorComponent Interface virtual void BeginPlay() override; diff --git a/Source/AGXUnrealBarrier/Private/OpenPLX/PLXModelRegistry.cpp b/Source/AGXUnrealBarrier/Private/OpenPLX/PLXModelRegistry.cpp index e183ff1a3..a9815a19a 100644 --- a/Source/AGXUnrealBarrier/Private/OpenPLX/PLXModelRegistry.cpp +++ b/Source/AGXUnrealBarrier/Private/OpenPLX/PLXModelRegistry.cpp @@ -146,8 +146,7 @@ FPLXModelRegistry::Handle FPLXModelRegistry::Register( // collisions when having mutliple instances of the same PLX model in the world. agx::ref_ptr OutputSignalListener = new BrickAgx::OutputSignalListener(Assembly.Native, ModelDatum->PLXModel); - ModelDatum->OutputSignalListeners.insert( - {Convert(Prefix), OutputSignalListener}); + ModelDatum->OutputSignalListeners.insert({Convert(Prefix), OutputSignalListener}); AGX_CHECK(!Simulation.GetNative()->Native->remove(OutputSignalListener)); Simulation.GetNative()->Native->add(OutputSignalListener); diff --git a/Source/AGXUnrealBarrier/Private/OpenPLX/PLXSignalHandler.cpp b/Source/AGXUnrealBarrier/Private/OpenPLX/PLXSignalHandler.cpp index 557172fca..589f6324b 100644 --- a/Source/AGXUnrealBarrier/Private/OpenPLX/PLXSignalHandler.cpp +++ b/Source/AGXUnrealBarrier/Private/OpenPLX/PLXSignalHandler.cpp @@ -8,14 +8,16 @@ #include "BarrierOnly/AGXRefs.h" #include "BarrierOnly/OpenPLX/OpenPLXRefs.h" #include "Constraints/ConstraintBarrier.h" +#include "OpenPLX/PLX_Inputs.h" +#include "OpenPLX/PLX_Outputs.h" #include "SimulationBarrier.h" #include "TypeConversions.h" #include "Utilities/PLXUtilities.h" // OpenPLX includes. #include "Brick/brickagx/Signals.h" +#include "Brick/brickagx/SignalListenerUtils.h" #include "Brick/Physics/Signals/RealInputSignal.h" -#include "Brick/Physics3D/Signals/LinearVelocityMotorVelocityInput.h" void FPLXSignalHandler::Init( const FString& PLXFile, const FString& UniqueModelInstancePrefix, @@ -32,6 +34,12 @@ void FPLXSignalHandler::Init( Assembly->add(Constraint->GetNative()->Native); } + // OpenPLX OutputSignalListener requires the assembly to contain a PowerLine with a + // cetain name. Remove once this has been cleaned up in OpenPLX, it's a bit hacky. + agxPowerLine::PowerLineRef RequiredDummyPowerLine = new agxPowerLine::PowerLine(); + RequiredDummyPowerLine->setName(agx::Name("BrickPowerLine")); + Assembly->add(RequiredDummyPowerLine); + FAssemblyRef AssemblyRef(Assembly); ModelInfo = &InModelInfo; ModelHandle = ModelInfo->Register(PLXFile, UniqueModelInstancePrefix, AssemblyRef, Simulation); @@ -52,7 +60,7 @@ bool FPLXSignalHandler::IsInitialized() const bool FPLXSignalHandler::Send(const FPLX_LinearVelocityMotorVelocityInput& Input, double Value) { check(IsInitialized()); - if (ModelInfo == nullptr) + if (ModelInfo == nullptr || ModelHandle == FPLXModelRegistry::InvalidHandle) return false; FPLXModelDatum* ModelDatum = ModelInfo->GetModelDatum(ModelHandle); @@ -63,12 +71,34 @@ bool FPLXSignalHandler::Send(const FPLX_LinearVelocityMotorVelocityInput& Input, if (PLXInput == ModelDatum->Inputs.end()) return false; - auto Signal = - Brick::Physics::Signals::RealInputSignal::create(ConvertDistanceToAGX(Value), PLXInput->second); + auto Signal = Brick::Physics::Signals::RealInputSignal::create( + ConvertDistanceToAGX(Value), PLXInput->second); // Todo: prepend unique instance name prefix to signal once supported. BrickAgx::Signals::sendInputSignal(Signal); return true; +} + +bool FPLXSignalHandler::Receive(const FPLX_HingeAngleOutput& Output, double& OutValue) +{ + check(IsInitialized()); + if (ModelInfo == nullptr || ModelHandle == FPLXModelRegistry::InvalidHandle) + return false; - return false; + // Todo: make this more efficient than looking through all outputs every time. + // We could build a map from name to signal in the ModelRegistry, each time step, for example. + // Do this once the signal refactoring in OpenPLX has been done. + auto ValueOutputSignal = + BrickAgx::getSignalBySourceName( + BrickAgx::Signals::getOutputSignals(), Convert(Output.Name)); + if (ValueOutputSignal == nullptr) + return false; + + auto Value = + std::dynamic_pointer_cast(ValueOutputSignal->value()); + if (Value == nullptr) + return false; + + OutValue = ConvertAngleToUnreal(Value->value()); + return true; } diff --git a/Source/AGXUnrealBarrier/Public/OpenPLX/PLXSignalHandler.h b/Source/AGXUnrealBarrier/Public/OpenPLX/PLXSignalHandler.h index eb0b814d2..3f4955576 100644 --- a/Source/AGXUnrealBarrier/Public/OpenPLX/PLXSignalHandler.h +++ b/Source/AGXUnrealBarrier/Public/OpenPLX/PLXSignalHandler.h @@ -11,6 +11,7 @@ class FConstraintBarrier; class FSimulationBarrier; +struct FPLX_HingeAngleOutput; struct FPLX_LinearVelocityMotorVelocityInput; struct FOutputSignalHandlerRef; @@ -25,9 +26,10 @@ class AGXUNREALBARRIER_API FPLXSignalHandler bool IsInitialized() const; - // todo: match the base class RealInput on the PLX side for less overloads. bool Send(const FPLX_LinearVelocityMotorVelocityInput& Input, double Value); + bool Receive(const FPLX_HingeAngleOutput& Output, double& OutValue); + private: bool bIsInitialized {false}; FPLXModelRegistry* ModelInfo {nullptr}; From 07740311611eda8ffc7ed040c9a0e001060a17e1 Mon Sep 17 00:00:00 2001 From: Josef Date: Mon, 2 Dec 2024 16:01:58 +0100 Subject: [PATCH 27/31] Rename Brick -> OpenPLX part 1 --- Source/AGXCommon/Public/OpenPLX/PLX_Inputs.h | 8 +- Source/AGXCommon/Public/OpenPLX/PLX_Outputs.h | 8 +- .../OpenPLX/PLX_SignalHandlerComponent.cpp | 4 +- .../OpenPLX/PLX_SignalHandlerComponent.h | 4 +- .../Private/AGXSimObjectsReader.cpp | 10 +- .../Private/OpenPLX/PLXModelRegistry.cpp | 26 +-- .../Private/OpenPLX/PLXSignalHandler.cpp | 27 ++- .../Private/OpenPLX/PLX_Experiments.h | 178 ------------------ .../Private/Utilities/PLXUtilities.cpp | 81 ++++---- .../Private/Utilities/PLXUtilities.h | 14 +- .../Public/AGXSimObjectsReader.h | 2 +- .../Public/BarrierOnly/OpenPLX/OpenPLXRefs.h | 29 ++- .../Public/OpenPLX/PLXSignalHandler.h | 8 +- .../Private/Utilities/AGX_ImportUtilities.cpp | 2 +- .../Private/Widgets/AGX_ImportDialog.cpp | 2 +- .../Widgets/AGX_SynchronizeModelDialog.cpp | 2 +- .../AGXDynamicsLibrary.Build.cs | 98 +++++----- 17 files changed, 162 insertions(+), 341 deletions(-) delete mode 100644 Source/AGXUnrealBarrier/Private/OpenPLX/PLX_Experiments.h diff --git a/Source/AGXCommon/Public/OpenPLX/PLX_Inputs.h b/Source/AGXCommon/Public/OpenPLX/PLX_Inputs.h index 857be5bbc..7ad29b79f 100644 --- a/Source/AGXCommon/Public/OpenPLX/PLX_Inputs.h +++ b/Source/AGXCommon/Public/OpenPLX/PLX_Inputs.h @@ -31,18 +31,18 @@ struct AGXCOMMON_API FPLX_Input }; USTRUCT(BlueprintType) -struct AGXCOMMON_API FPLX_LinearVelocityMotorVelocityInput : public FPLX_Input +struct AGXCOMMON_API FPLX_LinearVelocity1DInput : public FPLX_Input { GENERATED_BODY() - FPLX_LinearVelocityMotorVelocityInput() = default; - explicit FPLX_LinearVelocityMotorVelocityInput(const FString& InName) + FPLX_LinearVelocity1DInput() = default; + explicit FPLX_LinearVelocity1DInput(const FString& InName) : FPLX_Input(InName) { } virtual UScriptStruct* GetType() const override { - return FPLX_LinearVelocityMotorVelocityInput::StaticStruct(); + return FPLX_LinearVelocity1DInput::StaticStruct(); } }; diff --git a/Source/AGXCommon/Public/OpenPLX/PLX_Outputs.h b/Source/AGXCommon/Public/OpenPLX/PLX_Outputs.h index 57a6b3f2f..4506e470b 100644 --- a/Source/AGXCommon/Public/OpenPLX/PLX_Outputs.h +++ b/Source/AGXCommon/Public/OpenPLX/PLX_Outputs.h @@ -31,18 +31,18 @@ struct AGXCOMMON_API FPLX_Output }; USTRUCT(BlueprintType) -struct AGXCOMMON_API FPLX_HingeAngleOutput : public FPLX_Output +struct AGXCOMMON_API FPLX_AngleOutput : public FPLX_Output { GENERATED_BODY() - FPLX_HingeAngleOutput() = default; - explicit FPLX_HingeAngleOutput(const FString& InName) + FPLX_AngleOutput() = default; + explicit FPLX_AngleOutput(const FString& InName) : FPLX_Output(InName) { } virtual UScriptStruct* GetType() const override { - return FPLX_HingeAngleOutput::StaticStruct(); + return FPLX_AngleOutput::StaticStruct(); } }; diff --git a/Source/AGXUnreal/Private/OpenPLX/PLX_SignalHandlerComponent.cpp b/Source/AGXUnreal/Private/OpenPLX/PLX_SignalHandlerComponent.cpp index 6d80c9544..ab429c07b 100644 --- a/Source/AGXUnreal/Private/OpenPLX/PLX_SignalHandlerComponent.cpp +++ b/Source/AGXUnreal/Private/OpenPLX/PLX_SignalHandlerComponent.cpp @@ -15,7 +15,7 @@ UPLX_SignalHandlerComponent::UPLX_SignalHandlerComponent() PrimaryComponentTick.bCanEverTick = false; } -bool UPLX_SignalHandlerComponent::Send(const FPLX_LinearVelocityMotorVelocityInput& Input, double Value) +bool UPLX_SignalHandlerComponent::Send(const FPLX_LinearVelocity1DInput& Input, double Value) { if (!SignalHandler.IsInitialized()) return false; @@ -60,7 +60,7 @@ namespace PLX_SignalHandlerComponent_helpers } } -bool UPLX_SignalHandlerComponent::Receive(const FPLX_HingeAngleOutput& Output, double& OutValue) +bool UPLX_SignalHandlerComponent::Receive(const FPLX_AngleOutput& Output, double& OutValue) { return SignalHandler.Receive(Output, OutValue); } diff --git a/Source/AGXUnreal/Public/OpenPLX/PLX_SignalHandlerComponent.h b/Source/AGXUnreal/Public/OpenPLX/PLX_SignalHandlerComponent.h index cb23a438d..894f8a186 100644 --- a/Source/AGXUnreal/Public/OpenPLX/PLX_SignalHandlerComponent.h +++ b/Source/AGXUnreal/Public/OpenPLX/PLX_SignalHandlerComponent.h @@ -27,10 +27,10 @@ class AGXUNREAL_API UPLX_SignalHandlerComponent : public UActorComponent UPLX_SignalHandlerComponent(); UFUNCTION(BlueprintCallable, Category = "OpenPLX") - bool Send(const FPLX_LinearVelocityMotorVelocityInput& Input, double Value); + bool Send(const FPLX_LinearVelocity1DInput& Input, double Value); UFUNCTION(BlueprintCallable, Category = "OpenPLX") - bool Receive(const FPLX_HingeAngleOutput& Output, double& OutValue); + bool Receive(const FPLX_AngleOutput& Output, double& OutValue); //~ Begin UActorComponent Interface virtual void BeginPlay() override; diff --git a/Source/AGXUnrealBarrier/Private/AGXSimObjectsReader.cpp b/Source/AGXUnrealBarrier/Private/AGXSimObjectsReader.cpp index d4e2153e9..56bdb4cd8 100644 --- a/Source/AGXUnrealBarrier/Private/AGXSimObjectsReader.cpp +++ b/Source/AGXUnrealBarrier/Private/AGXSimObjectsReader.cpp @@ -16,7 +16,7 @@ #include "Utilities/PLXUtilities.h" // OpenPLX includes. -#include "Brick/brickagx/BrickToAgxMapper.h" +#include "OpenPLX/agx-openplx/OpenPlxToAgxMapper.h" // AGX Dynamics includes. #include "BeginAGXIncludes.h" @@ -638,8 +638,8 @@ bool FAGXSimObjectsReader::ReadUrdf( bool FAGXSimObjectsReader::ReadOpenPLXFile( const FString& Filename, FSimulationObjectCollection& OutSimObjects) { - std::shared_ptr AGXCache; - Brick::Core::ObjectPtr PLXModel = FPLXUtilities::LoadModel(Filename, AGXCache); + std::shared_ptr AGXCache; + openplx::Core::ObjectPtr PLXModel = FPLXUtilities::LoadModel(Filename, AGXCache); if (PLXModel == nullptr) { UE_LOG( @@ -651,14 +651,14 @@ bool FAGXSimObjectsReader::ReadOpenPLXFile( } agxSDK::SimulationRef Simulation {new agxSDK::Simulation()}; - BrickAgx::BrickToAgxMapper Mapper(Simulation, Convert(Filename), AGXCache); + agxopenplx::OpenPlxToAgxMapper Mapper(Simulation, Convert(Filename), AGXCache); agxSDK::AssemblyRef AssemblyAGX = Mapper.mapObject(PLXModel); Simulation->add(AssemblyAGX); ::ReadAll(*Simulation, Filename, OutSimObjects); // Read PLX inputs. - auto System = std::dynamic_pointer_cast(PLXModel); + auto System = std::dynamic_pointer_cast(PLXModel); OutSimObjects.GetPLXInputs() = FPLXUtilities::GetInputs(System.get()); OutSimObjects.GetPLXOutputs() = FPLXUtilities::GetOutputs(System.get()); diff --git a/Source/AGXUnrealBarrier/Private/OpenPLX/PLXModelRegistry.cpp b/Source/AGXUnrealBarrier/Private/OpenPLX/PLXModelRegistry.cpp index a9815a19a..897311e26 100644 --- a/Source/AGXUnrealBarrier/Private/OpenPLX/PLXModelRegistry.cpp +++ b/Source/AGXUnrealBarrier/Private/OpenPLX/PLXModelRegistry.cpp @@ -55,19 +55,19 @@ namespace FPLXModelRegistry_helpers } void MapAllInputs( - Brick::Physics3D::System* System, - std::unordered_map>& + openplx::Physics3D::System* System, + std::unordered_map>& OutInputs) { if (System == nullptr) return; - for (auto& Subsystem : System->getValues()) + for (auto& Subsystem : System->getValues()) { MapAllInputs(System, OutInputs); } - for (auto& Input : System->getValues()) + for (auto& Input : System->getValues()) { if (Input == nullptr) continue; @@ -138,17 +138,17 @@ FPLXModelRegistry::Handle FPLXModelRegistry::Register( // The InputSignalListener always needs to be re-created even if it existed before, since now // we have added another sub-assembly containing agx-objects it needs to know about. - ModelDatum->InputSignalListener = new BrickAgx::InputSignalListener(ModelDatum->Assembly); - Simulation.GetNative()->Native->add(ModelDatum->InputSignalListener); +// ModelDatum->InputSignalListener = new agxopenplx::InputSignalListener(ModelDatum->Assembly); // TODO!!!! + // Simulation.GetNative()->Native->add(ModelDatum->InputSignalListener); // TODO!!!! // Finally add an OutputSignalListener that is used for this PLX model instance only, producing // output signals. It has a unique name prefix that is used to keep signals from getting name // collisions when having mutliple instances of the same PLX model in the world. - agx::ref_ptr OutputSignalListener = - new BrickAgx::OutputSignalListener(Assembly.Native, ModelDatum->PLXModel); - ModelDatum->OutputSignalListeners.insert({Convert(Prefix), OutputSignalListener}); - AGX_CHECK(!Simulation.GetNative()->Native->remove(OutputSignalListener)); - Simulation.GetNative()->Native->add(OutputSignalListener); + //agx::ref_ptr OutputSignalListener = // TODO!!!! + // new agxopenplx::OutputSignalListener(Assembly.Native, ModelDatum->PLXModel); // TODO!!!! + //ModelDatum->OutputSignalListeners.insert({Convert(Prefix), OutputSignalListener}); // TODO!!!! + //AGX_CHECK(!Simulation.GetNative()->Native->remove(OutputSignalListener)); // TODO!!!! + //Simulation.GetNative()->Native->add(OutputSignalListener); // TODO!!!! return Handle; } @@ -203,14 +203,14 @@ FPLXModelRegistry::Handle FPLXModelRegistry::PrepareNewModel(const FString& PLXF return InvalidHandle; } - auto System = std::dynamic_pointer_cast(NewModel.PLXModel); + auto System = std::dynamic_pointer_cast(NewModel.PLXModel); FPLXModelRegistry_helpers::MapAllInputs(System.get(), NewModel.Inputs); // OpenPLX's InputSignalListener expects an Assembly with a PowerLine always. // Therefore we add one here, even though it's a bit of a hack. NewModel.Assembly = new agxSDK::Assembly(); agxPowerLine::PowerLineRef RequiredDummyPowerLine = new agxPowerLine::PowerLine(); - RequiredDummyPowerLine->setName(agx::Name("BrickPowerLine")); + RequiredDummyPowerLine->setName(agx::Name("OpenPlxPowerLine")); NewModel.Assembly->add(RequiredDummyPowerLine); const Handle NewHande = FPLXModelRegistry_helpers::Convert(Native->ModelData.size()); diff --git a/Source/AGXUnrealBarrier/Private/OpenPLX/PLXSignalHandler.cpp b/Source/AGXUnrealBarrier/Private/OpenPLX/PLXSignalHandler.cpp index 589f6324b..ae02450ba 100644 --- a/Source/AGXUnrealBarrier/Private/OpenPLX/PLXSignalHandler.cpp +++ b/Source/AGXUnrealBarrier/Private/OpenPLX/PLXSignalHandler.cpp @@ -15,9 +15,8 @@ #include "Utilities/PLXUtilities.h" // OpenPLX includes. -#include "Brick/brickagx/Signals.h" -#include "Brick/brickagx/SignalListenerUtils.h" -#include "Brick/Physics/Signals/RealInputSignal.h" +#include "OpenPLX/agx-openplx/SignalListenerUtils.h" +#include "OpenPLX/Physics/Signals/RealInputSignal.h" void FPLXSignalHandler::Init( const FString& PLXFile, const FString& UniqueModelInstancePrefix, @@ -37,7 +36,7 @@ void FPLXSignalHandler::Init( // OpenPLX OutputSignalListener requires the assembly to contain a PowerLine with a // cetain name. Remove once this has been cleaned up in OpenPLX, it's a bit hacky. agxPowerLine::PowerLineRef RequiredDummyPowerLine = new agxPowerLine::PowerLine(); - RequiredDummyPowerLine->setName(agx::Name("BrickPowerLine")); + RequiredDummyPowerLine->setName(agx::Name("OpenPlxPowerLine")); Assembly->add(RequiredDummyPowerLine); FAssemblyRef AssemblyRef(Assembly); @@ -57,7 +56,7 @@ bool FPLXSignalHandler::IsInitialized() const return bIsInitialized; } -bool FPLXSignalHandler::Send(const FPLX_LinearVelocityMotorVelocityInput& Input, double Value) +bool FPLXSignalHandler::Send(const FPLX_LinearVelocity1DInput& Input, double Value) { check(IsInitialized()); if (ModelInfo == nullptr || ModelHandle == FPLXModelRegistry::InvalidHandle) @@ -71,15 +70,15 @@ bool FPLXSignalHandler::Send(const FPLX_LinearVelocityMotorVelocityInput& Input, if (PLXInput == ModelDatum->Inputs.end()) return false; - auto Signal = Brick::Physics::Signals::RealInputSignal::create( + auto Signal = openplx::Physics::Signals::RealInputSignal::create( ConvertDistanceToAGX(Value), PLXInput->second); // Todo: prepend unique instance name prefix to signal once supported. - BrickAgx::Signals::sendInputSignal(Signal); + //agxopenplx::Signals::sendInputSignal(Signal); // // TODO!!!! return true; } -bool FPLXSignalHandler::Receive(const FPLX_HingeAngleOutput& Output, double& OutValue) +bool FPLXSignalHandler::Receive(const FPLX_AngleOutput& Output, double& OutValue) { check(IsInitialized()); if (ModelInfo == nullptr || ModelHandle == FPLXModelRegistry::InvalidHandle) @@ -88,17 +87,17 @@ bool FPLXSignalHandler::Receive(const FPLX_HingeAngleOutput& Output, double& Out // Todo: make this more efficient than looking through all outputs every time. // We could build a map from name to signal in the ModelRegistry, each time step, for example. // Do this once the signal refactoring in OpenPLX has been done. - auto ValueOutputSignal = - BrickAgx::getSignalBySourceName( - BrickAgx::Signals::getOutputSignals(), Convert(Output.Name)); + /*auto ValueOutputSignal = // // TODO!!!! + agxopenplx::getSignalBySourceName( // TODO!!!! + agxopenplx::Signals::getOutputSignals(), Convert(Output.Name)); // TODO!!!! if (ValueOutputSignal == nullptr) return false; auto Value = - std::dynamic_pointer_cast(ValueOutputSignal->value()); + std::dynamic_pointer_cast(ValueOutputSignal->value()); // TODO!!!! if (Value == nullptr) - return false; + return false; // TODO!!!! - OutValue = ConvertAngleToUnreal(Value->value()); + OutValue = ConvertAngleToUnreal(Value->value());*/ // TODO!!!! return true; } diff --git a/Source/AGXUnrealBarrier/Private/OpenPLX/PLX_Experiments.h b/Source/AGXUnrealBarrier/Private/OpenPLX/PLX_Experiments.h deleted file mode 100644 index 880a87624..000000000 --- a/Source/AGXUnrealBarrier/Private/OpenPLX/PLX_Experiments.h +++ /dev/null @@ -1,178 +0,0 @@ -#pragma once - -// Never merge this to master. Only for development experiments/tests. -// TODO @todo (just to make this file be found) - -// Brick includes. -#include "Brick/brick/BrickContext.h" -#include "Brick/brick/BrickContextInternal.h" -#include "Brick/Brick/BrickCoreApi.h" -#include "Brick/brickagx/AgxCache.h" -#include "Brick/brickagx/BrickAgxApi.h" -#include "Brick/brickagx/BrickToAgxMapper.h" -#include "Brick/brickagx/InputSignalListener.h" -#include "Brick/brickagx/Signals.h" -#include "Brick/Math/Math_all.h" -#include "Brick/Physics/Physics_all.h" -#include "Brick/Physics1D/Physics1D_all.h" -#include "Brick/Physics3D/Physics3D_all.h" -#include "Brick/DriveTrain/DriveTrain_all.h" -#include "Brick/Robotics/Robotics_all.h" -#include "Brick/Simulation/Simulation_all.h" -#include "Brick/Vehicles/Vehicles_all.h" -#include "Brick/Terrain/Terrain_all.h" -#include "Brick/Visuals/Visuals_all.h" -#include "Brick/Urdf/Urdf_all.h" - - -std::shared_ptr CreateBrickContext( - std::shared_ptr AGXCache) -{ - const FString BrickBundlesPath = FPaths::Combine( - FAGX_Environment::GetPluginSourcePath(), "Thirdparty", "agx", "brickbundles"); - auto BrickCtx = std::make_shared( - std::vector({Convert(BrickBundlesPath)})); - - auto InternalContext = Brick::Core::Api::BrickContextInternal::fromContext(*BrickCtx); - auto EvalCtx = InternalContext->evaluatorContext().get(); - - Math_register_factories(EvalCtx); - Physics_register_factories(EvalCtx); - Physics1D_register_factories(EvalCtx); - Physics3D_register_factories(EvalCtx); - DriveTrain_register_factories(EvalCtx); - Robotics_register_factories(EvalCtx); - Simulation_register_factories(EvalCtx); - Vehicles_register_factories(EvalCtx); - Terrain_register_factories(EvalCtx); - Visuals_register_factories(EvalCtx); - Urdf_register_factories(EvalCtx); - - BrickAgx::register_plugins(*BrickCtx, AGXCache); - return BrickCtx; -} - -Brick::Core::ObjectPtr LoadModelFromFile( - const std::string& BrickFile, std::shared_ptr AGXCache) -{ - auto Context = CreateBrickContext(AGXCache); - if (Context == nullptr) - { - UE_LOG(LogAGX, Error, TEXT("Error Creating Brick Context")); - return nullptr; - } - - auto LoadedModel = Brick::Core::Api::loadModelFromFile(BrickFile, {}, *Context); - - if (Context->hasErrors()) - { - LoadedModel = nullptr; - for (auto Error : Context->getErrors()) - UE_LOG(LogAGX, Error, TEXT("Error in Brick Context : %d"), Error->getErrorCode()); - - return nullptr; - } - - return LoadedModel; -} - -static Brick::Core::ObjectPtr BrickModel = nullptr; -static std::shared_ptr AGXCache = nullptr; -static std::vector> SignalInputs; -static agxSDK::AssemblyRef Assembly; -static std::shared_ptr InputSignalListener; - -void findAllSignalInputs( - Brick::Physics3D::System* System, - std::vector>& OutSignalInputs) -{ - for (auto& Subsystem : System->getValues()) - { - findAllSignalInputs(System, OutSignalInputs); - } - - for (auto& SignalInput : System->getValues()) - { - SignalInputs.push_back(SignalInput); - } -} - -void BrickExperimentInit(agxSDK::Simulation* Simulation) -{ - const FString Filename = "C:/Users/Admin/Desktop/inverted_pendulum.brick"; - BrickModel = LoadModelFromFile(Convert(Filename), AGXCache); - if (BrickModel == nullptr) - { - UE_LOG( - LogAGX, Error, - TEXT("Could not read Brick file '%s'. The Log category LogAGXDynamics may include more " - "details."), - *Filename); - return; - } - UE_LOG(LogTemp, Warning, TEXT("BrickExperimentInit %d"), Simulation != nullptr); - - auto System = std::dynamic_pointer_cast(BrickModel); - findAllSignalInputs(System.get(), SignalInputs); - - for (auto& Constraint : Simulation->getConstraints()) - { - for (size_t i = 0; i < Constraint->getNumSecondaryConstraints(); i++) - { - agx::ElementaryConstraint* ElemC = Constraint->getSecondaryConstraint(i); - agx::TargetSpeedController* TargetSC = dynamic_cast(ElemC); - if (TargetSC == nullptr) - continue; - - TargetSC->setName(agx::Name("PendulumScene.cart_motor")); - UE_LOG( - LogTemp, Warning, TEXT("Elem Constraint %s is targetspeed: %d"), - *Convert(ElemC->getName()), TargetSC != nullptr); - - Assembly = new agxSDK::Assembly(); - Assembly->add(Constraint); - } - } - - if (Assembly->getConstraints().size() == 0) - return; - - // Hacky hacky hack-hack start - agxPowerLine::PowerLineRef BrickPowerLineHack = new agxPowerLine::PowerLine(); - BrickPowerLineHack->setName(agx::Name("BrickPowerLine")); - Assembly->add(BrickPowerLineHack); - // Hacky hacky hack-hack end - - InputSignalListener = std::make_shared(Assembly); - Simulation->add(InputSignalListener.get()); -} - -void BrickExperimentCheck(agxSDK::Simulation* Simulation) -{ - UE_LOG(LogTemp, Warning, TEXT("BrickExperimentCheck %d"), Simulation != nullptr); - if (SignalInputs.size() == 0) - { - UE_LOG(LogTemp, Error, TEXT("Num signal inputs found in check: %d"), SignalInputs.size()); - return; - } - - std::shared_ptr MyCoolInputSignal; - - for (std::shared_ptr& SignalInput : SignalInputs) - { - auto LinearVelInput = - std::dynamic_pointer_cast( - SignalInput); - if (LinearVelInput == nullptr) - continue; - - UE_LOG( - LogTemp, Warning, TEXT("Found LinearVelocityMotorVelocityInput! %s"), - *Convert(LinearVelInput->getName())); - UE_LOG( - LogTemp, Warning, TEXT("Motor name: %s"), *Convert(LinearVelInput->motor()->getName())); - - MyCoolInputSignal = Brick::Physics::Signals::RealInputSignal::create(-1.0, LinearVelInput); - BrickAgx::Signals::sendInputSignal(MyCoolInputSignal); - } -} diff --git a/Source/AGXUnrealBarrier/Private/Utilities/PLXUtilities.cpp b/Source/AGXUnrealBarrier/Private/Utilities/PLXUtilities.cpp index 6a36ee10b..81397e6d8 100644 --- a/Source/AGXUnrealBarrier/Private/Utilities/PLXUtilities.cpp +++ b/Source/AGXUnrealBarrier/Private/Utilities/PLXUtilities.cpp @@ -7,22 +7,22 @@ #include "TypeConversions.h" // OpenPLX includes. -#include "Brick/brick/BrickContext.h" -#include "Brick/brick/BrickContextInternal.h" -#include "Brick/Brick/BrickCoreApi.h" -#include "Brick/brickagx/BrickAgxApi.h" -#include "Brick/Math/Math_all.h" -#include "Brick/Physics/Physics_all.h" -#include "Brick/Physics1D/Physics1D_all.h" -#include "Brick/Physics3D/Physics3D_all.h" -#include "Brick/Physics3D/Signals/LinearVelocityMotorVelocityInput.h" -#include "Brick/DriveTrain/DriveTrain_all.h" -#include "Brick/Robotics/Robotics_all.h" -#include "Brick/Simulation/Simulation_all.h" -#include "Brick/Vehicles/Vehicles_all.h" -#include "Brick/Terrain/Terrain_all.h" -#include "Brick/Visuals/Visuals_all.h" -#include "Brick/Urdf/Urdf_all.h" +#include "OpenPLX/openplx/OpenPlxContext.h" +#include "OpenPLX/openplx/OpenPlxContextInternal.h" +#include "OpenPLX/openplx/OpenPlxCoreAPI.h" +#include "OpenPLX/agx-openplx/AgxOpenPlxApi.h" +#include "OpenPLX/Math/Math_all.h" +#include "OpenPLX/Physics/Physics_all.h" +#include "OpenPLX/Physics1D/Physics1D_all.h" +#include "OpenPLX/Physics3D/Physics3D_all.h" +#include "OpenPLX/Physics/Signals/LinearVelocity1DInput.h" +#include "OpenPLX/DriveTrain/DriveTrain_all.h" +#include "OpenPLX/Robotics/Robotics_all.h" +#include "OpenPLX/Simulation/Simulation_all.h" +#include "OpenPLX/Vehicles/Vehicles_all.h" +#include "OpenPLX/Terrain/Terrain_all.h" +#include "OpenPLX/Visuals/Visuals_all.h" +#include "OpenPLX/Urdf/Urdf_all.h" // Unreal Engine includes. @@ -33,22 +33,22 @@ namespace PLXUtilities_helpers { - void GetInputs(Brick::Physics3D::System* System, TArray>& OutInputs) + void GetInputs(openplx::Physics3D::System* System, TArray>& OutInputs) { if (System == nullptr) return; - for (auto& Subsystem : System->getValues()) + for (auto& Subsystem : System->getValues()) { GetInputs(System, OutInputs); } - for (auto& Input : System->getValues()) + for (auto& Input : System->getValues()) { - if (auto Lvmvi = std::dynamic_pointer_cast< - Brick::Physics3D::Signals::LinearVelocityMotorVelocityInput>(Input)) + if (auto Lvmvi = std::dynamic_pointer_cast( + Input)) { - OutInputs.Emplace(MakeUnique( + OutInputs.Emplace(MakeUnique( Convert(Input->getName()))); continue; } @@ -57,22 +57,23 @@ namespace PLXUtilities_helpers } } - void GetOutputs(Brick::Physics3D::System* System, TArray>& OutOutputs) + void GetOutputs(openplx::Physics3D::System* System, TArray>& OutOutputs) { if (System == nullptr) return; - for (auto& Subsystem : System->getValues()) + for (auto& Subsystem : System->getValues()) { GetOutputs(System, OutOutputs); } - for (auto& Output : System->getValues()) + for (auto& Output : System->getValues()) { - if (auto Hao = std::dynamic_pointer_cast(Output)) + if (auto Hao = + std::dynamic_pointer_cast(Output)) { OutOutputs.Emplace( - MakeUnique(Convert(Output->getName()))); + MakeUnique(Convert(Output->getName()))); continue; } @@ -80,15 +81,15 @@ namespace PLXUtilities_helpers } } - std::shared_ptr CreatePLXContext( - std::shared_ptr AGXCache) + std::shared_ptr CreatePLXContext( + std::shared_ptr AGXCache) { const FString PLXBundlesPath = FPaths::Combine( - FAGX_Environment::GetPluginSourcePath(), "Thirdparty", "agx", "brickbundles"); - auto PLXCtx = std::make_shared( + FAGX_Environment::GetPluginSourcePath(), "Thirdparty", "agx", "openplxbundles"); + auto PLXCtx = std::make_shared( std::vector({Convert(PLXBundlesPath)})); - auto InternalContext = Brick::Core::Api::BrickContextInternal::fromContext(*PLXCtx); + auto InternalContext = openplx::Core::Api::OpenPlxContextInternal::fromContext(*PLXCtx); auto EvalCtx = InternalContext->evaluatorContext().get(); Math_register_factories(EvalCtx); @@ -103,12 +104,12 @@ namespace PLXUtilities_helpers Visuals_register_factories(EvalCtx); Urdf_register_factories(EvalCtx); - BrickAgx::register_plugins(*PLXCtx, AGXCache); + agxopenplx::register_plugins(*PLXCtx, AGXCache); return PLXCtx; } - Brick::Core::ObjectPtr LoadModelFromFile( - const std::string& OpenPLXFile, std::shared_ptr AGXCache) + openplx::Core::ObjectPtr LoadModelFromFile( + const std::string& OpenPLXFile, std::shared_ptr AGXCache) { auto Context = CreatePLXContext(AGXCache); if (Context == nullptr) @@ -117,7 +118,7 @@ namespace PLXUtilities_helpers return nullptr; } - auto LoadedModel = Brick::Core::Api::loadModelFromFile(OpenPLXFile, {}, *Context); + auto LoadedModel = openplx::Core::Api::loadModelFromFile(OpenPLXFile, {}, *Context); if (Context->hasErrors()) { @@ -132,8 +133,8 @@ namespace PLXUtilities_helpers } } -Brick::Core::ObjectPtr FPLXUtilities::LoadModel( - const FString& Filename, std::shared_ptr AGXCache) +openplx::Core::ObjectPtr FPLXUtilities::LoadModel( + const FString& Filename, std::shared_ptr AGXCache) { if (!FPaths::FileExists(Filename)) { @@ -147,7 +148,7 @@ Brick::Core::ObjectPtr FPLXUtilities::LoadModel( return PLXUtilities_helpers::LoadModelFromFile(Convert(Filename), AGXCache); } -TArray> FPLXUtilities::GetInputs(Brick::Physics3D::System* System) +TArray> FPLXUtilities::GetInputs(openplx::Physics3D::System* System) { TArray> Inputs; if (System == nullptr) @@ -157,7 +158,7 @@ TArray> FPLXUtilities::GetInputs(Brick::Physics3D::System return Inputs; } -TArray> FPLXUtilities::GetOutputs(Brick::Physics3D::System* System) +TArray> FPLXUtilities::GetOutputs(openplx::Physics3D::System* System) { TArray> Outputs; if (System == nullptr) diff --git a/Source/AGXUnrealBarrier/Private/Utilities/PLXUtilities.h b/Source/AGXUnrealBarrier/Private/Utilities/PLXUtilities.h index 205dbf1a3..f0abd9c61 100644 --- a/Source/AGXUnrealBarrier/Private/Utilities/PLXUtilities.h +++ b/Source/AGXUnrealBarrier/Private/Utilities/PLXUtilities.h @@ -8,10 +8,10 @@ #include "OpenPLX/PLX_Outputs.h" // OpenPLX includes. -#include "Brick/brick/Object.h" -#include "Brick/brickagx/AgxCache.h" +#include "OpenPLX/openplx/Object.h" +#include "OpenPLX/agx-openplx/AgxCache.h" -namespace Brick +namespace openplx { namespace Physics3D { @@ -22,9 +22,9 @@ namespace Brick class FPLXUtilities { public: - static Brick::Core::ObjectPtr LoadModel( - const FString& Filename, std::shared_ptr AGXCache); + static openplx::Core::ObjectPtr LoadModel( + const FString& Filename, std::shared_ptr AGXCache); - static TArray> GetInputs(Brick::Physics3D::System* System); - static TArray> GetOutputs(Brick::Physics3D::System* System); + static TArray> GetInputs(openplx::Physics3D::System* System); + static TArray> GetOutputs(openplx::Physics3D::System* System); }; diff --git a/Source/AGXUnrealBarrier/Public/AGXSimObjectsReader.h b/Source/AGXUnrealBarrier/Public/AGXSimObjectsReader.h index ec412ac6b..e23e0a770 100644 --- a/Source/AGXUnrealBarrier/Public/AGXSimObjectsReader.h +++ b/Source/AGXUnrealBarrier/Public/AGXSimObjectsReader.h @@ -36,5 +36,5 @@ namespace FAGXSimObjectsReader const TArray& InitJoints, FSimulationObjectCollection& OutSimObjects); AGXUNREALBARRIER_API bool ReadOpenPLXFile( - const FString& BrickFilePath, FSimulationObjectCollection& OutSimObjects); + const FString& OpenPLXFilePath, FSimulationObjectCollection& OutSimObjects); }; diff --git a/Source/AGXUnrealBarrier/Public/BarrierOnly/OpenPLX/OpenPLXRefs.h b/Source/AGXUnrealBarrier/Public/BarrierOnly/OpenPLX/OpenPLXRefs.h index 83ee7f1e3..12d33c9a2 100644 --- a/Source/AGXUnrealBarrier/Public/BarrierOnly/OpenPLX/OpenPLXRefs.h +++ b/Source/AGXUnrealBarrier/Public/BarrierOnly/OpenPLX/OpenPLXRefs.h @@ -1,11 +1,10 @@ #pragma once // OpenPLX includes. -#include "Brick/brickagx/AgxCache.h" -#include "Brick/brickagx/Signals.h" -#include "Brick/brickagx/InputSignalListener.h" -#include "Brick/brickagx/OutputSignalListener.h" -#include "Brick/Physics3D/System.h" +#include "OpenPLX/agx-openplx/AgxCache.h" +#include "OpenPLX/agx-openplx/InputSignalListener.h" +#include "OpenPLX/agx-openplx/OutputSignalListener.h" +#include "OpenPLX/Physics3D/System.h" // AGX Dynamics includes. #include "BeginAGXIncludes.h" @@ -20,35 +19,35 @@ struct FInputSignalHandlerRef { - agx::ref_ptr Native; + agx::ref_ptr Native; FInputSignalHandlerRef() = default; FInputSignalHandlerRef(agxSDK::Assembly* Assembly) - : Native(new BrickAgx::InputSignalListener(Assembly)) + // : Native(new agxopenplx::InputSignalListener(Assembly)) // TODO!!!! { } }; struct FOutputSignalHandlerRef { - agx::ref_ptr Native; + agx::ref_ptr Native; FOutputSignalHandlerRef() = default; FOutputSignalHandlerRef( - agxSDK::Assembly* Assembly, const std::shared_ptr& PlxModel) - : Native(new BrickAgx::OutputSignalListener(Assembly, PlxModel)) + agxSDK::Assembly* Assembly, const std::shared_ptr& PlxModel) + //: Native(new agxopenplx::OutputSignalListener(Assembly, PlxModel)) // TODO!!!! { } }; struct FPLXModelDatum { - std::shared_ptr AGXCache; - Brick::Core::ObjectPtr PLXModel; - agx::ref_ptr InputSignalListener; - std::unordered_map> OutputSignalListeners; + std::shared_ptr AGXCache; + openplx::Core::ObjectPtr PLXModel; + agx::ref_ptr InputSignalListener; + std::unordered_map> OutputSignalListeners; agxSDK::AssemblyRef Assembly; - std::unordered_map> Inputs; + std::unordered_map> Inputs; }; struct FPLXModelData diff --git a/Source/AGXUnrealBarrier/Public/OpenPLX/PLXSignalHandler.h b/Source/AGXUnrealBarrier/Public/OpenPLX/PLXSignalHandler.h index 3f4955576..67a97883d 100644 --- a/Source/AGXUnrealBarrier/Public/OpenPLX/PLXSignalHandler.h +++ b/Source/AGXUnrealBarrier/Public/OpenPLX/PLXSignalHandler.h @@ -11,8 +11,8 @@ class FConstraintBarrier; class FSimulationBarrier; -struct FPLX_HingeAngleOutput; -struct FPLX_LinearVelocityMotorVelocityInput; +struct FPLX_AngleOutput; +struct FPLX_LinearVelocity1DInput; struct FOutputSignalHandlerRef; class AGXUNREALBARRIER_API FPLXSignalHandler @@ -26,9 +26,9 @@ class AGXUNREALBARRIER_API FPLXSignalHandler bool IsInitialized() const; - bool Send(const FPLX_LinearVelocityMotorVelocityInput& Input, double Value); + bool Send(const FPLX_LinearVelocity1DInput& Input, double Value); - bool Receive(const FPLX_HingeAngleOutput& Output, double& OutValue); + bool Receive(const FPLX_AngleOutput& Output, double& OutValue); private: bool bIsInitialized {false}; diff --git a/Source/AGXUnrealEditor/Private/Utilities/AGX_ImportUtilities.cpp b/Source/AGXUnrealEditor/Private/Utilities/AGX_ImportUtilities.cpp index 167d35ea8..ef8596c08 100644 --- a/Source/AGXUnrealEditor/Private/Utilities/AGX_ImportUtilities.cpp +++ b/Source/AGXUnrealEditor/Private/Utilities/AGX_ImportUtilities.cpp @@ -609,7 +609,7 @@ EAGX_ImportType FAGX_ImportUtilities::GetFrom(const FString& FilePath) { return EAGX_ImportType::Agx; } - else if (FileExtension.Equals("brick")) + else if (FileExtension.Equals("openplx")) { return EAGX_ImportType::Plx; } diff --git a/Source/AGXUnrealEditor/Private/Widgets/AGX_ImportDialog.cpp b/Source/AGXUnrealEditor/Private/Widgets/AGX_ImportDialog.cpp index 93b1b69b1..1dc604bba 100644 --- a/Source/AGXUnrealEditor/Private/Widgets/AGX_ImportDialog.cpp +++ b/Source/AGXUnrealEditor/Private/Widgets/AGX_ImportDialog.cpp @@ -51,7 +51,7 @@ namespace AGX_ImportDialog_helpers void SAGX_ImportDialog::Construct(const FArguments& InArgs) { - FileTypes = ".agx;*.brick;*.urdf"; + FileTypes = ".agx;*.openplx;*.urdf"; // clang-format off ChildSlot diff --git a/Source/AGXUnrealEditor/Private/Widgets/AGX_SynchronizeModelDialog.cpp b/Source/AGXUnrealEditor/Private/Widgets/AGX_SynchronizeModelDialog.cpp index 40e239144..6fa41dd2f 100644 --- a/Source/AGXUnrealEditor/Private/Widgets/AGX_SynchronizeModelDialog.cpp +++ b/Source/AGXUnrealEditor/Private/Widgets/AGX_SynchronizeModelDialog.cpp @@ -17,7 +17,7 @@ void SAGX_SynchronizeModelDialog::Construct(const FArguments& InArgs) { - FileTypes = ".agx;*.brick"; + FileTypes = ".agx;*.openplx"; ImportType = EAGX_ImportType::Agx; // clang-format off diff --git a/Source/ThirdParty/AGXDynamicsLibrary/AGXDynamicsLibrary.Build.cs b/Source/ThirdParty/AGXDynamicsLibrary/AGXDynamicsLibrary.Build.cs index 40056fbba..2806cec37 100644 --- a/Source/ThirdParty/AGXDynamicsLibrary/AGXDynamicsLibrary.Build.cs +++ b/Source/ThirdParty/AGXDynamicsLibrary/AGXDynamicsLibrary.Build.cs @@ -56,8 +56,8 @@ private enum LibSource /// libraries. AGX, - /// Brick libraries location. - Brick, + /// OpenPLX libraries location. + OpenPLX, /// The configuration part of AGX Dynamics. Contains generated header /// files for things such as version and CMake settings. Does not @@ -85,7 +85,7 @@ private enum LibSource /// Points to AGX Dynamics external resources. External - }; + }; /// A carrier for the paths associated with a LibSource. /// @@ -246,45 +246,45 @@ public AGXDynamicsLibrary(ReadOnlyTargetRules Target) : base(Target) LinkLibFiles.Add("agx-nt-ros2", LibSource.AGX); // OpenPLX - LinkLibFiles.Add("fmt", LibSource.Brick); - LinkLibFiles.Add("spdlog", LibSource.Brick); - LinkLibFiles.Add("brick.agx", LibSource.Brick); - LinkLibFiles.Add("brick.analysis", LibSource.Brick); - LinkLibFiles.Add("brick.bundle", LibSource.Brick); - LinkLibFiles.Add("brick.core.api", LibSource.Brick); - LinkLibFiles.Add("brick.error", LibSource.Brick); - LinkLibFiles.Add("brick.eval", LibSource.Brick); - LinkLibFiles.Add("brick.generate", LibSource.Brick); - LinkLibFiles.Add("brick.internal", LibSource.Brick); - LinkLibFiles.Add("brick.nodes", LibSource.Brick); - LinkLibFiles.Add("brick.osg", LibSource.Brick); - LinkLibFiles.Add("brick.parser", LibSource.Brick); - LinkLibFiles.Add("brick.runtime", LibSource.Brick); - LinkLibFiles.Add("DriveTrain", LibSource.Brick); - LinkLibFiles.Add("Math", LibSource.Brick); - LinkLibFiles.Add("Physics", LibSource.Brick); - LinkLibFiles.Add("Physics1D", LibSource.Brick); - LinkLibFiles.Add("Physics3D", LibSource.Brick); - LinkLibFiles.Add("Robotics", LibSource.Brick); - LinkLibFiles.Add("Simulation", LibSource.Brick); - LinkLibFiles.Add("Terrain", LibSource.Brick); - LinkLibFiles.Add("Urdf", LibSource.Brick); - LinkLibFiles.Add("Vehicles", LibSource.Brick); - LinkLibFiles.Add("Visuals", LibSource.Brick); - LinkLibFiles.Add("brickurdfplugin", LibSource.Brick); - LinkLibFiles.Add("urdfdom_model", LibSource.Brick); - LinkLibFiles.Add("urdfdom_model_state", LibSource.Brick); - LinkLibFiles.Add("urdfdom_world", LibSource.Brick); - LinkLibFiles.Add("urdfdom_sensor", LibSource.Brick); - LinkLibFiles.Add("console_bridge", LibSource.Brick); - LinkLibFiles.Add("tinyxml", LibSource.Brick); + LinkLibFiles.Add("fmt", LibSource.OpenPLX); + LinkLibFiles.Add("spdlog", LibSource.OpenPLX); + LinkLibFiles.Add("agx-openplx.api", LibSource.OpenPLX); + LinkLibFiles.Add("openplx.analysis", LibSource.OpenPLX); + LinkLibFiles.Add("openplx.bundle", LibSource.OpenPLX); + LinkLibFiles.Add("openplx.core.api", LibSource.OpenPLX); + LinkLibFiles.Add("openplx.error", LibSource.OpenPLX); + LinkLibFiles.Add("openplx.eval", LibSource.OpenPLX); + LinkLibFiles.Add("openplx.generate", LibSource.OpenPLX); + LinkLibFiles.Add("openplx.internal", LibSource.OpenPLX); + LinkLibFiles.Add("openplx.nodes", LibSource.OpenPLX); + LinkLibFiles.Add("agx-openplx.osg", LibSource.OpenPLX); + LinkLibFiles.Add("openplx.parser", LibSource.OpenPLX); + LinkLibFiles.Add("openplx.runtime", LibSource.OpenPLX); + LinkLibFiles.Add("DriveTrain", LibSource.OpenPLX); + LinkLibFiles.Add("Math", LibSource.OpenPLX); + LinkLibFiles.Add("Physics", LibSource.OpenPLX); + LinkLibFiles.Add("Physics1D", LibSource.OpenPLX); + LinkLibFiles.Add("Physics3D", LibSource.OpenPLX); + LinkLibFiles.Add("Robotics", LibSource.OpenPLX); + LinkLibFiles.Add("Simulation", LibSource.OpenPLX); + LinkLibFiles.Add("Terrain", LibSource.OpenPLX); + LinkLibFiles.Add("Urdf", LibSource.OpenPLX); + LinkLibFiles.Add("Vehicles", LibSource.OpenPLX); + LinkLibFiles.Add("Visuals", LibSource.OpenPLX); + LinkLibFiles.Add("openplxurdfplugin", LibSource.OpenPLX); + LinkLibFiles.Add("urdfdom_model", LibSource.OpenPLX); + LinkLibFiles.Add("urdfdom_model_state", LibSource.OpenPLX); + LinkLibFiles.Add("urdfdom_world", LibSource.OpenPLX); + LinkLibFiles.Add("urdfdom_sensor", LibSource.OpenPLX); + LinkLibFiles.Add("console_bridge", LibSource.OpenPLX); + LinkLibFiles.Add("tinyxml", LibSource.OpenPLX); // List of the include directories from aGX Dynamics and its // dependenciesthat we need. These will be added to the Unreal Engine // PublicIncludePaths. List IncludePaths = new List(); IncludePaths.Add(LibSource.AGX); - IncludePaths.Add(LibSource.Brick); + IncludePaths.Add(LibSource.OpenPLX); // OS specific dependencies. if (Target.Platform == UnrealTargetPlatform.Linux) @@ -370,7 +370,7 @@ public AGXDynamicsLibrary(ReadOnlyTargetRules Target) : base(Target) RuntimeDependencies.Add(Path.Combine(BundledAGXResourcesPath, "plugins", "*")); RuntimeDependencies.Add(Path.Combine(BundledAGXResourcesPath, "include", "*")); RuntimeDependencies.Add(Path.Combine(BundledAGXResourcesPath, "lib", "*")); - RuntimeDependencies.Add(Path.Combine(BundledAGXResourcesPath, "brick", "*")); + RuntimeDependencies.Add(Path.Combine(BundledAGXResourcesPath, "openplx", "*")); SetLicenseForCopySafe(Target); // This is a work-around for Linux which ensures that the .so files are @@ -734,12 +734,12 @@ private void BundleAGXResources(ReadOnlyTargetRules Target, Dictionary Date: Mon, 2 Dec 2024 16:05:12 +0100 Subject: [PATCH 28/31] Add missing thirdparty lib --- Source/ThirdParty/AGXDynamicsLibrary/AGXDynamicsLibrary.Build.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/ThirdParty/AGXDynamicsLibrary/AGXDynamicsLibrary.Build.cs b/Source/ThirdParty/AGXDynamicsLibrary/AGXDynamicsLibrary.Build.cs index 2806cec37..945dd09f4 100644 --- a/Source/ThirdParty/AGXDynamicsLibrary/AGXDynamicsLibrary.Build.cs +++ b/Source/ThirdParty/AGXDynamicsLibrary/AGXDynamicsLibrary.Build.cs @@ -247,6 +247,7 @@ public AGXDynamicsLibrary(ReadOnlyTargetRules Target) : base(Target) // OpenPLX LinkLibFiles.Add("fmt", LibSource.OpenPLX); + LinkLibFiles.Add("libsodium", LibSource.OpenPLX); LinkLibFiles.Add("spdlog", LibSource.OpenPLX); LinkLibFiles.Add("agx-openplx.api", LibSource.OpenPLX); LinkLibFiles.Add("openplx.analysis", LibSource.OpenPLX); From dfa9de5f39c08ffc1fef0d40ad9f734c5371ec7e Mon Sep 17 00:00:00 2001 From: Josef Date: Wed, 4 Dec 2024 09:39:11 +0100 Subject: [PATCH 29/31] Link against needed hash-library --- Source/ThirdParty/AGXDynamicsLibrary/AGXDynamicsLibrary.Build.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/ThirdParty/AGXDynamicsLibrary/AGXDynamicsLibrary.Build.cs b/Source/ThirdParty/AGXDynamicsLibrary/AGXDynamicsLibrary.Build.cs index 945dd09f4..de35ae3fe 100644 --- a/Source/ThirdParty/AGXDynamicsLibrary/AGXDynamicsLibrary.Build.cs +++ b/Source/ThirdParty/AGXDynamicsLibrary/AGXDynamicsLibrary.Build.cs @@ -279,6 +279,7 @@ public AGXDynamicsLibrary(ReadOnlyTargetRules Target) : base(Target) LinkLibFiles.Add("urdfdom_sensor", LibSource.OpenPLX); LinkLibFiles.Add("console_bridge", LibSource.OpenPLX); LinkLibFiles.Add("tinyxml", LibSource.OpenPLX); + LinkLibFiles.Add("hash-library", LibSource.OpenPLX); // List of the include directories from aGX Dynamics and its // dependenciesthat we need. These will be added to the Unreal Engine From ac9bfee2e6c4125bb709592b52f33ee8cf5a19ed Mon Sep 17 00:00:00 2001 From: Josef Date: Wed, 4 Dec 2024 13:36:15 +0100 Subject: [PATCH 30/31] Restore signal handling with new implementation --- .../OpenPLX/PLX_SignalHandlerComponent.cpp | 5 +- .../Private/OpenPLX/PLXModelRegistry.cpp | 122 +++++------------- .../Private/OpenPLX/PLXSignalHandler.cpp | 115 +++++++++++++---- .../Private/Utilities/PLXUtilities.cpp | 34 +++++ .../Private/Utilities/PLXUtilities.h | 3 + .../Public/BarrierOnly/OpenPLX/OpenPLXRefs.h | 39 ++++-- .../Public/OpenPLX/PLXModelRegistry.h | 32 +++-- .../Public/OpenPLX/PLXSignalHandler.h | 21 ++- 8 files changed, 213 insertions(+), 158 deletions(-) diff --git a/Source/AGXUnreal/Private/OpenPLX/PLX_SignalHandlerComponent.cpp b/Source/AGXUnreal/Private/OpenPLX/PLX_SignalHandlerComponent.cpp index ab429c07b..97c296a57 100644 --- a/Source/AGXUnreal/Private/OpenPLX/PLX_SignalHandlerComponent.cpp +++ b/Source/AGXUnreal/Private/OpenPLX/PLX_SignalHandlerComponent.cpp @@ -102,8 +102,5 @@ void UPLX_SignalHandlerComponent::BeginPlay() TArray ConstraintBarriers = CollectConstraintBarriers(GetOwner()); // Initialize SignalHandler in Barrier module. - const FString UniqueInstancePrefix = GetOwner()->GetName(); - SignalHandler.Init( - *PLXFile, UniqueInstancePrefix, *SimulationBarrier, *PLXModelRegistryBarrier, - ConstraintBarriers); + SignalHandler.Init(*PLXFile, *SimulationBarrier, *PLXModelRegistryBarrier, ConstraintBarriers); } diff --git a/Source/AGXUnrealBarrier/Private/OpenPLX/PLXModelRegistry.cpp b/Source/AGXUnrealBarrier/Private/OpenPLX/PLXModelRegistry.cpp index 897311e26..2e0f8d74b 100644 --- a/Source/AGXUnrealBarrier/Private/OpenPLX/PLXModelRegistry.cpp +++ b/Source/AGXUnrealBarrier/Private/OpenPLX/PLXModelRegistry.cpp @@ -15,7 +15,7 @@ #include FPLXModelRegistry::FPLXModelRegistry() - : Native(std::make_unique()) + : Native(std::make_unique()) { } @@ -54,7 +54,7 @@ namespace FPLXModelRegistry_helpers return static_cast(Val); } - void MapAllInputs( + void MapInputs( openplx::Physics3D::System* System, std::unordered_map>& OutInputs) @@ -64,7 +64,7 @@ namespace FPLXModelRegistry_helpers for (auto& Subsystem : System->getValues()) { - MapAllInputs(System, OutInputs); + MapInputs(System, OutInputs); } for (auto& Input : System->getValues()) @@ -78,82 +78,19 @@ namespace FPLXModelRegistry_helpers } } -FPLXModelRegistry::Handle FPLXModelRegistry::Register( - const FString& PLXFile, const FString& Prefix, FAssemblyRef& Assembly, - FSimulationBarrier& Simulation) +FPLXModelRegistry::Handle FPLXModelRegistry::Register(const FString& PLXFile) { check(HasNative()); - check(Simulation.HasNative()); - - if (Prefix.IsEmpty()) - { - UE_LOG( - LogAGX, Warning, - TEXT("FPLXModelRegistry::Register got an empty UniqueModelInstancePrefix when " - "registering OpenPLX file '%s'. The OpenPLX model instance will not be " - "registered."), - *PLXFile); - return InvalidHandle; - } Handle Handle = GetFrom(PLXFile); - - // Check that we got a unique Model Instance Prefix. - if (Handle != InvalidHandle) - { - const size_t Index = FPLXModelRegistry_helpers::Convert(Handle); - if (Native->ModelData[Index].OutputSignalListeners.contains(Convert(Prefix))) - { - UE_LOG( - LogAGX, Warning, - TEXT("FPLXModelRegistry::Register got UniqueModelInstancePrefix '%s' that has " - "already been used when registering an instance using OpenPLX file '%s'. This " - "registration will fail and the behaviour of this instance may not work as " - "expected."), - *Prefix, *PLXFile); - return InvalidHandle; - } - } - if (Handle == InvalidHandle) // We have never seen this PLX Model before. - Handle = PrepareNewModel(PLXFile); - - if (Handle == InvalidHandle) - return InvalidHandle; // Something went wrong. - - // At this point, we know there exists valid PLXModelData for this PLXModel. - // We need to add the given Assembly to the base Assembly and (re)-create the - // InputSignalListener which is shared by all instances of this PLX Model. - - FPLXModelDatum* ModelDatum = GetModelDatum(Handle); - if (ModelDatum == nullptr) - return InvalidHandle; - - // Add the given assebly as a sub-assembly to the existing root assembly. - // This way, the InputSignalListener will correctly find all relevant AGX objects. - ModelDatum->Assembly->add(Assembly.Native); - - if (ModelDatum->InputSignalListener != nullptr) // Has been added before. - Simulation.GetNative()->Native->remove(ModelDatum->InputSignalListener); - - // The InputSignalListener always needs to be re-created even if it existed before, since now - // we have added another sub-assembly containing agx-objects it needs to know about. -// ModelDatum->InputSignalListener = new agxopenplx::InputSignalListener(ModelDatum->Assembly); // TODO!!!! - // Simulation.GetNative()->Native->add(ModelDatum->InputSignalListener); // TODO!!!! - - // Finally add an OutputSignalListener that is used for this PLX model instance only, producing - // output signals. It has a unique name prefix that is used to keep signals from getting name - // collisions when having mutliple instances of the same PLX model in the world. - //agx::ref_ptr OutputSignalListener = // TODO!!!! - // new agxopenplx::OutputSignalListener(Assembly.Native, ModelDatum->PLXModel); // TODO!!!! - //ModelDatum->OutputSignalListeners.insert({Convert(Prefix), OutputSignalListener}); // TODO!!!! - //AGX_CHECK(!Simulation.GetNative()->Native->remove(OutputSignalListener)); // TODO!!!! - //Simulation.GetNative()->Native->add(OutputSignalListener); // TODO!!!! + Handle = LoadNewModel(PLXFile); return Handle; } -const FPLXModelDatum* FPLXModelRegistry::GetModelDatum(Handle Handle) const +template +T* FPLXModelRegistry::GetModelDatumImpl(Handle Handle) const { check(HasNative()); if (Handle == InvalidHandle) @@ -166,17 +103,14 @@ const FPLXModelDatum* FPLXModelRegistry::GetModelDatum(Handle Handle) const return &Native->ModelData[Index]; } -FPLXModelDatum* FPLXModelRegistry::GetModelDatum(Handle Handle) +const FPLXModelData* FPLXModelRegistry::GetModelDatum(Handle Handle) const { - check(HasNative()); - if (Handle == InvalidHandle) - return nullptr; - - const size_t Index = FPLXModelRegistry_helpers::Convert(Handle); - if (Index >= Native->ModelData.size()) - return nullptr; + return GetModelDatumImpl(Handle); +} - return &Native->ModelData[Index]; +FPLXModelData* FPLXModelRegistry::GetModelDatum(Handle Handle) +{ + return GetModelDatumImpl(Handle); } FPLXModelRegistry::Handle FPLXModelRegistry::GetFrom(const FString& PLXFile) const @@ -185,13 +119,13 @@ FPLXModelRegistry::Handle FPLXModelRegistry::GetFrom(const FString& PLXFile) con return It != KnownModels.end() ? It->second : InvalidHandle; } -FPLXModelRegistry::Handle FPLXModelRegistry::PrepareNewModel(const FString& PLXFile) +FPLXModelRegistry::Handle FPLXModelRegistry::LoadNewModel(const FString& PLXFile) { // Here we create a new slot in the PLXModelData array with the PLX model tree as well - // as some other required objects line an agxSDK::Assembly that the InputSignalListener needs. + // as some other required objects like the AGX Cache. AGX_CHECK(GetFrom(PLXFile) == InvalidHandle); - FPLXModelDatum NewModel; + FPLXModelData NewModel; NewModel.PLXModel = FPLXUtilities::LoadModel(PLXFile, NewModel.AGXCache); if (NewModel.PLXModel == nullptr) { @@ -204,18 +138,20 @@ FPLXModelRegistry::Handle FPLXModelRegistry::PrepareNewModel(const FString& PLXF } auto System = std::dynamic_pointer_cast(NewModel.PLXModel); - FPLXModelRegistry_helpers::MapAllInputs(System.get(), NewModel.Inputs); - - // OpenPLX's InputSignalListener expects an Assembly with a PowerLine always. - // Therefore we add one here, even though it's a bit of a hack. - NewModel.Assembly = new agxSDK::Assembly(); - agxPowerLine::PowerLineRef RequiredDummyPowerLine = new agxPowerLine::PowerLine(); - RequiredDummyPowerLine->setName(agx::Name("OpenPlxPowerLine")); - NewModel.Assembly->add(RequiredDummyPowerLine); + if (System == nullptr) + { + UE_LOG( + LogAGX, Error, + TEXT("Could not get OpenPLX system from file '%s'. The OpenPLX model will not be loaded."), + *PLXFile); + return InvalidHandle; + } - const Handle NewHande = FPLXModelRegistry_helpers::Convert(Native->ModelData.size()); + FPLXModelRegistry_helpers::MapInputs(System.get(), NewModel.Inputs); + + const Handle NewHandle = FPLXModelRegistry_helpers::Convert(Native->ModelData.size()); Native->ModelData.emplace_back(std::move(NewModel)); - KnownModels.insert({Convert(PLXFile), NewHande}); + KnownModels.insert({Convert(PLXFile), NewHandle}); - return NewHande; + return NewHandle; } diff --git a/Source/AGXUnrealBarrier/Private/OpenPLX/PLXSignalHandler.cpp b/Source/AGXUnrealBarrier/Private/OpenPLX/PLXSignalHandler.cpp index ae02450ba..f53b12680 100644 --- a/Source/AGXUnrealBarrier/Private/OpenPLX/PLXSignalHandler.cpp +++ b/Source/AGXUnrealBarrier/Private/OpenPLX/PLXSignalHandler.cpp @@ -18,36 +18,80 @@ #include "OpenPLX/agx-openplx/SignalListenerUtils.h" #include "OpenPLX/Physics/Signals/RealInputSignal.h" +FPLXSignalHandler::FPLXSignalHandler() +{ +} + void FPLXSignalHandler::Init( - const FString& PLXFile, const FString& UniqueModelInstancePrefix, - FSimulationBarrier& Simulation, FPLXModelRegistry& InModelInfo, + const FString& PLXFile, FSimulationBarrier& Simulation, FPLXModelRegistry& InModelRegistry, TArray& Constraints) { check(Simulation.HasNative()); - check(InModelInfo.HasNative()); + check(InModelRegistry.HasNative()); - agxSDK::AssemblyRef Assembly = new agxSDK::Assembly(); + AssemblyRef = std::make_shared(new agxSDK::Assembly()); for (FConstraintBarrier* Constraint : Constraints) { AGX_CHECK(Constraint->HasNative()); - Assembly->add(Constraint->GetNative()->Native); + AssemblyRef->Native->add(Constraint->GetNative()->Native); } // OpenPLX OutputSignalListener requires the assembly to contain a PowerLine with a - // cetain name. Remove once this has been cleaned up in OpenPLX, it's a bit hacky. + // certain name. Remove once this has been cleaned up in OpenPLX, it's a bit hacky. agxPowerLine::PowerLineRef RequiredDummyPowerLine = new agxPowerLine::PowerLine(); RequiredDummyPowerLine->setName(agx::Name("OpenPlxPowerLine")); - Assembly->add(RequiredDummyPowerLine); + AssemblyRef->Native->add(RequiredDummyPowerLine); - FAssemblyRef AssemblyRef(Assembly); - ModelInfo = &InModelInfo; - ModelHandle = ModelInfo->Register(PLXFile, UniqueModelInstancePrefix, AssemblyRef, Simulation); + ModelRegistry = &InModelRegistry; + ModelHandle = ModelRegistry->Register(PLXFile); if (ModelHandle == FPLXModelRegistry::InvalidHandle) { - // Todo: log error + UE_LOG( + LogAGX, Warning, + TEXT( + "Could not load OpenPLX model '%s'. The Console Log may contain more information."), + *PLXFile); + return; + } + + FPLXModelData* ModelData = ModelRegistry->GetModelDatum(ModelHandle); + if (ModelData == nullptr) + { + UE_LOG( + LogAGX, Error, + TEXT("Unexpected error: Unable to get registered OpenPLX model '%s'. The OpenPLX model " + "may not behave as intended."), + *PLXFile); return; } + auto System = std::dynamic_pointer_cast(ModelData->PLXModel); + if (System == nullptr) + { + UE_LOG( + LogAGX, Warning, + TEXT("Unable to get a openplx::Physics3D::System from the registered OpenPLX model " + "'%s'. The OpenPLX model may not behave as intended."), + *PLXFile); + return; + } + + if (FPLXUtilities::HasInputs(System.get())) + { + InputQueueRef = + std::make_shared(agxopenplx::InputSignalQueue::create()); + InputSignalHandlerRef = + std::make_shared(AssemblyRef->Native, InputQueueRef->Native); + } + + if (FPLXUtilities::HasOutputs(System.get())) + { + OutputQueueRef = + std::make_shared(agxopenplx::OutputSignalQueue::create()); + OutputSignalHandlerRef = std::make_shared( + AssemblyRef->Native, ModelData->PLXModel, OutputQueueRef->Native); + } + bIsInitialized = true; } @@ -59,45 +103,60 @@ bool FPLXSignalHandler::IsInitialized() const bool FPLXSignalHandler::Send(const FPLX_LinearVelocity1DInput& Input, double Value) { check(IsInitialized()); - if (ModelInfo == nullptr || ModelHandle == FPLXModelRegistry::InvalidHandle) + if (ModelRegistry == nullptr || ModelHandle == FPLXModelRegistry::InvalidHandle) return false; - FPLXModelDatum* ModelDatum = ModelInfo->GetModelDatum(ModelHandle); - if (ModelDatum == nullptr) + if (InputQueueRef == nullptr || InputQueueRef->Native == nullptr) + { + UE_LOG( + LogAGX, Warning, + TEXT("Tried to send OpenPLX Linear Velocity 1D Input signal, value %f, but the OpenPLX " + "model does not have any registered inputs."), + Value); return false; + } - auto PLXInput = ModelDatum->Inputs.find(Convert(Input.Name)); - if (PLXInput == ModelDatum->Inputs.end()) + FPLXModelData* ModelData = ModelRegistry->GetModelDatum(ModelHandle); + if (ModelData == nullptr) + return false; + + auto PLXInput = ModelData->Inputs.find(Convert(Input.Name)); + if (PLXInput == ModelData->Inputs.end()) return false; auto Signal = openplx::Physics::Signals::RealInputSignal::create( ConvertDistanceToAGX(Value), PLXInput->second); - // Todo: prepend unique instance name prefix to signal once supported. - //agxopenplx::Signals::sendInputSignal(Signal); // // TODO!!!! + InputQueueRef->Native->send(Signal); return true; } bool FPLXSignalHandler::Receive(const FPLX_AngleOutput& Output, double& OutValue) { check(IsInitialized()); - if (ModelInfo == nullptr || ModelHandle == FPLXModelRegistry::InvalidHandle) + if (ModelRegistry == nullptr || ModelHandle == FPLXModelRegistry::InvalidHandle) + return false; + + if (OutputQueueRef == nullptr || OutputQueueRef->Native == nullptr) + { + UE_LOG( + LogAGX, Warning, + TEXT("Tried to receive OpenPLX Angle Output signal, but the OpenPLX " + "model does not have any registered outputs.")); return false; + } - // Todo: make this more efficient than looking through all outputs every time. - // We could build a map from name to signal in the ModelRegistry, each time step, for example. - // Do this once the signal refactoring in OpenPLX has been done. - /*auto ValueOutputSignal = // // TODO!!!! - agxopenplx::getSignalBySourceName( // TODO!!!! - agxopenplx::Signals::getOutputSignals(), Convert(Output.Name)); // TODO!!!! + auto ValueOutputSignal = + agxopenplx::getSignalBySourceName( + OutputQueueRef->Native->getSignals(), Convert(Output.Name)); if (ValueOutputSignal == nullptr) return false; auto Value = - std::dynamic_pointer_cast(ValueOutputSignal->value()); // TODO!!!! + std::dynamic_pointer_cast(ValueOutputSignal->value()); if (Value == nullptr) - return false; // TODO!!!! + return false; - OutValue = ConvertAngleToUnreal(Value->value());*/ // TODO!!!! + OutValue = ConvertAngleToUnreal(Value->value()); return true; } diff --git a/Source/AGXUnrealBarrier/Private/Utilities/PLXUtilities.cpp b/Source/AGXUnrealBarrier/Private/Utilities/PLXUtilities.cpp index 81397e6d8..eab0aa1b9 100644 --- a/Source/AGXUnrealBarrier/Private/Utilities/PLXUtilities.cpp +++ b/Source/AGXUnrealBarrier/Private/Utilities/PLXUtilities.cpp @@ -148,6 +148,40 @@ openplx::Core::ObjectPtr FPLXUtilities::LoadModel( return PLXUtilities_helpers::LoadModelFromFile(Convert(Filename), AGXCache); } +bool FPLXUtilities::HasInputs(openplx::Physics3D::System* System) +{ + if (System == nullptr) + return false; + + if (System->getValues().size() > 0) + return true; + + for (auto& Subsystem : System->getValues()) + { + if (HasInputs(Subsystem.get())) + return true; + } + + return false; +} + +bool FPLXUtilities::HasOutputs(openplx::Physics3D::System* System) +{ + if (System == nullptr) + return false; + + if (System->getValues().size() > 0) + return true; + + for (auto& Subsystem : System->getValues()) + { + if (HasOutputs(Subsystem.get())) + return true; + } + + return false; +} + TArray> FPLXUtilities::GetInputs(openplx::Physics3D::System* System) { TArray> Inputs; diff --git a/Source/AGXUnrealBarrier/Private/Utilities/PLXUtilities.h b/Source/AGXUnrealBarrier/Private/Utilities/PLXUtilities.h index f0abd9c61..82fbba930 100644 --- a/Source/AGXUnrealBarrier/Private/Utilities/PLXUtilities.h +++ b/Source/AGXUnrealBarrier/Private/Utilities/PLXUtilities.h @@ -25,6 +25,9 @@ class FPLXUtilities static openplx::Core::ObjectPtr LoadModel( const FString& Filename, std::shared_ptr AGXCache); + static bool HasInputs(openplx::Physics3D::System* System); + static bool HasOutputs(openplx::Physics3D::System* System); + static TArray> GetInputs(openplx::Physics3D::System* System); static TArray> GetOutputs(openplx::Physics3D::System* System); }; diff --git a/Source/AGXUnrealBarrier/Public/BarrierOnly/OpenPLX/OpenPLXRefs.h b/Source/AGXUnrealBarrier/Public/BarrierOnly/OpenPLX/OpenPLXRefs.h index 12d33c9a2..964429549 100644 --- a/Source/AGXUnrealBarrier/Public/BarrierOnly/OpenPLX/OpenPLXRefs.h +++ b/Source/AGXUnrealBarrier/Public/BarrierOnly/OpenPLX/OpenPLXRefs.h @@ -22,8 +22,9 @@ struct FInputSignalHandlerRef agx::ref_ptr Native; FInputSignalHandlerRef() = default; - FInputSignalHandlerRef(agxSDK::Assembly* Assembly) - // : Native(new agxopenplx::InputSignalListener(Assembly)) // TODO!!!! + FInputSignalHandlerRef( + agxSDK::Assembly* Assembly, std::shared_ptr InputQueue) + : Native(new agxopenplx::InputSignalListener(Assembly, InputQueue)) { } }; @@ -34,23 +35,41 @@ struct FOutputSignalHandlerRef FOutputSignalHandlerRef() = default; FOutputSignalHandlerRef( - agxSDK::Assembly* Assembly, const std::shared_ptr& PlxModel) - //: Native(new agxopenplx::OutputSignalListener(Assembly, PlxModel)) // TODO!!!! + agxSDK::Assembly* Assembly, const std::shared_ptr& PlxModel, + std::shared_ptr OutputQueue) + : Native(new agxopenplx::OutputSignalListener(Assembly, PlxModel, OutputQueue)) { } }; -struct FPLXModelDatum +struct FInputSignalQueueRef +{ + std::shared_ptr Native; + FInputSignalQueueRef() = default; + FInputSignalQueueRef(std::shared_ptr InNative) + : Native(InNative) + { + } +}; + +struct FOutputSignalQueueRef +{ + std::shared_ptr Native; + FOutputSignalQueueRef() = default; + FOutputSignalQueueRef(std::shared_ptr InNative) + : Native(InNative) + { + } +}; + +struct FPLXModelData { std::shared_ptr AGXCache; openplx::Core::ObjectPtr PLXModel; - agx::ref_ptr InputSignalListener; - std::unordered_map> OutputSignalListeners; - agxSDK::AssemblyRef Assembly; std::unordered_map> Inputs; }; -struct FPLXModelData +struct FPLXModelDataArray { - std::vector ModelData; + std::vector ModelData; }; diff --git a/Source/AGXUnrealBarrier/Public/OpenPLX/PLXModelRegistry.h b/Source/AGXUnrealBarrier/Public/OpenPLX/PLXModelRegistry.h index eb759a76b..921dc2d8e 100644 --- a/Source/AGXUnrealBarrier/Public/OpenPLX/PLXModelRegistry.h +++ b/Source/AGXUnrealBarrier/Public/OpenPLX/PLXModelRegistry.h @@ -11,7 +11,7 @@ class FSimulationBarrier; struct FAssemblyRef; struct FPLXModelData; -struct FPLXModelDatum; +struct FPLXModelDataArray; class AGXUNREALBARRIER_API FPLXModelRegistry { @@ -27,32 +27,30 @@ class AGXUNREALBARRIER_API FPLXModelRegistry /** * The PLXFile is the absolute path of a OpenPLX model to be loaded. - * The Assembly must contain the AGX objects relevant for the specific instance of this OpenPLX - * model (there can exist many). - * The Simulation is the Simulation object used by AGXUnreal during Play. - * - * The Handle returned can be used to later access the loaded OpenPLX model along with related - * data. This Handle will be shared by all who register the same PLX file, for example when the - * same PLX model is instanced many times in the same world. + * + * The Handle returned can be used to later access the loaded OpenPLX model. This Handle will be + * shared by all who register the same PLX file, for example when the same PLX model is + * instanced many times in the same Level. */ - Handle Register( - const FString& PLXFile, const FString& UniqueModelInstancePrefix, FAssemblyRef& Assembly, - FSimulationBarrier& Simulation); + Handle Register(const FString& PLXFile); /** - * Important note: the lifetime of the returned FPLXModelDatum is only guaranteed during direct - * usage in local scope. Do not store this pointer for later use. + * Important note: the lifetime of the returned FPLXModelData is only guaranteed during direct + * usage in local scope. It is not thread safe. Do not store this pointer for later use. */ - const FPLXModelDatum* GetModelDatum(Handle Handle) const; - FPLXModelDatum* GetModelDatum(Handle Handle); + const FPLXModelData* GetModelDatum(Handle Handle) const; + FPLXModelData* GetModelDatum(Handle Handle); private: FPLXModelRegistry(const FPLXModelRegistry&) = delete; void operator=(const FPLXModelRegistry&) = delete; + template + T* GetModelDatumImpl(Handle Handle) const; + Handle GetFrom(const FString& PLXFile) const; - Handle PrepareNewModel(const FString& PLXFile); + Handle LoadNewModel(const FString& PLXFile); - std::unique_ptr Native; + std::unique_ptr Native; std::unordered_map KnownModels; }; diff --git a/Source/AGXUnrealBarrier/Public/OpenPLX/PLXSignalHandler.h b/Source/AGXUnrealBarrier/Public/OpenPLX/PLXSignalHandler.h index 67a97883d..4b798dbea 100644 --- a/Source/AGXUnrealBarrier/Public/OpenPLX/PLXSignalHandler.h +++ b/Source/AGXUnrealBarrier/Public/OpenPLX/PLXSignalHandler.h @@ -11,17 +11,21 @@ class FConstraintBarrier; class FSimulationBarrier; +struct FAssemblyRef; +struct FInputSignalHandlerRef; +struct FOutputSignalHandlerRef; +struct FInputSignalQueueRef; +struct FOutputSignalQueueRef; struct FPLX_AngleOutput; struct FPLX_LinearVelocity1DInput; -struct FOutputSignalHandlerRef; class AGXUNREALBARRIER_API FPLXSignalHandler { public: + FPLXSignalHandler(); + void Init( - const FString& PLXFile, const FString& UniqueModelInstancePrefix, - FSimulationBarrier& Simulation, - FPLXModelRegistry& InModelInfo, + const FString& PLXFile, FSimulationBarrier& Simulation, FPLXModelRegistry& InModelRegistry, TArray& Constraints); bool IsInitialized() const; @@ -32,7 +36,12 @@ class AGXUNREALBARRIER_API FPLXSignalHandler private: bool bIsInitialized {false}; - FPLXModelRegistry* ModelInfo {nullptr}; - + FPLXModelRegistry* ModelRegistry {nullptr}; FPLXModelRegistry::Handle ModelHandle {FPLXModelRegistry::InvalidHandle}; + + std::shared_ptr AssemblyRef; + std::shared_ptr InputSignalHandlerRef; + std::shared_ptr OutputSignalHandlerRef; + std::shared_ptr InputQueueRef; + std::shared_ptr OutputQueueRef; }; From d4e2671950e6082356996d614773734f3d774367 Mon Sep 17 00:00:00 2001 From: Josef Date: Wed, 4 Dec 2024 14:48:02 +0100 Subject: [PATCH 31/31] Fixes regarding signal handling --- .../Private/OpenPLX/PLXModelRegistry.cpp | 36 +++---- .../Private/OpenPLX/PLXSignalHandler.cpp | 29 ++++-- .../Private/Utilities/PLXUtilities.cpp | 97 +++++-------------- .../Public/OpenPLX/PLXModelRegistry.h | 6 +- 4 files changed, 66 insertions(+), 102 deletions(-) diff --git a/Source/AGXUnrealBarrier/Private/OpenPLX/PLXModelRegistry.cpp b/Source/AGXUnrealBarrier/Private/OpenPLX/PLXModelRegistry.cpp index 2e0f8d74b..d0e11f191 100644 --- a/Source/AGXUnrealBarrier/Private/OpenPLX/PLXModelRegistry.cpp +++ b/Source/AGXUnrealBarrier/Private/OpenPLX/PLXModelRegistry.cpp @@ -54,27 +54,24 @@ namespace FPLXModelRegistry_helpers return static_cast(Val); } - void MapInputs( - openplx::Physics3D::System* System, - std::unordered_map>& - OutInputs) + using InputMap = std::unordered_map>; + + InputMap MapInputs(openplx::Physics3D::System* System) { + InputMap Inputs; if (System == nullptr) - return; + return Inputs; - for (auto& Subsystem : System->getValues()) - { - MapInputs(System, OutInputs); - } - - for (auto& Input : System->getValues()) + for (auto& Input : System->getNestedObjects()) { if (Input == nullptr) continue; - AGX_CHECK(!OutInputs.contains(Input->getName())); - OutInputs.insert({Input->getName(), Input}); + AGX_CHECK(!Inputs.contains(Input->getName())); + Inputs.insert({Input->getName(), Input}); } + + return Inputs; } } @@ -90,7 +87,7 @@ FPLXModelRegistry::Handle FPLXModelRegistry::Register(const FString& PLXFile) } template -T* FPLXModelRegistry::GetModelDatumImpl(Handle Handle) const +T* FPLXModelRegistry::GetModelDataImpl(Handle Handle) const { check(HasNative()); if (Handle == InvalidHandle) @@ -103,14 +100,14 @@ T* FPLXModelRegistry::GetModelDatumImpl(Handle Handle) const return &Native->ModelData[Index]; } -const FPLXModelData* FPLXModelRegistry::GetModelDatum(Handle Handle) const +const FPLXModelData* FPLXModelRegistry::GetModelData(Handle Handle) const { - return GetModelDatumImpl(Handle); + return GetModelDataImpl(Handle); } -FPLXModelData* FPLXModelRegistry::GetModelDatum(Handle Handle) +FPLXModelData* FPLXModelRegistry::GetModelData(Handle Handle) { - return GetModelDatumImpl(Handle); + return GetModelDataImpl(Handle); } FPLXModelRegistry::Handle FPLXModelRegistry::GetFrom(const FString& PLXFile) const @@ -147,8 +144,7 @@ FPLXModelRegistry::Handle FPLXModelRegistry::LoadNewModel(const FString& PLXFile return InvalidHandle; } - FPLXModelRegistry_helpers::MapInputs(System.get(), NewModel.Inputs); - + NewModel.Inputs = FPLXModelRegistry_helpers::MapInputs(System.get()); const Handle NewHandle = FPLXModelRegistry_helpers::Convert(Native->ModelData.size()); Native->ModelData.emplace_back(std::move(NewModel)); KnownModels.insert({Convert(PLXFile), NewHandle}); diff --git a/Source/AGXUnrealBarrier/Private/OpenPLX/PLXSignalHandler.cpp b/Source/AGXUnrealBarrier/Private/OpenPLX/PLXSignalHandler.cpp index f53b12680..7ae07af0a 100644 --- a/Source/AGXUnrealBarrier/Private/OpenPLX/PLXSignalHandler.cpp +++ b/Source/AGXUnrealBarrier/Private/OpenPLX/PLXSignalHandler.cpp @@ -54,7 +54,7 @@ void FPLXSignalHandler::Init( return; } - FPLXModelData* ModelData = ModelRegistry->GetModelDatum(ModelHandle); + FPLXModelData* ModelData = ModelRegistry->GetModelData(ModelHandle); if (ModelData == nullptr) { UE_LOG( @@ -82,6 +82,7 @@ void FPLXSignalHandler::Init( std::make_shared(agxopenplx::InputSignalQueue::create()); InputSignalHandlerRef = std::make_shared(AssemblyRef->Native, InputQueueRef->Native); + Simulation.GetNative()->Native->add(InputSignalHandlerRef->Native); } if (FPLXUtilities::HasOutputs(System.get())) @@ -90,6 +91,7 @@ void FPLXSignalHandler::Init( std::make_shared(agxopenplx::OutputSignalQueue::create()); OutputSignalHandlerRef = std::make_shared( AssemblyRef->Native, ModelData->PLXModel, OutputQueueRef->Native); + Simulation.GetNative()->Native->add(OutputSignalHandlerRef->Native); } bIsInitialized = true; @@ -116,13 +118,20 @@ bool FPLXSignalHandler::Send(const FPLX_LinearVelocity1DInput& Input, double Val return false; } - FPLXModelData* ModelData = ModelRegistry->GetModelDatum(ModelHandle); + FPLXModelData* ModelData = ModelRegistry->GetModelData(ModelHandle); if (ModelData == nullptr) return false; auto PLXInput = ModelData->Inputs.find(Convert(Input.Name)); if (PLXInput == ModelData->Inputs.end()) + { + UE_LOG( + LogAGX, Warning, + TEXT("Tried to send OpenPLX Linear Velocity 1D Input signal, value %f, but the " + "corresponding OpenPLX input '%s' was not found. The signal will not be sent."), + Value, *Input.Name); return false; + } auto Signal = openplx::Physics::Signals::RealInputSignal::create( ConvertDistanceToAGX(Value), PLXInput->second); @@ -141,8 +150,9 @@ bool FPLXSignalHandler::Receive(const FPLX_AngleOutput& Output, double& OutValue { UE_LOG( LogAGX, Warning, - TEXT("Tried to receive OpenPLX Angle Output signal, but the OpenPLX " - "model does not have any registered outputs.")); + TEXT("Tried to receive OpenPLX Angle Output signal for output '%s', but the OpenPLX " + "model does not have any registered outputs."), + *Output.Name); return false; } @@ -152,10 +162,17 @@ bool FPLXSignalHandler::Receive(const FPLX_AngleOutput& Output, double& OutValue if (ValueOutputSignal == nullptr) return false; - auto Value = - std::dynamic_pointer_cast(ValueOutputSignal->value()); + auto Value = std::dynamic_pointer_cast( + ValueOutputSignal->value()); if (Value == nullptr) + { + UE_LOG( + LogAGX, Error, + TEXT("Unexpected error: Tried to cast OpenPLX Angle Output signal for output '%s', but " + "got nullptr. Possible type miss match. The signal will not be received."), + *Output.Name); return false; + } OutValue = ConvertAngleToUnreal(Value->value()); return true; diff --git a/Source/AGXUnrealBarrier/Private/Utilities/PLXUtilities.cpp b/Source/AGXUnrealBarrier/Private/Utilities/PLXUtilities.cpp index eab0aa1b9..8ed9a29af 100644 --- a/Source/AGXUnrealBarrier/Private/Utilities/PLXUtilities.cpp +++ b/Source/AGXUnrealBarrier/Private/Utilities/PLXUtilities.cpp @@ -24,7 +24,6 @@ #include "OpenPLX/Visuals/Visuals_all.h" #include "OpenPLX/Urdf/Urdf_all.h" - // Unreal Engine includes. #include "Templates/UniquePtr.h" @@ -33,54 +32,6 @@ namespace PLXUtilities_helpers { - void GetInputs(openplx::Physics3D::System* System, TArray>& OutInputs) - { - if (System == nullptr) - return; - - for (auto& Subsystem : System->getValues()) - { - GetInputs(System, OutInputs); - } - - for (auto& Input : System->getValues()) - { - if (auto Lvmvi = std::dynamic_pointer_cast( - Input)) - { - OutInputs.Emplace(MakeUnique( - Convert(Input->getName()))); - continue; - } - - UE_LOG(LogAGX, Warning, TEXT("Unhandled PLX Input: %s"), *Convert(Input->getName())); - } - } - - void GetOutputs(openplx::Physics3D::System* System, TArray>& OutOutputs) - { - if (System == nullptr) - return; - - for (auto& Subsystem : System->getValues()) - { - GetOutputs(System, OutOutputs); - } - - for (auto& Output : System->getValues()) - { - if (auto Hao = - std::dynamic_pointer_cast(Output)) - { - OutOutputs.Emplace( - MakeUnique(Convert(Output->getName()))); - continue; - } - - UE_LOG(LogAGX, Warning, TEXT("Unhandled PLX Output: %s"), *Convert(Output->getName())); - } - } - std::shared_ptr CreatePLXContext( std::shared_ptr AGXCache) { @@ -139,8 +90,7 @@ openplx::Core::ObjectPtr FPLXUtilities::LoadModel( if (!FPaths::FileExists(Filename)) { UE_LOG( - LogAGX, Warning, - TEXT("Could not read OpenPLX file '%s'. The file does not exist."), + LogAGX, Warning, TEXT("Could not read OpenPLX file '%s'. The file does not exist."), *Filename); return nullptr; } @@ -153,16 +103,7 @@ bool FPLXUtilities::HasInputs(openplx::Physics3D::System* System) if (System == nullptr) return false; - if (System->getValues().size() > 0) - return true; - - for (auto& Subsystem : System->getValues()) - { - if (HasInputs(Subsystem.get())) - return true; - } - - return false; + return System->getNestedObjects().size() > 0; } bool FPLXUtilities::HasOutputs(openplx::Physics3D::System* System) @@ -170,16 +111,7 @@ bool FPLXUtilities::HasOutputs(openplx::Physics3D::System* System) if (System == nullptr) return false; - if (System->getValues().size() > 0) - return true; - - for (auto& Subsystem : System->getValues()) - { - if (HasOutputs(Subsystem.get())) - return true; - } - - return false; + return System->getNestedObjects().size() > 0; } TArray> FPLXUtilities::GetInputs(openplx::Physics3D::System* System) @@ -188,7 +120,17 @@ TArray> FPLXUtilities::GetInputs(openplx::Physics3D::Syst if (System == nullptr) return Inputs; - PLXUtilities_helpers::GetInputs(System, Inputs); + for (auto& Input : System->getNestedObjects()) + { + if (auto Lvmvi = + std::dynamic_pointer_cast(Input)) + { + Inputs.Emplace(MakeUnique(Convert(Input->getName()))); + continue; + } + + UE_LOG(LogAGX, Warning, TEXT("Unhandled PLX Input: %s"), *Convert(Input->getName())); + } return Inputs; } @@ -198,6 +140,15 @@ TArray> FPLXUtilities::GetOutputs(openplx::Physics3D::Sy if (System == nullptr) return Outputs; - PLXUtilities_helpers::GetOutputs(System, Outputs); + for (auto& Output : System->getNestedObjects()) + { + if (auto Hao = std::dynamic_pointer_cast(Output)) + { + Outputs.Emplace(MakeUnique(Convert(Output->getName()))); + continue; + } + + UE_LOG(LogAGX, Warning, TEXT("Unhandled PLX Output: %s"), *Convert(Output->getName())); + } return Outputs; } diff --git a/Source/AGXUnrealBarrier/Public/OpenPLX/PLXModelRegistry.h b/Source/AGXUnrealBarrier/Public/OpenPLX/PLXModelRegistry.h index 921dc2d8e..28c1ac818 100644 --- a/Source/AGXUnrealBarrier/Public/OpenPLX/PLXModelRegistry.h +++ b/Source/AGXUnrealBarrier/Public/OpenPLX/PLXModelRegistry.h @@ -38,15 +38,15 @@ class AGXUNREALBARRIER_API FPLXModelRegistry * Important note: the lifetime of the returned FPLXModelData is only guaranteed during direct * usage in local scope. It is not thread safe. Do not store this pointer for later use. */ - const FPLXModelData* GetModelDatum(Handle Handle) const; - FPLXModelData* GetModelDatum(Handle Handle); + const FPLXModelData* GetModelData(Handle Handle) const; + FPLXModelData* GetModelData(Handle Handle); private: FPLXModelRegistry(const FPLXModelRegistry&) = delete; void operator=(const FPLXModelRegistry&) = delete; template - T* GetModelDatumImpl(Handle Handle) const; + T* GetModelDataImpl(Handle Handle) const; Handle GetFrom(const FString& PLXFile) const; Handle LoadNewModel(const FString& PLXFile);