diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4a70e721..ed31bb47 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -28,17 +28,16 @@ jobs: - name: Configure shell: pwsh run: | - cmake -S . -B build ` - -A x64 ` + cmake --preset windows-x64 ` -DVCPKG_TARGET_TRIPLET=x64-windows ` -DCMAKE_TOOLCHAIN_FILE="${{ github.workspace }}/vcpkg/scripts/buildsystems/vcpkg.cmake" - name: Build shell: pwsh run: | - cmake --build build --config Release --verbose + cmake --build --preset windows-x64-release --verbose - name: Run tests shell: pwsh run: | - ctest --test-dir build --output-on-failure + ctest --preset windows-x64-release --output-on-failure diff --git a/.gitignore b/.gitignore index a58b3aa9..6cdcb229 100644 --- a/.gitignore +++ b/.gitignore @@ -476,3 +476,6 @@ vcpkg_downloads/ !CMakeLists.txt !**/imgui.ini +/build-ninja +/CMakeFiles +/_deps diff --git a/CMakeLists.txt b/CMakeLists.txt index a41f29ab..f3be3664 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,21 +1,15 @@ # CMakeLists.txt for DSFE project (root CMakeLists.txt) -cmake_minimum_required(VERSION 3.10) - -if (POLICY CMP0141) - cmake_policy(SET CMP0141 NEW) - set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT "$,$>,$<$:EditAndContinue>,$<$:ProgramDatabase>>") -endif() - +cmake_minimum_required(VERSION 3.17) project(DSFE LANGUAGES C CXX) set(CMAKE_CXX_STANDARD 23) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>DLL") -# Set common output directories for all targets -set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) -set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) -set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) +set(OUTPUT_BASE ${CMAKE_BINARY_DIR}) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${OUTPUT_BASE}/bin) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${OUTPUT_BASE}/lib) +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${OUTPUT_BASE}/lib) include(FetchContent) @@ -27,32 +21,6 @@ FetchContent_Declare( ) FetchContent_MakeAvailable(eigen) -FetchContent_Declare( - glfw - GIT_REPOSITORY https://github.com/glfw/glfw.git - GIT_TAG 3.4 -) -FetchContent_MakeAvailable(glfw) - -# Fetch GLM from GitHub -FetchContent_Declare( - glm - GIT_REPOSITORY https://github.com/g-truc/glm.git - GIT_TAG 1.0.3 -) -FetchContent_MakeAvailable(glm) - -FetchContent_Declare( - assimp - GIT_REPOSITORY https://github.com/assimp/assimp.git - GIT_TAG v6.0.4 -) - -set(ASSIMP_BUILD_TESTS OFF CACHE BOOL "" FORCE) -set(ASSIMP_BUILD_ASSIMP_TOOLS OFF CACHE BOOL "" FORCE) - -FetchContent_MakeAvailable(assimp) - FetchContent_Declare( googletest GIT_REPOSITORY https://github.com/google/googletest.git @@ -62,22 +30,6 @@ FetchContent_Declare( set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) FetchContent_MakeAvailable(googletest) -FetchContent_Declare( - imgui - GIT_REPOSITORY https://github.com/ocornut/imgui.git - GIT_TAG docking - GIT_SHALLOW TRUE -) -FetchContent_MakeAvailable(imgui) - -FetchContent_Declare( - implot - GIT_REPOSITORY https://github.com/epezent/implot.git - GIT_TAG v0.17 - GIT_SHALLOW TRUE -) -FetchContent_MakeAvailable(implot) - FetchContent_Declare( nlohmann_json GIT_REPOSITORY https://github.com/nlohmann/json.git diff --git a/CMakePresets.json b/CMakePresets.json index 6c6c0187..e97807c3 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -1,81 +1,145 @@ { - "version": 3, + "version": 8, "configurePresets": [ { - "name": "windows-base", + "name": "base", "hidden": true, - "generator": "Ninja", "binaryDir": "${sourceDir}/out/build/${presetName}", - "installDir": "${sourceDir}/out/install/${presetName}", "cacheVariables": { - "CMAKE_C_COMPILER": "cl.exe", - "CMAKE_CXX_COMPILER": "cl.exe" - }, + "CMAKE_EXPORT_COMPILE_COMMANDS": "ON", + "DSFE_ENABLE_GUI": "OFF" + } + }, + { + "name": "base-gui", + "hidden": true, + "binaryDir": "${sourceDir}/out/build/${presetName}", + "cacheVariables": { + "CMAKE_EXPORT_COMPILE_COMMANDS": "ON", + "DSFE_ENABLE_GUI": "ON" + } + }, + { + "name": "windows-x64", + "displayName": "Windows x64", + "inherits": "base", "condition": { "type": "equals", "lhs": "${hostSystemName}", "rhs": "Windows" - } - }, - { - "name": "x64-debug", - "displayName": "x64 Debug", - "inherits": "windows-base", + }, + "generator": "Visual Studio 18 2026", "architecture": { "value": "x64", "strategy": "external" - }, - "cacheVariables": { - "CMAKE_BUILD_TYPE": "Debug" } }, { - "name": "x64-release", - "displayName": "x64 Release", - "inherits": "x64-debug", + "name": "windows-x64-gui", + "displayName": "Windows x64 (GUI)", + "inherits": "base-gui", + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Windows" + }, + "generator": "Visual Studio 18 2026", "cacheVariables": { - "CMAKE_BUILD_TYPE": "Release" - } - }, - { - "name": "x86-debug", - "displayName": "x86 Debug", - "inherits": "windows-base", + "CMAKE_PREFIX_PATH": "C:/Qt/6.11.1/msvc2022_64" + }, "architecture": { - "value": "x86", + "value": "x64", "strategy": "external" - }, - "cacheVariables": { - "CMAKE_BUILD_TYPE": "Debug" } }, { - "name": "x86-release", - "displayName": "x86 Release", - "inherits": "x86-debug", + "name": "linux-x64-debug", + "displayName": "Linux x64 Debug", + "inherits": "base", + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Linux" + }, + "generator": "Ninja", "cacheVariables": { - "CMAKE_BUILD_TYPE": "Release" + "CMAKE_BUILD_TYPE": "Debug", + "CMAKE_PREFIX_PATH": "/usr/lib/x86_64-linux-gnu/cmake", + "HDF5_DIR": "", + "CMAKE_IGNORE_PATH": "/mnt/c/Program Files/HDF_Group/HDF5/2.0.0" + }, + "architecture": { + "value": "x64", + "strategy": "external" } }, { - "name": "linux-debug", - "displayName": "Linux Debug", - "generator": "Ninja", - "binaryDir": "${sourceDir}/out/build/${presetName}", - "installDir": "${sourceDir}/out/install/${presetName}", - "cacheVariables": { - "CMAKE_BUILD_TYPE": "Debug" - }, + "name": "linux-x64-release", + "displayName": "Linux x64 Release", + "inherits": "base", "condition": { "type": "equals", "lhs": "${hostSystemName}", "rhs": "Linux" }, - "vendor": { - "microsoft.com/VisualStudioRemoteSettings/CMake/1.0": { - "sourceDir": "$env{HOME}/.vs/$ms{projectDirName}" - } + "generator": "Ninja", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release", + "CMAKE_PREFIX_PATH": "/usr/lib/x86_64-linux-gnu/cmake", + "HDF5_DIR": "", + "CMAKE_IGNORE_PATH": "/mnt/c/Program Files/HDF_Group/HDF5/2.0.0" + }, + "architecture": { + "value": "x64", + "strategy": "external" } } + ], + "buildPresets": [ + { + "name": "windows-x64-debug", + "configurePreset": "windows-x64", + "configuration": "Debug" + }, + { + "name": "windows-x64-release", + "configurePreset": "windows-x64", + "configuration": "Release" + }, + { + "name": "windows-x64-gui-debug", + "configurePreset": "windows-x64-gui", + "configuration": "Debug" + }, + { + "name": "windows-x64-gui-release", + "configurePreset": "windows-x64-gui", + "configuration": "Release" + }, + { + "name": "linux-x64-debug", + "configurePreset": "linux-x64-debug" + }, + { + "name": "linux-x64-release", + "configurePreset": "linux-x64-release" + } + ], + "testPresets": [ + { + "name": "windows-x64-release", + "configurePreset": "windows-x64", + "output": { "outputOnFailure": true } + }, + { + "name": "windows-x64-gui-release", + "configurePreset": "windows-x64-gui", + "output": { "outputOnFailure": true } + }, + { + "name": "linux-x64-release", + "configurePreset": "linux-x64-release", + "output": { "outputOnFailure": true } + } ] } diff --git a/DSFE_App/CMakeLists.txt b/DSFE_App/CMakeLists.txt index 1fd34a64..d7dcadb1 100644 --- a/DSFE_App/CMakeLists.txt +++ b/DSFE_App/CMakeLists.txt @@ -1,8 +1,7 @@ -# CMakeLists.txt for the DSFE_App project -cmake_minimum_required(VERSION 3.10) -project(DSFE_App LANGUAGES C CXX) - -# Add projects within the DSFE_App directory +# CMakeLists.txt for the DSFE_App add_subdirectory(DSFE_Core) -add_subdirectory(DSFE_GUI) -add_subdirectory(DSFE_Engine) +option(DSFE_ENABLE_GUI "Enable GUI module" ON) +if(DSFE_ENABLE_GUI) + add_subdirectory(DSFE_GUI) +endif() +add_subdirectory(DSFE_Engine) \ No newline at end of file diff --git a/DSFE_App/DSFE_Core/CMakeLists.txt b/DSFE_App/DSFE_Core/CMakeLists.txt index a12a6709..c9764056 100644 --- a/DSFE_App/DSFE_Core/CMakeLists.txt +++ b/DSFE_App/DSFE_Core/CMakeLists.txt @@ -1,29 +1,59 @@ # CMakeLists.txt for DSFE_Core library -cmake_minimum_required(VERSION 3.10) +cmake_minimum_required(VERSION 3.17) project(DSFE_Core LANGUAGES C CXX) add_library(DSFE_Core SHARED) find_package(Threads REQUIRED) -find_package(HDF5 CONFIG REQUIRED) +find_package(HDF5 REQUIRED) -target_compile_definitions(DSFE_Core PRIVATE DSFE_CORE_EXPORTS) +target_compile_features(DSFE_Core PUBLIC cxx_std_23) if (MSVC) -target_compile_options(DSFE_Core PRIVATE /bigobj) + target_compile_options(DSFE_Core PRIVATE /bigobj) +else() + target_compile_options(DSFE_Core PRIVATE -Wall -Wextra -Wpedantic) endif() +target_compile_definitions(DSFE_Core PRIVATE DSFE_CORE_EXPORTS) -set(CORE_SRC - src/EngineCore.cpp +target_compile_definitions(DSFE_Core PUBLIC + $<$:DSFE_PLATFORM_WINDOWS> + $<$:DSFE_PLATFORM_LINUX> +) +if (NOT MSVC) + set_target_properties(DSFE_Core PROPERTIES + CXX_VISIBILITY_PRESET hidden + VISIBILITY_INLINES_HIDDEN YES + ) +endif() + +set(DEP_CORE_SRC + src/EngineCore.cpp src/Numerics/IntegrationService.cpp +) + +set(SINGLE_BODY_SYS_SRC + src/SingleBodySystem/Body.cpp +) +set(MULTI_BODY_SYS_SRC src/Robots/RobotDynamics.cpp src/Robots/RobotKinematics.cpp src/Robots/RobotLoader.cpp src/Robots/RobotSystem.cpp src/Robots/TrajectoryManager.cpp src/Robots/RobotSimSnapshot.cpp +) +set(PLATFORM_SRC + src/Platform/DataManager.cpp + src/Platform/Logger.cpp + src/Platform/Paths.cpp + src/Platform/StudyRunner.cpp + src/Analysis/Telemetry.cpp +) + +set(DSL_SRC src/Interpreter/Command.cpp src/Interpreter/CommandContext.cpp src/Interpreter/CommandFactory.cpp @@ -50,22 +80,16 @@ set(CORE_SRC src/Interpreter/Commands/WaitCmd.cpp ) -set(PLATFORM_SRC - src/Platform/DataManager.cpp - src/Platform/Logger.cpp - src/Platform/Paths.cpp - src/Platform/StudyRunner.cpp - - src/Analysis/Telemetry.cpp -) - -set(SCENE_SRC src/Scene/SimulationCore.cpp) +set(SIM_CORE_SRC src/Scene/SimulationCore.cpp) # Add sources for DSFE_Core, including ImGui files target_sources(DSFE_Core PRIVATE - ${CORE_SRC} + ${DEP_CORE_SRC} + ${SINGLE_BODY_SYS_SRC} + ${MULTI_BODY_SYS_SRC} ${PLATFORM_SRC} - ${SCENE_SRC} + ${DSL_SRC} + ${SIM_CORE_SRC} ) # Add Windows-specific source files @@ -77,8 +101,6 @@ endif() target_include_directories(DSFE_Core PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/include - - ${HDF5_INCLUDE_DIRS} ) # Use precompiled headers @@ -86,13 +108,22 @@ target_precompile_headers(DSFE_Core PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/pch.h") # Link DSFE_Core against its dependencies target_link_libraries(DSFE_Core - PUBLIC - MathLib + PUBLIC Eigen3::Eigen - hdf5::hdf5-shared + MathLib + PhysLib Threads::Threads PRIVATE - assimp nlohmann_json::nlohmann_json ) + +if (TARGET hdf5::hdf5-shared) + target_link_libraries(DSFE_Core PRIVATE hdf5::hdf5-shared) + +elseif (TARGET HDF5::HDF5) + target_link_libraries(DSFE_Core PRIVATE HDF5::HDF5) + +else() + target_link_libraries(DSFE_Core PRIVATE ${HDF5_LIBRARIES}) +endif() diff --git a/DSFE_App/DSFE_Core/framework.h b/DSFE_App/DSFE_Core/framework.h index 44b94928..0c34e144 100644 --- a/DSFE_App/DSFE_Core/framework.h +++ b/DSFE_App/DSFE_Core/framework.h @@ -1,7 +1,7 @@ +// DSFE_CORE framework.h #pragma once -// File: framework.h -// GitHub: SaltyJoss -#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers -// Windows Header Files -#define NOMINMAX -#include +#ifdef _WIN32 + #define WIN32_LEAN_AND_MEAN + #define NOMINMAX + #include +#endif diff --git a/DSFE_App/DSFE_Core/include/EngineCore.h b/DSFE_App/DSFE_Core/include/EngineCore.h index 39c5847a..5ff21c4b 100644 --- a/DSFE_App/DSFE_Core/include/EngineCore.h +++ b/DSFE_App/DSFE_Core/include/EngineCore.h @@ -3,13 +3,15 @@ // Conditional compilation for cross-platform symbol export/import #ifdef _WIN32 -#ifdef DSFE_CORE_EXPORTS -#define DSFE_API __declspec(dllexport) + #ifdef DSFE_CORE_EXPORTS + #define DSFE_API __declspec(dllexport) + #else + #define DSFE_API __declspec(dllimport) + #endif +#elif defined(__GNUC__) || defined(__clang__) + #define DSFE_API __attribute__((visibility("default"))) #else -#define DSFE_API __declspec(dllimport) -#endif -#else -#define DSFE_API + #define DSFE_API #endif // Forward declarations for core components diff --git a/DSFE_App/DSFE_Core/include/Interpreter/Command.h b/DSFE_App/DSFE_Core/include/Interpreter/Command.h index dbf78b89..6b223879 100644 --- a/DSFE_App/DSFE_Core/include/Interpreter/Command.h +++ b/DSFE_App/DSFE_Core/include/Interpreter/Command.h @@ -1,9 +1,6 @@ // DSFE_Core Command.h #pragma once -#pragma warning(disable : 4100) - #include "EngineCore.h" - #include "ICommand.h" #include "MainContext.h" diff --git a/DSFE_App/DSFE_Core/include/Interpreter/Commands/LoadCmd.h b/DSFE_App/DSFE_Core/include/Interpreter/Commands/LoadCmd.h index 5ae247a3..cb4af02f 100644 --- a/DSFE_App/DSFE_Core/include/Interpreter/Commands/LoadCmd.h +++ b/DSFE_App/DSFE_Core/include/Interpreter/Commands/LoadCmd.h @@ -3,7 +3,7 @@ #include "EngineCore.h" -#include + #include #include "Interpreter/Command.h" diff --git a/DSFE_App/DSFE_Core/include/Interpreter/Commands/RotateByCmd.h b/DSFE_App/DSFE_Core/include/Interpreter/Commands/RotateByCmd.h index 693c938c..f335e4e1 100644 --- a/DSFE_App/DSFE_Core/include/Interpreter/Commands/RotateByCmd.h +++ b/DSFE_App/DSFE_Core/include/Interpreter/Commands/RotateByCmd.h @@ -4,7 +4,7 @@ #include "EngineCore.h" -#include + #include #include "Interpreter/Command.h" diff --git a/DSFE_App/DSFE_Core/include/Interpreter/Commands/RotateJointByCmd.h b/DSFE_App/DSFE_Core/include/Interpreter/Commands/RotateJointByCmd.h index 9a27afee..82377fb9 100644 --- a/DSFE_App/DSFE_Core/include/Interpreter/Commands/RotateJointByCmd.h +++ b/DSFE_App/DSFE_Core/include/Interpreter/Commands/RotateJointByCmd.h @@ -3,7 +3,7 @@ #include "EngineCore.h" -#include + #include #include "Interpreter/Command.h" diff --git a/DSFE_App/DSFE_Core/include/Interpreter/Commands/RotateJointToCmd.h b/DSFE_App/DSFE_Core/include/Interpreter/Commands/RotateJointToCmd.h index 77b94e37..1801a83c 100644 --- a/DSFE_App/DSFE_Core/include/Interpreter/Commands/RotateJointToCmd.h +++ b/DSFE_App/DSFE_Core/include/Interpreter/Commands/RotateJointToCmd.h @@ -3,7 +3,7 @@ #include "EngineCore.h" -#include + #include #include "Interpreter/Command.h" diff --git a/DSFE_App/DSFE_Core/include/Interpreter/Commands/RotateToCmd.h b/DSFE_App/DSFE_Core/include/Interpreter/Commands/RotateToCmd.h index 3b6e8a70..c67295d6 100644 --- a/DSFE_App/DSFE_Core/include/Interpreter/Commands/RotateToCmd.h +++ b/DSFE_App/DSFE_Core/include/Interpreter/Commands/RotateToCmd.h @@ -3,7 +3,7 @@ #include "EngineCore.h" -#include + #include #include "Interpreter/Command.h" diff --git a/DSFE_App/DSFE_Core/include/Interpreter/Commands/SetCmd.h b/DSFE_App/DSFE_Core/include/Interpreter/Commands/SetCmd.h index 6a649b5e..f273026a 100644 --- a/DSFE_App/DSFE_Core/include/Interpreter/Commands/SetCmd.h +++ b/DSFE_App/DSFE_Core/include/Interpreter/Commands/SetCmd.h @@ -3,7 +3,7 @@ #include "EngineCore.h" -#include + #include #include "Interpreter/Command.h" diff --git a/DSFE_App/DSFE_Core/include/Interpreter/Commands/SetOmegaCmd.h b/DSFE_App/DSFE_Core/include/Interpreter/Commands/SetOmegaCmd.h index 0f8e352e..1b1bebe6 100644 --- a/DSFE_App/DSFE_Core/include/Interpreter/Commands/SetOmegaCmd.h +++ b/DSFE_App/DSFE_Core/include/Interpreter/Commands/SetOmegaCmd.h @@ -3,7 +3,7 @@ #include "EngineCore.h" -#include + #include #include "Interpreter/SimFwd.h" diff --git a/DSFE_App/DSFE_Core/include/Interpreter/Commands/SpinCmd.h b/DSFE_App/DSFE_Core/include/Interpreter/Commands/SpinCmd.h index 318e990c..39fd0c5d 100644 --- a/DSFE_App/DSFE_Core/include/Interpreter/Commands/SpinCmd.h +++ b/DSFE_App/DSFE_Core/include/Interpreter/Commands/SpinCmd.h @@ -3,7 +3,7 @@ #include "EngineCore.h" -#include + #include #include "Interpreter/Command.h" diff --git a/DSFE_App/DSFE_Core/include/Interpreter/Commands/TrajClearCmd.h b/DSFE_App/DSFE_Core/include/Interpreter/Commands/TrajClearCmd.h index 3f89ed4c..5c6ef41e 100644 --- a/DSFE_App/DSFE_Core/include/Interpreter/Commands/TrajClearCmd.h +++ b/DSFE_App/DSFE_Core/include/Interpreter/Commands/TrajClearCmd.h @@ -1,8 +1,6 @@ // DSFE_Core TrajClearCmd.h #pragma once - #include "EngineCore.h" - #include "Interpreter/SimFwd.h" #include "Interpreter/Command.h" #include "Interpreter/CommandContext.h" diff --git a/DSFE_App/DSFE_Core/include/Interpreter/Commands/TrajSetCmd.h b/DSFE_App/DSFE_Core/include/Interpreter/Commands/TrajSetCmd.h index 7091502f..f18f79e5 100644 --- a/DSFE_App/DSFE_Core/include/Interpreter/Commands/TrajSetCmd.h +++ b/DSFE_App/DSFE_Core/include/Interpreter/Commands/TrajSetCmd.h @@ -1,9 +1,6 @@ // DSFE_Core TrajSetCmd.h #pragma once -#pragma warning(disable : 4251) - #include "EngineCore.h" - #include "Interpreter/SimFwd.h" #include "Interpreter/Command.h" #include "Interpreter/CommandContext.h" diff --git a/DSFE_App/DSFE_Core/include/Interpreter/ProgramData.h b/DSFE_App/DSFE_Core/include/Interpreter/ProgramData.h index ea77cfec..5ac25d7d 100644 --- a/DSFE_App/DSFE_Core/include/Interpreter/ProgramData.h +++ b/DSFE_App/DSFE_Core/include/Interpreter/ProgramData.h @@ -1,13 +1,8 @@ // DSFE_Core ProgramData.h #pragma once -#pragma warning(disable : 4251) - #include "EngineCore.h" - -#include #include #include - #include #include diff --git a/DSFE_App/DSFE_Core/include/Interpreter/SimFwd.h b/DSFE_App/DSFE_Core/include/Interpreter/SimFwd.h index 6380da7e..fbc718fe 100644 --- a/DSFE_App/DSFE_Core/include/Interpreter/SimFwd.h +++ b/DSFE_App/DSFE_Core/include/Interpreter/SimFwd.h @@ -2,7 +2,7 @@ #pragma once #include "EngineCore.h" -#include + #include #include #include diff --git a/DSFE_App/DSFE_Core/include/Numerics/IntegrationMethods.h b/DSFE_App/DSFE_Core/include/Numerics/IntegrationMethods.h index 40322408..f7efa114 100644 --- a/DSFE_App/DSFE_Core/include/Numerics/IntegrationMethods.h +++ b/DSFE_App/DSFE_Core/include/Numerics/IntegrationMethods.h @@ -1,3 +1,4 @@ +// DSFE_Core IntegrationMethods.h #pragma once namespace integration { diff --git a/DSFE_App/DSFE_Core/include/Numerics/IntegrationService.h b/DSFE_App/DSFE_Core/include/Numerics/IntegrationService.h index 5854876e..979312e3 100644 --- a/DSFE_App/DSFE_Core/include/Numerics/IntegrationService.h +++ b/DSFE_App/DSFE_Core/include/Numerics/IntegrationService.h @@ -1,8 +1,5 @@ +// DSFE_Core IntegrationService.h #pragma once -// File: IntegrationService.h -// GitHub: SaltyJoss -#pragma warning(disable : 4251) - #include "EngineCore.h" #include #include diff --git a/DSFE_App/DSFE_Core/include/Numerics/IntegrationStep.inl b/DSFE_App/DSFE_Core/include/Numerics/IntegrationStep.inl index 7813691e..0090c76e 100644 --- a/DSFE_App/DSFE_Core/include/Numerics/IntegrationStep.inl +++ b/DSFE_App/DSFE_Core/include/Numerics/IntegrationStep.inl @@ -105,6 +105,11 @@ namespace integration { int substeps = 0; const int max_substeps = 500; // safety limit + if (h <= eps) { + LOG_WARN("Adaptive integration step size is too small (%e). Returning last computed state.", h); + return { x_curr, t_total, h }; + } + // Loop until we reach the target end time or exceed the maximum number of substeps while (t_curr < t_end && substeps < max_substeps) { double h_try = std::min(h, t_end - t_curr); diff --git a/DSFE_App/DSFE_Core/include/Platform/DataManager.h b/DSFE_App/DSFE_Core/include/Platform/DataManager.h index d15a04f0..b3b6f569 100644 --- a/DSFE_App/DSFE_Core/include/Platform/DataManager.h +++ b/DSFE_App/DSFE_Core/include/Platform/DataManager.h @@ -1,7 +1,5 @@ // DSFE_Core DataManager.h #pragma once - -#pragma warning(disable : 4251) #include "EngineCore.h" #include #include @@ -24,12 +22,7 @@ namespace robots { struct JointLogBuffer; } namespace data { // Variant type to hold different data types - using Value = std::variant< - std::nullptr_t, bool, int64_t, uint64_t, double, long, - std::vector, - std::vector, std::string - >; - + using Value = std::variant, std::vector, std::string>; // Field type representing a key-value pair using Field = std::pair; // List of fields diff --git a/DSFE_App/DSFE_Core/include/Platform/ISimulationCore.h b/DSFE_App/DSFE_Core/include/Platform/ISimulationCore.h index 27b7b6da..dec47dd0 100644 --- a/DSFE_App/DSFE_Core/include/Platform/ISimulationCore.h +++ b/DSFE_App/DSFE_Core/include/Platform/ISimulationCore.h @@ -9,6 +9,7 @@ // Forward Declarations namespace integration { enum class eIntegrationMethod; enum class eAutoDiffIntegrationMethod; } namespace robots { class RobotSystem; } +namespace single_body_system { class SingleBodySystem; } namespace control { class TrajectoryManager; } namespace diagnostics { class TelemetryRecorder; } namespace interpreter { class IStoredProgram; } @@ -47,7 +48,11 @@ namespace core { virtual void setRunTag(const std::string& tag) = 0; // Subsystems virtual robots::RobotSystem* robotSystem() = 0; + virtual single_body_system::SingleBodySystem* singleBodySystem() = 0; virtual control::TrajectoryManager* trajectoryManager() = 0; + // Body management + virtual bool hasSingleBody() const = 0; + virtual void loadSingleBody(const std::string& name) = 0; // Robot management virtual bool hasRobot() const = 0; virtual void loadRobot(const std::string& name) = 0; diff --git a/DSFE_App/DSFE_Core/include/Platform/Logger.h b/DSFE_App/DSFE_Core/include/Platform/Logger.h index 6de335ec..8dc918cd 100644 --- a/DSFE_App/DSFE_Core/include/Platform/Logger.h +++ b/DSFE_App/DSFE_Core/include/Platform/Logger.h @@ -1,7 +1,5 @@ +// DSFE_CORE Logger.h #pragma once -// File: Logger.h -// GitHub: SaltyJoss -#pragma warning(disable : 4251) #include "EngineCore.h" #include @@ -14,6 +12,7 @@ #include #include #include +#include // Log levels for debug panel and sim log enum class LogLevel { Trace, Debug, Info, Export, Warning, Error, Success, Fail, Runtime, Output }; @@ -47,7 +46,11 @@ class DSFE_API Debug { auto now = std::chrono::system_clock::now(); std::time_t now_time = std::chrono::system_clock::to_time_t(now); std::tm tm_data; + #ifdef _WIN32 localtime_s(&tm_data, &now_time); + #else + localtime_r(&now_time, &tm_data); + #endif std::ostringstream oss; oss << "Log/session_" << std::put_time(&tm_data, "%Y%m%d_%H%M%S") << ".txt"; @@ -157,7 +160,11 @@ class DSFE_API Debug { auto now = std::chrono::system_clock::now(); std::time_t now_time = std::chrono::system_clock::to_time_t(now); std::tm tm_data; + #ifdef _WIN32 localtime_s(&tm_data, &now_time); + #else + localtime_r(&now_time, &tm_data); + #endif std::ostringstream oss; oss << "[" << std::put_time(&tm_data, "%Y-%m-%d %H:%M:%S") << "] " diff --git a/DSFE_App/DSFE_Core/include/Platform/Paths.h b/DSFE_App/DSFE_Core/include/Platform/Paths.h index 15969be5..c4c1fbf6 100644 --- a/DSFE_App/DSFE_Core/include/Platform/Paths.h +++ b/DSFE_App/DSFE_Core/include/Platform/Paths.h @@ -1,6 +1,5 @@ +// DSFE_Core Paths.h #pragma once -// File: Paths.h -// GitHub: SaltyJoss #include "EngineCore.h" #include #include diff --git a/DSFE_App/DSFE_Core/include/Platform/SimulationState.h b/DSFE_App/DSFE_Core/include/Platform/SimulationState.h index 50173444..446bbee0 100644 --- a/DSFE_App/DSFE_Core/include/Platform/SimulationState.h +++ b/DSFE_App/DSFE_Core/include/Platform/SimulationState.h @@ -38,4 +38,12 @@ struct DSFE_API modes { eRunMode _runMode = eRunMode::Interactive; }; - +// Struct to hold comparison results for integrator analysis +struct ComparisonSnapshot { + std::string integratorName; + std::vector time; // time samples + std::vector errRms; // RMS error time series + std::vector errMax; // Max error time series + std::vector> jointErr; // [joint][sample] + int jointCount = 0; +}; \ No newline at end of file diff --git a/DSFE_App/DSFE_Core/include/Robots/DynamicsTypes.h b/DSFE_App/DSFE_Core/include/Robots/DynamicsTypes.h index 5baa5b61..d3a485a0 100644 --- a/DSFE_App/DSFE_Core/include/Robots/DynamicsTypes.h +++ b/DSFE_App/DSFE_Core/include/Robots/DynamicsTypes.h @@ -2,7 +2,6 @@ #pragma once #include "EngineCore.h" -#include "MathLibAPI.h" #include #include diff --git a/DSFE_App/DSFE_Core/include/Robots/RobotDynamics.h b/DSFE_App/DSFE_Core/include/Robots/RobotDynamics.h index ca7b0e92..c9a850b3 100644 --- a/DSFE_App/DSFE_Core/include/Robots/RobotDynamics.h +++ b/DSFE_App/DSFE_Core/include/Robots/RobotDynamics.h @@ -3,7 +3,6 @@ #include "EngineCore.h" #include -#include #include "Robots/DynamicsTypes.h" #include "Robots/RobotMetrics.h" diff --git a/DSFE_App/DSFE_Core/include/Robots/RobotDynamics.inl b/DSFE_App/DSFE_Core/include/Robots/RobotDynamics.inl index 25b8383f..3469ab99 100644 --- a/DSFE_App/DSFE_Core/include/Robots/RobotDynamics.inl +++ b/DSFE_App/DSFE_Core/include/Robots/RobotDynamics.inl @@ -47,7 +47,7 @@ namespace robots { Scalar I_trans = m * (axis_world.cross(r)).squaredNorm(); // Rotational contribution - mathlib::Mat3_T I_local = computeLinkInertiaTensor(link); + mathlib::Mat3_T I_local = computeLinkInertiaTensor(link); mathlib::Mat3_T I_world = R_link * I_local * R_link.transpose(); // Rotational contribution to inertia about the joint axis @@ -80,7 +80,7 @@ namespace robots { const mathlib::Vec3_T p = T_world[k].template block<3, 1>(0, 3); // Center of mass of the link in world frame const mathlib::Vec3_T com = R * link.inertial.com_xyz + p; // Center of mass in world frame - mathlib::Mat3_T I_local = computeLinkInertiaTensor(link); // inertia tensor in link frame + mathlib::Mat3_T I_local = computeLinkInertiaTensor(link); // inertia tensor in link frame mathlib::Mat3_T I_world = R * I_local * R.transpose(); // inertia tensor in world frame // Compute Jacobian columns for each joint and accumulate mass matrix contributions @@ -200,8 +200,8 @@ namespace robots { const mathlib::Pose_T& T_joint = jointWorldPoses[i]; // pose of joint i in world frame - const mathlib::Mat3_T R_i = T_joint.block<3, 3>(0, 0); - const mathlib::Vec3_T p_i = T_joint.block<3, 1>(0, 3); + const mathlib::Mat3_T R_i = T_joint.template block<3, 3>(0, 0); + const mathlib::Vec3_T p_i = T_joint.template block<3, 1>(0, 3); const mathlib::Vec3_T axis_world = mathlib::safeNormalised(R_i * robot.joints[i].axis); // For each link, compute the gravitational force and its torque contribution about joint i diff --git a/DSFE_App/DSFE_Core/include/Robots/RobotMetrics.h b/DSFE_App/DSFE_Core/include/Robots/RobotMetrics.h index 74da3b6d..0e1d1608 100644 --- a/DSFE_App/DSFE_Core/include/Robots/RobotMetrics.h +++ b/DSFE_App/DSFE_Core/include/Robots/RobotMetrics.h @@ -2,7 +2,7 @@ #pragma once #include "EngineCore.h" -#include + namespace robots { // Per-joint metrics diff --git a/DSFE_App/DSFE_Core/include/Robots/RobotModel.h b/DSFE_App/DSFE_Core/include/Robots/RobotModel.h index a3b6c4b7..78b54b3d 100644 --- a/DSFE_App/DSFE_Core/include/Robots/RobotModel.h +++ b/DSFE_App/DSFE_Core/include/Robots/RobotModel.h @@ -1,14 +1,11 @@ // DSFE_Core RobotModel.h #pragma once - #include "EngineCore.h" #include #include -#include - +#include #include - #include "Platform/Logger.h" #include "EngineLib/LogMacros.h" @@ -46,7 +43,7 @@ namespace robots { // Inertial properties of a link struct Inertial { - double mass = 0.0f; + double mass = 0.0; mathlib::Vec3 com_xyz{ 0.0,0.0,0.0 }; Inertia inertia{}; }; diff --git a/DSFE_App/DSFE_Core/include/Robots/RobotSystem.h b/DSFE_App/DSFE_Core/include/Robots/RobotSystem.h index 2a0149f6..53eee126 100644 --- a/DSFE_App/DSFE_Core/include/Robots/RobotSystem.h +++ b/DSFE_App/DSFE_Core/include/Robots/RobotSystem.h @@ -77,13 +77,11 @@ namespace robots { bool hasRobot() const { return _hasRobot; } void setGravity(double g); - double getGravity() const { return _gravity; } + const double getGravity() const { return _gravity; } void setNaturalFrequency(double wn) { _wn = wn; } double getNaturalFrequency() const { return _wn; } - void resetNaturalFrequencyToTarget() { - for (auto& joint : _robot.joints) { joint.wn_target = _wn; } - } + void resetNaturalFrequencyToTarget() { for (auto& joint : _robot.joints) { joint.wn_target = _wn; } } void setDampingRatio(double zeta) { _zeta = zeta; } double getDampingRatio() const { return _zeta; } @@ -324,30 +322,4 @@ namespace robots { }; } // namespace robot -#include "RobotSystemStep.inl" - -// --- Logging macros for robot syste debugging --- - -// LOG_ROT -#ifdef LOG_ROT -#error LOG_ROT macro already defined. Please undefine it before including RobotSystem.h to avoid conflicts. -#endif -// Logs the rotation part of a 4x4 matrix with a custom tag -#define LOG_ROT(tag, M) \ - LOG_INFO("[ROT] %s | X=(%.2f %.2f %.2f) Y=(%.2f %.2f %.2f) Z=(%.2f %.2f %.2f)", \ - tag, \ - M[0][0], M[0][1], M[0][2], \ - M[1][0], M[1][1], M[1][2], \ - M[2][0], M[2][1], M[2][2]) - -#define LOG_MAT4(tag, M) \ - LOG_INFO("[MAT4] %s:\n" \ - "[ % .3f % .3f % .3f % .3f ]\n" \ - "[ % .3f % .3f % .3f % .3f ]\n" \ - "[ % .3f % .3f % .3f % .3f ]\n" \ - "[ % .3f % .3f % .3f % .3f ]", \ - tag, \ - M[0][0], M[1][0], M[2][0], M[3][0], \ - M[0][1], M[1][1], M[2][1], M[3][1], \ - M[0][2], M[1][2], M[2][2], M[3][2], \ - M[0][3], M[1][3], M[2][3], M[3][3]) \ No newline at end of file +#include "RobotSystemStep.inl" \ No newline at end of file diff --git a/DSFE_App/DSFE_Core/include/Robots/TrajectoryManager.h b/DSFE_App/DSFE_Core/include/Robots/TrajectoryManager.h index 50cc1418..03c440fd 100644 --- a/DSFE_App/DSFE_Core/include/Robots/TrajectoryManager.h +++ b/DSFE_App/DSFE_Core/include/Robots/TrajectoryManager.h @@ -1,53 +1,36 @@ +// DSFE_Core TrajectoryManager.h #pragma once -// File: TrajectoryManager.h -// GitHub: SaltyJoss -// ----- -// Initially templated off a tutorial: -// GitHub: jayanam/jgl_demos/JGL_MeshLoader #include "EngineCore.h" #include -#include +#include namespace robots { class DSFE_API RobotSystem; } namespace control { - // Trajectory Manager Class class DSFE_API TrajectoryManager { public: TrajectoryManager() = default; ~TrajectoryManager() = default; - - // non-copyable (because unique_ptr) + // non-copyable TrajectoryManager(const TrajectoryManager&) = delete; TrajectoryManager& operator=(const TrajectoryManager&) = delete; - // movable is fine TrajectoryManager(TrajectoryManager&&) noexcept = default; TrajectoryManager& operator=(TrajectoryManager&&) noexcept = default; - // Clear trajectory for a specific robot link void clear(const std::string& link); - // Clears all active trajectories void clearAll(); bool empty() const { return _active.empty(); } - - // bool tryEval(const std::string& link, double t, control::TrajState& out) const; - // Check if a trajectory is active for a specific robot link bool hasActive(const std::string& link) const; - // Set a trajectory for a specific robot link void set(const std::string& link, std::unique_ptr traj); - // Apply active trajectories to the robot at time t void apply(robots::RobotSystem& robot, double t); std::size_t activeCount() const { return _active.size(); } private: -#pragma warning(push) -#pragma warning(disable: 4251) std::unordered_map> _active; -#pragma warning(pop) }; } // namespace control \ No newline at end of file diff --git a/DSFE_App/DSFE_Core/include/Scene/SimulationCore.h b/DSFE_App/DSFE_Core/include/Scene/SimulationCore.h index 22d6bc17..2da971e7 100644 --- a/DSFE_App/DSFE_Core/include/Scene/SimulationCore.h +++ b/DSFE_App/DSFE_Core/include/Scene/SimulationCore.h @@ -22,6 +22,7 @@ // Forward Declarations namespace control { class TrajectoryManager; } namespace robots { class RobotSystem; } +namespace single_body_system { class SingleBodySystem; } namespace interpreter { class IStoredProgram; } namespace core { @@ -77,9 +78,16 @@ namespace core { // Subsystems access robots::RobotSystem* robotSystem() override; const robots::RobotSystem* robotSystem() const; + single_body_system::SingleBodySystem* singleBodySystem() override; + const single_body_system::SingleBodySystem* singleBodySystem() const; control::TrajectoryManager* trajectoryManager() override; const control::TrajectoryManager* trajectoryManager() const; + // Body management + bool hasSingleBody() const override; + void loadSingleBody(const std::string& name) override; + void loadSingleBodyInternal(const std::string& name); // Internal method that assumes ownership + // Robot management bool hasRobot() const override; void loadRobot(const std::string& name) override; @@ -95,6 +103,7 @@ namespace core { // Setters for subsystems and scene objects void setRobotSystem(robots::RobotSystem* robot); + void setSingleBodySystem(single_body_system::SingleBodySystem* singleBody); void setTrajectoryManager(control::TrajectoryManager* traj); void setJointLogBuffer(robots::JointLogBuffer* buffer); void setTrajRefBuffer(robots::TrajRefBuffer* buffer); @@ -146,11 +155,13 @@ namespace core { // Owning storage (used only in owning mode) // std::unique_ptr>> _objectsOwned; std::unique_ptr _robotOwned; + std::unique_ptr _singleBodyOwned; std::unique_ptr _trajOwned; // Non-owning access (always used by logic) // std::vector>* _objects = nullptr; robots::RobotSystem* _robot = nullptr; + single_body_system::SingleBodySystem* _singleBody = nullptr; control::TrajectoryManager* _traj = nullptr; mutable std::mutex _stateMutex; @@ -175,6 +186,7 @@ namespace core { // Active Script Program interpreter::IStoredProgram* _activeProgram = nullptr; bool _robotPresentationDirty = false; + bool _singleBodyPresentationDirty = false; // Telemetry diagnostics::TelemetryRecorder _telemetry; // Dynamic telemetry recorder diff --git a/DSFE_App/DSFE_Core/include/SingleBodySystem/Body.h b/DSFE_App/DSFE_Core/include/SingleBodySystem/Body.h new file mode 100644 index 00000000..3b4224e0 --- /dev/null +++ b/DSFE_App/DSFE_Core/include/SingleBodySystem/Body.h @@ -0,0 +1,72 @@ +// DSFE_CORE Body.h +#pragma once + +#include "EngineCore.h" +#include +#include "SingleBodySystems/Dynamics.h" +#include "Numerics/IntegrationService.h" + +#include "Analysis/MetricLogger.h" + +namespace single_body_system { + class DSFE_API SingleBodySystem { // FOR NOW, this represents a single RIGID BODY, but in the near-near-near-future I will extend this for particles (or make a separate ParticleSystem class, changing this to RigidBodySystem) + public: + SingleBodySystem(); + ~SingleBodySystem() = default; + + void step(double dt, double t); + + Body* body() { return _body; } // return pointer to the body + const Body* body() const { return _body; } // return const pointer to the body + + void loadBody(const std::string& name); // this does not load the actual graphical model, just the physical properties of the body (mass, inertia, etc.) from the stl file or other source + void resetBody(); // reset the body to its initial state (position, orientation, velocity, etc.) + + bool hasBody() const { return _body != nullptr; } + + integration::eIntegrationMethod getIntegrationMethod() const { return _curIntMethod; } + std::string getIntegratorName() const { return _integrator->IntegratorName(_curIntMethod); } + void setStandardIntegrator(integration::eIntegrationMethod m) { _curIntMethod = m; } + + integration::eAutoDiffIntegrationMethod AD_IntegrationMethod() const { return _curIntMethod_AD; } + std::string AD_integratorName() const { return _AD_integrator->IntegratorName(_curIntMethod_AD); } + void setADIntegrator(integration::eAutoDiffIntegrationMethod m) { _curIntMethod_AD = m; } + + integration::IntegrationService* getIntegrator(); + const integration::IntegrationService* getIntegrator() const; + + integration::DifferentiableIntegrator* getADIntegrator(); + const integration::DifferentiableIntegrator* getADIntegrator() const; + + bool autoDiffEnabled() const { return _useAutoDiff; } + void enableAutoDiff(bool enable) { _useAutoDiff = enable; } + + std::shared_ptr runtimeIntegratorState(); + std::shared_ptr runtimeIntegratorState() const; + + private: + mathlib::VecX packState() const; + void unpackState(const mathlib::VecX& s); + + Body* _body; + std::unique_ptr _dynamics; + + std::unique_ptr _integrator; + integration::eIntegrationMethod _curIntMethod{}; + std::unique_ptr _AD_integrator; + integration::eAutoDiffIntegrationMethod _curIntMethod_AD{}; + + mathlib::Vec3 _F_ext{ 0.0, 0.0, 0.0 }; + mathlib::Vec3 _tau_ext{ 0.0, 0.0, 0.0 }; + + bool _useAutoDiff = false; + + // precomputed clamp lookup tables + mutable std::vector _clampVel; + mutable std::vector _clampAngVel; + + double _simTime = 0.0; + double _mass = 1.0; + mathlib::Vec3 _g{ 0.0, 0.0, 0.0 }; + }; +} \ No newline at end of file diff --git a/DSFE_App/DSFE_Core/src/EngineCore.cpp b/DSFE_App/DSFE_Core/src/EngineCore.cpp index bf752c23..489a2b36 100644 --- a/DSFE_App/DSFE_Core/src/EngineCore.cpp +++ b/DSFE_App/DSFE_Core/src/EngineCore.cpp @@ -1,6 +1,5 @@ +// DSFE_CORE EngineCore.cpp #include "pch.h" -// File: EngineCore.cpp -// GitHub: SaltyJoss #include "EngineCore.h" #include "Scene/SimulationCore.h" diff --git a/DSFE_App/DSFE_Core/src/Interpreter/Commands/TrajSetCmd.cpp b/DSFE_App/DSFE_Core/src/Interpreter/Commands/TrajSetCmd.cpp index 8c96e4ec..ea4325d9 100644 --- a/DSFE_App/DSFE_Core/src/Interpreter/Commands/TrajSetCmd.cpp +++ b/DSFE_App/DSFE_Core/src/Interpreter/Commands/TrajSetCmd.cpp @@ -6,9 +6,9 @@ #include "Robots/RobotSystem.h" #include "Robots/TrajectoryManager.h" -#include "Control/TrapezoidTrajectory.h" -#include "Control/SinusoidalTrajectory.h" -#include "Control/MultisineTrajectory.h" +#include "control/TrapezoidTrajectory.h" +#include "control/SinusoidalTrajectory.h" +#include "control/MultisineTrajectory.h" #include #include diff --git a/DSFE_App/DSFE_Core/src/Interpreter/Utils.cpp b/DSFE_App/DSFE_Core/src/Interpreter/Utils.cpp index fb6029f3..773cf221 100644 --- a/DSFE_App/DSFE_Core/src/Interpreter/Utils.cpp +++ b/DSFE_App/DSFE_Core/src/Interpreter/Utils.cpp @@ -135,7 +135,7 @@ namespace utils { } // Helper function to parse double from string_view - double utils::parseDouble(const std::string_view s) { + double parseDouble(const std::string_view s) { double out = 0.0; auto first = s.data(); auto last = s.data() + s.size(); @@ -145,7 +145,7 @@ namespace utils { return out; } - float utils::parseFloat(const std::string s) { + float parseFloat(const std::string s) { float out = 0.0f; auto first = s.data(); auto last = s.data() + s.size(); @@ -156,7 +156,7 @@ namespace utils { } // Helper function to parse Vec3 from string (format: "{x, y, z}") - mathlib::Vec3 utils::parseVec3(const std::string& str) { + mathlib::Vec3 parseVec3(const std::string& str) { std::string s = stripBraces(str); std::vector vStr; std::string cur; @@ -179,7 +179,7 @@ namespace utils { } // Helper function to parse an axis mask from a string (e.g. "X", "YZ", "{X Y Z}") - AxisMask utils::parseAxisMask(const std::string& args) { + AxisMask parseAxisMask(const std::string& args) { std::string s = stripBraces(args); AxisMask mask{}; // <-- start empty (no recursion - trust me this was a pain) diff --git a/DSFE_App/DSFE_Core/src/Platform/DataManager.cpp b/DSFE_App/DSFE_Core/src/Platform/DataManager.cpp index 232f4c18..31f815f5 100644 --- a/DSFE_App/DSFE_Core/src/Platform/DataManager.cpp +++ b/DSFE_App/DSFE_Core/src/Platform/DataManager.cpp @@ -42,12 +42,6 @@ namespace data { oss << std::setprecision(10) << d; return oss.str(); } - std::string operator()(long l) const { - std::ostringstream oss; - oss.setf(std::ios::fixed); - oss << std::setprecision(10) << l; - return oss.str(); - } std::string operator()(const std::vector& vec) const { std::ostringstream oss; oss << "["; @@ -497,8 +491,7 @@ namespace data { // Scalar numeric types -> 1D double dataset if (std::holds_alternative(val) || std::holds_alternative(val) || - std::holds_alternative(val) || - std::holds_alternative(val)) { + std::holds_alternative(val)) { if (auto it = _ds1D_D.find(dPath); it != _ds1D_D.end()) { return it->second; } @@ -559,9 +552,6 @@ namespace data { else if (std::holds_alternative(value)) { appendDouble1D(ds, static_cast(std::get(value))); } - else if (std::holds_alternative(value)) { - appendDouble1D(ds, static_cast(std::get(value))); - } else if (std::holds_alternative>(value)) { appendDouble2D(ds, std::get>(value)); } diff --git a/DSFE_App/DSFE_Core/src/Platform/Paths.cpp b/DSFE_App/DSFE_Core/src/Platform/Paths.cpp index 995321ab..0d546c44 100644 --- a/DSFE_App/DSFE_Core/src/Platform/Paths.cpp +++ b/DSFE_App/DSFE_Core/src/Platform/Paths.cpp @@ -1,29 +1,48 @@ +// DSFE_Core Paths.cpp #include "pch.h" -// File: Paths.cpp -// GitHub: SaltyJoss #include "Platform/Paths.h" +// Windows-specific includes for known folder paths #ifdef _WIN32 #define NOMINMAX #include - #ifdef _MSC_VER + #ifdef _MSC_VER #pragma comment(lib, "Shell32.lib") #endif #include // SHGetKnownFolderPath #include // CoTaskMemFree #endif +// Linux-specific includes for known folder paths +#ifdef __linux__ + #include + #include +#endif namespace { std::filesystem::path g_root, g_assets, g_configs, g_logs, g_runs; // Get the directory of the currently executing module (executable) inline std::filesystem::path getExecDir() { + #ifdef _WIN32 wchar_t buf[MAX_PATH]{}; GetModuleFileNameW(NULL, buf, MAX_PATH); return std::filesystem::path(buf).parent_path(); + #elif defined(__linux__) + char buf[PATH_MAX]{}; + ssize_t len = readlink("/proc/self/exe", buf, sizeof(buf) - 1); + if (len != -1) { + buf[len] = '\0'; + return std::filesystem::path(buf).parent_path(); + } + return std::filesystem::current_path(); + #else + return std::filesystem::current_path(); // Fallback for other platforms + #endif } + // Get the LocalAppData directory for the current user inline std::filesystem::path localAppDataDir() { + #ifdef _WIN32 PWSTR raw = nullptr; if (SUCCEEDED(SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, nullptr, &raw))) { std::filesystem::path p(raw); @@ -31,6 +50,16 @@ namespace { return p; } return {}; + #else + // Linux equivalent: use $XDG_DATA_HOME or fallback to ~/.local/share + if (const char* xdg = std::getenv("XDG_DATA_HOME")) { + return std::filesystem::path(xdg); + } + if (const char* home = std::getenv("HOME")) { + return std::filesystem::path(home) / ".local" / "share"; + } + return std::filesystem::current_path(); // Fallback to current path if all else fails + #endif } } diff --git a/DSFE_App/DSFE_Core/src/Robots/RobotSystem.cpp b/DSFE_App/DSFE_Core/src/Robots/RobotSystem.cpp index 8dbed328..888f166e 100644 --- a/DSFE_App/DSFE_Core/src/Robots/RobotSystem.cpp +++ b/DSFE_App/DSFE_Core/src/Robots/RobotSystem.cpp @@ -8,7 +8,7 @@ #include #include -#include +#include #include "Robots/TrajectoryManager.h" #include "Platform/Paths.h" diff --git a/DSFE_App/DSFE_Core/src/Robots/TrajectoryManager.cpp b/DSFE_App/DSFE_Core/src/Robots/TrajectoryManager.cpp index b699c5f6..ce546e2b 100644 --- a/DSFE_App/DSFE_Core/src/Robots/TrajectoryManager.cpp +++ b/DSFE_App/DSFE_Core/src/Robots/TrajectoryManager.cpp @@ -1,13 +1,11 @@ +// DSFE_Core TrajectoryManager.cpp #include "pch.h" -// File: TrajectoryManager.cpp -// GitHub: SaltyJoss #include "Robots/TrajectoryManager.h" #include "Robots/RobotSystem.h" -#include "Control/IJointTrajectory.h" -#include -#include -#include +#include +#include +#include namespace control { // Clear trajectory for a specific robot link diff --git a/DSFE_App/DSFE_Core/src/Scene/SimulationCore.cpp b/DSFE_App/DSFE_Core/src/Scene/SimulationCore.cpp index f0d18b8f..c7f9880b 100644 --- a/DSFE_App/DSFE_Core/src/Scene/SimulationCore.cpp +++ b/DSFE_App/DSFE_Core/src/Scene/SimulationCore.cpp @@ -5,6 +5,7 @@ #include "Robots/RobotSystem.h" #include "Robots/RobotModel.h" #include "Robots/TrajectoryManager.h" +#include "SingleBodySystem/Body.h" #include "Interpreter/StoredProgram.h" #include "Interpreter/Parser.h" @@ -15,10 +16,12 @@ namespace core { // Owned constructed subsystems (default) SimulationCore::SimulationCore() - : _trajOwned(std::make_unique()), _robotOwned(std::make_unique()) + : _trajOwned(std::make_unique()), _robotOwned(std::make_unique()), + _singleBodyOwned(std::make_unique()) { _traj = _trajOwned.get(); _robot = _robotOwned.get(); + _singleBody = _singleBodyOwned.get(); startExportThread(); } @@ -30,15 +33,23 @@ namespace core { // Non-owning constructor (used when subsystems are managed externally, e.g. by the SimulationManager) SimulationCore::SimulationCore(robots::RobotSystem& robot, control::TrajectoryManager& traj) - : _robot(&robot), _traj(&traj) { + : _robot(&robot), _traj(&traj), _singleBody(nullptr) { startExportThread(); } // Simulation System void SimulationCore::setupSimulationIntegrator() { - if (!_robot) { return; } - auto* intgr = _robot->getIntegrator(); - auto* adIntgr = _robot->getADIntegrator(); + if (!_robot && !_singleBody) { return; } + integration::IntegrationService* intgr; + integration::DifferentiableIntegrator* adIntgr; + if (_robot) { + intgr = _robot->getIntegrator(); + adIntgr = _robot->getADIntegrator(); + } + if (_singleBody) { + intgr = _singleBody->getIntegrator(); + adIntgr = _singleBody->getADIntegrator(); + } intgr->resetAdaptiveState(); intgr->setAdaptiveTolerances(1e-3, 1e-6); intgr->setMaxStep(_dt); @@ -47,37 +58,39 @@ namespace core { } // Set the integration method for the simulation (also updates the robot's integrator if it exists) void SimulationCore::setIntegrationMethod(integration::eIntegrationMethod method) { - if (!_robot) { return; } - _robot->setStandardIntegrator(method); + if (!_robot && !_singleBody) { return; } + if (_singleBody) { _singleBody->setStandardIntegrator(method); } + if (_robot) { _robot->setStandardIntegrator(method); } } // Set the auto-diff integration method for the simulation (also updates the robot's AD integrator if it exists) void SimulationCore::setADIntegrationMethod(integration::eAutoDiffIntegrationMethod method) { - if (!_robot) { return; } - _robot->setADIntegrator(method); + if (!_robot && !_singleBody) { return; } + if (_singleBody) { _singleBody->setADIntegrator(method); } + if (_robot) { _robot->setADIntegrator(method); } } // Get the name of the current integration method (returns "no_robot" if no robot is loaded) std::string SimulationCore::integrationMethodName() const { - if (!_robot) { return "no_robot"; } std::string intName; - if (_robot->autoDiffEnabled()) { - intName = _robot->getIntegratorName(); - } - else { - intName = _robot->AD_integratorName(); + if (_robot) { + if (_robot->autoDiffEnabled()) { intName = _robot->getIntegratorName(); } + else { intName = _robot->AD_integratorName(); } } + else if(_singleBody) { intName = _singleBody->getIntegratorName(); } + else { intName = "no_system"; } LOG_INFO("Integration Method: %s", intName.c_str()); - return intName; } // Get the current integration method integration::eIntegrationMethod SimulationCore::integrationMethod() const { - if (!_robot) { return integration::eIntegrationMethod::RK4; } - return _robot->getIntegrationMethod(); + if (_robot) { return _robot->getIntegrationMethod(); } + if (_singleBody) { return _singleBody->getIntegrationMethod(); } + return integration::eIntegrationMethod::RK4; } // Get the current auto-diff integration method integration::eAutoDiffIntegrationMethod SimulationCore::autoDiffIntegrationMethod() const { - if (!_robot) { return integration::eAutoDiffIntegrationMethod::AD_ImplicitEuler; } - return _robot->AD_IntegrationMethod(); + if (_robot) { return _robot->AD_IntegrationMethod(); } + if (_singleBody) { return _singleBody->AD_IntegrationMethod(); } + return integration::eAutoDiffIntegrationMethod::AD_ImplicitEuler; } void SimulationCore::enableAutoDiff(bool enable) { if (!_robot) { return; } @@ -135,6 +148,7 @@ namespace core { } _telemetry.update(simTime, *_robot, _traj, diagnostics::eTelemetryLevel::FULL); } + if (hasSingleBody()) { _singleBody->step(_dt, simTime); } } _accum -= _dt; // decrease accumulator by fixed timestep until we catch up to the current frame time } @@ -157,6 +171,8 @@ namespace core { _simTime.store(0.0, std::memory_order_relaxed); _accum = 0.0; + std::string intName; + // Reset simulation system if (_robot) { _robot->resetRobot(); @@ -182,11 +198,16 @@ namespace core { _trajRefBuffer.clear(); _trajRefBuffer.reserve(std::max(1024, total / (26 / 5))); // 26 to 5 entries, so reserving 1/(26/5) of total steps as a heuristic for ref buffer size _robot->setRefBuffer(&_trajRefBuffer); - } - - const auto state = _robot->runtimeIntegratorState(); - std::string intName = (_robot->autoDiffEnabled()) ? _robot->AD_integratorName() : _robot->getIntegratorName(); + const auto state = _robot->runtimeIntegratorState(); + intName = (_robot->autoDiffEnabled()) ? _robot->AD_integratorName() : _robot->getIntegratorName(); + } + if (_singleBody) { + _singleBody->resetBody(); + const auto state = _singleBody->runtimeIntegratorState(); + intName = (_singleBody->autoDiffEnabled()) ? _singleBody->AD_integratorName() : _singleBody->getIntegratorName(); + } + _data.setParentFolder(paths::runs().string()); // Ensure reference sim system have their integrators configured for the new run @@ -412,12 +433,31 @@ namespace core { loadRobotInternal(name); _robotPresentationDirty = true; } - + // Internal method to load a robot, assumes ownership of the robot system void SimulationCore::loadRobotInternal(const std::string& name) { if (!_robot) { LOG_ERROR("Cannot load robot: RobotSystem not set"); return; } _robot->loadRobot(name); } + // Accessor for the single body system (non-const and const versions) + single_body_system::SingleBodySystem* SimulationCore::singleBodySystem() { return _singleBody; } + const single_body_system::SingleBodySystem* SimulationCore::singleBodySystem() const { return _singleBody; } + + // Setter and checker for Single Body System + void SimulationCore::setSingleBodySystem(single_body_system::SingleBodySystem* singleBody) { _singleBody = singleBody; } + bool SimulationCore::hasSingleBody() const { return _singleBody && _singleBody->hasBody(); } + + // Loads a single body into the single body system by name + void SimulationCore::loadSingleBody(const std::string& name) { + loadSingleBodyInternal(name); + _singleBodyPresentationDirty = true; + } + // Internal method to load a single body, assumes ownership of the single body system + void SimulationCore::loadSingleBodyInternal(const std::string& name) { + if (!_singleBody) { LOG_ERROR("Cannot load single body: SingleBodySystem not set"); return; } + _singleBody->loadBody(name); + } + // Setter for the trajectory manager void SimulationCore::setTrajectoryManager(control::TrajectoryManager* traj) { _traj = traj; } @@ -440,8 +480,6 @@ namespace core { void SimulationCore::setActiveProgram(interpreter::IStoredProgram* p) { _activeProgram = p; } interpreter::IStoredProgram* SimulationCore::activeProgram() const { return _activeProgram; } - // Setter for the - // Thread-based methods // Starts the export thread if it's not already running diff --git a/DSFE_App/DSFE_Core/src/SingleBodySystem/Body.cpp b/DSFE_App/DSFE_Core/src/SingleBodySystem/Body.cpp new file mode 100644 index 00000000..04a560e1 --- /dev/null +++ b/DSFE_App/DSFE_Core/src/SingleBodySystem/Body.cpp @@ -0,0 +1,111 @@ +// DSFE_CORE Body.cpp +#include "pch.h" +#include "SingleBodySystem/Body.h" +#include "EngineLib/LogMacros.h" + +namespace single_body_system { + SingleBodySystem::SingleBodySystem() + : _body(new Body()), _dynamics(std::make_unique()), + _integrator(std::make_unique()), _AD_integrator(std::make_unique()) + { + } + + mathlib::VecX SingleBodySystem::packState() const { + mathlib::VecX x(13); + x.segment<3>(0) = _body->state.p; // position + x.segment<3>(3) = _body->state.pd; // linear velocity + x.segment<4>(6) = _body->state.q.coeffs(); + x.segment<3>(10) = _body->state.w; // angular velocity + + return x; + } + + void SingleBodySystem::unpackState(const mathlib::VecX& x) { + if (!_body) { LOG_ERROR("Body not initialised!"); return; } + if (x.size() != 13) { LOG_ERROR("State vector size mismatch! Expected 13, got %d", (int)x.size()); return; } + + if (_clampVel.size() != 3) { _clampVel.resize(3, 0); } + if (_clampAngVel.size() != 3) { _clampAngVel.resize(3, 0); } + + auto& p_in = x.segment<3>(0); + auto& pd_in = x.segment<3>(3); + auto& w_in = x.segment<3>(10); + + for (int i = 0; i < 3; ++i) { + double pd_i = pd_in[i]; // Store the original value before clamping + double w_i = w_in[i]; // Store the original value before clamping + + double pdMax_hpd = std::abs(_body->limits.pdMax[i]); + double pd_i_out = pd_i; + double wMax_hw = std::abs(_body->limits.wMax[i]); + double w_i_out = w_i; + + const double eps = 0.05; // 5 % tolerance for clamping + // TODO: Check the correct way to clamp the velocities close the S.o.L + // * Not really sure if I have done this correctly + if (pdMax_hpd > 0.0) { + if (pdMax_hpd == constants::c_0) { pd_i_out = std::clamp(pd_i, -pdMax_hpd, pdMax_hpd); } + else if (std::abs(pd_i) > (1.0 + eps) * pdMax_hpd) { pd_i_out = std::clamp(pd_i, -pdMax_hpd, pdMax_hpd); } + } + if (wMax_hw > 0.0) { + if (wMax_hw == constants::c_0) { w_i_out = std::clamp(w_i, -wMax_hw, wMax_hw); } + else if (std::abs(w_i) > (1.0 + eps) * wMax_hw) { w_i_out = std::clamp(w_i, -wMax_hw, wMax_hw); } + } + + _clampVel[i] = (pd_i_out != pd_i) ? 1 : 0; + _clampAngVel[i] = (w_i_out != w_i) ? 1 : 0; + + _body->state.p[i] = p_in[i]; + _body->state.pd[i] = pd_i_out; + _body->state.w[i] = w_i_out; + } + _body->state.q.coeffs() = x.segment<4>(6); + + + if (_clampVel[0] || _clampVel[1] || _clampVel[2]) { LOG_WARN("Linear velocity clamping applied: pd = [%f, %f, %f]", pd_in[0], pd_in[1], pd_in[2]); } + if (_clampAngVel[0] || _clampAngVel[1] || _clampAngVel[2]) { LOG_WARN("Angular velocity clamping applied: w = [%f, %f, %f]", w_in[0], w_in[1], w_in[2]); } + } + + // Step the simulation forward by dt seconds at time t + void SingleBodySystem::step(double dt, double t) { + if (!_body) { LOG_ERROR("Body not initialised!"); return; } + if (!_dynamics) { LOG_ERROR("Dynamics system not initialised!"); return; } + + _simTime = t; + VecX x = packState(); + // Not going to use the computeDynamics methods initially, just want a straight cut test first. + auto f_deriv = [&](auto /*t*/, const auto& x) { return _dynamics->derivatives(*_body, x, _F_ext, _tau_ext); }; + auto f_jac = [&](const auto& x, auto& J_out) { _dynamics->jacobian(*_body, x, J_out); }; + auto step = _integrator->step(_curIntMethod, x, t, dt, f_deriv, f_jac); // Wont work with Implicit since no jacobian provided, but will work with RK4 and other explicit methods + VecX x_next = step.x_next; + + unpackState(x); + + for (int i = 0; i < x_next.size(); ++i) { + const auto v = mathlib::real(x_next[i]); + if (std::isnan(v) || std::isinf(v)) { LOG_ERROR("Non-finite x_next[%d] = %f", i, (double)v); } + } + } + + void SingleBodySystem::loadBody(const std::string& name) { + if (!_body) { LOG_ERROR("Body not initialised!"); return; } + _body->name = name; + } + + void SingleBodySystem::resetBody() { + if (!_body) { LOG_ERROR("Body not initialised!"); return; } + _body->state.p = mathlib::Vec3::Zero(); + _body->state.pd = mathlib::Vec3::Zero(); + _body->state.q = mathlib::Quat::Identity(); + _body->state.w = mathlib::Vec3::Zero(); + } + + integration::IntegrationService* SingleBodySystem::getIntegrator() { return _integrator.get(); } + const integration::IntegrationService* SingleBodySystem::getIntegrator() const { return _integrator.get(); } + + integration::DifferentiableIntegrator* SingleBodySystem::getADIntegrator() { return _AD_integrator.get(); } + const integration::DifferentiableIntegrator* SingleBodySystem::getADIntegrator() const { return _AD_integrator.get(); } + + std::shared_ptr SingleBodySystem::runtimeIntegratorState() { return _useAutoDiff ? _AD_integrator->runtimeState() : _integrator->runtimeState(); } + std::shared_ptr SingleBodySystem::runtimeIntegratorState() const { return _useAutoDiff ? _AD_integrator->runtimeState() : _integrator->runtimeState(); } +} \ No newline at end of file diff --git a/DSFE_App/DSFE_Engine/CMakeLists.txt b/DSFE_App/DSFE_Engine/CMakeLists.txt index f68e79b6..aa9b4743 100644 --- a/DSFE_App/DSFE_Engine/CMakeLists.txt +++ b/DSFE_App/DSFE_Engine/CMakeLists.txt @@ -1,7 +1,4 @@ # CMakeLists.txt for the DSFE_Engine executable -cmake_minimum_required(VERSION 3.10) -project(DSFE_Engine LANGUAGES C CXX) - find_package(Threads REQUIRED) add_executable(DSFE_Engine @@ -14,17 +11,26 @@ target_include_directories(DSFE_Engine PRIVATE ) target_link_libraries(DSFE_Engine PRIVATE - DSFE_GUI DSFE_Core Threads::Threads ) +if (DSFE_ENABLE_GUI) + target_link_libraries(DSFE_Engine PRIVATE DSFE_GUI) + target_compile_definitions(DSFE_Engine PRIVATE DSFE_ENABLE_GUI) +endif() + add_custom_command(TARGET DSFE_Engine POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy_if_different - "$" + COMMAND ${CMAKE_COMMAND} -E make_directory "$" + + COMMAND $<$>:${CMAKE_COMMAND}> + $<$>:-E> + $<$>:copy_if_different> + $<$>:$> + $<$>:$> + COMMAND_EXPAND_LISTS - COMMENT "Copying runtime DLLs..." ) # Defines the source and destination for assets and configs diff --git a/DSFE_App/DSFE_Engine/assets/shaders/present.frag.glsl b/DSFE_App/DSFE_Engine/assets/shaders/present.frag.glsl new file mode 100644 index 00000000..51d0d135 --- /dev/null +++ b/DSFE_App/DSFE_Engine/assets/shaders/present.frag.glsl @@ -0,0 +1,11 @@ +#version 460 core + +in vec2 uv; + +out vec4 FragColour; + +uniform sampler2D screenTexture; + +void main() { + FragColour = texture(screenTexture, uv); +} \ No newline at end of file diff --git a/DSFE_App/DSFE_Engine/src/Main.cpp b/DSFE_App/DSFE_Engine/src/Main.cpp index 39994ac8..c477399a 100644 --- a/DSFE_App/DSFE_Engine/src/Main.cpp +++ b/DSFE_App/DSFE_Engine/src/Main.cpp @@ -4,10 +4,13 @@ #include #include #include -#include #include "BatchEntry.h" #include "BatchArgs.h" +#ifdef DSFE_ENABLE_GUI + #include +#endif + // Helper function to split a comma-separated string into a vector of strings, trimming whitespace static std::vector splitComma(const std::string& input) { std::vector result; @@ -46,41 +49,47 @@ int main(int argc, char** argv) { batchMode = true; break; } + else if (arg == "--about") { + std::cout + << "DSFE (Dynamic Systems Framework Engine)\n" + << " > A research-focused simulation engine for numerically modelling dynamic systems with different integration methods (explicit and implicit).\n" + << " > Version 0.9.1-alpha\n" + << " > Developed by Joss Salton\n"; + return 0; + } + else if (arg == "--help" || arg == "-h") { + std::cout + << "Usage:\n" + << " --batch -t