From 7d529723a1b3ab59d9119c5d278a679cb0d79e6e Mon Sep 17 00:00:00 2001 From: lnd3 Date: Wed, 7 Aug 2024 01:27:23 +0200 Subject: [PATCH 001/125] Remove file to be able to rename it. --- deps/ldeps | 1 - packages/tools/source/linux/platformlinux.cpp | 5 ----- 2 files changed, 6 deletions(-) delete mode 160000 deps/ldeps delete mode 100644 packages/tools/source/linux/platformlinux.cpp diff --git a/deps/ldeps b/deps/ldeps deleted file mode 160000 index 04eb2d05..00000000 --- a/deps/ldeps +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 04eb2d058f63836bf29bf56e4c45aaa7c7ab146c diff --git a/packages/tools/source/linux/platformlinux.cpp b/packages/tools/source/linux/platformlinux.cpp deleted file mode 100644 index f5b55d3f..00000000 --- a/packages/tools/source/linux/platformlinux.cpp +++ /dev/null @@ -1,5 +0,0 @@ -#if defined(__linux__) && !defined(__ANDROID__) - - - -#endif From 2fd60f5bf3ae7e8325f57a030bd75c6ad1e86b78 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Wed, 7 Aug 2024 03:33:52 +0200 Subject: [PATCH 002/125] Integrate platform definitions. Some clean up. --- .../tools/include/tools/platform/Platform.h | 15 ++++-- .../tools/source/common/platform/Platform.cpp | 50 +++++++++++++++---- .../windows/platform/PlatformWindows.cpp | 2 +- .../tools/source/windows/utils/Opengl.cpp | 2 - .../tools/source/windows/utils/Process.cpp | 2 - 5 files changed, 51 insertions(+), 20 deletions(-) diff --git a/packages/tools/include/tools/platform/Platform.h b/packages/tools/include/tools/platform/Platform.h index 48f03976..f3d4d7f3 100644 --- a/packages/tools/include/tools/platform/Platform.h +++ b/packages/tools/include/tools/platform/Platform.h @@ -13,14 +13,21 @@ namespace platform { } enum class platform { - WIN = 0, + WINDOWS = 0, + UWP, LINUX, - ANDROID,// also linux - APPLE, // OS X, iOS - //UNIX, + ANDROID, + CYGWIN, + MSYS, + GNU, + OSX, + IOS, UNKNOWN }; + bool IsPlatformPosixCompatible(); + std::string_view GetPlatformName(); + std::string_view GetPlatformVersion(); platform GetPlatform(); class FS { diff --git a/packages/tools/source/common/platform/Platform.cpp b/packages/tools/source/common/platform/Platform.cpp index 45def8c4..9aa9b485 100644 --- a/packages/tools/source/common/platform/Platform.cpp +++ b/packages/tools/source/common/platform/Platform.cpp @@ -1,5 +1,10 @@ #include "tools/platform/Platform.h" +// Useful platform define article +// https://stackoverflow.com/questions/2989810/which-cross-platform-preprocessor-defines-win32-or-win32-or-win32 +// Cmake supported system platforms +// https://cmake.org/cmake/help/latest/variable/CMAKE_SYSTEM_NAME.html + namespace l { namespace platform { namespace global { @@ -8,17 +13,42 @@ namespace platform { std::wstring params; } - platform GetPlatform() { -#if defined(WIN32) || defined(_WIN32) - return platform::WIN; -#elif defined(__linux__) + bool constexpr IsPlatformPosixCompatible() { +#if defined(BSYSTEM_PLATFORM_Linux) || defined(BSYSTEM_PLATFORM_Android) || defined(BSYSTEM_PLATFORM_CYGWIN) || defined(BSYSTEM_PLATFORM_MSYS) || defined(BSYSTEM_PLATFORM_GNU) || defined(BSYSTEM_PLATFORM_iOS) || defined(BSYSTEM_PLATFORM_Darwin) + return true; +#endif + return false; + } + + std::string_view constexpr GetPlatformName() { + return BSYSTEM_PLATFORM; + } + + std::string_view constexpr GetPlatformVersion() { + return BSYSTEM_VERSION; + } + + platform constexpr GetPlatform() { +#if defined(BSYSTEM_PLATFORM_Windows) + return platform::WINDOWS; +#elif defined(BSYSTEM_PLATFORM_WindowsStore) + return platform::UWP; + +#elif defined(BSYSTEM_PLATFORM_Linux) return platform::LINUX; -#elif defined(CYGWIN) +#elif defined(BSYSTEM_PLATFORM_Android) return platform::ANDROID; -#elif defined(APPLE) - return platform::APPLE; -#elif defined(LINUX) - return platform::APPLE; +#elif defined(BSYSTEM_PLATFORM_CYGWIN) + return platform::CYGWIN; +#elif defined(BSYSTEM_PLATFORM_MSYS) + return platform::MSYS; +#elif defined(BSYSTEM_PLATFORM_GNU) + return platform::GNU; + +#elif defined(BSYSTEM_PLATFORM_iOS) + return platform::IOS; +#elif defined(BSYSTEM_PLATFORM_Darwin) + return platform::OSX; #else return platform::UNKNOWN; #endif @@ -42,7 +72,5 @@ namespace platform { return global::argument.size(); } - - } } \ No newline at end of file diff --git a/packages/tools/source/windows/platform/PlatformWindows.cpp b/packages/tools/source/windows/platform/PlatformWindows.cpp index fb942c27..564de724 100644 --- a/packages/tools/source/windows/platform/PlatformWindows.cpp +++ b/packages/tools/source/windows/platform/PlatformWindows.cpp @@ -1,4 +1,4 @@ -#if defined(_WIN32) || defined(_WIN64) +#if defined(BSYSTEM_PLATFORM_Windows) #include "tools/platform/Platform.h" #include "logging/Log.h" diff --git a/packages/tools/source/windows/utils/Opengl.cpp b/packages/tools/source/windows/utils/Opengl.cpp index 5f3d315c..bf59b696 100644 --- a/packages/tools/source/windows/utils/Opengl.cpp +++ b/packages/tools/source/windows/utils/Opengl.cpp @@ -1,5 +1,3 @@ -#pragma once - #pragma comment(lib, "opengl32.lib") #pragma comment(lib, "glu32.lib") diff --git a/packages/tools/source/windows/utils/Process.cpp b/packages/tools/source/windows/utils/Process.cpp index 55f489d8..006b93ea 100644 --- a/packages/tools/source/windows/utils/Process.cpp +++ b/packages/tools/source/windows/utils/Process.cpp @@ -1,5 +1,3 @@ -#pragma once - #include "Process.h" #include "logging/Log.h" From 4a251d415b8693c98d2bd7668f05b9ba6fde5a13 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Wed, 7 Aug 2024 03:42:33 +0200 Subject: [PATCH 003/125] Add linux platform specifics. --- packages/tools/source/linux/PlatformLinux.cpp | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 packages/tools/source/linux/PlatformLinux.cpp diff --git a/packages/tools/source/linux/PlatformLinux.cpp b/packages/tools/source/linux/PlatformLinux.cpp new file mode 100644 index 00000000..4b243dab --- /dev/null +++ b/packages/tools/source/linux/PlatformLinux.cpp @@ -0,0 +1,58 @@ +#if defined(BSYSTEM_PLATFORM_Linux) + +#include "tools/platform/Platform.h" +#include "logging/Log.h" + +#include +#include + +namespace l { +namespace platform { + +std::wstring FS::GetSeparator() { + return L"/"; +} + +std::wstring FS::GetProgramPath() { + constexpr size_t buffer_size = 1024; + wchar_t wbuffer[buffer_size]; + return std::wstring(wbuffer); +} + +std::wstring FS::GetAppDataPath() { + constexpr size_t buffer_size = 1024; + wchar_t wbuffer[buffer_size]; + return std::wstring(wbuffer); +} + +void Cmd::ParseArguments(int argc, const char* []) { + std::lock_guard lock(global::argument_mutex); + if (global::argument.empty() && global::params.empty()) { + global::argument.clear(); + global::params.clear(); + for (int i = 0; i < argc; i++) { + auto arg = std::wstring(argvw2[i]); + global::argument.push_back(arg); + if (i > 0) { + global::params += arg + (i+1 < argc ? L" ": L""); + } + } + } +} + +bool Proc::IsElevated() { + return false; +} + +bool Proc::Execute(const std::wstring& file, const std::wstring& args, bool elevated) { + return 0; +} + +bool Proc::Fork(std::wstring title, std::wstring& file, const std::wstring& args, bool detach) { + return false; + +} + +} +} +#endif \ No newline at end of file From b77143cfc0a8099731f6c26cab3389e839cf3c0c Mon Sep 17 00:00:00 2001 From: lnd3 Date: Wed, 7 Aug 2024 04:12:15 +0200 Subject: [PATCH 004/125] Move platform code. --- .../tools/include/tools/platform/Platform.h | 44 +++++++++++++++++-- .../tools/source/common/platform/Platform.cpp | 41 ----------------- 2 files changed, 40 insertions(+), 45 deletions(-) diff --git a/packages/tools/include/tools/platform/Platform.h b/packages/tools/include/tools/platform/Platform.h index f3d4d7f3..8144e9ba 100644 --- a/packages/tools/include/tools/platform/Platform.h +++ b/packages/tools/include/tools/platform/Platform.h @@ -25,10 +25,46 @@ enum class platform { UNKNOWN }; - bool IsPlatformPosixCompatible(); - std::string_view GetPlatformName(); - std::string_view GetPlatformVersion(); - platform GetPlatform(); + bool constexpr IsPlatformPosixCompatible() { + #if defined(BSYSTEM_PLATFORM_Linux) || defined(BSYSTEM_PLATFORM_Android) || defined(BSYSTEM_PLATFORM_CYGWIN) || defined(BSYSTEM_PLATFORM_MSYS) || defined(BSYSTEM_PLATFORM_GNU) || defined(BSYSTEM_PLATFORM_iOS) || defined(BSYSTEM_PLATFORM_Darwin) + return true; + #endif + return false; + } + + std::string_view constexpr GetPlatformName() { + return BSYSTEM_PLATFORM; + } + + std::string_view constexpr GetPlatformVersion() { + return BSYSTEM_VERSION; + } + + platform constexpr GetPlatform() { + #if defined(BSYSTEM_PLATFORM_Windows) + return platform::WINDOWS; + #elif defined(BSYSTEM_PLATFORM_WindowsStore) + return platform::UWP; + + #elif defined(BSYSTEM_PLATFORM_Linux) + return platform::LINUX; + #elif defined(BSYSTEM_PLATFORM_Android) + return platform::ANDROID; + #elif defined(BSYSTEM_PLATFORM_CYGWIN) + return platform::CYGWIN; + #elif defined(BSYSTEM_PLATFORM_MSYS) + return platform::MSYS; + #elif defined(BSYSTEM_PLATFORM_GNU) + return platform::GNU; + + #elif defined(BSYSTEM_PLATFORM_iOS) + return platform::IOS; + #elif defined(BSYSTEM_PLATFORM_Darwin) + return platform::OSX; + #else + return platform::UNKNOWN; + #endif + } class FS { public: diff --git a/packages/tools/source/common/platform/Platform.cpp b/packages/tools/source/common/platform/Platform.cpp index 9aa9b485..67d30bf6 100644 --- a/packages/tools/source/common/platform/Platform.cpp +++ b/packages/tools/source/common/platform/Platform.cpp @@ -13,47 +13,6 @@ namespace platform { std::wstring params; } - bool constexpr IsPlatformPosixCompatible() { -#if defined(BSYSTEM_PLATFORM_Linux) || defined(BSYSTEM_PLATFORM_Android) || defined(BSYSTEM_PLATFORM_CYGWIN) || defined(BSYSTEM_PLATFORM_MSYS) || defined(BSYSTEM_PLATFORM_GNU) || defined(BSYSTEM_PLATFORM_iOS) || defined(BSYSTEM_PLATFORM_Darwin) - return true; -#endif - return false; - } - - std::string_view constexpr GetPlatformName() { - return BSYSTEM_PLATFORM; - } - - std::string_view constexpr GetPlatformVersion() { - return BSYSTEM_VERSION; - } - - platform constexpr GetPlatform() { -#if defined(BSYSTEM_PLATFORM_Windows) - return platform::WINDOWS; -#elif defined(BSYSTEM_PLATFORM_WindowsStore) - return platform::UWP; - -#elif defined(BSYSTEM_PLATFORM_Linux) - return platform::LINUX; -#elif defined(BSYSTEM_PLATFORM_Android) - return platform::ANDROID; -#elif defined(BSYSTEM_PLATFORM_CYGWIN) - return platform::CYGWIN; -#elif defined(BSYSTEM_PLATFORM_MSYS) - return platform::MSYS; -#elif defined(BSYSTEM_PLATFORM_GNU) - return platform::GNU; - -#elif defined(BSYSTEM_PLATFORM_iOS) - return platform::IOS; -#elif defined(BSYSTEM_PLATFORM_Darwin) - return platform::OSX; -#else - return platform::UNKNOWN; -#endif - } - std::wstring Cmd::GetCommandLineArgument(size_t index) { std::lock_guard lock(global::argument_mutex); if (index >= global::argument.size()) { From 73042705fecb64c259f30436952f13a6e657e8d6 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Wed, 7 Aug 2024 04:17:59 +0200 Subject: [PATCH 005/125] Readd ldeps. --- deps/ldeps | 1 + 1 file changed, 1 insertion(+) create mode 160000 deps/ldeps diff --git a/deps/ldeps b/deps/ldeps new file mode 160000 index 00000000..4f0f0dcc --- /dev/null +++ b/deps/ldeps @@ -0,0 +1 @@ +Subproject commit 4f0f0dcc41ea24535e37b2958dc4563a8f146407 From 019a18d642b3eee6efbfcaa6cf6aef1aae12cf0e Mon Sep 17 00:00:00 2001 From: lnd3 Date: Wed, 7 Aug 2024 04:24:12 +0200 Subject: [PATCH 006/125] Clean out linux platform code for now. --- packages/tools/source/linux/PlatformLinux.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/tools/source/linux/PlatformLinux.cpp b/packages/tools/source/linux/PlatformLinux.cpp index 4b243dab..15ef7b3c 100644 --- a/packages/tools/source/linux/PlatformLinux.cpp +++ b/packages/tools/source/linux/PlatformLinux.cpp @@ -25,13 +25,13 @@ std::wstring FS::GetAppDataPath() { return std::wstring(wbuffer); } -void Cmd::ParseArguments(int argc, const char* []) { +void Cmd::ParseArguments(int argc, const char* argv[]) { std::lock_guard lock(global::argument_mutex); if (global::argument.empty() && global::params.empty()) { global::argument.clear(); global::params.clear(); for (int i = 0; i < argc; i++) { - auto arg = std::wstring(argvw2[i]); + auto arg = std::wstring(argv[i]); global::argument.push_back(arg); if (i > 0) { global::params += arg + (i+1 < argc ? L" ": L""); @@ -44,11 +44,11 @@ bool Proc::IsElevated() { return false; } -bool Proc::Execute(const std::wstring& file, const std::wstring& args, bool elevated) { +bool Proc::Execute(const std::wstring&, const std::wstring&, bool) { return 0; } -bool Proc::Fork(std::wstring title, std::wstring& file, const std::wstring& args, bool detach) { +bool Proc::Fork(std::wstring, std::wstring&, const std::wstring&, bool) { return false; } From 82099b52b1bbb7bd27313a1b95fe7a5b8ac1e530 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Wed, 7 Aug 2024 04:34:05 +0200 Subject: [PATCH 007/125] Fix linux platform errors. --- packages/tools/source/linux/PlatformLinux.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/tools/source/linux/PlatformLinux.cpp b/packages/tools/source/linux/PlatformLinux.cpp index 15ef7b3c..96825ceb 100644 --- a/packages/tools/source/linux/PlatformLinux.cpp +++ b/packages/tools/source/linux/PlatformLinux.cpp @@ -31,7 +31,8 @@ void Cmd::ParseArguments(int argc, const char* argv[]) { global::argument.clear(); global::params.clear(); for (int i = 0; i < argc; i++) { - auto arg = std::wstring(argv[i]); + std::string argvi(argv[i]); + auto arg = std::wstring(argvi.begin(), argvi.end()); global::argument.push_back(arg); if (i > 0) { global::params += arg + (i+1 < argc ? L" ": L""); From d3dd3708d40c3a143889826c15d7a2389a9e9d66 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Wed, 7 Aug 2024 09:26:50 +0200 Subject: [PATCH 008/125] Upgrade cmake min version. Move linux platform file. --- CMakeLists.txt | 2 +- deps/ldeps | 2 +- packages/concurrency/CMakeLists.txt | 2 - packages/ecs/CMakeLists.txt | 2 - packages/filesystem/CMakeLists.txt | 2 - packages/logging/CMakeLists.txt | 2 - packages/memory/CMakeLists.txt | 2 - packages/meta/CMakeLists.txt | 2 - packages/network/CMakeLists.txt | 2 - packages/physics/CMakeLists.txt | 2 - packages/rendering/CMakeLists.txt | 2 - packages/storage/CMakeLists.txt | 2 - packages/testing/CMakeLists.txt | 2 - packages/tools/CMakeLists.txt | 2 - packages/tools/source/linux/PlatformLinux.cpp | 59 ------------------- 15 files changed, 2 insertions(+), 85 deletions(-) delete mode 100644 packages/tools/source/linux/PlatformLinux.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 2df109c9..bbadd7f8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 3.0.2) +cmake_minimum_required (VERSION 3.21.7) project(ltools) diff --git a/deps/ldeps b/deps/ldeps index 4f0f0dcc..a7f008bd 160000 --- a/deps/ldeps +++ b/deps/ldeps @@ -1 +1 @@ -Subproject commit 4f0f0dcc41ea24535e37b2958dc4563a8f146407 +Subproject commit a7f008bd9532c65980d8c1b7dc82a0a2ccb9b9ed diff --git a/packages/concurrency/CMakeLists.txt b/packages/concurrency/CMakeLists.txt index 6567106a..532cb732 100644 --- a/packages/concurrency/CMakeLists.txt +++ b/packages/concurrency/CMakeLists.txt @@ -1,5 +1,3 @@ -cmake_minimum_required (VERSION 3.0.2) - project (concurrency) set(deps diff --git a/packages/ecs/CMakeLists.txt b/packages/ecs/CMakeLists.txt index b709e215..1fac3781 100644 --- a/packages/ecs/CMakeLists.txt +++ b/packages/ecs/CMakeLists.txt @@ -1,5 +1,3 @@ -cmake_minimum_required (VERSION 3.0.2) - project (ecs) set(deps diff --git a/packages/filesystem/CMakeLists.txt b/packages/filesystem/CMakeLists.txt index 121085d6..3c8446cc 100644 --- a/packages/filesystem/CMakeLists.txt +++ b/packages/filesystem/CMakeLists.txt @@ -1,5 +1,3 @@ -cmake_minimum_required (VERSION 3.0.2) - project (filesystem) set(deps diff --git a/packages/logging/CMakeLists.txt b/packages/logging/CMakeLists.txt index 30de2cc6..d3b945ca 100644 --- a/packages/logging/CMakeLists.txt +++ b/packages/logging/CMakeLists.txt @@ -1,5 +1,3 @@ -cmake_minimum_required (VERSION 3.0.2) - project (logging) set(deps diff --git a/packages/memory/CMakeLists.txt b/packages/memory/CMakeLists.txt index 6d8bfed5..2c56b6be 100644 --- a/packages/memory/CMakeLists.txt +++ b/packages/memory/CMakeLists.txt @@ -1,5 +1,3 @@ -cmake_minimum_required (VERSION 3.0.2) - project (memory) set(deps diff --git a/packages/meta/CMakeLists.txt b/packages/meta/CMakeLists.txt index a9772acb..ef5e0e3f 100644 --- a/packages/meta/CMakeLists.txt +++ b/packages/meta/CMakeLists.txt @@ -1,5 +1,3 @@ -cmake_minimum_required (VERSION 3.0.2) - project (meta) set(deps diff --git a/packages/network/CMakeLists.txt b/packages/network/CMakeLists.txt index 25b0dfb6..96038204 100644 --- a/packages/network/CMakeLists.txt +++ b/packages/network/CMakeLists.txt @@ -1,5 +1,3 @@ -cmake_minimum_required (VERSION 3.0.2) - project (network) set(deps diff --git a/packages/physics/CMakeLists.txt b/packages/physics/CMakeLists.txt index bebaa63d..89b41de8 100644 --- a/packages/physics/CMakeLists.txt +++ b/packages/physics/CMakeLists.txt @@ -1,5 +1,3 @@ -cmake_minimum_required (VERSION 3.0.2) - project (physics) set(deps diff --git a/packages/rendering/CMakeLists.txt b/packages/rendering/CMakeLists.txt index e02ab957..c86a9d05 100644 --- a/packages/rendering/CMakeLists.txt +++ b/packages/rendering/CMakeLists.txt @@ -1,5 +1,3 @@ -cmake_minimum_required (VERSION 3.0.2) - project (rendering) # Version number diff --git a/packages/storage/CMakeLists.txt b/packages/storage/CMakeLists.txt index 0bd8d94e..5379b340 100644 --- a/packages/storage/CMakeLists.txt +++ b/packages/storage/CMakeLists.txt @@ -1,5 +1,3 @@ -cmake_minimum_required (VERSION 3.0.2) - project (storage) set(deps diff --git a/packages/testing/CMakeLists.txt b/packages/testing/CMakeLists.txt index 5dd88f70..db469865 100644 --- a/packages/testing/CMakeLists.txt +++ b/packages/testing/CMakeLists.txt @@ -1,5 +1,3 @@ -cmake_minimum_required (VERSION 3.0.2) - project (testing) set(deps diff --git a/packages/tools/CMakeLists.txt b/packages/tools/CMakeLists.txt index e6693928..cbccd656 100644 --- a/packages/tools/CMakeLists.txt +++ b/packages/tools/CMakeLists.txt @@ -1,5 +1,3 @@ -cmake_minimum_required (VERSION 3.0.2) - project (tools) set(deps diff --git a/packages/tools/source/linux/PlatformLinux.cpp b/packages/tools/source/linux/PlatformLinux.cpp deleted file mode 100644 index 96825ceb..00000000 --- a/packages/tools/source/linux/PlatformLinux.cpp +++ /dev/null @@ -1,59 +0,0 @@ -#if defined(BSYSTEM_PLATFORM_Linux) - -#include "tools/platform/Platform.h" -#include "logging/Log.h" - -#include -#include - -namespace l { -namespace platform { - -std::wstring FS::GetSeparator() { - return L"/"; -} - -std::wstring FS::GetProgramPath() { - constexpr size_t buffer_size = 1024; - wchar_t wbuffer[buffer_size]; - return std::wstring(wbuffer); -} - -std::wstring FS::GetAppDataPath() { - constexpr size_t buffer_size = 1024; - wchar_t wbuffer[buffer_size]; - return std::wstring(wbuffer); -} - -void Cmd::ParseArguments(int argc, const char* argv[]) { - std::lock_guard lock(global::argument_mutex); - if (global::argument.empty() && global::params.empty()) { - global::argument.clear(); - global::params.clear(); - for (int i = 0; i < argc; i++) { - std::string argvi(argv[i]); - auto arg = std::wstring(argvi.begin(), argvi.end()); - global::argument.push_back(arg); - if (i > 0) { - global::params += arg + (i+1 < argc ? L" ": L""); - } - } - } -} - -bool Proc::IsElevated() { - return false; -} - -bool Proc::Execute(const std::wstring&, const std::wstring&, bool) { - return 0; -} - -bool Proc::Fork(std::wstring, std::wstring&, const std::wstring&, bool) { - return false; - -} - -} -} -#endif \ No newline at end of file From 934a8f28ed498d9aa9a7bb5e366ee935a065f1a4 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Wed, 7 Aug 2024 09:34:36 +0200 Subject: [PATCH 009/125] Update ldeps. --- deps/ldeps | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/ldeps b/deps/ldeps index a7f008bd..c95a20fc 160000 --- a/deps/ldeps +++ b/deps/ldeps @@ -1 +1 @@ -Subproject commit a7f008bd9532c65980d8c1b7dc82a0a2ccb9b9ed +Subproject commit c95a20fc7a185e634e4e4a98249b0d4894092785 From bcfa3616017ee0aed2994f66ae7150fc13351ffc Mon Sep 17 00:00:00 2001 From: lnd3 Date: Wed, 7 Aug 2024 10:10:31 +0200 Subject: [PATCH 010/125] Add linux platform source. --- .../source/linux/platform/PlatformLinux.cpp | 59 +++++++++++++++++++ packages/tools/source/linux/utils/Process.cpp | 46 +++++++++++++++ packages/tools/source/linux/utils/Process.h | 36 +++++++++++ 3 files changed, 141 insertions(+) create mode 100644 packages/tools/source/linux/platform/PlatformLinux.cpp create mode 100644 packages/tools/source/linux/utils/Process.cpp create mode 100644 packages/tools/source/linux/utils/Process.h diff --git a/packages/tools/source/linux/platform/PlatformLinux.cpp b/packages/tools/source/linux/platform/PlatformLinux.cpp new file mode 100644 index 00000000..96825ceb --- /dev/null +++ b/packages/tools/source/linux/platform/PlatformLinux.cpp @@ -0,0 +1,59 @@ +#if defined(BSYSTEM_PLATFORM_Linux) + +#include "tools/platform/Platform.h" +#include "logging/Log.h" + +#include +#include + +namespace l { +namespace platform { + +std::wstring FS::GetSeparator() { + return L"/"; +} + +std::wstring FS::GetProgramPath() { + constexpr size_t buffer_size = 1024; + wchar_t wbuffer[buffer_size]; + return std::wstring(wbuffer); +} + +std::wstring FS::GetAppDataPath() { + constexpr size_t buffer_size = 1024; + wchar_t wbuffer[buffer_size]; + return std::wstring(wbuffer); +} + +void Cmd::ParseArguments(int argc, const char* argv[]) { + std::lock_guard lock(global::argument_mutex); + if (global::argument.empty() && global::params.empty()) { + global::argument.clear(); + global::params.clear(); + for (int i = 0; i < argc; i++) { + std::string argvi(argv[i]); + auto arg = std::wstring(argvi.begin(), argvi.end()); + global::argument.push_back(arg); + if (i > 0) { + global::params += arg + (i+1 < argc ? L" ": L""); + } + } + } +} + +bool Proc::IsElevated() { + return false; +} + +bool Proc::Execute(const std::wstring&, const std::wstring&, bool) { + return 0; +} + +bool Proc::Fork(std::wstring, std::wstring&, const std::wstring&, bool) { + return false; + +} + +} +} +#endif \ No newline at end of file diff --git a/packages/tools/source/linux/utils/Process.cpp b/packages/tools/source/linux/utils/Process.cpp new file mode 100644 index 00000000..8331695a --- /dev/null +++ b/packages/tools/source/linux/utils/Process.cpp @@ -0,0 +1,46 @@ +#include "Process.h" + +#include "logging/Log.h" + +#include +#include + +namespace l { +namespace process { + + Process::Process(std::wstring title, + std::wstring& exefile, + const std::wstring& args, + bool detach) + : mTitle(title) + , mExefile(exefile) + , mArgs(args) + , mDetach(detach) { + processRunner = std::async(std::launch::deferred, [this]() -> uint32_t { + return FAILED_TO_CREATE_PROCESS; + }); + } + + Process::~Process() { + if (processRunner.valid()) { + processRunner.wait(); + } + } + + [[nodiscard]] uint32_t Process::detach() { + if (processRunner.valid()) { + mDetach = true; + processRunner.wait(); + return processRunner.get(); + } + else { + LOG(LogError) << "Failed to detach process"; + return FAILED_TO_DETACH_PROCESS; + } + } + + [[nodiscard]] std::shared_future Process::get_future() { + return processRunner; + } +} +} diff --git a/packages/tools/source/linux/utils/Process.h b/packages/tools/source/linux/utils/Process.h new file mode 100644 index 00000000..d9558893 --- /dev/null +++ b/packages/tools/source/linux/utils/Process.h @@ -0,0 +1,36 @@ +#pragma once + +#include "logging/String.h" + +#include + + +namespace l { +namespace process { + + enum RESULTS { + SUCCESS = 0, + FAILED_TO_CREATE_PROCESS, + FAILED_TO_WAIT_FOR_PROCESS, + FAILED_TO_DETACH_PROCESS + }; + + class Process { + private: + std::wstring mTitle; + std::wstring mExefile; + std::wstring mArgs; + bool mDetach; + + std::shared_future processRunner; + + public: + Process() = delete; + Process(std::wstring title, std::wstring& exefile, const std::wstring& args, bool detach = false); + ~Process(); + + [[nodiscard]] uint32_t detach(); + [[nodiscard]] std::shared_future get_future(); + }; +} +} \ No newline at end of file From 239905a18252fd30ed5717131b71498a65ab8abc Mon Sep 17 00:00:00 2001 From: lnd3 Date: Wed, 7 Aug 2024 22:14:03 +0200 Subject: [PATCH 011/125] Skip some auto types in ecs to make intellisense work. --- packages/ecs/include/ecs/entityecs/ECSExt.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/ecs/include/ecs/entityecs/ECSExt.h b/packages/ecs/include/ecs/entityecs/ECSExt.h index 3054f6a1..6f71a797 100644 --- a/packages/ecs/include/ecs/entityecs/ECSExt.h +++ b/packages/ecs/include/ecs/entityecs/ECSExt.h @@ -289,10 +289,10 @@ namespace l::ecs { } template - auto getFirst() { - auto components = getComponentCache(); - auto entity = components->getFirst(); - return entity->get(); + ComponentHandle getFirst() { + ComponentViewCache* components = getComponentCache(); + Entity* entity = components->getFirst(); + return entity->get(); } template @@ -309,7 +309,7 @@ namespace l::ecs { template void each2(typename std::common_type...)>>::type viewFunc) { - auto* cache = getComponentCache(); + ComponentViewCache* cache = getComponentCache(); if (cache != nullptr) { cache->each(viewFunc); } From 914f0f6f9aa72c4c89183b57f741a256c1eb6d1b Mon Sep 17 00:00:00 2001 From: lnd3 Date: Wed, 7 Aug 2024 22:20:10 +0200 Subject: [PATCH 012/125] Fix initialization. --- packages/logging/source/common/String.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/logging/source/common/String.cpp b/packages/logging/source/common/String.cpp index d514e1d1..e1b72bc6 100644 --- a/packages/logging/source/common/String.cpp +++ b/packages/logging/source/common/String.cpp @@ -28,7 +28,7 @@ namespace l { namespace string { tm get_time_info(int32_t unixtime, bool adjustYearAndMonth) { - struct tm timeinfo; + struct tm timeinfo = {}; const time_t time = static_cast(unixtime); #ifdef WIN32 auto res = localtime_s(&timeinfo, &time); @@ -64,7 +64,7 @@ namespace string { } int32_t get_unix_time(int32_t* fullDateAndTime) { - struct tm timeinfo; + struct tm timeinfo = {}; timeinfo.tm_year = fullDateAndTime[0]; timeinfo.tm_mon = fullDateAndTime[1]; timeinfo.tm_mday = fullDateAndTime[2]; @@ -86,7 +86,7 @@ namespace string { auto hours = std::chrono::duration_cast(tp); const time_t now = system_clock::to_time_t(n); - struct tm newtime; + struct tm newtime = {}; #ifdef WIN32 localtime_s(&newtime, &now); #else @@ -103,7 +103,7 @@ namespace string { } std::string get_time_string(const int64_t unixtime, std::string_view format) { - struct std::tm tminfo {}; + struct std::tm tminfo = {}; #ifdef WIN32 localtime_s(&tminfo, &unixtime); #else @@ -124,7 +124,7 @@ namespace string { } int32_t to_unix_time(std::string_view utc_date) { - struct tm timeinfo; + struct tm timeinfo = {}; int ret = 0; @@ -169,7 +169,7 @@ namespace string { } int32_t to_unix_time2(std::string_view utc_date) { - struct tm timeinfo; + struct tm timeinfo = {}; int ret = 0; int microsec; @@ -200,7 +200,7 @@ namespace string { } int32_t to_local_unix_time(std::string_view utc_date) { - struct tm timeinfo; + struct tm timeinfo = {}; #ifdef WIN32 int ret = sscanf_s(utc_date.data(), "%4d-%2d-%2d %2d:%2d:%2d", @@ -223,7 +223,7 @@ namespace string { } int32_t to_local_unix_time2(std::string_view utc_date) { - struct tm timeinfo; + struct tm timeinfo = {}; int microsec; ASSERT(utc_date.size() == 28); #ifdef WIN32 @@ -247,7 +247,7 @@ namespace string { } int32_t to_unix_time(int year, int month, int day, int hour, int min, int sec) { - struct tm timeinfo; + struct tm timeinfo = {}; timeinfo.tm_year = year - 1900; timeinfo.tm_mon = month - 1; //months since January - [0,11] From afd17f74cd3157d957b9ffbaa98aa7fbd8b3b1c7 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Wed, 7 Aug 2024 22:25:50 +0200 Subject: [PATCH 013/125] Fix some edge cases with utc time parsing. --- packages/logging/source/common/String.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/logging/source/common/String.cpp b/packages/logging/source/common/String.cpp index e1b72bc6..6bd6a1ba 100644 --- a/packages/logging/source/common/String.cpp +++ b/packages/logging/source/common/String.cpp @@ -152,7 +152,7 @@ namespace string { timeinfo.tm_sec = 0; } - ASSERT(ret == 6); + ASSERT(ret <= 6); timeinfo.tm_year -= 1900; timeinfo.tm_mon -= 1; @@ -183,7 +183,7 @@ namespace string { &timeinfo.tm_year, &timeinfo.tm_mon, &timeinfo.tm_mday, &timeinfo.tm_hour, &timeinfo.tm_min, &timeinfo.tm_sec, µsec); #endif - ASSERT(ret == 6); + ASSERT(ret <= 6); timeinfo.tm_year -= 1900; timeinfo.tm_mon -= 1; @@ -210,7 +210,7 @@ namespace string { &timeinfo.tm_year, &timeinfo.tm_mon, &timeinfo.tm_mday, &timeinfo.tm_hour, &timeinfo.tm_min, &timeinfo.tm_sec); #endif - ASSERT(ret == 6); + ASSERT(ret <= 6); timeinfo.tm_year -= 1900; timeinfo.tm_mon -= 1; @@ -234,7 +234,7 @@ namespace string { &timeinfo.tm_year, &timeinfo.tm_mon, &timeinfo.tm_mday, &timeinfo.tm_hour, &timeinfo.tm_min, &timeinfo.tm_sec, µsec); #endif - ASSERT(ret == 6); + ASSERT(ret <= 6); timeinfo.tm_year -= 1900; timeinfo.tm_mon -= 1; From 0063b13f1cd0c3100603bd5c3c99d4f6d867c4a4 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Thu, 8 Aug 2024 17:14:03 +0200 Subject: [PATCH 014/125] Update ldeps. --- deps/ldeps | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/ldeps b/deps/ldeps index c95a20fc..97fbdeab 160000 --- a/deps/ldeps +++ b/deps/ldeps @@ -1 +1 @@ -Subproject commit c95a20fc7a185e634e4e4a98249b0d4894092785 +Subproject commit 97fbdeaba768cc14ca4cb68633e0a99baba01d42 From 2923ac43fb5d3457f749841a8dc26c2aec9e39c8 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Fri, 9 Aug 2024 20:49:33 +0200 Subject: [PATCH 015/125] Fix unintended implicit type conversion. --- packages/tools/include/tools/signals/Filters.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/tools/include/tools/signals/Filters.h b/packages/tools/include/tools/signals/Filters.h index 0fa13bf4..d615e405 100644 --- a/packages/tools/include/tools/signals/Filters.h +++ b/packages/tools/include/tools/signals/Filters.h @@ -165,7 +165,7 @@ namespace l::signals { T mCutoff = this->mData[0]; T mResonance = this->mData[1]; T cutoff = mCutoff * mCutoff; - double rc = 1 - mResonance * cutoff; + T rc = 1 - mResonance * cutoff; mFilterState[0] = rc * mFilterState[0] - cutoff * (mFilterState[1] + inVal); mFilterState[1] = rc * mFilterState[1] + cutoff * mFilterState[0]; return -mFilterState[1]; From cb2342abaca48537810950e3f598ad3c79e1a595 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Tue, 13 Aug 2024 01:35:13 +0200 Subject: [PATCH 016/125] Return data when no filter is applied. --- packages/tools/include/tools/signals/Filters.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/tools/include/tools/signals/Filters.h b/packages/tools/include/tools/signals/Filters.h index d615e405..44b8656e 100644 --- a/packages/tools/include/tools/signals/Filters.h +++ b/packages/tools/include/tools/signals/Filters.h @@ -255,6 +255,10 @@ namespace l::signals { int32_t width = 1 + static_cast(widthModifier * (kernelSize - 2)); mFilterStateIndex = (mFilterStateIndex + 1) % width; mFilterState[mFilterStateIndex] = inVal; + + if (width == 1) { + return inVal; + } T outVal = 0.0; for (int32_t i = 0; i < width; i++) { From b1b47ea7d2e8d0d595231fa441ba3c97029c3659 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Wed, 14 Aug 2024 15:46:15 +0200 Subject: [PATCH 017/125] Rename graphnode to nodegraph everywhere. --- .../GraphNode.h => nodegraph/NodeGraph.h} | 111 +++++++++--------- .../NodeGraphOperations.h} | 22 ++-- .../GraphNode.cpp => nodegraph/NodeGraph.cpp} | 64 +++++----- .../NodeGraphOperations.cpp} | 22 ++-- .../{GraphNodeTest.cpp => NodeGraphTest.cpp} | 52 ++++---- 5 files changed, 135 insertions(+), 136 deletions(-) rename packages/tools/include/tools/{graph/GraphNode.h => nodegraph/NodeGraph.h} (65%) rename packages/tools/include/tools/{graph/GraphOperations.h => nodegraph/NodeGraphOperations.h} (67%) rename packages/tools/source/common/{graph/GraphNode.cpp => nodegraph/NodeGraph.cpp} (69%) rename packages/tools/source/common/{graph/GraphOperations.cpp => nodegraph/NodeGraphOperations.cpp} (71%) rename packages/tools/tests/common/{GraphNodeTest.cpp => NodeGraphTest.cpp} (76%) diff --git a/packages/tools/include/tools/graph/GraphNode.h b/packages/tools/include/tools/nodegraph/NodeGraph.h similarity index 65% rename from packages/tools/include/tools/graph/GraphNode.h rename to packages/tools/include/tools/nodegraph/NodeGraph.h index b3733963..f39ada95 100644 --- a/packages/tools/include/tools/graph/GraphNode.h +++ b/packages/tools/include/tools/nodegraph/NodeGraph.h @@ -8,42 +8,49 @@ #include #include -namespace l::graph { +namespace l::nodegraph { + + enum class DataType { + FLOAT32, + INT32, + BITFIELD32 + }; + + enum class InputType { + INPUT_NODE, + INPUT_CONSTANT, + INPUT_VALUE + }; bool IsValidInOutNum(int8_t inoutNum, size_t inoutSize); - struct GraphNodeOutput { + struct NodeGraphOutput { float mOutput = 0.0f; }; - class GraphNodeBase; - struct GraphNodeInput; - class GraphNodeGroup; + class NodeGraphBase; + class NodeGraphGroup; union Input { - GraphNodeBase* mInputNode = nullptr; + NodeGraphBase* mInputNode = nullptr; float* mInputFloat; float mInputFloatConstant; int32_t* mInputInt; int32_t mInputIntConstant; }; - enum class DataType { - FLOAT32, - INT32, - BITFIELD32 - }; + struct NodeGraphInput { + Input mInput; + InputType mInputType = InputType::INPUT_NODE; + int8_t mInputFromOutputChannel = 0; - enum class InputType { - INPUT_NODE, - INPUT_CONSTANT, - INPUT_VALUE + float Get(); }; - class GraphNodeBase { + class NodeGraphBase { public: - GraphNodeBase(std::string_view name = ""); - virtual ~GraphNodeBase() = default; + NodeGraphBase(std::string_view name = ""); + virtual ~NodeGraphBase() = default; virtual void Reset(); @@ -54,8 +61,8 @@ namespace l::graph { virtual float Get(int8_t outputChannel); - virtual void SetInput(int8_t inputChannel, GraphNodeBase& source, int8_t sourceOutputChannel = 0); - virtual void SetInput(int8_t inputChannel, GraphNodeGroup& source, int8_t sourceOutputChannel = 0, bool useSourceInternalInput = true); + virtual void SetInput(int8_t inputChannel, NodeGraphBase& source, int8_t sourceOutputChannel = 0); + virtual void SetInput(int8_t inputChannel, NodeGraphGroup& source, int8_t sourceOutputChannel = 0, bool useSourceInternalInput = true); virtual void SetInput(int8_t inputChannel, float constant); virtual void SetInput(int8_t inputChannel, float* floatPtr); protected: @@ -63,20 +70,12 @@ namespace l::graph { virtual void ProcessOperation(); bool mProcessUpdateHasRun = false; - std::vector mInputs; - std::vector mOutputs; + std::vector mInputs; + std::vector mOutputs; std::string mName; }; - struct GraphNodeInput { - Input mInput; - InputType mInputType = InputType::INPUT_NODE; - int8_t mInputFromOutputChannel = 0; - - float Get(); - }; - class GraphOp { public: GraphOp(int8_t numInputs = 1, int8_t numOutputs = 1) : @@ -86,7 +85,7 @@ namespace l::graph { virtual ~GraphOp() = default; virtual void Reset() {} - virtual void Process(std::vector& mInputs, std::vector& outputs) = 0; + virtual void Process(std::vector& mInputs, std::vector& outputs) = 0; virtual void SetNumInputs(int8_t numInputs); virtual void SetNumOutputs(int8_t numOutputs); @@ -104,20 +103,20 @@ namespace l::graph { {} virtual ~GraphDataCopy() = default; - void Process(std::vector& inputs, std::vector& outputs) override; + void Process(std::vector& inputs, std::vector& outputs) override; }; template>> - class GraphNode : public GraphNodeBase { + class NodeGraph : public NodeGraphBase { public: - GraphNode(std::string_view name = "") : - GraphNodeBase(name) + NodeGraph(std::string_view name = "") : + NodeGraphBase(name) { SetNumInputs(mOperation.GetNumInputs()); SetNumOutputs(mOperation.GetNumOutputs()); } - virtual ~GraphNode() = default; + virtual ~NodeGraph() = default; void SetNumInputs(int8_t numInputs) { mInputs.resize(numInputs); @@ -130,12 +129,12 @@ namespace l::graph { } void Reset() override { - GraphNodeBase::Reset(); + NodeGraphBase::Reset(); mOperation.Reset(); } void ProcessOperation() override { - GraphNodeBase::ProcessOperation(); + NodeGraphBase::ProcessOperation(); mOperation.Process(mInputs, mOutputs); } @@ -143,48 +142,48 @@ namespace l::graph { T mOperation; }; - class GraphNodeGroup { + class NodeGraphGroup { public: - GraphNodeGroup() { + NodeGraphGroup() { SetNumInputs(1); SetNumOutputs(1); } - ~GraphNodeGroup() = default; + ~NodeGraphGroup() = default; void SetNumInputs(int8_t numInputs); void SetNumOutputs(int8_t outputCount); - void SetInput(int8_t inputChannel, GraphNodeBase& source, int8_t sourceOutputChannel); - void SetInput(int8_t inputChannel, GraphNodeGroup& source, int8_t sourceOutputChannel, bool useSourceInternalInput = false); + void SetInput(int8_t inputChannel, NodeGraphBase& source, int8_t sourceOutputChannel); + void SetInput(int8_t inputChannel, NodeGraphGroup& source, int8_t sourceOutputChannel, bool useSourceInternalInput = false); void SetInput(int8_t inputChannel, float constant); void SetInput(int8_t inputChannel, float* floatPtr); - void SetOutput(int8_t outputChannel, GraphNodeBase& source, int8_t sourceOutputChannel); - void SetOutput(int8_t outputChannel, GraphNodeGroup& source, int8_t sourceOutputChannel); + void SetOutput(int8_t outputChannel, NodeGraphBase& source, int8_t sourceOutputChannel); + void SetOutput(int8_t outputChannel, NodeGraphGroup& source, int8_t sourceOutputChannel); float Get(int8_t outputChannel); - GraphNodeBase& GetInputNode(); - GraphNodeBase& GetOutputNode(); + NodeGraphBase& GetInputNode(); + NodeGraphBase& GetOutputNode(); void Update(); protected: - GraphNode mInputNode; - GraphNode mOutputNode; + NodeGraph mInputNode; + NodeGraph mOutputNode; - std::vector> mNodes; + std::vector> mNodes; }; - class GraphNodeSchema { + class NodeGraphSchema { public: - GraphNodeSchema() = default; - ~GraphNodeSchema() = default; + NodeGraphSchema() = default; + ~NodeGraphSchema() = default; - template>> + template>> void NewNode(std::string_view name = "") { - mNodes.emplace_back(std::make_unique>(name)); + mNodes.emplace_back(std::make_unique>(name)); } protected: - std::vector> mNodes; + std::vector> mNodes; }; diff --git a/packages/tools/include/tools/graph/GraphOperations.h b/packages/tools/include/tools/nodegraph/NodeGraphOperations.h similarity index 67% rename from packages/tools/include/tools/graph/GraphOperations.h rename to packages/tools/include/tools/nodegraph/NodeGraphOperations.h index b882b797..30d9d288 100644 --- a/packages/tools/include/tools/graph/GraphOperations.h +++ b/packages/tools/include/tools/nodegraph/NodeGraphOperations.h @@ -1,5 +1,5 @@ #pragma once -#include "GraphNode.h" +#include "NodeGraph.h" #include "logging/LoggingAll.h" @@ -9,7 +9,7 @@ #include #include -namespace l::graph { +namespace l::nodegraph { /* Mathematical operations */ @@ -19,7 +19,7 @@ namespace l::graph { GraphOp(2, 1) {} virtual ~GraphNumericAdd() = default; - void Process(std::vector& inputs, std::vector& outputs) override; + void Process(std::vector& inputs, std::vector& outputs) override; }; class GraphNumericMultiply : public GraphOp { @@ -29,7 +29,7 @@ namespace l::graph { {} virtual ~GraphNumericMultiply() = default; - void Process(std::vector& inputs, std::vector& outputs) override; + void Process(std::vector& inputs, std::vector& outputs) override; }; class GraphNumericSubtract : public GraphOp { @@ -38,7 +38,7 @@ namespace l::graph { GraphOp(2, 1) {} virtual ~GraphNumericSubtract() = default; - void Process(std::vector& inputs, std::vector& outputs) override; + void Process(std::vector& inputs, std::vector& outputs) override; }; class GraphNumericNegate : public GraphOp { @@ -48,7 +48,7 @@ namespace l::graph { {} virtual ~GraphNumericNegate() = default; - void Process(std::vector& inputs, std::vector& outputs) override; + void Process(std::vector& inputs, std::vector& outputs) override; }; class GraphNumericIntegral : public GraphOp { @@ -59,7 +59,7 @@ namespace l::graph { virtual ~GraphNumericIntegral() = default; void Reset() override; - void Process(std::vector& inputs, std::vector& outputs) override; + void Process(std::vector& inputs, std::vector& outputs) override; protected: float mOutput = 0.0f; @@ -74,7 +74,7 @@ namespace l::graph { {} virtual ~GraphLogicalAnd() = default; - void Process(std::vector& inputs, std::vector& outputs) override; + void Process(std::vector& inputs, std::vector& outputs) override; }; class GraphLogicalOr : public GraphOp { @@ -84,7 +84,7 @@ namespace l::graph { {} virtual ~GraphLogicalOr() = default; - void Process(std::vector& inputs, std::vector& outputs) override; + void Process(std::vector& inputs, std::vector& outputs) override; }; class GraphLogicalXor : public GraphOp { @@ -94,7 +94,7 @@ namespace l::graph { {} virtual ~GraphLogicalXor() = default; - void Process(std::vector& inputs, std::vector& outputs) override; + void Process(std::vector& inputs, std::vector& outputs) override; }; /* Stateful filtering operations */ @@ -107,7 +107,7 @@ namespace l::graph { virtual ~GraphFilterLowpass() = default; void Reset() override; - void Process(std::vector& inputs, std::vector& outputs) override; + void Process(std::vector& inputs, std::vector& outputs) override; protected: float mState0 = 0.0f; diff --git a/packages/tools/source/common/graph/GraphNode.cpp b/packages/tools/source/common/nodegraph/NodeGraph.cpp similarity index 69% rename from packages/tools/source/common/graph/GraphNode.cpp rename to packages/tools/source/common/nodegraph/NodeGraph.cpp index 5a375e08..c2da42a4 100644 --- a/packages/tools/source/common/graph/GraphNode.cpp +++ b/packages/tools/source/common/nodegraph/NodeGraph.cpp @@ -1,29 +1,29 @@ -#include "tools/graph/GraphNode.h" +#include "tools/nodegraph/NodeGraph.h" #include "logging/Log.h" -namespace l::graph { +namespace l::nodegraph { bool IsValidInOutNum(int8_t inoutNum, size_t inoutSize) { return inoutNum >= 0 && inoutSize < 256u && inoutNum < static_cast(inoutSize); } - GraphNodeBase::GraphNodeBase(std::string_view name) : + NodeGraphBase::NodeGraphBase(std::string_view name) : mName(name) { mInputs.resize(1); mOutputs.resize(1); } - void GraphNodeBase::SetNumInputs(int8_t numInputs) { + void NodeGraphBase::SetNumInputs(int8_t numInputs) { mInputs.resize(numInputs); } - void GraphNodeBase::SetNumOutputs(int8_t outputCount) { + void NodeGraphBase::SetNumOutputs(int8_t outputCount) { mOutputs.resize(outputCount); } - void GraphNodeBase::Reset() { + void NodeGraphBase::Reset() { for (auto& link : mInputs) { if (link.mInputType == InputType::INPUT_NODE && link.mInput.mInputNode != nullptr) { link.mInput.mInputNode->Reset(); @@ -31,7 +31,7 @@ namespace l::graph { } } - void GraphNodeBase::PreUpdate() { + void NodeGraphBase::PreUpdate() { mProcessUpdateHasRun = false; for (auto& link : mInputs) { if (link.mInputType == InputType::INPUT_NODE && link.mInput.mInputNode != nullptr) { @@ -40,12 +40,12 @@ namespace l::graph { } } - void GraphNodeBase::Update() { + void NodeGraphBase::Update() { PreUpdate(); ProcessOperation(); } - void GraphNodeBase::ProcessOperation() { + void NodeGraphBase::ProcessOperation() { if (mProcessUpdateHasRun) { return; } @@ -57,19 +57,19 @@ namespace l::graph { mProcessUpdateHasRun = true; } - float GraphNodeBase::Get(int8_t outputChannel) { + float NodeGraphBase::Get(int8_t outputChannel) { ASSERT(IsValidInOutNum(outputChannel, mOutputs.size())); return mOutputs.at(outputChannel).mOutput; } - void GraphNodeBase::SetInput(int8_t inputChannel, GraphNodeBase& source, int8_t sourceOutputChannel) { + void NodeGraphBase::SetInput(int8_t inputChannel, NodeGraphBase& source, int8_t sourceOutputChannel) { ASSERT(IsValidInOutNum(inputChannel, mInputs.size())); Input input; input.mInputNode = &source; - mInputs.at(inputChannel) = GraphNodeInput{ std::move(input), InputType::INPUT_NODE, sourceOutputChannel }; + mInputs.at(inputChannel) = NodeGraphInput{ std::move(input), InputType::INPUT_NODE, sourceOutputChannel }; } - void GraphNodeBase::SetInput(int8_t inputChannel, GraphNodeGroup& source, int8_t sourceOutputChannel, bool useSourceInternalInput) { + void NodeGraphBase::SetInput(int8_t inputChannel, NodeGraphGroup& source, int8_t sourceOutputChannel, bool useSourceInternalInput) { ASSERT(IsValidInOutNum(inputChannel, mInputs.size())); Input input; if (useSourceInternalInput) { @@ -78,21 +78,21 @@ namespace l::graph { else { input.mInputNode = &source.GetOutputNode(); } - mInputs.at(inputChannel) = GraphNodeInput{ std::move(input), InputType::INPUT_NODE, sourceOutputChannel }; + mInputs.at(inputChannel) = NodeGraphInput{ std::move(input), InputType::INPUT_NODE, sourceOutputChannel }; } - void GraphNodeBase::SetInput(int8_t inputChannel, float constant) { + void NodeGraphBase::SetInput(int8_t inputChannel, float constant) { ASSERT(IsValidInOutNum(inputChannel, mInputs.size())); Input input; input.mInputFloatConstant = constant; - mInputs.at(inputChannel) = GraphNodeInput{ std::move(input), InputType::INPUT_CONSTANT, 0 }; + mInputs.at(inputChannel) = NodeGraphInput{ std::move(input), InputType::INPUT_CONSTANT, 0 }; } - void GraphNodeBase::SetInput(int8_t inputChannel, float* floatPtr) { + void NodeGraphBase::SetInput(int8_t inputChannel, float* floatPtr) { ASSERT(IsValidInOutNum(inputChannel, mInputs.size())); Input input; input.mInputFloat = floatPtr; - mInputs.at(inputChannel) = GraphNodeInput{ std::move(input), InputType::INPUT_VALUE, 0 }; + mInputs.at(inputChannel) = NodeGraphInput{ std::move(input), InputType::INPUT_VALUE, 0 }; } void GraphOp::SetNumInputs(int8_t numInputs) { @@ -111,13 +111,13 @@ namespace l::graph { return mNumOutputs; } - void GraphDataCopy::Process(std::vector& inputs, std::vector& outputs) { + void GraphDataCopy::Process(std::vector& inputs, std::vector& outputs) { for (size_t i = 0; i < inputs.size() && i < outputs.size(); i++) { outputs.at(i).mOutput = inputs.at(i).Get(); } } - float GraphNodeInput::Get() { + float NodeGraphInput::Get() { if (mInputType == InputType::INPUT_NODE) { if (mInput.mInputNode != nullptr) { return mInput.mInputNode->Get(mInputFromOutputChannel); @@ -135,53 +135,53 @@ namespace l::graph { return 0.0f; } - void GraphNodeGroup::SetNumInputs(int8_t numInputs) { + void NodeGraphGroup::SetNumInputs(int8_t numInputs) { mInputNode.SetNumInputs(numInputs); mInputNode.SetNumOutputs(numInputs); } - void GraphNodeGroup::SetNumOutputs(int8_t outputCount) { + void NodeGraphGroup::SetNumOutputs(int8_t outputCount) { mOutputNode.SetNumInputs(outputCount); mOutputNode.SetNumOutputs(outputCount); } - void GraphNodeGroup::SetInput(int8_t inputChannel, GraphNodeBase& source, int8_t sourceOutputChannel) { + void NodeGraphGroup::SetInput(int8_t inputChannel, NodeGraphBase& source, int8_t sourceOutputChannel) { mInputNode.SetInput(inputChannel, source, sourceOutputChannel); } - void GraphNodeGroup::SetInput(int8_t inputChannel, GraphNodeGroup& source, int8_t sourceOutputChannel, bool useSourceInternalInput) { + void NodeGraphGroup::SetInput(int8_t inputChannel, NodeGraphGroup& source, int8_t sourceOutputChannel, bool useSourceInternalInput) { mInputNode.SetInput(inputChannel, source, sourceOutputChannel, useSourceInternalInput); } - void GraphNodeGroup::SetInput(int8_t inputChannel, float constant) { + void NodeGraphGroup::SetInput(int8_t inputChannel, float constant) { mInputNode.SetInput(inputChannel, constant); } - void GraphNodeGroup::SetInput(int8_t inputChannel, float* floatPtr) { + void NodeGraphGroup::SetInput(int8_t inputChannel, float* floatPtr) { mInputNode.SetInput(inputChannel, floatPtr); } - void GraphNodeGroup::SetOutput(int8_t outputChannel, GraphNodeBase& source, int8_t sourceOutputChannel) { + void NodeGraphGroup::SetOutput(int8_t outputChannel, NodeGraphBase& source, int8_t sourceOutputChannel) { mOutputNode.SetInput(outputChannel, source, sourceOutputChannel); } - void GraphNodeGroup::SetOutput(int8_t outputChannel, GraphNodeGroup& source, int8_t sourceOutputChannel) { + void NodeGraphGroup::SetOutput(int8_t outputChannel, NodeGraphGroup& source, int8_t sourceOutputChannel) { mOutputNode.SetInput(outputChannel, source, sourceOutputChannel, false); } - float GraphNodeGroup::Get(int8_t outputChannel) { + float NodeGraphGroup::Get(int8_t outputChannel) { return mOutputNode.Get(outputChannel); } - GraphNodeBase& GraphNodeGroup::GetInputNode() { + NodeGraphBase& NodeGraphGroup::GetInputNode() { return mInputNode; } - GraphNodeBase& GraphNodeGroup::GetOutputNode() { + NodeGraphBase& NodeGraphGroup::GetOutputNode() { return mOutputNode; } - void GraphNodeGroup::Update() { + void NodeGraphGroup::Update() { mOutputNode.Update(); } diff --git a/packages/tools/source/common/graph/GraphOperations.cpp b/packages/tools/source/common/nodegraph/NodeGraphOperations.cpp similarity index 71% rename from packages/tools/source/common/graph/GraphOperations.cpp rename to packages/tools/source/common/nodegraph/NodeGraphOperations.cpp index 785a1e1b..b7438925 100644 --- a/packages/tools/source/common/graph/GraphOperations.cpp +++ b/packages/tools/source/common/nodegraph/NodeGraphOperations.cpp @@ -1,30 +1,30 @@ -#include "tools/graph/GraphOperations.h" +#include "tools/nodegraph/NodeGraphOperations.h" #include "logging/Log.h" -namespace l::graph { +namespace l::nodegraph { /* Mathematical operations */ - void GraphNumericAdd::Process(std::vector& inputs, std::vector& outputs) { + void GraphNumericAdd::Process(std::vector& inputs, std::vector& outputs) { ASSERT(inputs.size() == static_cast(mNumInputs)); ASSERT(outputs.size() == static_cast(mNumOutputs)); outputs.at(0).mOutput = inputs.at(0).Get() + inputs.at(1).Get(); } - void GraphNumericMultiply::Process(std::vector& inputs, std::vector& outputs) { + void GraphNumericMultiply::Process(std::vector& inputs, std::vector& outputs) { ASSERT(inputs.size() == static_cast(mNumInputs)); ASSERT(outputs.size() == static_cast(mNumOutputs)); outputs.at(0).mOutput = inputs.at(0).Get() * inputs.at(1).Get(); } - void GraphNumericSubtract::Process(std::vector& inputs, std::vector& outputs) { + void GraphNumericSubtract::Process(std::vector& inputs, std::vector& outputs) { ASSERT(inputs.size() == static_cast(mNumInputs)); ASSERT(outputs.size() == static_cast(mNumOutputs)); outputs.at(0).mOutput = inputs.at(0).Get() - inputs.at(1).Get(); } - void GraphNumericNegate::Process(std::vector& inputs, std::vector& outputs) { + void GraphNumericNegate::Process(std::vector& inputs, std::vector& outputs) { ASSERT(inputs.size() == static_cast(mNumInputs)); ASSERT(outputs.size() == static_cast(mNumOutputs)); outputs.at(0).mOutput = -inputs.at(0).Get(); @@ -34,7 +34,7 @@ namespace l::graph { mOutput = 0.0f; } - void GraphNumericIntegral::Process(std::vector& inputs, std::vector& outputs) { + void GraphNumericIntegral::Process(std::vector& inputs, std::vector& outputs) { ASSERT(inputs.size() == static_cast(mNumInputs)); ASSERT(outputs.size() == static_cast(mNumOutputs)); mOutput += inputs.at(0).Get(); @@ -43,7 +43,7 @@ namespace l::graph { /* Logical operations */ - void GraphLogicalAnd::Process(std::vector& inputs, std::vector& outputs) { + void GraphLogicalAnd::Process(std::vector& inputs, std::vector& outputs) { ASSERT(inputs.size() == static_cast(mNumInputs)); ASSERT(outputs.size() == static_cast(mNumOutputs)); bool input1 = inputs.at(0).Get() != 0.0f; @@ -51,7 +51,7 @@ namespace l::graph { outputs.at(0).mOutput = (input1 && input2) ? 1.0f : 0.0f; } - void GraphLogicalOr::Process(std::vector& inputs, std::vector& outputs) { + void GraphLogicalOr::Process(std::vector& inputs, std::vector& outputs) { ASSERT(inputs.size() == static_cast(mNumInputs)); ASSERT(outputs.size() == static_cast(mNumOutputs)); bool input1 = inputs.at(0).Get() != 0.0f; @@ -59,7 +59,7 @@ namespace l::graph { outputs.at(0).mOutput = (input1 || input2) ? 1.0f : 0.0f; } - void GraphLogicalXor::Process(std::vector& inputs, std::vector& outputs) { + void GraphLogicalXor::Process(std::vector& inputs, std::vector& outputs) { ASSERT(inputs.size() == static_cast(mNumInputs)); ASSERT(outputs.size() == static_cast(mNumOutputs)); bool input1 = inputs.at(0).Get() != 0.0f; @@ -74,7 +74,7 @@ namespace l::graph { mState1 = 0.0f; } - void GraphFilterLowpass::Process(std::vector& inputs, std::vector& outputs) { + void GraphFilterLowpass::Process(std::vector& inputs, std::vector& outputs) { ASSERT(inputs.size() == static_cast(mNumInputs)); ASSERT(outputs.size() == static_cast(mNumOutputs)); float cutoff = inputs.at(0).Get(); diff --git a/packages/tools/tests/common/GraphNodeTest.cpp b/packages/tools/tests/common/NodeGraphTest.cpp similarity index 76% rename from packages/tools/tests/common/GraphNodeTest.cpp rename to packages/tools/tests/common/NodeGraphTest.cpp index cd8108f1..d79b9ffb 100644 --- a/packages/tools/tests/common/GraphNodeTest.cpp +++ b/packages/tools/tests/common/NodeGraphTest.cpp @@ -1,15 +1,15 @@ #include "testing/Test.h" #include "logging/Log.h" -#include "tools/graph/GraphNode.h" -#include "tools/graph/GraphOperations.h" +#include "tools/graph/NodeGraph.h" +#include "tools/graph/NodeGraphOperations.h" using namespace l; -using namespace l::graph; +using namespace l::nodegraph; -TEST(GraphNode, BasicFunction) { +TEST(NodeGraph, BasicFunction) { - GraphNode node; + NodeGraph node; float in2 = 2.3f; @@ -22,11 +22,11 @@ TEST(GraphNode, BasicFunction) { return 0; } -TEST(GraphNode, SimpleAddNetwork) { +TEST(NodeGraph, SimpleAddNetwork) { - GraphNode node1; - GraphNode node2; - GraphNode nodeFinal; + NodeGraph node1; + NodeGraph node2; + NodeGraph nodeFinal; float in1 = 1.8f; float in3 = 5.2f; @@ -46,11 +46,11 @@ TEST(GraphNode, SimpleAddNetwork) { return 0; } -TEST(GraphNode, BasicMathematicalOperations) { - GraphNode node1; - GraphNode node2; - GraphNode node3; - GraphNode nodeOutput; +TEST(NodeGraph, BasicMathematicalOperations) { + NodeGraph node1; + NodeGraph node2; + NodeGraph node3; + NodeGraph nodeOutput; float in1 = 1.8f; float in3 = 2.0f; @@ -73,8 +73,8 @@ TEST(GraphNode, BasicMathematicalOperations) { return 0; } -TEST(GraphNode, NumericIntegral) { - GraphNode nodeIntegral; +TEST(NodeGraph, NumericIntegral) { + NodeGraph nodeIntegral; float input; nodeIntegral.SetInput(0, &input); @@ -91,8 +91,8 @@ TEST(GraphNode, NumericIntegral) { return 0; } -TEST(GraphNode, FilterLowpass) { - GraphNode nodeLowpass; +TEST(NodeGraph, FilterLowpass) { + NodeGraph nodeLowpass; float cutoff = 0.8f; float resonance = 0.1f; @@ -114,12 +114,12 @@ TEST(GraphNode, FilterLowpass) { return 0; } -TEST(GraphNode, GraphGroups) { +TEST(NodeGraph, GraphGroups) { - GraphNodeGroup group; + NodeGraphGroup group; - GraphNode nodeLowpass1; - GraphNode nodeLowpass2; + NodeGraph nodeLowpass1; + NodeGraph nodeLowpass2; float cutoff = 0.8f; float resonance = 0.0001f; @@ -151,8 +151,8 @@ TEST(GraphNode, GraphGroups) { group.SetOutput(1, nodeLowpass2, 0); } - GraphNodeGroup group2; - GraphNode copyNode; + NodeGraphGroup group2; + NodeGraph copyNode; { // wire sequential group with a simple copy node group2.SetNumInputs(2); @@ -181,9 +181,9 @@ TEST(GraphNode, GraphGroups) { return 0; } -TEST(GraphNode, SchemaBasic) { +TEST(NodeGraph, SchemaBasic) { - GraphNodeSchema schema; + NodeGraphSchema schema; schema.NewNode(); return 0; From 61e58779fd4172d83b20ca91fd2cf831a83f7205 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Wed, 14 Aug 2024 15:51:15 +0200 Subject: [PATCH 018/125] Update node graph test. --- packages/tools/tests/common/NodeGraphTest.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/tools/tests/common/NodeGraphTest.cpp b/packages/tools/tests/common/NodeGraphTest.cpp index d79b9ffb..85068d38 100644 --- a/packages/tools/tests/common/NodeGraphTest.cpp +++ b/packages/tools/tests/common/NodeGraphTest.cpp @@ -1,8 +1,8 @@ #include "testing/Test.h" #include "logging/Log.h" -#include "tools/graph/NodeGraph.h" -#include "tools/graph/NodeGraphOperations.h" +#include "tools/nodegraph/NodeGraph.h" +#include "tools/nodegraph/NodeGraphOperations.h" using namespace l; using namespace l::nodegraph; From 5acbb9c1acaaccf4be5cc0edd43f8e7822346d4a Mon Sep 17 00:00:00 2001 From: lnd3 Date: Thu, 15 Aug 2024 22:13:39 +0200 Subject: [PATCH 019/125] Add optional new frame function for the imgui integration code. --- .../rendering/include/rendering/ImguiSupport.h | 3 +++ .../rendering/source/common/ImguiSupport.cpp | 16 ++++++++++++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/packages/rendering/include/rendering/ImguiSupport.h b/packages/rendering/include/rendering/ImguiSupport.h index f9f34c49..a4763bf4 100644 --- a/packages/rendering/include/rendering/ImguiSupport.h +++ b/packages/rendering/include/rendering/ImguiSupport.h @@ -27,10 +27,13 @@ namespace rendering { ~GLFWImguiHandle(); void SetGuiBuilder(ImguiHandler builder); + + void NewFrame(); void Render(); protected: GLFWwindow* mParent; ImguiHandler mBuilder; + bool mNewFrame; }; std::unique_ptr CreateImgui( diff --git a/packages/rendering/source/common/ImguiSupport.cpp b/packages/rendering/source/common/ImguiSupport.cpp index 7eb7b34f..232261af 100644 --- a/packages/rendering/source/common/ImguiSupport.cpp +++ b/packages/rendering/source/common/ImguiSupport.cpp @@ -31,7 +31,9 @@ namespace l { GLFWImguiHandle::GLFWImguiHandle( GLFWwindow* parent - ) : mParent(parent) + ) : mParent(parent), + mBuilder(nullptr), + mNewFrame(false) { const char* glsl_version = "#version 130"; @@ -69,7 +71,7 @@ namespace l { ImGui::DestroyContext(); } - void GLFWImguiHandle::Render() { + void GLFWImguiHandle::NewFrame() { // Start the Dear ImGui frame ImGui_ImplOpenGL3_NewFrame(); ImGui_ImplGlfw_NewFrame(); @@ -78,6 +80,14 @@ namespace l { ImGui::CaptureKeyboardFromApp(); ImGui::CaptureMouseFromApp(); + mNewFrame = true; + } + + void GLFWImguiHandle::Render() { + if (!mNewFrame) { + NewFrame(); + } + if (mBuilder) { mBuilder(*this); } @@ -98,6 +108,8 @@ namespace l { ImGui::RenderPlatformWindowsDefault(); glfwMakeContextCurrent(backup_current_context); } + + mNewFrame = false; } void GLFWImguiHandle::SetGuiBuilder(ImguiHandler builder) { From 8573047c9249506077d7043ca936451d07655da0 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Sun, 18 Aug 2024 22:40:28 +0200 Subject: [PATCH 020/125] Add ui container. --- .../include/rendering/ui/UIContainer.h | 157 +++++++++++ .../source/common/ui/UIContainer.cpp | 266 ++++++++++++++++++ 2 files changed, 423 insertions(+) create mode 100644 packages/rendering/include/rendering/ui/UIContainer.h create mode 100644 packages/rendering/source/common/ui/UIContainer.cpp diff --git a/packages/rendering/include/rendering/ui/UIContainer.h b/packages/rendering/include/rendering/ui/UIContainer.h new file mode 100644 index 00000000..83f566e1 --- /dev/null +++ b/packages/rendering/include/rendering/ui/UIContainer.h @@ -0,0 +1,157 @@ +#pragma once + +#include "logging/LoggingAll.h" + +#define GLFW_INCLUDE_NONE +#include + +#include "imgui/imgui.h" +#include "imgui/imgui_internal.h" +#include "imgui/imgui_impl_glfw.h" +#include "imgui/imgui_impl_opengl3.h" +#include "implot/implot.h" +#include "implot/implot_internal.h" + +#include + +namespace l::ui { + + ImVec2 DragMovement(const ImVec2& prevPos, const ImVec2& curPos, float curScale); + + struct InputState { + ImVec2 mRootPos; + ImVec2 mCurPos; + ImVec2 mPrevPos; + float mScroll = 0.0f; + bool mStarted = false; + bool mStopped = false; + + ImVec2 GetLocalPos() const { + return ImVec2(mCurPos.x - mRootPos.x, mCurPos.y - mRootPos.y); + } + }; + + struct ContainerArea { + ImVec2 mPosition; + ImVec2 mSize; + float mScale = 1.0f; + + ImVec2 GetPositionAtSize() const { + return ImVec2(mPosition.x + mSize.x, mPosition.y + mSize.y); + } + + ImVec2 Transform(const ImVec2& rootPos, const ImVec2& p) const { + ImVec2 transformed; + transformed.x = rootPos.x + mPosition.x + p.x * mScale; + transformed.y = rootPos.y + mPosition.y + p.y * mScale; + return transformed; + } + }; + + bool Overlap(const ImVec2& p, const ImVec2& pMin, const ImVec2& pMax); + bool Overlap(const ImVec2 rootPos, const ImVec2& p, const ImVec2& pMin, const ImVec2& pMax, const ContainerArea& parent); + + class UIContainer; + + class UIVisitor { + public: + virtual bool Visit(UIContainer&, const InputState&, const ContainerArea&) { + return false; + } + }; + + const uint32_t UIContainer_ReservedMask = 0x0000000f; + const uint32_t UIContainer_ConfigMask = 0x000ffff0; + const uint32_t UIContainer_CustomMask = 0xfff00000; + + const uint32_t UIContainer_VisitAll = 0x00000000; + const uint32_t UIContainer_ExitOnAccept = 0x00000001; + + const uint32_t UIContainer_Reserved1 = 0x00000002; + const uint32_t UIContainer_Reserved2 = 0x00000004; + const uint32_t UIContainer_Reserved3 = 0x00000008; + + const uint32_t UIContainer_RenderFlag = 0x00000010; + const uint32_t UIContainer_DragFlag = 0x00000020; // Can be dragged regardless of size + const uint32_t UIContainer_ZoomFlag = 0x00000040; // Can be scaled, zoomed in/out + const uint32_t UIContainer_MoveFlag = 0x00000080; // Can be moved when grabbed + const uint32_t UIContainer_ResizeFlag = 0x00000100; // Can be resized when grabbing bottom right corner + const uint32_t UIContainer_InputFlag = 0x00000200; + const uint32_t UIContainer_OutputFlag = 0x00000400; + + + class UIContainer { + public: + UIContainer(std::string name, uint32_t flags = 0) : mName(name), mConfigFlags(flags) {} + ~UIContainer() = default; + + bool Accept(UIVisitor& visitor, const InputState& input, uint32_t flags = 0); + bool Accept(UIVisitor& visitor, const InputState& input, const ContainerArea& parent, uint32_t flags = 0); + void Add(UIContainer* container, int32_t i = -1); + void Remove(int32_t i); + void Move(ImVec2 localChange); + void Resize(ImVec2 localChange); + void Rescale(float localChange); + void ClearNotifications(); + void Notification(uint32_t flag); + bool HasNotification(uint32_t flag); + void SetPosition(ImVec2 p); + void SetSize(ImVec2 s); + void SetContainerArea(const ContainerArea& area); + ImVec2 GetPosition(bool untransformed = false); + ImVec2 GetPositionAtSize(ImVec2 offset = ImVec2(), bool untransformed = false); + ImVec2 GetSize(bool untransformed = false); + float GetScale(); + void DebugLog(); + + protected: + std::string mName; + ContainerArea mArea; + uint32_t mConfigFlags = 0; // Active visitor flags + uint32_t mNotificationFlags = 0; // Notification flags for ux feedback (resizing box animation etc) + + std::vector mContent; + }; + + class UIZoom : public UIVisitor { + public: + virtual bool Visit(UIContainer& container, const InputState& input, const ContainerArea& parent); + }; + + class UIDrag : public UIVisitor { + public: + virtual bool Visit(UIContainer& container, const InputState& input, const ContainerArea& parent); + protected: + bool mDragging = false; + UIContainer* mCurrentContainer = nullptr; + }; + + class UIMove : public UIVisitor { + public: + virtual bool Visit(UIContainer& container, const InputState& input, const ContainerArea& parent); + protected: + bool mMoving = false; + UIContainer* mCurrentContainer = nullptr; + }; + + class UIResize : public UIVisitor { + public: + virtual bool Visit(UIContainer& container, const InputState& input, const ContainerArea& parent); + protected: + bool mResizing = false; + float mResizeAreaSize = 8.0f; + UIContainer* mCurrentContainer = nullptr; + }; + + class UIDraw : public UIVisitor { + public: + UIDraw(ImDrawList* drawList) : mDrawList(drawList) {} + ~UIDraw() = default; + + void DebugLog(); + virtual bool Visit(UIContainer& container, const InputState& input, const ContainerArea& parent); + protected: + ImDrawList* mDrawList; + bool mDebugLog = false; + }; +} diff --git a/packages/rendering/source/common/ui/UIContainer.cpp b/packages/rendering/source/common/ui/UIContainer.cpp new file mode 100644 index 00000000..9f04f28f --- /dev/null +++ b/packages/rendering/source/common/ui/UIContainer.cpp @@ -0,0 +1,266 @@ +#include "rendering/ui/UIContainer.h" + +#include + +namespace l::ui { + + ImVec2 DragMovement(const ImVec2& prevPos, const ImVec2& curPos, float curScale) { + ImVec2 move = curPos; + move.x -= prevPos.x; + move.y -= prevPos.y; + move.x = move.x / curScale; + move.y = move.y / curScale; + return move; + } + + bool Overlap(const ImVec2& p, const ImVec2& pMin, const ImVec2& pMax) { + return pMin.x < p.x && pMin.y < p.y && pMax.x > p.x && pMax.y > p.y; + } + + bool Overlap(const ImVec2 rootPos, const ImVec2& p, const ImVec2& pMin, const ImVec2& pMax, const ContainerArea& parent) { + ImVec2 pMinT = parent.Transform(rootPos, pMin); + ImVec2 pMaxT = parent.Transform(rootPos, pMax); + return pMinT.x < p.x && pMinT.y < p.y && pMaxT.x > p.x && pMaxT.y > p.y; + } + + bool UIContainer::Accept(UIVisitor& visitor, const InputState& input, uint32_t flags) { + ContainerArea current; + return Accept(visitor, input, current, flags); + } + + bool UIContainer::Accept(UIVisitor& visitor, const InputState& input, const ContainerArea& parent, uint32_t flags) { + ContainerArea current; + // compute local scale + current.mScale = mArea.mScale * parent.mScale; + + // scale local position with accumulated world scale and add to accumulated world position + current.mPosition.x = (parent.mPosition.x + mArea.mPosition.x * mArea.mScale) * parent.mScale; + current.mPosition.y = (parent.mPosition.y + mArea.mPosition.y * mArea.mScale) * parent.mScale; + + // scale local size with accumulated world scale + current.mSize.x = mArea.mSize.x * mArea.mScale * parent.mScale; + current.mSize.y = mArea.mSize.y * mArea.mScale * parent.mScale; + + bool exitOnAccept = (UIContainer_ExitOnAccept & flags) == UIContainer_ExitOnAccept; + + for (auto& content : mContent) { + if (content->Accept(visitor, input, current, flags)) { + if (exitOnAccept) { + return true; + } + } + } + + if ((flags & mConfigFlags) == (flags & UIContainer_ConfigMask)) { + return visitor.Visit(*this, input, parent); + } + return false; + } + + void UIContainer::Add(UIContainer* container, int32_t i) { + if (i < 0) { + mContent.push_back(container); + } + else { + ASSERT(i < mContent.size()); + mContent.insert(mContent.begin() + i, container); + } + } + + void UIContainer::Remove(int32_t i) { + ASSERT(i >= 0 && i < mContent.size()); + mContent.erase(mContent.begin() + i); + } + + void UIContainer::Move(ImVec2 localChange) { + mArea.mPosition.x += localChange.x; + mArea.mPosition.y += localChange.y; + } + + void UIContainer::Resize(ImVec2 localChange) { + mArea.mSize.x += localChange.x; + mArea.mSize.y += localChange.y; + mArea.mSize.x = mArea.mSize.x < 1.0f ? 1.0f : mArea.mSize.x; + mArea.mSize.y = mArea.mSize.y < 1.0f ? 1.0f : mArea.mSize.y; + } + + void UIContainer::Rescale(float localChange) { + mArea.mScale *= localChange; + } + + void UIContainer::ClearNotifications() { + mNotificationFlags = 0; + } + + void UIContainer::Notification(uint32_t flag) { + mNotificationFlags |= flag; + } + + bool UIContainer::HasNotification(uint32_t flag) { + return (mNotificationFlags & flag) == flag; + } + + void UIContainer::SetPosition(ImVec2 p) { + mArea.mPosition = p; + } + + void UIContainer::SetSize(ImVec2 s) { + mArea.mSize = s; + } + + void UIContainer::SetContainerArea(const ContainerArea& area) { + mArea = area; + } + + ImVec2 UIContainer::GetPosition(bool untransformed) { + if (untransformed) { + return mArea.mPosition; + } + return ImVec2(mArea.mPosition.x * mArea.mScale, mArea.mPosition.y * mArea.mScale); + } + + ImVec2 UIContainer::GetPositionAtSize(ImVec2 offset, bool untransformed) { + if (untransformed) { + return ImVec2(mArea.mPosition.x + mArea.mSize.x + offset.x, mArea.mPosition.y + mArea.mSize.y + offset.y); + } + return ImVec2((mArea.mPosition.x + mArea.mSize.x + offset.x) * mArea.mScale, (mArea.mPosition.y + mArea.mSize.y + offset.y) * mArea.mScale); + } + + ImVec2 UIContainer::GetSize(bool untransformed) { + if (untransformed) { + return mArea.mSize; + } + return ImVec2(mArea.mSize.x * mArea.mScale, mArea.mSize.y * mArea.mScale); + } + + float UIContainer::GetScale() { + return mArea.mScale; + } + + void UIContainer::DebugLog() { + LOG(LogDebug) << "UIContainer: " << mName << ", [" << mArea.mScale << "][" << mArea.mPosition.x << ", " << mArea.mPosition.y << "][" << mArea.mSize.x << ", " << mArea.mSize.y << "]"; + } + + + + bool UIZoom::Visit(UIContainer& container, const InputState& input, const ContainerArea& parent) { + if (input.mScroll != 0.0f) { + float scaleChange = 1.0f; + float scaleDelta = 0.1f; + scaleChange = 1.0f + scaleDelta * input.mScroll; + if (input.mScroll < 0.0f) { + scaleChange = 1.0f / (1.0f - scaleDelta * input.mScroll); + } + if (container.GetScale() > 100.0f && scaleChange > 1.0f || container.GetScale() < 0.01f && scaleChange < 1.0f) { + return true; + } + + ImVec2 mousePos = input.GetLocalPos(); + ImVec2 localMousePos = ImVec2(parent.mPosition.x - mousePos.x, parent.mPosition.y - mousePos.y); + ImVec2 p = container.GetPosition(); + container.Rescale(scaleChange); + p.x = ((p.x + localMousePos.x) * scaleChange - localMousePos.x) / container.GetScale(); + p.y = ((p.y + localMousePos.y) * scaleChange - localMousePos.y) / container.GetScale(); + container.SetPosition(p); + return true; + } + return false; + } + + bool UIDrag::Visit(UIContainer& container, const InputState& input, const ContainerArea& parent) { + if (input.mStarted && !mDragging) { + if (input.GetLocalPos().x >= 0.0f && input.GetLocalPos().y >= 0.0f) { + mDragging = true; + mCurrentContainer = &container; + } + } + if (mDragging && mCurrentContainer == &container) { + ImVec2 move = DragMovement(input.mPrevPos, input.mCurPos, parent.mScale * container.GetScale()); + container.Move(move); + container.Notification(UIContainer_DragFlag); + + if (input.mStopped) { + mDragging = false; + mCurrentContainer = nullptr; + } + return mDragging; + } + return false; + } + + bool UIMove::Visit(UIContainer& container, const InputState& input, const ContainerArea& parent) { + if (input.mStarted && !mMoving) { + if (Overlap(ImVec2(), input.GetLocalPos(), container.GetPosition(), container.GetPositionAtSize(), parent)) { + mMoving = true; + mCurrentContainer = &container; + } + } + if (mMoving && mCurrentContainer == &container) { + ImVec2 move = DragMovement(input.mPrevPos, input.mCurPos, parent.mScale); + container.Move(move); + container.Notification(UIContainer_MoveFlag); + + if (input.mStopped) { + mMoving = false; + mCurrentContainer = nullptr; + } + return mMoving; + } + return false; + } + + bool UIResize::Visit(UIContainer& container, const InputState& input, const ContainerArea& parent) { + if (input.mStarted && !mResizing) { + const float radii = mResizeAreaSize * 0.5f; + ImVec2 p = container.GetPositionAtSize(); + ImVec2 p1 = ImVec2(p.x - radii, p.y - radii); + ImVec2 p2 = ImVec2(p.x + radii, p.y + radii); + if (Overlap(ImVec2(), input.GetLocalPos(), p1, p2, parent)) { + mResizing = true; + mCurrentContainer = &container; + } + } + if (mResizing && mCurrentContainer == &container) { + ImVec2 move = DragMovement(input.mPrevPos, input.mCurPos, parent.mScale); + container.Resize(move); + container.Notification(UIContainer_ResizeFlag); + + if (input.mStopped) { + mResizing = false; + mCurrentContainer = nullptr; + container.ClearNotifications(); + } + return mResizing; + } + return false; + } + + void UIDraw::DebugLog() { + mDebugLog = true; + } + + bool UIDraw::Visit(UIContainer& container, const InputState& input, const ContainerArea& parent) { + + if (mDebugLog) { + container.DebugLog(); + } + + ImVec2 p1 = parent.Transform(input.mRootPos, container.GetPosition()); + ImVec2 p2 = parent.Transform(input.mRootPos, container.GetPositionAtSize()); + + ImVec4 colf = ImVec4(1.0f, 0.4f, 0.4f, 1.0f); + const ImU32 col = ImColor(colf); + + mDrawList->AddRect(p1, p2, col, 2.0f, ImDrawFlags_RoundCornersAll, 2.0f); + + ImVec2 p3 = parent.Transform(input.mRootPos, container.GetPositionAtSize(ImVec2(-3.0f, -3.0f))); + ImVec2 p4 = parent.Transform(input.mRootPos, container.GetPositionAtSize(ImVec2(3.0f, 3.0f))); + if (container.HasNotification(ui::UIContainer_ResizeFlag)) { + p3 = parent.Transform(input.mRootPos, container.GetPositionAtSize(ImVec2(-5.0f, -5.0f))); + p4 = parent.Transform(input.mRootPos, container.GetPositionAtSize(ImVec2(5.0f, 5.0f))); + } + mDrawList->AddRectFilled(p3, p4, col); + return false; + } + +} From b6fb8ce9e36488df73c402c32429413962b892aa Mon Sep 17 00:00:00 2001 From: lnd3 Date: Sun, 18 Aug 2024 23:11:52 +0200 Subject: [PATCH 021/125] Add an empty ui container test. --- packages/rendering/tests/common/GLFWWindowTest.cpp | 2 +- packages/rendering/tests/common/GeometryTest.cpp | 5 +---- packages/rendering/tests/common/UIContainerTest.cpp | 13 +++++++++++++ 3 files changed, 15 insertions(+), 5 deletions(-) create mode 100644 packages/rendering/tests/common/UIContainerTest.cpp diff --git a/packages/rendering/tests/common/GLFWWindowTest.cpp b/packages/rendering/tests/common/GLFWWindowTest.cpp index 909da7f0..76a87bb7 100644 --- a/packages/rendering/tests/common/GLFWWindowTest.cpp +++ b/packages/rendering/tests/common/GLFWWindowTest.cpp @@ -3,7 +3,7 @@ #include "rendering/GLFWWindow.h" -TEST(RenderingTest, GLFW) { +TEST(GLFWWindowTest, GLFW) { auto handle = l::rendering::CreateGLFW("GLFW Test window", 1024, 768); if (handle) { diff --git a/packages/rendering/tests/common/GeometryTest.cpp b/packages/rendering/tests/common/GeometryTest.cpp index 0ddfb786..71c4f8d7 100644 --- a/packages/rendering/tests/common/GeometryTest.cpp +++ b/packages/rendering/tests/common/GeometryTest.cpp @@ -1,14 +1,11 @@ #include "testing/Test.h" -#include "logging/Log.h" +#include "logging/LoggingAll.h" #include "rendering/Geometry.h" -#include "logging/String.h" TEST(GeometryTest, GeometryParsing) { l::rendering::Mesh mesh; TEST_TRUE(mesh.Load("./tests/data/modelformats/old_truck/old_truck_2.dae"), ""); - //mesh.AssimpImport("./tests/data/modelformats/old_truck/old_truck_2.dae"); - return 0; } diff --git a/packages/rendering/tests/common/UIContainerTest.cpp b/packages/rendering/tests/common/UIContainerTest.cpp new file mode 100644 index 00000000..6349cae8 --- /dev/null +++ b/packages/rendering/tests/common/UIContainerTest.cpp @@ -0,0 +1,13 @@ +#include "testing/Test.h" +#include "logging/LoggingAll.h" +#include "rendering/ui/UIContainer.h" + +TEST(UIContainerTest, BasicSetup) { + l::ui::ContainerArea windowContainer; + + + + return 0; +} + + From 919a33db02f3066a4c1f24155f1813af8874a387 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Mon, 19 Aug 2024 00:02:31 +0200 Subject: [PATCH 022/125] Fix resize hover feedback. --- .../include/rendering/ui/UIContainer.h | 12 +++--- .../source/common/ui/UIContainer.cpp | 40 +++++++++++++------ 2 files changed, 34 insertions(+), 18 deletions(-) diff --git a/packages/rendering/include/rendering/ui/UIContainer.h b/packages/rendering/include/rendering/ui/UIContainer.h index 83f566e1..536d1e67 100644 --- a/packages/rendering/include/rendering/ui/UIContainer.h +++ b/packages/rendering/include/rendering/ui/UIContainer.h @@ -16,8 +16,6 @@ namespace l::ui { - ImVec2 DragMovement(const ImVec2& prevPos, const ImVec2& curPos, float curScale); - struct InputState { ImVec2 mRootPos; ImVec2 mCurPos; @@ -40,16 +38,18 @@ namespace l::ui { return ImVec2(mPosition.x + mSize.x, mPosition.y + mSize.y); } - ImVec2 Transform(const ImVec2& rootPos, const ImVec2& p) const { + ImVec2 Transform(const ImVec2& rootPos, const ImVec2& p, ImVec2 pixelOffset = ImVec2()) const { ImVec2 transformed; - transformed.x = rootPos.x + mPosition.x + p.x * mScale; - transformed.y = rootPos.y + mPosition.y + p.y * mScale; + transformed.x = rootPos.x + mPosition.x + p.x * mScale + pixelOffset.x; + transformed.y = rootPos.y + mPosition.y + p.y * mScale + pixelOffset.y; return transformed; } }; + ImVec2 DragMovement(const ImVec2& prevPos, const ImVec2& curPos, float curScale); bool Overlap(const ImVec2& p, const ImVec2& pMin, const ImVec2& pMax); bool Overlap(const ImVec2 rootPos, const ImVec2& p, const ImVec2& pMin, const ImVec2& pMax, const ContainerArea& parent); + bool OverlapPixelArea(const ImVec2 rootPos, const ImVec2& p, const ImVec2& pCenter, const ImVec2& offset, const ContainerArea& parent); class UIContainer; @@ -71,7 +71,7 @@ namespace l::ui { const uint32_t UIContainer_Reserved2 = 0x00000004; const uint32_t UIContainer_Reserved3 = 0x00000008; - const uint32_t UIContainer_RenderFlag = 0x00000010; + const uint32_t UIContainer_DrawFlag = 0x00000010; const uint32_t UIContainer_DragFlag = 0x00000020; // Can be dragged regardless of size const uint32_t UIContainer_ZoomFlag = 0x00000040; // Can be scaled, zoomed in/out const uint32_t UIContainer_MoveFlag = 0x00000080; // Can be moved when grabbed diff --git a/packages/rendering/source/common/ui/UIContainer.cpp b/packages/rendering/source/common/ui/UIContainer.cpp index 9f04f28f..736f5699 100644 --- a/packages/rendering/source/common/ui/UIContainer.cpp +++ b/packages/rendering/source/common/ui/UIContainer.cpp @@ -23,6 +23,12 @@ namespace l::ui { return pMinT.x < p.x && pMinT.y < p.y && pMaxT.x > p.x && pMaxT.y > p.y; } + bool OverlapPixelArea(const ImVec2 rootPos, const ImVec2& p, const ImVec2& pCenter, const ImVec2& offset, const ContainerArea& parent) { + ImVec2 pMinT = parent.Transform(rootPos, pCenter, ImVec2(-offset.x, -offset.y)); + ImVec2 pMaxT = parent.Transform(rootPos, pCenter, ImVec2(offset.x, offset.y)); + return pMinT.x < p.x && pMinT.y < p.y && pMaxT.x > p.x && pMaxT.y > p.y; + } + bool UIContainer::Accept(UIVisitor& visitor, const InputState& input, uint32_t flags) { ContainerArea current; return Accept(visitor, input, current, flags); @@ -210,20 +216,28 @@ namespace l::ui { } bool UIResize::Visit(UIContainer& container, const InputState& input, const ContainerArea& parent) { - if (input.mStarted && !mResizing) { + if (!mResizing) { const float radii = mResizeAreaSize * 0.5f; ImVec2 p = container.GetPositionAtSize(); - ImVec2 p1 = ImVec2(p.x - radii, p.y - radii); - ImVec2 p2 = ImVec2(p.x + radii, p.y + radii); - if (Overlap(ImVec2(), input.GetLocalPos(), p1, p2, parent)) { - mResizing = true; + if (OverlapPixelArea(ImVec2(), input.GetLocalPos(), p, ImVec2(radii, radii), parent)) { mCurrentContainer = &container; + container.Notification(UIContainer_ResizeFlag); + + if (input.mStarted) { + mResizing = true; + } + else { + return true; + } + } + else { + mCurrentContainer = nullptr; + container.ClearNotifications(); } } if (mResizing && mCurrentContainer == &container) { ImVec2 move = DragMovement(input.mPrevPos, input.mCurPos, parent.mScale); container.Resize(move); - container.Notification(UIContainer_ResizeFlag); if (input.mStopped) { mResizing = false; @@ -244,20 +258,22 @@ namespace l::ui { if (mDebugLog) { container.DebugLog(); } + ImVec2 pTopLeft = container.GetPosition(); + ImVec2 pLowRight = container.GetPositionAtSize(); - ImVec2 p1 = parent.Transform(input.mRootPos, container.GetPosition()); - ImVec2 p2 = parent.Transform(input.mRootPos, container.GetPositionAtSize()); + ImVec2 p1 = parent.Transform(input.mRootPos, pTopLeft); + ImVec2 p2 = parent.Transform(input.mRootPos, pLowRight); ImVec4 colf = ImVec4(1.0f, 0.4f, 0.4f, 1.0f); const ImU32 col = ImColor(colf); mDrawList->AddRect(p1, p2, col, 2.0f, ImDrawFlags_RoundCornersAll, 2.0f); - ImVec2 p3 = parent.Transform(input.mRootPos, container.GetPositionAtSize(ImVec2(-3.0f, -3.0f))); - ImVec2 p4 = parent.Transform(input.mRootPos, container.GetPositionAtSize(ImVec2(3.0f, 3.0f))); + ImVec2 p3 = parent.Transform(input.mRootPos, pLowRight, ImVec2(-3.0f, -3.0f)); + ImVec2 p4 = parent.Transform(input.mRootPos, pLowRight, ImVec2(3.0f, 3.0f)); if (container.HasNotification(ui::UIContainer_ResizeFlag)) { - p3 = parent.Transform(input.mRootPos, container.GetPositionAtSize(ImVec2(-5.0f, -5.0f))); - p4 = parent.Transform(input.mRootPos, container.GetPositionAtSize(ImVec2(5.0f, 5.0f))); + p3 = parent.Transform(input.mRootPos, pLowRight, ImVec2(-5.0f, -5.0f)); + p4 = parent.Transform(input.mRootPos, pLowRight, ImVec2(5.0f, 5.0f)); } mDrawList->AddRectFilled(p3, p4, col); return false; From fb01bc5c312ed88fe5c13e0de9df216654fa3a86 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Mon, 19 Aug 2024 00:06:40 +0200 Subject: [PATCH 023/125] Fix some gnu warnings. --- packages/rendering/source/common/ui/UIContainer.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/rendering/source/common/ui/UIContainer.cpp b/packages/rendering/source/common/ui/UIContainer.cpp index 736f5699..71c4f060 100644 --- a/packages/rendering/source/common/ui/UIContainer.cpp +++ b/packages/rendering/source/common/ui/UIContainer.cpp @@ -68,13 +68,13 @@ namespace l::ui { mContent.push_back(container); } else { - ASSERT(i < mContent.size()); + ASSERT(static_cast(i) < mContent.size()); mContent.insert(mContent.begin() + i, container); } } void UIContainer::Remove(int32_t i) { - ASSERT(i >= 0 && i < mContent.size()); + ASSERT(i >= 0 && static_cast(i) < mContent.size()); mContent.erase(mContent.begin() + i); } @@ -157,7 +157,7 @@ namespace l::ui { if (input.mScroll < 0.0f) { scaleChange = 1.0f / (1.0f - scaleDelta * input.mScroll); } - if (container.GetScale() > 100.0f && scaleChange > 1.0f || container.GetScale() < 0.01f && scaleChange < 1.0f) { + if ((container.GetScale() > 100.0f && scaleChange > 1.0f) || (container.GetScale() < 0.01f && scaleChange < 1.0f)) { return true; } From 7fa43a33c1d6d29e5b1031324e5cfed3bfefde56 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Mon, 19 Aug 2024 00:28:24 +0200 Subject: [PATCH 024/125] Add missing files. --- .../rendering/include/rendering/ui/UIWindow.h | 47 +++++++ .../rendering/source/common/ui/UIWindow.cpp | 115 ++++++++++++++++++ 2 files changed, 162 insertions(+) create mode 100644 packages/rendering/include/rendering/ui/UIWindow.h create mode 100644 packages/rendering/source/common/ui/UIWindow.cpp diff --git a/packages/rendering/include/rendering/ui/UIWindow.h b/packages/rendering/include/rendering/ui/UIWindow.h new file mode 100644 index 00000000..8346f7d1 --- /dev/null +++ b/packages/rendering/include/rendering/ui/UIWindow.h @@ -0,0 +1,47 @@ +#pragma once + +#include "logging/LoggingAll.h" + +#define GLFW_INCLUDE_NONE +#include + +#include "imgui/imgui.h" +#include "imgui/imgui_internal.h" +#include "imgui/imgui_impl_glfw.h" +#include "imgui/imgui_impl_opengl3.h" +#include "implot/implot.h" +#include "implot/implot_internal.h" + +#include + +namespace l::ui { + + class UIWindow { + public: + UIWindow() : mWindowPtr(nullptr), mOpened(false), mIsHovered(false), mWindowFunction(nullptr), mContentScale(1.0f), mMoving(false) {} + ~UIWindow() = default; + + void SetAction(std::function action); + void Open(); + bool IsShowing(); + bool IsHovered(); + ImVec2 GetPosition(); + ImVec2 GetSize(); + float GetScale(); + bool TryInput(const ImVec2& prevPos, const ImVec2& curPos, float zoom, bool, bool); + bool TryPan(const ImVec2& prevPos, const ImVec2& curPos, bool moveStart, bool moveStop); + bool TryScale(const ImVec2& scalePos, float scroll); + ImVec2 Transform(ImVec2 p, bool toWorld = true); + void Show(); + protected: + ImGuiWindow* mWindowPtr; + bool mOpened; + bool mIsHovered; + ImVec2 mContentPan; + float mContentScale; + bool mMoving; + + std::function mWindowFunction; + }; + +} diff --git a/packages/rendering/source/common/ui/UIWindow.cpp b/packages/rendering/source/common/ui/UIWindow.cpp new file mode 100644 index 00000000..624bb8fc --- /dev/null +++ b/packages/rendering/source/common/ui/UIWindow.cpp @@ -0,0 +1,115 @@ +#include "rendering/ui/UIWindow.h" + +#include + +namespace l::ui { + + void UIWindow::SetAction(std::function action) { + mWindowFunction = action; + } + + void UIWindow::Open() { + mOpened = true; + } + + bool UIWindow::IsShowing() { + return mWindowPtr && mOpened; + } + + bool UIWindow::IsHovered() { + return mWindowPtr && mIsHovered; + } + + ImVec2 UIWindow::GetPosition() { + return mWindowPtr->DC.CursorPos; + } + + ImVec2 UIWindow::GetSize() { + return mWindowPtr->Size; + } + + float UIWindow::GetScale() { + return mContentScale; + } + + bool UIWindow::TryInput(const ImVec2& prevPos, const ImVec2& curPos, float zoom, bool, bool) { + if (TryPan(prevPos, curPos, ImGui::IsMouseClicked(ImGuiMouseButton_Left), ImGui::IsMouseReleased(ImGuiMouseButton_Left))) { + return true; + } + else if (TryScale(curPos, zoom)) { + return true; + } + return false; + } + + bool UIWindow::TryPan(const ImVec2& prevPos, const ImVec2& curPos, bool moveStart, bool moveStop) { + if (IsShowing()) { + if (IsHovered() && moveStart) { + mMoving = true; + } + if (mMoving) { + ImVec2 drag = curPos; + drag.x -= prevPos.x; + drag.y -= prevPos.y; + mContentPan.x += drag.x; + mContentPan.y += drag.y; + + if (moveStop) { + mMoving = false; + } + } + } + return mMoving; + } + + bool UIWindow::TryScale(const ImVec2& scalePos, float scroll) { + if (IsShowing() && IsHovered() && scroll != 0.0f) { + ImVec2 parentPos = GetPosition(); + float scaleChange = (1.0f + 0.1f * scroll); + if (mContentScale > 100.0f && scaleChange > 1.0f || mContentScale < 0.01f && scaleChange < 1.0f) { + return true; + } + mContentScale *= scaleChange; + mContentPan.x = scalePos.x + (mContentPan.x + parentPos.x - scalePos.x) * scaleChange - parentPos.x; + mContentPan.y = scalePos.y + (mContentPan.y + parentPos.y - scalePos.y) * scaleChange - parentPos.y; + return true; + } + return false; + } + + ImVec2 UIWindow::Transform(ImVec2 p, bool toWorld) { + ImVec2 parentPos = GetPosition(); + + float x = p.x * mContentScale; + float y = p.y * mContentScale; + x += mContentPan.x; + y += mContentPan.y; + if (toWorld) { + x += parentPos.x; + y += parentPos.y; + } + return ImVec2(x, y); + } + + void UIWindow::Show() { + if (mOpened) { + if (ImGui::Begin("Test primitive rendering", &mOpened)) { + if (mOpened) { + mWindowPtr = ImGui::GetCurrentWindowRead(); + mIsHovered = ImGui::IsWindowHovered();; + } + + if (mWindowPtr && mWindowFunction) { + mWindowFunction(); + } + + if (!mOpened) { + mWindowPtr = nullptr; + mIsHovered = false; + } + } + ImGui::End(); + } + } + +} From 8465189978bedef1024ab1bca8fcd44630786168 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Mon, 19 Aug 2024 00:41:30 +0200 Subject: [PATCH 025/125] Rearrange members. --- packages/rendering/include/rendering/ui/UIWindow.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/rendering/include/rendering/ui/UIWindow.h b/packages/rendering/include/rendering/ui/UIWindow.h index 8346f7d1..a265bee7 100644 --- a/packages/rendering/include/rendering/ui/UIWindow.h +++ b/packages/rendering/include/rendering/ui/UIWindow.h @@ -37,11 +37,11 @@ namespace l::ui { ImGuiWindow* mWindowPtr; bool mOpened; bool mIsHovered; - ImVec2 mContentPan; + std::function mWindowFunction; float mContentScale; bool mMoving; - std::function mWindowFunction; + ImVec2 mContentPan; }; } From 19a033f7e61a5ec646fc5170cf20241eeac33cb8 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Mon, 19 Aug 2024 01:15:02 +0200 Subject: [PATCH 026/125] Fix warning. --- packages/rendering/source/common/ui/UIWindow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/rendering/source/common/ui/UIWindow.cpp b/packages/rendering/source/common/ui/UIWindow.cpp index 624bb8fc..444cdee7 100644 --- a/packages/rendering/source/common/ui/UIWindow.cpp +++ b/packages/rendering/source/common/ui/UIWindow.cpp @@ -66,7 +66,7 @@ namespace l::ui { if (IsShowing() && IsHovered() && scroll != 0.0f) { ImVec2 parentPos = GetPosition(); float scaleChange = (1.0f + 0.1f * scroll); - if (mContentScale > 100.0f && scaleChange > 1.0f || mContentScale < 0.01f && scaleChange < 1.0f) { + if ((mContentScale > 100.0f && scaleChange > 1.0f) || (mContentScale < 0.01f && scaleChange < 1.0f)) { return true; } mContentScale *= scaleChange; From 00d69bf78af40718c035a4b566b7f66392f94076 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Mon, 19 Aug 2024 20:28:08 +0200 Subject: [PATCH 027/125] Refactor time and date conversions. --- .../source/common/ExecutorService.cpp | 6 +- packages/logging/include/logging/String.h | 41 ++- packages/logging/source/common/String.cpp | 301 ++++++++++-------- .../memory/tests/common/ContainersTest.cpp | 2 +- .../network/include/network/NetworkManager.h | 6 +- .../source/common/NetworkInterface.cpp | 4 +- packages/testing/tests/common/LoggingTest.cpp | 58 +++- 7 files changed, 260 insertions(+), 158 deletions(-) diff --git a/packages/concurrency/source/common/ExecutorService.cpp b/packages/concurrency/source/common/ExecutorService.cpp index d081e8f9..34dd8cbd 100644 --- a/packages/concurrency/source/common/ExecutorService.cpp +++ b/packages/concurrency/source/common/ExecutorService.cpp @@ -37,13 +37,13 @@ namespace l::concurrency { } void Runnable::Reschedule() { - auto time = l::string::get_unix_timestamp_ms(); + auto time = l::string::get_unix_epoch_ms(); mNextTry = time + static_cast(500 + 500 * (rand() / (float)RAND_MAX)); // retry within 1 second } void Runnable::Backoff() { mTries++; - auto time = l::string::get_unix_timestamp_ms(); + auto time = l::string::get_unix_epoch_ms(); mNextTry = time + static_cast(round(powf(static_cast(mTries), 2.5f))); // 1 sec, 3 sec, 5 sec, 8 sec, 11 sec etc } @@ -186,7 +186,7 @@ namespace l::concurrency { mCondition.wait(lock); } else { - auto time = l::string::get_unix_timestamp_ms(); + auto time = l::string::get_unix_epoch_ms(); for (uint32_t i = 0; i < mRunnables.size(); i++) { if (mRunnables.at(i)->CanRun(time)) { runnable = std::move(mRunnables.at(i)); diff --git a/packages/logging/include/logging/String.h b/packages/logging/include/logging/String.h index 1f589488..601eedaf 100644 --- a/packages/logging/include/logging/String.h +++ b/packages/logging/include/logging/String.h @@ -11,17 +11,34 @@ namespace l { namespace string { + int32_t get_local_timezone(); + int32_t get_local_daylight_savings(bool inHours = false); - tm get_time_info(int32_t unixtime, bool adjustYearAndMonth = false); - int32_t get_unix_time(const tm& timeinfo, bool adjustYearAndMonth = false); - void get_time_info(int32_t* fullDateAndTime, int32_t unixtime); - int32_t get_unix_time(int32_t* fullDateAndTime); + time_t convert_to_local_time_from_utc_time(const time_t time); + time_t convert_to_utc_time_from_local_time(const time_t time); - size_t get_time_string(char* buf, size_t maxSize); - std::string get_time_string(const int64_t unixtime, std::string_view format = "%Y-%m-%d %X"); + void convert_to_tm(const time_t time, tm* timeinfo, bool adjustYearAndMonth = true); + time_t convert_to_time(const tm* timeinfo, bool adjustYearAndMonth = true); + void convert_to_local_tm_from_utc_time(const time_t utctime, tm* localtimeinfo, bool adjustYearAndMonth = true); + time_t convert_to_utc_time_from_local_tm(const tm* localtimeinfo, bool adjustYearAndMonth = true); + + int32_t get_unix_epoch(); + int64_t get_unix_epoch_ms(); + + int32_t to_unix_time(int year, int month, int day, int hour = 0, int min = 0, int sec = 0); + int32_t to_unix_time(std::string_view date = "2024-01-18 14:04:00"); + int32_t to_unix_time2(std::string_view date = "2024-01-18T14:04:00000Z"); + + int32_t to_unix_time_from_local(const int32_t* dateAndTime); + int32_t to_unix_time_from_local(const tm& timeinfo); + int32_t to_unix_time_local(std::string_view utcdate = "2024-01-18 14:04:00"); + int32_t to_unix_time_local2(std::string_view utcdate = "2024-01-18T14:04:00000Z"); + + void to_local_time(const int32_t unixtime, int32_t* fullDateAndTime); + std::string to_local_time(const int32_t unixtime, std::string_view format = "%Y-%m-%d %X"); + + size_t get_local_time_string(char* buf, size_t maxSize); - int32_t get_unix_timestamp(); - int64_t get_unix_timestamp_ms(); template T get_number(std::string_view number) { @@ -41,14 +58,6 @@ namespace string { } } - - - int32_t to_unix_time(std::string_view date = "2024-01-18 14:04:00"); - int32_t to_unix_time2(std::string_view date = "2024-01-18T14:04:00000Z"); - int32_t to_local_unix_time(std::string_view date = "2024-01-18 14:04:00"); - int32_t to_local_unix_time2(std::string_view date = "2024-01-18T14:04:00000Z"); - int32_t to_unix_time(int year = 2024, int month = 1, int day = 1, int hour = 0, int min = 0, int sec = 0); - bool cstring_equal(const char* a, const char* b, size_t a_offset = 0, size_t b_offset = 0); bool partial_equality(const char* a, const char* b, size_t a_offset = 0, size_t b_offset = 0); bool partial_equality(std::string_view a, std::string_view b, size_t a_offset = 0, size_t b_offset = 0); diff --git a/packages/logging/source/common/String.cpp b/packages/logging/source/common/String.cpp index 6bd6a1ba..63fd9861 100644 --- a/packages/logging/source/common/String.cpp +++ b/packages/logging/source/common/String.cpp @@ -13,6 +13,7 @@ #include #include #include +#include namespace { constexpr size_t buffer_size = 1024; @@ -22,249 +23,293 @@ namespace { std::mutex wbufferMutex; wchar_t wbuffer[buffer_size]; + } namespace l { namespace string { - tm get_time_info(int32_t unixtime, bool adjustYearAndMonth) { - struct tm timeinfo = {}; - const time_t time = static_cast(unixtime); + int32_t get_local_timezone() { #ifdef WIN32 - auto res = localtime_s(&timeinfo, &time); + long time; + auto res = _get_timezone(&time); + ASSERT(res == 0); #else - auto res = localtime_r(&time, &timeinfo); + auto time = __timezone; #endif - ASSERT(res == 0); - if (adjustYearAndMonth) { - timeinfo.tm_year += 1900; - timeinfo.tm_mon += 1; - } - return timeinfo; + return static_cast(- time); // negate since timezone is how to get utc time from local time (local time - utc time) } - int32_t get_unix_time(const tm& timeinfo, bool adjustYearAndMonth) { - struct tm ti = timeinfo; - if (adjustYearAndMonth) { - ti.tm_year -= 1900; - ti.tm_mon -= 1; - } - auto date = mktime(&ti); - return static_cast(date); + int32_t get_local_daylight_savings(bool inHours) { +#ifdef WIN32 + int time; + auto res = _get_daylight(&time); + ASSERT(res == 0); +#else + auto time = __daylight; +#endif + return static_cast(inHours ? time : time * 3600); } - void get_time_info(int32_t* fullDateAndTime, int32_t unixtime) { - auto timeinfo = l::string::get_time_info(unixtime, true); - fullDateAndTime[0] = timeinfo.tm_year; - fullDateAndTime[1] = timeinfo.tm_mon; - fullDateAndTime[2] = timeinfo.tm_mday; - fullDateAndTime[3] = timeinfo.tm_hour; - fullDateAndTime[4] = timeinfo.tm_min; - fullDateAndTime[5] = timeinfo.tm_sec; + time_t convert_to_local_time_from_utc_time(const time_t time) { + auto daylight = get_local_daylight_savings(); + auto timezone = get_local_timezone(); + return time + timezone + daylight; } - int32_t get_unix_time(int32_t* fullDateAndTime) { - struct tm timeinfo = {}; - timeinfo.tm_year = fullDateAndTime[0]; - timeinfo.tm_mon = fullDateAndTime[1]; - timeinfo.tm_mday = fullDateAndTime[2]; - timeinfo.tm_hour = fullDateAndTime[3]; - timeinfo.tm_min = fullDateAndTime[4]; - timeinfo.tm_sec = fullDateAndTime[5]; - return get_unix_time(timeinfo, true); + time_t convert_to_utc_time_from_local_time(const time_t time) { + auto daylight = get_local_daylight_savings(); + auto timezone = get_local_timezone(); + return time - timezone - daylight; } - size_t get_time_string(char* buf, size_t maxSize) { - using namespace std::chrono; - - auto n = system_clock::now(); - auto tp = n.time_since_epoch(); - - auto micros = std::chrono::duration_cast(tp); - auto seconds = std::chrono::duration_cast(tp); - auto minutes = std::chrono::duration_cast(tp); - auto hours = std::chrono::duration_cast(tp); - - const time_t now = system_clock::to_time_t(n); - struct tm newtime = {}; + time_t convert_to_time(const tm* timeinfo, bool adjustYearAndMonth) { + tm timeinfo2 = *timeinfo; + if (adjustYearAndMonth) { + timeinfo2.tm_year -= 1900; + timeinfo2.tm_mon -= 1; + } #ifdef WIN32 - localtime_s(&newtime, &now); + // Converts a UTC time represented by a struct tm to a UTC time represented by a time_t type. + // i.e does not correct for time zone + time_t time = _mkgmtime64(&timeinfo2); #else - localtime_r(&now, &newtime); + time_t time = timegm(&timeinfo2); #endif + return time; + } + void convert_to_tm(const time_t time, tm* timeinfo, bool adjustYearAndMonth) { +#ifdef WIN32 + auto res = _gmtime64_s(timeinfo, &time); + ASSERT(res == 0); +#else + tm* ti = gmtime(&time); + *timeinfo = *ti; +#endif - - newtime.tm_year += 1900; - auto micro = static_cast(micros.count() % 1000000); - auto count = std::snprintf(buf, maxSize, "%.2d-%.2d-%.2d %.2d:%.2d:%.2d.%.6d", newtime.tm_year, newtime.tm_mon, newtime.tm_mday, newtime.tm_hour, newtime.tm_min, newtime.tm_sec, micro); - - return static_cast(count); + if (adjustYearAndMonth) { + timeinfo->tm_year += 1900; + timeinfo->tm_mon += 1; + } } - std::string get_time_string(const int64_t unixtime, std::string_view format) { - struct std::tm tminfo = {}; + time_t convert_to_utc_time_from_local_tm(const tm* utctimeinfo, bool adjustYearAndMonth) { + tm timeinfo = *utctimeinfo; + if (adjustYearAndMonth) { + timeinfo.tm_year -= 1900; + timeinfo.tm_mon -= 1; + } #ifdef WIN32 - localtime_s(&tminfo, &unixtime); + // Convert the local time to a calendar value. + time_t time = _mktime64(&timeinfo); #else - localtime_r(&unixtime, &tminfo); + time_t time = mktime(&timeinfo); #endif + return time; + } - std::ostringstream out; - out << std::put_time(&tminfo, format.data()); - return out.str(); + void convert_to_local_tm_from_utc_time(const time_t utctime, tm* timeinfo, bool adjustYearAndMonth) { +#ifdef WIN32 + // Converts a time_t time value to a tm structure, and corrects for the local time zone. + auto res = _localtime64_s(timeinfo, &utctime); +#else + auto res = localtime_r(&utctime, timeinfo); +#endif + if (adjustYearAndMonth) { + timeinfo->tm_year += 1900; + timeinfo->tm_mon += 1; + } + ASSERT(res == 0); } - int32_t get_unix_timestamp() { + int32_t get_unix_epoch() { return static_cast(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count() / 1000LL); } - int64_t get_unix_timestamp_ms() { + int64_t get_unix_epoch_ms() { return std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); } - int32_t to_unix_time(std::string_view utc_date) { + int32_t to_unix_time(int year, int month, int day, int hour, int min, int sec) { + struct tm timeinfo = {}; + + timeinfo.tm_year = year - 1900; + timeinfo.tm_mon = month - 1; //months since January - [0,11] + timeinfo.tm_mday = day; //day of the month - [1,31] + timeinfo.tm_hour = hour; //hours since midnight - [0,23] + timeinfo.tm_min = min; //minutes after the hour - [0,59] + timeinfo.tm_sec = sec; //seconds after the minute - [0,59] + + // use gmtime for gmt/utc time, use it when local time zone is unknown, for example in storage + // use mktime for local time zone presentation + return static_cast(convert_to_time(&timeinfo)); + } + + int32_t to_unix_time(std::string_view date) { struct tm timeinfo = {}; int ret = 0; - if (utc_date.size() > 10) { - ASSERT(utc_date.size() == 19); + if (date.size() > 10) { + ASSERT(date.size() == 19); #ifdef WIN32 - ret = sscanf_s(utc_date.data(), "%4d-%2d-%2d %2d:%2d:%2d", + ret = sscanf_s(date.data(), "%4d-%2d-%2d %2d:%2d:%2d", &timeinfo.tm_year, &timeinfo.tm_mon, &timeinfo.tm_mday, &timeinfo.tm_hour, &timeinfo.tm_min, &timeinfo.tm_sec); #else - ret = sscanf(utc_date.data(), "%4d-%2d-%2d %2d:%2d:%2d", + ret = sscanf(date.data(), "%4d-%2d-%2d %2d:%2d:%2d", &timeinfo.tm_year, &timeinfo.tm_mon, &timeinfo.tm_mday, &timeinfo.tm_hour, &timeinfo.tm_min, &timeinfo.tm_sec); #endif + ASSERT(ret <= 6); } else { - ASSERT(utc_date.size() == 10); + ASSERT(date.size() == 10); #ifdef WIN32 - ret = sscanf_s(utc_date.data(), "%4d-%2d-%2d", + ret = sscanf_s(date.data(), "%4d-%2d-%2d", &timeinfo.tm_year, &timeinfo.tm_mon, &timeinfo.tm_mday); #else - ret = sscanf(utc_date.data(), "%4d-%2d-%2d", + ret = sscanf(date.data(), "%4d-%2d-%2d", &timeinfo.tm_year, &timeinfo.tm_mon, &timeinfo.tm_mday); #endif timeinfo.tm_hour = 0; timeinfo.tm_min = 0; timeinfo.tm_sec = 0; + ASSERT(ret <= 3); } - ASSERT(ret <= 6); timeinfo.tm_year -= 1900; timeinfo.tm_mon -= 1; // use _mkgmtime for gmt/utc time, use it when local time zone is unknown, for example in storage // use mktime for local time zone presentation -#ifdef WIN32 - auto unix_time = _mkgmtime(&timeinfo); -#else - auto unix_time = mktime(&timeinfo); -#endif - - return static_cast(unix_time); + return static_cast(convert_to_time(&timeinfo)); } - int32_t to_unix_time2(std::string_view utc_date) { + int32_t to_unix_time2(std::string_view date) { struct tm timeinfo = {}; int ret = 0; int microsec; - ASSERT(utc_date.size() == 28); + ASSERT(date.size() == 28); #ifdef WIN32 - ret = sscanf_s(utc_date.data(), "%4d-%2d-%2dT%2d:%2d:%2d.%7dZ", + ret = sscanf_s(date.data(), "%4d-%2d-%2dT%2d:%2d:%2d.%7dZ", &timeinfo.tm_year, &timeinfo.tm_mon, &timeinfo.tm_mday, &timeinfo.tm_hour, &timeinfo.tm_min, &timeinfo.tm_sec, µsec); #else - ret = sscanf(utc_date.data(), "%4d-%2d-%2dT%2d:%2d:%2d.%7dZ", + ret = sscanf(date.data(), "%4d-%2d-%2dT%2d:%2d:%2d.%7dZ", &timeinfo.tm_year, &timeinfo.tm_mon, &timeinfo.tm_mday, &timeinfo.tm_hour, &timeinfo.tm_min, &timeinfo.tm_sec, µsec); #endif - ASSERT(ret <= 6); + ASSERT(ret <= 7); timeinfo.tm_year -= 1900; timeinfo.tm_mon -= 1; // use _mkgmtime for gmt/utc time, use it when local time zone is unknown, for example in storage // use mktime for local time zone presentation -#ifdef WIN32 - auto unix_time = _mkgmtime(&timeinfo); -#else - auto unix_time = mktime(&timeinfo); -#endif + return static_cast(convert_to_time(&timeinfo)); + } - return static_cast(unix_time); + int32_t to_unix_time_from_local(const int32_t* dateAndTime) { + struct tm timeinfo = {}; + timeinfo.tm_year = dateAndTime[0]; + timeinfo.tm_mon = dateAndTime[1]; + timeinfo.tm_mday = dateAndTime[2]; + timeinfo.tm_hour = dateAndTime[3]; + timeinfo.tm_min = dateAndTime[4]; + timeinfo.tm_sec = dateAndTime[5]; + timeinfo.tm_isdst = get_local_daylight_savings(true); + + bool adjustYearAndMonth = timeinfo.tm_year > 1000 ? true : false; + return static_cast(convert_to_utc_time_from_local_tm(&timeinfo, adjustYearAndMonth)); } - int32_t to_local_unix_time(std::string_view utc_date) { + int32_t to_unix_time_from_local(const tm& timeinfo) { + bool adjustYearAndMonth = timeinfo.tm_year > 1000 ? true : false; + return static_cast(convert_to_utc_time_from_local_tm(&timeinfo, adjustYearAndMonth)); + } + + int32_t to_unix_time_local(std::string_view dateAndTime) { struct tm timeinfo = {}; #ifdef WIN32 - int ret = sscanf_s(utc_date.data(), "%4d-%2d-%2d %2d:%2d:%2d", + int ret = sscanf_s(dateAndTime.data(), "%4d-%2d-%2d %2d:%2d:%2d", &timeinfo.tm_year, &timeinfo.tm_mon, &timeinfo.tm_mday, &timeinfo.tm_hour, &timeinfo.tm_min, &timeinfo.tm_sec); #else - int ret = sscanf(utc_date.data(), "%4d-%2d-%2d %2d:%2d:%2d", + int ret = sscanf(dateAndTime.data(), "%4d-%2d-%2d %2d:%2d:%2d", &timeinfo.tm_year, &timeinfo.tm_mon, &timeinfo.tm_mday, &timeinfo.tm_hour, &timeinfo.tm_min, &timeinfo.tm_sec); #endif ASSERT(ret <= 6); - timeinfo.tm_year -= 1900; - timeinfo.tm_mon -= 1; - // use _mkgmtime for gmt/utc time, use it when local time zone is unknown, for example in storage // use mktime for local time zone presentation - auto unix_time = mktime(&timeinfo); - - return static_cast(unix_time); + bool adjustYearAndMonth = timeinfo.tm_year > 1000 ? true : false; + return static_cast(convert_to_utc_time_from_local_tm(&timeinfo), adjustYearAndMonth); } - int32_t to_local_unix_time2(std::string_view utc_date) { + int32_t to_unix_time_local2(std::string_view dateAndTime) { struct tm timeinfo = {}; int microsec; - ASSERT(utc_date.size() == 28); + ASSERT(dateAndTime.size() == 28); #ifdef WIN32 - int ret = sscanf_s(utc_date.data(), "%4d-%2d-%2dT%2d:%2d:%2d.%7dZ", + int ret = sscanf_s(dateAndTime.data(), "%4d-%2d-%2dT%2d:%2d:%2d.%7dZ", &timeinfo.tm_year, &timeinfo.tm_mon, &timeinfo.tm_mday, &timeinfo.tm_hour, &timeinfo.tm_min, &timeinfo.tm_sec, µsec); #else - int ret = sscanf(utc_date.data(), "%4d-%2d-%2dT%2d:%2d:%2d.%7dZ", + int ret = sscanf(dateAndTime.data(), "%4d-%2d-%2dT%2d:%2d:%2d.%7dZ", &timeinfo.tm_year, &timeinfo.tm_mon, &timeinfo.tm_mday, &timeinfo.tm_hour, &timeinfo.tm_min, &timeinfo.tm_sec, µsec); #endif - ASSERT(ret <= 6); - - timeinfo.tm_year -= 1900; - timeinfo.tm_mon -= 1; + ASSERT(ret <= 7); // use _mkgmtime for gmt/utc time, use it when local time zone is unknown, for example in storage // use mktime for local time zone presentation - auto unix_time = mktime(&timeinfo); + bool adjustYearAndMonth = timeinfo.tm_year > 1000 ? true : false; + return static_cast(convert_to_utc_time_from_local_tm(&timeinfo), adjustYearAndMonth); + } - return static_cast(unix_time); + void to_local_time(const int32_t unixtime, int32_t* dateAndTime) { + tm timeinfo; + convert_to_local_tm_from_utc_time(unixtime, &timeinfo); + dateAndTime[0] = timeinfo.tm_year; + dateAndTime[1] = timeinfo.tm_mon; + dateAndTime[2] = timeinfo.tm_mday; + dateAndTime[3] = timeinfo.tm_hour; + dateAndTime[4] = timeinfo.tm_min; + dateAndTime[5] = timeinfo.tm_sec; } - int32_t to_unix_time(int year, int month, int day, int hour, int min, int sec) { - struct tm timeinfo = {}; + std::string to_local_time(const int32_t unixtime, std::string_view format) { + struct std::tm tminfo = {}; - timeinfo.tm_year = year - 1900; - timeinfo.tm_mon = month - 1; //months since January - [0,11] - timeinfo.tm_mday = day; //day of the month - [1,31] - timeinfo.tm_hour = hour; //hours since midnight - [0,23] - timeinfo.tm_min = min; //minutes after the hour - [0,59] - timeinfo.tm_sec = sec; //seconds after the minute - [0,59] + convert_to_local_tm_from_utc_time(unixtime, &tminfo); - // use gmtime for gmt/utc time, use it when local time zone is unknown, for example in storage - // use mktime for local time zone presentation -#ifdef WIN32 - auto date = _mkgmtime(&timeinfo); -#else - auto date = mktime(&timeinfo); -#endif + std::ostringstream out; + out << std::put_time(&tminfo, format.data()); + return out.str(); + } + + size_t get_local_time_string(char* buf, size_t maxSize) { + using namespace std::chrono; - return static_cast(date); + auto n = system_clock::now(); + auto tp = n.time_since_epoch(); + + auto micros = std::chrono::duration_cast(tp); + auto seconds = std::chrono::duration_cast(tp); + auto minutes = std::chrono::duration_cast(tp); + auto hours = std::chrono::duration_cast(tp); + + const time_t now = system_clock::to_time_t(n); + struct tm newtime = {}; + + convert_to_local_tm_from_utc_time(now, &newtime); + + auto micro = static_cast(micros.count() % 1000000); + auto count = std::snprintf(buf, maxSize, "%.2d-%.2d-%.2d %.2d:%.2d:%.2d.%.6d", newtime.tm_year, newtime.tm_mon, newtime.tm_mday, newtime.tm_hour, newtime.tm_min, newtime.tm_sec, micro); + + return static_cast(count); } bool cstring_equal(const char* a, const char* b, size_t a_offset, size_t b_offset) { diff --git a/packages/memory/tests/common/ContainersTest.cpp b/packages/memory/tests/common/ContainersTest.cpp index 67bbdb82..4f586fd7 100644 --- a/packages/memory/tests/common/ContainersTest.cpp +++ b/packages/memory/tests/common/ContainersTest.cpp @@ -5,7 +5,7 @@ TEST(Containers, VectorOps) { { - LOG(LogInfo) << l::string::get_unix_timestamp_ms(); + LOG(LogInfo) << l::string::get_unix_epoch_ms(); std::vector v = { 1, 3, 5, 32, 53, 2, 95 }; auto v_sub3 = l::container::vector_extract(v, 4u, 3u); diff --git a/packages/network/include/network/NetworkManager.h b/packages/network/include/network/NetworkManager.h index 4bbe3516..d08ae18e 100644 --- a/packages/network/include/network/NetworkManager.h +++ b/packages/network/include/network/NetworkManager.h @@ -128,7 +128,7 @@ namespace l::network { bool available = false; if (mCompletedRequest && mOngoingRequest.compare_exchange_strong(available, true)) { mCompletedRequest = false; - mStarted = l::string::get_unix_timestamp_ms(); + mStarted = l::string::get_unix_epoch_ms(); return true; } return false; @@ -197,7 +197,7 @@ namespace l::network { curl_easy_setopt(mCurl, CURLOPT_BUFFERSIZE, mDefaultResponseSize); - mStarted = l::string::get_unix_timestamp_ms(); + mStarted = l::string::get_unix_epoch_ms(); mSuccess = true; if (multiHandle != nullptr) { @@ -265,7 +265,7 @@ namespace l::network { bool HasExpired() { if (mOngoingRequest && mTimeout > 0) { - auto timeWaitingMs = static_cast(l::string::get_unix_timestamp_ms() - mStarted); + auto timeWaitingMs = static_cast(l::string::get_unix_epoch_ms() - mStarted); auto expired = timeWaitingMs > mTimeout * 1000; return expired; } diff --git a/packages/network/source/common/NetworkInterface.cpp b/packages/network/source/common/NetworkInterface.cpp index 662b9aaa..5f50cd7d 100644 --- a/packages/network/source/common/NetworkInterface.cpp +++ b/packages/network/source/common/NetworkInterface.cpp @@ -14,7 +14,7 @@ namespace l::network { if (mNetworkStatusInterval == 0 || mIsHostResponding) { return true; } - auto now = l::string::get_unix_timestamp(); + auto now = l::string::get_unix_epoch(); if (mLastStatusCheck < now) { // every x seconds we allow one connection through to test connectivity mLastStatusCheck = now + mNetworkStatusInterval; @@ -24,7 +24,7 @@ namespace l::network { } void HostInfo::SetStatus(bool isup) { - mLastStatusCheck = l::string::get_unix_timestamp() + 20; + mLastStatusCheck = l::string::get_unix_epoch() + 20; mIsHostResponding = isup; } diff --git a/packages/testing/tests/common/LoggingTest.cpp b/packages/testing/tests/common/LoggingTest.cpp index dca35fd4..deb7a9f7 100644 --- a/packages/testing/tests/common/LoggingTest.cpp +++ b/packages/testing/tests/common/LoggingTest.cpp @@ -114,14 +114,62 @@ TEST(Logging, StringComparisons) { } TEST(Logging, TimeConversions) { - auto unixtime = l::string::get_unix_timestamp(); + { // general time_t/tm conversion + for (int i = 0; i < 2; i++) { + bool adjustDate = i == 0 ? false : true; + auto unixtime = l::string::get_unix_epoch(); + struct tm timeinfo; + l::string::convert_to_tm(unixtime, &timeinfo, adjustDate); + auto unixtime2 = l::string::convert_to_time(&timeinfo, adjustDate); + TEST_EQ(unixtime, unixtime2, ""); + } + } + { // utc to local time conversions + for (int i = 0; i < 2; i++) { + bool adjustDate = i == 0 ? false : true; + auto unixtime = l::string::get_unix_epoch(); + struct tm timeinfo; + l::string::convert_to_local_tm_from_utc_time(unixtime, &timeinfo, adjustDate); + auto unixtime2 = l::string::convert_to_utc_time_from_local_tm(&timeinfo, adjustDate); + TEST_EQ(unixtime, unixtime2, ""); + } + } + { + auto unixtime = l::string::get_unix_epoch(); + auto localtime = l::string::convert_to_local_time_from_utc_time(unixtime); + auto time = l::string::convert_to_utc_time_from_local_time(localtime); + TEST_EQ(time, unixtime, ""); + } + { + auto unixtime = l::string::get_unix_epoch(); - int32_t fullDate[6]; - l::string::get_time_info(fullDate, unixtime); + auto localtime = l::string::convert_to_local_time_from_utc_time(unixtime); + struct tm timeinfolocal; + l::string::convert_to_tm(localtime, &timeinfolocal, true); - auto unixtime2 = l::string::get_unix_time(fullDate); + struct tm timeinfo; + l::string::convert_to_local_tm_from_utc_time(unixtime, &timeinfo, true); + + TEST_EQ(timeinfo.tm_hour, timeinfolocal.tm_hour, ""); + } - TEST_TRUE(unixtime == unixtime2, ""); + { + auto unixtime = l::string::get_unix_epoch(); + + int32_t fullDate[6]; + l::string::to_local_time(unixtime, fullDate); + + auto unixtime2 = l::string::to_unix_time_from_local(fullDate); + + TEST_EQ(unixtime, unixtime2, ""); + } + { + auto unixtime = l::string::get_unix_epoch(); + int32_t fullDate[6]; + l::string::to_local_time(unixtime, fullDate); + auto unixtimelocal = l::string::to_unix_time_from_local(fullDate); + + } return 0; } From 47ae130a346c45af0a9b62b7864fe7b0fd4cf6a4 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Mon, 19 Aug 2024 21:23:39 +0200 Subject: [PATCH 028/125] Small update to name changes. --- packages/logging/include/logging/String.h | 4 ++-- packages/logging/source/common/String.cpp | 4 ++-- packages/tools/include/tools/signals/Filters.h | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/logging/include/logging/String.h b/packages/logging/include/logging/String.h index 601eedaf..a4ceaf80 100644 --- a/packages/logging/include/logging/String.h +++ b/packages/logging/include/logging/String.h @@ -35,9 +35,9 @@ namespace string { int32_t to_unix_time_local2(std::string_view utcdate = "2024-01-18T14:04:00000Z"); void to_local_time(const int32_t unixtime, int32_t* fullDateAndTime); - std::string to_local_time(const int32_t unixtime, std::string_view format = "%Y-%m-%d %X"); + std::string get_local_time_string(const int32_t unixtime, std::string_view format = "%Y-%m-%d %X"); - size_t get_local_time_string(char* buf, size_t maxSize); + size_t get_local_time_string_verbose(char* buf, size_t maxSize); template diff --git a/packages/logging/source/common/String.cpp b/packages/logging/source/common/String.cpp index 63fd9861..295de980 100644 --- a/packages/logging/source/common/String.cpp +++ b/packages/logging/source/common/String.cpp @@ -280,7 +280,7 @@ namespace string { dateAndTime[5] = timeinfo.tm_sec; } - std::string to_local_time(const int32_t unixtime, std::string_view format) { + std::string get_local_time_string(const int32_t unixtime, std::string_view format) { struct std::tm tminfo = {}; convert_to_local_tm_from_utc_time(unixtime, &tminfo); @@ -290,7 +290,7 @@ namespace string { return out.str(); } - size_t get_local_time_string(char* buf, size_t maxSize) { + size_t get_local_time_string_verbose(char* buf, size_t maxSize) { using namespace std::chrono; auto n = system_clock::now(); diff --git a/packages/tools/include/tools/signals/Filters.h b/packages/tools/include/tools/signals/Filters.h index 44b8656e..7d896fae 100644 --- a/packages/tools/include/tools/signals/Filters.h +++ b/packages/tools/include/tools/signals/Filters.h @@ -223,7 +223,7 @@ namespace l::signals { SignalMovingAverage(int32_t filterKernelSize = 50) : mFilterStateIndex(0) { - this->mData[0] = 0.999; + this->mData[0] = static_cast(0.999); this->mData[1] = static_cast(filterKernelSize); mFilterState.resize(filterKernelSize); Reset(); @@ -264,7 +264,7 @@ namespace l::signals { for (int32_t i = 0; i < width; i++) { outVal += mFilterState[i]; } - return outVal / static_cast(width); + return outVal / static_cast(width); } protected: From df5b56b0f0d2913371488fe2329c98b0eead9903 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Mon, 19 Aug 2024 21:35:31 +0200 Subject: [PATCH 029/125] Some minor fixes after the time string refactor. --- packages/logging/source/common/String.cpp | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/packages/logging/source/common/String.cpp b/packages/logging/source/common/String.cpp index 295de980..b5ebbb15 100644 --- a/packages/logging/source/common/String.cpp +++ b/packages/logging/source/common/String.cpp @@ -134,8 +134,8 @@ namespace string { int32_t to_unix_time(int year, int month, int day, int hour, int min, int sec) { struct tm timeinfo = {}; - timeinfo.tm_year = year - 1900; - timeinfo.tm_mon = month - 1; //months since January - [0,11] + timeinfo.tm_year = year; + timeinfo.tm_mon = month; //months since January - [0,11] timeinfo.tm_mday = day; //day of the month - [1,31] timeinfo.tm_hour = hour; //hours since midnight - [0,23] timeinfo.tm_min = min; //minutes after the hour - [0,59] @@ -143,7 +143,8 @@ namespace string { // use gmtime for gmt/utc time, use it when local time zone is unknown, for example in storage // use mktime for local time zone presentation - return static_cast(convert_to_time(&timeinfo)); + bool adjustYearAndMonth = timeinfo.tm_year > 1000 ? true : false; + return static_cast(convert_to_time(&timeinfo, adjustYearAndMonth)); } int32_t to_unix_time(std::string_view date) { @@ -177,13 +178,10 @@ namespace string { ASSERT(ret <= 3); } - - timeinfo.tm_year -= 1900; - timeinfo.tm_mon -= 1; - // use _mkgmtime for gmt/utc time, use it when local time zone is unknown, for example in storage // use mktime for local time zone presentation - return static_cast(convert_to_time(&timeinfo)); + bool adjustYearAndMonth = timeinfo.tm_year > 1000 ? true : false; + return static_cast(convert_to_time(&timeinfo, adjustYearAndMonth)); } int32_t to_unix_time2(std::string_view date) { @@ -203,12 +201,10 @@ namespace string { ASSERT(ret <= 7); - timeinfo.tm_year -= 1900; - timeinfo.tm_mon -= 1; - // use _mkgmtime for gmt/utc time, use it when local time zone is unknown, for example in storage // use mktime for local time zone presentation - return static_cast(convert_to_time(&timeinfo)); + bool adjustYearAndMonth = timeinfo.tm_year > 1000 ? true : false; + return static_cast(convert_to_time(&timeinfo, adjustYearAndMonth)); } int32_t to_unix_time_from_local(const int32_t* dateAndTime) { From 50ab88656e44dbf26588a22aebd65157b0f327cf Mon Sep 17 00:00:00 2001 From: lnd3 Date: Mon, 19 Aug 2024 21:49:33 +0200 Subject: [PATCH 030/125] Fix an issue with put_time expecting non adjusted tm struct. --- packages/logging/source/common/String.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/logging/source/common/String.cpp b/packages/logging/source/common/String.cpp index b5ebbb15..3cc19928 100644 --- a/packages/logging/source/common/String.cpp +++ b/packages/logging/source/common/String.cpp @@ -279,7 +279,7 @@ namespace string { std::string get_local_time_string(const int32_t unixtime, std::string_view format) { struct std::tm tminfo = {}; - convert_to_local_tm_from_utc_time(unixtime, &tminfo); + convert_to_local_tm_from_utc_time(unixtime, &tminfo, false); std::ostringstream out; out << std::put_time(&tminfo, format.data()); From c76822e5343e0ab08fbdc8003101664d2c2982e9 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Mon, 19 Aug 2024 22:13:08 +0200 Subject: [PATCH 031/125] Few fixes. --- packages/logging/include/logging/String.h | 4 ++-- packages/logging/source/common/String.cpp | 12 ++++++------ packages/testing/tests/common/LoggingTest.cpp | 19 ++++++++----------- 3 files changed, 16 insertions(+), 19 deletions(-) diff --git a/packages/logging/include/logging/String.h b/packages/logging/include/logging/String.h index a4ceaf80..137fed96 100644 --- a/packages/logging/include/logging/String.h +++ b/packages/logging/include/logging/String.h @@ -58,8 +58,8 @@ namespace string { } } - bool cstring_equal(const char* a, const char* b, size_t a_offset = 0, size_t b_offset = 0); - bool partial_equality(const char* a, const char* b, size_t a_offset = 0, size_t b_offset = 0); + bool cstring_equal(const char* a, const char* b, size_t a_offset = 0, size_t b_offset = 0, size_t maxCount = 20); + bool partial_equality(const char* a, const char* b, size_t a_offset = 0, size_t b_offset = 0, size_t maxCount = 20); bool partial_equality(std::string_view a, std::string_view b, size_t a_offset = 0, size_t b_offset = 0); std::vector split(std::wstring_view text, std::wstring_view delim = L" \t\n", char escapeChar = '\"'); diff --git a/packages/logging/source/common/String.cpp b/packages/logging/source/common/String.cpp index 3cc19928..2db0bb6f 100644 --- a/packages/logging/source/common/String.cpp +++ b/packages/logging/source/common/String.cpp @@ -308,17 +308,17 @@ namespace string { return static_cast(count); } - bool cstring_equal(const char* a, const char* b, size_t a_offset, size_t b_offset) { - return !strcmp(a + a_offset, b + b_offset); + bool cstring_equal(const char* a, const char* b, size_t a_offset, size_t b_offset, size_t maxCount) { + return !strncmp(a + a_offset, b + b_offset, maxCount); } - bool partial_equality(const char* a, const char* b, size_t a_offset, size_t b_offset) { - for (size_t i = 0; i < a_offset; i++) { // check for missed null terminators before a_offset + bool partial_equality(const char* a, const char* b, size_t a_offset, size_t b_offset, size_t maxCount) { + for (size_t i = 0; i < a_offset && i < maxCount; i++) { // check for missed null terminators before a_offset if (a[i] == 0) { return false; } } - for (size_t i = 0; i < b_offset; i++) { // check for missed null terminators before b_offset + for (size_t i = 0; i < b_offset && i < maxCount; i++) { // check for missed null terminators before b_offset if (b[i] == 0) { return false; } @@ -326,7 +326,7 @@ namespace string { const char* a1 = a + a_offset; const char* b1 = b + b_offset; int i = 0; - for (i = 0; a1[i] != 0 && b1[i] != 0; i++) { + for (i = 0; a1[i] != 0 && b1[i] != 0 && i < maxCount; i++) { if (a1[i] != b1[i]) { return false; } diff --git a/packages/testing/tests/common/LoggingTest.cpp b/packages/testing/tests/common/LoggingTest.cpp index deb7a9f7..c2ab17ee 100644 --- a/packages/testing/tests/common/LoggingTest.cpp +++ b/packages/testing/tests/common/LoggingTest.cpp @@ -109,6 +109,11 @@ TEST(Logging, StringComparisons) { TEST_TRUE(l::string::partial_equality(d, e, 2, 0), ""); TEST_TRUE(l::string::partial_equality(d, f, 1, 1), ""); } + { + std::string a = "asdgkösd"; + TEST_TRUE(l::string::cstring_equal(a.c_str(), "asdg34643", 0, 0, 4), ""); + TEST_FALSE(l::string::cstring_equal(a.c_str(), "asdg34643", 0, 0, 5), ""); + } return 0; } @@ -142,33 +147,25 @@ TEST(Logging, TimeConversions) { } { auto unixtime = l::string::get_unix_epoch(); - auto localtime = l::string::convert_to_local_time_from_utc_time(unixtime); struct tm timeinfolocal; l::string::convert_to_tm(localtime, &timeinfolocal, true); - struct tm timeinfo; l::string::convert_to_local_tm_from_utc_time(unixtime, &timeinfo, true); - TEST_EQ(timeinfo.tm_hour, timeinfolocal.tm_hour, ""); } { auto unixtime = l::string::get_unix_epoch(); - int32_t fullDate[6]; l::string::to_local_time(unixtime, fullDate); - auto unixtime2 = l::string::to_unix_time_from_local(fullDate); - TEST_EQ(unixtime, unixtime2, ""); } { - auto unixtime = l::string::get_unix_epoch(); - int32_t fullDate[6]; - l::string::to_local_time(unixtime, fullDate); - auto unixtimelocal = l::string::to_unix_time_from_local(fullDate); - + auto unixtime = 1724097382; + auto timeString = l::string::get_local_time_string(unixtime); + TEST_TRUE(l::string::cstring_equal(timeString.c_str(), "2024-08-19 21:56:22"), ""); } return 0; From 502d112f7c4849cafb379c715a4abceae152034d Mon Sep 17 00:00:00 2001 From: lnd3 Date: Tue, 20 Aug 2024 01:16:05 +0200 Subject: [PATCH 032/125] Remove unreliable test. Don't depend on local time zones. --- .../include/rendering/ui/UIContainer.h | 10 +++--- .../source/common/ui/UIContainer.cpp | 34 +++++++++++-------- packages/testing/tests/common/LoggingTest.cpp | 5 --- 3 files changed, 25 insertions(+), 24 deletions(-) diff --git a/packages/rendering/include/rendering/ui/UIContainer.h b/packages/rendering/include/rendering/ui/UIContainer.h index 536d1e67..72c013f3 100644 --- a/packages/rendering/include/rendering/ui/UIContainer.h +++ b/packages/rendering/include/rendering/ui/UIContainer.h @@ -38,18 +38,18 @@ namespace l::ui { return ImVec2(mPosition.x + mSize.x, mPosition.y + mSize.y); } - ImVec2 Transform(const ImVec2& rootPos, const ImVec2& p, ImVec2 pixelOffset = ImVec2()) const { + ImVec2 Transform(const ImVec2& p, ImVec2 rootPos = ImVec2()) const { ImVec2 transformed; - transformed.x = rootPos.x + mPosition.x + p.x * mScale + pixelOffset.x; - transformed.y = rootPos.y + mPosition.y + p.y * mScale + pixelOffset.y; + transformed.x = rootPos.x + mPosition.x + p.x * mScale; + transformed.y = rootPos.y + mPosition.y + p.y * mScale; return transformed; } }; ImVec2 DragMovement(const ImVec2& prevPos, const ImVec2& curPos, float curScale); bool Overlap(const ImVec2& p, const ImVec2& pMin, const ImVec2& pMax); - bool Overlap(const ImVec2 rootPos, const ImVec2& p, const ImVec2& pMin, const ImVec2& pMax, const ContainerArea& parent); - bool OverlapPixelArea(const ImVec2 rootPos, const ImVec2& p, const ImVec2& pCenter, const ImVec2& offset, const ContainerArea& parent); + bool Overlap(const ImVec2& p, const ImVec2& pMin, const ImVec2& pMax, const ContainerArea& parent); + bool OverlapScreenRect(const ImVec2& p, const ImVec2& pCenter, const ImVec2& offset, const ContainerArea& parent); class UIContainer; diff --git a/packages/rendering/source/common/ui/UIContainer.cpp b/packages/rendering/source/common/ui/UIContainer.cpp index 71c4f060..75141038 100644 --- a/packages/rendering/source/common/ui/UIContainer.cpp +++ b/packages/rendering/source/common/ui/UIContainer.cpp @@ -17,18 +17,24 @@ namespace l::ui { return pMin.x < p.x && pMin.y < p.y && pMax.x > p.x && pMax.y > p.y; } - bool Overlap(const ImVec2 rootPos, const ImVec2& p, const ImVec2& pMin, const ImVec2& pMax, const ContainerArea& parent) { - ImVec2 pMinT = parent.Transform(rootPos, pMin); - ImVec2 pMaxT = parent.Transform(rootPos, pMax); + bool Overlap(const ImVec2& p, const ImVec2& pMin, const ImVec2& pMax, const ContainerArea& parent) { + ImVec2 pMinT = parent.Transform(pMin); + ImVec2 pMaxT = parent.Transform(pMax); return pMinT.x < p.x && pMinT.y < p.y && pMaxT.x > p.x && pMaxT.y > p.y; } - bool OverlapPixelArea(const ImVec2 rootPos, const ImVec2& p, const ImVec2& pCenter, const ImVec2& offset, const ContainerArea& parent) { - ImVec2 pMinT = parent.Transform(rootPos, pCenter, ImVec2(-offset.x, -offset.y)); - ImVec2 pMaxT = parent.Transform(rootPos, pCenter, ImVec2(offset.x, offset.y)); + bool OverlapScreenRect(const ImVec2& p, const ImVec2& pCenter, const ImVec2& offset, const ContainerArea& parent) { + ImVec2 pMinT = parent.Transform(pCenter, ImVec2(-offset.x, -offset.y)); + ImVec2 pMaxT = parent.Transform(pCenter, ImVec2(offset.x, offset.y)); return pMinT.x < p.x && pMinT.y < p.y && pMaxT.x > p.x && pMaxT.y > p.y; } + bool OverlapScreenCircle(const ImVec2& p, const ImVec2& pCenter, float radii, const ContainerArea& parent) { + ImVec2 pT = parent.Transform(pCenter); + ImVec2 d = ImVec2(pT.x - p.x, pT.y - p.y); + return d.x*d.x + d.y*d.y < radii * radii; + } + bool UIContainer::Accept(UIVisitor& visitor, const InputState& input, uint32_t flags) { ContainerArea current; return Accept(visitor, input, current, flags); @@ -196,7 +202,7 @@ namespace l::ui { bool UIMove::Visit(UIContainer& container, const InputState& input, const ContainerArea& parent) { if (input.mStarted && !mMoving) { - if (Overlap(ImVec2(), input.GetLocalPos(), container.GetPosition(), container.GetPositionAtSize(), parent)) { + if (Overlap(input.GetLocalPos(), container.GetPosition(), container.GetPositionAtSize(), parent)) { mMoving = true; mCurrentContainer = &container; } @@ -219,7 +225,7 @@ namespace l::ui { if (!mResizing) { const float radii = mResizeAreaSize * 0.5f; ImVec2 p = container.GetPositionAtSize(); - if (OverlapPixelArea(ImVec2(), input.GetLocalPos(), p, ImVec2(radii, radii), parent)) { + if (OverlapScreenRect(input.GetLocalPos(), p, ImVec2(radii, radii), parent)) { mCurrentContainer = &container; container.Notification(UIContainer_ResizeFlag); @@ -261,19 +267,19 @@ namespace l::ui { ImVec2 pTopLeft = container.GetPosition(); ImVec2 pLowRight = container.GetPositionAtSize(); - ImVec2 p1 = parent.Transform(input.mRootPos, pTopLeft); - ImVec2 p2 = parent.Transform(input.mRootPos, pLowRight); + ImVec2 p1 = parent.Transform(pTopLeft, input.mRootPos); + ImVec2 p2 = parent.Transform(pLowRight, input.mRootPos); ImVec4 colf = ImVec4(1.0f, 0.4f, 0.4f, 1.0f); const ImU32 col = ImColor(colf); mDrawList->AddRect(p1, p2, col, 2.0f, ImDrawFlags_RoundCornersAll, 2.0f); - ImVec2 p3 = parent.Transform(input.mRootPos, pLowRight, ImVec2(-3.0f, -3.0f)); - ImVec2 p4 = parent.Transform(input.mRootPos, pLowRight, ImVec2(3.0f, 3.0f)); + ImVec2 p3 = parent.Transform(pLowRight, ImVec2(input.mRootPos.x - 3.0f, input.mRootPos.y - 3.0f)); + ImVec2 p4 = parent.Transform(pLowRight, ImVec2(input.mRootPos.x + 3.0f, input.mRootPos.y + 3.0f)); if (container.HasNotification(ui::UIContainer_ResizeFlag)) { - p3 = parent.Transform(input.mRootPos, pLowRight, ImVec2(-5.0f, -5.0f)); - p4 = parent.Transform(input.mRootPos, pLowRight, ImVec2(5.0f, 5.0f)); + p3 = parent.Transform(pLowRight, ImVec2(input.mRootPos.x - 5.0f, input.mRootPos.y - 5.0f)); + p4 = parent.Transform(pLowRight, ImVec2(input.mRootPos.x + 5.0f, input.mRootPos.y + 5.0f)); } mDrawList->AddRectFilled(p3, p4, col); return false; diff --git a/packages/testing/tests/common/LoggingTest.cpp b/packages/testing/tests/common/LoggingTest.cpp index c2ab17ee..8e1d4bcf 100644 --- a/packages/testing/tests/common/LoggingTest.cpp +++ b/packages/testing/tests/common/LoggingTest.cpp @@ -162,11 +162,6 @@ TEST(Logging, TimeConversions) { auto unixtime2 = l::string::to_unix_time_from_local(fullDate); TEST_EQ(unixtime, unixtime2, ""); } - { - auto unixtime = 1724097382; - auto timeString = l::string::get_local_time_string(unixtime); - TEST_TRUE(l::string::cstring_equal(timeString.c_str(), "2024-08-19 21:56:22"), ""); - } return 0; } From f4ba16b3387d510164071e92639e15fae0390d97 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Tue, 20 Aug 2024 08:09:20 +0200 Subject: [PATCH 033/125] Fix a few things with uicontainer. --- .../include/rendering/ui/UIContainer.h | 35 +++++++++++++++---- .../source/common/ui/UIContainer.cpp | 24 ++++++++----- 2 files changed, 45 insertions(+), 14 deletions(-) diff --git a/packages/rendering/include/rendering/ui/UIContainer.h b/packages/rendering/include/rendering/ui/UIContainer.h index 72c013f3..318de0a7 100644 --- a/packages/rendering/include/rendering/ui/UIContainer.h +++ b/packages/rendering/include/rendering/ui/UIContainer.h @@ -16,6 +16,18 @@ namespace l::ui { + enum class UIAlignH { + Left = 0, + Center = 1, + Right = 2 + }; + + enum class UIAlignV { + Top = 0, + Middle = 1, + Bottom = 2 + }; + struct InputState { ImVec2 mRootPos; ImVec2 mCurPos; @@ -29,10 +41,17 @@ namespace l::ui { } }; + struct ContainerLayout { + float mBorder = 3.0f; + UIAlignH mAlignH = UIAlignH::Left; + UIAlignV mAlignV = UIAlignV::Top; + }; + struct ContainerArea { ImVec2 mPosition; - ImVec2 mSize; + ImVec2 mSize = ImVec2(20.0f, 20.0f); float mScale = 1.0f; + ContainerLayout mLayout; ImVec2 GetPositionAtSize() const { return ImVec2(mPosition.x + mSize.x, mPosition.y + mSize.y); @@ -79,22 +98,25 @@ namespace l::ui { const uint32_t UIContainer_InputFlag = 0x00000200; const uint32_t UIContainer_OutputFlag = 0x00000400; + class UIDraw; class UIContainer { public: - UIContainer(std::string name, uint32_t flags = 0) : mName(name), mConfigFlags(flags) {} + UIContainer(std::string_view name, uint32_t flags = 0) : mName(name), mConfigFlags(flags) {} ~UIContainer() = default; - bool Accept(UIVisitor& visitor, const InputState& input, uint32_t flags = 0); - bool Accept(UIVisitor& visitor, const InputState& input, const ContainerArea& parent, uint32_t flags = 0); - void Add(UIContainer* container, int32_t i = -1); - void Remove(int32_t i); + virtual bool Accept(UIVisitor& visitor, const InputState& input, uint32_t flags = 0); + virtual bool Accept(UIVisitor& visitor, const InputState& input, const ContainerArea& parent, uint32_t flags = 0); + virtual void Add(UIContainer* container, int32_t i = -1); + virtual void Remove(int32_t i); + void Move(ImVec2 localChange); void Resize(ImVec2 localChange); void Rescale(float localChange); void ClearNotifications(); void Notification(uint32_t flag); bool HasNotification(uint32_t flag); + bool HasConfigFlag(uint32_t flag); void SetPosition(ImVec2 p); void SetSize(ImVec2 s); void SetContainerArea(const ContainerArea& area); @@ -104,6 +126,7 @@ namespace l::ui { float GetScale(); void DebugLog(); + friend UIVisitor; protected: std::string mName; ContainerArea mArea; diff --git a/packages/rendering/source/common/ui/UIContainer.cpp b/packages/rendering/source/common/ui/UIContainer.cpp index 75141038..0a1340bf 100644 --- a/packages/rendering/source/common/ui/UIContainer.cpp +++ b/packages/rendering/source/common/ui/UIContainer.cpp @@ -46,8 +46,8 @@ namespace l::ui { current.mScale = mArea.mScale * parent.mScale; // scale local position with accumulated world scale and add to accumulated world position - current.mPosition.x = (parent.mPosition.x + mArea.mPosition.x * mArea.mScale) * parent.mScale; - current.mPosition.y = (parent.mPosition.y + mArea.mPosition.y * mArea.mScale) * parent.mScale; + current.mPosition.x = parent.mPosition.x + (mArea.mPosition.x * mArea.mScale) * parent.mScale; + current.mPosition.y = parent.mPosition.y + (mArea.mPosition.y * mArea.mScale) * parent.mScale; // scale local size with accumulated world scale current.mSize.x = mArea.mSize.x * mArea.mScale * parent.mScale; @@ -112,6 +112,10 @@ namespace l::ui { return (mNotificationFlags & flag) == flag; } + bool UIContainer::HasConfigFlag(uint32_t flag) { + return (mConfigFlags & flag) == flag; + } + void UIContainer::SetPosition(ImVec2 p) { mArea.mPosition = p; } @@ -264,6 +268,7 @@ namespace l::ui { if (mDebugLog) { container.DebugLog(); } + ImVec2 pTopLeft = container.GetPosition(); ImVec2 pLowRight = container.GetPositionAtSize(); @@ -275,13 +280,16 @@ namespace l::ui { mDrawList->AddRect(p1, p2, col, 2.0f, ImDrawFlags_RoundCornersAll, 2.0f); - ImVec2 p3 = parent.Transform(pLowRight, ImVec2(input.mRootPos.x - 3.0f, input.mRootPos.y - 3.0f)); - ImVec2 p4 = parent.Transform(pLowRight, ImVec2(input.mRootPos.x + 3.0f, input.mRootPos.y + 3.0f)); - if (container.HasNotification(ui::UIContainer_ResizeFlag)) { - p3 = parent.Transform(pLowRight, ImVec2(input.mRootPos.x - 5.0f, input.mRootPos.y - 5.0f)); - p4 = parent.Transform(pLowRight, ImVec2(input.mRootPos.x + 5.0f, input.mRootPos.y + 5.0f)); + if (container.HasConfigFlag(ui::UIContainer_ResizeFlag)) { + ImVec2 p3 = parent.Transform(pLowRight, ImVec2(input.mRootPos.x - 3.0f, input.mRootPos.y - 3.0f)); + ImVec2 p4 = parent.Transform(pLowRight, ImVec2(input.mRootPos.x + 3.0f, input.mRootPos.y + 3.0f)); + if (container.HasNotification(ui::UIContainer_ResizeFlag)) { + p3 = parent.Transform(pLowRight, ImVec2(input.mRootPos.x - 5.0f, input.mRootPos.y - 5.0f)); + p4 = parent.Transform(pLowRight, ImVec2(input.mRootPos.x + 5.0f, input.mRootPos.y + 5.0f)); + } + mDrawList->AddRectFilled(p3, p4, col); } - mDrawList->AddRectFilled(p3, p4, col); + return false; } From 85fea86a371ee438ceb51eeb4e6c8e7b946c6223 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Tue, 20 Aug 2024 15:49:52 +0200 Subject: [PATCH 034/125] More changes for ui container. A fix for a linux warning. --- packages/logging/source/common/String.cpp | 2 +- .../include/rendering/ui/UIContainer.h | 88 ++++++++++++++++++- .../source/common/ui/UIContainer.cpp | 21 ++--- 3 files changed, 96 insertions(+), 15 deletions(-) diff --git a/packages/logging/source/common/String.cpp b/packages/logging/source/common/String.cpp index 2db0bb6f..b7151fc5 100644 --- a/packages/logging/source/common/String.cpp +++ b/packages/logging/source/common/String.cpp @@ -325,7 +325,7 @@ namespace string { } const char* a1 = a + a_offset; const char* b1 = b + b_offset; - int i = 0; + size_t i = 0; for (i = 0; a1[i] != 0 && b1[i] != 0 && i < maxCount; i++) { if (a1[i] != b1[i]) { return false; diff --git a/packages/rendering/include/rendering/ui/UIContainer.h b/packages/rendering/include/rendering/ui/UIContainer.h index 318de0a7..474b24a7 100644 --- a/packages/rendering/include/rendering/ui/UIContainer.h +++ b/packages/rendering/include/rendering/ui/UIContainer.h @@ -28,6 +28,32 @@ namespace l::ui { Bottom = 2 }; + enum class UIRenderType { + Rect = 0, + RectFilled = 1, + Triangle = 2, + TriangleFilled = 3, + Circle = 4, + CircleFilled = 5, + Polygon = 6, + PolygonFilled = 7, + Spline = 8, + Text = 9, + Texture = 10 + }; + + struct UIRenderData { + UIRenderType mType = UIRenderType::Rect; + ImU32 mColor = ImColor(ImVec4(1.0f, 0.4f, 0.4f, 1.0f)); + + ImVec2 mData0; + ImVec2 mData1; + + void SetColor(ImVec4 colf) { + mColor = ImColor(colf); + } + }; + struct InputState { ImVec2 mRootPos; ImVec2 mCurPos; @@ -41,7 +67,7 @@ namespace l::ui { } }; - struct ContainerLayout { + struct UILayoutData { float mBorder = 3.0f; UIAlignH mAlignH = UIAlignH::Left; UIAlignV mAlignV = UIAlignV::Top; @@ -51,18 +77,73 @@ namespace l::ui { ImVec2 mPosition; ImVec2 mSize = ImVec2(20.0f, 20.0f); float mScale = 1.0f; - ContainerLayout mLayout; + UILayoutData mLayout; + UIRenderData mRender; ImVec2 GetPositionAtSize() const { return ImVec2(mPosition.x + mSize.x, mPosition.y + mSize.y); } + // Used in visitors only and parent scale is already premultiplied ImVec2 Transform(const ImVec2& p, ImVec2 rootPos = ImVec2()) const { ImVec2 transformed; transformed.x = rootPos.x + mPosition.x + p.x * mScale; transformed.y = rootPos.y + mPosition.y + p.y * mScale; return transformed; } + + // Used in ui container layout, this is where we premultiply parent scale + ImVec2 GetWorldPos(float parentScale, ImVec2 parentPos) { + ImVec2 worldPos; + worldPos.x = parentPos.x + (mPosition.x + mLayout.mBorder) * mScale * parentScale; + worldPos.y = parentPos.y + (mPosition.y + mLayout.mBorder) * mScale * parentScale; + return worldPos; + } + + float GetWorldScale(float parentScale) { + return parentScale * mScale; + } + + ImVec2 GetWorldPosLayout(float parentScale, ImVec2 parentPos, ImVec2 contentSize, UIAlignH alignH, UIAlignV alignV) { + ImVec2 worldPos; + switch (alignH) { + case UIAlignH::Left: + worldPos.x = parentPos.x + (mPosition.x + mLayout.mBorder) * mScale * parentScale; + break; + case UIAlignH::Center: + worldPos.x = parentPos.x + (mPosition.x + mSize.x * 0.5f - contentSize.x * 0.5f) * mScale * parentScale; + break; + case UIAlignH::Right: + worldPos.x = parentPos.x + (mPosition.x - mLayout.mBorder * 2.0f + mSize.x - contentSize.x) * mScale * parentScale; + break; + } + switch (alignV) { + case UIAlignV::Top: + worldPos.y = parentPos.y + (mPosition.y + mLayout.mBorder) * mScale * parentScale; + break; + case UIAlignV::Middle: + worldPos.y = parentPos.y + (mPosition.y + mSize.y * 0.5f - contentSize.y * 0.5f) * mScale * parentScale; + break; + case UIAlignV::Bottom: + worldPos.y = parentPos.y + (mPosition.y - mLayout.mBorder * 2.0f + mSize.y - contentSize.y) * mScale * parentScale; + break; + } + return worldPos; + } + + ImVec2 GetWorldSize(float parentScale) const { + ImVec2 worldSize; + worldSize.x = (mSize.x - mLayout.mBorder * 2.0f) * mScale * parentScale; + worldSize.y = (mSize.y - mLayout.mBorder * 2.0f) * mScale * parentScale; + return worldSize; + } + + ImVec2 GetLocalSize() const { + ImVec2 localSize; + localSize.x = mSize.x / mScale; + localSize.y = mSize.y / mScale; + return localSize; + } }; ImVec2 DragMovement(const ImVec2& prevPos, const ImVec2& curPos, float curScale); @@ -95,8 +176,6 @@ namespace l::ui { const uint32_t UIContainer_ZoomFlag = 0x00000040; // Can be scaled, zoomed in/out const uint32_t UIContainer_MoveFlag = 0x00000080; // Can be moved when grabbed const uint32_t UIContainer_ResizeFlag = 0x00000100; // Can be resized when grabbing bottom right corner - const uint32_t UIContainer_InputFlag = 0x00000200; - const uint32_t UIContainer_OutputFlag = 0x00000400; class UIDraw; @@ -121,6 +200,7 @@ namespace l::ui { void SetSize(ImVec2 s); void SetContainerArea(const ContainerArea& area); ImVec2 GetPosition(bool untransformed = false); + ImVec2 GetPositionAtCenter(ImVec2 offset = ImVec2(), bool untransformed = false); ImVec2 GetPositionAtSize(ImVec2 offset = ImVec2(), bool untransformed = false); ImVec2 GetSize(bool untransformed = false); float GetScale(); diff --git a/packages/rendering/source/common/ui/UIContainer.cpp b/packages/rendering/source/common/ui/UIContainer.cpp index 0a1340bf..b0fc8648 100644 --- a/packages/rendering/source/common/ui/UIContainer.cpp +++ b/packages/rendering/source/common/ui/UIContainer.cpp @@ -42,20 +42,14 @@ namespace l::ui { bool UIContainer::Accept(UIVisitor& visitor, const InputState& input, const ContainerArea& parent, uint32_t flags) { ContainerArea current; - // compute local scale - current.mScale = mArea.mScale * parent.mScale; - - // scale local position with accumulated world scale and add to accumulated world position - current.mPosition.x = parent.mPosition.x + (mArea.mPosition.x * mArea.mScale) * parent.mScale; - current.mPosition.y = parent.mPosition.y + (mArea.mPosition.y * mArea.mScale) * parent.mScale; - - // scale local size with accumulated world scale - current.mSize.x = mArea.mSize.x * mArea.mScale * parent.mScale; - current.mSize.y = mArea.mSize.y * mArea.mScale * parent.mScale; + current.mScale = mArea.GetWorldScale(parent.mScale); + current.mPosition = mArea.GetWorldPos(parent.mScale, parent.mPosition); + current.mSize = mArea.GetWorldSize(parent.mScale); bool exitOnAccept = (UIContainer_ExitOnAccept & flags) == UIContainer_ExitOnAccept; for (auto& content : mContent) { + if (content->Accept(visitor, input, current, flags)) { if (exitOnAccept) { return true; @@ -135,6 +129,13 @@ namespace l::ui { return ImVec2(mArea.mPosition.x * mArea.mScale, mArea.mPosition.y * mArea.mScale); } + ImVec2 UIContainer::GetPositionAtCenter(ImVec2 offset, bool untransformed) { + if (untransformed) { + return ImVec2(mArea.mPosition.x + mArea.mSize.x * 0.5f + offset.x, mArea.mPosition.y * 0.5f + mArea.mSize.y + offset.y); + } + return ImVec2((mArea.mPosition.x + mArea.mSize.x * 0.5f + offset.x) * mArea.mScale, (mArea.mPosition.y * 0.5f + mArea.mSize.y + offset.y) * mArea.mScale); + } + ImVec2 UIContainer::GetPositionAtSize(ImVec2 offset, bool untransformed) { if (untransformed) { return ImVec2(mArea.mPosition.x + mArea.mSize.x + offset.x, mArea.mPosition.y + mArea.mSize.y + offset.y); From 039ed017d986c1cc530ee0f3aedbe4017ef300e5 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Tue, 20 Aug 2024 17:10:39 +0200 Subject: [PATCH 035/125] Add ui split container. --- .../include/rendering/ui/UIContainer.h | 27 ++++++++++++------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/packages/rendering/include/rendering/ui/UIContainer.h b/packages/rendering/include/rendering/ui/UIContainer.h index 474b24a7..555fc80b 100644 --- a/packages/rendering/include/rendering/ui/UIContainer.h +++ b/packages/rendering/include/rendering/ui/UIContainer.h @@ -92,6 +92,10 @@ namespace l::ui { return transformed; } + float GetWorldScale(float parentScale) { + return parentScale * mScale; + } + // Used in ui container layout, this is where we premultiply parent scale ImVec2 GetWorldPos(float parentScale, ImVec2 parentPos) { ImVec2 worldPos; @@ -100,8 +104,18 @@ namespace l::ui { return worldPos; } - float GetWorldScale(float parentScale) { - return parentScale * mScale; + ImVec2 GetWorldSize(float parentScale) const { + ImVec2 worldSize; + worldSize.x = (mSize.x - mLayout.mBorder * 2.0f) * mScale * parentScale; + worldSize.y = (mSize.y - mLayout.mBorder * 2.0f) * mScale * parentScale; + return worldSize; + } + + ImVec2 GetLocalSize() const { + ImVec2 localSize; + localSize.x = mSize.x / mScale; + localSize.y = mSize.y / mScale; + return localSize; } ImVec2 GetWorldPosLayout(float parentScale, ImVec2 parentPos, ImVec2 contentSize, UIAlignH alignH, UIAlignV alignV) { @@ -131,19 +145,12 @@ namespace l::ui { return worldPos; } - ImVec2 GetWorldSize(float parentScale) const { + ImVec2 GetWorldSizeLayout(float parentScale) const { ImVec2 worldSize; worldSize.x = (mSize.x - mLayout.mBorder * 2.0f) * mScale * parentScale; worldSize.y = (mSize.y - mLayout.mBorder * 2.0f) * mScale * parentScale; return worldSize; } - - ImVec2 GetLocalSize() const { - ImVec2 localSize; - localSize.x = mSize.x / mScale; - localSize.y = mSize.y / mScale; - return localSize; - } }; ImVec2 DragMovement(const ImVec2& prevPos, const ImVec2& curPos, float curScale); From e00139d48583b95ac003bfdd44f5d4eaf7c3e39a Mon Sep 17 00:00:00 2001 From: lnd3 Date: Wed, 21 Aug 2024 15:41:15 +0200 Subject: [PATCH 036/125] Add more rendering options to ui container and a active check for visitors. --- .../include/rendering/ui/UIContainer.h | 14 ++++ .../source/common/ui/UIContainer.cpp | 77 +++++++++++++++---- 2 files changed, 78 insertions(+), 13 deletions(-) diff --git a/packages/rendering/include/rendering/ui/UIContainer.h b/packages/rendering/include/rendering/ui/UIContainer.h index 555fc80b..52ddb833 100644 --- a/packages/rendering/include/rendering/ui/UIContainer.h +++ b/packages/rendering/include/rendering/ui/UIContainer.h @@ -162,6 +162,9 @@ namespace l::ui { class UIVisitor { public: + virtual bool Active(const InputState&) { + return true; + } virtual bool Visit(UIContainer&, const InputState&, const ContainerArea&) { return false; } @@ -213,6 +216,14 @@ namespace l::ui { float GetScale(); void DebugLog(); + const UIRenderData& GetRenderData() const { + return mArea.mRender; + } + + const UILayoutData& GetLayoutData() const { + return mArea.mLayout; + } + friend UIVisitor; protected: std::string mName; @@ -225,11 +236,13 @@ namespace l::ui { class UIZoom : public UIVisitor { public: + virtual bool Active(const InputState& input); virtual bool Visit(UIContainer& container, const InputState& input, const ContainerArea& parent); }; class UIDrag : public UIVisitor { public: + virtual bool Active(const InputState& input); virtual bool Visit(UIContainer& container, const InputState& input, const ContainerArea& parent); protected: bool mDragging = false; @@ -238,6 +251,7 @@ namespace l::ui { class UIMove : public UIVisitor { public: + virtual bool Active(const InputState& input); virtual bool Visit(UIContainer& container, const InputState& input, const ContainerArea& parent); protected: bool mMoving = false; diff --git a/packages/rendering/source/common/ui/UIContainer.cpp b/packages/rendering/source/common/ui/UIContainer.cpp index b0fc8648..086ed4f9 100644 --- a/packages/rendering/source/common/ui/UIContainer.cpp +++ b/packages/rendering/source/common/ui/UIContainer.cpp @@ -37,7 +37,10 @@ namespace l::ui { bool UIContainer::Accept(UIVisitor& visitor, const InputState& input, uint32_t flags) { ContainerArea current; - return Accept(visitor, input, current, flags); + if (visitor.Active(input)) { + return Accept(visitor, input, current, flags); + } + return false; } bool UIContainer::Accept(UIVisitor& visitor, const InputState& input, const ContainerArea& parent, uint32_t flags) { @@ -159,6 +162,9 @@ namespace l::ui { } + bool UIZoom::Active(const InputState& input) { + return input.mScroll != 0; + } bool UIZoom::Visit(UIContainer& container, const InputState& input, const ContainerArea& parent) { if (input.mScroll != 0.0f) { @@ -184,6 +190,10 @@ namespace l::ui { return false; } + bool UIDrag::Active(const InputState& input) { + return input.mStarted && !mDragging || mDragging; + } + bool UIDrag::Visit(UIContainer& container, const InputState& input, const ContainerArea& parent) { if (input.mStarted && !mDragging) { if (input.GetLocalPos().x >= 0.0f && input.GetLocalPos().y >= 0.0f) { @@ -205,6 +215,10 @@ namespace l::ui { return false; } + bool UIMove::Active(const InputState& input) { + return input.mStarted && !mMoving || mMoving; + } + bool UIMove::Visit(UIContainer& container, const InputState& input, const ContainerArea& parent) { if (input.mStarted && !mMoving) { if (Overlap(input.GetLocalPos(), container.GetPosition(), container.GetPositionAtSize(), parent)) { @@ -270,25 +284,62 @@ namespace l::ui { container.DebugLog(); } + ImU32 color = container.GetRenderData().mColor; ImVec2 pTopLeft = container.GetPosition(); + ImVec2 pCenter = container.GetPositionAtCenter(); ImVec2 pLowRight = container.GetPositionAtSize(); - + ImVec2 pSize = container.GetSize(); ImVec2 p1 = parent.Transform(pTopLeft, input.mRootPos); + ImVec2 p12 = parent.Transform(pCenter, input.mRootPos); ImVec2 p2 = parent.Transform(pLowRight, input.mRootPos); - ImVec4 colf = ImVec4(1.0f, 0.4f, 0.4f, 1.0f); - const ImU32 col = ImColor(colf); - - mDrawList->AddRect(p1, p2, col, 2.0f, ImDrawFlags_RoundCornersAll, 2.0f); + switch (container.GetRenderData().mType) { + case l::ui::UIRenderType::Rect: + mDrawList->AddRect(p1, p2, color, 2.0f, ImDrawFlags_RoundCornersAll, 2.0f); + break; + case l::ui::UIRenderType::RectFilled: + mDrawList->AddRectFilled(p1, p2, color, 2.0f, ImDrawFlags_RoundCornersAll); + break; + case l::ui::UIRenderType::Triangle: + break; + case l::ui::UIRenderType::TriangleFilled: + break; + case l::ui::UIRenderType::Circle: + mDrawList->AddCircle(p1, pSize.x, color, 15, 2.0f); + break; + case l::ui::UIRenderType::CircleFilled: + mDrawList->AddCircleFilled(p1, pSize.x, color, 15); + break; + case l::ui::UIRenderType::Polygon: + break; + case l::ui::UIRenderType::PolygonFilled: + break; + case l::ui::UIRenderType::Spline: + break; + case l::ui::UIRenderType::Text: + break; + case l::ui::UIRenderType::Texture: + break; + } - if (container.HasConfigFlag(ui::UIContainer_ResizeFlag)) { - ImVec2 p3 = parent.Transform(pLowRight, ImVec2(input.mRootPos.x - 3.0f, input.mRootPos.y - 3.0f)); - ImVec2 p4 = parent.Transform(pLowRight, ImVec2(input.mRootPos.x + 3.0f, input.mRootPos.y + 3.0f)); - if (container.HasNotification(ui::UIContainer_ResizeFlag)) { - p3 = parent.Transform(pLowRight, ImVec2(input.mRootPos.x - 5.0f, input.mRootPos.y - 5.0f)); - p4 = parent.Transform(pLowRight, ImVec2(input.mRootPos.x + 5.0f, input.mRootPos.y + 5.0f)); + switch (container.GetRenderData().mType) { + case l::ui::UIRenderType::Rect: + case l::ui::UIRenderType::RectFilled: + case l::ui::UIRenderType::Texture: + mDrawList->AddRect(p1, p2, color, 2.0f, ImDrawFlags_RoundCornersAll, 2.0f); + + if (container.HasConfigFlag(ui::UIContainer_ResizeFlag)) { + ImVec2 p3 = parent.Transform(pLowRight, ImVec2(input.mRootPos.x - 3.0f, input.mRootPos.y - 3.0f)); + ImVec2 p4 = parent.Transform(pLowRight, ImVec2(input.mRootPos.x + 3.0f, input.mRootPos.y + 3.0f)); + if (container.HasNotification(ui::UIContainer_ResizeFlag)) { + p3 = parent.Transform(pLowRight, ImVec2(input.mRootPos.x - 5.0f, input.mRootPos.y - 5.0f)); + p4 = parent.Transform(pLowRight, ImVec2(input.mRootPos.x + 5.0f, input.mRootPos.y + 5.0f)); + } + mDrawList->AddRectFilled(p3, p4, color); } - mDrawList->AddRectFilled(p3, p4, col); + break; + default: + break; } return false; From e995d5d3f415fdf4f51d33bced79c2716b828dff Mon Sep 17 00:00:00 2001 From: lnd3 Date: Wed, 21 Aug 2024 16:17:27 +0200 Subject: [PATCH 037/125] Prepare for better container traversal. --- .../include/rendering/ui/UIContainer.h | 10 +- .../source/common/ui/UIContainer.cpp | 180 ++++++++++-------- 2 files changed, 104 insertions(+), 86 deletions(-) diff --git a/packages/rendering/include/rendering/ui/UIContainer.h b/packages/rendering/include/rendering/ui/UIContainer.h index 52ddb833..fde2249f 100644 --- a/packages/rendering/include/rendering/ui/UIContainer.h +++ b/packages/rendering/include/rendering/ui/UIContainer.h @@ -42,6 +42,12 @@ namespace l::ui { Texture = 10 }; + enum class UITraversalMode { + All = 0, // when a visitor performs an action on all containers of its type for example rendering + Once = 1, // when a visitor performs an action on one container of its type for example resizing + Twice = 2 // when a visitor performs an action on two containers of its type for example drag and drop actions like connecting input/output between two containers + }; + struct UIRenderData { UIRenderType mType = UIRenderType::Rect; ImU32 mColor = ImColor(ImVec4(1.0f, 0.4f, 0.4f, 1.0f)); @@ -194,8 +200,8 @@ namespace l::ui { UIContainer(std::string_view name, uint32_t flags = 0) : mName(name), mConfigFlags(flags) {} ~UIContainer() = default; - virtual bool Accept(UIVisitor& visitor, const InputState& input, uint32_t flags = 0); - virtual bool Accept(UIVisitor& visitor, const InputState& input, const ContainerArea& parent, uint32_t flags = 0); + bool Accept(UIVisitor& visitor, const InputState& input, UITraversalMode mode = UITraversalMode::All); + virtual bool Accept(UIVisitor& visitor, const InputState& input, const ContainerArea& parent, UITraversalMode mode = UITraversalMode::All); virtual void Add(UIContainer* container, int32_t i = -1); virtual void Remove(int32_t i); diff --git a/packages/rendering/source/common/ui/UIContainer.cpp b/packages/rendering/source/common/ui/UIContainer.cpp index 086ed4f9..e331a7c2 100644 --- a/packages/rendering/source/common/ui/UIContainer.cpp +++ b/packages/rendering/source/common/ui/UIContainer.cpp @@ -35,32 +35,30 @@ namespace l::ui { return d.x*d.x + d.y*d.y < radii * radii; } - bool UIContainer::Accept(UIVisitor& visitor, const InputState& input, uint32_t flags) { + bool UIContainer::Accept(UIVisitor& visitor, const InputState& input, UITraversalMode mode) { ContainerArea current; if (visitor.Active(input)) { - return Accept(visitor, input, current, flags); + return Accept(visitor, input, current, mode); } return false; } - bool UIContainer::Accept(UIVisitor& visitor, const InputState& input, const ContainerArea& parent, uint32_t flags) { + bool UIContainer::Accept(UIVisitor& visitor, const InputState& input, const ContainerArea& parent, UITraversalMode mode) { ContainerArea current; current.mScale = mArea.GetWorldScale(parent.mScale); current.mPosition = mArea.GetWorldPos(parent.mScale, parent.mPosition); current.mSize = mArea.GetWorldSize(parent.mScale); - bool exitOnAccept = (UIContainer_ExitOnAccept & flags) == UIContainer_ExitOnAccept; - for (auto& content : mContent) { - if (content->Accept(visitor, input, current, flags)) { - if (exitOnAccept) { + if (content->Accept(visitor, input, current, mode)) { + if (mode == UITraversalMode::Once) { return true; } } } - if ((flags & mConfigFlags) == (flags & UIContainer_ConfigMask)) { + if (visitor.Active(input)) { return visitor.Visit(*this, input, parent); } return false; @@ -121,105 +119,114 @@ namespace l::ui { mArea.mSize = s; } - void UIContainer::SetContainerArea(const ContainerArea& area) { - mArea = area; - } - - ImVec2 UIContainer::GetPosition(bool untransformed) { - if (untransformed) { - return mArea.mPosition; - } - return ImVec2(mArea.mPosition.x * mArea.mScale, mArea.mPosition.y * mArea.mScale); - } +void UIContainer::SetContainerArea(const ContainerArea& area) { + mArea = area; +} - ImVec2 UIContainer::GetPositionAtCenter(ImVec2 offset, bool untransformed) { - if (untransformed) { - return ImVec2(mArea.mPosition.x + mArea.mSize.x * 0.5f + offset.x, mArea.mPosition.y * 0.5f + mArea.mSize.y + offset.y); - } - return ImVec2((mArea.mPosition.x + mArea.mSize.x * 0.5f + offset.x) * mArea.mScale, (mArea.mPosition.y * 0.5f + mArea.mSize.y + offset.y) * mArea.mScale); +ImVec2 UIContainer::GetPosition(bool untransformed) { + if (untransformed) { + return mArea.mPosition; } + return ImVec2(mArea.mPosition.x * mArea.mScale, mArea.mPosition.y * mArea.mScale); +} - ImVec2 UIContainer::GetPositionAtSize(ImVec2 offset, bool untransformed) { - if (untransformed) { - return ImVec2(mArea.mPosition.x + mArea.mSize.x + offset.x, mArea.mPosition.y + mArea.mSize.y + offset.y); - } - return ImVec2((mArea.mPosition.x + mArea.mSize.x + offset.x) * mArea.mScale, (mArea.mPosition.y + mArea.mSize.y + offset.y) * mArea.mScale); +ImVec2 UIContainer::GetPositionAtCenter(ImVec2 offset, bool untransformed) { + if (untransformed) { + return ImVec2(mArea.mPosition.x + mArea.mSize.x * 0.5f + offset.x, mArea.mPosition.y * 0.5f + mArea.mSize.y + offset.y); } + return ImVec2((mArea.mPosition.x + mArea.mSize.x * 0.5f + offset.x) * mArea.mScale, (mArea.mPosition.y * 0.5f + mArea.mSize.y + offset.y) * mArea.mScale); +} - ImVec2 UIContainer::GetSize(bool untransformed) { - if (untransformed) { - return mArea.mSize; - } - return ImVec2(mArea.mSize.x * mArea.mScale, mArea.mSize.y * mArea.mScale); +ImVec2 UIContainer::GetPositionAtSize(ImVec2 offset, bool untransformed) { + if (untransformed) { + return ImVec2(mArea.mPosition.x + mArea.mSize.x + offset.x, mArea.mPosition.y + mArea.mSize.y + offset.y); } + return ImVec2((mArea.mPosition.x + mArea.mSize.x + offset.x) * mArea.mScale, (mArea.mPosition.y + mArea.mSize.y + offset.y) * mArea.mScale); +} - float UIContainer::GetScale() { - return mArea.mScale; +ImVec2 UIContainer::GetSize(bool untransformed) { + if (untransformed) { + return mArea.mSize; } + return ImVec2(mArea.mSize.x * mArea.mScale, mArea.mSize.y * mArea.mScale); +} - void UIContainer::DebugLog() { - LOG(LogDebug) << "UIContainer: " << mName << ", [" << mArea.mScale << "][" << mArea.mPosition.x << ", " << mArea.mPosition.y << "][" << mArea.mSize.x << ", " << mArea.mSize.y << "]"; - } +float UIContainer::GetScale() { + return mArea.mScale; +} +void UIContainer::DebugLog() { + LOG(LogDebug) << "UIContainer: " << mName << ", [" << mArea.mScale << "][" << mArea.mPosition.x << ", " << mArea.mPosition.y << "][" << mArea.mSize.x << ", " << mArea.mSize.y << "]"; +} - bool UIZoom::Active(const InputState& input) { - return input.mScroll != 0; - } - bool UIZoom::Visit(UIContainer& container, const InputState& input, const ContainerArea& parent) { - if (input.mScroll != 0.0f) { - float scaleChange = 1.0f; - float scaleDelta = 0.1f; - scaleChange = 1.0f + scaleDelta * input.mScroll; - if (input.mScroll < 0.0f) { - scaleChange = 1.0f / (1.0f - scaleDelta * input.mScroll); - } - if ((container.GetScale() > 100.0f && scaleChange > 1.0f) || (container.GetScale() < 0.01f && scaleChange < 1.0f)) { - return true; - } +bool UIZoom::Active(const InputState& input) { + return input.mScroll != 0; +} - ImVec2 mousePos = input.GetLocalPos(); - ImVec2 localMousePos = ImVec2(parent.mPosition.x - mousePos.x, parent.mPosition.y - mousePos.y); - ImVec2 p = container.GetPosition(); - container.Rescale(scaleChange); - p.x = ((p.x + localMousePos.x) * scaleChange - localMousePos.x) / container.GetScale(); - p.y = ((p.y + localMousePos.y) * scaleChange - localMousePos.y) / container.GetScale(); - container.SetPosition(p); - return true; - } +bool UIZoom::Visit(UIContainer& container, const InputState& input, const ContainerArea& parent) { + if (!container.HasConfigFlag(UIContainer_ZoomFlag)) { return false; } + if (input.mScroll != 0.0f) { + float scaleChange = 1.0f; + float scaleDelta = 0.1f; + scaleChange = 1.0f + scaleDelta * input.mScroll; + if (input.mScroll < 0.0f) { + scaleChange = 1.0f / (1.0f - scaleDelta * input.mScroll); + } + if ((container.GetScale() > 100.0f && scaleChange > 1.0f) || (container.GetScale() < 0.01f && scaleChange < 1.0f)) { + return true; + } - bool UIDrag::Active(const InputState& input) { - return input.mStarted && !mDragging || mDragging; + ImVec2 mousePos = input.GetLocalPos(); + ImVec2 localMousePos = ImVec2(parent.mPosition.x - mousePos.x, parent.mPosition.y - mousePos.y); + ImVec2 p = container.GetPosition(); + container.Rescale(scaleChange); + p.x = ((p.x + localMousePos.x) * scaleChange - localMousePos.x) / container.GetScale(); + p.y = ((p.y + localMousePos.y) * scaleChange - localMousePos.y) / container.GetScale(); + container.SetPosition(p); + return true; } + return false; +} - bool UIDrag::Visit(UIContainer& container, const InputState& input, const ContainerArea& parent) { - if (input.mStarted && !mDragging) { - if (input.GetLocalPos().x >= 0.0f && input.GetLocalPos().y >= 0.0f) { - mDragging = true; - mCurrentContainer = &container; - } - } - if (mDragging && mCurrentContainer == &container) { - ImVec2 move = DragMovement(input.mPrevPos, input.mCurPos, parent.mScale * container.GetScale()); - container.Move(move); - container.Notification(UIContainer_DragFlag); +bool UIDrag::Active(const InputState& input) { + return input.mStarted && !mDragging || mDragging; +} - if (input.mStopped) { - mDragging = false; - mCurrentContainer = nullptr; - } - return mDragging; - } +bool UIDrag::Visit(UIContainer& container, const InputState& input, const ContainerArea& parent) { + if (!container.HasConfigFlag(UIContainer_DragFlag)) { return false; } + if (input.mStarted && !mDragging) { + if (input.GetLocalPos().x >= 0.0f && input.GetLocalPos().y >= 0.0f) { + mDragging = true; + mCurrentContainer = &container; + } + } + if (mDragging && mCurrentContainer == &container) { + ImVec2 move = DragMovement(input.mPrevPos, input.mCurPos, parent.mScale * container.GetScale()); + container.Move(move); + container.Notification(UIContainer_DragFlag); - bool UIMove::Active(const InputState& input) { - return input.mStarted && !mMoving || mMoving; + if (input.mStopped) { + mDragging = false; + mCurrentContainer = nullptr; + } + return mDragging; } + return false; +} - bool UIMove::Visit(UIContainer& container, const InputState& input, const ContainerArea& parent) { +bool UIMove::Active(const InputState& input) { + return input.mStarted && !mMoving || mMoving; +} + +bool UIMove::Visit(UIContainer& container, const InputState& input, const ContainerArea& parent) { + if(!container.HasConfigFlag(UIContainer_MoveFlag)){ + return false; + } if (input.mStarted && !mMoving) { if (Overlap(input.GetLocalPos(), container.GetPosition(), container.GetPositionAtSize(), parent)) { mMoving = true; @@ -241,6 +248,9 @@ namespace l::ui { } bool UIResize::Visit(UIContainer& container, const InputState& input, const ContainerArea& parent) { + if (!container.HasConfigFlag(UIContainer_ResizeFlag)) { + return false; + } if (!mResizing) { const float radii = mResizeAreaSize * 0.5f; ImVec2 p = container.GetPositionAtSize(); @@ -279,7 +289,9 @@ namespace l::ui { } bool UIDraw::Visit(UIContainer& container, const InputState& input, const ContainerArea& parent) { - + if (!container.HasConfigFlag(UIContainer_DrawFlag)) { + return false; + } if (mDebugLog) { container.DebugLog(); } From 5d7eedc4f84159b4b2404e60deb4968f79d0b30c Mon Sep 17 00:00:00 2001 From: lnd3 Date: Wed, 21 Aug 2024 16:18:49 +0200 Subject: [PATCH 038/125] Fix warnings. --- packages/rendering/source/common/ui/UIContainer.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/rendering/source/common/ui/UIContainer.cpp b/packages/rendering/source/common/ui/UIContainer.cpp index e331a7c2..f12dd191 100644 --- a/packages/rendering/source/common/ui/UIContainer.cpp +++ b/packages/rendering/source/common/ui/UIContainer.cpp @@ -192,7 +192,7 @@ bool UIZoom::Visit(UIContainer& container, const InputState& input, const Contai } bool UIDrag::Active(const InputState& input) { - return input.mStarted && !mDragging || mDragging; + return (input.mStarted && !mDragging) || mDragging; } bool UIDrag::Visit(UIContainer& container, const InputState& input, const ContainerArea& parent) { @@ -220,7 +220,7 @@ bool UIDrag::Visit(UIContainer& container, const InputState& input, const Contai } bool UIMove::Active(const InputState& input) { - return input.mStarted && !mMoving || mMoving; + return (input.mStarted && !mMoving) || mMoving; } bool UIMove::Visit(UIContainer& container, const InputState& input, const ContainerArea& parent) { @@ -317,10 +317,10 @@ bool UIMove::Visit(UIContainer& container, const InputState& input, const Contai case l::ui::UIRenderType::TriangleFilled: break; case l::ui::UIRenderType::Circle: - mDrawList->AddCircle(p1, pSize.x, color, 15, 2.0f); + mDrawList->AddCircle(p12, pSize.x, color, 15, 2.0f); break; case l::ui::UIRenderType::CircleFilled: - mDrawList->AddCircleFilled(p1, pSize.x, color, 15); + mDrawList->AddCircleFilled(p12, pSize.x, color, 15); break; case l::ui::UIRenderType::Polygon: break; From f9867e8724c70def5099a4cfbe843c95a64743b5 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Wed, 21 Aug 2024 16:40:07 +0200 Subject: [PATCH 039/125] Add uisplit and uilayout containers. --- .../include/rendering/ui/UIContainer.h | 51 +-- .../include/rendering/ui/UIVisitors.h | 61 ++++ .../source/common/ui/UIContainer.cpp | 325 ++++++------------ .../rendering/source/common/ui/UIVisitors.cpp | 202 +++++++++++ 4 files changed, 377 insertions(+), 262 deletions(-) create mode 100644 packages/rendering/include/rendering/ui/UIVisitors.h create mode 100644 packages/rendering/source/common/ui/UIVisitors.cpp diff --git a/packages/rendering/include/rendering/ui/UIContainer.h b/packages/rendering/include/rendering/ui/UIContainer.h index fde2249f..f73f8e74 100644 --- a/packages/rendering/include/rendering/ui/UIContainer.h +++ b/packages/rendering/include/rendering/ui/UIContainer.h @@ -240,48 +240,27 @@ namespace l::ui { std::vector mContent; }; - class UIZoom : public UIVisitor { + class UISplit : public UIContainer { public: - virtual bool Active(const InputState& input); - virtual bool Visit(UIContainer& container, const InputState& input, const ContainerArea& parent); - }; - - class UIDrag : public UIVisitor { - public: - virtual bool Active(const InputState& input); - virtual bool Visit(UIContainer& container, const InputState& input, const ContainerArea& parent); - protected: - bool mDragging = false; - UIContainer* mCurrentContainer = nullptr; - }; - - class UIMove : public UIVisitor { - public: - virtual bool Active(const InputState& input); - virtual bool Visit(UIContainer& container, const InputState& input, const ContainerArea& parent); - protected: - bool mMoving = false; - UIContainer* mCurrentContainer = nullptr; - }; + UISplit(std::string_view name, uint32_t flags, bool horizontalSplit, float border) : UIContainer(name, flags), mHorizontalSplit(horizontalSplit) { + mArea.mLayout.mBorder = border; + } + ~UISplit() = default; - class UIResize : public UIVisitor { - public: - virtual bool Visit(UIContainer& container, const InputState& input, const ContainerArea& parent); + virtual bool Accept(UIVisitor& visitor, const InputState& input, const ContainerArea& parent, UITraversalMode mode); protected: - bool mResizing = false; - float mResizeAreaSize = 8.0f; - UIContainer* mCurrentContainer = nullptr; + bool mHorizontalSplit; }; - class UIDraw : public UIVisitor { + class UILayout : public UIContainer { public: - UIDraw(ImDrawList* drawList) : mDrawList(drawList) {} - ~UIDraw() = default; + UILayout(std::string_view name, uint32_t flags, UIAlignH alignH, UIAlignV alignV, float border) : UIContainer(name, flags) { + mArea.mLayout.mAlignH = alignH; + mArea.mLayout.mAlignV = alignV; + mArea.mLayout.mBorder = border; + } + ~UILayout() = default; - void DebugLog(); - virtual bool Visit(UIContainer& container, const InputState& input, const ContainerArea& parent); - protected: - ImDrawList* mDrawList; - bool mDebugLog = false; + virtual bool Accept(UIVisitor& visitor, const InputState& input, const ContainerArea& parent, UITraversalMode mode); }; } diff --git a/packages/rendering/include/rendering/ui/UIVisitors.h b/packages/rendering/include/rendering/ui/UIVisitors.h new file mode 100644 index 00000000..f21b5365 --- /dev/null +++ b/packages/rendering/include/rendering/ui/UIVisitors.h @@ -0,0 +1,61 @@ +#pragma once + +#include "rendering/ui/UIContainer.h" + +#define GLFW_INCLUDE_NONE +#include + +#include "imgui/imgui.h" +#include "imgui/imgui_internal.h" +#include "imgui/imgui_impl_glfw.h" +#include "imgui/imgui_impl_opengl3.h" +#include "implot/implot.h" +#include "implot/implot_internal.h" + +namespace l::ui { + + class UIZoom : public UIVisitor { + public: + virtual bool Active(const InputState& input); + virtual bool Visit(UIContainer& container, const InputState& input, const ContainerArea& parent); + }; + + class UIDrag : public UIVisitor { + public: + virtual bool Active(const InputState& input); + virtual bool Visit(UIContainer& container, const InputState& input, const ContainerArea& parent); + protected: + bool mDragging = false; + UIContainer* mCurrentContainer = nullptr; + }; + + class UIMove : public UIVisitor { + public: + virtual bool Active(const InputState& input); + virtual bool Visit(UIContainer& container, const InputState& input, const ContainerArea& parent); + protected: + bool mMoving = false; + UIContainer* mCurrentContainer = nullptr; + }; + + class UIResize : public UIVisitor { + public: + virtual bool Visit(UIContainer& container, const InputState& input, const ContainerArea& parent); + protected: + bool mResizing = false; + float mResizeAreaSize = 8.0f; + UIContainer* mCurrentContainer = nullptr; + }; + + class UIDraw : public UIVisitor { + public: + UIDraw(ImDrawList* drawList) : mDrawList(drawList) {} + ~UIDraw() = default; + + void DebugLog(); + virtual bool Visit(UIContainer& container, const InputState& input, const ContainerArea& parent); + protected: + ImDrawList* mDrawList; + bool mDebugLog = false; + }; +} diff --git a/packages/rendering/source/common/ui/UIContainer.cpp b/packages/rendering/source/common/ui/UIContainer.cpp index f12dd191..29702f4f 100644 --- a/packages/rendering/source/common/ui/UIContainer.cpp +++ b/packages/rendering/source/common/ui/UIContainer.cpp @@ -35,35 +35,6 @@ namespace l::ui { return d.x*d.x + d.y*d.y < radii * radii; } - bool UIContainer::Accept(UIVisitor& visitor, const InputState& input, UITraversalMode mode) { - ContainerArea current; - if (visitor.Active(input)) { - return Accept(visitor, input, current, mode); - } - return false; - } - - bool UIContainer::Accept(UIVisitor& visitor, const InputState& input, const ContainerArea& parent, UITraversalMode mode) { - ContainerArea current; - current.mScale = mArea.GetWorldScale(parent.mScale); - current.mPosition = mArea.GetWorldPos(parent.mScale, parent.mPosition); - current.mSize = mArea.GetWorldSize(parent.mScale); - - for (auto& content : mContent) { - - if (content->Accept(visitor, input, current, mode)) { - if (mode == UITraversalMode::Once) { - return true; - } - } - } - - if (visitor.Active(input)) { - return visitor.Visit(*this, input, parent); - } - return false; - } - void UIContainer::Add(UIContainer* container, int32_t i) { if (i < 0) { mContent.push_back(container); @@ -119,242 +90,144 @@ namespace l::ui { mArea.mSize = s; } -void UIContainer::SetContainerArea(const ContainerArea& area) { - mArea = area; -} - -ImVec2 UIContainer::GetPosition(bool untransformed) { - if (untransformed) { - return mArea.mPosition; + void UIContainer::SetContainerArea(const ContainerArea& area) { + mArea = area; } - return ImVec2(mArea.mPosition.x * mArea.mScale, mArea.mPosition.y * mArea.mScale); -} -ImVec2 UIContainer::GetPositionAtCenter(ImVec2 offset, bool untransformed) { - if (untransformed) { - return ImVec2(mArea.mPosition.x + mArea.mSize.x * 0.5f + offset.x, mArea.mPosition.y * 0.5f + mArea.mSize.y + offset.y); + ImVec2 UIContainer::GetPosition(bool untransformed) { + if (untransformed) { + return mArea.mPosition; + } + return ImVec2(mArea.mPosition.x * mArea.mScale, mArea.mPosition.y * mArea.mScale); } - return ImVec2((mArea.mPosition.x + mArea.mSize.x * 0.5f + offset.x) * mArea.mScale, (mArea.mPosition.y * 0.5f + mArea.mSize.y + offset.y) * mArea.mScale); -} -ImVec2 UIContainer::GetPositionAtSize(ImVec2 offset, bool untransformed) { - if (untransformed) { - return ImVec2(mArea.mPosition.x + mArea.mSize.x + offset.x, mArea.mPosition.y + mArea.mSize.y + offset.y); + ImVec2 UIContainer::GetPositionAtCenter(ImVec2 offset, bool untransformed) { + if (untransformed) { + return ImVec2(mArea.mPosition.x + mArea.mSize.x * 0.5f + offset.x, mArea.mPosition.y * 0.5f + mArea.mSize.y + offset.y); + } + return ImVec2((mArea.mPosition.x + mArea.mSize.x * 0.5f + offset.x) * mArea.mScale, (mArea.mPosition.y * 0.5f + mArea.mSize.y + offset.y) * mArea.mScale); } - return ImVec2((mArea.mPosition.x + mArea.mSize.x + offset.x) * mArea.mScale, (mArea.mPosition.y + mArea.mSize.y + offset.y) * mArea.mScale); -} -ImVec2 UIContainer::GetSize(bool untransformed) { - if (untransformed) { - return mArea.mSize; + ImVec2 UIContainer::GetPositionAtSize(ImVec2 offset, bool untransformed) { + if (untransformed) { + return ImVec2(mArea.mPosition.x + mArea.mSize.x + offset.x, mArea.mPosition.y + mArea.mSize.y + offset.y); + } + return ImVec2((mArea.mPosition.x + mArea.mSize.x + offset.x) * mArea.mScale, (mArea.mPosition.y + mArea.mSize.y + offset.y) * mArea.mScale); } - return ImVec2(mArea.mSize.x * mArea.mScale, mArea.mSize.y * mArea.mScale); -} -float UIContainer::GetScale() { - return mArea.mScale; -} - -void UIContainer::DebugLog() { - LOG(LogDebug) << "UIContainer: " << mName << ", [" << mArea.mScale << "][" << mArea.mPosition.x << ", " << mArea.mPosition.y << "][" << mArea.mSize.x << ", " << mArea.mSize.y << "]"; -} + ImVec2 UIContainer::GetSize(bool untransformed) { + if (untransformed) { + return mArea.mSize; + } + return ImVec2(mArea.mSize.x * mArea.mScale, mArea.mSize.y * mArea.mScale); + } + float UIContainer::GetScale() { + return mArea.mScale; + } -bool UIZoom::Active(const InputState& input) { - return input.mScroll != 0; -} + void UIContainer::DebugLog() { + LOG(LogDebug) << "UIContainer: " << mName << ", [" << mArea.mScale << "][" << mArea.mPosition.x << ", " << mArea.mPosition.y << "][" << mArea.mSize.x << ", " << mArea.mSize.y << "]"; + } -bool UIZoom::Visit(UIContainer& container, const InputState& input, const ContainerArea& parent) { - if (!container.HasConfigFlag(UIContainer_ZoomFlag)) { + bool UIContainer::Accept(UIVisitor& visitor, const InputState& input, UITraversalMode mode) { + ContainerArea current; + if (visitor.Active(input)) { + return Accept(visitor, input, current, mode); + } return false; } - if (input.mScroll != 0.0f) { - float scaleChange = 1.0f; - float scaleDelta = 0.1f; - scaleChange = 1.0f + scaleDelta * input.mScroll; - if (input.mScroll < 0.0f) { - scaleChange = 1.0f / (1.0f - scaleDelta * input.mScroll); - } - if ((container.GetScale() > 100.0f && scaleChange > 1.0f) || (container.GetScale() < 0.01f && scaleChange < 1.0f)) { - return true; - } - ImVec2 mousePos = input.GetLocalPos(); - ImVec2 localMousePos = ImVec2(parent.mPosition.x - mousePos.x, parent.mPosition.y - mousePos.y); - ImVec2 p = container.GetPosition(); - container.Rescale(scaleChange); - p.x = ((p.x + localMousePos.x) * scaleChange - localMousePos.x) / container.GetScale(); - p.y = ((p.y + localMousePos.y) * scaleChange - localMousePos.y) / container.GetScale(); - container.SetPosition(p); - return true; - } - return false; -} + bool UIContainer::Accept(UIVisitor& visitor, const InputState& input, const ContainerArea& parent, UITraversalMode mode) { + ContainerArea current; + current.mScale = mArea.GetWorldScale(parent.mScale); + current.mPosition = mArea.GetWorldPos(parent.mScale, parent.mPosition); + current.mSize = mArea.GetWorldSize(parent.mScale); -bool UIDrag::Active(const InputState& input) { - return (input.mStarted && !mDragging) || mDragging; -} + for (auto& content : mContent) { -bool UIDrag::Visit(UIContainer& container, const InputState& input, const ContainerArea& parent) { - if (!container.HasConfigFlag(UIContainer_DragFlag)) { - return false; - } - if (input.mStarted && !mDragging) { - if (input.GetLocalPos().x >= 0.0f && input.GetLocalPos().y >= 0.0f) { - mDragging = true; - mCurrentContainer = &container; + if (content->Accept(visitor, input, current, mode)) { + if (mode == UITraversalMode::Once) { + return true; + } + } } - } - if (mDragging && mCurrentContainer == &container) { - ImVec2 move = DragMovement(input.mPrevPos, input.mCurPos, parent.mScale * container.GetScale()); - container.Move(move); - container.Notification(UIContainer_DragFlag); - - if (input.mStopped) { - mDragging = false; - mCurrentContainer = nullptr; + + if (visitor.Active(input)) { + return visitor.Visit(*this, input, parent); } - return mDragging; + return false; } - return false; -} -bool UIMove::Active(const InputState& input) { - return (input.mStarted && !mMoving) || mMoving; -} + bool UISplit::Accept(UIVisitor& visitor, const InputState& input, const ContainerArea& parent, UITraversalMode mode) { + // Since we can have multiple layouts in a container for different content, it will act as + // an anchor rather than a container, therefore it has to align within it and size + mArea.mSize = parent.GetLocalSize(); -bool UIMove::Visit(UIContainer& container, const InputState& input, const ContainerArea& parent) { - if(!container.HasConfigFlag(UIContainer_MoveFlag)){ - return false; - } - if (input.mStarted && !mMoving) { - if (Overlap(input.GetLocalPos(), container.GetPosition(), container.GetPositionAtSize(), parent)) { - mMoving = true; - mCurrentContainer = &container; + float contentCount = static_cast(mContent.size()); + + int32_t i = 0; + for (auto& content : mContent) { + auto contentSize = content->GetSize(); + + ContainerArea current; + current.mScale = mArea.GetWorldScale(parent.mScale); + current.mSize = mArea.GetWorldSize(parent.mScale); + current.mPosition = mArea.GetWorldPos(parent.mScale, parent.mPosition); + + + ImVec2 size = mArea.mSize; + size.x -= mArea.mLayout.mBorder * 2.0f; + size.y -= mArea.mLayout.mBorder * 2.0f; + + float split = i++ / contentCount; + if (mHorizontalSplit) { + current.mPosition.x += current.mSize.x * split; + size.x /= contentCount; } - } - if (mMoving && mCurrentContainer == &container) { - ImVec2 move = DragMovement(input.mPrevPos, input.mCurPos, parent.mScale); - container.Move(move); - container.Notification(UIContainer_MoveFlag); - - if (input.mStopped) { - mMoving = false; - mCurrentContainer = nullptr; + else { + current.mPosition.y += current.mSize.y * split; + size.y /= contentCount; } - return mMoving; - } - return false; - } + content->SetSize(size); - bool UIResize::Visit(UIContainer& container, const InputState& input, const ContainerArea& parent) { - if (!container.HasConfigFlag(UIContainer_ResizeFlag)) { - return false; - } - if (!mResizing) { - const float radii = mResizeAreaSize * 0.5f; - ImVec2 p = container.GetPositionAtSize(); - if (OverlapScreenRect(input.GetLocalPos(), p, ImVec2(radii, radii), parent)) { - mCurrentContainer = &container; - container.Notification(UIContainer_ResizeFlag); - - if (input.mStarted) { - mResizing = true; - } - else { + + if (content->Accept(visitor, input, current, mode)) { + if (mode == UITraversalMode::Once) { return true; } } - else { - mCurrentContainer = nullptr; - container.ClearNotifications(); - } } - if (mResizing && mCurrentContainer == &container) { - ImVec2 move = DragMovement(input.mPrevPos, input.mCurPos, parent.mScale); - container.Resize(move); - - if (input.mStopped) { - mResizing = false; - mCurrentContainer = nullptr; - container.ClearNotifications(); - } - return mResizing; + + if (visitor.Active(input)) { + return visitor.Visit(*this, input, parent); } return false; } - void UIDraw::DebugLog() { - mDebugLog = true; - } + bool UILayout::Accept(UIVisitor& visitor, const InputState& input, const ContainerArea& parent, UITraversalMode mode) { + // Since we can have multiple layout in a container for different content, it will act as + // an anchor rather than a container, therefore it has to align within it and size + mArea.mSize = parent.GetLocalSize(); - bool UIDraw::Visit(UIContainer& container, const InputState& input, const ContainerArea& parent) { - if (!container.HasConfigFlag(UIContainer_DrawFlag)) { - return false; - } - if (mDebugLog) { - container.DebugLog(); - } + for (auto& content : mContent) { + auto contentSize = content->GetSize(); - ImU32 color = container.GetRenderData().mColor; - ImVec2 pTopLeft = container.GetPosition(); - ImVec2 pCenter = container.GetPositionAtCenter(); - ImVec2 pLowRight = container.GetPositionAtSize(); - ImVec2 pSize = container.GetSize(); - ImVec2 p1 = parent.Transform(pTopLeft, input.mRootPos); - ImVec2 p12 = parent.Transform(pCenter, input.mRootPos); - ImVec2 p2 = parent.Transform(pLowRight, input.mRootPos); - - switch (container.GetRenderData().mType) { - case l::ui::UIRenderType::Rect: - mDrawList->AddRect(p1, p2, color, 2.0f, ImDrawFlags_RoundCornersAll, 2.0f); - break; - case l::ui::UIRenderType::RectFilled: - mDrawList->AddRectFilled(p1, p2, color, 2.0f, ImDrawFlags_RoundCornersAll); - break; - case l::ui::UIRenderType::Triangle: - break; - case l::ui::UIRenderType::TriangleFilled: - break; - case l::ui::UIRenderType::Circle: - mDrawList->AddCircle(p12, pSize.x, color, 15, 2.0f); - break; - case l::ui::UIRenderType::CircleFilled: - mDrawList->AddCircleFilled(p12, pSize.x, color, 15); - break; - case l::ui::UIRenderType::Polygon: - break; - case l::ui::UIRenderType::PolygonFilled: - break; - case l::ui::UIRenderType::Spline: - break; - case l::ui::UIRenderType::Text: - break; - case l::ui::UIRenderType::Texture: - break; - } + ContainerArea current; + current.mScale = mArea.GetWorldScale(parent.mScale); + current.mSize = mArea.GetWorldSizeLayout(parent.mScale); + current.mPosition = mArea.GetWorldPosLayout(parent.mScale, parent.mPosition, contentSize, mArea.mLayout.mAlignH, mArea.mLayout.mAlignV); - switch (container.GetRenderData().mType) { - case l::ui::UIRenderType::Rect: - case l::ui::UIRenderType::RectFilled: - case l::ui::UIRenderType::Texture: - mDrawList->AddRect(p1, p2, color, 2.0f, ImDrawFlags_RoundCornersAll, 2.0f); - - if (container.HasConfigFlag(ui::UIContainer_ResizeFlag)) { - ImVec2 p3 = parent.Transform(pLowRight, ImVec2(input.mRootPos.x - 3.0f, input.mRootPos.y - 3.0f)); - ImVec2 p4 = parent.Transform(pLowRight, ImVec2(input.mRootPos.x + 3.0f, input.mRootPos.y + 3.0f)); - if (container.HasNotification(ui::UIContainer_ResizeFlag)) { - p3 = parent.Transform(pLowRight, ImVec2(input.mRootPos.x - 5.0f, input.mRootPos.y - 5.0f)); - p4 = parent.Transform(pLowRight, ImVec2(input.mRootPos.x + 5.0f, input.mRootPos.y + 5.0f)); + if (content->Accept(visitor, input, current, mode)) { + if (mode == UITraversalMode::Once) { + return true; } - mDrawList->AddRectFilled(p3, p4, color); } - break; - default: - break; } + if (visitor.Active(input)) { + return visitor.Visit(*this, input, parent); + } return false; } - } diff --git a/packages/rendering/source/common/ui/UIVisitors.cpp b/packages/rendering/source/common/ui/UIVisitors.cpp new file mode 100644 index 00000000..fb296ec2 --- /dev/null +++ b/packages/rendering/source/common/ui/UIVisitors.cpp @@ -0,0 +1,202 @@ +#include "rendering/ui/UIVisitors.h" + +namespace l::ui { + +bool UIZoom::Active(const InputState& input) { + return input.mScroll != 0; +} + +bool UIZoom::Visit(UIContainer& container, const InputState& input, const ContainerArea& parent) { + if (!container.HasConfigFlag(UIContainer_ZoomFlag)) { + return false; + } + if (input.mScroll != 0.0f) { + float scaleChange = 1.0f; + float scaleDelta = 0.1f; + scaleChange = 1.0f + scaleDelta * input.mScroll; + if (input.mScroll < 0.0f) { + scaleChange = 1.0f / (1.0f - scaleDelta * input.mScroll); + } + if ((container.GetScale() > 100.0f && scaleChange > 1.0f) || (container.GetScale() < 0.01f && scaleChange < 1.0f)) { + return true; + } + + ImVec2 mousePos = input.GetLocalPos(); + ImVec2 localMousePos = ImVec2(parent.mPosition.x - mousePos.x, parent.mPosition.y - mousePos.y); + ImVec2 p = container.GetPosition(); + container.Rescale(scaleChange); + p.x = ((p.x + localMousePos.x) * scaleChange - localMousePos.x) / container.GetScale(); + p.y = ((p.y + localMousePos.y) * scaleChange - localMousePos.y) / container.GetScale(); + container.SetPosition(p); + return true; + } + return false; +} + +bool UIDrag::Active(const InputState& input) { + return (input.mStarted && !mDragging) || mDragging; +} + +bool UIDrag::Visit(UIContainer& container, const InputState& input, const ContainerArea& parent) { + if (!container.HasConfigFlag(UIContainer_DragFlag)) { + return false; + } + if (input.mStarted && !mDragging) { + if (input.GetLocalPos().x >= 0.0f && input.GetLocalPos().y >= 0.0f) { + mDragging = true; + mCurrentContainer = &container; + } + } + if (mDragging && mCurrentContainer == &container) { + ImVec2 move = DragMovement(input.mPrevPos, input.mCurPos, parent.mScale * container.GetScale()); + container.Move(move); + container.Notification(UIContainer_DragFlag); + + if (input.mStopped) { + mDragging = false; + mCurrentContainer = nullptr; + } + return mDragging; + } + return false; +} + +bool UIMove::Active(const InputState& input) { + return (input.mStarted && !mMoving) || mMoving; +} + +bool UIMove::Visit(UIContainer& container, const InputState& input, const ContainerArea& parent) { + if(!container.HasConfigFlag(UIContainer_MoveFlag)){ + return false; + } + if (input.mStarted && !mMoving) { + if (Overlap(input.GetLocalPos(), container.GetPosition(), container.GetPositionAtSize(), parent)) { + mMoving = true; + mCurrentContainer = &container; + } + } + if (mMoving && mCurrentContainer == &container) { + ImVec2 move = DragMovement(input.mPrevPos, input.mCurPos, parent.mScale); + container.Move(move); + container.Notification(UIContainer_MoveFlag); + + if (input.mStopped) { + mMoving = false; + mCurrentContainer = nullptr; + } + return mMoving; + } + return false; + } + + bool UIResize::Visit(UIContainer& container, const InputState& input, const ContainerArea& parent) { + if (!container.HasConfigFlag(UIContainer_ResizeFlag)) { + return false; + } + if (!mResizing) { + const float radii = mResizeAreaSize * 0.5f; + ImVec2 p = container.GetPositionAtSize(); + if (OverlapScreenRect(input.GetLocalPos(), p, ImVec2(radii, radii), parent)) { + mCurrentContainer = &container; + container.Notification(UIContainer_ResizeFlag); + + if (input.mStarted) { + mResizing = true; + } + else { + return true; + } + } + else { + mCurrentContainer = nullptr; + container.ClearNotifications(); + } + } + if (mResizing && mCurrentContainer == &container) { + ImVec2 move = DragMovement(input.mPrevPos, input.mCurPos, parent.mScale); + container.Resize(move); + + if (input.mStopped) { + mResizing = false; + mCurrentContainer = nullptr; + container.ClearNotifications(); + } + return mResizing; + } + return false; + } + + void UIDraw::DebugLog() { + mDebugLog = true; + } + + bool UIDraw::Visit(UIContainer& container, const InputState& input, const ContainerArea& parent) { + if (!container.HasConfigFlag(UIContainer_DrawFlag)) { + return false; + } + if (mDebugLog) { + container.DebugLog(); + } + + ImU32 color = container.GetRenderData().mColor; + ImVec2 pTopLeft = container.GetPosition(); + ImVec2 pCenter = container.GetPositionAtCenter(); + ImVec2 pLowRight = container.GetPositionAtSize(); + ImVec2 pSize = container.GetSize(); + ImVec2 p1 = parent.Transform(pTopLeft, input.mRootPos); + ImVec2 p12 = parent.Transform(pCenter, input.mRootPos); + ImVec2 p2 = parent.Transform(pLowRight, input.mRootPos); + + switch (container.GetRenderData().mType) { + case l::ui::UIRenderType::Rect: + mDrawList->AddRect(p1, p2, color, 2.0f, ImDrawFlags_RoundCornersAll, 2.0f); + break; + case l::ui::UIRenderType::RectFilled: + mDrawList->AddRectFilled(p1, p2, color, 2.0f, ImDrawFlags_RoundCornersAll); + break; + case l::ui::UIRenderType::Triangle: + break; + case l::ui::UIRenderType::TriangleFilled: + break; + case l::ui::UIRenderType::Circle: + mDrawList->AddCircle(p12, pSize.x, color, 15, 2.0f); + break; + case l::ui::UIRenderType::CircleFilled: + mDrawList->AddCircleFilled(p12, pSize.x, color, 15); + break; + case l::ui::UIRenderType::Polygon: + break; + case l::ui::UIRenderType::PolygonFilled: + break; + case l::ui::UIRenderType::Spline: + break; + case l::ui::UIRenderType::Text: + break; + case l::ui::UIRenderType::Texture: + break; + } + + switch (container.GetRenderData().mType) { + case l::ui::UIRenderType::Rect: + case l::ui::UIRenderType::RectFilled: + case l::ui::UIRenderType::Texture: + mDrawList->AddRect(p1, p2, color, 2.0f, ImDrawFlags_RoundCornersAll, 2.0f); + + if (container.HasConfigFlag(ui::UIContainer_ResizeFlag)) { + ImVec2 p3 = parent.Transform(pLowRight, ImVec2(input.mRootPos.x - 3.0f, input.mRootPos.y - 3.0f)); + ImVec2 p4 = parent.Transform(pLowRight, ImVec2(input.mRootPos.x + 3.0f, input.mRootPos.y + 3.0f)); + if (container.HasNotification(ui::UIContainer_ResizeFlag)) { + p3 = parent.Transform(pLowRight, ImVec2(input.mRootPos.x - 5.0f, input.mRootPos.y - 5.0f)); + p4 = parent.Transform(pLowRight, ImVec2(input.mRootPos.x + 5.0f, input.mRootPos.y + 5.0f)); + } + mDrawList->AddRectFilled(p3, p4, color); + } + break; + default: + break; + } + + return false; + } + +} From 2165df5e112e35d9adadc6966253bf9f3de0e3d6 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Wed, 21 Aug 2024 18:05:58 +0200 Subject: [PATCH 040/125] Add container text rendering and debug name rendering. --- .../include/rendering/ui/UIContainer.h | 22 ++++++++++++++----- .../source/common/ui/UIContainer.cpp | 2 +- .../rendering/source/common/ui/UIVisitors.cpp | 10 +++++++++ 3 files changed, 27 insertions(+), 7 deletions(-) diff --git a/packages/rendering/include/rendering/ui/UIContainer.h b/packages/rendering/include/rendering/ui/UIContainer.h index f73f8e74..04a3a46a 100644 --- a/packages/rendering/include/rendering/ui/UIContainer.h +++ b/packages/rendering/include/rendering/ui/UIContainer.h @@ -197,7 +197,10 @@ namespace l::ui { class UIContainer { public: - UIContainer(std::string_view name, uint32_t flags = 0) : mName(name), mConfigFlags(flags) {} + UIContainer(std::string_view name, uint32_t flags = 0, UIRenderType renderType = UIRenderType::Rect) : mDisplayName(name), mConfigFlags(flags) { + mArea.mRender.mType = renderType; + mArea.mLayout.mBorder = 3.0f; + } ~UIContainer() = default; bool Accept(UIVisitor& visitor, const InputState& input, UITraversalMode mode = UITraversalMode::All); @@ -230,9 +233,13 @@ namespace l::ui { return mArea.mLayout; } + std::string_view GetDisplayName() { + return mDisplayName; + } + friend UIVisitor; protected: - std::string mName; + std::string mDisplayName; ContainerArea mArea; uint32_t mConfigFlags = 0; // Active visitor flags uint32_t mNotificationFlags = 0; // Notification flags for ux feedback (resizing box animation etc) @@ -242,8 +249,9 @@ namespace l::ui { class UISplit : public UIContainer { public: - UISplit(std::string_view name, uint32_t flags, bool horizontalSplit, float border) : UIContainer(name, flags), mHorizontalSplit(horizontalSplit) { - mArea.mLayout.mBorder = border; + UISplit(std::string_view name, uint32_t flags, bool horizontalSplit = true) : UIContainer(name, flags), mHorizontalSplit(horizontalSplit) { + mArea.mRender.mType = UIRenderType::Rect; + mArea.mLayout.mBorder = 3.0f; } ~UISplit() = default; @@ -254,13 +262,15 @@ namespace l::ui { class UILayout : public UIContainer { public: - UILayout(std::string_view name, uint32_t flags, UIAlignH alignH, UIAlignV alignV, float border) : UIContainer(name, flags) { + UILayout(std::string_view name, uint32_t flags, UIAlignH alignH, UIAlignV alignV) : UIContainer(name, flags) { + mArea.mRender.mType = UIRenderType::Rect; mArea.mLayout.mAlignH = alignH; mArea.mLayout.mAlignV = alignV; - mArea.mLayout.mBorder = border; + mArea.mLayout.mBorder = 0.0f; } ~UILayout() = default; virtual bool Accept(UIVisitor& visitor, const InputState& input, const ContainerArea& parent, UITraversalMode mode); }; + } diff --git a/packages/rendering/source/common/ui/UIContainer.cpp b/packages/rendering/source/common/ui/UIContainer.cpp index 29702f4f..07e2ce6a 100644 --- a/packages/rendering/source/common/ui/UIContainer.cpp +++ b/packages/rendering/source/common/ui/UIContainer.cpp @@ -127,7 +127,7 @@ namespace l::ui { } void UIContainer::DebugLog() { - LOG(LogDebug) << "UIContainer: " << mName << ", [" << mArea.mScale << "][" << mArea.mPosition.x << ", " << mArea.mPosition.y << "][" << mArea.mSize.x << ", " << mArea.mSize.y << "]"; + LOG(LogDebug) << "UIContainer: " << mDisplayName << ", [" << mArea.mScale << "][" << mArea.mPosition.x << ", " << mArea.mPosition.y << "][" << mArea.mSize.x << ", " << mArea.mSize.y << "]"; } bool UIContainer::Accept(UIVisitor& visitor, const InputState& input, UITraversalMode mode) { diff --git a/packages/rendering/source/common/ui/UIVisitors.cpp b/packages/rendering/source/common/ui/UIVisitors.cpp index fb296ec2..528b074a 100644 --- a/packages/rendering/source/common/ui/UIVisitors.cpp +++ b/packages/rendering/source/common/ui/UIVisitors.cpp @@ -146,6 +146,8 @@ bool UIMove::Visit(UIContainer& container, const InputState& input, const Contai ImVec2 p1 = parent.Transform(pTopLeft, input.mRootPos); ImVec2 p12 = parent.Transform(pCenter, input.mRootPos); ImVec2 p2 = parent.Transform(pLowRight, input.mRootPos); + const char* nameStart = container.GetDisplayName().data(); + const char* nameEnd = container.GetDisplayName().data() + container.GetDisplayName().size(); switch (container.GetRenderData().mType) { case l::ui::UIRenderType::Rect: @@ -171,11 +173,19 @@ bool UIMove::Visit(UIContainer& container, const InputState& input, const Contai case l::ui::UIRenderType::Spline: break; case l::ui::UIRenderType::Text: + if (!container.GetDisplayName().empty()) { + mDrawList->AddText(ImGui::GetDefaultFont(), 13.0f * parent.mScale, p1, color, nameStart, nameEnd); + } break; case l::ui::UIRenderType::Texture: break; } + if (container.GetRenderData().mType != l::ui::UIRenderType::Text && !container.GetDisplayName().empty()) { + // also render name if it is non empty as debug text + mDrawList->AddText(p1, color, nameStart, nameEnd); + } + switch (container.GetRenderData().mType) { case l::ui::UIRenderType::Rect: case l::ui::UIRenderType::RectFilled: From 2d5bcde859470304b0e9c476ab2b228d6261af2e Mon Sep 17 00:00:00 2001 From: lnd3 Date: Wed, 21 Aug 2024 18:06:52 +0200 Subject: [PATCH 041/125] Add a ui creator for easy ui creation. --- .../include/rendering/ui/UICreator.h | 42 ++++ .../rendering/source/common/ui/UICreator.cpp | 233 ++++++++++++++++++ 2 files changed, 275 insertions(+) create mode 100644 packages/rendering/include/rendering/ui/UICreator.h create mode 100644 packages/rendering/source/common/ui/UICreator.cpp diff --git a/packages/rendering/include/rendering/ui/UICreator.h b/packages/rendering/include/rendering/ui/UICreator.h new file mode 100644 index 00000000..4a9af7bb --- /dev/null +++ b/packages/rendering/include/rendering/ui/UICreator.h @@ -0,0 +1,42 @@ +#pragma once + +#include "logging/LoggingAll.h" +#include "rendering/ui/UIContainer.h" +#include "rendering/ui/UIVisitors.h" +#include "rendering/ui/UIWindow.h" + +#define GLFW_INCLUDE_NONE +#include + +#include "imgui/imgui.h" +#include "imgui/imgui_internal.h" +#include "imgui/imgui_impl_glfw.h" +#include "imgui/imgui_impl_opengl3.h" +#include "implot/implot.h" +#include "implot/implot_internal.h" + +#include + +namespace l::ui { + + struct UIHandle { + std::string mId; + UIContainer* mContainer; + }; + + class UI { + public: + UI() = default; + ~UI() = default; + + UIHandle CreateContainer(std::string_view name, uint32_t flags); + UIHandle CreateSplit(std::string_view name, uint32_t flags, bool horizontalSplit = true); + UIHandle CreateLayout(std::string_view name, uint32_t flags, UIAlignH alignH = UIAlignH::Left, UIAlignV alignV = UIAlignV::Top); + + protected: + std::unordered_map> mContainers; + std::vector mVisitors; + + }; + +} diff --git a/packages/rendering/source/common/ui/UICreator.cpp b/packages/rendering/source/common/ui/UICreator.cpp new file mode 100644 index 00000000..07e2ce6a --- /dev/null +++ b/packages/rendering/source/common/ui/UICreator.cpp @@ -0,0 +1,233 @@ +#include "rendering/ui/UIContainer.h" + +#include + +namespace l::ui { + + ImVec2 DragMovement(const ImVec2& prevPos, const ImVec2& curPos, float curScale) { + ImVec2 move = curPos; + move.x -= prevPos.x; + move.y -= prevPos.y; + move.x = move.x / curScale; + move.y = move.y / curScale; + return move; + } + + bool Overlap(const ImVec2& p, const ImVec2& pMin, const ImVec2& pMax) { + return pMin.x < p.x && pMin.y < p.y && pMax.x > p.x && pMax.y > p.y; + } + + bool Overlap(const ImVec2& p, const ImVec2& pMin, const ImVec2& pMax, const ContainerArea& parent) { + ImVec2 pMinT = parent.Transform(pMin); + ImVec2 pMaxT = parent.Transform(pMax); + return pMinT.x < p.x && pMinT.y < p.y && pMaxT.x > p.x && pMaxT.y > p.y; + } + + bool OverlapScreenRect(const ImVec2& p, const ImVec2& pCenter, const ImVec2& offset, const ContainerArea& parent) { + ImVec2 pMinT = parent.Transform(pCenter, ImVec2(-offset.x, -offset.y)); + ImVec2 pMaxT = parent.Transform(pCenter, ImVec2(offset.x, offset.y)); + return pMinT.x < p.x && pMinT.y < p.y && pMaxT.x > p.x && pMaxT.y > p.y; + } + + bool OverlapScreenCircle(const ImVec2& p, const ImVec2& pCenter, float radii, const ContainerArea& parent) { + ImVec2 pT = parent.Transform(pCenter); + ImVec2 d = ImVec2(pT.x - p.x, pT.y - p.y); + return d.x*d.x + d.y*d.y < radii * radii; + } + + void UIContainer::Add(UIContainer* container, int32_t i) { + if (i < 0) { + mContent.push_back(container); + } + else { + ASSERT(static_cast(i) < mContent.size()); + mContent.insert(mContent.begin() + i, container); + } + } + + void UIContainer::Remove(int32_t i) { + ASSERT(i >= 0 && static_cast(i) < mContent.size()); + mContent.erase(mContent.begin() + i); + } + + void UIContainer::Move(ImVec2 localChange) { + mArea.mPosition.x += localChange.x; + mArea.mPosition.y += localChange.y; + } + + void UIContainer::Resize(ImVec2 localChange) { + mArea.mSize.x += localChange.x; + mArea.mSize.y += localChange.y; + mArea.mSize.x = mArea.mSize.x < 1.0f ? 1.0f : mArea.mSize.x; + mArea.mSize.y = mArea.mSize.y < 1.0f ? 1.0f : mArea.mSize.y; + } + + void UIContainer::Rescale(float localChange) { + mArea.mScale *= localChange; + } + + void UIContainer::ClearNotifications() { + mNotificationFlags = 0; + } + + void UIContainer::Notification(uint32_t flag) { + mNotificationFlags |= flag; + } + + bool UIContainer::HasNotification(uint32_t flag) { + return (mNotificationFlags & flag) == flag; + } + + bool UIContainer::HasConfigFlag(uint32_t flag) { + return (mConfigFlags & flag) == flag; + } + + void UIContainer::SetPosition(ImVec2 p) { + mArea.mPosition = p; + } + + void UIContainer::SetSize(ImVec2 s) { + mArea.mSize = s; + } + + void UIContainer::SetContainerArea(const ContainerArea& area) { + mArea = area; + } + + ImVec2 UIContainer::GetPosition(bool untransformed) { + if (untransformed) { + return mArea.mPosition; + } + return ImVec2(mArea.mPosition.x * mArea.mScale, mArea.mPosition.y * mArea.mScale); + } + + ImVec2 UIContainer::GetPositionAtCenter(ImVec2 offset, bool untransformed) { + if (untransformed) { + return ImVec2(mArea.mPosition.x + mArea.mSize.x * 0.5f + offset.x, mArea.mPosition.y * 0.5f + mArea.mSize.y + offset.y); + } + return ImVec2((mArea.mPosition.x + mArea.mSize.x * 0.5f + offset.x) * mArea.mScale, (mArea.mPosition.y * 0.5f + mArea.mSize.y + offset.y) * mArea.mScale); + } + + ImVec2 UIContainer::GetPositionAtSize(ImVec2 offset, bool untransformed) { + if (untransformed) { + return ImVec2(mArea.mPosition.x + mArea.mSize.x + offset.x, mArea.mPosition.y + mArea.mSize.y + offset.y); + } + return ImVec2((mArea.mPosition.x + mArea.mSize.x + offset.x) * mArea.mScale, (mArea.mPosition.y + mArea.mSize.y + offset.y) * mArea.mScale); + } + + ImVec2 UIContainer::GetSize(bool untransformed) { + if (untransformed) { + return mArea.mSize; + } + return ImVec2(mArea.mSize.x * mArea.mScale, mArea.mSize.y * mArea.mScale); + } + + float UIContainer::GetScale() { + return mArea.mScale; + } + + void UIContainer::DebugLog() { + LOG(LogDebug) << "UIContainer: " << mDisplayName << ", [" << mArea.mScale << "][" << mArea.mPosition.x << ", " << mArea.mPosition.y << "][" << mArea.mSize.x << ", " << mArea.mSize.y << "]"; + } + + bool UIContainer::Accept(UIVisitor& visitor, const InputState& input, UITraversalMode mode) { + ContainerArea current; + if (visitor.Active(input)) { + return Accept(visitor, input, current, mode); + } + return false; + } + + bool UIContainer::Accept(UIVisitor& visitor, const InputState& input, const ContainerArea& parent, UITraversalMode mode) { + ContainerArea current; + current.mScale = mArea.GetWorldScale(parent.mScale); + current.mPosition = mArea.GetWorldPos(parent.mScale, parent.mPosition); + current.mSize = mArea.GetWorldSize(parent.mScale); + + for (auto& content : mContent) { + + if (content->Accept(visitor, input, current, mode)) { + if (mode == UITraversalMode::Once) { + return true; + } + } + } + + if (visitor.Active(input)) { + return visitor.Visit(*this, input, parent); + } + return false; + } + + bool UISplit::Accept(UIVisitor& visitor, const InputState& input, const ContainerArea& parent, UITraversalMode mode) { + // Since we can have multiple layouts in a container for different content, it will act as + // an anchor rather than a container, therefore it has to align within it and size + mArea.mSize = parent.GetLocalSize(); + + float contentCount = static_cast(mContent.size()); + + int32_t i = 0; + for (auto& content : mContent) { + auto contentSize = content->GetSize(); + + ContainerArea current; + current.mScale = mArea.GetWorldScale(parent.mScale); + current.mSize = mArea.GetWorldSize(parent.mScale); + current.mPosition = mArea.GetWorldPos(parent.mScale, parent.mPosition); + + + ImVec2 size = mArea.mSize; + size.x -= mArea.mLayout.mBorder * 2.0f; + size.y -= mArea.mLayout.mBorder * 2.0f; + + float split = i++ / contentCount; + if (mHorizontalSplit) { + current.mPosition.x += current.mSize.x * split; + size.x /= contentCount; + } + else { + current.mPosition.y += current.mSize.y * split; + size.y /= contentCount; + } + content->SetSize(size); + + + if (content->Accept(visitor, input, current, mode)) { + if (mode == UITraversalMode::Once) { + return true; + } + } + } + + if (visitor.Active(input)) { + return visitor.Visit(*this, input, parent); + } + return false; + } + + bool UILayout::Accept(UIVisitor& visitor, const InputState& input, const ContainerArea& parent, UITraversalMode mode) { + // Since we can have multiple layout in a container for different content, it will act as + // an anchor rather than a container, therefore it has to align within it and size + mArea.mSize = parent.GetLocalSize(); + + for (auto& content : mContent) { + auto contentSize = content->GetSize(); + + ContainerArea current; + current.mScale = mArea.GetWorldScale(parent.mScale); + current.mSize = mArea.GetWorldSizeLayout(parent.mScale); + current.mPosition = mArea.GetWorldPosLayout(parent.mScale, parent.mPosition, contentSize, mArea.mLayout.mAlignH, mArea.mLayout.mAlignV); + + if (content->Accept(visitor, input, current, mode)) { + if (mode == UITraversalMode::Once) { + return true; + } + } + } + + if (visitor.Active(input)) { + return visitor.Visit(*this, input, parent); + } + return false; + } +} From e439b84e2d9bad5f60ee182c5ffaf77b4f75846a Mon Sep 17 00:00:00 2001 From: lnd3 Date: Thu, 22 Aug 2024 00:54:42 +0200 Subject: [PATCH 042/125] Add debug rendering. Add a layout and alignment config to all containers. Remove layout container. --- .../include/rendering/ui/UIContainer.h | 79 ++++-- .../include/rendering/ui/UICreator.h | 39 ++- .../include/rendering/ui/UIVisitors.h | 2 - .../source/common/ui/UIContainer.cpp | 113 +++++---- .../rendering/source/common/ui/UICreator.cpp | 233 +----------------- .../rendering/source/common/ui/UIVisitors.cpp | 19 +- 6 files changed, 169 insertions(+), 316 deletions(-) diff --git a/packages/rendering/include/rendering/ui/UIContainer.h b/packages/rendering/include/rendering/ui/UIContainer.h index 04a3a46a..b50b2c55 100644 --- a/packages/rendering/include/rendering/ui/UIContainer.h +++ b/packages/rendering/include/rendering/ui/UIContainer.h @@ -28,6 +28,12 @@ namespace l::ui { Bottom = 2 }; + enum class UILayout { + Fixed = 0, + Scaled = 1, + Parent = 2 + }; + enum class UIRenderType { Rect = 0, RectFilled = 1, @@ -77,6 +83,7 @@ namespace l::ui { float mBorder = 3.0f; UIAlignH mAlignH = UIAlignH::Left; UIAlignV mAlignV = UIAlignV::Top; + UILayout mLayout = UILayout::Fixed; }; struct ContainerArea { @@ -174,15 +181,19 @@ namespace l::ui { virtual bool Visit(UIContainer&, const InputState&, const ContainerArea&) { return false; } + virtual void Debug(bool on = true) { + mDebug = on; + } + + protected: + bool mDebug = false; }; const uint32_t UIContainer_ReservedMask = 0x0000000f; const uint32_t UIContainer_ConfigMask = 0x000ffff0; const uint32_t UIContainer_CustomMask = 0xfff00000; - const uint32_t UIContainer_VisitAll = 0x00000000; - const uint32_t UIContainer_ExitOnAccept = 0x00000001; - + const uint32_t UIContainer_Reserved0 = 0x00000001; const uint32_t UIContainer_Reserved1 = 0x00000002; const uint32_t UIContainer_Reserved2 = 0x00000004; const uint32_t UIContainer_Reserved3 = 0x00000008; @@ -195,17 +206,52 @@ namespace l::ui { class UIDraw; + template>> + class UIHandle { + public: + UIHandle(std::string_view id, UIContainer* container) : mId(id), mContainer(container) {}; + ~UIHandle() = default; + + std::string mId; + UIContainer* mContainer = nullptr; + + std::string_view id() { + return mId; + } + + T* get() { + return reinterpret_cast(mContainer); + } + + T* operator->() { + return reinterpret_cast(mContainer); + } + + T& ref() { + return &reinterpret_cast(mContainer); + } + }; + class UIContainer { public: - UIContainer(std::string_view name, uint32_t flags = 0, UIRenderType renderType = UIRenderType::Rect) : mDisplayName(name), mConfigFlags(flags) { + UIContainer(uint32_t flags = 0, UIRenderType renderType = UIRenderType::Rect, UIAlignH alignH = UIAlignH::Left, UIAlignV alignV = UIAlignV::Top, UILayout layout = UILayout::Fixed) : mConfigFlags(flags) { mArea.mRender.mType = renderType; mArea.mLayout.mBorder = 3.0f; + mArea.mLayout.mAlignH = alignH; + mArea.mLayout.mAlignV = alignV; + mArea.mLayout.mLayout = layout; } ~UIContainer() = default; bool Accept(UIVisitor& visitor, const InputState& input, UITraversalMode mode = UITraversalMode::All); virtual bool Accept(UIVisitor& visitor, const InputState& input, const ContainerArea& parent, UITraversalMode mode = UITraversalMode::All); virtual void Add(UIContainer* container, int32_t i = -1); + + template + void Add(UIHandle& handle, int32_t i = -1) { + Add(handle.get()); + } + virtual void Remove(int32_t i); void Move(ImVec2 localChange); @@ -217,12 +263,16 @@ namespace l::ui { bool HasConfigFlag(uint32_t flag); void SetPosition(ImVec2 p); void SetSize(ImVec2 s); + void SetDisplayName(std::string_view id); + void SetId(std::string_view id); void SetContainerArea(const ContainerArea& area); ImVec2 GetPosition(bool untransformed = false); ImVec2 GetPositionAtCenter(ImVec2 offset = ImVec2(), bool untransformed = false); ImVec2 GetPositionAtSize(ImVec2 offset = ImVec2(), bool untransformed = false); ImVec2 GetSize(bool untransformed = false); float GetScale(); + ContainerArea& GetContainerArea(); + void DebugLog(); const UIRenderData& GetRenderData() const { @@ -237,8 +287,12 @@ namespace l::ui { return mDisplayName; } - friend UIVisitor; + std::string_view GetId() { + return mId; + } + protected: + std::string mId; std::string mDisplayName; ContainerArea mArea; uint32_t mConfigFlags = 0; // Active visitor flags @@ -249,7 +303,7 @@ namespace l::ui { class UISplit : public UIContainer { public: - UISplit(std::string_view name, uint32_t flags, bool horizontalSplit = true) : UIContainer(name, flags), mHorizontalSplit(horizontalSplit) { + UISplit(uint32_t flags, bool horizontalSplit = true) : UIContainer(flags), mHorizontalSplit(horizontalSplit) { mArea.mRender.mType = UIRenderType::Rect; mArea.mLayout.mBorder = 3.0f; } @@ -260,17 +314,4 @@ namespace l::ui { bool mHorizontalSplit; }; - class UILayout : public UIContainer { - public: - UILayout(std::string_view name, uint32_t flags, UIAlignH alignH, UIAlignV alignV) : UIContainer(name, flags) { - mArea.mRender.mType = UIRenderType::Rect; - mArea.mLayout.mAlignH = alignH; - mArea.mLayout.mAlignV = alignV; - mArea.mLayout.mBorder = 0.0f; - } - ~UILayout() = default; - - virtual bool Accept(UIVisitor& visitor, const InputState& input, const ContainerArea& parent, UITraversalMode mode); - }; - } diff --git a/packages/rendering/include/rendering/ui/UICreator.h b/packages/rendering/include/rendering/ui/UICreator.h index 4a9af7bb..0108128d 100644 --- a/packages/rendering/include/rendering/ui/UICreator.h +++ b/packages/rendering/include/rendering/ui/UICreator.h @@ -18,20 +18,35 @@ #include namespace l::ui { - - struct UIHandle { - std::string mId; - UIContainer* mContainer; - }; - - class UI { + template>> + std::string CreateUniqueId() { + static uint32_t mId = 0; + std::string id; + if constexpr (std::is_same_v) { + id += "UIContainer"; + } + else if constexpr (std::is_same_v) { + id += "UISplit"; + } + else if constexpr (std::is_same_v) { + id += "UILayout"; + } + else { + id += "Unknown"; + } + + id += l::string::to_hex(mId++, 4); + + return id; + } + + class UICreator { public: - UI() = default; - ~UI() = default; + UICreator() = default; + ~UICreator() = default; - UIHandle CreateContainer(std::string_view name, uint32_t flags); - UIHandle CreateSplit(std::string_view name, uint32_t flags, bool horizontalSplit = true); - UIHandle CreateLayout(std::string_view name, uint32_t flags, UIAlignH alignH = UIAlignH::Left, UIAlignV alignV = UIAlignV::Top); + UIHandle CreateContainer(uint32_t flags, UIRenderType renderType = UIRenderType::Rect, UIAlignH alignH = UIAlignH::Left, UIAlignV alignV = UIAlignV::Top, UILayout layout = UILayout::Fixed); + UIHandle CreateSplit(uint32_t flags, bool horizontalSplit = true); protected: std::unordered_map> mContainers; diff --git a/packages/rendering/include/rendering/ui/UIVisitors.h b/packages/rendering/include/rendering/ui/UIVisitors.h index f21b5365..64123cda 100644 --- a/packages/rendering/include/rendering/ui/UIVisitors.h +++ b/packages/rendering/include/rendering/ui/UIVisitors.h @@ -52,10 +52,8 @@ namespace l::ui { UIDraw(ImDrawList* drawList) : mDrawList(drawList) {} ~UIDraw() = default; - void DebugLog(); virtual bool Visit(UIContainer& container, const InputState& input, const ContainerArea& parent); protected: ImDrawList* mDrawList; - bool mDebugLog = false; }; } diff --git a/packages/rendering/source/common/ui/UIContainer.cpp b/packages/rendering/source/common/ui/UIContainer.cpp index 07e2ce6a..69723f68 100644 --- a/packages/rendering/source/common/ui/UIContainer.cpp +++ b/packages/rendering/source/common/ui/UIContainer.cpp @@ -90,6 +90,15 @@ namespace l::ui { mArea.mSize = s; } + void UIContainer::SetDisplayName(std::string_view displayName) { + mDisplayName = displayName; + } + + void UIContainer::SetId(std::string_view id) { + mId = id; + } + + void UIContainer::SetContainerArea(const ContainerArea& area) { mArea = area; } @@ -126,6 +135,10 @@ namespace l::ui { return mArea.mScale; } + ContainerArea& UIContainer::GetContainerArea() { + return mArea; + } + void UIContainer::DebugLog() { LOG(LogDebug) << "UIContainer: " << mDisplayName << ", [" << mArea.mScale << "][" << mArea.mPosition.x << ", " << mArea.mPosition.y << "][" << mArea.mSize.x << ", " << mArea.mSize.y << "]"; } @@ -139,12 +152,29 @@ namespace l::ui { } bool UIContainer::Accept(UIVisitor& visitor, const InputState& input, const ContainerArea& parent, UITraversalMode mode) { - ContainerArea current; - current.mScale = mArea.GetWorldScale(parent.mScale); - current.mPosition = mArea.GetWorldPos(parent.mScale, parent.mPosition); - current.mSize = mArea.GetWorldSize(parent.mScale); + //ContainerArea current; + //current.mScale = mArea.GetWorldScale(parent.mScale); + //current.mPosition = mArea.GetWorldPos(parent.mScale, parent.mPosition); + //current.mSize = mArea.GetWorldSize(parent.mScale); + auto& layout = GetContainerArea().mLayout; + switch (layout.mLayout) { + case UILayout::Fixed: + break; + case UILayout::Scaled: + break; + case UILayout::Parent: + SetSize(parent.GetLocalSize()); + break; + } + for (auto& content : mContent) { + auto contentSize = content->GetSize(); + auto& contentLayout = content->GetContainerArea().mLayout; + ContainerArea current; + current.mScale = mArea.GetWorldScale(parent.mScale); + current.mSize = mArea.GetWorldSizeLayout(parent.mScale); + current.mPosition = mArea.GetWorldPosLayout(parent.mScale, parent.mPosition, contentSize, contentLayout.mAlignH, contentLayout.mAlignV); if (content->Accept(visitor, input, current, mode)) { if (mode == UITraversalMode::Once) { @@ -162,67 +192,45 @@ namespace l::ui { bool UISplit::Accept(UIVisitor& visitor, const InputState& input, const ContainerArea& parent, UITraversalMode mode) { // Since we can have multiple layouts in a container for different content, it will act as // an anchor rather than a container, therefore it has to align within it and size - mArea.mSize = parent.GetLocalSize(); - - float contentCount = static_cast(mContent.size()); - - int32_t i = 0; - for (auto& content : mContent) { - auto contentSize = content->GetSize(); - ContainerArea current; - current.mScale = mArea.GetWorldScale(parent.mScale); - current.mSize = mArea.GetWorldSize(parent.mScale); - current.mPosition = mArea.GetWorldPos(parent.mScale, parent.mPosition); - - - ImVec2 size = mArea.mSize; - size.x -= mArea.mLayout.mBorder * 2.0f; - size.y -= mArea.mLayout.mBorder * 2.0f; + auto& layout = GetContainerArea().mLayout; + switch (layout.mLayout) { + case UILayout::Fixed: + break; + case UILayout::Scaled: + break; + case UILayout::Parent: + SetSize(parent.GetLocalSize()); + break; + } - float split = i++ / contentCount; - if (mHorizontalSplit) { - current.mPosition.x += current.mSize.x * split; - size.x /= contentCount; - } - else { - current.mPosition.y += current.mSize.y * split; - size.y /= contentCount; - } - content->SetSize(size); + float contentCount = static_cast(mContent.size()); + ContainerArea current; + current.mScale = mArea.GetWorldScale(parent.mScale); + current.mSize = mArea.GetWorldSize(parent.mScale); + current.mPosition = mArea.GetWorldPos(parent.mScale, parent.mPosition); - if (content->Accept(visitor, input, current, mode)) { - if (mode == UITraversalMode::Once) { - return true; - } - } + if (mHorizontalSplit) { + current.mSize.x /= contentCount; } - - if (visitor.Active(input)) { - return visitor.Visit(*this, input, parent); + else { + current.mSize.y /= contentCount; } - return false; - } - - bool UILayout::Accept(UIVisitor& visitor, const InputState& input, const ContainerArea& parent, UITraversalMode mode) { - // Since we can have multiple layout in a container for different content, it will act as - // an anchor rather than a container, therefore it has to align within it and size - mArea.mSize = parent.GetLocalSize(); for (auto& content : mContent) { - auto contentSize = content->GetSize(); - - ContainerArea current; - current.mScale = mArea.GetWorldScale(parent.mScale); - current.mSize = mArea.GetWorldSizeLayout(parent.mScale); - current.mPosition = mArea.GetWorldPosLayout(parent.mScale, parent.mPosition, contentSize, mArea.mLayout.mAlignH, mArea.mLayout.mAlignV); - if (content->Accept(visitor, input, current, mode)) { if (mode == UITraversalMode::Once) { return true; } } + + if (mHorizontalSplit) { + current.mPosition.x += current.mSize.x; + } + else { + current.mPosition.y += current.mSize.y; + } } if (visitor.Active(input)) { @@ -230,4 +238,5 @@ namespace l::ui { } return false; } + } diff --git a/packages/rendering/source/common/ui/UICreator.cpp b/packages/rendering/source/common/ui/UICreator.cpp index 07e2ce6a..3f371bbb 100644 --- a/packages/rendering/source/common/ui/UICreator.cpp +++ b/packages/rendering/source/common/ui/UICreator.cpp @@ -1,233 +1,26 @@ -#include "rendering/ui/UIContainer.h" +#include "rendering/ui/UICreator.h" #include namespace l::ui { - ImVec2 DragMovement(const ImVec2& prevPos, const ImVec2& curPos, float curScale) { - ImVec2 move = curPos; - move.x -= prevPos.x; - move.y -= prevPos.y; - move.x = move.x / curScale; - move.y = move.y / curScale; - return move; - } - - bool Overlap(const ImVec2& p, const ImVec2& pMin, const ImVec2& pMax) { - return pMin.x < p.x && pMin.y < p.y && pMax.x > p.x && pMax.y > p.y; - } - - bool Overlap(const ImVec2& p, const ImVec2& pMin, const ImVec2& pMax, const ContainerArea& parent) { - ImVec2 pMinT = parent.Transform(pMin); - ImVec2 pMaxT = parent.Transform(pMax); - return pMinT.x < p.x && pMinT.y < p.y && pMaxT.x > p.x && pMaxT.y > p.y; - } - - bool OverlapScreenRect(const ImVec2& p, const ImVec2& pCenter, const ImVec2& offset, const ContainerArea& parent) { - ImVec2 pMinT = parent.Transform(pCenter, ImVec2(-offset.x, -offset.y)); - ImVec2 pMaxT = parent.Transform(pCenter, ImVec2(offset.x, offset.y)); - return pMinT.x < p.x && pMinT.y < p.y && pMaxT.x > p.x && pMaxT.y > p.y; - } - - bool OverlapScreenCircle(const ImVec2& p, const ImVec2& pCenter, float radii, const ContainerArea& parent) { - ImVec2 pT = parent.Transform(pCenter); - ImVec2 d = ImVec2(pT.x - p.x, pT.y - p.y); - return d.x*d.x + d.y*d.y < radii * radii; - } - - void UIContainer::Add(UIContainer* container, int32_t i) { - if (i < 0) { - mContent.push_back(container); - } - else { - ASSERT(static_cast(i) < mContent.size()); - mContent.insert(mContent.begin() + i, container); - } - } - - void UIContainer::Remove(int32_t i) { - ASSERT(i >= 0 && static_cast(i) < mContent.size()); - mContent.erase(mContent.begin() + i); - } - - void UIContainer::Move(ImVec2 localChange) { - mArea.mPosition.x += localChange.x; - mArea.mPosition.y += localChange.y; - } - - void UIContainer::Resize(ImVec2 localChange) { - mArea.mSize.x += localChange.x; - mArea.mSize.y += localChange.y; - mArea.mSize.x = mArea.mSize.x < 1.0f ? 1.0f : mArea.mSize.x; - mArea.mSize.y = mArea.mSize.y < 1.0f ? 1.0f : mArea.mSize.y; - } - - void UIContainer::Rescale(float localChange) { - mArea.mScale *= localChange; - } - - void UIContainer::ClearNotifications() { - mNotificationFlags = 0; - } - - void UIContainer::Notification(uint32_t flag) { - mNotificationFlags |= flag; - } - - bool UIContainer::HasNotification(uint32_t flag) { - return (mNotificationFlags & flag) == flag; - } - - bool UIContainer::HasConfigFlag(uint32_t flag) { - return (mConfigFlags & flag) == flag; - } - - void UIContainer::SetPosition(ImVec2 p) { - mArea.mPosition = p; - } - - void UIContainer::SetSize(ImVec2 s) { - mArea.mSize = s; - } + UIHandle UICreator::CreateContainer(uint32_t flags, UIRenderType renderType, UIAlignH alignH, UIAlignV alignV, UILayout layout) { + std::unique_ptr container = std::make_unique(flags, renderType, alignH, alignV, layout); - void UIContainer::SetContainerArea(const ContainerArea& area) { - mArea = area; - } - - ImVec2 UIContainer::GetPosition(bool untransformed) { - if (untransformed) { - return mArea.mPosition; - } - return ImVec2(mArea.mPosition.x * mArea.mScale, mArea.mPosition.y * mArea.mScale); - } - - ImVec2 UIContainer::GetPositionAtCenter(ImVec2 offset, bool untransformed) { - if (untransformed) { - return ImVec2(mArea.mPosition.x + mArea.mSize.x * 0.5f + offset.x, mArea.mPosition.y * 0.5f + mArea.mSize.y + offset.y); - } - return ImVec2((mArea.mPosition.x + mArea.mSize.x * 0.5f + offset.x) * mArea.mScale, (mArea.mPosition.y * 0.5f + mArea.mSize.y + offset.y) * mArea.mScale); - } - - ImVec2 UIContainer::GetPositionAtSize(ImVec2 offset, bool untransformed) { - if (untransformed) { - return ImVec2(mArea.mPosition.x + mArea.mSize.x + offset.x, mArea.mPosition.y + mArea.mSize.y + offset.y); - } - return ImVec2((mArea.mPosition.x + mArea.mSize.x + offset.x) * mArea.mScale, (mArea.mPosition.y + mArea.mSize.y + offset.y) * mArea.mScale); - } - - ImVec2 UIContainer::GetSize(bool untransformed) { - if (untransformed) { - return mArea.mSize; - } - return ImVec2(mArea.mSize.x * mArea.mScale, mArea.mSize.y * mArea.mScale); - } - - float UIContainer::GetScale() { - return mArea.mScale; - } - - void UIContainer::DebugLog() { - LOG(LogDebug) << "UIContainer: " << mDisplayName << ", [" << mArea.mScale << "][" << mArea.mPosition.x << ", " << mArea.mPosition.y << "][" << mArea.mSize.x << ", " << mArea.mSize.y << "]"; - } + std::string id = CreateUniqueId(); + container->SetId(id); + mContainers.insert({ id, std::move(container)}); - bool UIContainer::Accept(UIVisitor& visitor, const InputState& input, UITraversalMode mode) { - ContainerArea current; - if (visitor.Active(input)) { - return Accept(visitor, input, current, mode); - } - return false; + return UIHandle{ id, mContainers.at(id).get() }; } - bool UIContainer::Accept(UIVisitor& visitor, const InputState& input, const ContainerArea& parent, UITraversalMode mode) { - ContainerArea current; - current.mScale = mArea.GetWorldScale(parent.mScale); - current.mPosition = mArea.GetWorldPos(parent.mScale, parent.mPosition); - current.mSize = mArea.GetWorldSize(parent.mScale); - - for (auto& content : mContent) { - - if (content->Accept(visitor, input, current, mode)) { - if (mode == UITraversalMode::Once) { - return true; - } - } - } - - if (visitor.Active(input)) { - return visitor.Visit(*this, input, parent); - } - return false; - } - - bool UISplit::Accept(UIVisitor& visitor, const InputState& input, const ContainerArea& parent, UITraversalMode mode) { - // Since we can have multiple layouts in a container for different content, it will act as - // an anchor rather than a container, therefore it has to align within it and size - mArea.mSize = parent.GetLocalSize(); - - float contentCount = static_cast(mContent.size()); - - int32_t i = 0; - for (auto& content : mContent) { - auto contentSize = content->GetSize(); - - ContainerArea current; - current.mScale = mArea.GetWorldScale(parent.mScale); - current.mSize = mArea.GetWorldSize(parent.mScale); - current.mPosition = mArea.GetWorldPos(parent.mScale, parent.mPosition); - - - ImVec2 size = mArea.mSize; - size.x -= mArea.mLayout.mBorder * 2.0f; - size.y -= mArea.mLayout.mBorder * 2.0f; - - float split = i++ / contentCount; - if (mHorizontalSplit) { - current.mPosition.x += current.mSize.x * split; - size.x /= contentCount; - } - else { - current.mPosition.y += current.mSize.y * split; - size.y /= contentCount; - } - content->SetSize(size); - - - if (content->Accept(visitor, input, current, mode)) { - if (mode == UITraversalMode::Once) { - return true; - } - } - } - - if (visitor.Active(input)) { - return visitor.Visit(*this, input, parent); - } - return false; - } - - bool UILayout::Accept(UIVisitor& visitor, const InputState& input, const ContainerArea& parent, UITraversalMode mode) { - // Since we can have multiple layout in a container for different content, it will act as - // an anchor rather than a container, therefore it has to align within it and size - mArea.mSize = parent.GetLocalSize(); - - for (auto& content : mContent) { - auto contentSize = content->GetSize(); - - ContainerArea current; - current.mScale = mArea.GetWorldScale(parent.mScale); - current.mSize = mArea.GetWorldSizeLayout(parent.mScale); - current.mPosition = mArea.GetWorldPosLayout(parent.mScale, parent.mPosition, contentSize, mArea.mLayout.mAlignH, mArea.mLayout.mAlignV); + UIHandle UICreator::CreateSplit(uint32_t flags, bool horizontalSplit) { + std::unique_ptr container = std::make_unique(flags, horizontalSplit); - if (content->Accept(visitor, input, current, mode)) { - if (mode == UITraversalMode::Once) { - return true; - } - } - } + std::string id = CreateUniqueId(); + container->SetId(id); + mContainers.insert({ id, std::move(container) }); - if (visitor.Active(input)) { - return visitor.Visit(*this, input, parent); - } - return false; + return UIHandle{ id, mContainers.at(id).get() }; } } diff --git a/packages/rendering/source/common/ui/UIVisitors.cpp b/packages/rendering/source/common/ui/UIVisitors.cpp index 528b074a..e4f6ce8e 100644 --- a/packages/rendering/source/common/ui/UIVisitors.cpp +++ b/packages/rendering/source/common/ui/UIVisitors.cpp @@ -126,17 +126,10 @@ bool UIMove::Visit(UIContainer& container, const InputState& input, const Contai return false; } - void UIDraw::DebugLog() { - mDebugLog = true; - } - bool UIDraw::Visit(UIContainer& container, const InputState& input, const ContainerArea& parent) { - if (!container.HasConfigFlag(UIContainer_DrawFlag)) { + if (!mDebug && !container.HasConfigFlag(UIContainer_DrawFlag)) { return false; } - if (mDebugLog) { - container.DebugLog(); - } ImU32 color = container.GetRenderData().mColor; ImVec2 pTopLeft = container.GetPosition(); @@ -146,8 +139,8 @@ bool UIMove::Visit(UIContainer& container, const InputState& input, const Contai ImVec2 p1 = parent.Transform(pTopLeft, input.mRootPos); ImVec2 p12 = parent.Transform(pCenter, input.mRootPos); ImVec2 p2 = parent.Transform(pLowRight, input.mRootPos); - const char* nameStart = container.GetDisplayName().data(); - const char* nameEnd = container.GetDisplayName().data() + container.GetDisplayName().size(); + const char* nameStart; + const char* nameEnd; switch (container.GetRenderData().mType) { case l::ui::UIRenderType::Rect: @@ -174,6 +167,8 @@ bool UIMove::Visit(UIContainer& container, const InputState& input, const Contai break; case l::ui::UIRenderType::Text: if (!container.GetDisplayName().empty()) { + nameStart = container.GetDisplayName().data(); + nameEnd = container.GetDisplayName().data() + container.GetDisplayName().size(); mDrawList->AddText(ImGui::GetDefaultFont(), 13.0f * parent.mScale, p1, color, nameStart, nameEnd); } break; @@ -181,8 +176,10 @@ bool UIMove::Visit(UIContainer& container, const InputState& input, const Contai break; } - if (container.GetRenderData().mType != l::ui::UIRenderType::Text && !container.GetDisplayName().empty()) { + if (mDebug && !container.GetId().empty()) { // also render name if it is non empty as debug text + nameStart = container.GetId().data(); + nameEnd = container.GetId().data() + container.GetId().size(); mDrawList->AddText(p1, color, nameStart, nameEnd); } From 34cd86416a403766e8967402a387f7abb29448f0 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Thu, 22 Aug 2024 08:17:14 +0200 Subject: [PATCH 043/125] Introduce layout config for auto sizing to parent size. --- .../include/rendering/ui/UIContainer.h | 24 ++++++++---- .../include/rendering/ui/UICreator.h | 4 +- .../source/common/ui/UIContainer.cpp | 38 ++++++++++++++----- .../rendering/source/common/ui/UICreator.cpp | 4 +- .../rendering/source/common/ui/UIVisitors.cpp | 7 ++++ 5 files changed, 55 insertions(+), 22 deletions(-) diff --git a/packages/rendering/include/rendering/ui/UIContainer.h b/packages/rendering/include/rendering/ui/UIContainer.h index b50b2c55..66746740 100644 --- a/packages/rendering/include/rendering/ui/UIContainer.h +++ b/packages/rendering/include/rendering/ui/UIContainer.h @@ -28,7 +28,13 @@ namespace l::ui { Bottom = 2 }; - enum class UILayout { + enum class UILayoutH { + Fixed = 0, + Scaled = 1, + Parent = 2 + }; + + enum class UILayoutV { Fixed = 0, Scaled = 1, Parent = 2 @@ -83,7 +89,8 @@ namespace l::ui { float mBorder = 3.0f; UIAlignH mAlignH = UIAlignH::Left; UIAlignV mAlignV = UIAlignV::Top; - UILayout mLayout = UILayout::Fixed; + UILayoutH mLayoutH = UILayoutH::Fixed; + UILayoutV mLayoutV = UILayoutV::Fixed; }; struct ContainerArea { @@ -141,7 +148,7 @@ namespace l::ui { worldPos.x = parentPos.x + (mPosition.x + mSize.x * 0.5f - contentSize.x * 0.5f) * mScale * parentScale; break; case UIAlignH::Right: - worldPos.x = parentPos.x + (mPosition.x - mLayout.mBorder * 2.0f + mSize.x - contentSize.x) * mScale * parentScale; + worldPos.x = parentPos.x + (mPosition.x - mLayout.mBorder + mSize.x - contentSize.x) * mScale * parentScale; break; } switch (alignV) { @@ -152,7 +159,7 @@ namespace l::ui { worldPos.y = parentPos.y + (mPosition.y + mSize.y * 0.5f - contentSize.y * 0.5f) * mScale * parentScale; break; case UIAlignV::Bottom: - worldPos.y = parentPos.y + (mPosition.y - mLayout.mBorder * 2.0f + mSize.y - contentSize.y) * mScale * parentScale; + worldPos.y = parentPos.y + (mPosition.y - mLayout.mBorder + mSize.y - contentSize.y) * mScale * parentScale; break; } return worldPos; @@ -160,8 +167,8 @@ namespace l::ui { ImVec2 GetWorldSizeLayout(float parentScale) const { ImVec2 worldSize; - worldSize.x = (mSize.x - mLayout.mBorder * 2.0f) * mScale * parentScale; - worldSize.y = (mSize.y - mLayout.mBorder * 2.0f) * mScale * parentScale; + worldSize.x = (mSize.x - mLayout.mBorder) * mScale * parentScale; + worldSize.y = (mSize.y - mLayout.mBorder) * mScale * parentScale; return worldSize; } }; @@ -234,12 +241,13 @@ namespace l::ui { class UIContainer { public: - UIContainer(uint32_t flags = 0, UIRenderType renderType = UIRenderType::Rect, UIAlignH alignH = UIAlignH::Left, UIAlignV alignV = UIAlignV::Top, UILayout layout = UILayout::Fixed) : mConfigFlags(flags) { + UIContainer(uint32_t flags = 0, UIRenderType renderType = UIRenderType::Rect, UIAlignH alignH = UIAlignH::Left, UIAlignV alignV = UIAlignV::Top, UILayoutH layoutH = UILayoutH::Fixed, UILayoutV layoutV = UILayoutV::Fixed) : mConfigFlags(flags) { mArea.mRender.mType = renderType; mArea.mLayout.mBorder = 3.0f; mArea.mLayout.mAlignH = alignH; mArea.mLayout.mAlignV = alignV; - mArea.mLayout.mLayout = layout; + mArea.mLayout.mLayoutH = layoutH; + mArea.mLayout.mLayoutV = layoutV; } ~UIContainer() = default; diff --git a/packages/rendering/include/rendering/ui/UICreator.h b/packages/rendering/include/rendering/ui/UICreator.h index 0108128d..8c1c5fec 100644 --- a/packages/rendering/include/rendering/ui/UICreator.h +++ b/packages/rendering/include/rendering/ui/UICreator.h @@ -28,7 +28,7 @@ namespace l::ui { else if constexpr (std::is_same_v) { id += "UISplit"; } - else if constexpr (std::is_same_v) { + else if constexpr (std::is_same_v) { id += "UILayout"; } else { @@ -45,7 +45,7 @@ namespace l::ui { UICreator() = default; ~UICreator() = default; - UIHandle CreateContainer(uint32_t flags, UIRenderType renderType = UIRenderType::Rect, UIAlignH alignH = UIAlignH::Left, UIAlignV alignV = UIAlignV::Top, UILayout layout = UILayout::Fixed); + UIHandle CreateContainer(uint32_t flags, UIRenderType renderType = UIRenderType::Rect, UIAlignH alignH = UIAlignH::Left, UIAlignV alignV = UIAlignV::Top, UILayoutH layoutH = UILayoutH::Fixed, UILayoutV layoutV = UILayoutV::Fixed); UIHandle CreateSplit(uint32_t flags, bool horizontalSplit = true); protected: diff --git a/packages/rendering/source/common/ui/UIContainer.cpp b/packages/rendering/source/common/ui/UIContainer.cpp index 69723f68..1f82957e 100644 --- a/packages/rendering/source/common/ui/UIContainer.cpp +++ b/packages/rendering/source/common/ui/UIContainer.cpp @@ -157,13 +157,22 @@ namespace l::ui { //current.mPosition = mArea.GetWorldPos(parent.mScale, parent.mPosition); //current.mSize = mArea.GetWorldSize(parent.mScale); auto& layout = GetContainerArea().mLayout; - switch (layout.mLayout) { - case UILayout::Fixed: + switch (layout.mLayoutH) { + case UILayoutH::Fixed: break; - case UILayout::Scaled: + case UILayoutH::Scaled: break; - case UILayout::Parent: - SetSize(parent.GetLocalSize()); + case UILayoutH::Parent: + mArea.mSize.x = parent.GetLocalSize().x; + break; + } + switch (layout.mLayoutV) { + case UILayoutV::Fixed: + break; + case UILayoutV::Scaled: + break; + case UILayoutV::Parent: + mArea.mSize.y = parent.GetLocalSize().y; break; } @@ -194,13 +203,22 @@ namespace l::ui { // an anchor rather than a container, therefore it has to align within it and size auto& layout = GetContainerArea().mLayout; - switch (layout.mLayout) { - case UILayout::Fixed: + switch (layout.mLayoutH) { + case UILayoutH::Fixed: + break; + case UILayoutH::Scaled: + break; + case UILayoutH::Parent: + mArea.mSize.y = parent.GetLocalSize().y; + break; + } + switch (layout.mLayoutV) { + case UILayoutV::Fixed: break; - case UILayout::Scaled: + case UILayoutV::Scaled: break; - case UILayout::Parent: - SetSize(parent.GetLocalSize()); + case UILayoutV::Parent: + mArea.mSize.x = parent.GetLocalSize().x; break; } diff --git a/packages/rendering/source/common/ui/UICreator.cpp b/packages/rendering/source/common/ui/UICreator.cpp index 3f371bbb..66678aca 100644 --- a/packages/rendering/source/common/ui/UICreator.cpp +++ b/packages/rendering/source/common/ui/UICreator.cpp @@ -4,8 +4,8 @@ namespace l::ui { - UIHandle UICreator::CreateContainer(uint32_t flags, UIRenderType renderType, UIAlignH alignH, UIAlignV alignV, UILayout layout) { - std::unique_ptr container = std::make_unique(flags, renderType, alignH, alignV, layout); + UIHandle UICreator::CreateContainer(uint32_t flags, UIRenderType renderType, UIAlignH alignH, UIAlignV alignV, UILayoutH layoutH, UILayoutV layoutV) { + std::unique_ptr container = std::make_unique(flags, renderType, alignH, alignV, layoutH, layoutV); std::string id = CreateUniqueId(); container->SetId(id); diff --git a/packages/rendering/source/common/ui/UIVisitors.cpp b/packages/rendering/source/common/ui/UIVisitors.cpp index e4f6ce8e..992fef71 100644 --- a/packages/rendering/source/common/ui/UIVisitors.cpp +++ b/packages/rendering/source/common/ui/UIVisitors.cpp @@ -166,9 +166,16 @@ bool UIMove::Visit(UIContainer& container, const InputState& input, const Contai case l::ui::UIRenderType::Spline: break; case l::ui::UIRenderType::Text: + + //const ImVec2 text_pos(window->DC.CursorPos.x, window->DC.CursorPos.y + window->DC.CurrLineTextBaseOffset); + //const float wrap_pos_x = window->DC.TextWrapPos; + + // if (window->DC.CurrentColumns) + if (!container.GetDisplayName().empty()) { nameStart = container.GetDisplayName().data(); nameEnd = container.GetDisplayName().data() + container.GetDisplayName().size(); + container.SetSize(ImGui::CalcTextSize(nameStart, nameEnd)); mDrawList->AddText(ImGui::GetDefaultFont(), 13.0f * parent.mScale, p1, color, nameStart, nameEnd); } break; From 5a5d2ff99902f4c8fd6c1350a5ea9decf5b78dd1 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Thu, 22 Aug 2024 09:59:29 +0200 Subject: [PATCH 044/125] Add config for depth first or breadth first. Fix scaling for texts. Fix some bugs. --- .../include/rendering/ui/UIContainer.h | 27 +++++++--- .../include/rendering/ui/UICreator.h | 2 +- .../source/common/ui/UIContainer.cpp | 54 +++++++++++++++---- .../rendering/source/common/ui/UICreator.cpp | 4 +- .../rendering/source/common/ui/UIVisitors.cpp | 4 +- 5 files changed, 70 insertions(+), 21 deletions(-) diff --git a/packages/rendering/include/rendering/ui/UIContainer.h b/packages/rendering/include/rendering/ui/UIContainer.h index 66746740..9f212008 100644 --- a/packages/rendering/include/rendering/ui/UIContainer.h +++ b/packages/rendering/include/rendering/ui/UIContainer.h @@ -55,9 +55,10 @@ namespace l::ui { }; enum class UITraversalMode { - All = 0, // when a visitor performs an action on all containers of its type for example rendering - Once = 1, // when a visitor performs an action on one container of its type for example resizing - Twice = 2 // when a visitor performs an action on two containers of its type for example drag and drop actions like connecting input/output between two containers + AllDFS = 0, // when a visitor performs an action on all containers of its type for example rendering (visiting leaves first) + AllBFS = 1, // when a visitor performs an action on all containers starting with the root (visiting leaves last) + Once = 2, // when a visitor performs an action on one container of its type for example resizing + Twice = 3, // when a visitor performs an action on two containers of its type for example drag and drop actions like connecting input/output between two containers }; struct UIRenderData { @@ -251,8 +252,8 @@ namespace l::ui { } ~UIContainer() = default; - bool Accept(UIVisitor& visitor, const InputState& input, UITraversalMode mode = UITraversalMode::All); - virtual bool Accept(UIVisitor& visitor, const InputState& input, const ContainerArea& parent, UITraversalMode mode = UITraversalMode::All); + bool Accept(UIVisitor& visitor, const InputState& input, UITraversalMode mode = UITraversalMode::AllBFS); + virtual bool Accept(UIVisitor& visitor, const InputState& input, const ContainerArea& parent, UITraversalMode mode = UITraversalMode::AllBFS); virtual void Add(UIContainer* container, int32_t i = -1); template @@ -269,6 +270,7 @@ namespace l::ui { void Notification(uint32_t flag); bool HasNotification(uint32_t flag); bool HasConfigFlag(uint32_t flag); + void SetScale(float scale); void SetPosition(ImVec2 p); void SetSize(ImVec2 s); void SetDisplayName(std::string_view id); @@ -309,17 +311,28 @@ namespace l::ui { std::vector mContent; }; + enum class UISplitMode { + EqualSplitH = 0, + EqualSplitV = 1, + AppendH = 2, + AppendV = 3, + EqualResizeH = 4, + EqualResizeV = 5 + }; + class UISplit : public UIContainer { public: - UISplit(uint32_t flags, bool horizontalSplit = true) : UIContainer(flags), mHorizontalSplit(horizontalSplit) { + UISplit(uint32_t flags, UISplitMode splitMode, UILayoutH layoutH, UILayoutV layoutV) : UIContainer(flags), mSplitMode(splitMode) { mArea.mRender.mType = UIRenderType::Rect; mArea.mLayout.mBorder = 3.0f; + mArea.mLayout.mLayoutH = layoutH; + mArea.mLayout.mLayoutV = layoutV; } ~UISplit() = default; virtual bool Accept(UIVisitor& visitor, const InputState& input, const ContainerArea& parent, UITraversalMode mode); protected: - bool mHorizontalSplit; + UISplitMode mSplitMode; }; } diff --git a/packages/rendering/include/rendering/ui/UICreator.h b/packages/rendering/include/rendering/ui/UICreator.h index 8c1c5fec..771793c6 100644 --- a/packages/rendering/include/rendering/ui/UICreator.h +++ b/packages/rendering/include/rendering/ui/UICreator.h @@ -46,7 +46,7 @@ namespace l::ui { ~UICreator() = default; UIHandle CreateContainer(uint32_t flags, UIRenderType renderType = UIRenderType::Rect, UIAlignH alignH = UIAlignH::Left, UIAlignV alignV = UIAlignV::Top, UILayoutH layoutH = UILayoutH::Fixed, UILayoutV layoutV = UILayoutV::Fixed); - UIHandle CreateSplit(uint32_t flags, bool horizontalSplit = true); + UIHandle CreateSplit(uint32_t flags, UISplitMode splitMode = UISplitMode::AppendV, UILayoutH layoutH = UILayoutH::Fixed, UILayoutV layoutV = UILayoutV::Fixed); protected: std::unordered_map> mContainers; diff --git a/packages/rendering/source/common/ui/UIContainer.cpp b/packages/rendering/source/common/ui/UIContainer.cpp index 1f82957e..bf92e62e 100644 --- a/packages/rendering/source/common/ui/UIContainer.cpp +++ b/packages/rendering/source/common/ui/UIContainer.cpp @@ -82,6 +82,10 @@ namespace l::ui { return (mConfigFlags & flag) == flag; } + void UIContainer::SetScale(float scale) { + mArea.mScale = scale; + } + void UIContainer::SetPosition(ImVec2 p) { mArea.mPosition = p; } @@ -156,6 +160,7 @@ namespace l::ui { //current.mScale = mArea.GetWorldScale(parent.mScale); //current.mPosition = mArea.GetWorldPos(parent.mScale, parent.mPosition); //current.mSize = mArea.GetWorldSize(parent.mScale); + auto& layout = GetContainerArea().mLayout; switch (layout.mLayoutH) { case UILayoutH::Fixed: @@ -176,6 +181,9 @@ namespace l::ui { break; } + if (mode == UITraversalMode::AllBFS && visitor.Active(input)) { + visitor.Visit(*this, input, parent); + } for (auto& content : mContent) { auto contentSize = content->GetSize(); @@ -192,7 +200,7 @@ namespace l::ui { } } - if (visitor.Active(input)) { + if ((mode == UITraversalMode::AllDFS || mode == UITraversalMode::Once || mode == UITraversalMode::Twice) && visitor.Active(input)) { return visitor.Visit(*this, input, parent); } return false; @@ -209,7 +217,7 @@ namespace l::ui { case UILayoutH::Scaled: break; case UILayoutH::Parent: - mArea.mSize.y = parent.GetLocalSize().y; + mArea.mSize.x = parent.GetLocalSize().x; break; } switch (layout.mLayoutV) { @@ -218,7 +226,7 @@ namespace l::ui { case UILayoutV::Scaled: break; case UILayoutV::Parent: - mArea.mSize.x = parent.GetLocalSize().x; + mArea.mSize.y = parent.GetLocalSize().y; break; } @@ -229,11 +237,25 @@ namespace l::ui { current.mSize = mArea.GetWorldSize(parent.mScale); current.mPosition = mArea.GetWorldPos(parent.mScale, parent.mPosition); - if (mHorizontalSplit) { + switch (mSplitMode) { + case UISplitMode::EqualSplitH: current.mSize.x /= contentCount; - } - else { + break; + case UISplitMode::EqualSplitV: current.mSize.y /= contentCount; + break; + case UISplitMode::AppendH: + break; + case UISplitMode::AppendV: + break; + case UISplitMode::EqualResizeH: + break; + case UISplitMode::EqualResizeV: + break; + } + + if (mode == UITraversalMode::AllBFS && visitor.Active(input)) { + visitor.Visit(*this, input, parent); } for (auto& content : mContent) { @@ -243,15 +265,27 @@ namespace l::ui { } } - if (mHorizontalSplit) { + switch (mSplitMode) { + case UISplitMode::EqualSplitH: current.mPosition.x += current.mSize.x; - } - else { + break; + case UISplitMode::EqualSplitV: current.mPosition.y += current.mSize.y; + break; + case UISplitMode::AppendH: + current.mPosition.x += content->GetSize().x * parent.mScale; + break; + case UISplitMode::AppendV: + current.mPosition.y += content->GetSize().y * parent.mScale; + break; + case UISplitMode::EqualResizeH: + break; + case UISplitMode::EqualResizeV: + break; } } - if (visitor.Active(input)) { + if ((mode == UITraversalMode::AllDFS || mode == UITraversalMode::Once || mode == UITraversalMode::Twice) && visitor.Active(input)) { return visitor.Visit(*this, input, parent); } return false; diff --git a/packages/rendering/source/common/ui/UICreator.cpp b/packages/rendering/source/common/ui/UICreator.cpp index 66678aca..c2de7dae 100644 --- a/packages/rendering/source/common/ui/UICreator.cpp +++ b/packages/rendering/source/common/ui/UICreator.cpp @@ -14,8 +14,8 @@ namespace l::ui { return UIHandle{ id, mContainers.at(id).get() }; } - UIHandle UICreator::CreateSplit(uint32_t flags, bool horizontalSplit) { - std::unique_ptr container = std::make_unique(flags, horizontalSplit); + UIHandle UICreator::CreateSplit(uint32_t flags, UISplitMode splitMode, UILayoutH layoutH, UILayoutV layoutV) { + std::unique_ptr container = std::make_unique(flags, splitMode, layoutH, layoutV); std::string id = CreateUniqueId(); container->SetId(id); diff --git a/packages/rendering/source/common/ui/UIVisitors.cpp b/packages/rendering/source/common/ui/UIVisitors.cpp index 992fef71..a3ab2425 100644 --- a/packages/rendering/source/common/ui/UIVisitors.cpp +++ b/packages/rendering/source/common/ui/UIVisitors.cpp @@ -136,6 +136,8 @@ bool UIMove::Visit(UIContainer& container, const InputState& input, const Contai ImVec2 pCenter = container.GetPositionAtCenter(); ImVec2 pLowRight = container.GetPositionAtSize(); ImVec2 pSize = container.GetSize(); + pSize.x *= parent.mScale; + pSize.y *= parent.mScale; ImVec2 p1 = parent.Transform(pTopLeft, input.mRootPos); ImVec2 p12 = parent.Transform(pCenter, input.mRootPos); ImVec2 p2 = parent.Transform(pLowRight, input.mRootPos); @@ -176,7 +178,7 @@ bool UIMove::Visit(UIContainer& container, const InputState& input, const Contai nameStart = container.GetDisplayName().data(); nameEnd = container.GetDisplayName().data() + container.GetDisplayName().size(); container.SetSize(ImGui::CalcTextSize(nameStart, nameEnd)); - mDrawList->AddText(ImGui::GetDefaultFont(), 13.0f * parent.mScale, p1, color, nameStart, nameEnd); + mDrawList->AddText(ImGui::GetDefaultFont(), 13.0f * container.GetScale() * parent.mScale, p1, color, nameStart, nameEnd); } break; case l::ui::UIRenderType::Texture: From 06905ab7356b4c38952272ceece84bedaba58393 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Thu, 22 Aug 2024 10:43:32 +0200 Subject: [PATCH 045/125] Clean up. --- packages/rendering/include/rendering/ui/UICreator.h | 3 --- packages/rendering/source/common/ui/UIVisitors.cpp | 7 +++---- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/packages/rendering/include/rendering/ui/UICreator.h b/packages/rendering/include/rendering/ui/UICreator.h index 771793c6..c2cf62ef 100644 --- a/packages/rendering/include/rendering/ui/UICreator.h +++ b/packages/rendering/include/rendering/ui/UICreator.h @@ -28,9 +28,6 @@ namespace l::ui { else if constexpr (std::is_same_v) { id += "UISplit"; } - else if constexpr (std::is_same_v) { - id += "UILayout"; - } else { id += "Unknown"; } diff --git a/packages/rendering/source/common/ui/UIVisitors.cpp b/packages/rendering/source/common/ui/UIVisitors.cpp index a3ab2425..5a9bd603 100644 --- a/packages/rendering/source/common/ui/UIVisitors.cpp +++ b/packages/rendering/source/common/ui/UIVisitors.cpp @@ -146,17 +146,17 @@ bool UIMove::Visit(UIContainer& container, const InputState& input, const Contai switch (container.GetRenderData().mType) { case l::ui::UIRenderType::Rect: - mDrawList->AddRect(p1, p2, color, 2.0f, ImDrawFlags_RoundCornersAll, 2.0f); + mDrawList->AddRect(p1, p2, color, 5.0f, ImDrawFlags_RoundCornersAll, 1.0f * container.GetScale() * parent.mScale); break; case l::ui::UIRenderType::RectFilled: - mDrawList->AddRectFilled(p1, p2, color, 2.0f, ImDrawFlags_RoundCornersAll); + mDrawList->AddRectFilled(p1, p2, color, 5.0f, ImDrawFlags_RoundCornersAll); break; case l::ui::UIRenderType::Triangle: break; case l::ui::UIRenderType::TriangleFilled: break; case l::ui::UIRenderType::Circle: - mDrawList->AddCircle(p12, pSize.x, color, 15, 2.0f); + mDrawList->AddCircle(p12, pSize.x, color, 15, 2.0f * container.GetScale() * parent.mScale); break; case l::ui::UIRenderType::CircleFilled: mDrawList->AddCircleFilled(p12, pSize.x, color, 15); @@ -196,7 +196,6 @@ bool UIMove::Visit(UIContainer& container, const InputState& input, const Contai case l::ui::UIRenderType::Rect: case l::ui::UIRenderType::RectFilled: case l::ui::UIRenderType::Texture: - mDrawList->AddRect(p1, p2, color, 2.0f, ImDrawFlags_RoundCornersAll, 2.0f); if (container.HasConfigFlag(ui::UIContainer_ResizeFlag)) { ImVec2 p3 = parent.Transform(pLowRight, ImVec2(input.mRootPos.x - 3.0f, input.mRootPos.y - 3.0f)); From de578f2e64d7ced8dd8b90c6c978e0958b4012f1 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Fri, 23 Aug 2024 12:03:32 +0200 Subject: [PATCH 046/125] Massaging the code. --- .../include/rendering/ui/UIContainer.h | 81 +++++++++++++++---- .../include/rendering/ui/UICreator.h | 34 -------- .../include/rendering/ui/UIVisitors.h | 20 ++++- .../source/common/ui/UIContainer.cpp | 43 +++++++++- .../rendering/source/common/ui/UICreator.cpp | 18 ----- .../rendering/source/common/ui/UIVisitors.cpp | 80 +++++++++++++++--- 6 files changed, 193 insertions(+), 83 deletions(-) diff --git a/packages/rendering/include/rendering/ui/UIContainer.h b/packages/rendering/include/rendering/ui/UIContainer.h index 9f212008..3787d111 100644 --- a/packages/rendering/include/rendering/ui/UIContainer.h +++ b/packages/rendering/include/rendering/ui/UIContainer.h @@ -13,6 +13,7 @@ #include "implot/implot_internal.h" #include +#include namespace l::ui { @@ -87,7 +88,6 @@ namespace l::ui { }; struct UILayoutData { - float mBorder = 3.0f; UIAlignH mAlignH = UIAlignH::Left; UIAlignV mAlignV = UIAlignV::Top; UILayoutH mLayoutH = UILayoutH::Fixed; @@ -98,6 +98,8 @@ namespace l::ui { ImVec2 mPosition; ImVec2 mSize = ImVec2(20.0f, 20.0f); float mScale = 1.0f; + float mBorder = 3.0f; + UILayoutData mLayout; UIRenderData mRender; @@ -120,15 +122,15 @@ namespace l::ui { // Used in ui container layout, this is where we premultiply parent scale ImVec2 GetWorldPos(float parentScale, ImVec2 parentPos) { ImVec2 worldPos; - worldPos.x = parentPos.x + (mPosition.x + mLayout.mBorder) * mScale * parentScale; - worldPos.y = parentPos.y + (mPosition.y + mLayout.mBorder) * mScale * parentScale; + worldPos.x = parentPos.x + (mPosition.x + mBorder) * mScale * parentScale; + worldPos.y = parentPos.y + (mPosition.y + mBorder) * mScale * parentScale; return worldPos; } ImVec2 GetWorldSize(float parentScale) const { ImVec2 worldSize; - worldSize.x = (mSize.x - mLayout.mBorder * 2.0f) * mScale * parentScale; - worldSize.y = (mSize.y - mLayout.mBorder * 2.0f) * mScale * parentScale; + worldSize.x = (mSize.x - mBorder * 2.0f) * mScale * parentScale; + worldSize.y = (mSize.y - mBorder * 2.0f) * mScale * parentScale; return worldSize; } @@ -143,24 +145,24 @@ namespace l::ui { ImVec2 worldPos; switch (alignH) { case UIAlignH::Left: - worldPos.x = parentPos.x + (mPosition.x + mLayout.mBorder) * mScale * parentScale; + worldPos.x = parentPos.x + (mPosition.x + mBorder) * mScale * parentScale; break; case UIAlignH::Center: worldPos.x = parentPos.x + (mPosition.x + mSize.x * 0.5f - contentSize.x * 0.5f) * mScale * parentScale; break; case UIAlignH::Right: - worldPos.x = parentPos.x + (mPosition.x - mLayout.mBorder + mSize.x - contentSize.x) * mScale * parentScale; + worldPos.x = parentPos.x + (mPosition.x - mBorder + mSize.x - contentSize.x) * mScale * parentScale; break; } switch (alignV) { case UIAlignV::Top: - worldPos.y = parentPos.y + (mPosition.y + mLayout.mBorder) * mScale * parentScale; + worldPos.y = parentPos.y + (mPosition.y + mBorder) * mScale * parentScale; break; case UIAlignV::Middle: worldPos.y = parentPos.y + (mPosition.y + mSize.y * 0.5f - contentSize.y * 0.5f) * mScale * parentScale; break; case UIAlignV::Bottom: - worldPos.y = parentPos.y + (mPosition.y - mLayout.mBorder + mSize.y - contentSize.y) * mScale * parentScale; + worldPos.y = parentPos.y + (mPosition.y - mBorder + mSize.y - contentSize.y) * mScale * parentScale; break; } return worldPos; @@ -168,8 +170,8 @@ namespace l::ui { ImVec2 GetWorldSizeLayout(float parentScale) const { ImVec2 worldSize; - worldSize.x = (mSize.x - mLayout.mBorder) * mScale * parentScale; - worldSize.y = (mSize.y - mLayout.mBorder) * mScale * parentScale; + worldSize.x = (mSize.x - mBorder) * mScale * parentScale; + worldSize.y = (mSize.y - mBorder) * mScale * parentScale; return worldSize; } }; @@ -183,6 +185,8 @@ namespace l::ui { class UIVisitor { public: + virtual ~UIVisitor() = default; + virtual bool Active(const InputState&) { return true; } @@ -211,12 +215,16 @@ namespace l::ui { const uint32_t UIContainer_ZoomFlag = 0x00000040; // Can be scaled, zoomed in/out const uint32_t UIContainer_MoveFlag = 0x00000080; // Can be moved when grabbed const uint32_t UIContainer_ResizeFlag = 0x00000100; // Can be resized when grabbing bottom right corner + const uint32_t UIContainer_InputFlag = 0x00000200; // Can be grabbed and dropped on a container with output flag + const uint32_t UIContainer_OutputFlag = 0x00000400; // Can be dropped a grabbed + const uint32_t UIContainer_LinkFlag = 0x00000800; // Can be dropped a grabbed class UIDraw; template>> class UIHandle { public: + UIHandle() : mContainer(nullptr) {}; UIHandle(std::string_view id, UIContainer* container) : mId(id), mContainer(container) {}; ~UIHandle() = default; @@ -227,6 +235,11 @@ namespace l::ui { return mId; } + void reset() { + mId.clear(); + mContainer = nullptr; + } + T* get() { return reinterpret_cast(mContainer); } @@ -244,13 +257,13 @@ namespace l::ui { public: UIContainer(uint32_t flags = 0, UIRenderType renderType = UIRenderType::Rect, UIAlignH alignH = UIAlignH::Left, UIAlignV alignV = UIAlignV::Top, UILayoutH layoutH = UILayoutH::Fixed, UILayoutV layoutV = UILayoutV::Fixed) : mConfigFlags(flags) { mArea.mRender.mType = renderType; - mArea.mLayout.mBorder = 3.0f; + mArea.mBorder = 3.0f; mArea.mLayout.mAlignH = alignH; mArea.mLayout.mAlignV = alignV; mArea.mLayout.mLayoutH = layoutH; mArea.mLayout.mLayoutV = layoutV; } - ~UIContainer() = default; + virtual ~UIContainer() = default; bool Accept(UIVisitor& visitor, const InputState& input, UITraversalMode mode = UITraversalMode::AllBFS); virtual bool Accept(UIVisitor& visitor, const InputState& input, const ContainerArea& parent, UITraversalMode mode = UITraversalMode::AllBFS); @@ -258,7 +271,7 @@ namespace l::ui { template void Add(UIHandle& handle, int32_t i = -1) { - Add(handle.get()); + Add(handle.get(), i); } virtual void Remove(int32_t i); @@ -276,6 +289,10 @@ namespace l::ui { void SetDisplayName(std::string_view id); void SetId(std::string_view id); void SetContainerArea(const ContainerArea& area); + void SetParent(UIContainer* input); + void SetCoParent(UIContainer* input); + UIContainer* GetParent(); + UIContainer* GetCoParent(); ImVec2 GetPosition(bool untransformed = false); ImVec2 GetPositionAtCenter(ImVec2 offset = ImVec2(), bool untransformed = false); ImVec2 GetPositionAtSize(ImVec2 offset = ImVec2(), bool untransformed = false); @@ -308,6 +325,8 @@ namespace l::ui { uint32_t mConfigFlags = 0; // Active visitor flags uint32_t mNotificationFlags = 0; // Notification flags for ux feedback (resizing box animation etc) + UIContainer* mParent; + UIContainer* mCoParent; // when a container is influenced by two parent in a specific way defined by the type of container and the visitor std::vector mContent; }; @@ -324,7 +343,7 @@ namespace l::ui { public: UISplit(uint32_t flags, UISplitMode splitMode, UILayoutH layoutH, UILayoutV layoutV) : UIContainer(flags), mSplitMode(splitMode) { mArea.mRender.mType = UIRenderType::Rect; - mArea.mLayout.mBorder = 3.0f; + mArea.mBorder = 3.0f; mArea.mLayout.mLayoutH = layoutH; mArea.mLayout.mLayoutV = layoutV; } @@ -335,4 +354,36 @@ namespace l::ui { UISplitMode mSplitMode; }; + template>> + std::string CreateUniqueId() { + static uint32_t mId = 0; + std::string id; + if constexpr (std::is_same_v) { + id += "UIContainer"; + } + else if constexpr (std::is_same_v) { + id += "UISplit"; + } + else { + id += "Unknown"; + } + + id += l::string::to_hex(mId++, 4); + + return id; + } + + class UICreator { + public: + UICreator() = default; + ~UICreator() = default; + + UIHandle CreateContainer(uint32_t flags, UIRenderType renderType = UIRenderType::Rect, UIAlignH alignH = UIAlignH::Left, UIAlignV alignV = UIAlignV::Top, UILayoutH layoutH = UILayoutH::Fixed, UILayoutV layoutV = UILayoutV::Fixed); + UIHandle CreateSplit(uint32_t flags, UISplitMode splitMode = UISplitMode::AppendV, UILayoutH layoutH = UILayoutH::Fixed, UILayoutV layoutV = UILayoutV::Fixed); + + protected: + std::unordered_map> mContainers; + std::vector mVisitors; + + }; } diff --git a/packages/rendering/include/rendering/ui/UICreator.h b/packages/rendering/include/rendering/ui/UICreator.h index c2cf62ef..f1ea3325 100644 --- a/packages/rendering/include/rendering/ui/UICreator.h +++ b/packages/rendering/include/rendering/ui/UICreator.h @@ -1,9 +1,6 @@ #pragma once #include "logging/LoggingAll.h" -#include "rendering/ui/UIContainer.h" -#include "rendering/ui/UIVisitors.h" -#include "rendering/ui/UIWindow.h" #define GLFW_INCLUDE_NONE #include @@ -18,37 +15,6 @@ #include namespace l::ui { - template>> - std::string CreateUniqueId() { - static uint32_t mId = 0; - std::string id; - if constexpr (std::is_same_v) { - id += "UIContainer"; - } - else if constexpr (std::is_same_v) { - id += "UISplit"; - } - else { - id += "Unknown"; - } - id += l::string::to_hex(mId++, 4); - - return id; - } - - class UICreator { - public: - UICreator() = default; - ~UICreator() = default; - - UIHandle CreateContainer(uint32_t flags, UIRenderType renderType = UIRenderType::Rect, UIAlignH alignH = UIAlignH::Left, UIAlignV alignV = UIAlignV::Top, UILayoutH layoutH = UILayoutH::Fixed, UILayoutV layoutV = UILayoutV::Fixed); - UIHandle CreateSplit(uint32_t flags, UISplitMode splitMode = UISplitMode::AppendV, UILayoutH layoutH = UILayoutH::Fixed, UILayoutV layoutV = UILayoutV::Fixed); - - protected: - std::unordered_map> mContainers; - std::vector mVisitors; - - }; } diff --git a/packages/rendering/include/rendering/ui/UIVisitors.h b/packages/rendering/include/rendering/ui/UIVisitors.h index 64123cda..6b4ae59b 100644 --- a/packages/rendering/include/rendering/ui/UIVisitors.h +++ b/packages/rendering/include/rendering/ui/UIVisitors.h @@ -26,7 +26,7 @@ namespace l::ui { virtual bool Visit(UIContainer& container, const InputState& input, const ContainerArea& parent); protected: bool mDragging = false; - UIContainer* mCurrentContainer = nullptr; + UIContainer* mSourceContainer = nullptr; }; class UIMove : public UIVisitor { @@ -35,7 +35,7 @@ namespace l::ui { virtual bool Visit(UIContainer& container, const InputState& input, const ContainerArea& parent); protected: bool mMoving = false; - UIContainer* mCurrentContainer = nullptr; + UIContainer* mSourceContainer = nullptr; }; class UIResize : public UIVisitor { @@ -44,7 +44,7 @@ namespace l::ui { protected: bool mResizing = false; float mResizeAreaSize = 8.0f; - UIContainer* mCurrentContainer = nullptr; + UIContainer* mSourceContainer = nullptr; }; class UIDraw : public UIVisitor { @@ -56,4 +56,18 @@ namespace l::ui { protected: ImDrawList* mDrawList; }; + + class UILinkIO : public UIVisitor { + public: + UILinkIO(UICreator* creator = nullptr) : mCreator(creator) {} + ~UILinkIO() = default; + + virtual bool Visit(UIContainer& container, const InputState& input, const ContainerArea& parent); + protected: + bool mDragging = false; + UIHandle mLinkContainer; + UICreator* mCreator = nullptr; + float mResizeAreaSize = 8.0f; + }; + } diff --git a/packages/rendering/source/common/ui/UIContainer.cpp b/packages/rendering/source/common/ui/UIContainer.cpp index bf92e62e..20e3c575 100644 --- a/packages/rendering/source/common/ui/UIContainer.cpp +++ b/packages/rendering/source/common/ui/UIContainer.cpp @@ -4,6 +4,26 @@ namespace l::ui { + UIHandle UICreator::CreateContainer(uint32_t flags, UIRenderType renderType, UIAlignH alignH, UIAlignV alignV, UILayoutH layoutH, UILayoutV layoutV) { + std::unique_ptr container = std::make_unique(flags, renderType, alignH, alignV, layoutH, layoutV); + + std::string id = CreateUniqueId(); + container->SetId(id); + mContainers.insert({ id, std::move(container) }); + + return UIHandle{ id, mContainers.at(id).get() }; + } + + UIHandle UICreator::CreateSplit(uint32_t flags, UISplitMode splitMode, UILayoutH layoutH, UILayoutV layoutV) { + std::unique_ptr container = std::make_unique(flags, splitMode, layoutH, layoutV); + + std::string id = CreateUniqueId(); + container->SetId(id); + mContainers.insert({ id, std::move(container) }); + + return UIHandle{ id, mContainers.at(id).get() }; + } + ImVec2 DragMovement(const ImVec2& prevPos, const ImVec2& curPos, float curScale) { ImVec2 move = curPos; move.x -= prevPos.x; @@ -38,6 +58,7 @@ namespace l::ui { void UIContainer::Add(UIContainer* container, int32_t i) { if (i < 0) { mContent.push_back(container); + container->SetParent(this); } else { ASSERT(static_cast(i) < mContent.size()); @@ -58,8 +79,10 @@ namespace l::ui { void UIContainer::Resize(ImVec2 localChange) { mArea.mSize.x += localChange.x; mArea.mSize.y += localChange.y; - mArea.mSize.x = mArea.mSize.x < 1.0f ? 1.0f : mArea.mSize.x; - mArea.mSize.y = mArea.mSize.y < 1.0f ? 1.0f : mArea.mSize.y; + if (mArea.mRender.mType != UIRenderType::Spline) { + mArea.mSize.x = mArea.mSize.x < 1.0f ? 1.0f : mArea.mSize.x; + mArea.mSize.y = mArea.mSize.y < 1.0f ? 1.0f : mArea.mSize.y; + } } void UIContainer::Rescale(float localChange) { @@ -107,6 +130,22 @@ namespace l::ui { mArea = area; } + UIContainer* UIContainer::GetParent() { + return mParent; + } + + void UIContainer::SetParent(UIContainer* parent) { + mParent = parent; + } + + void UIContainer::SetCoParent(UIContainer* coParent) { + mCoParent = coParent; + } + + UIContainer* UIContainer::GetCoParent() { + return mCoParent; + } + ImVec2 UIContainer::GetPosition(bool untransformed) { if (untransformed) { return mArea.mPosition; diff --git a/packages/rendering/source/common/ui/UICreator.cpp b/packages/rendering/source/common/ui/UICreator.cpp index c2de7dae..04b717d8 100644 --- a/packages/rendering/source/common/ui/UICreator.cpp +++ b/packages/rendering/source/common/ui/UICreator.cpp @@ -4,23 +4,5 @@ namespace l::ui { - UIHandle UICreator::CreateContainer(uint32_t flags, UIRenderType renderType, UIAlignH alignH, UIAlignV alignV, UILayoutH layoutH, UILayoutV layoutV) { - std::unique_ptr container = std::make_unique(flags, renderType, alignH, alignV, layoutH, layoutV); - std::string id = CreateUniqueId(); - container->SetId(id); - mContainers.insert({ id, std::move(container)}); - - return UIHandle{ id, mContainers.at(id).get() }; - } - - UIHandle UICreator::CreateSplit(uint32_t flags, UISplitMode splitMode, UILayoutH layoutH, UILayoutV layoutV) { - std::unique_ptr container = std::make_unique(flags, splitMode, layoutH, layoutV); - - std::string id = CreateUniqueId(); - container->SetId(id); - mContainers.insert({ id, std::move(container) }); - - return UIHandle{ id, mContainers.at(id).get() }; - } } diff --git a/packages/rendering/source/common/ui/UIVisitors.cpp b/packages/rendering/source/common/ui/UIVisitors.cpp index 5a9bd603..decb8d30 100644 --- a/packages/rendering/source/common/ui/UIVisitors.cpp +++ b/packages/rendering/source/common/ui/UIVisitors.cpp @@ -44,17 +44,17 @@ bool UIDrag::Visit(UIContainer& container, const InputState& input, const Contai if (input.mStarted && !mDragging) { if (input.GetLocalPos().x >= 0.0f && input.GetLocalPos().y >= 0.0f) { mDragging = true; - mCurrentContainer = &container; + mSourceContainer = &container; } } - if (mDragging && mCurrentContainer == &container) { + if (mDragging && mSourceContainer == &container) { ImVec2 move = DragMovement(input.mPrevPos, input.mCurPos, parent.mScale * container.GetScale()); container.Move(move); container.Notification(UIContainer_DragFlag); if (input.mStopped) { mDragging = false; - mCurrentContainer = nullptr; + mSourceContainer = nullptr; } return mDragging; } @@ -72,17 +72,17 @@ bool UIMove::Visit(UIContainer& container, const InputState& input, const Contai if (input.mStarted && !mMoving) { if (Overlap(input.GetLocalPos(), container.GetPosition(), container.GetPositionAtSize(), parent)) { mMoving = true; - mCurrentContainer = &container; + mSourceContainer = &container; } } - if (mMoving && mCurrentContainer == &container) { + if (mMoving && mSourceContainer == &container) { ImVec2 move = DragMovement(input.mPrevPos, input.mCurPos, parent.mScale); container.Move(move); container.Notification(UIContainer_MoveFlag); if (input.mStopped) { mMoving = false; - mCurrentContainer = nullptr; + mSourceContainer = nullptr; } return mMoving; } @@ -97,7 +97,7 @@ bool UIMove::Visit(UIContainer& container, const InputState& input, const Contai const float radii = mResizeAreaSize * 0.5f; ImVec2 p = container.GetPositionAtSize(); if (OverlapScreenRect(input.GetLocalPos(), p, ImVec2(radii, radii), parent)) { - mCurrentContainer = &container; + mSourceContainer = &container; container.Notification(UIContainer_ResizeFlag); if (input.mStarted) { @@ -108,17 +108,17 @@ bool UIMove::Visit(UIContainer& container, const InputState& input, const Contai } } else { - mCurrentContainer = nullptr; + mSourceContainer = nullptr; container.ClearNotifications(); } } - if (mResizing && mCurrentContainer == &container) { + if (mResizing && mSourceContainer == &container) { ImVec2 move = DragMovement(input.mPrevPos, input.mCurPos, parent.mScale); container.Resize(move); if (input.mStopped) { mResizing = false; - mCurrentContainer = nullptr; + mSourceContainer = nullptr; container.ClearNotifications(); } return mResizing; @@ -166,6 +166,16 @@ bool UIMove::Visit(UIContainer& container, const InputState& input, const Contai case l::ui::UIRenderType::PolygonFilled: break; case l::ui::UIRenderType::Spline: + if (container.HasConfigFlag(UIContainer_LinkFlag)) { + ImVec2 pLinkInput = container.GetParent()->GetPositionAtCenter(); + ImVec2 pLinkOutput = container.GetCoParent()->GetPositionAtCenter(); + + } + else { + ImVec2 p11 = parent.Transform(ImVec2(pTopLeft.x + 120.0f, pTopLeft.y), input.mRootPos); + ImVec2 p22 = parent.Transform(ImVec2(pLowRight.x - 120.0f, pLowRight.y), input.mRootPos); + mDrawList->AddBezierCubic(p1, p11, p22, p2, color, 2.0f, 15); + } break; case l::ui::UIRenderType::Text: @@ -196,7 +206,7 @@ bool UIMove::Visit(UIContainer& container, const InputState& input, const Contai case l::ui::UIRenderType::Rect: case l::ui::UIRenderType::RectFilled: case l::ui::UIRenderType::Texture: - + case l::ui::UIRenderType::Spline: if (container.HasConfigFlag(ui::UIContainer_ResizeFlag)) { ImVec2 p3 = parent.Transform(pLowRight, ImVec2(input.mRootPos.x - 3.0f, input.mRootPos.y - 3.0f)); ImVec2 p4 = parent.Transform(pLowRight, ImVec2(input.mRootPos.x + 3.0f, input.mRootPos.y + 3.0f)); @@ -214,4 +224,52 @@ bool UIMove::Visit(UIContainer& container, const InputState& input, const Contai return false; } + bool UILinkIO::Visit(UIContainer& container, const InputState& input, const ContainerArea& parent) { + // Create link at from a clicked output container + const float radii = mResizeAreaSize * 0.5f; + if (!mDragging && input.mStarted && container.HasConfigFlag(UIContainer_OutputFlag)) { + ImVec2 p = container.GetPosition(); + if (OverlapScreenRect(input.GetLocalPos(), p, ImVec2(radii, radii), parent)) { + mDragging = true; + mLinkContainer = mCreator->CreateContainer(UIContainer_LinkFlag | UIContainer_DrawFlag, UIRenderType::Spline); + container.Add(mLinkContainer); + return true; + } + } + + if (mDragging) { + if (mLinkContainer.get() == &container) { + // On the newly create link container, drag the end point along the mouse movement + ImVec2 move = DragMovement(input.mPrevPos, input.mCurPos, parent.mScale * container.GetScale()); + mLinkContainer->Move(move); + } + else if (mLinkContainer->GetParent() != &container) { + // When checking for an input container we check all but the output and link containers + if (container.HasConfigFlag(UIContainer_InputFlag)) { + ImVec2 p = container.GetPosition(); + if (OverlapScreenRect(input.GetLocalPos(), p, ImVec2(radii, radii), parent)) { + container.Notification(UIContainer_LinkFlag); + + if (input.mStopped) { + mLinkContainer->SetCoParent(&container); + mLinkContainer.reset(); + mDragging = false; + container.ClearNotifications(); + return true; + } + } + else { + container.ClearNotifications(); + } + } + if (input.mStopped) { + mLinkContainer.reset(); + mDragging = false; + container.ClearNotifications(); + return true; + } + } + } + return false; + } } From 4c99ba2b298854a4cb58bbf52dc6ee45b35d085b Mon Sep 17 00:00:00 2001 From: lnd3 Date: Fri, 23 Aug 2024 18:44:26 +0200 Subject: [PATCH 047/125] Add ui links for connecting container input and output. --- .../include/rendering/ui/UIContainer.h | 41 ++- .../include/rendering/ui/UIVisitors.h | 18 +- .../source/common/ui/UIContainer.cpp | 243 ++++++++++-------- .../rendering/source/common/ui/UIVisitors.cpp | 233 +++++++++-------- 4 files changed, 311 insertions(+), 224 deletions(-) diff --git a/packages/rendering/include/rendering/ui/UIContainer.h b/packages/rendering/include/rendering/ui/UIContainer.h index 3787d111..fe2681cb 100644 --- a/packages/rendering/include/rendering/ui/UIContainer.h +++ b/packages/rendering/include/rendering/ui/UIContainer.h @@ -108,10 +108,10 @@ namespace l::ui { } // Used in visitors only and parent scale is already premultiplied - ImVec2 Transform(const ImVec2& p, ImVec2 rootPos = ImVec2()) const { + ImVec2 Transform(const ImVec2& p, ImVec2 screenRootPos = ImVec2()) const { ImVec2 transformed; - transformed.x = rootPos.x + mPosition.x + p.x * mScale; - transformed.y = rootPos.y + mPosition.y + p.y * mScale; + transformed.x = screenRootPos.x + mPosition.x + p.x * mScale; + transformed.y = screenRootPos.y + mPosition.y + p.y * mScale; return transformed; } @@ -180,6 +180,8 @@ namespace l::ui { bool Overlap(const ImVec2& p, const ImVec2& pMin, const ImVec2& pMax); bool Overlap(const ImVec2& p, const ImVec2& pMin, const ImVec2& pMax, const ContainerArea& parent); bool OverlapScreenRect(const ImVec2& p, const ImVec2& pCenter, const ImVec2& offset, const ContainerArea& parent); + bool OverlapScreenCircle(const ImVec2& p, const ImVec2& pCenter, float radii, const ContainerArea& parent); + bool OverlapCircle(const ImVec2& p, const ImVec2& pCenter, float radii); class UIContainer; @@ -187,7 +189,7 @@ namespace l::ui { public: virtual ~UIVisitor() = default; - virtual bool Active(const InputState&) { + virtual bool Active(UIContainer&, const InputState&) { return true; } virtual bool Visit(UIContainer&, const InputState&, const ContainerArea&) { @@ -196,6 +198,9 @@ namespace l::ui { virtual void Debug(bool on = true) { mDebug = on; } + virtual bool ShouldUpdateContainer() { + return false; + } protected: bool mDebug = false; @@ -240,8 +245,8 @@ namespace l::ui { mContainer = nullptr; } - T* get() { - return reinterpret_cast(mContainer); + UIContainer* get() { + return mContainer; } T* operator->() { @@ -266,7 +271,7 @@ namespace l::ui { virtual ~UIContainer() = default; bool Accept(UIVisitor& visitor, const InputState& input, UITraversalMode mode = UITraversalMode::AllBFS); - virtual bool Accept(UIVisitor& visitor, const InputState& input, const ContainerArea& parent, UITraversalMode mode = UITraversalMode::AllBFS); + virtual bool Accept(UIVisitor& visitor, const InputState& input, const ContainerArea& contentArea, UITraversalMode mode = UITraversalMode::AllBFS); virtual void Add(UIContainer* container, int32_t i = -1); template @@ -276,6 +281,17 @@ namespace l::ui { virtual void Remove(int32_t i); + template + void Remove(UIHandle& handle) { + for (auto it = mContent.begin(); it != mContent.end();it++) { + auto containerPtr = *it; + if (containerPtr == handle.get()) { + mContent.erase(it); + break; + } + } + } + void Move(ImVec2 localChange); void Resize(ImVec2 localChange); void Rescale(float localChange); @@ -289,6 +305,7 @@ namespace l::ui { void SetDisplayName(std::string_view id); void SetId(std::string_view id); void SetContainerArea(const ContainerArea& area); + void SetLayoutArea(const ContainerArea& area); void SetParent(UIContainer* input); void SetCoParent(UIContainer* input); UIContainer* GetParent(); @@ -299,6 +316,7 @@ namespace l::ui { ImVec2 GetSize(bool untransformed = false); float GetScale(); ContainerArea& GetContainerArea(); + const ContainerArea& GetLayoutArea() const; void DebugLog(); @@ -325,9 +343,12 @@ namespace l::ui { uint32_t mConfigFlags = 0; // Active visitor flags uint32_t mNotificationFlags = 0; // Notification flags for ux feedback (resizing box animation etc) - UIContainer* mParent; - UIContainer* mCoParent; // when a container is influenced by two parent in a specific way defined by the type of container and the visitor + ContainerArea mAreaT; + + UIContainer* mParent = nullptr; + UIContainer* mCoParent = nullptr; // when a container is influenced by two parent in a specific way defined by the type of container and the visitor std::vector mContent; + std::vector mContentAreas; }; enum class UISplitMode { @@ -349,7 +370,7 @@ namespace l::ui { } ~UISplit() = default; - virtual bool Accept(UIVisitor& visitor, const InputState& input, const ContainerArea& parent, UITraversalMode mode); + virtual bool Accept(UIVisitor& visitor, const InputState& input, const ContainerArea& contentArea, UITraversalMode mode); protected: UISplitMode mSplitMode; }; diff --git a/packages/rendering/include/rendering/ui/UIVisitors.h b/packages/rendering/include/rendering/ui/UIVisitors.h index 6b4ae59b..36c175b8 100644 --- a/packages/rendering/include/rendering/ui/UIVisitors.h +++ b/packages/rendering/include/rendering/ui/UIVisitors.h @@ -14,15 +14,23 @@ namespace l::ui { + class UIUpdate : public UIVisitor { + public: + UIUpdate() {} + ~UIUpdate() = default; + + virtual bool ShouldUpdateContainer(); + }; + class UIZoom : public UIVisitor { public: - virtual bool Active(const InputState& input); + virtual bool Active(UIContainer& container, const InputState& input); virtual bool Visit(UIContainer& container, const InputState& input, const ContainerArea& parent); }; class UIDrag : public UIVisitor { public: - virtual bool Active(const InputState& input); + virtual bool Active(UIContainer& container, const InputState& input); virtual bool Visit(UIContainer& container, const InputState& input, const ContainerArea& parent); protected: bool mDragging = false; @@ -31,7 +39,7 @@ namespace l::ui { class UIMove : public UIVisitor { public: - virtual bool Active(const InputState& input); + virtual bool Active(UIContainer& container, const InputState& input); virtual bool Visit(UIContainer& container, const InputState& input, const ContainerArea& parent); protected: bool mMoving = false; @@ -59,15 +67,17 @@ namespace l::ui { class UILinkIO : public UIVisitor { public: + virtual bool Active(UIContainer& container, const InputState& input); + UILinkIO(UICreator* creator = nullptr) : mCreator(creator) {} ~UILinkIO() = default; virtual bool Visit(UIContainer& container, const InputState& input, const ContainerArea& parent); protected: bool mDragging = false; + bool mPossibleLinkImminent = false; UIHandle mLinkContainer; UICreator* mCreator = nullptr; - float mResizeAreaSize = 8.0f; }; } diff --git a/packages/rendering/source/common/ui/UIContainer.cpp b/packages/rendering/source/common/ui/UIContainer.cpp index 20e3c575..7cbfa13e 100644 --- a/packages/rendering/source/common/ui/UIContainer.cpp +++ b/packages/rendering/source/common/ui/UIContainer.cpp @@ -37,37 +37,43 @@ namespace l::ui { return pMin.x < p.x && pMin.y < p.y && pMax.x > p.x && pMax.y > p.y; } - bool Overlap(const ImVec2& p, const ImVec2& pMin, const ImVec2& pMax, const ContainerArea& parent) { - ImVec2 pMinT = parent.Transform(pMin); - ImVec2 pMaxT = parent.Transform(pMax); + bool Overlap(const ImVec2& p, const ImVec2& pMin, const ImVec2& pMax, const ContainerArea& contentArea) { + ImVec2 pMinT = contentArea.Transform(pMin); + ImVec2 pMaxT = contentArea.Transform(pMax); return pMinT.x < p.x && pMinT.y < p.y && pMaxT.x > p.x && pMaxT.y > p.y; } - bool OverlapScreenRect(const ImVec2& p, const ImVec2& pCenter, const ImVec2& offset, const ContainerArea& parent) { - ImVec2 pMinT = parent.Transform(pCenter, ImVec2(-offset.x, -offset.y)); - ImVec2 pMaxT = parent.Transform(pCenter, ImVec2(offset.x, offset.y)); + bool OverlapScreenRect(const ImVec2& p, const ImVec2& pCenter, const ImVec2& screenOffset, const ContainerArea& contentArea) { + ImVec2 pMinT = contentArea.Transform(pCenter, ImVec2(-screenOffset.x, -screenOffset.y)); + ImVec2 pMaxT = contentArea.Transform(pCenter, ImVec2(screenOffset.x, screenOffset.y)); return pMinT.x < p.x && pMinT.y < p.y && pMaxT.x > p.x && pMaxT.y > p.y; } - bool OverlapScreenCircle(const ImVec2& p, const ImVec2& pCenter, float radii, const ContainerArea& parent) { - ImVec2 pT = parent.Transform(pCenter); + bool OverlapScreenCircle(const ImVec2& p, const ImVec2& pCenter, float radii, const ContainerArea& contentArea) { + ImVec2 pT = contentArea.Transform(pCenter); ImVec2 d = ImVec2(pT.x - p.x, pT.y - p.y); - return d.x*d.x + d.y*d.y < radii * radii; + return d.x * d.x + d.y * d.y < radii * radii; + } + + bool OverlapCircle(const ImVec2& p, const ImVec2& pCenter, float radii) { + ImVec2 d = ImVec2(pCenter.x - p.x, pCenter.y - p.y); + return d.x * d.x + d.y * d.y < radii * radii; } void UIContainer::Add(UIContainer* container, int32_t i) { if (i < 0) { mContent.push_back(container); - container->SetParent(this); } else { ASSERT(static_cast(i) < mContent.size()); mContent.insert(mContent.begin() + i, container); } + container->SetParent(this); } void UIContainer::Remove(int32_t i) { ASSERT(i >= 0 && static_cast(i) < mContent.size()); + mContent.at(i)->SetParent(nullptr); mContent.erase(mContent.begin() + i); } @@ -125,11 +131,14 @@ namespace l::ui { mId = id; } - void UIContainer::SetContainerArea(const ContainerArea& area) { mArea = area; } + void UIContainer::SetLayoutArea(const ContainerArea& transformedLayoutArea) { + mAreaT = transformedLayoutArea; + } + UIContainer* UIContainer::GetParent() { return mParent; } @@ -182,140 +191,121 @@ namespace l::ui { return mArea; } + const ContainerArea& UIContainer::GetLayoutArea() const { + return mAreaT; + } + void UIContainer::DebugLog() { LOG(LogDebug) << "UIContainer: " << mDisplayName << ", [" << mArea.mScale << "][" << mArea.mPosition.x << ", " << mArea.mPosition.y << "][" << mArea.mSize.x << ", " << mArea.mSize.y << "]"; } bool UIContainer::Accept(UIVisitor& visitor, const InputState& input, UITraversalMode mode) { ContainerArea current; - if (visitor.Active(input)) { + if (visitor.Active(*this, input)) { return Accept(visitor, input, current, mode); } return false; } - bool UIContainer::Accept(UIVisitor& visitor, const InputState& input, const ContainerArea& parent, UITraversalMode mode) { - //ContainerArea current; - //current.mScale = mArea.GetWorldScale(parent.mScale); - //current.mPosition = mArea.GetWorldPos(parent.mScale, parent.mPosition); - //current.mSize = mArea.GetWorldSize(parent.mScale); - - auto& layout = GetContainerArea().mLayout; - switch (layout.mLayoutH) { - case UILayoutH::Fixed: - break; - case UILayoutH::Scaled: - break; - case UILayoutH::Parent: - mArea.mSize.x = parent.GetLocalSize().x; - break; - } - switch (layout.mLayoutV) { - case UILayoutV::Fixed: - break; - case UILayoutV::Scaled: - break; - case UILayoutV::Parent: - mArea.mSize.y = parent.GetLocalSize().y; - break; + bool UIContainer::Accept(UIVisitor& visitor, const InputState& input, const ContainerArea& contentArea, UITraversalMode mode) { + if (visitor.ShouldUpdateContainer()) { + auto& layout = GetContainerArea().mLayout; + switch (layout.mLayoutH) { + case UILayoutH::Fixed: + break; + case UILayoutH::Scaled: + break; + case UILayoutH::Parent: + mArea.mSize.x = contentArea.GetLocalSize().x; + break; + } + switch (layout.mLayoutV) { + case UILayoutV::Fixed: + break; + case UILayoutV::Scaled: + break; + case UILayoutV::Parent: + mArea.mSize.y = contentArea.GetLocalSize().y; + break; + } } - if (mode == UITraversalMode::AllBFS && visitor.Active(input)) { - visitor.Visit(*this, input, parent); + if (mode == UITraversalMode::AllBFS && visitor.Active(*this, input)) { + visitor.Visit(*this, input, contentArea); } + size_t i = 0; for (auto& content : mContent) { - auto contentSize = content->GetSize(); - auto& contentLayout = content->GetContainerArea().mLayout; - ContainerArea current; - current.mScale = mArea.GetWorldScale(parent.mScale); - current.mSize = mArea.GetWorldSizeLayout(parent.mScale); - current.mPosition = mArea.GetWorldPosLayout(parent.mScale, parent.mPosition, contentSize, contentLayout.mAlignH, contentLayout.mAlignV); - - if (content->Accept(visitor, input, current, mode)) { + if (visitor.ShouldUpdateContainer()) { + auto contentSize = content->GetSize(); + auto& contentLayout = content->GetContainerArea().mLayout; + ContainerArea current; + current.mScale = mArea.GetWorldScale(contentArea.mScale); + current.mSize = mArea.GetWorldSizeLayout(contentArea.mScale); + current.mPosition = mArea.GetWorldPosLayout(contentArea.mScale, contentArea.mPosition, contentSize, contentLayout.mAlignH, contentLayout.mAlignV); + + mContentAreas.resize(mContent.size()); + mContentAreas.at(i) = current; + + content->SetLayoutArea(current); + } + + if (content->Accept(visitor, input, mContentAreas.at(i), mode)) { if (mode == UITraversalMode::Once) { return true; } } + i++; } - if ((mode == UITraversalMode::AllDFS || mode == UITraversalMode::Once || mode == UITraversalMode::Twice) && visitor.Active(input)) { - return visitor.Visit(*this, input, parent); + if ((mode == UITraversalMode::AllDFS || mode == UITraversalMode::Once || mode == UITraversalMode::Twice) && visitor.Active(*this, input)) { + return visitor.Visit(*this, input, contentArea); } return false; } - bool UISplit::Accept(UIVisitor& visitor, const InputState& input, const ContainerArea& parent, UITraversalMode mode) { + bool UISplit::Accept(UIVisitor& visitor, const InputState& input, const ContainerArea& contentArea, UITraversalMode mode) { // Since we can have multiple layouts in a container for different content, it will act as // an anchor rather than a container, therefore it has to align within it and size - auto& layout = GetContainerArea().mLayout; - switch (layout.mLayoutH) { - case UILayoutH::Fixed: - break; - case UILayoutH::Scaled: - break; - case UILayoutH::Parent: - mArea.mSize.x = parent.GetLocalSize().x; - break; - } - switch (layout.mLayoutV) { - case UILayoutV::Fixed: - break; - case UILayoutV::Scaled: - break; - case UILayoutV::Parent: - mArea.mSize.y = parent.GetLocalSize().y; - break; - } - - float contentCount = static_cast(mContent.size()); - ContainerArea current; - current.mScale = mArea.GetWorldScale(parent.mScale); - current.mSize = mArea.GetWorldSize(parent.mScale); - current.mPosition = mArea.GetWorldPos(parent.mScale, parent.mPosition); - - switch (mSplitMode) { - case UISplitMode::EqualSplitH: - current.mSize.x /= contentCount; - break; - case UISplitMode::EqualSplitV: - current.mSize.y /= contentCount; - break; - case UISplitMode::AppendH: - break; - case UISplitMode::AppendV: - break; - case UISplitMode::EqualResizeH: - break; - case UISplitMode::EqualResizeV: - break; - } - if (mode == UITraversalMode::AllBFS && visitor.Active(input)) { - visitor.Visit(*this, input, parent); - } - - for (auto& content : mContent) { - if (content->Accept(visitor, input, current, mode)) { - if (mode == UITraversalMode::Once) { - return true; - } + if (visitor.ShouldUpdateContainer()) { + auto& layout = GetContainerArea().mLayout; + switch (layout.mLayoutH) { + case UILayoutH::Fixed: + break; + case UILayoutH::Scaled: + break; + case UILayoutH::Parent: + mArea.mSize.x = contentArea.GetLocalSize().x; + break; + } + switch (layout.mLayoutV) { + case UILayoutV::Fixed: + break; + case UILayoutV::Scaled: + break; + case UILayoutV::Parent: + mArea.mSize.y = contentArea.GetLocalSize().y; + break; } + float contentCount = static_cast(mContent.size()); + current.mScale = mArea.GetWorldScale(contentArea.mScale); + current.mSize = mArea.GetWorldSize(contentArea.mScale); + current.mPosition = mArea.GetWorldPos(contentArea.mScale, contentArea.mPosition); + switch (mSplitMode) { case UISplitMode::EqualSplitH: - current.mPosition.x += current.mSize.x; + current.mSize.x /= contentCount; break; case UISplitMode::EqualSplitV: - current.mPosition.y += current.mSize.y; + current.mSize.y /= contentCount; break; case UISplitMode::AppendH: - current.mPosition.x += content->GetSize().x * parent.mScale; break; case UISplitMode::AppendV: - current.mPosition.y += content->GetSize().y * parent.mScale; break; case UISplitMode::EqualResizeH: break; @@ -324,8 +314,49 @@ namespace l::ui { } } - if ((mode == UITraversalMode::AllDFS || mode == UITraversalMode::Once || mode == UITraversalMode::Twice) && visitor.Active(input)) { - return visitor.Visit(*this, input, parent); + if (mode == UITraversalMode::AllBFS && visitor.Active(*this, input)) { + visitor.Visit(*this, input, contentArea); + } + + size_t i = 0; + for (auto& content : mContent) { + if (visitor.ShouldUpdateContainer()) { + mContentAreas.resize(mContent.size()); + mContentAreas.at(i) = current; + content->SetLayoutArea(current); + } + + if (content->Accept(visitor, input, mContentAreas.at(i), mode)) { + if (mode == UITraversalMode::Once) { + return true; + } + } + + if (visitor.ShouldUpdateContainer()) { + switch (mSplitMode) { + case UISplitMode::EqualSplitH: + current.mPosition.x += current.mSize.x; + break; + case UISplitMode::EqualSplitV: + current.mPosition.y += current.mSize.y; + break; + case UISplitMode::AppendH: + current.mPosition.x += content->GetSize().x * contentArea.mScale; + break; + case UISplitMode::AppendV: + current.mPosition.y += content->GetSize().y * contentArea.mScale; + break; + case UISplitMode::EqualResizeH: + break; + case UISplitMode::EqualResizeV: + break; + } + } + i++; + } + + if ((mode == UITraversalMode::AllDFS || mode == UITraversalMode::Once || mode == UITraversalMode::Twice) && visitor.Active(*this, input)) { + return visitor.Visit(*this, input, contentArea); } return false; } diff --git a/packages/rendering/source/common/ui/UIVisitors.cpp b/packages/rendering/source/common/ui/UIVisitors.cpp index decb8d30..030e551a 100644 --- a/packages/rendering/source/common/ui/UIVisitors.cpp +++ b/packages/rendering/source/common/ui/UIVisitors.cpp @@ -2,81 +2,85 @@ namespace l::ui { -bool UIZoom::Active(const InputState& input) { - return input.mScroll != 0; -} + bool UIUpdate::ShouldUpdateContainer() { + return true; + } -bool UIZoom::Visit(UIContainer& container, const InputState& input, const ContainerArea& parent) { - if (!container.HasConfigFlag(UIContainer_ZoomFlag)) { - return false; + bool UIZoom::Active(UIContainer&, const InputState& input) { + return input.mScroll != 0; } - if (input.mScroll != 0.0f) { - float scaleChange = 1.0f; - float scaleDelta = 0.1f; - scaleChange = 1.0f + scaleDelta * input.mScroll; - if (input.mScroll < 0.0f) { - scaleChange = 1.0f / (1.0f - scaleDelta * input.mScroll); + + bool UIZoom::Visit(UIContainer& container, const InputState& input, const ContainerArea& contentArea) { + if (!container.HasConfigFlag(UIContainer_ZoomFlag)) { + return false; } - if ((container.GetScale() > 100.0f && scaleChange > 1.0f) || (container.GetScale() < 0.01f && scaleChange < 1.0f)) { + if (input.mScroll != 0.0f) { + float scaleChange = 1.0f; + float scaleDelta = 0.1f; + scaleChange = 1.0f + scaleDelta * input.mScroll; + if (input.mScroll < 0.0f) { + scaleChange = 1.0f / (1.0f - scaleDelta * input.mScroll); + } + if ((container.GetScale() > 100.0f && scaleChange > 1.0f) || (container.GetScale() < 0.01f && scaleChange < 1.0f)) { + return true; + } + + ImVec2 mousePos = input.GetLocalPos(); + ImVec2 localMousePos = ImVec2(contentArea.mPosition.x - mousePos.x, contentArea.mPosition.y - mousePos.y); + ImVec2 p = container.GetPosition(); + container.Rescale(scaleChange); + p.x = ((p.x + localMousePos.x) * scaleChange - localMousePos.x) / container.GetScale(); + p.y = ((p.y + localMousePos.y) * scaleChange - localMousePos.y) / container.GetScale(); + container.SetPosition(p); return true; } - - ImVec2 mousePos = input.GetLocalPos(); - ImVec2 localMousePos = ImVec2(parent.mPosition.x - mousePos.x, parent.mPosition.y - mousePos.y); - ImVec2 p = container.GetPosition(); - container.Rescale(scaleChange); - p.x = ((p.x + localMousePos.x) * scaleChange - localMousePos.x) / container.GetScale(); - p.y = ((p.y + localMousePos.y) * scaleChange - localMousePos.y) / container.GetScale(); - container.SetPosition(p); - return true; + return false; } - return false; -} - -bool UIDrag::Active(const InputState& input) { - return (input.mStarted && !mDragging) || mDragging; -} -bool UIDrag::Visit(UIContainer& container, const InputState& input, const ContainerArea& parent) { - if (!container.HasConfigFlag(UIContainer_DragFlag)) { - return false; + bool UIDrag::Active(UIContainer&, const InputState& input) { + return (input.mStarted && !mDragging) || mDragging; } - if (input.mStarted && !mDragging) { - if (input.GetLocalPos().x >= 0.0f && input.GetLocalPos().y >= 0.0f) { - mDragging = true; - mSourceContainer = &container; + + bool UIDrag::Visit(UIContainer& container, const InputState& input, const ContainerArea& contentArea) { + if (!container.HasConfigFlag(UIContainer_DragFlag)) { + return false; } - } - if (mDragging && mSourceContainer == &container) { - ImVec2 move = DragMovement(input.mPrevPos, input.mCurPos, parent.mScale * container.GetScale()); - container.Move(move); - container.Notification(UIContainer_DragFlag); + if (input.mStarted && !mDragging) { + if (input.GetLocalPos().x >= 0.0f && input.GetLocalPos().y >= 0.0f) { + mDragging = true; + mSourceContainer = &container; + } + } + if (mDragging && mSourceContainer == &container) { + ImVec2 move = DragMovement(input.mPrevPos, input.mCurPos, contentArea.mScale * container.GetScale()); + container.Move(move); + container.Notification(UIContainer_DragFlag); - if (input.mStopped) { - mDragging = false; - mSourceContainer = nullptr; + if (input.mStopped) { + mDragging = false; + mSourceContainer = nullptr; + } + return mDragging; } - return mDragging; + return false; } - return false; -} -bool UIMove::Active(const InputState& input) { - return (input.mStarted && !mMoving) || mMoving; -} + bool UIMove::Active(UIContainer&, const InputState& input) { + return (input.mStarted && !mMoving) || mMoving; + } -bool UIMove::Visit(UIContainer& container, const InputState& input, const ContainerArea& parent) { + bool UIMove::Visit(UIContainer& container, const InputState& input, const ContainerArea& contentArea) { if(!container.HasConfigFlag(UIContainer_MoveFlag)){ return false; } if (input.mStarted && !mMoving) { - if (Overlap(input.GetLocalPos(), container.GetPosition(), container.GetPositionAtSize(), parent)) { + if (Overlap(input.GetLocalPos(), container.GetPosition(), container.GetPositionAtSize(), contentArea)) { mMoving = true; mSourceContainer = &container; } } if (mMoving && mSourceContainer == &container) { - ImVec2 move = DragMovement(input.mPrevPos, input.mCurPos, parent.mScale); + ImVec2 move = DragMovement(input.mPrevPos, input.mCurPos, contentArea.mScale); container.Move(move); container.Notification(UIContainer_MoveFlag); @@ -89,14 +93,14 @@ bool UIMove::Visit(UIContainer& container, const InputState& input, const Contai return false; } - bool UIResize::Visit(UIContainer& container, const InputState& input, const ContainerArea& parent) { + bool UIResize::Visit(UIContainer& container, const InputState& input, const ContainerArea& contentArea) { if (!container.HasConfigFlag(UIContainer_ResizeFlag)) { return false; } if (!mResizing) { const float radii = mResizeAreaSize * 0.5f; ImVec2 p = container.GetPositionAtSize(); - if (OverlapScreenRect(input.GetLocalPos(), p, ImVec2(radii, radii), parent)) { + if (OverlapScreenRect(input.GetLocalPos(), p, ImVec2(radii, radii), contentArea)) { mSourceContainer = &container; container.Notification(UIContainer_ResizeFlag); @@ -113,7 +117,7 @@ bool UIMove::Visit(UIContainer& container, const InputState& input, const Contai } } if (mResizing && mSourceContainer == &container) { - ImVec2 move = DragMovement(input.mPrevPos, input.mCurPos, parent.mScale); + ImVec2 move = DragMovement(input.mPrevPos, input.mCurPos, contentArea.mScale); container.Resize(move); if (input.mStopped) { @@ -126,27 +130,31 @@ bool UIMove::Visit(UIContainer& container, const InputState& input, const Contai return false; } - bool UIDraw::Visit(UIContainer& container, const InputState& input, const ContainerArea& parent) { + bool UIDraw::Visit(UIContainer& container, const InputState& input, const ContainerArea& contentArea) { if (!mDebug && !container.HasConfigFlag(UIContainer_DrawFlag)) { return false; } + float splineThickness = 2.0f; ImU32 color = container.GetRenderData().mColor; ImVec2 pTopLeft = container.GetPosition(); ImVec2 pCenter = container.GetPositionAtCenter(); ImVec2 pLowRight = container.GetPositionAtSize(); ImVec2 pSize = container.GetSize(); - pSize.x *= parent.mScale; - pSize.y *= parent.mScale; - ImVec2 p1 = parent.Transform(pTopLeft, input.mRootPos); - ImVec2 p12 = parent.Transform(pCenter, input.mRootPos); - ImVec2 p2 = parent.Transform(pLowRight, input.mRootPos); + pSize.x *= contentArea.mScale; + pSize.y *= contentArea.mScale; + ImVec2 p1 = contentArea.Transform(pTopLeft, input.mRootPos); + ImVec2 p12 = contentArea.Transform(pCenter, input.mRootPos); + ImVec2 p2 = contentArea.Transform(pLowRight, input.mRootPos); + ImVec2 p11; + ImVec2 p22; + const char* nameStart; const char* nameEnd; switch (container.GetRenderData().mType) { case l::ui::UIRenderType::Rect: - mDrawList->AddRect(p1, p2, color, 5.0f, ImDrawFlags_RoundCornersAll, 1.0f * container.GetScale() * parent.mScale); + mDrawList->AddRect(p1, p2, color, 5.0f, ImDrawFlags_RoundCornersAll, 1.0f * container.GetScale() * contentArea.mScale); break; case l::ui::UIRenderType::RectFilled: mDrawList->AddRectFilled(p1, p2, color, 5.0f, ImDrawFlags_RoundCornersAll); @@ -156,7 +164,7 @@ bool UIMove::Visit(UIContainer& container, const InputState& input, const Contai case l::ui::UIRenderType::TriangleFilled: break; case l::ui::UIRenderType::Circle: - mDrawList->AddCircle(p12, pSize.x, color, 15, 2.0f * container.GetScale() * parent.mScale); + mDrawList->AddCircle(p12, pSize.x, color, 15, 2.0f * container.GetScale() * contentArea.mScale); break; case l::ui::UIRenderType::CircleFilled: mDrawList->AddCircleFilled(p12, pSize.x, color, 15); @@ -167,15 +175,27 @@ bool UIMove::Visit(UIContainer& container, const InputState& input, const Contai break; case l::ui::UIRenderType::Spline: if (container.HasConfigFlag(UIContainer_LinkFlag)) { - ImVec2 pLinkInput = container.GetParent()->GetPositionAtCenter(); - ImVec2 pLinkOutput = container.GetCoParent()->GetPositionAtCenter(); + splineThickness = container.HasNotification(UIContainer_LinkFlag) ? 2.0f * splineThickness : splineThickness; + ImVec2 pLinkInput = container.GetParent()->GetPosition(); + p1 = contentArea.Transform(ImVec2(), input.mRootPos); + p11 = contentArea.Transform(ImVec2(pLinkInput.x + 120.0f, pLinkInput.y), input.mRootPos); + if (container.GetCoParent() != nullptr) { + ImVec2 pLinkOutput = container.GetCoParent()->GetPosition(); + auto& coContentArea = container.GetCoParent()->GetLayoutArea(); + p2 = coContentArea.Transform(pLinkOutput, input.mRootPos); + p22 = coContentArea.Transform(ImVec2(pLinkOutput.x - 120.0f, pLinkOutput.y), input.mRootPos); + } + else { + p2 = input.mCurPos; + p22 = ImVec2(p2.x - 120.0f, p2.y); + } } else { - ImVec2 p11 = parent.Transform(ImVec2(pTopLeft.x + 120.0f, pTopLeft.y), input.mRootPos); - ImVec2 p22 = parent.Transform(ImVec2(pLowRight.x - 120.0f, pLowRight.y), input.mRootPos); - mDrawList->AddBezierCubic(p1, p11, p22, p2, color, 2.0f, 15); + p11 = contentArea.Transform(ImVec2(pTopLeft.x + 120.0f, pTopLeft.y), input.mRootPos); + p22 = contentArea.Transform(ImVec2(pLowRight.x - 120.0f, pLowRight.y), input.mRootPos); } + mDrawList->AddBezierCubic(p1, p11, p22, p2, color, splineThickness, 30); break; case l::ui::UIRenderType::Text: @@ -188,7 +208,7 @@ bool UIMove::Visit(UIContainer& container, const InputState& input, const Contai nameStart = container.GetDisplayName().data(); nameEnd = container.GetDisplayName().data() + container.GetDisplayName().size(); container.SetSize(ImGui::CalcTextSize(nameStart, nameEnd)); - mDrawList->AddText(ImGui::GetDefaultFont(), 13.0f * container.GetScale() * parent.mScale, p1, color, nameStart, nameEnd); + mDrawList->AddText(ImGui::GetDefaultFont(), 13.0f * container.GetScale() * contentArea.mScale, p1, color, nameStart, nameEnd); } break; case l::ui::UIRenderType::Texture: @@ -208,11 +228,11 @@ bool UIMove::Visit(UIContainer& container, const InputState& input, const Contai case l::ui::UIRenderType::Texture: case l::ui::UIRenderType::Spline: if (container.HasConfigFlag(ui::UIContainer_ResizeFlag)) { - ImVec2 p3 = parent.Transform(pLowRight, ImVec2(input.mRootPos.x - 3.0f, input.mRootPos.y - 3.0f)); - ImVec2 p4 = parent.Transform(pLowRight, ImVec2(input.mRootPos.x + 3.0f, input.mRootPos.y + 3.0f)); + ImVec2 p3 = contentArea.Transform(pLowRight, ImVec2(input.mRootPos.x - 3.0f, input.mRootPos.y - 3.0f)); + ImVec2 p4 = contentArea.Transform(pLowRight, ImVec2(input.mRootPos.x + 3.0f, input.mRootPos.y + 3.0f)); if (container.HasNotification(ui::UIContainer_ResizeFlag)) { - p3 = parent.Transform(pLowRight, ImVec2(input.mRootPos.x - 5.0f, input.mRootPos.y - 5.0f)); - p4 = parent.Transform(pLowRight, ImVec2(input.mRootPos.x + 5.0f, input.mRootPos.y + 5.0f)); + p3 = contentArea.Transform(pLowRight, ImVec2(input.mRootPos.x - 5.0f, input.mRootPos.y - 5.0f)); + p4 = contentArea.Transform(pLowRight, ImVec2(input.mRootPos.x + 5.0f, input.mRootPos.y + 5.0f)); } mDrawList->AddRectFilled(p3, p4, color); } @@ -224,12 +244,17 @@ bool UIMove::Visit(UIContainer& container, const InputState& input, const Contai return false; } - bool UILinkIO::Visit(UIContainer& container, const InputState& input, const ContainerArea& parent) { + bool UILinkIO::Active(UIContainer& container, const InputState&) { + return container.HasConfigFlag(UIContainer_InputFlag) || container.HasConfigFlag(UIContainer_OutputFlag) || container.HasConfigFlag(UIContainer_LinkFlag); + } + + bool UILinkIO::Visit(UIContainer& container, const InputState& input, const ContainerArea& contentArea) { // Create link at from a clicked output container - const float radii = mResizeAreaSize * 0.5f; - if (!mDragging && input.mStarted && container.HasConfigFlag(UIContainer_OutputFlag)) { - ImVec2 p = container.GetPosition(); - if (OverlapScreenRect(input.GetLocalPos(), p, ImVec2(radii, radii), parent)) { + if (container.HasConfigFlag(UIContainer_OutputFlag) && !mDragging && input.mStarted && mLinkContainer.get() == nullptr) { + ImVec2 pCenter = container.GetPosition(); + ImVec2 size = container.GetSize(); + ImVec2 pT = contentArea.Transform(pCenter, input.mRootPos); + if (OverlapCircle(input.mCurPos, pT, size.x * contentArea.mScale)) { mDragging = true; mLinkContainer = mCreator->CreateContainer(UIContainer_LinkFlag | UIContainer_DrawFlag, UIRenderType::Spline); container.Add(mLinkContainer); @@ -237,36 +262,36 @@ bool UIMove::Visit(UIContainer& container, const InputState& input, const Contai } } - if (mDragging) { - if (mLinkContainer.get() == &container) { - // On the newly create link container, drag the end point along the mouse movement - ImVec2 move = DragMovement(input.mPrevPos, input.mCurPos, parent.mScale * container.GetScale()); - mLinkContainer->Move(move); + if (mDragging && mLinkContainer.get() != nullptr && container.HasConfigFlag(UIContainer_LinkFlag) && mLinkContainer.get() == &container) { + // On the newly created link container, drag the end point along the mouse movement + ImVec2 move = DragMovement(input.mPrevPos, input.mCurPos, contentArea.mScale * container.GetScale()); + mLinkContainer->Move(move); + } + + if (mDragging && mLinkContainer.get() != nullptr && container.HasConfigFlag(UIContainer_InputFlag)) { + ImVec2 pCenter = container.GetPosition(); + ImVec2 size = container.GetSize(); + ImVec2 pT = contentArea.Transform(pCenter, input.mRootPos); + + if (OverlapCircle(input.mCurPos, pT, size.x * contentArea.mScale)) { + mLinkContainer->Notification(UIContainer_LinkFlag); + mLinkContainer->SetCoParent(&container); + } + else if (mLinkContainer->GetCoParent() == &container){ + mLinkContainer->SetCoParent(nullptr); + mLinkContainer->ClearNotifications(); } - else if (mLinkContainer->GetParent() != &container) { - // When checking for an input container we check all but the output and link containers - if (container.HasConfigFlag(UIContainer_InputFlag)) { - ImVec2 p = container.GetPosition(); - if (OverlapScreenRect(input.GetLocalPos(), p, ImVec2(radii, radii), parent)) { - container.Notification(UIContainer_LinkFlag); - if (input.mStopped) { - mLinkContainer->SetCoParent(&container); - mLinkContainer.reset(); - mDragging = false; - container.ClearNotifications(); - return true; - } - } - else { - container.ClearNotifications(); - } - } - if (input.mStopped) { + if (input.mStopped) { + mLinkContainer->ClearNotifications(); + if (mLinkContainer->GetCoParent() != nullptr) { + mDragging = false; mLinkContainer.reset(); + } + else { + mLinkContainer->GetParent()->Remove(mLinkContainer); mDragging = false; - container.ClearNotifications(); - return true; + mLinkContainer.reset(); } } } From f458500fc5152680d2e5b7183090791003394f7c Mon Sep 17 00:00:00 2001 From: lnd3 Date: Fri, 23 Aug 2024 18:57:07 +0200 Subject: [PATCH 048/125] Add ui link disconnect logic. --- packages/rendering/source/common/ui/UIVisitors.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/packages/rendering/source/common/ui/UIVisitors.cpp b/packages/rendering/source/common/ui/UIVisitors.cpp index 030e551a..c759fb00 100644 --- a/packages/rendering/source/common/ui/UIVisitors.cpp +++ b/packages/rendering/source/common/ui/UIVisitors.cpp @@ -261,6 +261,16 @@ namespace l::ui { return true; } } + if (container.HasConfigFlag(UIContainer_LinkFlag) && !mDragging && input.mStarted && mLinkContainer.get() == nullptr && container.GetCoParent() != nullptr) { + ImVec2 pCenter = container.GetCoParent()->GetPosition(); + ImVec2 size = container.GetCoParent()->GetSize(); + ImVec2 pT = container.GetCoParent()->GetLayoutArea().Transform(pCenter, input.mRootPos); + if (OverlapCircle(input.mCurPos, pT, size.x * container.GetCoParent()->GetLayoutArea().mScale)) { + mDragging = true; + mLinkContainer.mContainer = &container; + return true; + } + } if (mDragging && mLinkContainer.get() != nullptr && container.HasConfigFlag(UIContainer_LinkFlag) && mLinkContainer.get() == &container) { // On the newly created link container, drag the end point along the mouse movement From 23b2da2c6720c5dd37eb372200e39e7f9de2a827 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Sat, 24 Aug 2024 01:42:04 +0200 Subject: [PATCH 049/125] Fix a scaling issue. --- .../rendering/source/common/ui/UIVisitors.cpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/packages/rendering/source/common/ui/UIVisitors.cpp b/packages/rendering/source/common/ui/UIVisitors.cpp index c759fb00..77d200dd 100644 --- a/packages/rendering/source/common/ui/UIVisitors.cpp +++ b/packages/rendering/source/common/ui/UIVisitors.cpp @@ -178,22 +178,27 @@ namespace l::ui { splineThickness = container.HasNotification(UIContainer_LinkFlag) ? 2.0f * splineThickness : splineThickness; ImVec2 pLinkInput = container.GetParent()->GetPosition(); p1 = contentArea.Transform(ImVec2(), input.mRootPos); - p11 = contentArea.Transform(ImVec2(pLinkInput.x + 120.0f, pLinkInput.y), input.mRootPos); if (container.GetCoParent() != nullptr) { ImVec2 pLinkOutput = container.GetCoParent()->GetPosition(); auto& coContentArea = container.GetCoParent()->GetLayoutArea(); p2 = coContentArea.Transform(pLinkOutput, input.mRootPos); - p22 = coContentArea.Transform(ImVec2(pLinkOutput.x - 120.0f, pLinkOutput.y), input.mRootPos); + float d12 = sqrt(0.25f*((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y)) / (contentArea.mScale * contentArea.mScale)); + + p11 = contentArea.Transform(ImVec2(pLinkInput.x + d12, pLinkInput.y), input.mRootPos); + p22 = coContentArea.Transform(ImVec2(pLinkOutput.x - d12, pLinkOutput.y), input.mRootPos); } else { p2 = input.mCurPos; - p22 = ImVec2(p2.x - 120.0f, p2.y); + float d12 = sqrt(0.25f * ((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y)) / (contentArea.mScale * contentArea.mScale)); + p11 = contentArea.Transform(ImVec2(pLinkInput.x + d12, pLinkInput.y), input.mRootPos); + p22 = ImVec2(p2.x - d12 * contentArea.mScale, p2.y); } } else { - p11 = contentArea.Transform(ImVec2(pTopLeft.x + 120.0f, pTopLeft.y), input.mRootPos); - p22 = contentArea.Transform(ImVec2(pLowRight.x - 120.0f, pLowRight.y), input.mRootPos); + float d12 = sqrt(0.25f * ((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y))); + p11 = contentArea.Transform(ImVec2(pTopLeft.x + d12, pTopLeft.y), input.mRootPos); + p22 = contentArea.Transform(ImVec2(pLowRight.x - d12, pLowRight.y), input.mRootPos); } mDrawList->AddBezierCubic(p1, p11, p22, p2, color, splineThickness, 30); break; From e0a8d4d6ca44355aa0f8e24c87fec9b480dad296 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Sat, 24 Aug 2024 06:29:37 +0200 Subject: [PATCH 050/125] Simplify some things in ui container implementation. --- .../include/rendering/ui/UIContainer.h | 10 ++++---- .../source/common/ui/UIContainer.cpp | 24 +++++++++---------- 2 files changed, 16 insertions(+), 18 deletions(-) diff --git a/packages/rendering/include/rendering/ui/UIContainer.h b/packages/rendering/include/rendering/ui/UIContainer.h index fe2681cb..001f7358 100644 --- a/packages/rendering/include/rendering/ui/UIContainer.h +++ b/packages/rendering/include/rendering/ui/UIContainer.h @@ -56,10 +56,8 @@ namespace l::ui { }; enum class UITraversalMode { - AllDFS = 0, // when a visitor performs an action on all containers of its type for example rendering (visiting leaves first) - AllBFS = 1, // when a visitor performs an action on all containers starting with the root (visiting leaves last) - Once = 2, // when a visitor performs an action on one container of its type for example resizing - Twice = 3, // when a visitor performs an action on two containers of its type for example drag and drop actions like connecting input/output between two containers + DFS = 0, // Depth first search, leaves first, root last + BFS = 1, // Breadth first search, root first, leaves last }; struct UIRenderData { @@ -270,8 +268,8 @@ namespace l::ui { } virtual ~UIContainer() = default; - bool Accept(UIVisitor& visitor, const InputState& input, UITraversalMode mode = UITraversalMode::AllBFS); - virtual bool Accept(UIVisitor& visitor, const InputState& input, const ContainerArea& contentArea, UITraversalMode mode = UITraversalMode::AllBFS); + bool Accept(UIVisitor& visitor, const InputState& input, UITraversalMode mode = UITraversalMode::BFS); + virtual bool Accept(UIVisitor& visitor, const InputState& input, const ContainerArea& contentArea, UITraversalMode mode = UITraversalMode::BFS); virtual void Add(UIContainer* container, int32_t i = -1); template diff --git a/packages/rendering/source/common/ui/UIContainer.cpp b/packages/rendering/source/common/ui/UIContainer.cpp index 7cbfa13e..17006c59 100644 --- a/packages/rendering/source/common/ui/UIContainer.cpp +++ b/packages/rendering/source/common/ui/UIContainer.cpp @@ -230,8 +230,10 @@ namespace l::ui { } } - if (mode == UITraversalMode::AllBFS && visitor.Active(*this, input)) { - visitor.Visit(*this, input, contentArea); + if (mode == UITraversalMode::BFS && visitor.Active(*this, input)) { + if (visitor.Visit(*this, input, contentArea)) { + return true; + } } size_t i = 0; @@ -251,14 +253,12 @@ namespace l::ui { } if (content->Accept(visitor, input, mContentAreas.at(i), mode)) { - if (mode == UITraversalMode::Once) { - return true; - } + return true; } i++; } - if ((mode == UITraversalMode::AllDFS || mode == UITraversalMode::Once || mode == UITraversalMode::Twice) && visitor.Active(*this, input)) { + if (mode == UITraversalMode::DFS && visitor.Active(*this, input)) { return visitor.Visit(*this, input, contentArea); } return false; @@ -314,8 +314,10 @@ namespace l::ui { } } - if (mode == UITraversalMode::AllBFS && visitor.Active(*this, input)) { - visitor.Visit(*this, input, contentArea); + if (mode == UITraversalMode::BFS && visitor.Active(*this, input)) { + if (visitor.Visit(*this, input, contentArea)) { + return true; + } } size_t i = 0; @@ -327,9 +329,7 @@ namespace l::ui { } if (content->Accept(visitor, input, mContentAreas.at(i), mode)) { - if (mode == UITraversalMode::Once) { - return true; - } + return true; } if (visitor.ShouldUpdateContainer()) { @@ -355,7 +355,7 @@ namespace l::ui { i++; } - if ((mode == UITraversalMode::AllDFS || mode == UITraversalMode::Once || mode == UITraversalMode::Twice) && visitor.Active(*this, input)) { + if ((mode == UITraversalMode::DFS) && visitor.Active(*this, input)) { return visitor.Visit(*this, input, contentArea); } return false; From 0ca5e18af817c8ff7fb109102bd8c19eefafd289 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Sat, 24 Aug 2024 06:41:38 +0200 Subject: [PATCH 051/125] Remove container area from visitor interface as it now comes from the container being visited instead. --- .../include/rendering/ui/UIContainer.h | 2 +- .../include/rendering/ui/UIVisitors.h | 12 ++++---- .../source/common/ui/UIContainer.cpp | 10 +++---- .../rendering/source/common/ui/UIVisitors.cpp | 28 +++++++++++++++---- 4 files changed, 34 insertions(+), 18 deletions(-) diff --git a/packages/rendering/include/rendering/ui/UIContainer.h b/packages/rendering/include/rendering/ui/UIContainer.h index 001f7358..972328c1 100644 --- a/packages/rendering/include/rendering/ui/UIContainer.h +++ b/packages/rendering/include/rendering/ui/UIContainer.h @@ -190,7 +190,7 @@ namespace l::ui { virtual bool Active(UIContainer&, const InputState&) { return true; } - virtual bool Visit(UIContainer&, const InputState&, const ContainerArea&) { + virtual bool Visit(UIContainer&, const InputState&) { return false; } virtual void Debug(bool on = true) { diff --git a/packages/rendering/include/rendering/ui/UIVisitors.h b/packages/rendering/include/rendering/ui/UIVisitors.h index 36c175b8..4afc6479 100644 --- a/packages/rendering/include/rendering/ui/UIVisitors.h +++ b/packages/rendering/include/rendering/ui/UIVisitors.h @@ -25,13 +25,13 @@ namespace l::ui { class UIZoom : public UIVisitor { public: virtual bool Active(UIContainer& container, const InputState& input); - virtual bool Visit(UIContainer& container, const InputState& input, const ContainerArea& parent); + virtual bool Visit(UIContainer& container, const InputState& input); }; class UIDrag : public UIVisitor { public: virtual bool Active(UIContainer& container, const InputState& input); - virtual bool Visit(UIContainer& container, const InputState& input, const ContainerArea& parent); + virtual bool Visit(UIContainer& container, const InputState& input); protected: bool mDragging = false; UIContainer* mSourceContainer = nullptr; @@ -40,7 +40,7 @@ namespace l::ui { class UIMove : public UIVisitor { public: virtual bool Active(UIContainer& container, const InputState& input); - virtual bool Visit(UIContainer& container, const InputState& input, const ContainerArea& parent); + virtual bool Visit(UIContainer& container, const InputState& input); protected: bool mMoving = false; UIContainer* mSourceContainer = nullptr; @@ -48,7 +48,7 @@ namespace l::ui { class UIResize : public UIVisitor { public: - virtual bool Visit(UIContainer& container, const InputState& input, const ContainerArea& parent); + virtual bool Visit(UIContainer& container, const InputState& input); protected: bool mResizing = false; float mResizeAreaSize = 8.0f; @@ -60,7 +60,7 @@ namespace l::ui { UIDraw(ImDrawList* drawList) : mDrawList(drawList) {} ~UIDraw() = default; - virtual bool Visit(UIContainer& container, const InputState& input, const ContainerArea& parent); + virtual bool Visit(UIContainer& container, const InputState& input); protected: ImDrawList* mDrawList; }; @@ -72,7 +72,7 @@ namespace l::ui { UILinkIO(UICreator* creator = nullptr) : mCreator(creator) {} ~UILinkIO() = default; - virtual bool Visit(UIContainer& container, const InputState& input, const ContainerArea& parent); + virtual bool Visit(UIContainer& container, const InputState& input); protected: bool mDragging = false; bool mPossibleLinkImminent = false; diff --git a/packages/rendering/source/common/ui/UIContainer.cpp b/packages/rendering/source/common/ui/UIContainer.cpp index 17006c59..b945899e 100644 --- a/packages/rendering/source/common/ui/UIContainer.cpp +++ b/packages/rendering/source/common/ui/UIContainer.cpp @@ -216,7 +216,7 @@ namespace l::ui { case UILayoutH::Scaled: break; case UILayoutH::Parent: - mArea.mSize.x = contentArea.GetLocalSize().x; + mArea.mSize.x = GetLayoutArea().GetLocalSize().x; break; } switch (layout.mLayoutV) { @@ -231,7 +231,7 @@ namespace l::ui { } if (mode == UITraversalMode::BFS && visitor.Active(*this, input)) { - if (visitor.Visit(*this, input, contentArea)) { + if (visitor.Visit(*this, input)) { return true; } } @@ -259,7 +259,7 @@ namespace l::ui { } if (mode == UITraversalMode::DFS && visitor.Active(*this, input)) { - return visitor.Visit(*this, input, contentArea); + return visitor.Visit(*this, input); } return false; } @@ -315,7 +315,7 @@ namespace l::ui { } if (mode == UITraversalMode::BFS && visitor.Active(*this, input)) { - if (visitor.Visit(*this, input, contentArea)) { + if (visitor.Visit(*this, input)) { return true; } } @@ -356,7 +356,7 @@ namespace l::ui { } if ((mode == UITraversalMode::DFS) && visitor.Active(*this, input)) { - return visitor.Visit(*this, input, contentArea); + return visitor.Visit(*this, input); } return false; } diff --git a/packages/rendering/source/common/ui/UIVisitors.cpp b/packages/rendering/source/common/ui/UIVisitors.cpp index 77d200dd..c91d32f7 100644 --- a/packages/rendering/source/common/ui/UIVisitors.cpp +++ b/packages/rendering/source/common/ui/UIVisitors.cpp @@ -10,7 +10,7 @@ namespace l::ui { return input.mScroll != 0; } - bool UIZoom::Visit(UIContainer& container, const InputState& input, const ContainerArea& contentArea) { + bool UIZoom::Visit(UIContainer& container, const InputState& input) { if (!container.HasConfigFlag(UIContainer_ZoomFlag)) { return false; } @@ -25,6 +25,8 @@ namespace l::ui { return true; } + auto& contentArea = container.GetLayoutArea(); + ImVec2 mousePos = input.GetLocalPos(); ImVec2 localMousePos = ImVec2(contentArea.mPosition.x - mousePos.x, contentArea.mPosition.y - mousePos.y); ImVec2 p = container.GetPosition(); @@ -41,7 +43,7 @@ namespace l::ui { return (input.mStarted && !mDragging) || mDragging; } - bool UIDrag::Visit(UIContainer& container, const InputState& input, const ContainerArea& contentArea) { + bool UIDrag::Visit(UIContainer& container, const InputState& input) { if (!container.HasConfigFlag(UIContainer_DragFlag)) { return false; } @@ -52,6 +54,8 @@ namespace l::ui { } } if (mDragging && mSourceContainer == &container) { + auto& contentArea = container.GetLayoutArea(); + ImVec2 move = DragMovement(input.mPrevPos, input.mCurPos, contentArea.mScale * container.GetScale()); container.Move(move); container.Notification(UIContainer_DragFlag); @@ -69,17 +73,19 @@ namespace l::ui { return (input.mStarted && !mMoving) || mMoving; } - bool UIMove::Visit(UIContainer& container, const InputState& input, const ContainerArea& contentArea) { + bool UIMove::Visit(UIContainer& container, const InputState& input) { if(!container.HasConfigFlag(UIContainer_MoveFlag)){ return false; } if (input.mStarted && !mMoving) { + auto& contentArea = container.GetLayoutArea(); if (Overlap(input.GetLocalPos(), container.GetPosition(), container.GetPositionAtSize(), contentArea)) { mMoving = true; mSourceContainer = &container; } } if (mMoving && mSourceContainer == &container) { + auto& contentArea = container.GetLayoutArea(); ImVec2 move = DragMovement(input.mPrevPos, input.mCurPos, contentArea.mScale); container.Move(move); container.Notification(UIContainer_MoveFlag); @@ -93,13 +99,14 @@ namespace l::ui { return false; } - bool UIResize::Visit(UIContainer& container, const InputState& input, const ContainerArea& contentArea) { + bool UIResize::Visit(UIContainer& container, const InputState& input) { if (!container.HasConfigFlag(UIContainer_ResizeFlag)) { return false; } if (!mResizing) { const float radii = mResizeAreaSize * 0.5f; ImVec2 p = container.GetPositionAtSize(); + auto& contentArea = container.GetLayoutArea(); if (OverlapScreenRect(input.GetLocalPos(), p, ImVec2(radii, radii), contentArea)) { mSourceContainer = &container; container.Notification(UIContainer_ResizeFlag); @@ -117,6 +124,7 @@ namespace l::ui { } } if (mResizing && mSourceContainer == &container) { + auto& contentArea = container.GetLayoutArea(); ImVec2 move = DragMovement(input.mPrevPos, input.mCurPos, contentArea.mScale); container.Resize(move); @@ -130,11 +138,13 @@ namespace l::ui { return false; } - bool UIDraw::Visit(UIContainer& container, const InputState& input, const ContainerArea& contentArea) { + bool UIDraw::Visit(UIContainer& container, const InputState& input) { if (!mDebug && !container.HasConfigFlag(UIContainer_DrawFlag)) { return false; } + auto& contentArea = container.GetLayoutArea(); + float splineThickness = 2.0f; ImU32 color = container.GetRenderData().mColor; ImVec2 pTopLeft = container.GetPosition(); @@ -253,11 +263,13 @@ namespace l::ui { return container.HasConfigFlag(UIContainer_InputFlag) || container.HasConfigFlag(UIContainer_OutputFlag) || container.HasConfigFlag(UIContainer_LinkFlag); } - bool UILinkIO::Visit(UIContainer& container, const InputState& input, const ContainerArea& contentArea) { + bool UILinkIO::Visit(UIContainer& container, const InputState& input) { // Create link at from a clicked output container if (container.HasConfigFlag(UIContainer_OutputFlag) && !mDragging && input.mStarted && mLinkContainer.get() == nullptr) { ImVec2 pCenter = container.GetPosition(); ImVec2 size = container.GetSize(); + auto& contentArea = container.GetLayoutArea(); + ImVec2 pT = contentArea.Transform(pCenter, input.mRootPos); if (OverlapCircle(input.mCurPos, pT, size.x * contentArea.mScale)) { mDragging = true; @@ -279,6 +291,8 @@ namespace l::ui { if (mDragging && mLinkContainer.get() != nullptr && container.HasConfigFlag(UIContainer_LinkFlag) && mLinkContainer.get() == &container) { // On the newly created link container, drag the end point along the mouse movement + auto& contentArea = container.GetLayoutArea(); + ImVec2 move = DragMovement(input.mPrevPos, input.mCurPos, contentArea.mScale * container.GetScale()); mLinkContainer->Move(move); } @@ -286,6 +300,8 @@ namespace l::ui { if (mDragging && mLinkContainer.get() != nullptr && container.HasConfigFlag(UIContainer_InputFlag)) { ImVec2 pCenter = container.GetPosition(); ImVec2 size = container.GetSize(); + auto& contentArea = container.GetLayoutArea(); + ImVec2 pT = contentArea.Transform(pCenter, input.mRootPos); if (OverlapCircle(input.mCurPos, pT, size.x * contentArea.mScale)) { From dcd76be971305de2715e87cab80546ff0c077fd2 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Sat, 24 Aug 2024 07:00:45 +0200 Subject: [PATCH 052/125] Also remove the container area for the tree search since it is now in the containers as well. --- .../include/rendering/ui/UIContainer.h | 8 ++-- .../source/common/ui/UIContainer.cpp | 46 ++++++++----------- .../rendering/source/common/ui/UIVisitors.cpp | 2 +- 3 files changed, 23 insertions(+), 33 deletions(-) diff --git a/packages/rendering/include/rendering/ui/UIContainer.h b/packages/rendering/include/rendering/ui/UIContainer.h index 972328c1..3e952c7f 100644 --- a/packages/rendering/include/rendering/ui/UIContainer.h +++ b/packages/rendering/include/rendering/ui/UIContainer.h @@ -265,11 +265,11 @@ namespace l::ui { mArea.mLayout.mAlignV = alignV; mArea.mLayout.mLayoutH = layoutH; mArea.mLayout.mLayoutV = layoutV; + mAreaT.mBorder = 0.0f; } virtual ~UIContainer() = default; - bool Accept(UIVisitor& visitor, const InputState& input, UITraversalMode mode = UITraversalMode::BFS); - virtual bool Accept(UIVisitor& visitor, const InputState& input, const ContainerArea& contentArea, UITraversalMode mode = UITraversalMode::BFS); + virtual bool Accept(UIVisitor& visitor, const InputState& input, UITraversalMode mode = UITraversalMode::BFS); virtual void Add(UIContainer* container, int32_t i = -1); template @@ -300,6 +300,7 @@ namespace l::ui { void SetScale(float scale); void SetPosition(ImVec2 p); void SetSize(ImVec2 s); + void SetLayoutSize(ImVec2 s); void SetDisplayName(std::string_view id); void SetId(std::string_view id); void SetContainerArea(const ContainerArea& area); @@ -346,7 +347,6 @@ namespace l::ui { UIContainer* mParent = nullptr; UIContainer* mCoParent = nullptr; // when a container is influenced by two parent in a specific way defined by the type of container and the visitor std::vector mContent; - std::vector mContentAreas; }; enum class UISplitMode { @@ -368,7 +368,7 @@ namespace l::ui { } ~UISplit() = default; - virtual bool Accept(UIVisitor& visitor, const InputState& input, const ContainerArea& contentArea, UITraversalMode mode); + virtual bool Accept(UIVisitor& visitor, const InputState& input, UITraversalMode mode); protected: UISplitMode mSplitMode; }; diff --git a/packages/rendering/source/common/ui/UIContainer.cpp b/packages/rendering/source/common/ui/UIContainer.cpp index b945899e..9ed92e8a 100644 --- a/packages/rendering/source/common/ui/UIContainer.cpp +++ b/packages/rendering/source/common/ui/UIContainer.cpp @@ -123,6 +123,10 @@ namespace l::ui { mArea.mSize = s; } + void UIContainer::SetLayoutSize(ImVec2 s) { + mAreaT.mSize = s; + } + void UIContainer::SetDisplayName(std::string_view displayName) { mDisplayName = displayName; } @@ -200,14 +204,6 @@ namespace l::ui { } bool UIContainer::Accept(UIVisitor& visitor, const InputState& input, UITraversalMode mode) { - ContainerArea current; - if (visitor.Active(*this, input)) { - return Accept(visitor, input, current, mode); - } - return false; - } - - bool UIContainer::Accept(UIVisitor& visitor, const InputState& input, const ContainerArea& contentArea, UITraversalMode mode) { if (visitor.ShouldUpdateContainer()) { auto& layout = GetContainerArea().mLayout; switch (layout.mLayoutH) { @@ -225,7 +221,7 @@ namespace l::ui { case UILayoutV::Scaled: break; case UILayoutV::Parent: - mArea.mSize.y = contentArea.GetLocalSize().y; + mArea.mSize.y = GetLayoutArea().GetLocalSize().y; break; } } @@ -242,17 +238,13 @@ namespace l::ui { auto contentSize = content->GetSize(); auto& contentLayout = content->GetContainerArea().mLayout; ContainerArea current; - current.mScale = mArea.GetWorldScale(contentArea.mScale); - current.mSize = mArea.GetWorldSizeLayout(contentArea.mScale); - current.mPosition = mArea.GetWorldPosLayout(contentArea.mScale, contentArea.mPosition, contentSize, contentLayout.mAlignH, contentLayout.mAlignV); - - mContentAreas.resize(mContent.size()); - mContentAreas.at(i) = current; - + current.mScale = mArea.GetWorldScale(GetLayoutArea().mScale); + current.mSize = mArea.GetWorldSizeLayout(GetLayoutArea().mScale); + current.mPosition = mArea.GetWorldPosLayout(GetLayoutArea().mScale, GetLayoutArea().mPosition, contentSize, contentLayout.mAlignH, contentLayout.mAlignV); content->SetLayoutArea(current); } - if (content->Accept(visitor, input, mContentAreas.at(i), mode)) { + if (content->Accept(visitor, input, mode)) { return true; } i++; @@ -264,7 +256,7 @@ namespace l::ui { return false; } - bool UISplit::Accept(UIVisitor& visitor, const InputState& input, const ContainerArea& contentArea, UITraversalMode mode) { + bool UISplit::Accept(UIVisitor& visitor, const InputState& input, UITraversalMode mode) { // Since we can have multiple layouts in a container for different content, it will act as // an anchor rather than a container, therefore it has to align within it and size @@ -278,7 +270,7 @@ namespace l::ui { case UILayoutH::Scaled: break; case UILayoutH::Parent: - mArea.mSize.x = contentArea.GetLocalSize().x; + mArea.mSize.x = GetLayoutArea().GetLocalSize().x; break; } switch (layout.mLayoutV) { @@ -287,14 +279,14 @@ namespace l::ui { case UILayoutV::Scaled: break; case UILayoutV::Parent: - mArea.mSize.y = contentArea.GetLocalSize().y; + mArea.mSize.y = GetLayoutArea().GetLocalSize().y; break; } float contentCount = static_cast(mContent.size()); - current.mScale = mArea.GetWorldScale(contentArea.mScale); - current.mSize = mArea.GetWorldSize(contentArea.mScale); - current.mPosition = mArea.GetWorldPos(contentArea.mScale, contentArea.mPosition); + current.mScale = mArea.GetWorldScale(GetLayoutArea().mScale); + current.mSize = mArea.GetWorldSize(GetLayoutArea().mScale); + current.mPosition = mArea.GetWorldPos(GetLayoutArea().mScale, GetLayoutArea().mPosition); switch (mSplitMode) { case UISplitMode::EqualSplitH: @@ -323,12 +315,10 @@ namespace l::ui { size_t i = 0; for (auto& content : mContent) { if (visitor.ShouldUpdateContainer()) { - mContentAreas.resize(mContent.size()); - mContentAreas.at(i) = current; content->SetLayoutArea(current); } - if (content->Accept(visitor, input, mContentAreas.at(i), mode)) { + if (content->Accept(visitor, input, mode)) { return true; } @@ -341,10 +331,10 @@ namespace l::ui { current.mPosition.y += current.mSize.y; break; case UISplitMode::AppendH: - current.mPosition.x += content->GetSize().x * contentArea.mScale; + current.mPosition.x += content->GetSize().x * GetLayoutArea().mScale; break; case UISplitMode::AppendV: - current.mPosition.y += content->GetSize().y * contentArea.mScale; + current.mPosition.y += content->GetSize().y * GetLayoutArea().mScale; break; case UISplitMode::EqualResizeH: break; diff --git a/packages/rendering/source/common/ui/UIVisitors.cpp b/packages/rendering/source/common/ui/UIVisitors.cpp index c91d32f7..9bd9f4c5 100644 --- a/packages/rendering/source/common/ui/UIVisitors.cpp +++ b/packages/rendering/source/common/ui/UIVisitors.cpp @@ -206,7 +206,7 @@ namespace l::ui { } } else { - float d12 = sqrt(0.25f * ((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y))); + float d12 = sqrt(0.25f * ((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y)) / (contentArea.mScale * contentArea.mScale)); p11 = contentArea.Transform(ImVec2(pTopLeft.x + d12, pTopLeft.y), input.mRootPos); p22 = contentArea.Transform(ImVec2(pLowRight.x - d12, pLowRight.y), input.mRootPos); } From 9c84f840eaf48e0a01b97d7faa71be73fe00c932 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Sat, 24 Aug 2024 08:08:33 +0200 Subject: [PATCH 053/125] Rename. --- .../include/rendering/ui/UIContainer.h | 30 +++--- .../source/common/ui/UIContainer.cpp | 92 +++++++++---------- .../rendering/source/common/ui/UIVisitors.cpp | 90 +++++++++--------- 3 files changed, 106 insertions(+), 106 deletions(-) diff --git a/packages/rendering/include/rendering/ui/UIContainer.h b/packages/rendering/include/rendering/ui/UIContainer.h index 3e952c7f..831b7a04 100644 --- a/packages/rendering/include/rendering/ui/UIContainer.h +++ b/packages/rendering/include/rendering/ui/UIContainer.h @@ -259,13 +259,13 @@ namespace l::ui { class UIContainer { public: UIContainer(uint32_t flags = 0, UIRenderType renderType = UIRenderType::Rect, UIAlignH alignH = UIAlignH::Left, UIAlignV alignV = UIAlignV::Top, UILayoutH layoutH = UILayoutH::Fixed, UILayoutV layoutV = UILayoutV::Fixed) : mConfigFlags(flags) { - mArea.mRender.mType = renderType; - mArea.mBorder = 3.0f; - mArea.mLayout.mAlignH = alignH; - mArea.mLayout.mAlignV = alignV; - mArea.mLayout.mLayoutH = layoutH; - mArea.mLayout.mLayoutV = layoutV; - mAreaT.mBorder = 0.0f; + mDisplayArea.mRender.mType = renderType; + mDisplayArea.mBorder = 3.0f; + mDisplayArea.mLayout.mAlignH = alignH; + mDisplayArea.mLayout.mAlignV = alignV; + mDisplayArea.mLayout.mLayoutH = layoutH; + mDisplayArea.mLayout.mLayoutV = layoutV; + mLayoutArea.mBorder = 0.0f; } virtual ~UIContainer() = default; @@ -320,11 +320,11 @@ namespace l::ui { void DebugLog(); const UIRenderData& GetRenderData() const { - return mArea.mRender; + return mDisplayArea.mRender; } const UILayoutData& GetLayoutData() const { - return mArea.mLayout; + return mDisplayArea.mLayout; } std::string_view GetDisplayName() { @@ -338,11 +338,11 @@ namespace l::ui { protected: std::string mId; std::string mDisplayName; - ContainerArea mArea; uint32_t mConfigFlags = 0; // Active visitor flags uint32_t mNotificationFlags = 0; // Notification flags for ux feedback (resizing box animation etc) - ContainerArea mAreaT; + ContainerArea mDisplayArea; + ContainerArea mLayoutArea; UIContainer* mParent = nullptr; UIContainer* mCoParent = nullptr; // when a container is influenced by two parent in a specific way defined by the type of container and the visitor @@ -361,10 +361,10 @@ namespace l::ui { class UISplit : public UIContainer { public: UISplit(uint32_t flags, UISplitMode splitMode, UILayoutH layoutH, UILayoutV layoutV) : UIContainer(flags), mSplitMode(splitMode) { - mArea.mRender.mType = UIRenderType::Rect; - mArea.mBorder = 3.0f; - mArea.mLayout.mLayoutH = layoutH; - mArea.mLayout.mLayoutV = layoutV; + mDisplayArea.mRender.mType = UIRenderType::Rect; + mDisplayArea.mBorder = 3.0f; + mDisplayArea.mLayout.mLayoutH = layoutH; + mDisplayArea.mLayout.mLayoutV = layoutV; } ~UISplit() = default; diff --git a/packages/rendering/source/common/ui/UIContainer.cpp b/packages/rendering/source/common/ui/UIContainer.cpp index 9ed92e8a..efffaeb8 100644 --- a/packages/rendering/source/common/ui/UIContainer.cpp +++ b/packages/rendering/source/common/ui/UIContainer.cpp @@ -37,20 +37,20 @@ namespace l::ui { return pMin.x < p.x && pMin.y < p.y && pMax.x > p.x && pMax.y > p.y; } - bool Overlap(const ImVec2& p, const ImVec2& pMin, const ImVec2& pMax, const ContainerArea& contentArea) { - ImVec2 pMinT = contentArea.Transform(pMin); - ImVec2 pMaxT = contentArea.Transform(pMax); + bool Overlap(const ImVec2& p, const ImVec2& pMin, const ImVec2& pMax, const ContainerArea& area) { + ImVec2 pMinT = area.Transform(pMin); + ImVec2 pMaxT = area.Transform(pMax); return pMinT.x < p.x && pMinT.y < p.y && pMaxT.x > p.x && pMaxT.y > p.y; } - bool OverlapScreenRect(const ImVec2& p, const ImVec2& pCenter, const ImVec2& screenOffset, const ContainerArea& contentArea) { - ImVec2 pMinT = contentArea.Transform(pCenter, ImVec2(-screenOffset.x, -screenOffset.y)); - ImVec2 pMaxT = contentArea.Transform(pCenter, ImVec2(screenOffset.x, screenOffset.y)); + bool OverlapScreenRect(const ImVec2& p, const ImVec2& pCenter, const ImVec2& screenOffset, const ContainerArea& area) { + ImVec2 pMinT = area.Transform(pCenter, ImVec2(-screenOffset.x, -screenOffset.y)); + ImVec2 pMaxT = area.Transform(pCenter, ImVec2(screenOffset.x, screenOffset.y)); return pMinT.x < p.x && pMinT.y < p.y && pMaxT.x > p.x && pMaxT.y > p.y; } - bool OverlapScreenCircle(const ImVec2& p, const ImVec2& pCenter, float radii, const ContainerArea& contentArea) { - ImVec2 pT = contentArea.Transform(pCenter); + bool OverlapScreenCircle(const ImVec2& p, const ImVec2& pCenter, float radii, const ContainerArea& area) { + ImVec2 pT = area.Transform(pCenter); ImVec2 d = ImVec2(pT.x - p.x, pT.y - p.y); return d.x * d.x + d.y * d.y < radii * radii; } @@ -78,21 +78,21 @@ namespace l::ui { } void UIContainer::Move(ImVec2 localChange) { - mArea.mPosition.x += localChange.x; - mArea.mPosition.y += localChange.y; + mDisplayArea.mPosition.x += localChange.x; + mDisplayArea.mPosition.y += localChange.y; } void UIContainer::Resize(ImVec2 localChange) { - mArea.mSize.x += localChange.x; - mArea.mSize.y += localChange.y; - if (mArea.mRender.mType != UIRenderType::Spline) { - mArea.mSize.x = mArea.mSize.x < 1.0f ? 1.0f : mArea.mSize.x; - mArea.mSize.y = mArea.mSize.y < 1.0f ? 1.0f : mArea.mSize.y; + mDisplayArea.mSize.x += localChange.x; + mDisplayArea.mSize.y += localChange.y; + if (mDisplayArea.mRender.mType != UIRenderType::Spline) { + mDisplayArea.mSize.x = mDisplayArea.mSize.x < 1.0f ? 1.0f : mDisplayArea.mSize.x; + mDisplayArea.mSize.y = mDisplayArea.mSize.y < 1.0f ? 1.0f : mDisplayArea.mSize.y; } } void UIContainer::Rescale(float localChange) { - mArea.mScale *= localChange; + mDisplayArea.mScale *= localChange; } void UIContainer::ClearNotifications() { @@ -112,19 +112,19 @@ namespace l::ui { } void UIContainer::SetScale(float scale) { - mArea.mScale = scale; + mDisplayArea.mScale = scale; } void UIContainer::SetPosition(ImVec2 p) { - mArea.mPosition = p; + mDisplayArea.mPosition = p; } void UIContainer::SetSize(ImVec2 s) { - mArea.mSize = s; + mDisplayArea.mSize = s; } void UIContainer::SetLayoutSize(ImVec2 s) { - mAreaT.mSize = s; + mLayoutArea.mSize = s; } void UIContainer::SetDisplayName(std::string_view displayName) { @@ -136,11 +136,11 @@ namespace l::ui { } void UIContainer::SetContainerArea(const ContainerArea& area) { - mArea = area; + mDisplayArea = area; } void UIContainer::SetLayoutArea(const ContainerArea& transformedLayoutArea) { - mAreaT = transformedLayoutArea; + mLayoutArea = transformedLayoutArea; } UIContainer* UIContainer::GetParent() { @@ -161,46 +161,46 @@ namespace l::ui { ImVec2 UIContainer::GetPosition(bool untransformed) { if (untransformed) { - return mArea.mPosition; + return mDisplayArea.mPosition; } - return ImVec2(mArea.mPosition.x * mArea.mScale, mArea.mPosition.y * mArea.mScale); + return ImVec2(mDisplayArea.mPosition.x * mDisplayArea.mScale, mDisplayArea.mPosition.y * mDisplayArea.mScale); } ImVec2 UIContainer::GetPositionAtCenter(ImVec2 offset, bool untransformed) { if (untransformed) { - return ImVec2(mArea.mPosition.x + mArea.mSize.x * 0.5f + offset.x, mArea.mPosition.y * 0.5f + mArea.mSize.y + offset.y); + return ImVec2(mDisplayArea.mPosition.x + mDisplayArea.mSize.x * 0.5f + offset.x, mDisplayArea.mPosition.y * 0.5f + mDisplayArea.mSize.y + offset.y); } - return ImVec2((mArea.mPosition.x + mArea.mSize.x * 0.5f + offset.x) * mArea.mScale, (mArea.mPosition.y * 0.5f + mArea.mSize.y + offset.y) * mArea.mScale); + return ImVec2((mDisplayArea.mPosition.x + mDisplayArea.mSize.x * 0.5f + offset.x) * mDisplayArea.mScale, (mDisplayArea.mPosition.y * 0.5f + mDisplayArea.mSize.y + offset.y) * mDisplayArea.mScale); } ImVec2 UIContainer::GetPositionAtSize(ImVec2 offset, bool untransformed) { if (untransformed) { - return ImVec2(mArea.mPosition.x + mArea.mSize.x + offset.x, mArea.mPosition.y + mArea.mSize.y + offset.y); + return ImVec2(mDisplayArea.mPosition.x + mDisplayArea.mSize.x + offset.x, mDisplayArea.mPosition.y + mDisplayArea.mSize.y + offset.y); } - return ImVec2((mArea.mPosition.x + mArea.mSize.x + offset.x) * mArea.mScale, (mArea.mPosition.y + mArea.mSize.y + offset.y) * mArea.mScale); + return ImVec2((mDisplayArea.mPosition.x + mDisplayArea.mSize.x + offset.x) * mDisplayArea.mScale, (mDisplayArea.mPosition.y + mDisplayArea.mSize.y + offset.y) * mDisplayArea.mScale); } ImVec2 UIContainer::GetSize(bool untransformed) { if (untransformed) { - return mArea.mSize; + return mDisplayArea.mSize; } - return ImVec2(mArea.mSize.x * mArea.mScale, mArea.mSize.y * mArea.mScale); + return ImVec2(mDisplayArea.mSize.x * mDisplayArea.mScale, mDisplayArea.mSize.y * mDisplayArea.mScale); } float UIContainer::GetScale() { - return mArea.mScale; + return mDisplayArea.mScale; } ContainerArea& UIContainer::GetContainerArea() { - return mArea; + return mDisplayArea; } const ContainerArea& UIContainer::GetLayoutArea() const { - return mAreaT; + return mLayoutArea; } void UIContainer::DebugLog() { - LOG(LogDebug) << "UIContainer: " << mDisplayName << ", [" << mArea.mScale << "][" << mArea.mPosition.x << ", " << mArea.mPosition.y << "][" << mArea.mSize.x << ", " << mArea.mSize.y << "]"; + LOG(LogDebug) << "UIContainer: " << mDisplayName << ", [" << mDisplayArea.mScale << "][" << mDisplayArea.mPosition.x << ", " << mDisplayArea.mPosition.y << "][" << mDisplayArea.mSize.x << ", " << mDisplayArea.mSize.y << "]"; } bool UIContainer::Accept(UIVisitor& visitor, const InputState& input, UITraversalMode mode) { @@ -212,7 +212,7 @@ namespace l::ui { case UILayoutH::Scaled: break; case UILayoutH::Parent: - mArea.mSize.x = GetLayoutArea().GetLocalSize().x; + mDisplayArea.mSize.x = mLayoutArea.GetLocalSize().x; break; } switch (layout.mLayoutV) { @@ -221,7 +221,7 @@ namespace l::ui { case UILayoutV::Scaled: break; case UILayoutV::Parent: - mArea.mSize.y = GetLayoutArea().GetLocalSize().y; + mDisplayArea.mSize.y = mLayoutArea.GetLocalSize().y; break; } } @@ -238,9 +238,9 @@ namespace l::ui { auto contentSize = content->GetSize(); auto& contentLayout = content->GetContainerArea().mLayout; ContainerArea current; - current.mScale = mArea.GetWorldScale(GetLayoutArea().mScale); - current.mSize = mArea.GetWorldSizeLayout(GetLayoutArea().mScale); - current.mPosition = mArea.GetWorldPosLayout(GetLayoutArea().mScale, GetLayoutArea().mPosition, contentSize, contentLayout.mAlignH, contentLayout.mAlignV); + current.mScale = mDisplayArea.GetWorldScale(mLayoutArea.mScale); + current.mSize = mDisplayArea.GetWorldSizeLayout(mLayoutArea.mScale); + current.mPosition = mDisplayArea.GetWorldPosLayout(mLayoutArea.mScale, mLayoutArea.mPosition, contentSize, contentLayout.mAlignH, contentLayout.mAlignV); content->SetLayoutArea(current); } @@ -270,7 +270,7 @@ namespace l::ui { case UILayoutH::Scaled: break; case UILayoutH::Parent: - mArea.mSize.x = GetLayoutArea().GetLocalSize().x; + mDisplayArea.mSize.x = mLayoutArea.GetLocalSize().x; break; } switch (layout.mLayoutV) { @@ -279,14 +279,14 @@ namespace l::ui { case UILayoutV::Scaled: break; case UILayoutV::Parent: - mArea.mSize.y = GetLayoutArea().GetLocalSize().y; + mDisplayArea.mSize.y = mLayoutArea.GetLocalSize().y; break; } float contentCount = static_cast(mContent.size()); - current.mScale = mArea.GetWorldScale(GetLayoutArea().mScale); - current.mSize = mArea.GetWorldSize(GetLayoutArea().mScale); - current.mPosition = mArea.GetWorldPos(GetLayoutArea().mScale, GetLayoutArea().mPosition); + current.mScale = mDisplayArea.GetWorldScale(mLayoutArea.mScale); + current.mSize = mDisplayArea.GetWorldSize(mLayoutArea.mScale); + current.mPosition = mDisplayArea.GetWorldPos(mLayoutArea.mScale, mLayoutArea.mPosition); switch (mSplitMode) { case UISplitMode::EqualSplitH: @@ -331,10 +331,10 @@ namespace l::ui { current.mPosition.y += current.mSize.y; break; case UISplitMode::AppendH: - current.mPosition.x += content->GetSize().x * GetLayoutArea().mScale; + current.mPosition.x += content->GetSize().x * mLayoutArea.mScale; break; case UISplitMode::AppendV: - current.mPosition.y += content->GetSize().y * GetLayoutArea().mScale; + current.mPosition.y += content->GetSize().y * mLayoutArea.mScale; break; case UISplitMode::EqualResizeH: break; diff --git a/packages/rendering/source/common/ui/UIVisitors.cpp b/packages/rendering/source/common/ui/UIVisitors.cpp index 9bd9f4c5..812f17ca 100644 --- a/packages/rendering/source/common/ui/UIVisitors.cpp +++ b/packages/rendering/source/common/ui/UIVisitors.cpp @@ -25,10 +25,10 @@ namespace l::ui { return true; } - auto& contentArea = container.GetLayoutArea(); + auto& layoutArea = container.GetLayoutArea(); ImVec2 mousePos = input.GetLocalPos(); - ImVec2 localMousePos = ImVec2(contentArea.mPosition.x - mousePos.x, contentArea.mPosition.y - mousePos.y); + ImVec2 localMousePos = ImVec2(layoutArea.mPosition.x - mousePos.x, layoutArea.mPosition.y - mousePos.y); ImVec2 p = container.GetPosition(); container.Rescale(scaleChange); p.x = ((p.x + localMousePos.x) * scaleChange - localMousePos.x) / container.GetScale(); @@ -54,9 +54,9 @@ namespace l::ui { } } if (mDragging && mSourceContainer == &container) { - auto& contentArea = container.GetLayoutArea(); + auto& layoutArea = container.GetLayoutArea(); - ImVec2 move = DragMovement(input.mPrevPos, input.mCurPos, contentArea.mScale * container.GetScale()); + ImVec2 move = DragMovement(input.mPrevPos, input.mCurPos, layoutArea.mScale * container.GetScale()); container.Move(move); container.Notification(UIContainer_DragFlag); @@ -78,15 +78,15 @@ namespace l::ui { return false; } if (input.mStarted && !mMoving) { - auto& contentArea = container.GetLayoutArea(); - if (Overlap(input.GetLocalPos(), container.GetPosition(), container.GetPositionAtSize(), contentArea)) { + auto& layoutArea = container.GetLayoutArea(); + if (Overlap(input.GetLocalPos(), container.GetPosition(), container.GetPositionAtSize(), layoutArea)) { mMoving = true; mSourceContainer = &container; } } if (mMoving && mSourceContainer == &container) { - auto& contentArea = container.GetLayoutArea(); - ImVec2 move = DragMovement(input.mPrevPos, input.mCurPos, contentArea.mScale); + auto& layoutArea = container.GetLayoutArea(); + ImVec2 move = DragMovement(input.mPrevPos, input.mCurPos, layoutArea.mScale); container.Move(move); container.Notification(UIContainer_MoveFlag); @@ -106,8 +106,8 @@ namespace l::ui { if (!mResizing) { const float radii = mResizeAreaSize * 0.5f; ImVec2 p = container.GetPositionAtSize(); - auto& contentArea = container.GetLayoutArea(); - if (OverlapScreenRect(input.GetLocalPos(), p, ImVec2(radii, radii), contentArea)) { + auto& layoutArea = container.GetLayoutArea(); + if (OverlapScreenRect(input.GetLocalPos(), p, ImVec2(radii, radii), layoutArea)) { mSourceContainer = &container; container.Notification(UIContainer_ResizeFlag); @@ -124,8 +124,8 @@ namespace l::ui { } } if (mResizing && mSourceContainer == &container) { - auto& contentArea = container.GetLayoutArea(); - ImVec2 move = DragMovement(input.mPrevPos, input.mCurPos, contentArea.mScale); + auto& layoutArea = container.GetLayoutArea(); + ImVec2 move = DragMovement(input.mPrevPos, input.mCurPos, layoutArea.mScale); container.Resize(move); if (input.mStopped) { @@ -143,7 +143,7 @@ namespace l::ui { return false; } - auto& contentArea = container.GetLayoutArea(); + auto& layoutArea = container.GetLayoutArea(); float splineThickness = 2.0f; ImU32 color = container.GetRenderData().mColor; @@ -151,11 +151,11 @@ namespace l::ui { ImVec2 pCenter = container.GetPositionAtCenter(); ImVec2 pLowRight = container.GetPositionAtSize(); ImVec2 pSize = container.GetSize(); - pSize.x *= contentArea.mScale; - pSize.y *= contentArea.mScale; - ImVec2 p1 = contentArea.Transform(pTopLeft, input.mRootPos); - ImVec2 p12 = contentArea.Transform(pCenter, input.mRootPos); - ImVec2 p2 = contentArea.Transform(pLowRight, input.mRootPos); + pSize.x *= layoutArea.mScale; + pSize.y *= layoutArea.mScale; + ImVec2 p1 = layoutArea.Transform(pTopLeft, input.mRootPos); + ImVec2 p12 = layoutArea.Transform(pCenter, input.mRootPos); + ImVec2 p2 = layoutArea.Transform(pLowRight, input.mRootPos); ImVec2 p11; ImVec2 p22; @@ -164,7 +164,7 @@ namespace l::ui { switch (container.GetRenderData().mType) { case l::ui::UIRenderType::Rect: - mDrawList->AddRect(p1, p2, color, 5.0f, ImDrawFlags_RoundCornersAll, 1.0f * container.GetScale() * contentArea.mScale); + mDrawList->AddRect(p1, p2, color, 5.0f, ImDrawFlags_RoundCornersAll, 1.0f * container.GetScale() * layoutArea.mScale); break; case l::ui::UIRenderType::RectFilled: mDrawList->AddRectFilled(p1, p2, color, 5.0f, ImDrawFlags_RoundCornersAll); @@ -174,7 +174,7 @@ namespace l::ui { case l::ui::UIRenderType::TriangleFilled: break; case l::ui::UIRenderType::Circle: - mDrawList->AddCircle(p12, pSize.x, color, 15, 2.0f * container.GetScale() * contentArea.mScale); + mDrawList->AddCircle(p12, pSize.x, color, 15, 2.0f * container.GetScale() * layoutArea.mScale); break; case l::ui::UIRenderType::CircleFilled: mDrawList->AddCircleFilled(p12, pSize.x, color, 15); @@ -187,28 +187,28 @@ namespace l::ui { if (container.HasConfigFlag(UIContainer_LinkFlag)) { splineThickness = container.HasNotification(UIContainer_LinkFlag) ? 2.0f * splineThickness : splineThickness; ImVec2 pLinkInput = container.GetParent()->GetPosition(); - p1 = contentArea.Transform(ImVec2(), input.mRootPos); + p1 = layoutArea.Transform(ImVec2(), input.mRootPos); if (container.GetCoParent() != nullptr) { ImVec2 pLinkOutput = container.GetCoParent()->GetPosition(); - auto& coContentArea = container.GetCoParent()->GetLayoutArea(); - p2 = coContentArea.Transform(pLinkOutput, input.mRootPos); - float d12 = sqrt(0.25f*((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y)) / (contentArea.mScale * contentArea.mScale)); + auto& coLayoutArea = container.GetCoParent()->GetLayoutArea(); + p2 = coLayoutArea.Transform(pLinkOutput, input.mRootPos); + float d12 = sqrt(0.25f*((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y)) / (layoutArea.mScale * layoutArea.mScale)); - p11 = contentArea.Transform(ImVec2(pLinkInput.x + d12, pLinkInput.y), input.mRootPos); - p22 = coContentArea.Transform(ImVec2(pLinkOutput.x - d12, pLinkOutput.y), input.mRootPos); + p11 = layoutArea.Transform(ImVec2(pLinkInput.x + d12, pLinkInput.y), input.mRootPos); + p22 = coLayoutArea.Transform(ImVec2(pLinkOutput.x - d12, pLinkOutput.y), input.mRootPos); } else { p2 = input.mCurPos; - float d12 = sqrt(0.25f * ((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y)) / (contentArea.mScale * contentArea.mScale)); - p11 = contentArea.Transform(ImVec2(pLinkInput.x + d12, pLinkInput.y), input.mRootPos); - p22 = ImVec2(p2.x - d12 * contentArea.mScale, p2.y); + float d12 = sqrt(0.25f * ((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y)) / (layoutArea.mScale * layoutArea.mScale)); + p11 = layoutArea.Transform(ImVec2(pLinkInput.x + d12, pLinkInput.y), input.mRootPos); + p22 = ImVec2(p2.x - d12 * layoutArea.mScale, p2.y); } } else { - float d12 = sqrt(0.25f * ((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y)) / (contentArea.mScale * contentArea.mScale)); - p11 = contentArea.Transform(ImVec2(pTopLeft.x + d12, pTopLeft.y), input.mRootPos); - p22 = contentArea.Transform(ImVec2(pLowRight.x - d12, pLowRight.y), input.mRootPos); + float d12 = sqrt(0.25f * ((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y)) / (layoutArea.mScale * layoutArea.mScale)); + p11 = layoutArea.Transform(ImVec2(pTopLeft.x + d12, pTopLeft.y), input.mRootPos); + p22 = layoutArea.Transform(ImVec2(pLowRight.x - d12, pLowRight.y), input.mRootPos); } mDrawList->AddBezierCubic(p1, p11, p22, p2, color, splineThickness, 30); break; @@ -223,7 +223,7 @@ namespace l::ui { nameStart = container.GetDisplayName().data(); nameEnd = container.GetDisplayName().data() + container.GetDisplayName().size(); container.SetSize(ImGui::CalcTextSize(nameStart, nameEnd)); - mDrawList->AddText(ImGui::GetDefaultFont(), 13.0f * container.GetScale() * contentArea.mScale, p1, color, nameStart, nameEnd); + mDrawList->AddText(ImGui::GetDefaultFont(), 13.0f * container.GetScale() * layoutArea.mScale, p1, color, nameStart, nameEnd); } break; case l::ui::UIRenderType::Texture: @@ -243,11 +243,11 @@ namespace l::ui { case l::ui::UIRenderType::Texture: case l::ui::UIRenderType::Spline: if (container.HasConfigFlag(ui::UIContainer_ResizeFlag)) { - ImVec2 p3 = contentArea.Transform(pLowRight, ImVec2(input.mRootPos.x - 3.0f, input.mRootPos.y - 3.0f)); - ImVec2 p4 = contentArea.Transform(pLowRight, ImVec2(input.mRootPos.x + 3.0f, input.mRootPos.y + 3.0f)); + ImVec2 p3 = layoutArea.Transform(pLowRight, ImVec2(input.mRootPos.x - 3.0f, input.mRootPos.y - 3.0f)); + ImVec2 p4 = layoutArea.Transform(pLowRight, ImVec2(input.mRootPos.x + 3.0f, input.mRootPos.y + 3.0f)); if (container.HasNotification(ui::UIContainer_ResizeFlag)) { - p3 = contentArea.Transform(pLowRight, ImVec2(input.mRootPos.x - 5.0f, input.mRootPos.y - 5.0f)); - p4 = contentArea.Transform(pLowRight, ImVec2(input.mRootPos.x + 5.0f, input.mRootPos.y + 5.0f)); + p3 = layoutArea.Transform(pLowRight, ImVec2(input.mRootPos.x - 5.0f, input.mRootPos.y - 5.0f)); + p4 = layoutArea.Transform(pLowRight, ImVec2(input.mRootPos.x + 5.0f, input.mRootPos.y + 5.0f)); } mDrawList->AddRectFilled(p3, p4, color); } @@ -268,10 +268,10 @@ namespace l::ui { if (container.HasConfigFlag(UIContainer_OutputFlag) && !mDragging && input.mStarted && mLinkContainer.get() == nullptr) { ImVec2 pCenter = container.GetPosition(); ImVec2 size = container.GetSize(); - auto& contentArea = container.GetLayoutArea(); + auto& layoutArea = container.GetLayoutArea(); - ImVec2 pT = contentArea.Transform(pCenter, input.mRootPos); - if (OverlapCircle(input.mCurPos, pT, size.x * contentArea.mScale)) { + ImVec2 pT = layoutArea.Transform(pCenter, input.mRootPos); + if (OverlapCircle(input.mCurPos, pT, size.x * layoutArea.mScale)) { mDragging = true; mLinkContainer = mCreator->CreateContainer(UIContainer_LinkFlag | UIContainer_DrawFlag, UIRenderType::Spline); container.Add(mLinkContainer); @@ -291,20 +291,20 @@ namespace l::ui { if (mDragging && mLinkContainer.get() != nullptr && container.HasConfigFlag(UIContainer_LinkFlag) && mLinkContainer.get() == &container) { // On the newly created link container, drag the end point along the mouse movement - auto& contentArea = container.GetLayoutArea(); + auto& layoutArea = container.GetLayoutArea(); - ImVec2 move = DragMovement(input.mPrevPos, input.mCurPos, contentArea.mScale * container.GetScale()); + ImVec2 move = DragMovement(input.mPrevPos, input.mCurPos, layoutArea.mScale * container.GetScale()); mLinkContainer->Move(move); } if (mDragging && mLinkContainer.get() != nullptr && container.HasConfigFlag(UIContainer_InputFlag)) { ImVec2 pCenter = container.GetPosition(); ImVec2 size = container.GetSize(); - auto& contentArea = container.GetLayoutArea(); + auto& layoutArea = container.GetLayoutArea(); - ImVec2 pT = contentArea.Transform(pCenter, input.mRootPos); + ImVec2 pT = layoutArea.Transform(pCenter, input.mRootPos); - if (OverlapCircle(input.mCurPos, pT, size.x * contentArea.mScale)) { + if (OverlapCircle(input.mCurPos, pT, size.x * layoutArea.mScale)) { mLinkContainer->Notification(UIContainer_LinkFlag); mLinkContainer->SetCoParent(&container); } From feb706f79894686d468aa5725ae43b22c9983868 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Sat, 24 Aug 2024 08:37:25 +0200 Subject: [PATCH 054/125] Clean out useless stuff. --- .../include/rendering/ui/UIContainer.h | 4 +-- .../source/common/ui/UIContainer.cpp | 10 ++++-- .../rendering/source/common/ui/UIVisitors.cpp | 34 +++++++++---------- 3 files changed, 26 insertions(+), 22 deletions(-) diff --git a/packages/rendering/include/rendering/ui/UIContainer.h b/packages/rendering/include/rendering/ui/UIContainer.h index 831b7a04..56b24e15 100644 --- a/packages/rendering/include/rendering/ui/UIContainer.h +++ b/packages/rendering/include/rendering/ui/UIContainer.h @@ -73,7 +73,6 @@ namespace l::ui { }; struct InputState { - ImVec2 mRootPos; ImVec2 mCurPos; ImVec2 mPrevPos; float mScroll = 0.0f; @@ -81,7 +80,7 @@ namespace l::ui { bool mStopped = false; ImVec2 GetLocalPos() const { - return ImVec2(mCurPos.x - mRootPos.x, mCurPos.y - mRootPos.y); + return ImVec2(mCurPos.x, mCurPos.y); } }; @@ -300,6 +299,7 @@ namespace l::ui { void SetScale(float scale); void SetPosition(ImVec2 p); void SetSize(ImVec2 s); + void SetLayoutPosition(ImVec2 s); void SetLayoutSize(ImVec2 s); void SetDisplayName(std::string_view id); void SetId(std::string_view id); diff --git a/packages/rendering/source/common/ui/UIContainer.cpp b/packages/rendering/source/common/ui/UIContainer.cpp index efffaeb8..2d811ae3 100644 --- a/packages/rendering/source/common/ui/UIContainer.cpp +++ b/packages/rendering/source/common/ui/UIContainer.cpp @@ -123,6 +123,10 @@ namespace l::ui { mDisplayArea.mSize = s; } + void UIContainer::SetLayoutPosition(ImVec2 s) { + mLayoutArea.mPosition = s; + } + void UIContainer::SetLayoutSize(ImVec2 s) { mLayoutArea.mSize = s; } @@ -205,7 +209,7 @@ namespace l::ui { bool UIContainer::Accept(UIVisitor& visitor, const InputState& input, UITraversalMode mode) { if (visitor.ShouldUpdateContainer()) { - auto& layout = GetContainerArea().mLayout; + auto& layout = mDisplayArea.mLayout; switch (layout.mLayoutH) { case UILayoutH::Fixed: break; @@ -236,7 +240,7 @@ namespace l::ui { for (auto& content : mContent) { if (visitor.ShouldUpdateContainer()) { auto contentSize = content->GetSize(); - auto& contentLayout = content->GetContainerArea().mLayout; + auto& contentLayout = content->mDisplayArea.mLayout; ContainerArea current; current.mScale = mDisplayArea.GetWorldScale(mLayoutArea.mScale); current.mSize = mDisplayArea.GetWorldSizeLayout(mLayoutArea.mScale); @@ -263,7 +267,7 @@ namespace l::ui { ContainerArea current; if (visitor.ShouldUpdateContainer()) { - auto& layout = GetContainerArea().mLayout; + auto& layout = mDisplayArea.mLayout; switch (layout.mLayoutH) { case UILayoutH::Fixed: break; diff --git a/packages/rendering/source/common/ui/UIVisitors.cpp b/packages/rendering/source/common/ui/UIVisitors.cpp index 812f17ca..482a32f4 100644 --- a/packages/rendering/source/common/ui/UIVisitors.cpp +++ b/packages/rendering/source/common/ui/UIVisitors.cpp @@ -153,9 +153,9 @@ namespace l::ui { ImVec2 pSize = container.GetSize(); pSize.x *= layoutArea.mScale; pSize.y *= layoutArea.mScale; - ImVec2 p1 = layoutArea.Transform(pTopLeft, input.mRootPos); - ImVec2 p12 = layoutArea.Transform(pCenter, input.mRootPos); - ImVec2 p2 = layoutArea.Transform(pLowRight, input.mRootPos); + ImVec2 p1 = layoutArea.Transform(pTopLeft); + ImVec2 p12 = layoutArea.Transform(pCenter); + ImVec2 p2 = layoutArea.Transform(pLowRight); ImVec2 p11; ImVec2 p22; @@ -187,28 +187,28 @@ namespace l::ui { if (container.HasConfigFlag(UIContainer_LinkFlag)) { splineThickness = container.HasNotification(UIContainer_LinkFlag) ? 2.0f * splineThickness : splineThickness; ImVec2 pLinkInput = container.GetParent()->GetPosition(); - p1 = layoutArea.Transform(ImVec2(), input.mRootPos); + p1 = layoutArea.Transform(ImVec2()); if (container.GetCoParent() != nullptr) { ImVec2 pLinkOutput = container.GetCoParent()->GetPosition(); auto& coLayoutArea = container.GetCoParent()->GetLayoutArea(); - p2 = coLayoutArea.Transform(pLinkOutput, input.mRootPos); + p2 = coLayoutArea.Transform(pLinkOutput); float d12 = sqrt(0.25f*((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y)) / (layoutArea.mScale * layoutArea.mScale)); - p11 = layoutArea.Transform(ImVec2(pLinkInput.x + d12, pLinkInput.y), input.mRootPos); - p22 = coLayoutArea.Transform(ImVec2(pLinkOutput.x - d12, pLinkOutput.y), input.mRootPos); + p11 = layoutArea.Transform(ImVec2(pLinkInput.x + d12, pLinkInput.y)); + p22 = coLayoutArea.Transform(ImVec2(pLinkOutput.x - d12, pLinkOutput.y)); } else { p2 = input.mCurPos; float d12 = sqrt(0.25f * ((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y)) / (layoutArea.mScale * layoutArea.mScale)); - p11 = layoutArea.Transform(ImVec2(pLinkInput.x + d12, pLinkInput.y), input.mRootPos); + p11 = layoutArea.Transform(ImVec2(pLinkInput.x + d12, pLinkInput.y)); p22 = ImVec2(p2.x - d12 * layoutArea.mScale, p2.y); } } else { float d12 = sqrt(0.25f * ((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y)) / (layoutArea.mScale * layoutArea.mScale)); - p11 = layoutArea.Transform(ImVec2(pTopLeft.x + d12, pTopLeft.y), input.mRootPos); - p22 = layoutArea.Transform(ImVec2(pLowRight.x - d12, pLowRight.y), input.mRootPos); + p11 = layoutArea.Transform(ImVec2(pTopLeft.x + d12, pTopLeft.y)); + p22 = layoutArea.Transform(ImVec2(pLowRight.x - d12, pLowRight.y)); } mDrawList->AddBezierCubic(p1, p11, p22, p2, color, splineThickness, 30); break; @@ -243,11 +243,11 @@ namespace l::ui { case l::ui::UIRenderType::Texture: case l::ui::UIRenderType::Spline: if (container.HasConfigFlag(ui::UIContainer_ResizeFlag)) { - ImVec2 p3 = layoutArea.Transform(pLowRight, ImVec2(input.mRootPos.x - 3.0f, input.mRootPos.y - 3.0f)); - ImVec2 p4 = layoutArea.Transform(pLowRight, ImVec2(input.mRootPos.x + 3.0f, input.mRootPos.y + 3.0f)); + ImVec2 p3 = layoutArea.Transform(pLowRight, ImVec2(- 3.0f, - 3.0f)); + ImVec2 p4 = layoutArea.Transform(pLowRight, ImVec2(3.0f, 3.0f)); if (container.HasNotification(ui::UIContainer_ResizeFlag)) { - p3 = layoutArea.Transform(pLowRight, ImVec2(input.mRootPos.x - 5.0f, input.mRootPos.y - 5.0f)); - p4 = layoutArea.Transform(pLowRight, ImVec2(input.mRootPos.x + 5.0f, input.mRootPos.y + 5.0f)); + p3 = layoutArea.Transform(pLowRight, ImVec2(- 5.0f, - 5.0f)); + p4 = layoutArea.Transform(pLowRight, ImVec2(5.0f, 5.0f)); } mDrawList->AddRectFilled(p3, p4, color); } @@ -270,7 +270,7 @@ namespace l::ui { ImVec2 size = container.GetSize(); auto& layoutArea = container.GetLayoutArea(); - ImVec2 pT = layoutArea.Transform(pCenter, input.mRootPos); + ImVec2 pT = layoutArea.Transform(pCenter); if (OverlapCircle(input.mCurPos, pT, size.x * layoutArea.mScale)) { mDragging = true; mLinkContainer = mCreator->CreateContainer(UIContainer_LinkFlag | UIContainer_DrawFlag, UIRenderType::Spline); @@ -281,7 +281,7 @@ namespace l::ui { if (container.HasConfigFlag(UIContainer_LinkFlag) && !mDragging && input.mStarted && mLinkContainer.get() == nullptr && container.GetCoParent() != nullptr) { ImVec2 pCenter = container.GetCoParent()->GetPosition(); ImVec2 size = container.GetCoParent()->GetSize(); - ImVec2 pT = container.GetCoParent()->GetLayoutArea().Transform(pCenter, input.mRootPos); + ImVec2 pT = container.GetCoParent()->GetLayoutArea().Transform(pCenter); if (OverlapCircle(input.mCurPos, pT, size.x * container.GetCoParent()->GetLayoutArea().mScale)) { mDragging = true; mLinkContainer.mContainer = &container; @@ -302,7 +302,7 @@ namespace l::ui { ImVec2 size = container.GetSize(); auto& layoutArea = container.GetLayoutArea(); - ImVec2 pT = layoutArea.Transform(pCenter, input.mRootPos); + ImVec2 pT = layoutArea.Transform(pCenter); if (OverlapCircle(input.mCurPos, pT, size.x * layoutArea.mScale)) { mLinkContainer->Notification(UIContainer_LinkFlag); From 0db97785371ea0d312af91ea60101e4f5d77c8db Mon Sep 17 00:00:00 2001 From: lnd3 Date: Sat, 24 Aug 2024 23:58:07 +0200 Subject: [PATCH 055/125] Fix some issues. --- .../include/rendering/ui/UIContainer.h | 3 +-- .../source/common/ui/UIContainer.cpp | 2 +- .../rendering/source/common/ui/UIVisitors.cpp | 24 +++++++++---------- 3 files changed, 14 insertions(+), 15 deletions(-) diff --git a/packages/rendering/include/rendering/ui/UIContainer.h b/packages/rendering/include/rendering/ui/UIContainer.h index 56b24e15..85274029 100644 --- a/packages/rendering/include/rendering/ui/UIContainer.h +++ b/packages/rendering/include/rendering/ui/UIContainer.h @@ -309,7 +309,7 @@ namespace l::ui { void SetCoParent(UIContainer* input); UIContainer* GetParent(); UIContainer* GetCoParent(); - ImVec2 GetPosition(bool untransformed = false); + ImVec2 GetPosition(bool untransformed = false) const; ImVec2 GetPositionAtCenter(ImVec2 offset = ImVec2(), bool untransformed = false); ImVec2 GetPositionAtSize(ImVec2 offset = ImVec2(), bool untransformed = false); ImVec2 GetSize(bool untransformed = false); @@ -402,7 +402,6 @@ namespace l::ui { protected: std::unordered_map> mContainers; - std::vector mVisitors; }; } diff --git a/packages/rendering/source/common/ui/UIContainer.cpp b/packages/rendering/source/common/ui/UIContainer.cpp index 2d811ae3..73bb8e3a 100644 --- a/packages/rendering/source/common/ui/UIContainer.cpp +++ b/packages/rendering/source/common/ui/UIContainer.cpp @@ -163,7 +163,7 @@ namespace l::ui { return mCoParent; } - ImVec2 UIContainer::GetPosition(bool untransformed) { + ImVec2 UIContainer::GetPosition(bool untransformed) const { if (untransformed) { return mDisplayArea.mPosition; } diff --git a/packages/rendering/source/common/ui/UIVisitors.cpp b/packages/rendering/source/common/ui/UIVisitors.cpp index 482a32f4..7dff987e 100644 --- a/packages/rendering/source/common/ui/UIVisitors.cpp +++ b/packages/rendering/source/common/ui/UIVisitors.cpp @@ -174,10 +174,10 @@ namespace l::ui { case l::ui::UIRenderType::TriangleFilled: break; case l::ui::UIRenderType::Circle: - mDrawList->AddCircle(p12, pSize.x, color, 15, 2.0f * container.GetScale() * layoutArea.mScale); + mDrawList->AddCircle(p1, pSize.x, color, 18, 2.0f * container.GetScale() * layoutArea.mScale); break; case l::ui::UIRenderType::CircleFilled: - mDrawList->AddCircleFilled(p12, pSize.x, color, 15); + mDrawList->AddCircleFilled(p1, pSize.x, color, 15); break; case l::ui::UIRenderType::Polygon: break; @@ -189,28 +189,28 @@ namespace l::ui { ImVec2 pLinkInput = container.GetParent()->GetPosition(); p1 = layoutArea.Transform(ImVec2()); - if (container.GetCoParent() != nullptr) { + if (container.GetCoParent() == nullptr) { + p2 = input.mCurPos; + float d12 = sqrt(0.25f * ((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y)) / (layoutArea.mScale * layoutArea.mScale)); + p11 = layoutArea.Transform(ImVec2(pLinkInput.x + d12, 0.0f)); + p22 = ImVec2(p2.x - d12 * layoutArea.mScale, p2.y); + } + else { ImVec2 pLinkOutput = container.GetCoParent()->GetPosition(); auto& coLayoutArea = container.GetCoParent()->GetLayoutArea(); p2 = coLayoutArea.Transform(pLinkOutput); - float d12 = sqrt(0.25f*((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y)) / (layoutArea.mScale * layoutArea.mScale)); + float d12 = sqrt(0.25f * ((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y)) / (layoutArea.mScale * layoutArea.mScale)); - p11 = layoutArea.Transform(ImVec2(pLinkInput.x + d12, pLinkInput.y)); + p11 = layoutArea.Transform(ImVec2(pLinkInput.x + d12, 0.0f)); p22 = coLayoutArea.Transform(ImVec2(pLinkOutput.x - d12, pLinkOutput.y)); } - else { - p2 = input.mCurPos; - float d12 = sqrt(0.25f * ((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y)) / (layoutArea.mScale * layoutArea.mScale)); - p11 = layoutArea.Transform(ImVec2(pLinkInput.x + d12, pLinkInput.y)); - p22 = ImVec2(p2.x - d12 * layoutArea.mScale, p2.y); - } } else { float d12 = sqrt(0.25f * ((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y)) / (layoutArea.mScale * layoutArea.mScale)); p11 = layoutArea.Transform(ImVec2(pTopLeft.x + d12, pTopLeft.y)); p22 = layoutArea.Transform(ImVec2(pLowRight.x - d12, pLowRight.y)); } - mDrawList->AddBezierCubic(p1, p11, p22, p2, color, splineThickness, 30); + mDrawList->AddBezierCubic(p1, p11, p22, p2, color, splineThickness * layoutArea.mScale, static_cast(10.0f + 20.0f * sqrt(layoutArea.mScale))); break; case l::ui::UIRenderType::Text: From 28d64f11a75189053e30abb32f3cd48e9528cea6 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Sun, 25 Aug 2024 20:03:42 +0200 Subject: [PATCH 056/125] Name refactor. Fix curve scale. --- .../include/rendering/ui/UIContainer.h | 30 +++++++------- .../include/rendering/ui/UIVisitors.h | 3 +- .../source/common/ui/UIContainer.cpp | 2 +- .../rendering/source/common/ui/UIVisitors.cpp | 40 ++++++++++++------- .../tools/include/tools/nodegraph/NodeGraph.h | 3 +- 5 files changed, 45 insertions(+), 33 deletions(-) diff --git a/packages/rendering/include/rendering/ui/UIContainer.h b/packages/rendering/include/rendering/ui/UIContainer.h index 85274029..e099acd3 100644 --- a/packages/rendering/include/rendering/ui/UIContainer.h +++ b/packages/rendering/include/rendering/ui/UIContainer.h @@ -50,7 +50,7 @@ namespace l::ui { CircleFilled = 5, Polygon = 6, PolygonFilled = 7, - Spline = 8, + LinkH = 8, Text = 9, Texture = 10 }; @@ -95,7 +95,7 @@ namespace l::ui { ImVec2 mPosition; ImVec2 mSize = ImVec2(20.0f, 20.0f); float mScale = 1.0f; - float mBorder = 3.0f; + float mMargin = 3.0f; UILayoutData mLayout; UIRenderData mRender; @@ -119,15 +119,15 @@ namespace l::ui { // Used in ui container layout, this is where we premultiply parent scale ImVec2 GetWorldPos(float parentScale, ImVec2 parentPos) { ImVec2 worldPos; - worldPos.x = parentPos.x + (mPosition.x + mBorder) * mScale * parentScale; - worldPos.y = parentPos.y + (mPosition.y + mBorder) * mScale * parentScale; + worldPos.x = parentPos.x + (mPosition.x + mMargin) * mScale * parentScale; + worldPos.y = parentPos.y + (mPosition.y + mMargin) * mScale * parentScale; return worldPos; } ImVec2 GetWorldSize(float parentScale) const { ImVec2 worldSize; - worldSize.x = (mSize.x - mBorder * 2.0f) * mScale * parentScale; - worldSize.y = (mSize.y - mBorder * 2.0f) * mScale * parentScale; + worldSize.x = (mSize.x - mMargin * 2.0f) * mScale * parentScale; + worldSize.y = (mSize.y - mMargin * 2.0f) * mScale * parentScale; return worldSize; } @@ -142,24 +142,24 @@ namespace l::ui { ImVec2 worldPos; switch (alignH) { case UIAlignH::Left: - worldPos.x = parentPos.x + (mPosition.x + mBorder) * mScale * parentScale; + worldPos.x = parentPos.x + (mPosition.x + mMargin) * mScale * parentScale; break; case UIAlignH::Center: worldPos.x = parentPos.x + (mPosition.x + mSize.x * 0.5f - contentSize.x * 0.5f) * mScale * parentScale; break; case UIAlignH::Right: - worldPos.x = parentPos.x + (mPosition.x - mBorder + mSize.x - contentSize.x) * mScale * parentScale; + worldPos.x = parentPos.x + (mPosition.x - mMargin + mSize.x - contentSize.x) * mScale * parentScale; break; } switch (alignV) { case UIAlignV::Top: - worldPos.y = parentPos.y + (mPosition.y + mBorder) * mScale * parentScale; + worldPos.y = parentPos.y + (mPosition.y + mMargin) * mScale * parentScale; break; case UIAlignV::Middle: worldPos.y = parentPos.y + (mPosition.y + mSize.y * 0.5f - contentSize.y * 0.5f) * mScale * parentScale; break; case UIAlignV::Bottom: - worldPos.y = parentPos.y + (mPosition.y - mBorder + mSize.y - contentSize.y) * mScale * parentScale; + worldPos.y = parentPos.y + (mPosition.y - mMargin + mSize.y - contentSize.y) * mScale * parentScale; break; } return worldPos; @@ -167,8 +167,8 @@ namespace l::ui { ImVec2 GetWorldSizeLayout(float parentScale) const { ImVec2 worldSize; - worldSize.x = (mSize.x - mBorder) * mScale * parentScale; - worldSize.y = (mSize.y - mBorder) * mScale * parentScale; + worldSize.x = (mSize.x - mMargin) * mScale * parentScale; + worldSize.y = (mSize.y - mMargin) * mScale * parentScale; return worldSize; } }; @@ -259,12 +259,12 @@ namespace l::ui { public: UIContainer(uint32_t flags = 0, UIRenderType renderType = UIRenderType::Rect, UIAlignH alignH = UIAlignH::Left, UIAlignV alignV = UIAlignV::Top, UILayoutH layoutH = UILayoutH::Fixed, UILayoutV layoutV = UILayoutV::Fixed) : mConfigFlags(flags) { mDisplayArea.mRender.mType = renderType; - mDisplayArea.mBorder = 3.0f; + mDisplayArea.mMargin = 3.0f; mDisplayArea.mLayout.mAlignH = alignH; mDisplayArea.mLayout.mAlignV = alignV; mDisplayArea.mLayout.mLayoutH = layoutH; mDisplayArea.mLayout.mLayoutV = layoutV; - mLayoutArea.mBorder = 0.0f; + mLayoutArea.mMargin = 0.0f; } virtual ~UIContainer() = default; @@ -362,7 +362,7 @@ namespace l::ui { public: UISplit(uint32_t flags, UISplitMode splitMode, UILayoutH layoutH, UILayoutV layoutV) : UIContainer(flags), mSplitMode(splitMode) { mDisplayArea.mRender.mType = UIRenderType::Rect; - mDisplayArea.mBorder = 3.0f; + mDisplayArea.mMargin = 3.0f; mDisplayArea.mLayout.mLayoutH = layoutH; mDisplayArea.mLayout.mLayoutV = layoutV; } diff --git a/packages/rendering/include/rendering/ui/UIVisitors.h b/packages/rendering/include/rendering/ui/UIVisitors.h index 4afc6479..a3a56b17 100644 --- a/packages/rendering/include/rendering/ui/UIVisitors.h +++ b/packages/rendering/include/rendering/ui/UIVisitors.h @@ -51,7 +51,6 @@ namespace l::ui { virtual bool Visit(UIContainer& container, const InputState& input); protected: bool mResizing = false; - float mResizeAreaSize = 8.0f; UIContainer* mSourceContainer = nullptr; }; @@ -78,6 +77,8 @@ namespace l::ui { bool mPossibleLinkImminent = false; UIHandle mLinkContainer; UICreator* mCreator = nullptr; + + std::function mLink; }; } diff --git a/packages/rendering/source/common/ui/UIContainer.cpp b/packages/rendering/source/common/ui/UIContainer.cpp index 73bb8e3a..aea5e34b 100644 --- a/packages/rendering/source/common/ui/UIContainer.cpp +++ b/packages/rendering/source/common/ui/UIContainer.cpp @@ -85,7 +85,7 @@ namespace l::ui { void UIContainer::Resize(ImVec2 localChange) { mDisplayArea.mSize.x += localChange.x; mDisplayArea.mSize.y += localChange.y; - if (mDisplayArea.mRender.mType != UIRenderType::Spline) { + if (mDisplayArea.mRender.mType != UIRenderType::LinkH) { mDisplayArea.mSize.x = mDisplayArea.mSize.x < 1.0f ? 1.0f : mDisplayArea.mSize.x; mDisplayArea.mSize.y = mDisplayArea.mSize.y < 1.0f ? 1.0f : mDisplayArea.mSize.y; } diff --git a/packages/rendering/source/common/ui/UIVisitors.cpp b/packages/rendering/source/common/ui/UIVisitors.cpp index 7dff987e..e8920d8a 100644 --- a/packages/rendering/source/common/ui/UIVisitors.cpp +++ b/packages/rendering/source/common/ui/UIVisitors.cpp @@ -104,10 +104,10 @@ namespace l::ui { return false; } if (!mResizing) { - const float radii = mResizeAreaSize * 0.5f; ImVec2 p = container.GetPositionAtSize(); auto& layoutArea = container.GetLayoutArea(); - if (OverlapScreenRect(input.GetLocalPos(), p, ImVec2(radii, radii), layoutArea)) { + float size = 3.0f * layoutArea.mScale; + if (OverlapScreenRect(input.GetLocalPos(), p, ImVec2(size, size), layoutArea)) { mSourceContainer = &container; container.Notification(UIContainer_ResizeFlag); @@ -148,16 +148,15 @@ namespace l::ui { float splineThickness = 2.0f; ImU32 color = container.GetRenderData().mColor; ImVec2 pTopLeft = container.GetPosition(); - ImVec2 pCenter = container.GetPositionAtCenter(); ImVec2 pLowRight = container.GetPositionAtSize(); ImVec2 pSize = container.GetSize(); pSize.x *= layoutArea.mScale; pSize.y *= layoutArea.mScale; ImVec2 p1 = layoutArea.Transform(pTopLeft); - ImVec2 p12 = layoutArea.Transform(pCenter); ImVec2 p2 = layoutArea.Transform(pLowRight); ImVec2 p11; ImVec2 p22; + float d12 = 1.0f; const char* nameStart; const char* nameEnd; @@ -183,7 +182,7 @@ namespace l::ui { break; case l::ui::UIRenderType::PolygonFilled: break; - case l::ui::UIRenderType::Spline: + case l::ui::UIRenderType::LinkH: if (container.HasConfigFlag(UIContainer_LinkFlag)) { splineThickness = container.HasNotification(UIContainer_LinkFlag) ? 2.0f * splineThickness : splineThickness; ImVec2 pLinkInput = container.GetParent()->GetPosition(); @@ -191,7 +190,7 @@ namespace l::ui { if (container.GetCoParent() == nullptr) { p2 = input.mCurPos; - float d12 = sqrt(0.25f * ((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y)) / (layoutArea.mScale * layoutArea.mScale)); + d12 = sqrt(0.25f * ((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y)) / (layoutArea.mScale * layoutArea.mScale)); p11 = layoutArea.Transform(ImVec2(pLinkInput.x + d12, 0.0f)); p22 = ImVec2(p2.x - d12 * layoutArea.mScale, p2.y); } @@ -199,18 +198,24 @@ namespace l::ui { ImVec2 pLinkOutput = container.GetCoParent()->GetPosition(); auto& coLayoutArea = container.GetCoParent()->GetLayoutArea(); p2 = coLayoutArea.Transform(pLinkOutput); - float d12 = sqrt(0.25f * ((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y)) / (layoutArea.mScale * layoutArea.mScale)); + d12 = sqrt(0.25f * ((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y)) / (layoutArea.mScale * layoutArea.mScale)); p11 = layoutArea.Transform(ImVec2(pLinkInput.x + d12, 0.0f)); p22 = coLayoutArea.Transform(ImVec2(pLinkOutput.x - d12, pLinkOutput.y)); } } else { - float d12 = sqrt(0.25f * ((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y)) / (layoutArea.mScale * layoutArea.mScale)); + d12 = sqrt(0.25f * ((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y)) / (layoutArea.mScale * layoutArea.mScale)); p11 = layoutArea.Transform(ImVec2(pTopLeft.x + d12, pTopLeft.y)); p22 = layoutArea.Transform(ImVec2(pLowRight.x - d12, pLowRight.y)); } - mDrawList->AddBezierCubic(p1, p11, p22, p2, color, splineThickness * layoutArea.mScale, static_cast(10.0f + 20.0f * sqrt(layoutArea.mScale))); + { + float scaleFactor = sqrt(d12 * layoutArea.mScale); + if (scaleFactor > 50.0f) { + scaleFactor = 50.0f; + } + mDrawList->AddBezierCubic(p1, p11, p22, p2, color, splineThickness * layoutArea.mScale, static_cast(10.0f + scaleFactor)); + } break; case l::ui::UIRenderType::Text: @@ -241,13 +246,18 @@ namespace l::ui { case l::ui::UIRenderType::Rect: case l::ui::UIRenderType::RectFilled: case l::ui::UIRenderType::Texture: - case l::ui::UIRenderType::Spline: + case l::ui::UIRenderType::LinkH: + if (container.HasConfigFlag(UIContainer_InputFlag)) { + + } if (container.HasConfigFlag(ui::UIContainer_ResizeFlag)) { - ImVec2 p3 = layoutArea.Transform(pLowRight, ImVec2(- 3.0f, - 3.0f)); - ImVec2 p4 = layoutArea.Transform(pLowRight, ImVec2(3.0f, 3.0f)); + float size = 3.0f * layoutArea.mScale; + ImVec2 p3 = layoutArea.Transform(pLowRight, ImVec2(-size, -size)); + ImVec2 p4 = layoutArea.Transform(pLowRight, ImVec2(size, size)); if (container.HasNotification(ui::UIContainer_ResizeFlag)) { - p3 = layoutArea.Transform(pLowRight, ImVec2(- 5.0f, - 5.0f)); - p4 = layoutArea.Transform(pLowRight, ImVec2(5.0f, 5.0f)); + float size2 = 5.0f * layoutArea.mScale; + p3 = layoutArea.Transform(pLowRight, ImVec2(-size2, -size2)); + p4 = layoutArea.Transform(pLowRight, ImVec2(size2, size2)); } mDrawList->AddRectFilled(p3, p4, color); } @@ -273,7 +283,7 @@ namespace l::ui { ImVec2 pT = layoutArea.Transform(pCenter); if (OverlapCircle(input.mCurPos, pT, size.x * layoutArea.mScale)) { mDragging = true; - mLinkContainer = mCreator->CreateContainer(UIContainer_LinkFlag | UIContainer_DrawFlag, UIRenderType::Spline); + mLinkContainer = mCreator->CreateContainer(UIContainer_LinkFlag | UIContainer_DrawFlag, UIRenderType::LinkH); container.Add(mLinkContainer); return true; } diff --git a/packages/tools/include/tools/nodegraph/NodeGraph.h b/packages/tools/include/tools/nodegraph/NodeGraph.h index f39ada95..e89e1f98 100644 --- a/packages/tools/include/tools/nodegraph/NodeGraph.h +++ b/packages/tools/include/tools/nodegraph/NodeGraph.h @@ -19,7 +19,8 @@ namespace l::nodegraph { enum class InputType { INPUT_NODE, INPUT_CONSTANT, - INPUT_VALUE + INPUT_VALUE, + //INPUT_ARRAY // TODO: is it possible to process batches for example for audio processing? }; bool IsValidInOutNum(int8_t inoutNum, size_t inoutSize); From 2d5827fe716c25e24d4c16a37446e8c4ef17fc58 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Mon, 26 Aug 2024 03:18:33 +0200 Subject: [PATCH 057/125] Add handler functionality for connecting ui containers to be able to map io containers to nodegraph nodes. --- .../include/rendering/ui/UIContainer.h | 141 +++++++++--------- .../include/rendering/ui/UIVisitors.h | 11 +- .../rendering/include/rendering/ui/UIWindow.h | 3 + .../source/common/ui/UIContainer.cpp | 109 ++------------ .../rendering/source/common/ui/UIVisitors.cpp | 24 ++- .../rendering/source/common/ui/UIWindow.cpp | 7 + .../tools/include/tools/nodegraph/NodeGraph.h | 21 ++- .../source/common/nodegraph/NodeGraph.cpp | 122 +++++++++++---- 8 files changed, 231 insertions(+), 207 deletions(-) diff --git a/packages/rendering/include/rendering/ui/UIContainer.h b/packages/rendering/include/rendering/ui/UIContainer.h index e099acd3..86e5a646 100644 --- a/packages/rendering/include/rendering/ui/UIContainer.h +++ b/packages/rendering/include/rendering/ui/UIContainer.h @@ -181,6 +181,28 @@ namespace l::ui { bool OverlapCircle(const ImVec2& p, const ImVec2& pCenter, float radii); class UIContainer; + class UISplit; + + int32_t CreateUniqueId(); + + template>> + std::string CreateUniqueStringId() { + static uint32_t mStringId = 0; + std::string id; + if constexpr (std::is_same_v) { + id += "UIContainer"; + } + else if constexpr (std::is_same_v) { + id += "UISplit"; + } + else { + id += "Unknown"; + } + + id += l::string::to_hex(mStringId++, 4); + + return id; + } class UIVisitor { public: @@ -226,19 +248,20 @@ namespace l::ui { template>> class UIHandle { public: - UIHandle() : mContainer(nullptr) {}; - UIHandle(std::string_view id, UIContainer* container) : mId(id), mContainer(container) {}; + UIHandle() : mId(0), mContainer(nullptr) {}; + UIHandle(int32_t id, std::string_view stringId, UIContainer* container) : mId(id), mStringId(stringId), mContainer(container) {}; ~UIHandle() = default; - std::string mId; + int32_t mId; + std::string mStringId; UIContainer* mContainer = nullptr; std::string_view id() { - return mId; + return mStringId; } void reset() { - mId.clear(); + mStringId.clear(); mContainer = nullptr; } @@ -257,7 +280,10 @@ namespace l::ui { class UIContainer { public: - UIContainer(uint32_t flags = 0, UIRenderType renderType = UIRenderType::Rect, UIAlignH alignH = UIAlignH::Left, UIAlignV alignV = UIAlignV::Top, UILayoutH layoutH = UILayoutH::Fixed, UILayoutV layoutV = UILayoutV::Fixed) : mConfigFlags(flags) { + UIContainer(uint32_t flags = 0, UIRenderType renderType = UIRenderType::Rect, UIAlignH alignH = UIAlignH::Left, UIAlignV alignV = UIAlignV::Top, UILayoutH layoutH = UILayoutH::Fixed, UILayoutV layoutV = UILayoutV::Fixed) : + mConfigFlags(flags), + mId(0) + { mDisplayArea.mRender.mType = renderType; mDisplayArea.mMargin = 3.0f; mDisplayArea.mLayout.mAlignH = alignH; @@ -289,54 +315,49 @@ namespace l::ui { } } + void SetNotification(uint32_t flag) { mNotificationFlags |= flag; } + void ClearNotifications() { mNotificationFlags = 0; } + bool HasNotification(uint32_t flag) { return (mNotificationFlags & flag) == flag; } + bool HasConfigFlag(uint32_t flag) { return (mConfigFlags & flag) == flag; } + void Move(ImVec2 localChange); void Resize(ImVec2 localChange); - void Rescale(float localChange); - void ClearNotifications(); - void Notification(uint32_t flag); - bool HasNotification(uint32_t flag); - bool HasConfigFlag(uint32_t flag); - void SetScale(float scale); - void SetPosition(ImVec2 p); - void SetSize(ImVec2 s); - void SetLayoutPosition(ImVec2 s); - void SetLayoutSize(ImVec2 s); - void SetDisplayName(std::string_view id); - void SetId(std::string_view id); - void SetContainerArea(const ContainerArea& area); - void SetLayoutArea(const ContainerArea& area); - void SetParent(UIContainer* input); - void SetCoParent(UIContainer* input); - UIContainer* GetParent(); - UIContainer* GetCoParent(); + void Rescale(float localChange) { mDisplayArea.mScale *= localChange; } + ImVec2 GetPosition(bool untransformed = false) const; ImVec2 GetPositionAtCenter(ImVec2 offset = ImVec2(), bool untransformed = false); ImVec2 GetPositionAtSize(ImVec2 offset = ImVec2(), bool untransformed = false); ImVec2 GetSize(bool untransformed = false); - float GetScale(); - ContainerArea& GetContainerArea(); - const ContainerArea& GetLayoutArea() const; - - void DebugLog(); - - const UIRenderData& GetRenderData() const { - return mDisplayArea.mRender; - } - - const UILayoutData& GetLayoutData() const { - return mDisplayArea.mLayout; - } - - std::string_view GetDisplayName() { - return mDisplayName; - } - - std::string_view GetId() { - return mId; - } + float GetScale() {return mDisplayArea.mScale;} + ContainerArea& GetContainerArea() {return mDisplayArea;} + const ContainerArea& GetLayoutArea() const {return mLayoutArea;} + UIContainer* GetParent() { return mParent; } + UIContainer* GetCoParent() { return mCoParent; } + const UIRenderData& GetRenderData() const { return mDisplayArea.mRender; } + const UILayoutData& GetLayoutData() const { return mDisplayArea.mLayout; } + std::string_view GetDisplayName() { return mDisplayName; } + std::string_view GetStringId() { return mStringId; } + int32_t GetId() { return mId; } + + void SetColor(ImVec4 color) { mDisplayArea.mRender.mColor = ImColor(color); } + void SetScale(float scale) {mDisplayArea.mScale = scale;} + void SetPosition(ImVec2 p) {mDisplayArea.mPosition = p;} + void SetSize(ImVec2 s) {mDisplayArea.mSize = s;} + void SetLayoutPosition(ImVec2 s) {mLayoutArea.mPosition = s;} + void SetLayoutSize(ImVec2 s) {mLayoutArea.mSize = s;} + void SetDisplayName(std::string_view displayName) {mDisplayName = displayName;} + void SetStringId(std::string_view id) {mStringId = id;} + void SetId(int32_t id) {mId = id;} + void SetContainerArea(const ContainerArea& area) {mDisplayArea = area;} + void SetLayoutArea(const ContainerArea& transformedLayoutArea) {mLayoutArea = transformedLayoutArea;} + void SetParent(UIContainer* parent) {mParent = parent;} + void SetCoParent(UIContainer* coParent) {mCoParent = coParent;} + + void DebugLog() { LOG(LogDebug) << "UIContainer: " << mDisplayName << ", [" << mDisplayArea.mScale << "][" << mDisplayArea.mPosition.x << ", " << mDisplayArea.mPosition.y << "][" << mDisplayArea.mSize.x << ", " << mDisplayArea.mSize.y << "]"; } protected: - std::string mId; + int32_t mId = 0; + std::string mStringId; std::string mDisplayName; uint32_t mConfigFlags = 0; // Active visitor flags uint32_t mNotificationFlags = 0; // Notification flags for ux feedback (resizing box animation etc) @@ -360,11 +381,12 @@ namespace l::ui { class UISplit : public UIContainer { public: - UISplit(uint32_t flags, UISplitMode splitMode, UILayoutH layoutH, UILayoutV layoutV) : UIContainer(flags), mSplitMode(splitMode) { - mDisplayArea.mRender.mType = UIRenderType::Rect; + UISplit(uint32_t flags, UIRenderType renderType, UISplitMode splitMode, UILayoutH layoutH, UILayoutV layoutV) : UIContainer(flags), mSplitMode(splitMode) { + mDisplayArea.mRender.mType = renderType; mDisplayArea.mMargin = 3.0f; mDisplayArea.mLayout.mLayoutH = layoutH; mDisplayArea.mLayout.mLayoutV = layoutV; + mDisplayArea.mRender.mColor = ImColor(ImVec4(0.0f, 0.0f, 0.0f, 1.0f)); } ~UISplit() = default; @@ -373,35 +395,16 @@ namespace l::ui { UISplitMode mSplitMode; }; - template>> - std::string CreateUniqueId() { - static uint32_t mId = 0; - std::string id; - if constexpr (std::is_same_v) { - id += "UIContainer"; - } - else if constexpr (std::is_same_v) { - id += "UISplit"; - } - else { - id += "Unknown"; - } - - id += l::string::to_hex(mId++, 4); - - return id; - } - class UICreator { public: UICreator() = default; ~UICreator() = default; UIHandle CreateContainer(uint32_t flags, UIRenderType renderType = UIRenderType::Rect, UIAlignH alignH = UIAlignH::Left, UIAlignV alignV = UIAlignV::Top, UILayoutH layoutH = UILayoutH::Fixed, UILayoutV layoutV = UILayoutV::Fixed); - UIHandle CreateSplit(uint32_t flags, UISplitMode splitMode = UISplitMode::AppendV, UILayoutH layoutH = UILayoutH::Fixed, UILayoutV layoutV = UILayoutV::Fixed); + UIHandle CreateSplit(uint32_t flags, UIRenderType renderType, UISplitMode splitMode = UISplitMode::AppendV, UILayoutH layoutH = UILayoutH::Fixed, UILayoutV layoutV = UILayoutV::Fixed); protected: - std::unordered_map> mContainers; + std::unordered_map> mContainers; }; } diff --git a/packages/rendering/include/rendering/ui/UIVisitors.h b/packages/rendering/include/rendering/ui/UIVisitors.h index a3a56b17..216dd4ef 100644 --- a/packages/rendering/include/rendering/ui/UIVisitors.h +++ b/packages/rendering/include/rendering/ui/UIVisitors.h @@ -66,19 +66,26 @@ namespace l::ui { class UILinkIO : public UIVisitor { public: + using HandlerFunctionType = bool(int32_t, int32_t, int8_t, int8_t, bool); + virtual bool Active(UIContainer& container, const InputState& input); - UILinkIO(UICreator* creator = nullptr) : mCreator(creator) {} + UILinkIO(UICreator* creator, std::function handler = nullptr) : mCreator(creator), mHandler(std::move(handler)) {} ~UILinkIO() = default; virtual bool Visit(UIContainer& container, const InputState& input); + + void SetHandler(std::function handler) { + mHandler = std::move(handler); + } + protected: bool mDragging = false; bool mPossibleLinkImminent = false; UIHandle mLinkContainer; UICreator* mCreator = nullptr; - std::function mLink; + std::function mHandler; }; } diff --git a/packages/rendering/include/rendering/ui/UIWindow.h b/packages/rendering/include/rendering/ui/UIWindow.h index a265bee7..96990800 100644 --- a/packages/rendering/include/rendering/ui/UIWindow.h +++ b/packages/rendering/include/rendering/ui/UIWindow.h @@ -33,6 +33,7 @@ namespace l::ui { bool TryScale(const ImVec2& scalePos, float scroll); ImVec2 Transform(ImVec2 p, bool toWorld = true); void Show(); + void SetBgColor(ImVec4 bgColor); protected: ImGuiWindow* mWindowPtr; bool mOpened; @@ -41,6 +42,8 @@ namespace l::ui { float mContentScale; bool mMoving; + ImVec4 mBgColor = ImVec4(0.01f, 0.01f, 0.01f, 1.0f); + ImVec2 mContentPan; }; diff --git a/packages/rendering/source/common/ui/UIContainer.cpp b/packages/rendering/source/common/ui/UIContainer.cpp index aea5e34b..c40cdcb9 100644 --- a/packages/rendering/source/common/ui/UIContainer.cpp +++ b/packages/rendering/source/common/ui/UIContainer.cpp @@ -7,21 +7,25 @@ namespace l::ui { UIHandle UICreator::CreateContainer(uint32_t flags, UIRenderType renderType, UIAlignH alignH, UIAlignV alignV, UILayoutH layoutH, UILayoutV layoutV) { std::unique_ptr container = std::make_unique(flags, renderType, alignH, alignV, layoutH, layoutV); - std::string id = CreateUniqueId(); + auto id = CreateUniqueId(); + auto stringId = CreateUniqueStringId(); container->SetId(id); + container->SetStringId(stringId); mContainers.insert({ id, std::move(container) }); - return UIHandle{ id, mContainers.at(id).get() }; + return UIHandle{ id, stringId, mContainers.at(id).get() }; } - UIHandle UICreator::CreateSplit(uint32_t flags, UISplitMode splitMode, UILayoutH layoutH, UILayoutV layoutV) { - std::unique_ptr container = std::make_unique(flags, splitMode, layoutH, layoutV); + UIHandle UICreator::CreateSplit(uint32_t flags, UIRenderType renderType, UISplitMode splitMode, UILayoutH layoutH, UILayoutV layoutV) { + std::unique_ptr container = std::make_unique(flags, renderType, splitMode, layoutH, layoutV); - std::string id = CreateUniqueId(); + auto id = CreateUniqueId(); + auto stringId = CreateUniqueStringId(); container->SetId(id); + container->SetStringId(stringId); mContainers.insert({ id, std::move(container) }); - return UIHandle{ id, mContainers.at(id).get() }; + return UIHandle{ id, stringId, mContainers.at(id).get() }; } ImVec2 DragMovement(const ImVec2& prevPos, const ImVec2& curPos, float curScale) { @@ -91,78 +95,6 @@ namespace l::ui { } } - void UIContainer::Rescale(float localChange) { - mDisplayArea.mScale *= localChange; - } - - void UIContainer::ClearNotifications() { - mNotificationFlags = 0; - } - - void UIContainer::Notification(uint32_t flag) { - mNotificationFlags |= flag; - } - - bool UIContainer::HasNotification(uint32_t flag) { - return (mNotificationFlags & flag) == flag; - } - - bool UIContainer::HasConfigFlag(uint32_t flag) { - return (mConfigFlags & flag) == flag; - } - - void UIContainer::SetScale(float scale) { - mDisplayArea.mScale = scale; - } - - void UIContainer::SetPosition(ImVec2 p) { - mDisplayArea.mPosition = p; - } - - void UIContainer::SetSize(ImVec2 s) { - mDisplayArea.mSize = s; - } - - void UIContainer::SetLayoutPosition(ImVec2 s) { - mLayoutArea.mPosition = s; - } - - void UIContainer::SetLayoutSize(ImVec2 s) { - mLayoutArea.mSize = s; - } - - void UIContainer::SetDisplayName(std::string_view displayName) { - mDisplayName = displayName; - } - - void UIContainer::SetId(std::string_view id) { - mId = id; - } - - void UIContainer::SetContainerArea(const ContainerArea& area) { - mDisplayArea = area; - } - - void UIContainer::SetLayoutArea(const ContainerArea& transformedLayoutArea) { - mLayoutArea = transformedLayoutArea; - } - - UIContainer* UIContainer::GetParent() { - return mParent; - } - - void UIContainer::SetParent(UIContainer* parent) { - mParent = parent; - } - - void UIContainer::SetCoParent(UIContainer* coParent) { - mCoParent = coParent; - } - - UIContainer* UIContainer::GetCoParent() { - return mCoParent; - } - ImVec2 UIContainer::GetPosition(bool untransformed) const { if (untransformed) { return mDisplayArea.mPosition; @@ -191,22 +123,6 @@ namespace l::ui { return ImVec2(mDisplayArea.mSize.x * mDisplayArea.mScale, mDisplayArea.mSize.y * mDisplayArea.mScale); } - float UIContainer::GetScale() { - return mDisplayArea.mScale; - } - - ContainerArea& UIContainer::GetContainerArea() { - return mDisplayArea; - } - - const ContainerArea& UIContainer::GetLayoutArea() const { - return mLayoutArea; - } - - void UIContainer::DebugLog() { - LOG(LogDebug) << "UIContainer: " << mDisplayName << ", [" << mDisplayArea.mScale << "][" << mDisplayArea.mPosition.x << ", " << mDisplayArea.mPosition.y << "][" << mDisplayArea.mSize.x << ", " << mDisplayArea.mSize.y << "]"; - } - bool UIContainer::Accept(UIVisitor& visitor, const InputState& input, UITraversalMode mode) { if (visitor.ShouldUpdateContainer()) { auto& layout = mDisplayArea.mLayout; @@ -355,4 +271,9 @@ namespace l::ui { return false; } + int32_t CreateUniqueId() { + static int32_t mId = 0; + return mId++; + } + } diff --git a/packages/rendering/source/common/ui/UIVisitors.cpp b/packages/rendering/source/common/ui/UIVisitors.cpp index e8920d8a..c9c6e1f5 100644 --- a/packages/rendering/source/common/ui/UIVisitors.cpp +++ b/packages/rendering/source/common/ui/UIVisitors.cpp @@ -58,7 +58,7 @@ namespace l::ui { ImVec2 move = DragMovement(input.mPrevPos, input.mCurPos, layoutArea.mScale * container.GetScale()); container.Move(move); - container.Notification(UIContainer_DragFlag); + container.SetNotification(UIContainer_DragFlag); if (input.mStopped) { mDragging = false; @@ -88,7 +88,7 @@ namespace l::ui { auto& layoutArea = container.GetLayoutArea(); ImVec2 move = DragMovement(input.mPrevPos, input.mCurPos, layoutArea.mScale); container.Move(move); - container.Notification(UIContainer_MoveFlag); + container.SetNotification(UIContainer_MoveFlag); if (input.mStopped) { mMoving = false; @@ -109,7 +109,7 @@ namespace l::ui { float size = 3.0f * layoutArea.mScale; if (OverlapScreenRect(input.GetLocalPos(), p, ImVec2(size, size), layoutArea)) { mSourceContainer = &container; - container.Notification(UIContainer_ResizeFlag); + container.SetNotification(UIContainer_ResizeFlag); if (input.mStarted) { mResizing = true; @@ -235,10 +235,10 @@ namespace l::ui { break; } - if (mDebug && !container.GetId().empty()) { + if (mDebug && !container.GetStringId().empty()) { // also render name if it is non empty as debug text - nameStart = container.GetId().data(); - nameEnd = container.GetId().data() + container.GetId().size(); + nameStart = container.GetStringId().data(); + nameEnd = container.GetStringId().data() + container.GetStringId().size(); mDrawList->AddText(p1, color, nameStart, nameEnd); } @@ -315,10 +315,18 @@ namespace l::ui { ImVec2 pT = layoutArea.Transform(pCenter); if (OverlapCircle(input.mCurPos, pT, size.x * layoutArea.mScale)) { - mLinkContainer->Notification(UIContainer_LinkFlag); - mLinkContainer->SetCoParent(&container); + if (!mHandler || mHandler(container.GetId(), mLinkContainer->GetParent()->GetId(), 0, 0, true)) { + mLinkContainer->SetNotification(UIContainer_LinkFlag); + mLinkContainer->SetCoParent(&container); + } + else { + // Failed to connect link + } } else if (mLinkContainer->GetCoParent() == &container){ + if (mHandler) { + mHandler(container.GetId(), mLinkContainer->GetParent()->GetId(), 0, 0, false); + } mLinkContainer->SetCoParent(nullptr); mLinkContainer->ClearNotifications(); } diff --git a/packages/rendering/source/common/ui/UIWindow.cpp b/packages/rendering/source/common/ui/UIWindow.cpp index 444cdee7..e5436d19 100644 --- a/packages/rendering/source/common/ui/UIWindow.cpp +++ b/packages/rendering/source/common/ui/UIWindow.cpp @@ -93,6 +93,7 @@ namespace l::ui { void UIWindow::Show() { if (mOpened) { + ImGui::PushStyleColor(ImGuiCol_WindowBg, mBgColor); if (ImGui::Begin("Test primitive rendering", &mOpened)) { if (mOpened) { mWindowPtr = ImGui::GetCurrentWindowRead(); @@ -109,7 +110,13 @@ namespace l::ui { } } ImGui::End(); + ImGui::PopStyleColor(); } } + void UIWindow::SetBgColor(ImVec4 bgColor) { + mBgColor = bgColor; + } + + } diff --git a/packages/tools/include/tools/nodegraph/NodeGraph.h b/packages/tools/include/tools/nodegraph/NodeGraph.h index e89e1f98..fdc5f952 100644 --- a/packages/tools/include/tools/nodegraph/NodeGraph.h +++ b/packages/tools/include/tools/nodegraph/NodeGraph.h @@ -17,6 +17,7 @@ namespace l::nodegraph { }; enum class InputType { + INPUT_EMPTY, INPUT_NODE, INPUT_CONSTANT, INPUT_VALUE, @@ -42,9 +43,10 @@ namespace l::nodegraph { struct NodeGraphInput { Input mInput; - InputType mInputType = InputType::INPUT_NODE; + InputType mInputType = InputType::INPUT_EMPTY; int8_t mInputFromOutputChannel = 0; + bool HasInput(); float Get(); }; @@ -54,6 +56,8 @@ namespace l::nodegraph { virtual ~NodeGraphBase() = default; virtual void Reset(); + void SetId(int32_t id) { mId = id; } + int32_t GetId() const { return mId; } void Update(); @@ -62,10 +66,12 @@ namespace l::nodegraph { virtual float Get(int8_t outputChannel); - virtual void SetInput(int8_t inputChannel, NodeGraphBase& source, int8_t sourceOutputChannel = 0); - virtual void SetInput(int8_t inputChannel, NodeGraphGroup& source, int8_t sourceOutputChannel = 0, bool useSourceInternalInput = true); - virtual void SetInput(int8_t inputChannel, float constant); - virtual void SetInput(int8_t inputChannel, float* floatPtr); + virtual bool ClearInput(int8_t inputChannel); + + virtual bool SetInput(int8_t inputChannel, NodeGraphBase& source, int8_t sourceOutputChannel = 0); + virtual bool SetInput(int8_t inputChannel, NodeGraphGroup& source, int8_t sourceOutputChannel = 0, bool useSourceInternalInput = true); + virtual bool SetInput(int8_t inputChannel, float constant); + virtual bool SetInput(int8_t inputChannel, float* floatPtr); protected: void PreUpdate(); virtual void ProcessOperation(); @@ -74,6 +80,7 @@ namespace l::nodegraph { std::vector mInputs; std::vector mOutputs; + int32_t mId; std::string mName; }; @@ -165,6 +172,10 @@ namespace l::nodegraph { NodeGraphBase& GetInputNode(); NodeGraphBase& GetOutputNode(); + NodeGraphBase* GetNode(int32_t id); + + void AddNode(); + void Update(); protected: NodeGraph mInputNode; diff --git a/packages/tools/source/common/nodegraph/NodeGraph.cpp b/packages/tools/source/common/nodegraph/NodeGraph.cpp index c2da42a4..f916f935 100644 --- a/packages/tools/source/common/nodegraph/NodeGraph.cpp +++ b/packages/tools/source/common/nodegraph/NodeGraph.cpp @@ -62,37 +62,68 @@ namespace l::nodegraph { return mOutputs.at(outputChannel).mOutput; } - void NodeGraphBase::SetInput(int8_t inputChannel, NodeGraphBase& source, int8_t sourceOutputChannel) { - ASSERT(IsValidInOutNum(inputChannel, mInputs.size())); - Input input; - input.mInputNode = &source; - mInputs.at(inputChannel) = NodeGraphInput{ std::move(input), InputType::INPUT_NODE, sourceOutputChannel }; + bool NodeGraphBase::ClearInput(int8_t inputChannel) { + auto& input = mInputs.at(inputChannel); + if (!IsValidInOutNum(inputChannel, mInputs.size()) || !input.HasInput()) { + return false; + } + input.mInputType = InputType::INPUT_EMPTY; + input.mInput.mInputNode = nullptr; + input.mInputFromOutputChannel = 0; + return true; + } + + bool NodeGraphBase::SetInput(int8_t inputChannel, NodeGraphBase& source, int8_t sourceOutputChannel) { + auto& input = mInputs.at(inputChannel); + if (!IsValidInOutNum(inputChannel, mInputs.size()) || input.HasInput()) { + return false; + } + + Input newInput; + newInput.mInputNode = &source; + input = NodeGraphInput{ std::move(newInput), InputType::INPUT_NODE, sourceOutputChannel }; + return true; } - void NodeGraphBase::SetInput(int8_t inputChannel, NodeGraphGroup& source, int8_t sourceOutputChannel, bool useSourceInternalInput) { - ASSERT(IsValidInOutNum(inputChannel, mInputs.size())); - Input input; + bool NodeGraphBase::SetInput(int8_t inputChannel, NodeGraphGroup& source, int8_t sourceOutputChannel, bool useSourceInternalInput) { + auto& input = mInputs.at(inputChannel); + if (!IsValidInOutNum(inputChannel, mInputs.size()) || input.HasInput()){ + return false; + } + + Input newInput; if (useSourceInternalInput) { - input.mInputNode = &source.GetInputNode(); + newInput.mInputNode = &source.GetInputNode(); } else { - input.mInputNode = &source.GetOutputNode(); + newInput.mInputNode = &source.GetOutputNode(); } - mInputs.at(inputChannel) = NodeGraphInput{ std::move(input), InputType::INPUT_NODE, sourceOutputChannel }; + input = NodeGraphInput{ std::move(newInput), InputType::INPUT_NODE, sourceOutputChannel }; + return true; } - void NodeGraphBase::SetInput(int8_t inputChannel, float constant) { - ASSERT(IsValidInOutNum(inputChannel, mInputs.size())); - Input input; - input.mInputFloatConstant = constant; - mInputs.at(inputChannel) = NodeGraphInput{ std::move(input), InputType::INPUT_CONSTANT, 0 }; + bool NodeGraphBase::SetInput(int8_t inputChannel, float constant) { + auto& input = mInputs.at(inputChannel); + if (!IsValidInOutNum(inputChannel, mInputs.size()) || input.HasInput()) { + return false; + } + + Input newInput; + newInput.mInputFloatConstant = constant; + input = NodeGraphInput{ std::move(newInput), InputType::INPUT_CONSTANT, 0 }; + return true; } - void NodeGraphBase::SetInput(int8_t inputChannel, float* floatPtr) { - ASSERT(IsValidInOutNum(inputChannel, mInputs.size())); - Input input; - input.mInputFloat = floatPtr; - mInputs.at(inputChannel) = NodeGraphInput{ std::move(input), InputType::INPUT_VALUE, 0 }; + bool NodeGraphBase::SetInput(int8_t inputChannel, float* floatPtr) { + auto& input = mInputs.at(inputChannel); + if (!IsValidInOutNum(inputChannel, mInputs.size()) || input.HasInput()) { + return false; + } + + Input newInput; + newInput.mInputFloat = floatPtr; + input = NodeGraphInput{ std::move(newInput), InputType::INPUT_VALUE, 0 }; + return true; } void GraphOp::SetNumInputs(int8_t numInputs) { @@ -117,20 +148,40 @@ namespace l::nodegraph { } } + bool NodeGraphInput::HasInput() { + switch (mInputType) { + case InputType::INPUT_NODE: + if (mInput.mInputNode != nullptr) { + return true; + } + break; + case InputType::INPUT_CONSTANT: + return true; + break; + case InputType::INPUT_VALUE: + return mInput.mInputFloat != nullptr; + break; + case InputType::INPUT_EMPTY: + break; + } + return false; + } + float NodeGraphInput::Get() { - if (mInputType == InputType::INPUT_NODE) { + switch (mInputType) { + case InputType::INPUT_NODE: if (mInput.mInputNode != nullptr) { return mInput.mInputNode->Get(mInputFromOutputChannel); } - } - else if (mInputType == InputType::INPUT_CONSTANT) { + break; + case InputType::INPUT_CONSTANT: return mInput.mInputFloatConstant; - } - else if (mInputType == InputType::INPUT_VALUE) { + break; + case InputType::INPUT_VALUE: return *mInput.mInputFloat; - } - else { - // error + break; + case InputType::INPUT_EMPTY: + break; } return 0.0f; } @@ -177,6 +228,19 @@ namespace l::nodegraph { return mInputNode; } + NodeGraphBase* NodeGraphGroup::GetNode(int32_t id) { + auto it = std::find_if(mNodes.begin(), mNodes.end(), [&](const std::unique_ptr& node) { + if (node->GetId() == id) { + return true; + } + return false; + }); + if (it != mNodes.end()) { + return it->get(); + } + return nullptr; + } + NodeGraphBase& NodeGraphGroup::GetOutputNode() { return mOutputNode; } From e9cb5222bedcb4b4b18f338a7c72ad1b585d1e87 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Mon, 26 Aug 2024 06:18:29 +0200 Subject: [PATCH 058/125] Fix init order. --- packages/rendering/include/rendering/ui/UIContainer.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/rendering/include/rendering/ui/UIContainer.h b/packages/rendering/include/rendering/ui/UIContainer.h index 86e5a646..63b85a97 100644 --- a/packages/rendering/include/rendering/ui/UIContainer.h +++ b/packages/rendering/include/rendering/ui/UIContainer.h @@ -281,8 +281,11 @@ namespace l::ui { class UIContainer { public: UIContainer(uint32_t flags = 0, UIRenderType renderType = UIRenderType::Rect, UIAlignH alignH = UIAlignH::Left, UIAlignV alignV = UIAlignV::Top, UILayoutH layoutH = UILayoutH::Fixed, UILayoutV layoutV = UILayoutV::Fixed) : + mId(0), mConfigFlags(flags), - mId(0) + mNotificationFlags(0), + mParent(nullptr), + mCoParent(nullptr) { mDisplayArea.mRender.mType = renderType; mDisplayArea.mMargin = 3.0f; From 512a1464f73e5ac999f4cb02f93d65f5e8d0d9c4 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Mon, 26 Aug 2024 19:21:32 +0200 Subject: [PATCH 059/125] Make both node graph and ui container work with id's and channels to be compatible. --- .../include/rendering/ui/UIContainer.h | 45 +++++++++---------- .../include/rendering/ui/UIVisitors.h | 6 +-- .../source/common/ui/UIContainer.cpp | 20 ++++++--- .../rendering/source/common/ui/UIVisitors.cpp | 20 +++++---- .../tools/include/tools/nodegraph/NodeGraph.h | 36 ++++++++------- .../tools/nodegraph/NodeGraphOperations.h | 36 +++++++-------- .../source/common/nodegraph/NodeGraph.cpp | 44 +++++++++++++----- 7 files changed, 120 insertions(+), 87 deletions(-) diff --git a/packages/rendering/include/rendering/ui/UIContainer.h b/packages/rendering/include/rendering/ui/UIContainer.h index 63b85a97..3d2ab363 100644 --- a/packages/rendering/include/rendering/ui/UIContainer.h +++ b/packages/rendering/include/rendering/ui/UIContainer.h @@ -245,7 +245,6 @@ namespace l::ui { class UIDraw; - template>> class UIHandle { public: UIHandle() : mId(0), mContainer(nullptr) {}; @@ -256,24 +255,29 @@ namespace l::ui { std::string mStringId; UIContainer* mContainer = nullptr; - std::string_view id() { + std::string_view GetStringId() const { return mStringId; } - void reset() { + void Reset() { mStringId.clear(); mContainer = nullptr; } - UIContainer* get() { + bool IsValid() const { + return mContainer != nullptr; + } + + UIContainer* Get() const { return mContainer; } - T* operator->() { - return reinterpret_cast(mContainer); + UIContainer* operator->() { + return mContainer; } - T& ref() { + template>> + T& Ref() { return &reinterpret_cast(mContainer); } }; @@ -282,6 +286,7 @@ namespace l::ui { public: UIContainer(uint32_t flags = 0, UIRenderType renderType = UIRenderType::Rect, UIAlignH alignH = UIAlignH::Left, UIAlignV alignV = UIAlignV::Top, UILayoutH layoutH = UILayoutH::Fixed, UILayoutV layoutV = UILayoutV::Fixed) : mId(0), + mSubId(0), mConfigFlags(flags), mNotificationFlags(0), mParent(nullptr), @@ -300,23 +305,12 @@ namespace l::ui { virtual bool Accept(UIVisitor& visitor, const InputState& input, UITraversalMode mode = UITraversalMode::BFS); virtual void Add(UIContainer* container, int32_t i = -1); - template - void Add(UIHandle& handle, int32_t i = -1) { - Add(handle.get(), i); + void Add(UIHandle& handle, int32_t i = -1) { + Add(handle.Get(), i); } virtual void Remove(int32_t i); - - template - void Remove(UIHandle& handle) { - for (auto it = mContent.begin(); it != mContent.end();it++) { - auto containerPtr = *it; - if (containerPtr == handle.get()) { - mContent.erase(it); - break; - } - } - } + virtual void Remove(const UIHandle& handle); void SetNotification(uint32_t flag) { mNotificationFlags |= flag; } void ClearNotifications() { mNotificationFlags = 0; } @@ -342,6 +336,7 @@ namespace l::ui { std::string_view GetDisplayName() { return mDisplayName; } std::string_view GetStringId() { return mStringId; } int32_t GetId() { return mId; } + int32_t GetSubId() { return mSubId; } void SetColor(ImVec4 color) { mDisplayArea.mRender.mColor = ImColor(color); } void SetScale(float scale) {mDisplayArea.mScale = scale;} @@ -351,7 +346,8 @@ namespace l::ui { void SetLayoutSize(ImVec2 s) {mLayoutArea.mSize = s;} void SetDisplayName(std::string_view displayName) {mDisplayName = displayName;} void SetStringId(std::string_view id) {mStringId = id;} - void SetId(int32_t id) {mId = id;} + void SetId(int32_t id, int32_t subId = 0) { mId = id; mSubId = subId; } + void SetContainerArea(const ContainerArea& area) {mDisplayArea = area;} void SetLayoutArea(const ContainerArea& transformedLayoutArea) {mLayoutArea = transformedLayoutArea;} void SetParent(UIContainer* parent) {mParent = parent;} @@ -360,6 +356,7 @@ namespace l::ui { void DebugLog() { LOG(LogDebug) << "UIContainer: " << mDisplayName << ", [" << mDisplayArea.mScale << "][" << mDisplayArea.mPosition.x << ", " << mDisplayArea.mPosition.y << "][" << mDisplayArea.mSize.x << ", " << mDisplayArea.mSize.y << "]"; } protected: int32_t mId = 0; + int32_t mSubId = 0; std::string mStringId; std::string mDisplayName; uint32_t mConfigFlags = 0; // Active visitor flags @@ -403,8 +400,8 @@ namespace l::ui { UICreator() = default; ~UICreator() = default; - UIHandle CreateContainer(uint32_t flags, UIRenderType renderType = UIRenderType::Rect, UIAlignH alignH = UIAlignH::Left, UIAlignV alignV = UIAlignV::Top, UILayoutH layoutH = UILayoutH::Fixed, UILayoutV layoutV = UILayoutV::Fixed); - UIHandle CreateSplit(uint32_t flags, UIRenderType renderType, UISplitMode splitMode = UISplitMode::AppendV, UILayoutH layoutH = UILayoutH::Fixed, UILayoutV layoutV = UILayoutV::Fixed); + UIHandle CreateContainer(uint32_t flags, UIRenderType renderType = UIRenderType::Rect, UIAlignH alignH = UIAlignH::Left, UIAlignV alignV = UIAlignV::Top, UILayoutH layoutH = UILayoutH::Fixed, UILayoutV layoutV = UILayoutV::Fixed); + UIHandle CreateSplit(uint32_t flags, UIRenderType renderType, UISplitMode splitMode = UISplitMode::AppendV, UILayoutH layoutH = UILayoutH::Fixed, UILayoutV layoutV = UILayoutV::Fixed); protected: std::unordered_map> mContainers; diff --git a/packages/rendering/include/rendering/ui/UIVisitors.h b/packages/rendering/include/rendering/ui/UIVisitors.h index 216dd4ef..078e07b0 100644 --- a/packages/rendering/include/rendering/ui/UIVisitors.h +++ b/packages/rendering/include/rendering/ui/UIVisitors.h @@ -66,7 +66,7 @@ namespace l::ui { class UILinkIO : public UIVisitor { public: - using HandlerFunctionType = bool(int32_t, int32_t, int8_t, int8_t, bool); + using HandlerFunctionType = bool(int32_t, int32_t, int32_t, int32_t, bool); virtual bool Active(UIContainer& container, const InputState& input); @@ -81,11 +81,11 @@ namespace l::ui { protected: bool mDragging = false; - bool mPossibleLinkImminent = false; - UIHandle mLinkContainer; + UIHandle mLinkContainer; UICreator* mCreator = nullptr; std::function mHandler; }; + } diff --git a/packages/rendering/source/common/ui/UIContainer.cpp b/packages/rendering/source/common/ui/UIContainer.cpp index c40cdcb9..6a3cfc5d 100644 --- a/packages/rendering/source/common/ui/UIContainer.cpp +++ b/packages/rendering/source/common/ui/UIContainer.cpp @@ -4,7 +4,7 @@ namespace l::ui { - UIHandle UICreator::CreateContainer(uint32_t flags, UIRenderType renderType, UIAlignH alignH, UIAlignV alignV, UILayoutH layoutH, UILayoutV layoutV) { + UIHandle UICreator::CreateContainer(uint32_t flags, UIRenderType renderType, UIAlignH alignH, UIAlignV alignV, UILayoutH layoutH, UILayoutV layoutV) { std::unique_ptr container = std::make_unique(flags, renderType, alignH, alignV, layoutH, layoutV); auto id = CreateUniqueId(); @@ -13,10 +13,10 @@ namespace l::ui { container->SetStringId(stringId); mContainers.insert({ id, std::move(container) }); - return UIHandle{ id, stringId, mContainers.at(id).get() }; + return UIHandle{ id, stringId, mContainers.at(id).get() }; } - UIHandle UICreator::CreateSplit(uint32_t flags, UIRenderType renderType, UISplitMode splitMode, UILayoutH layoutH, UILayoutV layoutV) { + UIHandle UICreator::CreateSplit(uint32_t flags, UIRenderType renderType, UISplitMode splitMode, UILayoutH layoutH, UILayoutV layoutV) { std::unique_ptr container = std::make_unique(flags, renderType, splitMode, layoutH, layoutV); auto id = CreateUniqueId(); @@ -25,7 +25,7 @@ namespace l::ui { container->SetStringId(stringId); mContainers.insert({ id, std::move(container) }); - return UIHandle{ id, stringId, mContainers.at(id).get() }; + return UIHandle{ id, stringId, mContainers.at(id).get() }; } ImVec2 DragMovement(const ImVec2& prevPos, const ImVec2& curPos, float curScale) { @@ -81,6 +81,16 @@ namespace l::ui { mContent.erase(mContent.begin() + i); } + void UIContainer::Remove(const UIHandle& handle) { + for (auto it = mContent.begin(); it != mContent.end(); it++) { + auto containerPtr = *it; + if (containerPtr == handle.Get()) { + mContent.erase(it); + break; + } + } + } + void UIContainer::Move(ImVec2 localChange) { mDisplayArea.mPosition.x += localChange.x; mDisplayArea.mPosition.y += localChange.y; @@ -272,7 +282,7 @@ namespace l::ui { } int32_t CreateUniqueId() { - static int32_t mId = 0; + static int32_t mId = 1; return mId++; } diff --git a/packages/rendering/source/common/ui/UIVisitors.cpp b/packages/rendering/source/common/ui/UIVisitors.cpp index c9c6e1f5..634849a8 100644 --- a/packages/rendering/source/common/ui/UIVisitors.cpp +++ b/packages/rendering/source/common/ui/UIVisitors.cpp @@ -275,7 +275,7 @@ namespace l::ui { bool UILinkIO::Visit(UIContainer& container, const InputState& input) { // Create link at from a clicked output container - if (container.HasConfigFlag(UIContainer_OutputFlag) && !mDragging && input.mStarted && mLinkContainer.get() == nullptr) { + if (container.HasConfigFlag(UIContainer_OutputFlag) && !mDragging && input.mStarted && mLinkContainer.Get() == nullptr) { ImVec2 pCenter = container.GetPosition(); ImVec2 size = container.GetSize(); auto& layoutArea = container.GetLayoutArea(); @@ -288,7 +288,7 @@ namespace l::ui { return true; } } - if (container.HasConfigFlag(UIContainer_LinkFlag) && !mDragging && input.mStarted && mLinkContainer.get() == nullptr && container.GetCoParent() != nullptr) { + if (container.HasConfigFlag(UIContainer_LinkFlag) && !mDragging && input.mStarted && mLinkContainer.Get() == nullptr && container.GetCoParent() != nullptr) { ImVec2 pCenter = container.GetCoParent()->GetPosition(); ImVec2 size = container.GetCoParent()->GetSize(); ImVec2 pT = container.GetCoParent()->GetLayoutArea().Transform(pCenter); @@ -299,7 +299,7 @@ namespace l::ui { } } - if (mDragging && mLinkContainer.get() != nullptr && container.HasConfigFlag(UIContainer_LinkFlag) && mLinkContainer.get() == &container) { + if (mDragging && mLinkContainer.Get() != nullptr && container.HasConfigFlag(UIContainer_LinkFlag) && mLinkContainer.Get() == &container) { // On the newly created link container, drag the end point along the mouse movement auto& layoutArea = container.GetLayoutArea(); @@ -307,7 +307,7 @@ namespace l::ui { mLinkContainer->Move(move); } - if (mDragging && mLinkContainer.get() != nullptr && container.HasConfigFlag(UIContainer_InputFlag)) { + if (mDragging && mLinkContainer.Get() != nullptr && container.HasConfigFlag(UIContainer_InputFlag)) { ImVec2 pCenter = container.GetPosition(); ImVec2 size = container.GetSize(); auto& layoutArea = container.GetLayoutArea(); @@ -315,7 +315,7 @@ namespace l::ui { ImVec2 pT = layoutArea.Transform(pCenter); if (OverlapCircle(input.mCurPos, pT, size.x * layoutArea.mScale)) { - if (!mHandler || mHandler(container.GetId(), mLinkContainer->GetParent()->GetId(), 0, 0, true)) { + if (!mHandler || mHandler(container.GetId(), mLinkContainer->GetParent()->GetId(), container.GetSubId(), mLinkContainer->GetParent()->GetSubId(), true)) { mLinkContainer->SetNotification(UIContainer_LinkFlag); mLinkContainer->SetCoParent(&container); } @@ -323,9 +323,9 @@ namespace l::ui { // Failed to connect link } } - else if (mLinkContainer->GetCoParent() == &container){ + else if (mLinkContainer->GetCoParent() == &container) { if (mHandler) { - mHandler(container.GetId(), mLinkContainer->GetParent()->GetId(), 0, 0, false); + mHandler(container.GetId(), mLinkContainer->GetParent()->GetId(), container.GetSubId(), mLinkContainer->GetParent()->GetSubId(), false); } mLinkContainer->SetCoParent(nullptr); mLinkContainer->ClearNotifications(); @@ -335,15 +335,17 @@ namespace l::ui { mLinkContainer->ClearNotifications(); if (mLinkContainer->GetCoParent() != nullptr) { mDragging = false; - mLinkContainer.reset(); + mLinkContainer.Reset(); } else { mLinkContainer->GetParent()->Remove(mLinkContainer); mDragging = false; - mLinkContainer.reset(); + mLinkContainer.Reset(); } } } return false; } + + } diff --git a/packages/tools/include/tools/nodegraph/NodeGraph.h b/packages/tools/include/tools/nodegraph/NodeGraph.h index fdc5f952..873a5006 100644 --- a/packages/tools/include/tools/nodegraph/NodeGraph.h +++ b/packages/tools/include/tools/nodegraph/NodeGraph.h @@ -7,6 +7,7 @@ #include #include #include +#include namespace l::nodegraph { @@ -56,10 +57,10 @@ namespace l::nodegraph { virtual ~NodeGraphBase() = default; virtual void Reset(); - void SetId(int32_t id) { mId = id; } - int32_t GetId() const { return mId; } + virtual void SetId(int32_t id) { mId = id; } + virtual int32_t GetId() const { return mId; } - void Update(); + virtual void Update(); virtual void SetNumInputs(int8_t numInputs); virtual void SetNumOutputs(int8_t outputCount); @@ -84,14 +85,14 @@ namespace l::nodegraph { std::string mName; }; - class GraphOp { + class NodeGraphOp { public: - GraphOp(int8_t numInputs = 1, int8_t numOutputs = 1) : + NodeGraphOp(int8_t numInputs = 1, int8_t numOutputs = 1) : mNumInputs(numInputs), mNumOutputs(numOutputs) {} - virtual ~GraphOp() = default; + virtual ~NodeGraphOp() = default; virtual void Reset() {} virtual void Process(std::vector& mInputs, std::vector& outputs) = 0; @@ -104,17 +105,17 @@ namespace l::nodegraph { int8_t mNumOutputs; }; - class GraphDataCopy : public GraphOp { + class GraphDataCopy : public NodeGraphOp { public: GraphDataCopy(int8_t numChannels = 1) : - GraphOp(numChannels, numChannels) + NodeGraphOp(numChannels, numChannels) {} virtual ~GraphDataCopy() = default; void Process(std::vector& inputs, std::vector& outputs) override; }; - template>> + template>> class NodeGraph : public NodeGraphBase { public: NodeGraph(std::string_view name = "") : @@ -124,24 +125,22 @@ namespace l::nodegraph { SetNumOutputs(mOperation.GetNumOutputs()); } - virtual ~NodeGraph() = default; - - void SetNumInputs(int8_t numInputs) { + virtual void SetNumInputs(int8_t numInputs) { mInputs.resize(numInputs); mOperation.SetNumInputs(numInputs); } - void SetNumOutputs(int8_t numOutputs) { + virtual void SetNumOutputs(int8_t numOutputs) { mOutputs.resize(numOutputs); mOperation.SetNumOutputs(numOutputs); } - void Reset() override { + virtual void Reset() override { NodeGraphBase::Reset(); mOperation.Reset(); } - void ProcessOperation() override { + virtual void ProcessOperation() override { NodeGraphBase::ProcessOperation(); mOperation.Process(mInputs, mOutputs); } @@ -173,8 +172,13 @@ namespace l::nodegraph { NodeGraphBase& GetOutputNode(); NodeGraphBase* GetNode(int32_t id); + bool RemoveNode(int32_t id); - void AddNode(); + template>> + l::nodegraph::NodeGraphBase* NewNode() { + mNodes.push_back(std::make_unique>()); + return mNodes.back().get(); + } void Update(); protected: diff --git a/packages/tools/include/tools/nodegraph/NodeGraphOperations.h b/packages/tools/include/tools/nodegraph/NodeGraphOperations.h index 30d9d288..10734622 100644 --- a/packages/tools/include/tools/nodegraph/NodeGraphOperations.h +++ b/packages/tools/include/tools/nodegraph/NodeGraphOperations.h @@ -13,48 +13,48 @@ namespace l::nodegraph { /* Mathematical operations */ - class GraphNumericAdd : public GraphOp { + class GraphNumericAdd : public NodeGraphOp { public: GraphNumericAdd() : - GraphOp(2, 1) + NodeGraphOp(2, 1) {} virtual ~GraphNumericAdd() = default; void Process(std::vector& inputs, std::vector& outputs) override; }; - class GraphNumericMultiply : public GraphOp { + class GraphNumericMultiply : public NodeGraphOp { public: GraphNumericMultiply() : - GraphOp(2, 1) + NodeGraphOp(2, 1) {} virtual ~GraphNumericMultiply() = default; void Process(std::vector& inputs, std::vector& outputs) override; }; - class GraphNumericSubtract : public GraphOp { + class GraphNumericSubtract : public NodeGraphOp { public: GraphNumericSubtract() : - GraphOp(2, 1) + NodeGraphOp(2, 1) {} virtual ~GraphNumericSubtract() = default; void Process(std::vector& inputs, std::vector& outputs) override; }; - class GraphNumericNegate : public GraphOp { + class GraphNumericNegate : public NodeGraphOp { public: GraphNumericNegate() : - GraphOp(1, 1) + NodeGraphOp(1, 1) {} virtual ~GraphNumericNegate() = default; void Process(std::vector& inputs, std::vector& outputs) override; }; - class GraphNumericIntegral : public GraphOp { + class GraphNumericIntegral : public NodeGraphOp { public: GraphNumericIntegral() : - GraphOp(1, 1) + NodeGraphOp(1, 1) {} virtual ~GraphNumericIntegral() = default; @@ -67,30 +67,30 @@ namespace l::nodegraph { /* Logical operations */ - class GraphLogicalAnd : public GraphOp { + class GraphLogicalAnd : public NodeGraphOp { public: GraphLogicalAnd() : - GraphOp(2, 1) + NodeGraphOp(2, 1) {} virtual ~GraphLogicalAnd() = default; void Process(std::vector& inputs, std::vector& outputs) override; }; - class GraphLogicalOr : public GraphOp { + class GraphLogicalOr : public NodeGraphOp { public: GraphLogicalOr() : - GraphOp(2, 1) + NodeGraphOp(2, 1) {} virtual ~GraphLogicalOr() = default; void Process(std::vector& inputs, std::vector& outputs) override; }; - class GraphLogicalXor : public GraphOp { + class GraphLogicalXor : public NodeGraphOp { public: GraphLogicalXor() : - GraphOp(2, 1) + NodeGraphOp(2, 1) {} virtual ~GraphLogicalXor() = default; @@ -99,10 +99,10 @@ namespace l::nodegraph { /* Stateful filtering operations */ - class GraphFilterLowpass : public GraphOp { + class GraphFilterLowpass : public NodeGraphOp { public: GraphFilterLowpass() : - GraphOp(3, 1) + NodeGraphOp(3, 1) {} virtual ~GraphFilterLowpass() = default; diff --git a/packages/tools/source/common/nodegraph/NodeGraph.cpp b/packages/tools/source/common/nodegraph/NodeGraph.cpp index f916f935..ee29fe78 100644 --- a/packages/tools/source/common/nodegraph/NodeGraph.cpp +++ b/packages/tools/source/common/nodegraph/NodeGraph.cpp @@ -8,7 +8,13 @@ namespace l::nodegraph { return inoutNum >= 0 && inoutSize < 256u && inoutNum < static_cast(inoutSize); } + int32_t CreateUniqueId() { + static int32_t id = 1; + return id++; + } + NodeGraphBase::NodeGraphBase(std::string_view name) : + mId(CreateUniqueId()), mName(name) { mInputs.resize(1); @@ -75,7 +81,9 @@ namespace l::nodegraph { bool NodeGraphBase::SetInput(int8_t inputChannel, NodeGraphBase& source, int8_t sourceOutputChannel) { auto& input = mInputs.at(inputChannel); - if (!IsValidInOutNum(inputChannel, mInputs.size()) || input.HasInput()) { + if (!IsValidInOutNum(sourceOutputChannel, source.mOutputs.size()) || + !IsValidInOutNum(inputChannel, mInputs.size()) || + input.HasInput()) { return false; } @@ -87,7 +95,9 @@ namespace l::nodegraph { bool NodeGraphBase::SetInput(int8_t inputChannel, NodeGraphGroup& source, int8_t sourceOutputChannel, bool useSourceInternalInput) { auto& input = mInputs.at(inputChannel); - if (!IsValidInOutNum(inputChannel, mInputs.size()) || input.HasInput()){ + if (!IsValidInOutNum(sourceOutputChannel, source.GetOutputNode().mOutputs.size()) || + !IsValidInOutNum(inputChannel, mInputs.size()) || + input.HasInput()){ return false; } @@ -126,19 +136,19 @@ namespace l::nodegraph { return true; } - void GraphOp::SetNumInputs(int8_t numInputs) { + void NodeGraphOp::SetNumInputs(int8_t numInputs) { mNumInputs = numInputs; } - void GraphOp::SetNumOutputs(int8_t numOutputs) { + void NodeGraphOp::SetNumOutputs(int8_t numOutputs) { mNumOutputs = numOutputs; } - int8_t GraphOp::GetNumInputs() { + int8_t NodeGraphOp::GetNumInputs() { return mNumInputs; } - int8_t GraphOp::GetNumOutputs() { + int8_t NodeGraphOp::GetNumOutputs() { return mNumOutputs; } @@ -228,12 +238,16 @@ namespace l::nodegraph { return mInputNode; } + NodeGraphBase& NodeGraphGroup::GetOutputNode() { + return mOutputNode; + } + NodeGraphBase* NodeGraphGroup::GetNode(int32_t id) { auto it = std::find_if(mNodes.begin(), mNodes.end(), [&](const std::unique_ptr& node) { - if (node->GetId() == id) { - return true; - } - return false; + if (node->GetId() == id) { + return true; + } + return false; }); if (it != mNodes.end()) { return it->get(); @@ -241,8 +255,14 @@ namespace l::nodegraph { return nullptr; } - NodeGraphBase& NodeGraphGroup::GetOutputNode() { - return mOutputNode; + bool NodeGraphGroup::RemoveNode(int32_t id) { + auto count = std::erase_if(mNodes, [&](const std::unique_ptr& node) { + if (node->GetId() == id) { + return true; + } + return false; + }); + return count > 0 ? true : false; } void NodeGraphGroup::Update() { From f292d0b24e57e8bc28b7dddf704d8ef4b52d27c3 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Mon, 26 Aug 2024 19:40:04 +0200 Subject: [PATCH 060/125] Fix a misunderstanding with input nodes in groups as their output are the input of the group's nodes but when used as input to an external node, you would use the groups output node obviously. Should improve the semantics on this. --- .../tools/source/common/nodegraph/NodeGraph.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/packages/tools/source/common/nodegraph/NodeGraph.cpp b/packages/tools/source/common/nodegraph/NodeGraph.cpp index ee29fe78..3bde15ef 100644 --- a/packages/tools/source/common/nodegraph/NodeGraph.cpp +++ b/packages/tools/source/common/nodegraph/NodeGraph.cpp @@ -94,13 +94,6 @@ namespace l::nodegraph { } bool NodeGraphBase::SetInput(int8_t inputChannel, NodeGraphGroup& source, int8_t sourceOutputChannel, bool useSourceInternalInput) { - auto& input = mInputs.at(inputChannel); - if (!IsValidInOutNum(sourceOutputChannel, source.GetOutputNode().mOutputs.size()) || - !IsValidInOutNum(inputChannel, mInputs.size()) || - input.HasInput()){ - return false; - } - Input newInput; if (useSourceInternalInput) { newInput.mInputNode = &source.GetInputNode(); @@ -108,6 +101,14 @@ namespace l::nodegraph { else { newInput.mInputNode = &source.GetOutputNode(); } + + auto& input = mInputs.at(inputChannel); + if (!IsValidInOutNum(sourceOutputChannel, newInput.mInputNode->mOutputs.size()) || + !IsValidInOutNum(inputChannel, mInputs.size()) || + input.HasInput()){ + return false; + } + input = NodeGraphInput{ std::move(newInput), InputType::INPUT_NODE, sourceOutputChannel }; return true; } From d46b1dab004b8d08068a444ede300792eab4deec Mon Sep 17 00:00:00 2001 From: lnd3 Date: Tue, 27 Aug 2024 09:43:58 +0200 Subject: [PATCH 061/125] Add getter. --- packages/tools/include/tools/nodegraph/NodeGraph.h | 3 +++ packages/tools/source/common/nodegraph/NodeGraph.cpp | 8 ++++++++ 2 files changed, 11 insertions(+) diff --git a/packages/tools/include/tools/nodegraph/NodeGraph.h b/packages/tools/include/tools/nodegraph/NodeGraph.h index 873a5006..8591de8a 100644 --- a/packages/tools/include/tools/nodegraph/NodeGraph.h +++ b/packages/tools/include/tools/nodegraph/NodeGraph.h @@ -65,6 +65,9 @@ namespace l::nodegraph { virtual void SetNumInputs(int8_t numInputs); virtual void SetNumOutputs(int8_t outputCount); + virtual int8_t GetNumInputs(); + virtual int8_t GetNumOutputs(); + virtual float Get(int8_t outputChannel); virtual bool ClearInput(int8_t inputChannel); diff --git a/packages/tools/source/common/nodegraph/NodeGraph.cpp b/packages/tools/source/common/nodegraph/NodeGraph.cpp index 3bde15ef..8259e837 100644 --- a/packages/tools/source/common/nodegraph/NodeGraph.cpp +++ b/packages/tools/source/common/nodegraph/NodeGraph.cpp @@ -29,6 +29,14 @@ namespace l::nodegraph { mOutputs.resize(outputCount); } + int8_t NodeGraphBase::GetNumInputs() { + return static_cast(mInputs.size()); + } + + int8_t NodeGraphBase::GetNumOutputs() { + return static_cast(mOutputs.size()); + } + void NodeGraphBase::Reset() { for (auto& link : mInputs) { if (link.mInputType == InputType::INPUT_NODE && link.mInput.mInputNode != nullptr) { From 92466171a35383f5efbeb2a0053d7f39624901c4 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Tue, 27 Aug 2024 15:03:49 +0200 Subject: [PATCH 062/125] Add default node io names and functions to set custom io names. --- .../tools/include/tools/nodegraph/NodeGraph.h | 38 ++++++++++++++++++- .../tools/nodegraph/NodeGraphOperations.h | 11 ++++++ .../source/common/nodegraph/NodeGraph.cpp | 33 ++++++++++++---- .../common/nodegraph/NodeGraphOperations.cpp | 2 - packages/tools/tests/common/NodeGraphTest.cpp | 22 +++++------ 5 files changed, 84 insertions(+), 22 deletions(-) diff --git a/packages/tools/include/tools/nodegraph/NodeGraph.h b/packages/tools/include/tools/nodegraph/NodeGraph.h index 8591de8a..231f1923 100644 --- a/packages/tools/include/tools/nodegraph/NodeGraph.h +++ b/packages/tools/include/tools/nodegraph/NodeGraph.h @@ -29,6 +29,7 @@ namespace l::nodegraph { struct NodeGraphOutput { float mOutput = 0.0f; + std::string mName; }; class NodeGraphBase; @@ -46,6 +47,7 @@ namespace l::nodegraph { Input mInput; InputType mInputType = InputType::INPUT_EMPTY; int8_t mInputFromOutputChannel = 0; + std::string mName; bool HasInput(); float Get(); @@ -70,10 +72,15 @@ namespace l::nodegraph { virtual float Get(int8_t outputChannel); + virtual std::string_view GetInputName(int8_t inputChannel); + virtual std::string_view GetOutputName(int8_t outputChannel); + virtual void SetInputName(int8_t inputChannel, std::string_view name); + virtual void SetOutputName(int8_t outputChannel, std::string_view name); + virtual bool ClearInput(int8_t inputChannel); - virtual bool SetInput(int8_t inputChannel, NodeGraphBase& source, int8_t sourceOutputChannel = 0); - virtual bool SetInput(int8_t inputChannel, NodeGraphGroup& source, int8_t sourceOutputChannel = 0, bool useSourceInternalInput = true); + virtual bool SetInput(int8_t inputChannel, NodeGraphBase& source, int8_t sourceOutputChannel); + virtual bool SetInput(int8_t inputChannel, NodeGraphGroup& source, int8_t sourceOutputChannel, bool nodeIsInsideGroup); virtual bool SetInput(int8_t inputChannel, float constant); virtual bool SetInput(int8_t inputChannel, float* floatPtr); protected: @@ -95,6 +102,9 @@ namespace l::nodegraph { mNumOutputs(numOutputs) {} + std::string defaultInStrings[3] = { "In 1", "In 2", "In 3" }; + std::string defaultOutStrings[3] = { "Out 1", "Out 2", "Out 3" }; + virtual ~NodeGraphOp() = default; virtual void Reset() {} virtual void Process(std::vector& mInputs, std::vector& outputs) = 0; @@ -103,6 +113,14 @@ namespace l::nodegraph { virtual void SetNumOutputs(int8_t numOutputs); int8_t GetNumInputs(); int8_t GetNumOutputs(); + + virtual std::string_view GetInputName(int8_t inputChannel) { + return defaultInStrings[inputChannel]; + }; + virtual std::string_view GetOutputName(int8_t outputChannel) { + return defaultOutStrings[outputChannel]; + } + protected: int8_t mNumInputs; int8_t mNumOutputs; @@ -148,6 +166,22 @@ namespace l::nodegraph { mOperation.Process(mInputs, mOutputs); } + virtual std::string_view GetInputName(int8_t inputChannel) { + auto& customName = mInputs.at(inputChannel).mName; + if (!customName.empty()) { + return customName; + } + return mOperation.GetInputName(inputChannel); + } + + virtual std::string_view GetOutputName(int8_t outputChannel) { + auto& customName = mOutputs.at(outputChannel).mName; + if (!customName.empty()) { + return customName; + } + return mOperation.GetOutputName(outputChannel); + } + protected: T mOperation; }; diff --git a/packages/tools/include/tools/nodegraph/NodeGraphOperations.h b/packages/tools/include/tools/nodegraph/NodeGraphOperations.h index 10734622..af599756 100644 --- a/packages/tools/include/tools/nodegraph/NodeGraphOperations.h +++ b/packages/tools/include/tools/nodegraph/NodeGraphOperations.h @@ -101,6 +101,9 @@ namespace l::nodegraph { class GraphFilterLowpass : public NodeGraphOp { public: + std::string defaultInStrings[3] = { "Cutoff", "Resonance", "Data"}; + std::string defaultOutStrings[1] = { "Out" }; + GraphFilterLowpass() : NodeGraphOp(3, 1) {} @@ -109,6 +112,14 @@ namespace l::nodegraph { void Reset() override; void Process(std::vector& inputs, std::vector& outputs) override; + std::string_view GetInputName(int8_t inputChannel) { + return defaultInStrings[inputChannel]; + } + + std::string_view GetOutputName(int8_t outputChannel) { + return defaultOutStrings[outputChannel]; + } + protected: float mState0 = 0.0f; float mState1 = 0.0f; diff --git a/packages/tools/source/common/nodegraph/NodeGraph.cpp b/packages/tools/source/common/nodegraph/NodeGraph.cpp index 8259e837..d5634ce5 100644 --- a/packages/tools/source/common/nodegraph/NodeGraph.cpp +++ b/packages/tools/source/common/nodegraph/NodeGraph.cpp @@ -101,9 +101,9 @@ namespace l::nodegraph { return true; } - bool NodeGraphBase::SetInput(int8_t inputChannel, NodeGraphGroup& source, int8_t sourceOutputChannel, bool useSourceInternalInput) { + bool NodeGraphBase::SetInput(int8_t inputChannel, NodeGraphGroup& source, int8_t sourceChannel, bool nodeIsInsideGroup) { Input newInput; - if (useSourceInternalInput) { + if (nodeIsInsideGroup) { newInput.mInputNode = &source.GetInputNode(); } else { @@ -111,13 +111,17 @@ namespace l::nodegraph { } auto& input = mInputs.at(inputChannel); - if (!IsValidInOutNum(sourceOutputChannel, newInput.mInputNode->mOutputs.size()) || + if (!IsValidInOutNum(sourceChannel, newInput.mInputNode->mOutputs.size()) || !IsValidInOutNum(inputChannel, mInputs.size()) || input.HasInput()){ return false; } - input = NodeGraphInput{ std::move(newInput), InputType::INPUT_NODE, sourceOutputChannel }; + if (nodeIsInsideGroup) { + source.GetInputNode().SetInputName(sourceChannel, input.mName); + } + + input = NodeGraphInput{ std::move(newInput), InputType::INPUT_NODE, sourceChannel }; return true; } @@ -145,6 +149,22 @@ namespace l::nodegraph { return true; } + std::string_view NodeGraphBase::GetInputName(int8_t inputChannel) { + return mInputs.at(inputChannel).mName; + } + + std::string_view NodeGraphBase::GetOutputName(int8_t outputChannel) { + return mOutputs.at(outputChannel).mName; + } + + void NodeGraphBase::SetInputName(int8_t inputChannel, std::string_view name) { + mInputs.at(inputChannel).mName = name; + } + + void NodeGraphBase::SetOutputName(int8_t outputChannel, std::string_view name) { + mOutputs.at(outputChannel).mName = name; + } + void NodeGraphOp::SetNumInputs(int8_t numInputs) { mNumInputs = numInputs; } @@ -173,13 +193,10 @@ namespace l::nodegraph { if (mInput.mInputNode != nullptr) { return true; } - break; case InputType::INPUT_CONSTANT: return true; - break; case InputType::INPUT_VALUE: return mInput.mInputFloat != nullptr; - break; case InputType::INPUT_EMPTY: break; } @@ -233,10 +250,12 @@ namespace l::nodegraph { void NodeGraphGroup::SetOutput(int8_t outputChannel, NodeGraphBase& source, int8_t sourceOutputChannel) { mOutputNode.SetInput(outputChannel, source, sourceOutputChannel); + mOutputNode.SetOutputName(outputChannel, source.GetOutputName(sourceOutputChannel)); } void NodeGraphGroup::SetOutput(int8_t outputChannel, NodeGraphGroup& source, int8_t sourceOutputChannel) { mOutputNode.SetInput(outputChannel, source, sourceOutputChannel, false); + mOutputNode.SetOutputName(outputChannel, source.GetOutputNode().GetOutputName(sourceOutputChannel)); } float NodeGraphGroup::Get(int8_t outputChannel) { diff --git a/packages/tools/source/common/nodegraph/NodeGraphOperations.cpp b/packages/tools/source/common/nodegraph/NodeGraphOperations.cpp index b7438925..22d40f07 100644 --- a/packages/tools/source/common/nodegraph/NodeGraphOperations.cpp +++ b/packages/tools/source/common/nodegraph/NodeGraphOperations.cpp @@ -90,6 +90,4 @@ namespace l::nodegraph { outputs.at(0).mOutput = -mState1; } - - } diff --git a/packages/tools/tests/common/NodeGraphTest.cpp b/packages/tools/tests/common/NodeGraphTest.cpp index 85068d38..7cf1b6e6 100644 --- a/packages/tools/tests/common/NodeGraphTest.cpp +++ b/packages/tools/tests/common/NodeGraphTest.cpp @@ -59,13 +59,13 @@ TEST(NodeGraph, BasicMathematicalOperations) { node1.SetInput(0, &in1); node1.SetInput(1, 2.3f); - node2.SetInput(0, node1); + node2.SetInput(0, node1, 0); node2.SetInput(1, &in3); // (in1 + in2) * in3 - node3.SetInput(0, node2); + node3.SetInput(0, node2, 0); node3.SetInput(1, &in4); // (in1 + in2) * in3 - in4 - nodeOutput.SetInput(0, node3); // - (in1 + in2) * in3 - in4 + nodeOutput.SetInput(0, node3, 0); // - (in1 + in2) * in3 - in4 nodeOutput.Update(); TEST_FUZZY(nodeOutput.Get(0), -6.9f, 0.0001f, ""); @@ -136,16 +136,16 @@ TEST(NodeGraph, GraphGroups) { group.SetInput(3, &input2); // cutoff - nodeLowpass1.SetInput(0, group, 0); - nodeLowpass2.SetInput(0, group, 0); + nodeLowpass1.SetInput(0, group, 0, true); + nodeLowpass2.SetInput(0, group, 0, true); // resonance - nodeLowpass1.SetInput(1, group, 1); - nodeLowpass2.SetInput(1, group, 1); + nodeLowpass1.SetInput(1, group, 1, true); + nodeLowpass2.SetInput(1, group, 1, true); // left, right - nodeLowpass1.SetInput(2, group, 2); - nodeLowpass2.SetInput(2, group, 3); + nodeLowpass1.SetInput(2, group, 2, true); + nodeLowpass2.SetInput(2, group, 3, true); group.SetOutput(0, nodeLowpass1, 0); group.SetOutput(1, nodeLowpass2, 0); @@ -162,8 +162,8 @@ TEST(NodeGraph, GraphGroups) { copyNode.SetNumInputs(2); copyNode.SetNumOutputs(2); - copyNode.SetInput(0, group2, 0); - copyNode.SetInput(1, group2, 1); + copyNode.SetInput(0, group2, 0, true); + copyNode.SetInput(1, group2, 1, true); group2.SetOutput(0, copyNode, 0); group2.SetOutput(1, copyNode, 1); From 1fa2c235269ce25f4e64534eb2d5a8ad5939ac56 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Tue, 27 Aug 2024 15:15:55 +0200 Subject: [PATCH 063/125] Have a name identifier for a node be defined by its operation. --- .../tools/include/tools/nodegraph/NodeGraph.h | 8 ++++++ .../tools/nodegraph/NodeGraphOperations.h | 28 ++++++++++++++++++- .../source/common/nodegraph/NodeGraph.cpp | 4 +++ 3 files changed, 39 insertions(+), 1 deletion(-) diff --git a/packages/tools/include/tools/nodegraph/NodeGraph.h b/packages/tools/include/tools/nodegraph/NodeGraph.h index 231f1923..ead95ae0 100644 --- a/packages/tools/include/tools/nodegraph/NodeGraph.h +++ b/packages/tools/include/tools/nodegraph/NodeGraph.h @@ -72,6 +72,7 @@ namespace l::nodegraph { virtual float Get(int8_t outputChannel); + virtual std::string_view GetName(); virtual std::string_view GetInputName(int8_t inputChannel); virtual std::string_view GetOutputName(int8_t outputChannel); virtual void SetInputName(int8_t inputChannel, std::string_view name); @@ -120,6 +121,9 @@ namespace l::nodegraph { virtual std::string_view GetOutputName(int8_t outputChannel) { return defaultOutStrings[outputChannel]; } + virtual std::string_view GetName() { + return ""; + } protected: int8_t mNumInputs; @@ -182,6 +186,10 @@ namespace l::nodegraph { return mOperation.GetOutputName(outputChannel); } + virtual std::string_view GetName() { + return mOperation.GetName(); + } + protected: T mOperation; }; diff --git a/packages/tools/include/tools/nodegraph/NodeGraphOperations.h b/packages/tools/include/tools/nodegraph/NodeGraphOperations.h index af599756..81e5a956 100644 --- a/packages/tools/include/tools/nodegraph/NodeGraphOperations.h +++ b/packages/tools/include/tools/nodegraph/NodeGraphOperations.h @@ -20,6 +20,9 @@ namespace l::nodegraph { {} virtual ~GraphNumericAdd() = default; void Process(std::vector& inputs, std::vector& outputs) override; + std::string_view GetName() override { + return "Add"; + } }; class GraphNumericMultiply : public NodeGraphOp { @@ -30,6 +33,9 @@ namespace l::nodegraph { virtual ~GraphNumericMultiply() = default; void Process(std::vector& inputs, std::vector& outputs) override; + std::string_view GetName() override { + return "Multiply"; + } }; class GraphNumericSubtract : public NodeGraphOp { @@ -39,6 +45,9 @@ namespace l::nodegraph { {} virtual ~GraphNumericSubtract() = default; void Process(std::vector& inputs, std::vector& outputs) override; + std::string_view GetName() override { + return "Subtract"; + } }; class GraphNumericNegate : public NodeGraphOp { @@ -49,6 +58,9 @@ namespace l::nodegraph { virtual ~GraphNumericNegate() = default; void Process(std::vector& inputs, std::vector& outputs) override; + std::string_view GetName() override { + return "Negate"; + } }; class GraphNumericIntegral : public NodeGraphOp { @@ -60,6 +72,9 @@ namespace l::nodegraph { virtual ~GraphNumericIntegral() = default; void Reset() override; void Process(std::vector& inputs, std::vector& outputs) override; + std::string_view GetName() override { + return "Integral"; + } protected: float mOutput = 0.0f; @@ -75,6 +90,9 @@ namespace l::nodegraph { virtual ~GraphLogicalAnd() = default; void Process(std::vector& inputs, std::vector& outputs) override; + std::string_view GetName() override { + return "And"; + } }; class GraphLogicalOr : public NodeGraphOp { @@ -85,6 +103,9 @@ namespace l::nodegraph { virtual ~GraphLogicalOr() = default; void Process(std::vector& inputs, std::vector& outputs) override; + std::string_view GetName() override { + return "Or"; + } }; class GraphLogicalXor : public NodeGraphOp { @@ -95,6 +116,9 @@ namespace l::nodegraph { virtual ~GraphLogicalXor() = default; void Process(std::vector& inputs, std::vector& outputs) override; + std::string_view GetName() override { + return "Xor"; + } }; /* Stateful filtering operations */ @@ -119,7 +143,9 @@ namespace l::nodegraph { std::string_view GetOutputName(int8_t outputChannel) { return defaultOutStrings[outputChannel]; } - + std::string_view GetName() override { + return "Lowpass"; + } protected: float mState0 = 0.0f; float mState1 = 0.0f; diff --git a/packages/tools/source/common/nodegraph/NodeGraph.cpp b/packages/tools/source/common/nodegraph/NodeGraph.cpp index d5634ce5..6303a2e3 100644 --- a/packages/tools/source/common/nodegraph/NodeGraph.cpp +++ b/packages/tools/source/common/nodegraph/NodeGraph.cpp @@ -149,6 +149,10 @@ namespace l::nodegraph { return true; } + std::string_view NodeGraphBase::GetName() { + return mName; + } + std::string_view NodeGraphBase::GetInputName(int8_t inputChannel) { return mInputs.at(inputChannel).mName; } From 464222f90e0f2bd1133d7451a727a2e531e80595 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Tue, 27 Aug 2024 15:23:36 +0200 Subject: [PATCH 064/125] Fix warnings. --- packages/tools/source/common/nodegraph/NodeGraph.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/tools/source/common/nodegraph/NodeGraph.cpp b/packages/tools/source/common/nodegraph/NodeGraph.cpp index 6303a2e3..fce49924 100644 --- a/packages/tools/source/common/nodegraph/NodeGraph.cpp +++ b/packages/tools/source/common/nodegraph/NodeGraph.cpp @@ -97,7 +97,7 @@ namespace l::nodegraph { Input newInput; newInput.mInputNode = &source; - input = NodeGraphInput{ std::move(newInput), InputType::INPUT_NODE, sourceOutputChannel }; + input = NodeGraphInput{ std::move(newInput), InputType::INPUT_NODE, sourceOutputChannel, ""}; return true; } @@ -121,7 +121,7 @@ namespace l::nodegraph { source.GetInputNode().SetInputName(sourceChannel, input.mName); } - input = NodeGraphInput{ std::move(newInput), InputType::INPUT_NODE, sourceChannel }; + input = NodeGraphInput{ std::move(newInput), InputType::INPUT_NODE, sourceChannel, "" }; return true; } @@ -133,7 +133,7 @@ namespace l::nodegraph { Input newInput; newInput.mInputFloatConstant = constant; - input = NodeGraphInput{ std::move(newInput), InputType::INPUT_CONSTANT, 0 }; + input = NodeGraphInput{ std::move(newInput), InputType::INPUT_CONSTANT, 0, "" }; return true; } @@ -145,7 +145,7 @@ namespace l::nodegraph { Input newInput; newInput.mInputFloat = floatPtr; - input = NodeGraphInput{ std::move(newInput), InputType::INPUT_VALUE, 0 }; + input = NodeGraphInput{ std::move(newInput), InputType::INPUT_VALUE, 0, "" }; return true; } @@ -197,6 +197,7 @@ namespace l::nodegraph { if (mInput.mInputNode != nullptr) { return true; } + break; case InputType::INPUT_CONSTANT: return true; case InputType::INPUT_VALUE: From 5a7f38b697e240ec032bd8bf63420d9e72f93d8f Mon Sep 17 00:00:00 2001 From: lnd3 Date: Tue, 27 Aug 2024 19:36:26 +0200 Subject: [PATCH 065/125] Move creation stuff to new node graph schema. --- .../tools/include/tools/nodegraph/NodeGraph.h | 15 ------ .../include/tools/nodegraph/NodeGraphSchema.h | 31 ++++++++++++ .../common/nodegraph/NodeGraphSchema.cpp | 47 +++++++++++++++++++ 3 files changed, 78 insertions(+), 15 deletions(-) create mode 100644 packages/tools/include/tools/nodegraph/NodeGraphSchema.h create mode 100644 packages/tools/source/common/nodegraph/NodeGraphSchema.cpp diff --git a/packages/tools/include/tools/nodegraph/NodeGraph.h b/packages/tools/include/tools/nodegraph/NodeGraph.h index ead95ae0..9603417d 100644 --- a/packages/tools/include/tools/nodegraph/NodeGraph.h +++ b/packages/tools/include/tools/nodegraph/NodeGraph.h @@ -233,20 +233,5 @@ namespace l::nodegraph { std::vector> mNodes; }; - class NodeGraphSchema { - public: - NodeGraphSchema() = default; - ~NodeGraphSchema() = default; - - template>> - void NewNode(std::string_view name = "") { - mNodes.emplace_back(std::make_unique>(name)); - } - - protected: - std::vector> mNodes; - - }; - } diff --git a/packages/tools/include/tools/nodegraph/NodeGraphSchema.h b/packages/tools/include/tools/nodegraph/NodeGraphSchema.h new file mode 100644 index 00000000..c775d242 --- /dev/null +++ b/packages/tools/include/tools/nodegraph/NodeGraphSchema.h @@ -0,0 +1,31 @@ +#pragma once + +#include "logging/LoggingAll.h" + +#include "tools/nodegraph/NodeGraph.h" +#include "tools/nodegraph/NodeGraphOperations.h" + +#include +#include +#include +#include +#include +#include + +namespace l::nodegraph { + + class NodeGraphSchema { + public: + NodeGraphSchema() = default; + ~NodeGraphSchema() = default; + + void NewNode(int32_t type, std::function createHandler); + NodeGraphBase* GetNode(int32_t id); + + protected: + NodeGraphGroup mMainNodeGraph; + + }; + +} + diff --git a/packages/tools/source/common/nodegraph/NodeGraphSchema.cpp b/packages/tools/source/common/nodegraph/NodeGraphSchema.cpp new file mode 100644 index 00000000..2049406d --- /dev/null +++ b/packages/tools/source/common/nodegraph/NodeGraphSchema.cpp @@ -0,0 +1,47 @@ +#include "tools/nodegraph/NodeGraphSchema.h" + +#include "logging/Log.h" + +namespace l::nodegraph { + + void NodeGraphSchema::NewNode(int32_t type, std::function createHandler) { + l::nodegraph::NodeGraphBase* node = nullptr; + switch (type) { + case 0: + node = mMainNodeGraph.NewNode(); + break; + case 1: + node = mMainNodeGraph.NewNode(); + break; + case 2: + node = mMainNodeGraph.NewNode(); + break; + case 3: + node = mMainNodeGraph.NewNode(); + break; + case 4: + node = mMainNodeGraph.NewNode(); + break; + case 20: + node = mMainNodeGraph.NewNode(); + break; + case 21: + node = mMainNodeGraph.NewNode(); + break; + case 22: + node = mMainNodeGraph.NewNode(); + break; + case 40: + node = mMainNodeGraph.NewNode(); + break; + default: + break; + }; + createHandler(node); + } + + NodeGraphBase* NodeGraphSchema::GetNode(int32_t id) { + return mMainNodeGraph.GetNode(id); + } + +} \ No newline at end of file From ac65c6a890b35c4556b6d653920ef15af3d93def Mon Sep 17 00:00:00 2001 From: lnd3 Date: Tue, 27 Aug 2024 20:48:15 +0200 Subject: [PATCH 066/125] Move node graph to its separate package. --- packages/nodegraph/CMakeLists.txt | 8 +++ .../include}/nodegraph/NodeGraph.h | 0 .../include}/nodegraph/NodeGraphOperations.h | 2 +- .../include}/nodegraph/NodeGraphSchema.h | 12 +++- .../source/common}/NodeGraph.cpp | 2 +- .../source/common}/NodeGraphOperations.cpp | 2 +- .../source/common}/NodeGraphSchema.cpp | 13 +++- .../tests/common/NodeGraphSchemaTest.cpp} | 8 +-- .../nodegraph/tests/common/NodeGraphTest.cpp | 7 +++ packages/rendering/CMakeLists.txt | 4 +- .../include/rendering/ui/UIContainer.h | 16 ++--- .../include/rendering/ui/UICreator.h | 5 +- .../include/rendering/ui/UIVisitors.h | 4 +- .../source/common/ui/UIContainer.cpp | 56 ++++++++++------- .../rendering/source/common/ui/UICreator.cpp | 62 +++++++++++++++++++ .../rendering/source/common/ui/UIVisitors.cpp | 2 +- 16 files changed, 153 insertions(+), 50 deletions(-) create mode 100644 packages/nodegraph/CMakeLists.txt rename packages/{tools/include/tools => nodegraph/include}/nodegraph/NodeGraph.h (100%) rename packages/{tools/include/tools => nodegraph/include}/nodegraph/NodeGraphOperations.h (99%) rename packages/{tools/include/tools => nodegraph/include}/nodegraph/NodeGraphSchema.h (53%) rename packages/{tools/source/common/nodegraph => nodegraph/source/common}/NodeGraph.cpp (99%) rename packages/{tools/source/common/nodegraph => nodegraph/source/common}/NodeGraphOperations.cpp (98%) rename packages/{tools/source/common/nodegraph => nodegraph/source/common}/NodeGraphSchema.cpp (75%) rename packages/{tools/tests/common/NodeGraphTest.cpp => nodegraph/tests/common/NodeGraphSchemaTest.cpp} (96%) create mode 100644 packages/nodegraph/tests/common/NodeGraphTest.cpp diff --git a/packages/nodegraph/CMakeLists.txt b/packages/nodegraph/CMakeLists.txt new file mode 100644 index 00000000..ceceeb6b --- /dev/null +++ b/packages/nodegraph/CMakeLists.txt @@ -0,0 +1,8 @@ +project (nodegraph) + +set(deps + logging + testing +) + +bs_generate_package(nodegraph "tier1" "${deps}" "") diff --git a/packages/tools/include/tools/nodegraph/NodeGraph.h b/packages/nodegraph/include/nodegraph/NodeGraph.h similarity index 100% rename from packages/tools/include/tools/nodegraph/NodeGraph.h rename to packages/nodegraph/include/nodegraph/NodeGraph.h diff --git a/packages/tools/include/tools/nodegraph/NodeGraphOperations.h b/packages/nodegraph/include/nodegraph/NodeGraphOperations.h similarity index 99% rename from packages/tools/include/tools/nodegraph/NodeGraphOperations.h rename to packages/nodegraph/include/nodegraph/NodeGraphOperations.h index 81e5a956..38ba813b 100644 --- a/packages/tools/include/tools/nodegraph/NodeGraphOperations.h +++ b/packages/nodegraph/include/nodegraph/NodeGraphOperations.h @@ -1,5 +1,5 @@ #pragma once -#include "NodeGraph.h" +#include "nodegraph/NodeGraph.h" #include "logging/LoggingAll.h" diff --git a/packages/tools/include/tools/nodegraph/NodeGraphSchema.h b/packages/nodegraph/include/nodegraph/NodeGraphSchema.h similarity index 53% rename from packages/tools/include/tools/nodegraph/NodeGraphSchema.h rename to packages/nodegraph/include/nodegraph/NodeGraphSchema.h index c775d242..de1b03fa 100644 --- a/packages/tools/include/tools/nodegraph/NodeGraphSchema.h +++ b/packages/nodegraph/include/nodegraph/NodeGraphSchema.h @@ -2,8 +2,8 @@ #include "logging/LoggingAll.h" -#include "tools/nodegraph/NodeGraph.h" -#include "tools/nodegraph/NodeGraphOperations.h" +#include "nodegraph/NodeGraph.h" +#include "nodegraph/NodeGraphOperations.h" #include #include @@ -16,15 +16,21 @@ namespace l::nodegraph { class NodeGraphSchema { public: + + using CustomCreateFunctionType = NodeGraphBase * (int32_t type, NodeGraphGroup&); + NodeGraphSchema() = default; ~NodeGraphSchema() = default; - void NewNode(int32_t type, std::function createHandler); + void SetCustomCreator(std::function customCreator); + int32_t NewNode(int32_t type); NodeGraphBase* GetNode(int32_t id); protected: NodeGraphGroup mMainNodeGraph; + std::function mCreateCustomNode; + }; } diff --git a/packages/tools/source/common/nodegraph/NodeGraph.cpp b/packages/nodegraph/source/common/NodeGraph.cpp similarity index 99% rename from packages/tools/source/common/nodegraph/NodeGraph.cpp rename to packages/nodegraph/source/common/NodeGraph.cpp index fce49924..f936debd 100644 --- a/packages/tools/source/common/nodegraph/NodeGraph.cpp +++ b/packages/nodegraph/source/common/NodeGraph.cpp @@ -1,4 +1,4 @@ -#include "tools/nodegraph/NodeGraph.h" +#include "nodegraph/NodeGraph.h" #include "logging/Log.h" diff --git a/packages/tools/source/common/nodegraph/NodeGraphOperations.cpp b/packages/nodegraph/source/common/NodeGraphOperations.cpp similarity index 98% rename from packages/tools/source/common/nodegraph/NodeGraphOperations.cpp rename to packages/nodegraph/source/common/NodeGraphOperations.cpp index 22d40f07..769daa50 100644 --- a/packages/tools/source/common/nodegraph/NodeGraphOperations.cpp +++ b/packages/nodegraph/source/common/NodeGraphOperations.cpp @@ -1,4 +1,4 @@ -#include "tools/nodegraph/NodeGraphOperations.h" +#include "nodegraph/NodeGraphOperations.h" #include "logging/Log.h" diff --git a/packages/tools/source/common/nodegraph/NodeGraphSchema.cpp b/packages/nodegraph/source/common/NodeGraphSchema.cpp similarity index 75% rename from packages/tools/source/common/nodegraph/NodeGraphSchema.cpp rename to packages/nodegraph/source/common/NodeGraphSchema.cpp index 2049406d..4b6c3417 100644 --- a/packages/tools/source/common/nodegraph/NodeGraphSchema.cpp +++ b/packages/nodegraph/source/common/NodeGraphSchema.cpp @@ -1,10 +1,14 @@ -#include "tools/nodegraph/NodeGraphSchema.h" +#include "nodegraph/NodeGraphSchema.h" #include "logging/Log.h" namespace l::nodegraph { - void NodeGraphSchema::NewNode(int32_t type, std::function createHandler) { + void NodeGraphSchema::SetCustomCreator(std::function customCreator) { + mCreateCustomNode = customCreator; + } + + int32_t NodeGraphSchema::NewNode(int32_t type) { l::nodegraph::NodeGraphBase* node = nullptr; switch (type) { case 0: @@ -35,9 +39,12 @@ namespace l::nodegraph { node = mMainNodeGraph.NewNode(); break; default: + ASSERT(type < 1000) << "Custom node id's begin at id 1000"; + node = mCreateCustomNode(type, mMainNodeGraph); break; }; - createHandler(node); + + return node == nullptr ? 0 : node->GetId(); } NodeGraphBase* NodeGraphSchema::GetNode(int32_t id) { diff --git a/packages/tools/tests/common/NodeGraphTest.cpp b/packages/nodegraph/tests/common/NodeGraphSchemaTest.cpp similarity index 96% rename from packages/tools/tests/common/NodeGraphTest.cpp rename to packages/nodegraph/tests/common/NodeGraphSchemaTest.cpp index 7cf1b6e6..48c25975 100644 --- a/packages/tools/tests/common/NodeGraphTest.cpp +++ b/packages/nodegraph/tests/common/NodeGraphSchemaTest.cpp @@ -1,8 +1,9 @@ #include "testing/Test.h" #include "logging/Log.h" -#include "tools/nodegraph/NodeGraph.h" -#include "tools/nodegraph/NodeGraphOperations.h" +#include "nodegraph/NodeGraph.h" +#include "nodegraph/NodeGraphOperations.h" +#include "nodegraph/NodeGraphSchema.h" using namespace l; using namespace l::nodegraph; @@ -183,9 +184,6 @@ TEST(NodeGraph, GraphGroups) { TEST(NodeGraph, SchemaBasic) { - NodeGraphSchema schema; - schema.NewNode(); - return 0; } diff --git a/packages/nodegraph/tests/common/NodeGraphTest.cpp b/packages/nodegraph/tests/common/NodeGraphTest.cpp new file mode 100644 index 00000000..6d728e9e --- /dev/null +++ b/packages/nodegraph/tests/common/NodeGraphTest.cpp @@ -0,0 +1,7 @@ +#include "testing/Test.h" + +int main(int, char* argw[]) { + TEST_RUN(argw[0]); + + return 0; +} diff --git a/packages/rendering/CMakeLists.txt b/packages/rendering/CMakeLists.txt index c86a9d05..4fff279d 100644 --- a/packages/rendering/CMakeLists.txt +++ b/packages/rendering/CMakeLists.txt @@ -8,9 +8,11 @@ set(deps logging testing + memory + nodegraph filesystem - memory + tools physics diff --git a/packages/rendering/include/rendering/ui/UIContainer.h b/packages/rendering/include/rendering/ui/UIContainer.h index 3d2ab363..66dd97f1 100644 --- a/packages/rendering/include/rendering/ui/UIContainer.h +++ b/packages/rendering/include/rendering/ui/UIContainer.h @@ -395,16 +395,18 @@ namespace l::ui { UISplitMode mSplitMode; }; - class UICreator { + class UIStorage { public: - UICreator() = default; - ~UICreator() = default; - - UIHandle CreateContainer(uint32_t flags, UIRenderType renderType = UIRenderType::Rect, UIAlignH alignH = UIAlignH::Left, UIAlignV alignV = UIAlignV::Top, UILayoutH layoutH = UILayoutH::Fixed, UILayoutV layoutV = UILayoutV::Fixed); - UIHandle CreateSplit(uint32_t flags, UIRenderType renderType, UISplitMode splitMode = UISplitMode::AppendV, UILayoutH layoutH = UILayoutH::Fixed, UILayoutV layoutV = UILayoutV::Fixed); + UIStorage() = default; + ~UIStorage() = default; + UIHandle Add(std::unique_ptr container); + void Remove(int32_t id); protected: std::unordered_map> mContainers; - }; + + UIHandle CreateContainer(UIStorage& uiStorage, uint32_t flags, UIRenderType renderType = UIRenderType::Rect, UIAlignH alignH = UIAlignH::Left, UIAlignV alignV = UIAlignV::Top, UILayoutH layoutH = UILayoutH::Fixed, UILayoutV layoutV = UILayoutV::Fixed); + UIHandle CreateSplit(UIStorage& uiStorage, uint32_t flags, UIRenderType renderType, UISplitMode splitMode = UISplitMode::AppendV, UILayoutH layoutH = UILayoutH::Fixed, UILayoutV layoutV = UILayoutV::Fixed); + } diff --git a/packages/rendering/include/rendering/ui/UICreator.h b/packages/rendering/include/rendering/ui/UICreator.h index f1ea3325..d431abbf 100644 --- a/packages/rendering/include/rendering/ui/UICreator.h +++ b/packages/rendering/include/rendering/ui/UICreator.h @@ -12,9 +12,12 @@ #include "implot/implot.h" #include "implot/implot_internal.h" +#include "rendering/ui/UIContainer.h" +#include "nodegraph/NodeGraph.h" + #include namespace l::ui { - + UIHandle CreateUINode(UIStorage& uiStorage, l::nodegraph::NodeGraphBase& node, ImVec2 p); } diff --git a/packages/rendering/include/rendering/ui/UIVisitors.h b/packages/rendering/include/rendering/ui/UIVisitors.h index 078e07b0..2847aeb5 100644 --- a/packages/rendering/include/rendering/ui/UIVisitors.h +++ b/packages/rendering/include/rendering/ui/UIVisitors.h @@ -70,7 +70,7 @@ namespace l::ui { virtual bool Active(UIContainer& container, const InputState& input); - UILinkIO(UICreator* creator, std::function handler = nullptr) : mCreator(creator), mHandler(std::move(handler)) {} + UILinkIO(UIStorage& uiStorage, std::function handler = nullptr) : mUIStorage(uiStorage), mHandler(std::move(handler)) {} ~UILinkIO() = default; virtual bool Visit(UIContainer& container, const InputState& input); @@ -82,7 +82,7 @@ namespace l::ui { protected: bool mDragging = false; UIHandle mLinkContainer; - UICreator* mCreator = nullptr; + UIStorage& mUIStorage; std::function mHandler; }; diff --git a/packages/rendering/source/common/ui/UIContainer.cpp b/packages/rendering/source/common/ui/UIContainer.cpp index 6a3cfc5d..1801e928 100644 --- a/packages/rendering/source/common/ui/UIContainer.cpp +++ b/packages/rendering/source/common/ui/UIContainer.cpp @@ -4,30 +4,6 @@ namespace l::ui { - UIHandle UICreator::CreateContainer(uint32_t flags, UIRenderType renderType, UIAlignH alignH, UIAlignV alignV, UILayoutH layoutH, UILayoutV layoutV) { - std::unique_ptr container = std::make_unique(flags, renderType, alignH, alignV, layoutH, layoutV); - - auto id = CreateUniqueId(); - auto stringId = CreateUniqueStringId(); - container->SetId(id); - container->SetStringId(stringId); - mContainers.insert({ id, std::move(container) }); - - return UIHandle{ id, stringId, mContainers.at(id).get() }; - } - - UIHandle UICreator::CreateSplit(uint32_t flags, UIRenderType renderType, UISplitMode splitMode, UILayoutH layoutH, UILayoutV layoutV) { - std::unique_ptr container = std::make_unique(flags, renderType, splitMode, layoutH, layoutV); - - auto id = CreateUniqueId(); - auto stringId = CreateUniqueStringId(); - container->SetId(id); - container->SetStringId(stringId); - mContainers.insert({ id, std::move(container) }); - - return UIHandle{ id, stringId, mContainers.at(id).get() }; - } - ImVec2 DragMovement(const ImVec2& prevPos, const ImVec2& curPos, float curScale) { ImVec2 move = curPos; move.x -= prevPos.x; @@ -286,4 +262,36 @@ namespace l::ui { return mId++; } + UIHandle UIStorage::Add(std::unique_ptr container) { + auto id = container->GetId(); + auto stringId = container->GetStringId(); + mContainers.insert({ id, std::move(container) }); + return UIHandle{ id, stringId, mContainers.at(id).get() }; + } + + void UIStorage::Remove(int32_t id) { + mContainers.erase(id); + } + + UIHandle CreateContainer(UIStorage& uiStorage, uint32_t flags, UIRenderType renderType, UIAlignH alignH, UIAlignV alignV, UILayoutH layoutH, UILayoutV layoutV) { + std::unique_ptr container = std::make_unique(flags, renderType, alignH, alignV, layoutH, layoutV); + + auto id = CreateUniqueId(); + auto stringId = CreateUniqueStringId(); + container->SetId(id); + container->SetStringId(stringId); + + return uiStorage.Add(std::move(container)); + } + + UIHandle CreateSplit(UIStorage& uiStorage, uint32_t flags, UIRenderType renderType, UISplitMode splitMode, UILayoutH layoutH, UILayoutV layoutV) { + std::unique_ptr container = std::make_unique(flags, renderType, splitMode, layoutH, layoutV); + + auto id = CreateUniqueId(); + auto stringId = CreateUniqueStringId(); + container->SetId(id); + container->SetStringId(stringId); + + return uiStorage.Add(std::move(container)); + } } diff --git a/packages/rendering/source/common/ui/UICreator.cpp b/packages/rendering/source/common/ui/UICreator.cpp index 04b717d8..ffe699d8 100644 --- a/packages/rendering/source/common/ui/UICreator.cpp +++ b/packages/rendering/source/common/ui/UICreator.cpp @@ -5,4 +5,66 @@ namespace l::ui { + UIHandle CreateUINode(UIStorage& uiStorage, l::nodegraph::NodeGraphBase& node, ImVec2 p) { + + auto numInputChannels = node.GetNumInputs(); + auto numOutputChannels = node.GetNumOutputs(); + + auto node4 = CreateSplit(uiStorage, l::ui::UIContainer_MoveFlag | l::ui::UIContainer_DrawFlag, l::ui::UIRenderType::RectFilled, l::ui::UISplitMode::AppendV); + node4->SetPosition(p); + node4->GetContainerArea().mMargin = 0.0f; + ImVec2 titleSize = ImGui::CalcTextSize(node.GetName().data()); + titleSize.x += node4->GetContainerArea().mMargin * 2 + 2.0f; + node4->SetSize(ImVec2(titleSize.x < 100.0f ? 100.0f : titleSize.x, 22.0f + 19.0f * (numInputChannels > numOutputChannels ? numInputChannels : numOutputChannels))); + node4->SetColor(ImVec4(0.15f, 0.15f, 0.15f, 1.0f)); + { + float ioSize = 4.0f; + float ioOffsetV = 1.6f; + + auto row0 = CreateContainer(uiStorage, l::ui::UIContainer_DrawFlag, l::ui::UIRenderType::RectFilled, l::ui::UIAlignH::Left, l::ui::UIAlignV::Top, l::ui::UILayoutH::Parent, l::ui::UILayoutV::Fixed); + row0->SetSize(ImVec2(1.0f, 18.0f)); + row0->GetContainerArea().mMargin = 2.0f; + node4->Add(row0); + { + auto connector1Text = CreateContainer(uiStorage, l::ui::UIContainer_DrawFlag, l::ui::UIRenderType::Text, l::ui::UIAlignH::Center, l::ui::UIAlignV::Middle); + connector1Text->SetDisplayName(node.GetName()); + connector1Text->GetContainerArea().mRender.mColor = ImColor(ImVec4(0.5f, 1.0f, 0.4f, 1.0f)); + row0->Add(connector1Text); + } + for (int8_t i = 0; i < numInputChannels || i < numOutputChannels; i++) { + auto row = CreateContainer(uiStorage, 0, l::ui::UIRenderType::Rect, l::ui::UIAlignH::Left, l::ui::UIAlignV::Top, l::ui::UILayoutH::Parent, l::ui::UILayoutV::Fixed); + row->GetContainerArea().mMargin = ioSize; + node4->Add(row); + { + if (i < numInputChannels) { + auto in = CreateContainer(uiStorage, l::ui::UIContainer_InputFlag | l::ui::UIContainer_DrawFlag, l::ui::UIRenderType::CircleFilled, l::ui::UIAlignH::Left); + in->SetPosition(ImVec2(-ioSize, ioSize * ioOffsetV)); + in->SetSize(ImVec2(ioSize, ioSize)); + in->GetContainerArea().mMargin = 0.0f; + in->SetId(node.GetId(), i); + row->Add(in); + auto inText = CreateContainer(uiStorage, l::ui::UIContainer_DrawFlag, l::ui::UIRenderType::Text, l::ui::UIAlignH::Left); + inText->SetPosition(ImVec2(0.0f, 0.0f)); + inText->SetDisplayName(node.GetInputName(i)); + row->Add(inText); + } + + if (i < numOutputChannels) { + auto out = CreateContainer(uiStorage, l::ui::UIContainer_OutputFlag | l::ui::UIContainer_DrawFlag, l::ui::UIRenderType::CircleFilled, l::ui::UIAlignH::Right); + out->SetPosition(ImVec2(ioSize * 2.0f, ioSize * ioOffsetV)); + out->SetSize(ImVec2(ioSize, ioSize)); + out->GetContainerArea().mMargin = 0.0f; + out->SetId(node.GetId(), i); + row->Add(out); + auto outText = CreateContainer(uiStorage, l::ui::UIContainer_DrawFlag, l::ui::UIRenderType::Text, l::ui::UIAlignH::Right); + outText->SetPosition(ImVec2(0.0f, 0.0f)); + outText->SetDisplayName(node.GetOutputName(i)); + row->Add(outText); + } + } + } + } + return node4; + }; + } diff --git a/packages/rendering/source/common/ui/UIVisitors.cpp b/packages/rendering/source/common/ui/UIVisitors.cpp index 634849a8..0fcad12b 100644 --- a/packages/rendering/source/common/ui/UIVisitors.cpp +++ b/packages/rendering/source/common/ui/UIVisitors.cpp @@ -283,7 +283,7 @@ namespace l::ui { ImVec2 pT = layoutArea.Transform(pCenter); if (OverlapCircle(input.mCurPos, pT, size.x * layoutArea.mScale)) { mDragging = true; - mLinkContainer = mCreator->CreateContainer(UIContainer_LinkFlag | UIContainer_DrawFlag, UIRenderType::LinkH); + mLinkContainer = CreateContainer(mUIStorage, UIContainer_LinkFlag | UIContainer_DrawFlag, UIRenderType::LinkH); container.Add(mLinkContainer); return true; } From 782a70cc1dd73ad4742b1c43ab137c429c3bd894 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Wed, 28 Aug 2024 00:10:21 +0200 Subject: [PATCH 067/125] Move more code into ui visitors. --- .../rendering/include/rendering/ui/UIVisitors.h | 16 +++++++++++++++- .../rendering/source/common/ui/UIVisitors.cpp | 6 ++---- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/packages/rendering/include/rendering/ui/UIVisitors.h b/packages/rendering/include/rendering/ui/UIVisitors.h index 2847aeb5..15234d3d 100644 --- a/packages/rendering/include/rendering/ui/UIVisitors.h +++ b/packages/rendering/include/rendering/ui/UIVisitors.h @@ -1,6 +1,7 @@ #pragma once #include "rendering/ui/UIContainer.h" +#include "nodegraph/NodeGraphSchema.h" #define GLFW_INCLUDE_NONE #include @@ -70,7 +71,7 @@ namespace l::ui { virtual bool Active(UIContainer& container, const InputState& input); - UILinkIO(UIStorage& uiStorage, std::function handler = nullptr) : mUIStorage(uiStorage), mHandler(std::move(handler)) {} + UILinkIO(UIStorage& uiStorage, l::nodegraph::NodeGraphSchema& ngSchema) : mUIStorage(uiStorage), mNGSchema(ngSchema) {} ~UILinkIO() = default; virtual bool Visit(UIContainer& container, const InputState& input); @@ -79,10 +80,23 @@ namespace l::ui { mHandler = std::move(handler); } + bool LinkHandler(int32_t linkInputId, int32_t linkOutputId, int32_t inputChannel, int32_t outputChannel, bool connected) { + auto inputNode = mNGSchema.GetNode(linkInputId); + if (inputNode == nullptr) { + return false; + } + if (connected) { + auto outputNode = mNGSchema.GetNode(linkOutputId); + return outputNode != nullptr && inputNode->SetInput(static_cast(inputChannel), *outputNode, static_cast(outputChannel)); + } + return inputNode->ClearInput(static_cast(inputChannel)); + } + protected: bool mDragging = false; UIHandle mLinkContainer; UIStorage& mUIStorage; + l::nodegraph::NodeGraphSchema& mNGSchema; std::function mHandler; }; diff --git a/packages/rendering/source/common/ui/UIVisitors.cpp b/packages/rendering/source/common/ui/UIVisitors.cpp index 0fcad12b..a5faebec 100644 --- a/packages/rendering/source/common/ui/UIVisitors.cpp +++ b/packages/rendering/source/common/ui/UIVisitors.cpp @@ -315,7 +315,7 @@ namespace l::ui { ImVec2 pT = layoutArea.Transform(pCenter); if (OverlapCircle(input.mCurPos, pT, size.x * layoutArea.mScale)) { - if (!mHandler || mHandler(container.GetId(), mLinkContainer->GetParent()->GetId(), container.GetSubId(), mLinkContainer->GetParent()->GetSubId(), true)) { + if (LinkHandler(container.GetId(), mLinkContainer->GetParent()->GetId(), container.GetSubId(), mLinkContainer->GetParent()->GetSubId(), true)) { mLinkContainer->SetNotification(UIContainer_LinkFlag); mLinkContainer->SetCoParent(&container); } @@ -324,9 +324,7 @@ namespace l::ui { } } else if (mLinkContainer->GetCoParent() == &container) { - if (mHandler) { - mHandler(container.GetId(), mLinkContainer->GetParent()->GetId(), container.GetSubId(), mLinkContainer->GetParent()->GetSubId(), false); - } + LinkHandler(container.GetId(), mLinkContainer->GetParent()->GetId(), container.GetSubId(), mLinkContainer->GetParent()->GetSubId(), false); mLinkContainer->SetCoParent(nullptr); mLinkContainer->ClearNotifications(); } From 61c17de5504575d705e236e0117e9329e1174f52 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Wed, 28 Aug 2024 00:22:57 +0200 Subject: [PATCH 068/125] Clean up. --- packages/nodegraph/source/common/NodeGraphSchema.cpp | 1 + packages/rendering/include/rendering/ui/UIVisitors.h | 6 ------ 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/packages/nodegraph/source/common/NodeGraphSchema.cpp b/packages/nodegraph/source/common/NodeGraphSchema.cpp index 4b6c3417..da6ff107 100644 --- a/packages/nodegraph/source/common/NodeGraphSchema.cpp +++ b/packages/nodegraph/source/common/NodeGraphSchema.cpp @@ -40,6 +40,7 @@ namespace l::nodegraph { break; default: ASSERT(type < 1000) << "Custom node id's begin at id 1000"; + ASSERT(mCreateCustomNode != nullptr) << "Custom nodes needs a handler to create them. It's missing."; node = mCreateCustomNode(type, mMainNodeGraph); break; }; diff --git a/packages/rendering/include/rendering/ui/UIVisitors.h b/packages/rendering/include/rendering/ui/UIVisitors.h index 15234d3d..753ea23f 100644 --- a/packages/rendering/include/rendering/ui/UIVisitors.h +++ b/packages/rendering/include/rendering/ui/UIVisitors.h @@ -76,10 +76,6 @@ namespace l::ui { virtual bool Visit(UIContainer& container, const InputState& input); - void SetHandler(std::function handler) { - mHandler = std::move(handler); - } - bool LinkHandler(int32_t linkInputId, int32_t linkOutputId, int32_t inputChannel, int32_t outputChannel, bool connected) { auto inputNode = mNGSchema.GetNode(linkInputId); if (inputNode == nullptr) { @@ -97,8 +93,6 @@ namespace l::ui { UIHandle mLinkContainer; UIStorage& mUIStorage; l::nodegraph::NodeGraphSchema& mNGSchema; - - std::function mHandler; }; From 3643be24781fb0f28f3e94740b3648a72f676c65 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Wed, 28 Aug 2024 00:34:14 +0200 Subject: [PATCH 069/125] Add nodegraph to standalone project deps. --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index bbadd7f8..2f8e41b8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,6 +23,7 @@ else() testing meta memory + nodegraph concurrency filesystem From 644dece27a7190154c31256cf7887b89ae853a9e Mon Sep 17 00:00:00 2001 From: lnd3 Date: Wed, 28 Aug 2024 10:29:31 +0200 Subject: [PATCH 070/125] Add pointer popup window callback. --- .../rendering/include/rendering/ui/UIWindow.h | 6 ++++- .../rendering/source/common/ui/UIWindow.cpp | 24 ++++++++++++++++++- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/packages/rendering/include/rendering/ui/UIWindow.h b/packages/rendering/include/rendering/ui/UIWindow.h index 96990800..7b033435 100644 --- a/packages/rendering/include/rendering/ui/UIWindow.h +++ b/packages/rendering/include/rendering/ui/UIWindow.h @@ -21,7 +21,9 @@ namespace l::ui { UIWindow() : mWindowPtr(nullptr), mOpened(false), mIsHovered(false), mWindowFunction(nullptr), mContentScale(1.0f), mMoving(false) {} ~UIWindow() = default; - void SetAction(std::function action); + void SetContentWindow(std::function action); + void SetPointerPopup(std::function popup); + void Open(); bool IsShowing(); bool IsHovered(); @@ -39,8 +41,10 @@ namespace l::ui { bool mOpened; bool mIsHovered; std::function mWindowFunction; + std::function mPointerPopupMenu; float mContentScale; bool mMoving; + bool mPointerPopupOpen = false; ImVec4 mBgColor = ImVec4(0.01f, 0.01f, 0.01f, 1.0f); diff --git a/packages/rendering/source/common/ui/UIWindow.cpp b/packages/rendering/source/common/ui/UIWindow.cpp index e5436d19..b674553c 100644 --- a/packages/rendering/source/common/ui/UIWindow.cpp +++ b/packages/rendering/source/common/ui/UIWindow.cpp @@ -4,10 +4,14 @@ namespace l::ui { - void UIWindow::SetAction(std::function action) { + void UIWindow::SetContentWindow(std::function action) { mWindowFunction = action; } + void UIWindow::SetPointerPopup(std::function popup) { + mPointerPopupMenu = popup; + } + void UIWindow::Open() { mOpened = true; } @@ -104,6 +108,24 @@ namespace l::ui { mWindowFunction(); } + if (mPointerPopupMenu) { + if (ImGui::IsMouseClicked(ImGuiMouseButton_Right)) { + ImGui::OpenPopup("PointerPopupMenu"); + mPointerPopupOpen = true; + } + if (mPointerPopupOpen) { + ImGuiIO& io = ImGui::GetIO(); + ImGui::SetNextWindowPos(io.MousePos, ImGuiCond_Appearing, ImVec2(0.0f, 0.0f)); + } + if (ImGui::BeginPopup("PointerPopupMenu", ImGuiWindowFlags_AlwaysAutoResize)) { + mPointerPopupMenu(); + ImGui::EndPopup(); + } + else { + mPointerPopupOpen = false; + } + } + if (!mOpened) { mWindowPtr = nullptr; mIsHovered = false; From cede21d29a55eb309afd3d36bf59c628905fc720 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Wed, 28 Aug 2024 10:42:36 +0200 Subject: [PATCH 071/125] Clean up. --- .../rendering/include/rendering/ui/UIWindow.h | 5 -- .../rendering/source/common/ui/UIWindow.cpp | 63 ------------------- 2 files changed, 68 deletions(-) diff --git a/packages/rendering/include/rendering/ui/UIWindow.h b/packages/rendering/include/rendering/ui/UIWindow.h index 7b033435..9fcbd7de 100644 --- a/packages/rendering/include/rendering/ui/UIWindow.h +++ b/packages/rendering/include/rendering/ui/UIWindow.h @@ -29,11 +29,6 @@ namespace l::ui { bool IsHovered(); ImVec2 GetPosition(); ImVec2 GetSize(); - float GetScale(); - bool TryInput(const ImVec2& prevPos, const ImVec2& curPos, float zoom, bool, bool); - bool TryPan(const ImVec2& prevPos, const ImVec2& curPos, bool moveStart, bool moveStop); - bool TryScale(const ImVec2& scalePos, float scroll); - ImVec2 Transform(ImVec2 p, bool toWorld = true); void Show(); void SetBgColor(ImVec4 bgColor); protected: diff --git a/packages/rendering/source/common/ui/UIWindow.cpp b/packages/rendering/source/common/ui/UIWindow.cpp index b674553c..25da4bd8 100644 --- a/packages/rendering/source/common/ui/UIWindow.cpp +++ b/packages/rendering/source/common/ui/UIWindow.cpp @@ -32,69 +32,6 @@ namespace l::ui { return mWindowPtr->Size; } - float UIWindow::GetScale() { - return mContentScale; - } - - bool UIWindow::TryInput(const ImVec2& prevPos, const ImVec2& curPos, float zoom, bool, bool) { - if (TryPan(prevPos, curPos, ImGui::IsMouseClicked(ImGuiMouseButton_Left), ImGui::IsMouseReleased(ImGuiMouseButton_Left))) { - return true; - } - else if (TryScale(curPos, zoom)) { - return true; - } - return false; - } - - bool UIWindow::TryPan(const ImVec2& prevPos, const ImVec2& curPos, bool moveStart, bool moveStop) { - if (IsShowing()) { - if (IsHovered() && moveStart) { - mMoving = true; - } - if (mMoving) { - ImVec2 drag = curPos; - drag.x -= prevPos.x; - drag.y -= prevPos.y; - mContentPan.x += drag.x; - mContentPan.y += drag.y; - - if (moveStop) { - mMoving = false; - } - } - } - return mMoving; - } - - bool UIWindow::TryScale(const ImVec2& scalePos, float scroll) { - if (IsShowing() && IsHovered() && scroll != 0.0f) { - ImVec2 parentPos = GetPosition(); - float scaleChange = (1.0f + 0.1f * scroll); - if ((mContentScale > 100.0f && scaleChange > 1.0f) || (mContentScale < 0.01f && scaleChange < 1.0f)) { - return true; - } - mContentScale *= scaleChange; - mContentPan.x = scalePos.x + (mContentPan.x + parentPos.x - scalePos.x) * scaleChange - parentPos.x; - mContentPan.y = scalePos.y + (mContentPan.y + parentPos.y - scalePos.y) * scaleChange - parentPos.y; - return true; - } - return false; - } - - ImVec2 UIWindow::Transform(ImVec2 p, bool toWorld) { - ImVec2 parentPos = GetPosition(); - - float x = p.x * mContentScale; - float y = p.y * mContentScale; - x += mContentPan.x; - y += mContentPan.y; - if (toWorld) { - x += parentPos.x; - y += parentPos.y; - } - return ImVec2(x, y); - } - void UIWindow::Show() { if (mOpened) { ImGui::PushStyleColor(ImGuiCol_WindowBg, mBgColor); From f94b771393e710db101f5653cb240eaa084a9688 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Wed, 28 Aug 2024 12:47:26 +0200 Subject: [PATCH 072/125] Add a node editor. --- .../include/rendering/ui/UIContainer.h | 16 +- .../include/rendering/ui/UINodeEditor.h | 138 ++++++++++++++++++ .../include/rendering/ui/UIVisitors.h | 10 -- .../rendering/include/rendering/ui/UIWindow.h | 34 +++-- .../source/common/ui/UIContainer.cpp | 12 +- .../rendering/source/common/ui/UICreator.cpp | 6 +- .../source/common/ui/UINodeEditor.cpp | 49 +++++++ .../rendering/source/common/ui/UIVisitors.cpp | 17 ++- .../rendering/source/common/ui/UIWindow.cpp | 10 +- 9 files changed, 245 insertions(+), 47 deletions(-) create mode 100644 packages/rendering/include/rendering/ui/UINodeEditor.h create mode 100644 packages/rendering/source/common/ui/UINodeEditor.cpp diff --git a/packages/rendering/include/rendering/ui/UIContainer.h b/packages/rendering/include/rendering/ui/UIContainer.h index 66dd97f1..8aaba50e 100644 --- a/packages/rendering/include/rendering/ui/UIContainer.h +++ b/packages/rendering/include/rendering/ui/UIContainer.h @@ -183,8 +183,6 @@ namespace l::ui { class UIContainer; class UISplit; - int32_t CreateUniqueId(); - template>> std::string CreateUniqueStringId() { static uint32_t mStringId = 0; @@ -286,7 +284,8 @@ namespace l::ui { public: UIContainer(uint32_t flags = 0, UIRenderType renderType = UIRenderType::Rect, UIAlignH alignH = UIAlignH::Left, UIAlignV alignV = UIAlignV::Top, UILayoutH layoutH = UILayoutH::Fixed, UILayoutV layoutV = UILayoutV::Fixed) : mId(0), - mSubId(0), + mNodeId(0), + mChannelId(0), mConfigFlags(flags), mNotificationFlags(0), mParent(nullptr), @@ -336,7 +335,8 @@ namespace l::ui { std::string_view GetDisplayName() { return mDisplayName; } std::string_view GetStringId() { return mStringId; } int32_t GetId() { return mId; } - int32_t GetSubId() { return mSubId; } + int32_t GetNodeId() { return mNodeId; } + int32_t GetChannelId() { return mChannelId; } void SetColor(ImVec4 color) { mDisplayArea.mRender.mColor = ImColor(color); } void SetScale(float scale) {mDisplayArea.mScale = scale;} @@ -346,7 +346,9 @@ namespace l::ui { void SetLayoutSize(ImVec2 s) {mLayoutArea.mSize = s;} void SetDisplayName(std::string_view displayName) {mDisplayName = displayName;} void SetStringId(std::string_view id) {mStringId = id;} - void SetId(int32_t id, int32_t subId = 0) { mId = id; mSubId = subId; } + void SetId(int32_t id) { mId = id; } + void SetNodeId(int32_t nodeId) { mNodeId = nodeId; } + void SetChannelId(int32_t channelId) { mChannelId = channelId; } void SetContainerArea(const ContainerArea& area) {mDisplayArea = area;} void SetLayoutArea(const ContainerArea& transformedLayoutArea) {mLayoutArea = transformedLayoutArea;} @@ -356,7 +358,8 @@ namespace l::ui { void DebugLog() { LOG(LogDebug) << "UIContainer: " << mDisplayName << ", [" << mDisplayArea.mScale << "][" << mDisplayArea.mPosition.x << ", " << mDisplayArea.mPosition.y << "][" << mDisplayArea.mSize.x << ", " << mDisplayArea.mSize.y << "]"; } protected: int32_t mId = 0; - int32_t mSubId = 0; + int32_t mNodeId = 0; + int32_t mChannelId = 0; std::string mStringId; std::string mDisplayName; uint32_t mConfigFlags = 0; // Active visitor flags @@ -404,6 +407,7 @@ namespace l::ui { void Remove(int32_t id); protected: std::unordered_map> mContainers; + int32_t mIdCounter = 1; }; UIHandle CreateContainer(UIStorage& uiStorage, uint32_t flags, UIRenderType renderType = UIRenderType::Rect, UIAlignH alignH = UIAlignH::Left, UIAlignV alignV = UIAlignV::Top, UILayoutH layoutH = UILayoutH::Fixed, UILayoutV layoutV = UILayoutV::Fixed); diff --git a/packages/rendering/include/rendering/ui/UINodeEditor.h b/packages/rendering/include/rendering/ui/UINodeEditor.h new file mode 100644 index 00000000..ea2e53ed --- /dev/null +++ b/packages/rendering/include/rendering/ui/UINodeEditor.h @@ -0,0 +1,138 @@ +#pragma once + +#include "logging/LoggingAll.h" + +#include "rendering/ui/UIContainer.h" +#include "rendering/ui/UIVisitors.h" +#include "rendering/ui/UIWindow.h" +#include "rendering/ui/UICreator.h" + +#include "nodegraph/NodeGraph.h" +#include "nodegraph/NodeGraphOperations.h" +#include "nodegraph/NodeGraphSchema.h" + +#include +#include +#include +#include + +namespace l::ui { + + struct UINodeDesc { + std::string_view GetTypeName() { + return mTypeName; + } + std::string_view GetName() { + return mName; + } + int32_t GetId() { + return mId; + } + + int32_t mId; + std::string mName; + std::string mTypeName; + }; + + class UINodeEditor : public UIBase { + public: + UINodeEditor(std::string_view editorName) : mUIWindow(editorName), mLinkIOVisitor(mUIStorage, mNGSchema) { + mUIRoot = CreateContainer(mUIStorage, l::ui::UIContainer_DragFlag | l::ui::UIContainer_ZoomFlag); + + mRegisteredNodeTypes.push_back({ 0, "Add", "Numerical" }); + mRegisteredNodeTypes.push_back({ 1, "Subtract", "Numerical" }); + mRegisteredNodeTypes.push_back({ 2, "Negate", "Numerical" }); + mRegisteredNodeTypes.push_back({ 3, "Multiply", "Numerical" }); + mRegisteredNodeTypes.push_back({ 4, "Integral", "Numerical" }); + mRegisteredNodeTypes.push_back({ 20, "And", "Logical" }); + mRegisteredNodeTypes.push_back({ 21, "Or", "Logical" }); + mRegisteredNodeTypes.push_back({ 22, "Xor", "Logical" }); + mRegisteredNodeTypes.push_back({ 40, "Lowpass Filter", "" }); + + + mUIWindow.SetContentWindow([&]() { + ImGui::PushItemWidth(400); + + UIDraw uiDrawVisitor(ImGui::GetWindowDrawList()); + UIUpdate updateVisitor; + + mUIRoot->SetLayoutSize(mUIWindow.GetSize()); + mUIRoot->SetLayoutPosition(mUIWindow.GetPosition()); + mUIRoot->Accept(updateVisitor, mUIInput, l::ui::UITraversalMode::BFS); + mUIRoot->Accept(uiDrawVisitor, mUIInput, l::ui::UITraversalMode::BFS); + + ImGui::PopItemWidth(); + + }); + + mUIWindow.SetPointerPopup([&]() { + ImGui::Text("Node picker"); + ImGui::Separator(); + + std::set nodeTypes; + for (auto nodeType : mRegisteredNodeTypes) { + nodeTypes.emplace(nodeType.GetTypeName()); + } + + for (auto it = nodeTypes.rbegin(); it != nodeTypes.rend(); it++) { + if (it->empty() || ImGui::TreeNode(it->c_str())) { + for (auto& nodedesc : mRegisteredNodeTypes) { + if (*it != nodedesc.GetTypeName()) { + continue; + } + + if (ImGui::MenuItem(nodedesc.GetName().data())) { + ImVec2 p = ImVec2(mUIInput.mCurPos.x - mUIWindow.GetPosition().x, mUIInput.mCurPos.y - mUIWindow.GetPosition().y); + p.x -= mUIRoot->GetPosition().x; + p.y -= mUIRoot->GetPosition().y; + p.x /= mUIRoot->GetScale(); + p.y /= mUIRoot->GetScale(); + p.x -= 3.0f; + p.y -= 3.0f; + auto nodeId = mNGSchema.NewNode(nodedesc.GetId()); + auto node = mNGSchema.GetNode(nodeId); + if (node != nullptr) { + auto uiNode = l::ui::CreateUINode(mUIStorage, *node, p); + mUIRoot->Add(uiNode); + } + } + } + if (!it->empty()) { + ImGui::TreePop(); + } + } + } + + }); + + + } + ~UINodeEditor() = default; + + void Show() override; + bool IsShowing() override; + + void Open(); + void Close(); + + void Update(); + + protected: + UIWindow mUIWindow; + UIStorage mUIStorage; + UIHandle mUIRoot; + InputState mUIInput; + + l::nodegraph::NodeGraphSchema mNGSchema; + + std::vector mRegisteredNodeTypes; + + UIZoom mZoomVisitor; + UIDrag mDragVisitor; + UIMove mMoveVisitor; + UIResize mResizeVisitor; + UILinkIO mLinkIOVisitor; + + }; + +} diff --git a/packages/rendering/include/rendering/ui/UIVisitors.h b/packages/rendering/include/rendering/ui/UIVisitors.h index 753ea23f..1e496fd1 100644 --- a/packages/rendering/include/rendering/ui/UIVisitors.h +++ b/packages/rendering/include/rendering/ui/UIVisitors.h @@ -3,16 +3,6 @@ #include "rendering/ui/UIContainer.h" #include "nodegraph/NodeGraphSchema.h" -#define GLFW_INCLUDE_NONE -#include - -#include "imgui/imgui.h" -#include "imgui/imgui_internal.h" -#include "imgui/imgui_impl_glfw.h" -#include "imgui/imgui_impl_opengl3.h" -#include "implot/implot.h" -#include "implot/implot_internal.h" - namespace l::ui { class UIUpdate : public UIVisitor { diff --git a/packages/rendering/include/rendering/ui/UIWindow.h b/packages/rendering/include/rendering/ui/UIWindow.h index 9fcbd7de..9f17bb02 100644 --- a/packages/rendering/include/rendering/ui/UIWindow.h +++ b/packages/rendering/include/rendering/ui/UIWindow.h @@ -16,34 +16,42 @@ namespace l::ui { - class UIWindow { + class UIBase { public: - UIWindow() : mWindowPtr(nullptr), mOpened(false), mIsHovered(false), mWindowFunction(nullptr), mContentScale(1.0f), mMoving(false) {} + UIBase() = default; + virtual ~UIBase() = default; + + virtual void Show() = 0; + virtual bool IsShowing() = 0; + }; + + class UIWindow final : public UIBase { + public: + UIWindow(std::string_view windowName) : mWindowName(windowName) {} ~UIWindow() = default; + void Show() override; + bool IsShowing() override; + void SetContentWindow(std::function action); void SetPointerPopup(std::function popup); void Open(); - bool IsShowing(); + void Close(); bool IsHovered(); ImVec2 GetPosition(); ImVec2 GetSize(); - void Show(); void SetBgColor(ImVec4 bgColor); protected: - ImGuiWindow* mWindowPtr; - bool mOpened; - bool mIsHovered; - std::function mWindowFunction; - std::function mPointerPopupMenu; - float mContentScale; - bool mMoving; + std::string mWindowName; + ImGuiWindow* mWindowPtr = nullptr; + bool mOpened = false; + bool mIsHovered = false; + std::function mWindowFunction = nullptr; + std::function mPointerPopupMenu = nullptr; bool mPointerPopupOpen = false; ImVec4 mBgColor = ImVec4(0.01f, 0.01f, 0.01f, 1.0f); - - ImVec2 mContentPan; }; } diff --git a/packages/rendering/source/common/ui/UIContainer.cpp b/packages/rendering/source/common/ui/UIContainer.cpp index 1801e928..05957b42 100644 --- a/packages/rendering/source/common/ui/UIContainer.cpp +++ b/packages/rendering/source/common/ui/UIContainer.cpp @@ -257,14 +257,10 @@ namespace l::ui { return false; } - int32_t CreateUniqueId() { - static int32_t mId = 1; - return mId++; - } - UIHandle UIStorage::Add(std::unique_ptr container) { - auto id = container->GetId(); + auto id = mIdCounter++; auto stringId = container->GetStringId(); + container->SetId(id); mContainers.insert({ id, std::move(container) }); return UIHandle{ id, stringId, mContainers.at(id).get() }; } @@ -276,9 +272,7 @@ namespace l::ui { UIHandle CreateContainer(UIStorage& uiStorage, uint32_t flags, UIRenderType renderType, UIAlignH alignH, UIAlignV alignV, UILayoutH layoutH, UILayoutV layoutV) { std::unique_ptr container = std::make_unique(flags, renderType, alignH, alignV, layoutH, layoutV); - auto id = CreateUniqueId(); auto stringId = CreateUniqueStringId(); - container->SetId(id); container->SetStringId(stringId); return uiStorage.Add(std::move(container)); @@ -287,9 +281,7 @@ namespace l::ui { UIHandle CreateSplit(UIStorage& uiStorage, uint32_t flags, UIRenderType renderType, UISplitMode splitMode, UILayoutH layoutH, UILayoutV layoutV) { std::unique_ptr container = std::make_unique(flags, renderType, splitMode, layoutH, layoutV); - auto id = CreateUniqueId(); auto stringId = CreateUniqueStringId(); - container->SetId(id); container->SetStringId(stringId); return uiStorage.Add(std::move(container)); diff --git a/packages/rendering/source/common/ui/UICreator.cpp b/packages/rendering/source/common/ui/UICreator.cpp index ffe699d8..29389e0f 100644 --- a/packages/rendering/source/common/ui/UICreator.cpp +++ b/packages/rendering/source/common/ui/UICreator.cpp @@ -41,7 +41,8 @@ namespace l::ui { in->SetPosition(ImVec2(-ioSize, ioSize * ioOffsetV)); in->SetSize(ImVec2(ioSize, ioSize)); in->GetContainerArea().mMargin = 0.0f; - in->SetId(node.GetId(), i); + in->SetNodeId(node.GetId()); + in->SetChannelId(i); row->Add(in); auto inText = CreateContainer(uiStorage, l::ui::UIContainer_DrawFlag, l::ui::UIRenderType::Text, l::ui::UIAlignH::Left); inText->SetPosition(ImVec2(0.0f, 0.0f)); @@ -54,7 +55,8 @@ namespace l::ui { out->SetPosition(ImVec2(ioSize * 2.0f, ioSize * ioOffsetV)); out->SetSize(ImVec2(ioSize, ioSize)); out->GetContainerArea().mMargin = 0.0f; - out->SetId(node.GetId(), i); + out->SetNodeId(node.GetId()); + out->SetChannelId(i); row->Add(out); auto outText = CreateContainer(uiStorage, l::ui::UIContainer_DrawFlag, l::ui::UIRenderType::Text, l::ui::UIAlignH::Right); outText->SetPosition(ImVec2(0.0f, 0.0f)); diff --git a/packages/rendering/source/common/ui/UINodeEditor.cpp b/packages/rendering/source/common/ui/UINodeEditor.cpp new file mode 100644 index 00000000..7e5bcdc7 --- /dev/null +++ b/packages/rendering/source/common/ui/UINodeEditor.cpp @@ -0,0 +1,49 @@ +#include "rendering/ui/UINodeEditor.h" + +#include + +namespace l::ui { + + bool UINodeEditor::IsShowing() { + return mUIWindow.IsShowing(); + } + + void UINodeEditor::Show() { + mUIWindow.Show(); + } + + void UINodeEditor::Open() { + mUIWindow.Open(); + } + + void UINodeEditor::Close() { + mUIWindow.Close(); + } + + void UINodeEditor::Update() { + if (mUIWindow.IsShowing()) { + ImGuiIO& io = ImGui::GetIO(); + io.ConfigWindowsMoveFromTitleBarOnly = true; + + mUIInput.mCurPos = io.MousePos; + mUIInput.mPrevPos = io.MousePosPrev; + mUIInput.mScroll = io.MouseWheel; + mUIInput.mStarted = ImGui::IsMouseClicked(ImGuiMouseButton_Left); + mUIInput.mStopped = ImGui::IsMouseReleased(ImGuiMouseButton_Left); + + if (mUIWindow.IsHovered()) { + if (mUIRoot->Accept(mLinkIOVisitor, mUIInput, l::ui::UITraversalMode::DFS)) { + } + else if (mUIRoot->Accept(mResizeVisitor, mUIInput, l::ui::UITraversalMode::DFS)) { + } + else if (mUIRoot->Accept(mMoveVisitor, mUIInput, l::ui::UITraversalMode::DFS)) { + } + else if (mUIRoot->Accept(mZoomVisitor, mUIInput, l::ui::UITraversalMode::DFS)) { + } + else if (mUIRoot->Accept(mDragVisitor, mUIInput, l::ui::UITraversalMode::DFS)) { + } + } + } + } + +} diff --git a/packages/rendering/source/common/ui/UIVisitors.cpp b/packages/rendering/source/common/ui/UIVisitors.cpp index a5faebec..d9e2614d 100644 --- a/packages/rendering/source/common/ui/UIVisitors.cpp +++ b/packages/rendering/source/common/ui/UIVisitors.cpp @@ -48,7 +48,18 @@ namespace l::ui { return false; } if (input.mStarted && !mDragging) { - if (input.GetLocalPos().x >= 0.0f && input.GetLocalPos().y >= 0.0f) { + auto& layoutArea = container.GetLayoutArea(); + ImVec2 layoutPosition = layoutArea.mPosition; + ImVec2 layoutSize = layoutArea.mSize; + layoutPosition.x -= 6.0f; + layoutPosition.y -= 8.0f; + layoutSize.x -= 14.0f; + layoutSize.y -= 14.0f; + + ImVec2 localMousePos = input.GetLocalPos(); + localMousePos.x -= layoutPosition.x; + localMousePos.y -= layoutPosition.y; + if (Overlap(localMousePos, ImVec2(), layoutSize)) { mDragging = true; mSourceContainer = &container; } @@ -315,7 +326,7 @@ namespace l::ui { ImVec2 pT = layoutArea.Transform(pCenter); if (OverlapCircle(input.mCurPos, pT, size.x * layoutArea.mScale)) { - if (LinkHandler(container.GetId(), mLinkContainer->GetParent()->GetId(), container.GetSubId(), mLinkContainer->GetParent()->GetSubId(), true)) { + if (LinkHandler(container.GetNodeId(), mLinkContainer->GetParent()->GetNodeId(), container.GetChannelId(), mLinkContainer->GetParent()->GetChannelId(), true)) { mLinkContainer->SetNotification(UIContainer_LinkFlag); mLinkContainer->SetCoParent(&container); } @@ -324,7 +335,7 @@ namespace l::ui { } } else if (mLinkContainer->GetCoParent() == &container) { - LinkHandler(container.GetId(), mLinkContainer->GetParent()->GetId(), container.GetSubId(), mLinkContainer->GetParent()->GetSubId(), false); + LinkHandler(container.GetNodeId(), mLinkContainer->GetParent()->GetNodeId(), container.GetChannelId(), mLinkContainer->GetParent()->GetChannelId(), false); mLinkContainer->SetCoParent(nullptr); mLinkContainer->ClearNotifications(); } diff --git a/packages/rendering/source/common/ui/UIWindow.cpp b/packages/rendering/source/common/ui/UIWindow.cpp index 25da4bd8..78cdfe3e 100644 --- a/packages/rendering/source/common/ui/UIWindow.cpp +++ b/packages/rendering/source/common/ui/UIWindow.cpp @@ -4,6 +4,10 @@ namespace l::ui { + bool UIWindow::IsShowing() { + return mWindowPtr && mOpened; + } + void UIWindow::SetContentWindow(std::function action) { mWindowFunction = action; } @@ -16,8 +20,8 @@ namespace l::ui { mOpened = true; } - bool UIWindow::IsShowing() { - return mWindowPtr && mOpened; + void UIWindow::Close() { + mOpened = false; } bool UIWindow::IsHovered() { @@ -35,7 +39,7 @@ namespace l::ui { void UIWindow::Show() { if (mOpened) { ImGui::PushStyleColor(ImGuiCol_WindowBg, mBgColor); - if (ImGui::Begin("Test primitive rendering", &mOpened)) { + if (ImGui::Begin(mWindowName.c_str(), &mOpened, ImGuiWindowFlags_MenuBar)) { if (mOpened) { mWindowPtr = ImGui::GetCurrentWindowRead(); mIsHovered = ImGui::IsWindowHovered();; From 0c22b7f279b54823e82af1c1192251e7812254be Mon Sep 17 00:00:00 2001 From: lnd3 Date: Wed, 28 Aug 2024 14:26:44 +0200 Subject: [PATCH 073/125] Improve node type register. --- .../include/nodegraph/NodeGraphSchema.h | 33 ++++++++++- .../source/common/NodeGraphSchema.cpp | 8 +++ .../include/rendering/ui/UINodeEditor.h | 57 +++---------------- 3 files changed, 48 insertions(+), 50 deletions(-) diff --git a/packages/nodegraph/include/nodegraph/NodeGraphSchema.h b/packages/nodegraph/include/nodegraph/NodeGraphSchema.h index de1b03fa..98f2f936 100644 --- a/packages/nodegraph/include/nodegraph/NodeGraphSchema.h +++ b/packages/nodegraph/include/nodegraph/NodeGraphSchema.h @@ -14,23 +14,54 @@ namespace l::nodegraph { + struct UINodeDesc { + std::string_view GetName() const { + return mName; + } + int32_t GetId() const { + return mId; + } + + int32_t mId; + std::string mName; + }; + class NodeGraphSchema { public: using CustomCreateFunctionType = NodeGraphBase * (int32_t type, NodeGraphGroup&); - NodeGraphSchema() = default; + NodeGraphSchema() { + mRegisteredNodeTypes.emplace("Numerical", std::vector{ + UINodeDesc{ 0, "Add"}, + UINodeDesc{ 1, "Subtract" }, + UINodeDesc{ 2, "Negate" }, + UINodeDesc{ 3, "Multiply}" }, + UINodeDesc{ 4, "Integral" } + }); + mRegisteredNodeTypes.emplace("Logical", std::vector{ + UINodeDesc{ 20, "And" }, + UINodeDesc{ 21, "Or" }, + UINodeDesc{ 22, "Xor" } + }); + mRegisteredNodeTypes.emplace("", std::vector{ + UINodeDesc{ 40, "Lowpass Filter" } + }); + } + ~NodeGraphSchema() = default; void SetCustomCreator(std::function customCreator); int32_t NewNode(int32_t type); NodeGraphBase* GetNode(int32_t id); + void ForEachNodeType(std::function&)> cb) const; protected: NodeGraphGroup mMainNodeGraph; std::function mCreateCustomNode; + std::map> mRegisteredNodeTypes; }; } diff --git a/packages/nodegraph/source/common/NodeGraphSchema.cpp b/packages/nodegraph/source/common/NodeGraphSchema.cpp index da6ff107..5f2e0d98 100644 --- a/packages/nodegraph/source/common/NodeGraphSchema.cpp +++ b/packages/nodegraph/source/common/NodeGraphSchema.cpp @@ -2,6 +2,8 @@ #include "logging/Log.h" +#include + namespace l::nodegraph { void NodeGraphSchema::SetCustomCreator(std::function customCreator) { @@ -52,4 +54,10 @@ namespace l::nodegraph { return mMainNodeGraph.GetNode(id); } + void NodeGraphSchema::ForEachNodeType(std::function&)> cb) const { + for (auto it : mRegisteredNodeTypes) { + cb(it.first, it.second); + } + } + } \ No newline at end of file diff --git a/packages/rendering/include/rendering/ui/UINodeEditor.h b/packages/rendering/include/rendering/ui/UINodeEditor.h index ea2e53ed..1d845a34 100644 --- a/packages/rendering/include/rendering/ui/UINodeEditor.h +++ b/packages/rendering/include/rendering/ui/UINodeEditor.h @@ -12,44 +12,17 @@ #include "nodegraph/NodeGraphSchema.h" #include -#include #include #include +#include namespace l::ui { - struct UINodeDesc { - std::string_view GetTypeName() { - return mTypeName; - } - std::string_view GetName() { - return mName; - } - int32_t GetId() { - return mId; - } - - int32_t mId; - std::string mName; - std::string mTypeName; - }; - class UINodeEditor : public UIBase { public: UINodeEditor(std::string_view editorName) : mUIWindow(editorName), mLinkIOVisitor(mUIStorage, mNGSchema) { mUIRoot = CreateContainer(mUIStorage, l::ui::UIContainer_DragFlag | l::ui::UIContainer_ZoomFlag); - mRegisteredNodeTypes.push_back({ 0, "Add", "Numerical" }); - mRegisteredNodeTypes.push_back({ 1, "Subtract", "Numerical" }); - mRegisteredNodeTypes.push_back({ 2, "Negate", "Numerical" }); - mRegisteredNodeTypes.push_back({ 3, "Multiply", "Numerical" }); - mRegisteredNodeTypes.push_back({ 4, "Integral", "Numerical" }); - mRegisteredNodeTypes.push_back({ 20, "And", "Logical" }); - mRegisteredNodeTypes.push_back({ 21, "Or", "Logical" }); - mRegisteredNodeTypes.push_back({ 22, "Xor", "Logical" }); - mRegisteredNodeTypes.push_back({ 40, "Lowpass Filter", "" }); - - mUIWindow.SetContentWindow([&]() { ImGui::PushItemWidth(400); @@ -69,19 +42,10 @@ namespace l::ui { ImGui::Text("Node picker"); ImGui::Separator(); - std::set nodeTypes; - for (auto nodeType : mRegisteredNodeTypes) { - nodeTypes.emplace(nodeType.GetTypeName()); - } - - for (auto it = nodeTypes.rbegin(); it != nodeTypes.rend(); it++) { - if (it->empty() || ImGui::TreeNode(it->c_str())) { - for (auto& nodedesc : mRegisteredNodeTypes) { - if (*it != nodedesc.GetTypeName()) { - continue; - } - - if (ImGui::MenuItem(nodedesc.GetName().data())) { + mNGSchema.ForEachNodeType([&](std::string_view typeName, const std::vector& types) { + if (typeName.empty() || ImGui::TreeNode(typeName.data())) { + for (auto it : types) { + if (ImGui::MenuItem(it.GetName().data())) { ImVec2 p = ImVec2(mUIInput.mCurPos.x - mUIWindow.GetPosition().x, mUIInput.mCurPos.y - mUIWindow.GetPosition().y); p.x -= mUIRoot->GetPosition().x; p.y -= mUIRoot->GetPosition().y; @@ -89,7 +53,7 @@ namespace l::ui { p.y /= mUIRoot->GetScale(); p.x -= 3.0f; p.y -= 3.0f; - auto nodeId = mNGSchema.NewNode(nodedesc.GetId()); + auto nodeId = mNGSchema.NewNode(it.GetId()); auto node = mNGSchema.GetNode(nodeId); if (node != nullptr) { auto uiNode = l::ui::CreateUINode(mUIStorage, *node, p); @@ -97,15 +61,12 @@ namespace l::ui { } } } - if (!it->empty()) { + if (!typeName.empty()) { ImGui::TreePop(); } } - } - + }); }); - - } ~UINodeEditor() = default; @@ -125,8 +86,6 @@ namespace l::ui { l::nodegraph::NodeGraphSchema mNGSchema; - std::vector mRegisteredNodeTypes; - UIZoom mZoomVisitor; UIDrag mDragVisitor; UIMove mMoveVisitor; From 527db037b58a8269aed5d2aba32146fc71361fe7 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Wed, 28 Aug 2024 15:01:28 +0200 Subject: [PATCH 074/125] Some improvements. --- .../include/nodegraph/NodeGraphSchema.h | 32 ++++++++----------- .../source/common/NodeGraphSchema.cpp | 28 ++++++++++------ 2 files changed, 31 insertions(+), 29 deletions(-) diff --git a/packages/nodegraph/include/nodegraph/NodeGraphSchema.h b/packages/nodegraph/include/nodegraph/NodeGraphSchema.h index 98f2f936..3f150183 100644 --- a/packages/nodegraph/include/nodegraph/NodeGraphSchema.h +++ b/packages/nodegraph/include/nodegraph/NodeGraphSchema.h @@ -29,33 +29,27 @@ namespace l::nodegraph { class NodeGraphSchema { public: - using CustomCreateFunctionType = NodeGraphBase * (int32_t type, NodeGraphGroup&); + using CustomCreateFunctionType = NodeGraphBase*(int32_t, NodeGraphGroup&); NodeGraphSchema() { - mRegisteredNodeTypes.emplace("Numerical", std::vector{ - UINodeDesc{ 0, "Add"}, - UINodeDesc{ 1, "Subtract" }, - UINodeDesc{ 2, "Negate" }, - UINodeDesc{ 3, "Multiply}" }, - UINodeDesc{ 4, "Integral" } - }); - mRegisteredNodeTypes.emplace("Logical", std::vector{ - UINodeDesc{ 20, "And" }, - UINodeDesc{ 21, "Or" }, - UINodeDesc{ 22, "Xor" } - }); - mRegisteredNodeTypes.emplace("", std::vector{ - UINodeDesc{ 40, "Lowpass Filter" } - }); + RegisterNodeType("Numeric", 0, "Add"); + RegisterNodeType("Numeric", 1, "Subtract"); + RegisterNodeType("Numeric", 2, "Negate"); + RegisterNodeType("Numeric", 3, "Multiply"); + RegisterNodeType("Numeric", 4, "Integral"); + RegisterNodeType("Logic", 50, "And"); + RegisterNodeType("Logic", 51, "Or"); + RegisterNodeType("Logic", 52, "Xor"); + RegisterNodeType("Filter", 100, "Lowpass Filter"); } ~NodeGraphSchema() = default; void SetCustomCreator(std::function customCreator); - int32_t NewNode(int32_t type); - NodeGraphBase* GetNode(int32_t id); + int32_t NewNode(int32_t typeId); + NodeGraphBase* GetNode(int32_t nodeId); void ForEachNodeType(std::function&)> cb) const; - + void RegisterNodeType(const std::string& typeGroup, int32_t uniqueTypeId, std::string_view typeName); protected: NodeGraphGroup mMainNodeGraph; diff --git a/packages/nodegraph/source/common/NodeGraphSchema.cpp b/packages/nodegraph/source/common/NodeGraphSchema.cpp index 5f2e0d98..c2ba0eb2 100644 --- a/packages/nodegraph/source/common/NodeGraphSchema.cpp +++ b/packages/nodegraph/source/common/NodeGraphSchema.cpp @@ -10,9 +10,9 @@ namespace l::nodegraph { mCreateCustomNode = customCreator; } - int32_t NodeGraphSchema::NewNode(int32_t type) { + int32_t NodeGraphSchema::NewNode(int32_t typeId) { l::nodegraph::NodeGraphBase* node = nullptr; - switch (type) { + switch (typeId) { case 0: node = mMainNodeGraph.NewNode(); break; @@ -28,30 +28,30 @@ namespace l::nodegraph { case 4: node = mMainNodeGraph.NewNode(); break; - case 20: + case 50: node = mMainNodeGraph.NewNode(); break; - case 21: + case 51: node = mMainNodeGraph.NewNode(); break; - case 22: + case 52: node = mMainNodeGraph.NewNode(); break; - case 40: + case 100: node = mMainNodeGraph.NewNode(); break; default: - ASSERT(type < 1000) << "Custom node id's begin at id 1000"; + ASSERT(typeId < 10000) << "Custom node id's begin at id 1000"; ASSERT(mCreateCustomNode != nullptr) << "Custom nodes needs a handler to create them. It's missing."; - node = mCreateCustomNode(type, mMainNodeGraph); + node = mCreateCustomNode(typeId, mMainNodeGraph); break; }; return node == nullptr ? 0 : node->GetId(); } - NodeGraphBase* NodeGraphSchema::GetNode(int32_t id) { - return mMainNodeGraph.GetNode(id); + NodeGraphBase* NodeGraphSchema::GetNode(int32_t nodeId) { + return mMainNodeGraph.GetNode(nodeId); } void NodeGraphSchema::ForEachNodeType(std::function&)> cb) const { @@ -60,4 +60,12 @@ namespace l::nodegraph { } } + void NodeGraphSchema::RegisterNodeType(const std::string& typeGroup, int32_t uniqueTypeId, std::string_view typeName) { + UINodeDesc nodeDesc; + nodeDesc.mId = uniqueTypeId; + nodeDesc.mName = typeName; + mRegisteredNodeTypes[typeGroup].push_back(UINodeDesc{ uniqueTypeId, std::string(typeName) }); + + } + } \ No newline at end of file From a4977fbdf184ca09e1986e47f1419c038f2f8d61 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Wed, 28 Aug 2024 23:46:48 +0200 Subject: [PATCH 075/125] Add a selection visitor with ui container delete. --- .../include/nodegraph/NodeGraphSchema.h | 2 + .../include/rendering/ui/UIContainer.h | 14 ++++- .../include/rendering/ui/UINodeEditor.h | 53 +++++++++++++++--- .../include/rendering/ui/UIVisitors.h | 31 ++++++++-- .../rendering/include/rendering/ui/UIWindow.h | 8 ++- .../source/common/ui/UIContainer.cpp | 47 +++++++++++++++- .../rendering/source/common/ui/UICreator.cpp | 2 +- .../source/common/ui/UINodeEditor.cpp | 28 ---------- .../rendering/source/common/ui/UIVisitors.cpp | 43 +++++++++++++- .../rendering/source/common/ui/UIWindow.cpp | 56 ++++++++----------- 10 files changed, 200 insertions(+), 84 deletions(-) diff --git a/packages/nodegraph/include/nodegraph/NodeGraphSchema.h b/packages/nodegraph/include/nodegraph/NodeGraphSchema.h index 3f150183..c6e8190e 100644 --- a/packages/nodegraph/include/nodegraph/NodeGraphSchema.h +++ b/packages/nodegraph/include/nodegraph/NodeGraphSchema.h @@ -50,6 +50,8 @@ namespace l::nodegraph { NodeGraphBase* GetNode(int32_t nodeId); void ForEachNodeType(std::function&)> cb) const; void RegisterNodeType(const std::string& typeGroup, int32_t uniqueTypeId, std::string_view typeName); + + void AddInput(); protected: NodeGraphGroup mMainNodeGraph; diff --git a/packages/rendering/include/rendering/ui/UIContainer.h b/packages/rendering/include/rendering/ui/UIContainer.h index 8aaba50e..67a63fd1 100644 --- a/packages/rendering/include/rendering/ui/UIContainer.h +++ b/packages/rendering/include/rendering/ui/UIContainer.h @@ -240,6 +240,7 @@ namespace l::ui { const uint32_t UIContainer_InputFlag = 0x00000200; // Can be grabbed and dropped on a container with output flag const uint32_t UIContainer_OutputFlag = 0x00000400; // Can be dropped a grabbed const uint32_t UIContainer_LinkFlag = 0x00000800; // Can be dropped a grabbed + const uint32_t UIContainer_SelectFlag = 0x00001000; // Can be moved when grabbed class UIDraw; @@ -253,6 +254,10 @@ namespace l::ui { std::string mStringId; UIContainer* mContainer = nullptr; + int32_t GetId() const { + return mId; + } + std::string_view GetStringId() const { return mStringId; } @@ -310,6 +315,10 @@ namespace l::ui { virtual void Remove(int32_t i); virtual void Remove(const UIHandle& handle); + virtual void Remove(UIContainer* container); + virtual void RemoveAll(); + + virtual void ForEachChild(std::function cb); void SetNotification(uint32_t flag) { mNotificationFlags |= flag; } void ClearNotifications() { mNotificationFlags = 0; } @@ -404,7 +413,8 @@ namespace l::ui { ~UIStorage() = default; UIHandle Add(std::unique_ptr container); - void Remove(int32_t id); + void Remove(const UIHandle& handle); + void Remove(UIContainer* container); protected: std::unordered_map> mContainers; int32_t mIdCounter = 1; @@ -412,5 +422,7 @@ namespace l::ui { UIHandle CreateContainer(UIStorage& uiStorage, uint32_t flags, UIRenderType renderType = UIRenderType::Rect, UIAlignH alignH = UIAlignH::Left, UIAlignV alignV = UIAlignV::Top, UILayoutH layoutH = UILayoutH::Fixed, UILayoutV layoutV = UILayoutV::Fixed); UIHandle CreateSplit(UIStorage& uiStorage, uint32_t flags, UIRenderType renderType, UISplitMode splitMode = UISplitMode::AppendV, UILayoutH layoutH = UILayoutH::Fixed, UILayoutV layoutV = UILayoutV::Fixed); + void DeleteContainer(UIStorage& uiStorage, UIHandle handle); + void DeleteContainer(UIStorage& uiStorage, UIContainer* container); } diff --git a/packages/rendering/include/rendering/ui/UINodeEditor.h b/packages/rendering/include/rendering/ui/UINodeEditor.h index 1d845a34..d3c96fcc 100644 --- a/packages/rendering/include/rendering/ui/UINodeEditor.h +++ b/packages/rendering/include/rendering/ui/UINodeEditor.h @@ -20,7 +20,7 @@ namespace l::ui { class UINodeEditor : public UIBase { public: - UINodeEditor(std::string_view editorName) : mUIWindow(editorName), mLinkIOVisitor(mUIStorage, mNGSchema) { + UINodeEditor(std::string_view editorName) : mUIWindow(editorName), mLinkIOVisitor(mUIStorage), mSelectVisitor(mUIStorage) { mUIRoot = CreateContainer(mUIStorage, l::ui::UIContainer_DragFlag | l::ui::UIContainer_ZoomFlag); mUIWindow.SetContentWindow([&]() { @@ -42,7 +42,11 @@ namespace l::ui { ImGui::Text("Node picker"); ImGui::Separator(); - mNGSchema.ForEachNodeType([&](std::string_view typeName, const std::vector& types) { + if (mNGSchema == nullptr) { + return; + } + + mNGSchema->ForEachNodeType([&](std::string_view typeName, const std::vector& types) { if (typeName.empty() || ImGui::TreeNode(typeName.data())) { for (auto it : types) { if (ImGui::MenuItem(it.GetName().data())) { @@ -53,8 +57,8 @@ namespace l::ui { p.y /= mUIRoot->GetScale(); p.x -= 3.0f; p.y -= 3.0f; - auto nodeId = mNGSchema.NewNode(it.GetId()); - auto node = mNGSchema.GetNode(nodeId); + auto nodeId = mNGSchema->NewNode(it.GetId()); + auto node = mNGSchema->GetNode(nodeId); if (node != nullptr) { auto uiNode = l::ui::CreateUINode(mUIStorage, *node, p); mUIRoot->Add(uiNode); @@ -68,15 +72,47 @@ namespace l::ui { }); }); } + ~UINodeEditor() = default; void Show() override; bool IsShowing() override; - void Open(); void Close(); - void Update(); + void SetNGSchema(l::nodegraph::NodeGraphSchema* ngSchema) { + mNGSchema = ngSchema; + mLinkIOVisitor.SetNGSchema(ngSchema); + } + + void Update() { + if (mUIWindow.IsShowing()) { + ImGuiIO& io = ImGui::GetIO(); + io.ConfigWindowsMoveFromTitleBarOnly = true; + + mUIInput.mCurPos = io.MousePos; + mUIInput.mPrevPos = io.MousePosPrev; + mUIInput.mScroll = io.MouseWheel; + mUIInput.mStarted = ImGui::IsMouseClicked(ImGuiMouseButton_Left); + mUIInput.mStopped = ImGui::IsMouseReleased(ImGuiMouseButton_Left); + + if (mUIWindow.IsHovered()) { + if (mUIRoot->Accept(mLinkIOVisitor, mUIInput, l::ui::UITraversalMode::DFS)) { + } + else if (mUIRoot->Accept(mSelectVisitor, mUIInput, l::ui::UITraversalMode::BFS)) { + } + else if (mUIRoot->Accept(mResizeVisitor, mUIInput, l::ui::UITraversalMode::DFS)) { + } + else if (mUIRoot->Accept(mMoveVisitor, mUIInput, l::ui::UITraversalMode::DFS)) { + } + else if (mUIRoot->Accept(mZoomVisitor, mUIInput, l::ui::UITraversalMode::DFS)) { + } + else if (mUIRoot->Accept(mDragVisitor, mUIInput, l::ui::UITraversalMode::DFS)) { + } + } + } + } + protected: UIWindow mUIWindow; @@ -84,13 +120,14 @@ namespace l::ui { UIHandle mUIRoot; InputState mUIInput; - l::nodegraph::NodeGraphSchema mNGSchema; + l::nodegraph::NodeGraphSchema* mNGSchema = nullptr; + UILinkIO mLinkIOVisitor; + UISelect mSelectVisitor; UIZoom mZoomVisitor; UIDrag mDragVisitor; UIMove mMoveVisitor; UIResize mResizeVisitor; - UILinkIO mLinkIOVisitor; }; diff --git a/packages/rendering/include/rendering/ui/UIVisitors.h b/packages/rendering/include/rendering/ui/UIVisitors.h index 1e496fd1..0935fbab 100644 --- a/packages/rendering/include/rendering/ui/UIVisitors.h +++ b/packages/rendering/include/rendering/ui/UIVisitors.h @@ -3,6 +3,8 @@ #include "rendering/ui/UIContainer.h" #include "nodegraph/NodeGraphSchema.h" +#include + namespace l::ui { class UIUpdate : public UIVisitor { @@ -45,6 +47,16 @@ namespace l::ui { UIContainer* mSourceContainer = nullptr; }; + class UISelect : public UIVisitor { + public: + UISelect(UIStorage& uiStorage) : mUIStorage(uiStorage) {} + + virtual bool Visit(UIContainer& container, const InputState& input); + protected: + std::unordered_set mSelectedContainers; + UIStorage& mUIStorage; + }; + class UIDraw : public UIVisitor { public: UIDraw(ImDrawList* drawList) : mDrawList(drawList) {} @@ -59,30 +71,37 @@ namespace l::ui { public: using HandlerFunctionType = bool(int32_t, int32_t, int32_t, int32_t, bool); - virtual bool Active(UIContainer& container, const InputState& input); - - UILinkIO(UIStorage& uiStorage, l::nodegraph::NodeGraphSchema& ngSchema) : mUIStorage(uiStorage), mNGSchema(ngSchema) {} + UILinkIO(UIStorage& uiStorage, l::nodegraph::NodeGraphSchema* ngSchema = nullptr) : mUIStorage(uiStorage), mNGSchema(ngSchema) {} ~UILinkIO() = default; + virtual bool Active(UIContainer& container, const InputState& input); virtual bool Visit(UIContainer& container, const InputState& input); bool LinkHandler(int32_t linkInputId, int32_t linkOutputId, int32_t inputChannel, int32_t outputChannel, bool connected) { - auto inputNode = mNGSchema.GetNode(linkInputId); + if (mNGSchema == nullptr) { + return false; + } + + auto inputNode = mNGSchema->GetNode(linkInputId); if (inputNode == nullptr) { return false; } if (connected) { - auto outputNode = mNGSchema.GetNode(linkOutputId); + auto outputNode = mNGSchema->GetNode(linkOutputId); return outputNode != nullptr && inputNode->SetInput(static_cast(inputChannel), *outputNode, static_cast(outputChannel)); } return inputNode->ClearInput(static_cast(inputChannel)); } + void SetNGSchema(l::nodegraph::NodeGraphSchema* ngSchema) { + mNGSchema = ngSchema; + } + protected: bool mDragging = false; UIHandle mLinkContainer; UIStorage& mUIStorage; - l::nodegraph::NodeGraphSchema& mNGSchema; + l::nodegraph::NodeGraphSchema* mNGSchema; }; diff --git a/packages/rendering/include/rendering/ui/UIWindow.h b/packages/rendering/include/rendering/ui/UIWindow.h index 9f17bb02..959d5be8 100644 --- a/packages/rendering/include/rendering/ui/UIWindow.h +++ b/packages/rendering/include/rendering/ui/UIWindow.h @@ -27,7 +27,7 @@ namespace l::ui { class UIWindow final : public UIBase { public: - UIWindow(std::string_view windowName) : mWindowName(windowName) {} + UIWindow(std::string_view windowName) : mWindowName(windowName), mPopupName(mWindowName + "Popup") {} ~UIWindow() = default; void Show() override; @@ -44,12 +44,14 @@ namespace l::ui { void SetBgColor(ImVec4 bgColor); protected: std::string mWindowName; + std::string mPopupName; ImGuiWindow* mWindowPtr = nullptr; - bool mOpened = false; + bool mOpened = false; bool mIsHovered = false; + bool mIsFocused = false; std::function mWindowFunction = nullptr; std::function mPointerPopupMenu = nullptr; - bool mPointerPopupOpen = false; + bool mPopupOpen = false; ImVec4 mBgColor = ImVec4(0.01f, 0.01f, 0.01f, 1.0f); }; diff --git a/packages/rendering/source/common/ui/UIContainer.cpp b/packages/rendering/source/common/ui/UIContainer.cpp index 05957b42..d328ddbc 100644 --- a/packages/rendering/source/common/ui/UIContainer.cpp +++ b/packages/rendering/source/common/ui/UIContainer.cpp @@ -67,6 +67,27 @@ namespace l::ui { } } + void UIContainer::Remove(UIContainer* container) { + for (auto it = mContent.begin(); it != mContent.end(); it++) { + auto containerPtr = *it; + if (containerPtr == container) { + mContent.erase(it); + break; + } + } + } + + void UIContainer::RemoveAll() { + mContent.clear(); + } + + void UIContainer::ForEachChild(std::function cb) { + for (auto it : mContent) { + it->ForEachChild(cb); + cb(it); + } + } + void UIContainer::Move(ImVec2 localChange) { mDisplayArea.mPosition.x += localChange.x; mDisplayArea.mPosition.y += localChange.y; @@ -265,8 +286,15 @@ namespace l::ui { return UIHandle{ id, stringId, mContainers.at(id).get() }; } - void UIStorage::Remove(int32_t id) { - mContainers.erase(id); + void UIStorage::Remove(const UIHandle& handle) { + //ASSERT(handle.GetId() == handle.Get()->GetId()); + mContainers.erase(handle.GetId()); + } + + void UIStorage::Remove(UIContainer* container) { + if (container != nullptr) { + mContainers.erase(container->GetId()); + } } UIHandle CreateContainer(UIStorage& uiStorage, uint32_t flags, UIRenderType renderType, UIAlignH alignH, UIAlignV alignV, UILayoutH layoutH, UILayoutV layoutV) { @@ -286,4 +314,19 @@ namespace l::ui { return uiStorage.Add(std::move(container)); } + + void DeleteContainer(UIStorage& uiStorage, UIHandle handle) { + handle.Get()->GetParent()->Remove(handle); + handle->ForEachChild([&](UIContainer* container) { + uiStorage.Remove(container); + }); + } + + void DeleteContainer(UIStorage& uiStorage, UIContainer* container) { + container->GetParent()->Remove(container); + container->ForEachChild([&](UIContainer* c) { + uiStorage.Remove(c); + }); + uiStorage.Remove(container); + } } diff --git a/packages/rendering/source/common/ui/UICreator.cpp b/packages/rendering/source/common/ui/UICreator.cpp index 29389e0f..817cf70f 100644 --- a/packages/rendering/source/common/ui/UICreator.cpp +++ b/packages/rendering/source/common/ui/UICreator.cpp @@ -10,7 +10,7 @@ namespace l::ui { auto numInputChannels = node.GetNumInputs(); auto numOutputChannels = node.GetNumOutputs(); - auto node4 = CreateSplit(uiStorage, l::ui::UIContainer_MoveFlag | l::ui::UIContainer_DrawFlag, l::ui::UIRenderType::RectFilled, l::ui::UISplitMode::AppendV); + auto node4 = CreateSplit(uiStorage, l::ui::UIContainer_MoveFlag | l::ui::UIContainer_DrawFlag | UIContainer_SelectFlag, l::ui::UIRenderType::RectFilled, l::ui::UISplitMode::AppendV); node4->SetPosition(p); node4->GetContainerArea().mMargin = 0.0f; ImVec2 titleSize = ImGui::CalcTextSize(node.GetName().data()); diff --git a/packages/rendering/source/common/ui/UINodeEditor.cpp b/packages/rendering/source/common/ui/UINodeEditor.cpp index 7e5bcdc7..02bb7009 100644 --- a/packages/rendering/source/common/ui/UINodeEditor.cpp +++ b/packages/rendering/source/common/ui/UINodeEditor.cpp @@ -3,7 +3,6 @@ #include namespace l::ui { - bool UINodeEditor::IsShowing() { return mUIWindow.IsShowing(); } @@ -19,31 +18,4 @@ namespace l::ui { void UINodeEditor::Close() { mUIWindow.Close(); } - - void UINodeEditor::Update() { - if (mUIWindow.IsShowing()) { - ImGuiIO& io = ImGui::GetIO(); - io.ConfigWindowsMoveFromTitleBarOnly = true; - - mUIInput.mCurPos = io.MousePos; - mUIInput.mPrevPos = io.MousePosPrev; - mUIInput.mScroll = io.MouseWheel; - mUIInput.mStarted = ImGui::IsMouseClicked(ImGuiMouseButton_Left); - mUIInput.mStopped = ImGui::IsMouseReleased(ImGuiMouseButton_Left); - - if (mUIWindow.IsHovered()) { - if (mUIRoot->Accept(mLinkIOVisitor, mUIInput, l::ui::UITraversalMode::DFS)) { - } - else if (mUIRoot->Accept(mResizeVisitor, mUIInput, l::ui::UITraversalMode::DFS)) { - } - else if (mUIRoot->Accept(mMoveVisitor, mUIInput, l::ui::UITraversalMode::DFS)) { - } - else if (mUIRoot->Accept(mZoomVisitor, mUIInput, l::ui::UITraversalMode::DFS)) { - } - else if (mUIRoot->Accept(mDragVisitor, mUIInput, l::ui::UITraversalMode::DFS)) { - } - } - } - } - } diff --git a/packages/rendering/source/common/ui/UIVisitors.cpp b/packages/rendering/source/common/ui/UIVisitors.cpp index d9e2614d..126632a9 100644 --- a/packages/rendering/source/common/ui/UIVisitors.cpp +++ b/packages/rendering/source/common/ui/UIVisitors.cpp @@ -149,6 +149,44 @@ namespace l::ui { return false; } + bool UISelect::Visit(UIContainer& container, const InputState& input) { + if (!container.HasConfigFlag(UIContainer_SelectFlag)) { + return false; + } + if (input.mStarted) { + if (ImGui::IsKeyDown(ImGuiKey::ImGuiKey_LeftShift)) { + auto& layoutArea = container.GetLayoutArea(); + if (Overlap(input.GetLocalPos(), container.GetPosition(), container.GetPositionAtSize(), layoutArea)) { + if (!mSelectedContainers.contains(&container)) { + mSelectedContainers.emplace(&container); + container.SetNotification(UIContainer_SelectFlag); + } + else { + mSelectedContainers.erase(&container); + container.ClearNotifications(); + } + } + } + else if (!mSelectedContainers.empty()) { + for (auto it : mSelectedContainers) { + it->ClearNotifications(); + } + mSelectedContainers.clear(); + return true; + } + } + if (!mSelectedContainers.empty()) { + if (ImGui::IsKeyDown(ImGuiKey::ImGuiKey_Delete)) { + for (auto it : mSelectedContainers) { + DeleteContainer(mUIStorage, it); + } + mSelectedContainers.clear(); + return true; + } + } + return false; + } + bool UIDraw::Visit(UIContainer& container, const InputState& input) { if (!mDebug && !container.HasConfigFlag(UIContainer_DrawFlag)) { return false; @@ -258,8 +296,8 @@ namespace l::ui { case l::ui::UIRenderType::RectFilled: case l::ui::UIRenderType::Texture: case l::ui::UIRenderType::LinkH: - if (container.HasConfigFlag(UIContainer_InputFlag)) { - + if (container.HasConfigFlag(UIContainer_SelectFlag) && container.HasNotification(UIContainer_SelectFlag)) { + mDrawList->AddRect(p1, p2, ImColor(ImVec4(0.9f, 1.0f, 1.0f, 1.0f)), 0.0f, 0, 2.0f); } if (container.HasConfigFlag(ui::UIContainer_ResizeFlag)) { float size = 3.0f * layoutArea.mScale; @@ -348,6 +386,7 @@ namespace l::ui { } else { mLinkContainer->GetParent()->Remove(mLinkContainer); + mUIStorage.Remove(mLinkContainer); mDragging = false; mLinkContainer.Reset(); } diff --git a/packages/rendering/source/common/ui/UIWindow.cpp b/packages/rendering/source/common/ui/UIWindow.cpp index 78cdfe3e..0be30923 100644 --- a/packages/rendering/source/common/ui/UIWindow.cpp +++ b/packages/rendering/source/common/ui/UIWindow.cpp @@ -37,44 +37,34 @@ namespace l::ui { } void UIWindow::Show() { - if (mOpened) { - ImGui::PushStyleColor(ImGuiCol_WindowBg, mBgColor); - if (ImGui::Begin(mWindowName.c_str(), &mOpened, ImGuiWindowFlags_MenuBar)) { - if (mOpened) { - mWindowPtr = ImGui::GetCurrentWindowRead(); - mIsHovered = ImGui::IsWindowHovered();; - } - - if (mWindowPtr && mWindowFunction) { - mWindowFunction(); - } + ImGui::PushStyleColor(ImGuiCol_WindowBg, mBgColor); + if (ImGui::Begin(mWindowName.c_str(), &mOpened, ImGuiWindowFlags_MenuBar)) { + mWindowPtr = ImGui::GetCurrentWindowRead(); + mIsHovered = ImGui::IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup); + mIsFocused = ImGui::IsWindowFocused(ImGuiFocusedFlags_NoPopupHierarchy); + + if (mWindowPtr && mWindowFunction) { + mWindowFunction(); + } - if (mPointerPopupMenu) { - if (ImGui::IsMouseClicked(ImGuiMouseButton_Right)) { - ImGui::OpenPopup("PointerPopupMenu"); - mPointerPopupOpen = true; - } - if (mPointerPopupOpen) { - ImGuiIO& io = ImGui::GetIO(); - ImGui::SetNextWindowPos(io.MousePos, ImGuiCond_Appearing, ImVec2(0.0f, 0.0f)); - } - if (ImGui::BeginPopup("PointerPopupMenu", ImGuiWindowFlags_AlwaysAutoResize)) { - mPointerPopupMenu(); - ImGui::EndPopup(); - } - else { - mPointerPopupOpen = false; - } + if (mPointerPopupMenu) { + if (mIsHovered && ImGui::IsMouseClicked(ImGuiMouseButton_Right)) { + ImGui::OpenPopup(mPopupName.c_str(), ImGuiPopupFlags_NoOpenOverExistingPopup); + ImGuiIO& io = ImGui::GetIO(); + ImGui::SetNextWindowPos(io.MousePos, ImGuiCond_Appearing, ImVec2(0.0f, 0.0f)); } - - if (!mOpened) { - mWindowPtr = nullptr; - mIsHovered = false; + if (ImGui::BeginPopup(mPopupName.c_str(), ImGuiWindowFlags_AlwaysAutoResize)) { + mPointerPopupMenu(); + ImGui::EndPopup(); } } - ImGui::End(); - ImGui::PopStyleColor(); } + if (!mOpened) { + mIsHovered = false; + mIsFocused = false; + } + ImGui::End(); + ImGui::PopStyleColor(); } void UIWindow::SetBgColor(ImVec4 bgColor) { From 5aeae2d4b3a74a0c357e960f47320b4c7abd3bff Mon Sep 17 00:00:00 2001 From: lnd3 Date: Thu, 29 Aug 2024 01:17:43 +0200 Subject: [PATCH 076/125] Propagate removal of ui nodes into the node graph. --- .../nodegraph/include/nodegraph/NodeGraph.h | 3 ++ .../include/nodegraph/NodeGraphSchema.h | 2 +- .../nodegraph/source/common/NodeGraph.cpp | 25 +++++++++++++++ .../source/common/NodeGraphSchema.cpp | 4 +++ .../include/rendering/ui/UINodeEditor.h | 1 + .../include/rendering/ui/UIVisitors.h | 9 ++++-- .../source/common/ui/UIContainer.cpp | 31 ++++++++++++------- .../rendering/source/common/ui/UICreator.cpp | 1 + .../rendering/source/common/ui/UIVisitors.cpp | 10 ++++-- 9 files changed, 69 insertions(+), 17 deletions(-) diff --git a/packages/nodegraph/include/nodegraph/NodeGraph.h b/packages/nodegraph/include/nodegraph/NodeGraph.h index 9603417d..e37e6f69 100644 --- a/packages/nodegraph/include/nodegraph/NodeGraph.h +++ b/packages/nodegraph/include/nodegraph/NodeGraph.h @@ -49,6 +49,7 @@ namespace l::nodegraph { int8_t mInputFromOutputChannel = 0; std::string mName; + void Reset(); bool HasInput(); float Get(); }; @@ -84,6 +85,8 @@ namespace l::nodegraph { virtual bool SetInput(int8_t inputChannel, NodeGraphGroup& source, int8_t sourceOutputChannel, bool nodeIsInsideGroup); virtual bool SetInput(int8_t inputChannel, float constant); virtual bool SetInput(int8_t inputChannel, float* floatPtr); + + virtual bool RemoveSource(void* source); protected: void PreUpdate(); virtual void ProcessOperation(); diff --git a/packages/nodegraph/include/nodegraph/NodeGraphSchema.h b/packages/nodegraph/include/nodegraph/NodeGraphSchema.h index c6e8190e..4ea9bda1 100644 --- a/packages/nodegraph/include/nodegraph/NodeGraphSchema.h +++ b/packages/nodegraph/include/nodegraph/NodeGraphSchema.h @@ -47,11 +47,11 @@ namespace l::nodegraph { void SetCustomCreator(std::function customCreator); int32_t NewNode(int32_t typeId); + bool RemoveNode(int32_t nodeId); NodeGraphBase* GetNode(int32_t nodeId); void ForEachNodeType(std::function&)> cb) const; void RegisterNodeType(const std::string& typeGroup, int32_t uniqueTypeId, std::string_view typeName); - void AddInput(); protected: NodeGraphGroup mMainNodeGraph; diff --git a/packages/nodegraph/source/common/NodeGraph.cpp b/packages/nodegraph/source/common/NodeGraph.cpp index f936debd..9097c35d 100644 --- a/packages/nodegraph/source/common/NodeGraph.cpp +++ b/packages/nodegraph/source/common/NodeGraph.cpp @@ -149,6 +149,18 @@ namespace l::nodegraph { return true; } + bool NodeGraphBase::RemoveSource(void* source) { + int32_t sourceRemoved = 0; + for (auto& it : mInputs) { + if (it.mInputType == InputType::INPUT_NODE && it.mInput.mInputNode == source || + it.mInputType == InputType::INPUT_VALUE && it.mInput.mInputFloat == source) { + it.Reset(); + sourceRemoved++; + } + } + return sourceRemoved > 0 ? true : false; + } + std::string_view NodeGraphBase::GetName() { return mName; } @@ -191,6 +203,12 @@ namespace l::nodegraph { } } + void NodeGraphInput::Reset() { + mInput.mInputNode = nullptr; + mInputType = InputType::INPUT_EMPTY; + mInputFromOutputChannel = 0; + } + bool NodeGraphInput::HasInput() { switch (mInputType) { case InputType::INPUT_NODE: @@ -289,6 +307,13 @@ namespace l::nodegraph { } bool NodeGraphGroup::RemoveNode(int32_t id) { + auto node = GetNode(id); + int32_t sourceCount = 0; + for (auto& it : mNodes) { + if (it->RemoveSource(node)) { + sourceCount++; + } + } auto count = std::erase_if(mNodes, [&](const std::unique_ptr& node) { if (node->GetId() == id) { return true; diff --git a/packages/nodegraph/source/common/NodeGraphSchema.cpp b/packages/nodegraph/source/common/NodeGraphSchema.cpp index c2ba0eb2..befe8b20 100644 --- a/packages/nodegraph/source/common/NodeGraphSchema.cpp +++ b/packages/nodegraph/source/common/NodeGraphSchema.cpp @@ -50,6 +50,10 @@ namespace l::nodegraph { return node == nullptr ? 0 : node->GetId(); } + bool NodeGraphSchema::RemoveNode(int32_t nodeId) { + return mMainNodeGraph.RemoveNode(nodeId); + } + NodeGraphBase* NodeGraphSchema::GetNode(int32_t nodeId) { return mMainNodeGraph.GetNode(nodeId); } diff --git a/packages/rendering/include/rendering/ui/UINodeEditor.h b/packages/rendering/include/rendering/ui/UINodeEditor.h index d3c96fcc..c88a390a 100644 --- a/packages/rendering/include/rendering/ui/UINodeEditor.h +++ b/packages/rendering/include/rendering/ui/UINodeEditor.h @@ -83,6 +83,7 @@ namespace l::ui { void SetNGSchema(l::nodegraph::NodeGraphSchema* ngSchema) { mNGSchema = ngSchema; mLinkIOVisitor.SetNGSchema(ngSchema); + mSelectVisitor.SetNGSchema(ngSchema); } void Update() { diff --git a/packages/rendering/include/rendering/ui/UIVisitors.h b/packages/rendering/include/rendering/ui/UIVisitors.h index 0935fbab..cd2b6a38 100644 --- a/packages/rendering/include/rendering/ui/UIVisitors.h +++ b/packages/rendering/include/rendering/ui/UIVisitors.h @@ -52,9 +52,14 @@ namespace l::ui { UISelect(UIStorage& uiStorage) : mUIStorage(uiStorage) {} virtual bool Visit(UIContainer& container, const InputState& input); + + void SetNGSchema(l::nodegraph::NodeGraphSchema* ngSchema) { + mNGSchema = ngSchema; + } protected: std::unordered_set mSelectedContainers; UIStorage& mUIStorage; + l::nodegraph::NodeGraphSchema* mNGSchema = nullptr; }; class UIDraw : public UIVisitor { @@ -71,7 +76,7 @@ namespace l::ui { public: using HandlerFunctionType = bool(int32_t, int32_t, int32_t, int32_t, bool); - UILinkIO(UIStorage& uiStorage, l::nodegraph::NodeGraphSchema* ngSchema = nullptr) : mUIStorage(uiStorage), mNGSchema(ngSchema) {} + UILinkIO(UIStorage& uiStorage) : mUIStorage(uiStorage) {} ~UILinkIO() = default; virtual bool Active(UIContainer& container, const InputState& input); @@ -101,7 +106,7 @@ namespace l::ui { bool mDragging = false; UIHandle mLinkContainer; UIStorage& mUIStorage; - l::nodegraph::NodeGraphSchema* mNGSchema; + l::nodegraph::NodeGraphSchema* mNGSchema = nullptr; }; diff --git a/packages/rendering/source/common/ui/UIContainer.cpp b/packages/rendering/source/common/ui/UIContainer.cpp index d328ddbc..78a860cb 100644 --- a/packages/rendering/source/common/ui/UIContainer.cpp +++ b/packages/rendering/source/common/ui/UIContainer.cpp @@ -287,8 +287,9 @@ namespace l::ui { } void UIStorage::Remove(const UIHandle& handle) { - //ASSERT(handle.GetId() == handle.Get()->GetId()); - mContainers.erase(handle.GetId()); + if (handle.IsValid()) { + mContainers.erase(handle.GetId()); + } } void UIStorage::Remove(UIContainer* container) { @@ -316,17 +317,25 @@ namespace l::ui { } void DeleteContainer(UIStorage& uiStorage, UIHandle handle) { - handle.Get()->GetParent()->Remove(handle); - handle->ForEachChild([&](UIContainer* container) { - uiStorage.Remove(container); - }); + if (handle.IsValid()) { + ASSERT(handle.Get()->GetParent() != nullptr); + handle.Get()->GetParent()->Remove(handle); + handle->ForEachChild([&](UIContainer* container) { + uiStorage.Remove(container); + }); + uiStorage.Remove(handle); + } } void DeleteContainer(UIStorage& uiStorage, UIContainer* container) { - container->GetParent()->Remove(container); - container->ForEachChild([&](UIContainer* c) { - uiStorage.Remove(c); - }); - uiStorage.Remove(container); + if (container != nullptr) { + ASSERT(container->GetParent() != nullptr); + container->GetParent()->Remove(container); + container->ForEachChild([&](UIContainer* c) { + uiStorage.Remove(c); + }); + + uiStorage.Remove(container); + } } } diff --git a/packages/rendering/source/common/ui/UICreator.cpp b/packages/rendering/source/common/ui/UICreator.cpp index 817cf70f..a18960ef 100644 --- a/packages/rendering/source/common/ui/UICreator.cpp +++ b/packages/rendering/source/common/ui/UICreator.cpp @@ -13,6 +13,7 @@ namespace l::ui { auto node4 = CreateSplit(uiStorage, l::ui::UIContainer_MoveFlag | l::ui::UIContainer_DrawFlag | UIContainer_SelectFlag, l::ui::UIRenderType::RectFilled, l::ui::UISplitMode::AppendV); node4->SetPosition(p); node4->GetContainerArea().mMargin = 0.0f; + node4->SetNodeId(node.GetId()); ImVec2 titleSize = ImGui::CalcTextSize(node.GetName().data()); titleSize.x += node4->GetContainerArea().mMargin * 2 + 2.0f; node4->SetSize(ImVec2(titleSize.x < 100.0f ? 100.0f : titleSize.x, 22.0f + 19.0f * (numInputChannels > numOutputChannels ? numInputChannels : numOutputChannels))); diff --git a/packages/rendering/source/common/ui/UIVisitors.cpp b/packages/rendering/source/common/ui/UIVisitors.cpp index 126632a9..80bbdf19 100644 --- a/packages/rendering/source/common/ui/UIVisitors.cpp +++ b/packages/rendering/source/common/ui/UIVisitors.cpp @@ -178,6 +178,9 @@ namespace l::ui { if (!mSelectedContainers.empty()) { if (ImGui::IsKeyDown(ImGuiKey::ImGuiKey_Delete)) { for (auto it : mSelectedContainers) { + if (mNGSchema != nullptr) { + mNGSchema->RemoveNode(it->GetNodeId()); + } DeleteContainer(mUIStorage, it); } mSelectedContainers.clear(); @@ -342,8 +345,10 @@ namespace l::ui { ImVec2 size = container.GetCoParent()->GetSize(); ImVec2 pT = container.GetCoParent()->GetLayoutArea().Transform(pCenter); if (OverlapCircle(input.mCurPos, pT, size.x * container.GetCoParent()->GetLayoutArea().mScale)) { - mDragging = true; + DeleteContainer(mUIStorage, mLinkContainer); mLinkContainer.mContainer = &container; + LinkHandler(container.GetNodeId(), mLinkContainer->GetParent()->GetNodeId(), container.GetChannelId(), mLinkContainer->GetParent()->GetChannelId(), false); + mDragging = true; return true; } } @@ -385,8 +390,7 @@ namespace l::ui { mLinkContainer.Reset(); } else { - mLinkContainer->GetParent()->Remove(mLinkContainer); - mUIStorage.Remove(mLinkContainer); + DeleteContainer(mUIStorage, mLinkContainer); mDragging = false; mLinkContainer.Reset(); } From 90cbc50a51eb19ea59c9c5328aebaa2675b42bd7 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Thu, 29 Aug 2024 01:19:54 +0200 Subject: [PATCH 077/125] Fix warning. --- packages/nodegraph/source/common/NodeGraph.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/nodegraph/source/common/NodeGraph.cpp b/packages/nodegraph/source/common/NodeGraph.cpp index 9097c35d..457f31f8 100644 --- a/packages/nodegraph/source/common/NodeGraph.cpp +++ b/packages/nodegraph/source/common/NodeGraph.cpp @@ -152,8 +152,8 @@ namespace l::nodegraph { bool NodeGraphBase::RemoveSource(void* source) { int32_t sourceRemoved = 0; for (auto& it : mInputs) { - if (it.mInputType == InputType::INPUT_NODE && it.mInput.mInputNode == source || - it.mInputType == InputType::INPUT_VALUE && it.mInput.mInputFloat == source) { + if ((it.mInputType == InputType::INPUT_NODE && it.mInput.mInputNode == source) || + (it.mInputType == InputType::INPUT_VALUE && it.mInput.mInputFloat == source)) { it.Reset(); sourceRemoved++; } From 3df99bf53c2ceb613297debea61ac09c1e41c454 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Thu, 29 Aug 2024 15:33:36 +0200 Subject: [PATCH 078/125] Add constants nodes and a sine generator. Fix some bugs and hook in node graph to node ui creation. --- .../nodegraph/include/nodegraph/NodeGraph.h | 58 ++++++++++------ .../include/nodegraph/NodeGraphOperations.h | 66 ++++++++++++++++--- .../include/nodegraph/NodeGraphSchema.h | 25 ++++--- .../nodegraph/source/common/NodeGraph.cpp | 53 ++++++++++----- .../source/common/NodeGraphOperations.cpp | 36 ++++++++++ .../source/common/NodeGraphSchema.cpp | 35 +++++++--- .../include/rendering/ui/UIContainer.h | 4 +- .../include/rendering/ui/UINodeEditor.h | 7 +- .../include/rendering/ui/UIVisitors.h | 12 +++- .../rendering/source/common/ui/UICreator.cpp | 25 ++++++- .../rendering/source/common/ui/UIVisitors.cpp | 12 +++- 11 files changed, 262 insertions(+), 71 deletions(-) diff --git a/packages/nodegraph/include/nodegraph/NodeGraph.h b/packages/nodegraph/include/nodegraph/NodeGraph.h index e37e6f69..3224f2e6 100644 --- a/packages/nodegraph/include/nodegraph/NodeGraph.h +++ b/packages/nodegraph/include/nodegraph/NodeGraph.h @@ -67,11 +67,13 @@ namespace l::nodegraph { virtual void SetNumInputs(int8_t numInputs); virtual void SetNumOutputs(int8_t outputCount); + virtual void SetNumConstants(int8_t numConstants); virtual int8_t GetNumInputs(); virtual int8_t GetNumOutputs(); + virtual int8_t GetNumConstants(); - virtual float Get(int8_t outputChannel); + virtual float& Get(int8_t outputChannel); virtual std::string_view GetName(); virtual std::string_view GetInputName(int8_t inputChannel); @@ -83,10 +85,10 @@ namespace l::nodegraph { virtual bool SetInput(int8_t inputChannel, NodeGraphBase& source, int8_t sourceOutputChannel); virtual bool SetInput(int8_t inputChannel, NodeGraphGroup& source, int8_t sourceOutputChannel, bool nodeIsInsideGroup); - virtual bool SetInput(int8_t inputChannel, float constant); - virtual bool SetInput(int8_t inputChannel, float* floatPtr); + virtual bool SetConstant(int8_t inputChannel, float constant); + virtual bool SetExternalInput(int8_t inputChannel, float* floatPtr); - virtual bool RemoveSource(void* source); + virtual bool RemoveInput(void* source); protected: void PreUpdate(); virtual void ProcessOperation(); @@ -95,19 +97,22 @@ namespace l::nodegraph { std::vector mInputs; std::vector mOutputs; - int32_t mId; + int32_t mId = -1; std::string mName; + int8_t mInputCount = 0; + int8_t mConstantCount = 0; }; class NodeGraphOp { public: - NodeGraphOp(int8_t numInputs = 1, int8_t numOutputs = 1) : + NodeGraphOp(int8_t numInputs = 1, int8_t numOutputs = 1, int8_t numConstants = 0) : mNumInputs(numInputs), - mNumOutputs(numOutputs) + mNumOutputs(numOutputs), + mNumConstants(numConstants) {} - std::string defaultInStrings[3] = { "In 1", "In 2", "In 3" }; - std::string defaultOutStrings[3] = { "Out 1", "Out 2", "Out 3" }; + std::string defaultInStrings[4] = { "In 1", "In 2", "In 3", "In 4" }; + std::string defaultOutStrings[4] = { "Out 1", "Out 2", "Out 3", "Out 4" }; virtual ~NodeGraphOp() = default; virtual void Reset() {} @@ -115,9 +120,11 @@ namespace l::nodegraph { virtual void SetNumInputs(int8_t numInputs); virtual void SetNumOutputs(int8_t numOutputs); + virtual void SetNumConstants(int8_t numConstants); int8_t GetNumInputs(); int8_t GetNumOutputs(); - + int8_t GetNumConstants(); + virtual std::string_view GetInputName(int8_t inputChannel) { return defaultInStrings[inputChannel]; }; @@ -129,14 +136,15 @@ namespace l::nodegraph { } protected: - int8_t mNumInputs; - int8_t mNumOutputs; + int8_t mNumInputs = 0; + int8_t mNumOutputs = 0; + int8_t mNumConstants = 0; }; class GraphDataCopy : public NodeGraphOp { public: - GraphDataCopy(int8_t numChannels = 1) : - NodeGraphOp(numChannels, numChannels) + GraphDataCopy(int8_t) : + NodeGraphOp(0) {} virtual ~GraphDataCopy() = default; @@ -146,23 +154,31 @@ namespace l::nodegraph { template>> class NodeGraph : public NodeGraphBase { public: - NodeGraph(std::string_view name = "") : - NodeGraphBase(name) - { + NodeGraph(int8_t mode = 0) : mOperation(mode) { SetNumInputs(mOperation.GetNumInputs()); SetNumOutputs(mOperation.GetNumOutputs()); + SetNumConstants(mOperation.GetNumConstants()); } virtual void SetNumInputs(int8_t numInputs) { - mInputs.resize(numInputs); + NodeGraphBase::SetNumInputs(numInputs); mOperation.SetNumInputs(numInputs); } virtual void SetNumOutputs(int8_t numOutputs) { - mOutputs.resize(numOutputs); + NodeGraphBase::SetNumOutputs(numOutputs); mOperation.SetNumOutputs(numOutputs); } + virtual void SetNumConstants(int8_t numConstants) { + NodeGraphBase::SetNumConstants(numConstants); + mOperation.SetNumConstants(numConstants); + for (int8_t i = mInputCount; i < mInputCount + mConstantCount; i++) { + SetConstant(i, 1.0f); + } + Update(); + } + virtual void Reset() override { NodeGraphBase::Reset(); mOperation.Reset(); @@ -223,8 +239,8 @@ namespace l::nodegraph { bool RemoveNode(int32_t id); template>> - l::nodegraph::NodeGraphBase* NewNode() { - mNodes.push_back(std::make_unique>()); + l::nodegraph::NodeGraphBase* NewNode(int8_t mode = 0) { + mNodes.push_back(std::make_unique>(mode)); return mNodes.back().get(); } diff --git a/packages/nodegraph/include/nodegraph/NodeGraphOperations.h b/packages/nodegraph/include/nodegraph/NodeGraphOperations.h index 38ba813b..abedf740 100644 --- a/packages/nodegraph/include/nodegraph/NodeGraphOperations.h +++ b/packages/nodegraph/include/nodegraph/NodeGraphOperations.h @@ -13,9 +13,56 @@ namespace l::nodegraph { /* Mathematical operations */ + class GraphSourceConstants : public NodeGraphOp { + public: + GraphSourceConstants(int8_t mode = 0) : + NodeGraphOp(0, mode + 1, mode + 1) + {} + + virtual ~GraphSourceConstants() = default; + void Process(std::vector& inputs, std::vector& outputs) override; + std::string_view GetName() override { + return "Constant"; + } + }; + + class GraphSourceSine : public NodeGraphOp { + public: + GraphSourceSine(int8_t) : + NodeGraphOp(4, 2) + {} + + std::string defaultInStrings[4] = { "Time", "Freq Hz", "Freq Mod", "Phase Mod"}; + std::string defaultOutStrings[2] = { "Sine", "Phase"}; + + virtual ~GraphSourceSine() = default; + void Process(std::vector& inputs, std::vector& outputs) override; + + void Reset() override { + mPhase = 0.0f; + mPrevTime = 0.0f; + } + + std::string_view GetInputName(int8_t inputChannel) { + return defaultInStrings[inputChannel]; + } + + std::string_view GetOutputName(int8_t outputChannel) { + return defaultOutStrings[outputChannel]; + } + + std::string_view GetName() override { + return "Sine"; + } + + protected: + float mPhase = 0.0f; + float mPrevTime = 0.0f; + }; + class GraphNumericAdd : public NodeGraphOp { public: - GraphNumericAdd() : + GraphNumericAdd(int8_t) : NodeGraphOp(2, 1) {} virtual ~GraphNumericAdd() = default; @@ -27,7 +74,7 @@ namespace l::nodegraph { class GraphNumericMultiply : public NodeGraphOp { public: - GraphNumericMultiply() : + GraphNumericMultiply(int8_t) : NodeGraphOp(2, 1) {} @@ -40,7 +87,7 @@ namespace l::nodegraph { class GraphNumericSubtract : public NodeGraphOp { public: - GraphNumericSubtract() : + GraphNumericSubtract(int8_t) : NodeGraphOp(2, 1) {} virtual ~GraphNumericSubtract() = default; @@ -52,7 +99,7 @@ namespace l::nodegraph { class GraphNumericNegate : public NodeGraphOp { public: - GraphNumericNegate() : + GraphNumericNegate(int8_t) : NodeGraphOp(1, 1) {} @@ -65,7 +112,7 @@ namespace l::nodegraph { class GraphNumericIntegral : public NodeGraphOp { public: - GraphNumericIntegral() : + GraphNumericIntegral(int8_t) : NodeGraphOp(1, 1) {} @@ -84,7 +131,7 @@ namespace l::nodegraph { class GraphLogicalAnd : public NodeGraphOp { public: - GraphLogicalAnd() : + GraphLogicalAnd(int8_t) : NodeGraphOp(2, 1) {} @@ -97,7 +144,7 @@ namespace l::nodegraph { class GraphLogicalOr : public NodeGraphOp { public: - GraphLogicalOr() : + GraphLogicalOr(int8_t) : NodeGraphOp(2, 1) {} @@ -110,7 +157,7 @@ namespace l::nodegraph { class GraphLogicalXor : public NodeGraphOp { public: - GraphLogicalXor() : + GraphLogicalXor(int8_t) : NodeGraphOp(2, 1) {} @@ -128,7 +175,7 @@ namespace l::nodegraph { std::string defaultInStrings[3] = { "Cutoff", "Resonance", "Data"}; std::string defaultOutStrings[1] = { "Out" }; - GraphFilterLowpass() : + GraphFilterLowpass(int8_t) : NodeGraphOp(3, 1) {} @@ -143,6 +190,7 @@ namespace l::nodegraph { std::string_view GetOutputName(int8_t outputChannel) { return defaultOutStrings[outputChannel]; } + std::string_view GetName() override { return "Lowpass"; } diff --git a/packages/nodegraph/include/nodegraph/NodeGraphSchema.h b/packages/nodegraph/include/nodegraph/NodeGraphSchema.h index 4ea9bda1..035a3b2b 100644 --- a/packages/nodegraph/include/nodegraph/NodeGraphSchema.h +++ b/packages/nodegraph/include/nodegraph/NodeGraphSchema.h @@ -32,15 +32,20 @@ namespace l::nodegraph { using CustomCreateFunctionType = NodeGraphBase*(int32_t, NodeGraphGroup&); NodeGraphSchema() { - RegisterNodeType("Numeric", 0, "Add"); - RegisterNodeType("Numeric", 1, "Subtract"); - RegisterNodeType("Numeric", 2, "Negate"); - RegisterNodeType("Numeric", 3, "Multiply"); - RegisterNodeType("Numeric", 4, "Integral"); - RegisterNodeType("Logic", 50, "And"); - RegisterNodeType("Logic", 51, "Or"); - RegisterNodeType("Logic", 52, "Xor"); - RegisterNodeType("Filter", 100, "Lowpass Filter"); + RegisterNodeType("Source", 0, "Constant1"); + RegisterNodeType("Source", 1, "Constant2"); + RegisterNodeType("Source", 2, "Constant3"); + RegisterNodeType("Source", 3, "Constant4"); + RegisterNodeType("Source", 4, "Sine"); + RegisterNodeType("Numeric", 50, "Add"); + RegisterNodeType("Numeric", 51, "Subtract"); + RegisterNodeType("Numeric", 52, "Negate"); + RegisterNodeType("Numeric", 53, "Multiply"); + RegisterNodeType("Numeric", 54, "Integral"); + RegisterNodeType("Logic", 100, "And"); + RegisterNodeType("Logic", 101, "Or"); + RegisterNodeType("Logic", 102, "Xor"); + RegisterNodeType("Filter", 150, "Lowpass Filter"); } ~NodeGraphSchema() = default; @@ -51,7 +56,7 @@ namespace l::nodegraph { NodeGraphBase* GetNode(int32_t nodeId); void ForEachNodeType(std::function&)> cb) const; void RegisterNodeType(const std::string& typeGroup, int32_t uniqueTypeId, std::string_view typeName); - + void Update(); protected: NodeGraphGroup mMainNodeGraph; diff --git a/packages/nodegraph/source/common/NodeGraph.cpp b/packages/nodegraph/source/common/NodeGraph.cpp index 457f31f8..dd05d20a 100644 --- a/packages/nodegraph/source/common/NodeGraph.cpp +++ b/packages/nodegraph/source/common/NodeGraph.cpp @@ -22,21 +22,33 @@ namespace l::nodegraph { } void NodeGraphBase::SetNumInputs(int8_t numInputs) { - mInputs.resize(numInputs); + mInputCount = numInputs; + mInputs.resize(mInputCount + mConstantCount); } void NodeGraphBase::SetNumOutputs(int8_t outputCount) { mOutputs.resize(outputCount); } + void NodeGraphBase::SetNumConstants(int8_t numConstants) { + mConstantCount = numConstants; + mInputs.resize(mInputCount + mConstantCount); + } + int8_t NodeGraphBase::GetNumInputs() { - return static_cast(mInputs.size()); + ASSERT(mInputs.size() == mInputCount + mConstantCount); + return static_cast(mInputCount); } int8_t NodeGraphBase::GetNumOutputs() { return static_cast(mOutputs.size()); } + int8_t NodeGraphBase::GetNumConstants() { + ASSERT(mInputs.size() == mInputCount + mConstantCount); + return static_cast(mConstantCount); + } + void NodeGraphBase::Reset() { for (auto& link : mInputs) { if (link.mInputType == InputType::INPUT_NODE && link.mInput.mInputNode != nullptr) { @@ -71,7 +83,7 @@ namespace l::nodegraph { mProcessUpdateHasRun = true; } - float NodeGraphBase::Get(int8_t outputChannel) { + float& NodeGraphBase::Get(int8_t outputChannel) { ASSERT(IsValidInOutNum(outputChannel, mOutputs.size())); return mOutputs.at(outputChannel).mOutput; } @@ -125,9 +137,10 @@ namespace l::nodegraph { return true; } - bool NodeGraphBase::SetInput(int8_t inputChannel, float constant) { + bool NodeGraphBase::SetConstant(int8_t inputChannel, float constant) { + ASSERT(inputChannel >= mInputCount && inputChannel < mInputCount + mConstantCount); auto& input = mInputs.at(inputChannel); - if (!IsValidInOutNum(inputChannel, mInputs.size()) || input.HasInput()) { + if (!IsValidInOutNum(inputChannel, mInputs.size())) { return false; } @@ -137,9 +150,9 @@ namespace l::nodegraph { return true; } - bool NodeGraphBase::SetInput(int8_t inputChannel, float* floatPtr) { + bool NodeGraphBase::SetExternalInput(int8_t inputChannel, float* floatPtr) { auto& input = mInputs.at(inputChannel); - if (!IsValidInOutNum(inputChannel, mInputs.size()) || input.HasInput()) { + if (!IsValidInOutNum(inputChannel, mInputs.size())) { return false; } @@ -149,7 +162,7 @@ namespace l::nodegraph { return true; } - bool NodeGraphBase::RemoveSource(void* source) { + bool NodeGraphBase::RemoveInput(void* source) { int32_t sourceRemoved = 0; for (auto& it : mInputs) { if ((it.mInputType == InputType::INPUT_NODE && it.mInput.mInputNode == source) || @@ -189,6 +202,10 @@ namespace l::nodegraph { mNumOutputs = numOutputs; } + void NodeGraphOp::SetNumConstants(int8_t numConstants) { + mNumConstants = numConstants; + } + int8_t NodeGraphOp::GetNumInputs() { return mNumInputs; } @@ -197,6 +214,10 @@ namespace l::nodegraph { return mNumOutputs; } + int8_t NodeGraphOp::GetNumConstants() { + return mNumConstants; + } + void GraphDataCopy::Process(std::vector& inputs, std::vector& outputs) { for (size_t i = 0; i < inputs.size() && i < outputs.size(); i++) { outputs.at(i).mOutput = inputs.at(i).Get(); @@ -204,9 +225,11 @@ namespace l::nodegraph { } void NodeGraphInput::Reset() { - mInput.mInputNode = nullptr; - mInputType = InputType::INPUT_EMPTY; - mInputFromOutputChannel = 0; + if (mInputType == InputType::INPUT_NODE || mInputType == InputType::INPUT_VALUE) { + mInput.mInputNode = nullptr; + mInputType = InputType::INPUT_EMPTY; + mInputFromOutputChannel = 0; + } } bool NodeGraphInput::HasInput() { @@ -217,7 +240,7 @@ namespace l::nodegraph { } break; case InputType::INPUT_CONSTANT: - return true; + break; case InputType::INPUT_VALUE: return mInput.mInputFloat != nullptr; case InputType::INPUT_EMPTY: @@ -264,11 +287,11 @@ namespace l::nodegraph { } void NodeGraphGroup::SetInput(int8_t inputChannel, float constant) { - mInputNode.SetInput(inputChannel, constant); + mInputNode.SetConstant(inputChannel, constant); } void NodeGraphGroup::SetInput(int8_t inputChannel, float* floatPtr) { - mInputNode.SetInput(inputChannel, floatPtr); + mInputNode.SetExternalInput(inputChannel, floatPtr); } void NodeGraphGroup::SetOutput(int8_t outputChannel, NodeGraphBase& source, int8_t sourceOutputChannel) { @@ -310,7 +333,7 @@ namespace l::nodegraph { auto node = GetNode(id); int32_t sourceCount = 0; for (auto& it : mNodes) { - if (it->RemoveSource(node)) { + if (it->RemoveInput(node)) { sourceCount++; } } diff --git a/packages/nodegraph/source/common/NodeGraphOperations.cpp b/packages/nodegraph/source/common/NodeGraphOperations.cpp index 769daa50..728e22ef 100644 --- a/packages/nodegraph/source/common/NodeGraphOperations.cpp +++ b/packages/nodegraph/source/common/NodeGraphOperations.cpp @@ -6,6 +6,42 @@ namespace l::nodegraph { /* Mathematical operations */ + void GraphSourceConstants::Process(std::vector& inputs, std::vector& outputs) { + ASSERT(inputs.size() == static_cast(mNumConstants)); + ASSERT(outputs.size() == static_cast(mNumConstants)); + for (int8_t i = 0; i < mNumConstants; i++) { + outputs.at(i).mOutput = inputs.at(i).Get(); + } + } + + void GraphSourceSine::Process(std::vector& inputs, std::vector& outputs) { + ASSERT(inputs.size() == static_cast(mNumInputs)); + ASSERT(outputs.size() == static_cast(mNumOutputs)); + + float time = inputs.at(0).Get(); + float freq = inputs.at(1).Get(); + float freqMod = inputs.at(2).Get(); + float phaseMod = inputs.at(3).Get(); + + if (mPrevTime == 0.0f) { + mPrevTime = time; + } + + float deltaTime = time - mPrevTime; + mPrevTime = time; + + float phaseDelta = deltaTime * freq; + + mPhase += phaseDelta * freqMod; + mPhase -= floorf(mPhase); + + float phase = (mPhase + phaseMod) * 0.5f; + phase -= floorf(phase); + + outputs.at(0).mOutput = sinf(2.0f * 3.141529f * phase); + outputs.at(1).mOutput = phase; + } + void GraphNumericAdd::Process(std::vector& inputs, std::vector& outputs) { ASSERT(inputs.size() == static_cast(mNumInputs)); ASSERT(outputs.size() == static_cast(mNumOutputs)); diff --git a/packages/nodegraph/source/common/NodeGraphSchema.cpp b/packages/nodegraph/source/common/NodeGraphSchema.cpp index befe8b20..2a572ad7 100644 --- a/packages/nodegraph/source/common/NodeGraphSchema.cpp +++ b/packages/nodegraph/source/common/NodeGraphSchema.cpp @@ -14,30 +14,45 @@ namespace l::nodegraph { l::nodegraph::NodeGraphBase* node = nullptr; switch (typeId) { case 0: - node = mMainNodeGraph.NewNode(); + node = mMainNodeGraph.NewNode(0); break; case 1: - node = mMainNodeGraph.NewNode(); + node = mMainNodeGraph.NewNode(1); break; case 2: - node = mMainNodeGraph.NewNode(); + node = mMainNodeGraph.NewNode(2); break; case 3: - node = mMainNodeGraph.NewNode(); + node = mMainNodeGraph.NewNode(3); break; case 4: - node = mMainNodeGraph.NewNode(); + node = mMainNodeGraph.NewNode(); break; case 50: - node = mMainNodeGraph.NewNode(); + node = mMainNodeGraph.NewNode(); break; case 51: - node = mMainNodeGraph.NewNode(); + node = mMainNodeGraph.NewNode(); break; case 52: - node = mMainNodeGraph.NewNode(); + node = mMainNodeGraph.NewNode(); + break; + case 53: + node = mMainNodeGraph.NewNode(); + break; + case 54: + node = mMainNodeGraph.NewNode(); break; case 100: + node = mMainNodeGraph.NewNode(); + break; + case 101: + node = mMainNodeGraph.NewNode(); + break; + case 102: + node = mMainNodeGraph.NewNode(); + break; + case 150: node = mMainNodeGraph.NewNode(); break; default: @@ -69,7 +84,11 @@ namespace l::nodegraph { nodeDesc.mId = uniqueTypeId; nodeDesc.mName = typeName; mRegisteredNodeTypes[typeGroup].push_back(UINodeDesc{ uniqueTypeId, std::string(typeName) }); + } + void NodeGraphSchema::Update() { + mMainNodeGraph.Update(); } + } \ No newline at end of file diff --git a/packages/rendering/include/rendering/ui/UIContainer.h b/packages/rendering/include/rendering/ui/UIContainer.h index 67a63fd1..228290b7 100644 --- a/packages/rendering/include/rendering/ui/UIContainer.h +++ b/packages/rendering/include/rendering/ui/UIContainer.h @@ -52,7 +52,8 @@ namespace l::ui { PolygonFilled = 7, LinkH = 8, Text = 9, - Texture = 10 + Texture = 10, + NodeOutputValue = 11, }; enum class UITraversalMode { @@ -371,6 +372,7 @@ namespace l::ui { int32_t mChannelId = 0; std::string mStringId; std::string mDisplayName; + uint32_t mConfigFlags = 0; // Active visitor flags uint32_t mNotificationFlags = 0; // Notification flags for ux feedback (resizing box animation etc) diff --git a/packages/rendering/include/rendering/ui/UINodeEditor.h b/packages/rendering/include/rendering/ui/UINodeEditor.h index c88a390a..a60537a9 100644 --- a/packages/rendering/include/rendering/ui/UINodeEditor.h +++ b/packages/rendering/include/rendering/ui/UINodeEditor.h @@ -26,13 +26,12 @@ namespace l::ui { mUIWindow.SetContentWindow([&]() { ImGui::PushItemWidth(400); - UIDraw uiDrawVisitor(ImGui::GetWindowDrawList()); UIUpdate updateVisitor; - + mDrawVisitor.SetDrawList(ImGui::GetWindowDrawList()); mUIRoot->SetLayoutSize(mUIWindow.GetSize()); mUIRoot->SetLayoutPosition(mUIWindow.GetPosition()); mUIRoot->Accept(updateVisitor, mUIInput, l::ui::UITraversalMode::BFS); - mUIRoot->Accept(uiDrawVisitor, mUIInput, l::ui::UITraversalMode::BFS); + mUIRoot->Accept(mDrawVisitor, mUIInput, l::ui::UITraversalMode::BFS); ImGui::PopItemWidth(); @@ -82,6 +81,7 @@ namespace l::ui { void SetNGSchema(l::nodegraph::NodeGraphSchema* ngSchema) { mNGSchema = ngSchema; + mDrawVisitor.SetNGSchema(ngSchema); mLinkIOVisitor.SetNGSchema(ngSchema); mSelectVisitor.SetNGSchema(ngSchema); } @@ -123,6 +123,7 @@ namespace l::ui { l::nodegraph::NodeGraphSchema* mNGSchema = nullptr; + UIDraw mDrawVisitor; UILinkIO mLinkIOVisitor; UISelect mSelectVisitor; UIZoom mZoomVisitor; diff --git a/packages/rendering/include/rendering/ui/UIVisitors.h b/packages/rendering/include/rendering/ui/UIVisitors.h index cd2b6a38..3e1f29e4 100644 --- a/packages/rendering/include/rendering/ui/UIVisitors.h +++ b/packages/rendering/include/rendering/ui/UIVisitors.h @@ -64,12 +64,22 @@ namespace l::ui { class UIDraw : public UIVisitor { public: - UIDraw(ImDrawList* drawList) : mDrawList(drawList) {} + UIDraw(ImDrawList* drawList = nullptr) : mDrawList(drawList) {} ~UIDraw() = default; virtual bool Visit(UIContainer& container, const InputState& input); + + void SetDrawList(ImDrawList* drawList) { + mDrawList = drawList; + } + + void SetNGSchema(l::nodegraph::NodeGraphSchema* ngSchema) { + mNGSchema = ngSchema; + } + protected: ImDrawList* mDrawList; + l::nodegraph::NodeGraphSchema* mNGSchema = nullptr; }; class UILinkIO : public UIVisitor { diff --git a/packages/rendering/source/common/ui/UICreator.cpp b/packages/rendering/source/common/ui/UICreator.cpp index a18960ef..0f954fba 100644 --- a/packages/rendering/source/common/ui/UICreator.cpp +++ b/packages/rendering/source/common/ui/UICreator.cpp @@ -9,8 +9,9 @@ namespace l::ui { auto numInputChannels = node.GetNumInputs(); auto numOutputChannels = node.GetNumOutputs(); + auto numConstants = node.GetNumConstants(); - auto node4 = CreateSplit(uiStorage, l::ui::UIContainer_MoveFlag | l::ui::UIContainer_DrawFlag | UIContainer_SelectFlag, l::ui::UIRenderType::RectFilled, l::ui::UISplitMode::AppendV); + auto node4 = CreateSplit(uiStorage, l::ui::UIContainer_ResizeFlag | l::ui::UIContainer_MoveFlag | l::ui::UIContainer_DrawFlag | UIContainer_SelectFlag, l::ui::UIRenderType::RectFilled, l::ui::UISplitMode::AppendV); node4->SetPosition(p); node4->GetContainerArea().mMargin = 0.0f; node4->SetNodeId(node.GetId()); @@ -51,7 +52,27 @@ namespace l::ui { row->Add(inText); } - if (i < numOutputChannels) { + if (i < numOutputChannels - numConstants) { + auto out = CreateContainer(uiStorage, l::ui::UIContainer_OutputFlag | l::ui::UIContainer_DrawFlag, l::ui::UIRenderType::CircleFilled, l::ui::UIAlignH::Right); + out->SetPosition(ImVec2(ioSize * 2.0f, ioSize * ioOffsetV)); + out->SetSize(ImVec2(ioSize, ioSize)); + out->GetContainerArea().mMargin = 0.0f; + out->SetNodeId(node.GetId()); + out->SetChannelId(i); + row->Add(out); + auto outText = CreateContainer(uiStorage, l::ui::UIContainer_DrawFlag, l::ui::UIRenderType::Text, l::ui::UIAlignH::Right); + outText->SetPosition(ImVec2(0.0f, 0.0f)); + outText->SetDisplayName(node.GetOutputName(i)); + row->Add(outText); + } + else if (i < numOutputChannels) { + auto inText = CreateContainer(uiStorage, l::ui::UIContainer_DrawFlag, l::ui::UIRenderType::NodeOutputValue, l::ui::UIAlignH::Left); + inText->SetPosition(ImVec2(0.0f, 0.0f)); + inText->SetDisplayName(""); + inText->SetNodeId(node.GetId()); + inText->SetChannelId(i); + row->Add(inText); + auto out = CreateContainer(uiStorage, l::ui::UIContainer_OutputFlag | l::ui::UIContainer_DrawFlag, l::ui::UIRenderType::CircleFilled, l::ui::UIAlignH::Right); out->SetPosition(ImVec2(ioSize * 2.0f, ioSize * ioOffsetV)); out->SetSize(ImVec2(ioSize, ioSize)); diff --git a/packages/rendering/source/common/ui/UIVisitors.cpp b/packages/rendering/source/common/ui/UIVisitors.cpp index 80bbdf19..08ec9f3a 100644 --- a/packages/rendering/source/common/ui/UIVisitors.cpp +++ b/packages/rendering/source/common/ui/UIVisitors.cpp @@ -213,7 +213,8 @@ namespace l::ui { const char* nameStart; const char* nameEnd; - switch (container.GetRenderData().mType) { + auto renderType = container.GetRenderData().mType; + switch (renderType) { case l::ui::UIRenderType::Rect: mDrawList->AddRect(p1, p2, color, 5.0f, ImDrawFlags_RoundCornersAll, 1.0f * container.GetScale() * layoutArea.mScale); break; @@ -285,6 +286,15 @@ namespace l::ui { break; case l::ui::UIRenderType::Texture: break; + + case l::ui::UIRenderType::NodeOutputValue: + if (mNGSchema) { + auto node = mNGSchema->GetNode(container.GetNodeId()); + auto nodeValue = node->Get(static_cast(container.GetChannelId())); + auto nodeString = std::to_string(nodeValue); + mDrawList->AddText(ImGui::GetDefaultFont(), 13.0f * container.GetScale() * layoutArea.mScale, p1, color, nodeString.c_str()); + } + break; } if (mDebug && !container.GetStringId().empty()) { From 8462c3501fb1786e719308512f782efd684b5067 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Thu, 29 Aug 2024 15:34:46 +0200 Subject: [PATCH 079/125] Fix warnings. --- packages/nodegraph/source/common/NodeGraph.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/nodegraph/source/common/NodeGraph.cpp b/packages/nodegraph/source/common/NodeGraph.cpp index dd05d20a..3d504d00 100644 --- a/packages/nodegraph/source/common/NodeGraph.cpp +++ b/packages/nodegraph/source/common/NodeGraph.cpp @@ -36,7 +36,7 @@ namespace l::nodegraph { } int8_t NodeGraphBase::GetNumInputs() { - ASSERT(mInputs.size() == mInputCount + mConstantCount); + ASSERT(mInputs.size() == static_cast(mInputCount + mConstantCount)); return static_cast(mInputCount); } @@ -45,7 +45,7 @@ namespace l::nodegraph { } int8_t NodeGraphBase::GetNumConstants() { - ASSERT(mInputs.size() == mInputCount + mConstantCount); + ASSERT(mInputs.size() == static_cast(mInputCount + mConstantCount)); return static_cast(mConstantCount); } From 05d127c367264e4d39d7f920b8ffc70ce268789f Mon Sep 17 00:00:00 2001 From: lnd3 Date: Thu, 29 Aug 2024 15:40:22 +0200 Subject: [PATCH 080/125] Fix some bugs. --- packages/nodegraph/include/nodegraph/NodeGraph.h | 6 +++--- packages/nodegraph/source/common/NodeGraph.cpp | 8 ++++---- packages/nodegraph/source/common/NodeGraphOperations.cpp | 2 ++ 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/packages/nodegraph/include/nodegraph/NodeGraph.h b/packages/nodegraph/include/nodegraph/NodeGraph.h index 3224f2e6..1bee29e1 100644 --- a/packages/nodegraph/include/nodegraph/NodeGraph.h +++ b/packages/nodegraph/include/nodegraph/NodeGraph.h @@ -85,8 +85,8 @@ namespace l::nodegraph { virtual bool SetInput(int8_t inputChannel, NodeGraphBase& source, int8_t sourceOutputChannel); virtual bool SetInput(int8_t inputChannel, NodeGraphGroup& source, int8_t sourceOutputChannel, bool nodeIsInsideGroup); - virtual bool SetConstant(int8_t inputChannel, float constant); - virtual bool SetExternalInput(int8_t inputChannel, float* floatPtr); + virtual bool SetInput(int8_t inputChannel, float constant); + virtual bool SetInput(int8_t inputChannel, float* floatPtr); virtual bool RemoveInput(void* source); protected: @@ -174,7 +174,7 @@ namespace l::nodegraph { NodeGraphBase::SetNumConstants(numConstants); mOperation.SetNumConstants(numConstants); for (int8_t i = mInputCount; i < mInputCount + mConstantCount; i++) { - SetConstant(i, 1.0f); + SetInput(i, 1.0f); } Update(); } diff --git a/packages/nodegraph/source/common/NodeGraph.cpp b/packages/nodegraph/source/common/NodeGraph.cpp index 3d504d00..937119fe 100644 --- a/packages/nodegraph/source/common/NodeGraph.cpp +++ b/packages/nodegraph/source/common/NodeGraph.cpp @@ -137,7 +137,7 @@ namespace l::nodegraph { return true; } - bool NodeGraphBase::SetConstant(int8_t inputChannel, float constant) { + bool NodeGraphBase::SetInput(int8_t inputChannel, float constant) { ASSERT(inputChannel >= mInputCount && inputChannel < mInputCount + mConstantCount); auto& input = mInputs.at(inputChannel); if (!IsValidInOutNum(inputChannel, mInputs.size())) { @@ -150,7 +150,7 @@ namespace l::nodegraph { return true; } - bool NodeGraphBase::SetExternalInput(int8_t inputChannel, float* floatPtr) { + bool NodeGraphBase::SetInput(int8_t inputChannel, float* floatPtr) { auto& input = mInputs.at(inputChannel); if (!IsValidInOutNum(inputChannel, mInputs.size())) { return false; @@ -287,11 +287,11 @@ namespace l::nodegraph { } void NodeGraphGroup::SetInput(int8_t inputChannel, float constant) { - mInputNode.SetConstant(inputChannel, constant); + mInputNode.SetInput(inputChannel, constant); } void NodeGraphGroup::SetInput(int8_t inputChannel, float* floatPtr) { - mInputNode.SetExternalInput(inputChannel, floatPtr); + mInputNode.SetInput(inputChannel, floatPtr); } void NodeGraphGroup::SetOutput(int8_t outputChannel, NodeGraphBase& source, int8_t sourceOutputChannel) { diff --git a/packages/nodegraph/source/common/NodeGraphOperations.cpp b/packages/nodegraph/source/common/NodeGraphOperations.cpp index 728e22ef..0ea58c5c 100644 --- a/packages/nodegraph/source/common/NodeGraphOperations.cpp +++ b/packages/nodegraph/source/common/NodeGraphOperations.cpp @@ -2,6 +2,8 @@ #include "logging/Log.h" +#include + namespace l::nodegraph { /* Mathematical operations */ From d87aba5dcb04e44551c904d86dcafec3bcca2746 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Thu, 29 Aug 2024 17:16:54 +0200 Subject: [PATCH 081/125] Add a edit visitor for editing value. Fixed a notification issue. Only clear single notifications, not all. --- .../include/nodegraph/NodeGraphOperations.h | 27 ++++++++- .../include/nodegraph/NodeGraphSchema.h | 8 +-- .../source/common/NodeGraphOperations.cpp | 5 +- .../include/rendering/ui/UIContainer.h | 8 ++- .../include/rendering/ui/UINodeEditor.h | 4 ++ .../include/rendering/ui/UIVisitors.h | 13 +++++ .../rendering/source/common/ui/UICreator.cpp | 25 ++++++-- .../rendering/source/common/ui/UIVisitors.cpp | 57 +++++++++++++++++-- 8 files changed, 125 insertions(+), 22 deletions(-) diff --git a/packages/nodegraph/include/nodegraph/NodeGraphOperations.h b/packages/nodegraph/include/nodegraph/NodeGraphOperations.h index abedf740..b04919e2 100644 --- a/packages/nodegraph/include/nodegraph/NodeGraphOperations.h +++ b/packages/nodegraph/include/nodegraph/NodeGraphOperations.h @@ -16,14 +16,37 @@ namespace l::nodegraph { class GraphSourceConstants : public NodeGraphOp { public: GraphSourceConstants(int8_t mode = 0) : - NodeGraphOp(0, mode + 1, mode + 1) - {} + NodeGraphOp(0, 1, 1) + { + switch (mode) { + case 0: + mMax = 1.0f; + mMin = 0.0f; + break; + case 1: + mMax = 1.0f; + mMin = -1.0f; + break; + case 2: + mMax = 100.0f; + mMin = 0.0f; + break; + default: + mMax = 0.0f; + mMin = 0.0f; + break; + } + } virtual ~GraphSourceConstants() = default; void Process(std::vector& inputs, std::vector& outputs) override; std::string_view GetName() override { return "Constant"; } + + protected: + float mMax = 1.0f; + float mMin = 0.0f; }; class GraphSourceSine : public NodeGraphOp { diff --git a/packages/nodegraph/include/nodegraph/NodeGraphSchema.h b/packages/nodegraph/include/nodegraph/NodeGraphSchema.h index 035a3b2b..387a539b 100644 --- a/packages/nodegraph/include/nodegraph/NodeGraphSchema.h +++ b/packages/nodegraph/include/nodegraph/NodeGraphSchema.h @@ -32,10 +32,10 @@ namespace l::nodegraph { using CustomCreateFunctionType = NodeGraphBase*(int32_t, NodeGraphGroup&); NodeGraphSchema() { - RegisterNodeType("Source", 0, "Constant1"); - RegisterNodeType("Source", 1, "Constant2"); - RegisterNodeType("Source", 2, "Constant3"); - RegisterNodeType("Source", 3, "Constant4"); + RegisterNodeType("Source", 0, "Value [0,1]"); + RegisterNodeType("Source", 1, "Value [-1,1]"); + RegisterNodeType("Source", 2, "Value [0,100]"); + RegisterNodeType("Source", 3, "Value [-inf,inf]"); RegisterNodeType("Source", 4, "Sine"); RegisterNodeType("Numeric", 50, "Add"); RegisterNodeType("Numeric", 51, "Subtract"); diff --git a/packages/nodegraph/source/common/NodeGraphOperations.cpp b/packages/nodegraph/source/common/NodeGraphOperations.cpp index 0ea58c5c..a156f46c 100644 --- a/packages/nodegraph/source/common/NodeGraphOperations.cpp +++ b/packages/nodegraph/source/common/NodeGraphOperations.cpp @@ -12,7 +12,10 @@ namespace l::nodegraph { ASSERT(inputs.size() == static_cast(mNumConstants)); ASSERT(outputs.size() == static_cast(mNumConstants)); for (int8_t i = 0; i < mNumConstants; i++) { - outputs.at(i).mOutput = inputs.at(i).Get(); + float val = inputs.at(i).Get(); + val = val > mMax ? mMax : val < mMin ? mMin : val; + inputs.at(i).mInput.mInputFloatConstant = val; + outputs.at(i).mOutput = val; } } diff --git a/packages/rendering/include/rendering/ui/UIContainer.h b/packages/rendering/include/rendering/ui/UIContainer.h index 228290b7..4e69a800 100644 --- a/packages/rendering/include/rendering/ui/UIContainer.h +++ b/packages/rendering/include/rendering/ui/UIContainer.h @@ -239,9 +239,10 @@ namespace l::ui { const uint32_t UIContainer_MoveFlag = 0x00000080; // Can be moved when grabbed const uint32_t UIContainer_ResizeFlag = 0x00000100; // Can be resized when grabbing bottom right corner const uint32_t UIContainer_InputFlag = 0x00000200; // Can be grabbed and dropped on a container with output flag - const uint32_t UIContainer_OutputFlag = 0x00000400; // Can be dropped a grabbed - const uint32_t UIContainer_LinkFlag = 0x00000800; // Can be dropped a grabbed - const uint32_t UIContainer_SelectFlag = 0x00001000; // Can be moved when grabbed + const uint32_t UIContainer_OutputFlag = 0x00000400; + const uint32_t UIContainer_LinkFlag = 0x00000800; + const uint32_t UIContainer_SelectFlag = 0x00001000; + const uint32_t UIContainer_EditFlag = 0x00002000; class UIDraw; @@ -323,6 +324,7 @@ namespace l::ui { void SetNotification(uint32_t flag) { mNotificationFlags |= flag; } void ClearNotifications() { mNotificationFlags = 0; } + void ClearNotification(uint32_t flag) { mNotificationFlags = mNotificationFlags & (~flag); } bool HasNotification(uint32_t flag) { return (mNotificationFlags & flag) == flag; } bool HasConfigFlag(uint32_t flag) { return (mConfigFlags & flag) == flag; } diff --git a/packages/rendering/include/rendering/ui/UINodeEditor.h b/packages/rendering/include/rendering/ui/UINodeEditor.h index a60537a9..6e40cf8c 100644 --- a/packages/rendering/include/rendering/ui/UINodeEditor.h +++ b/packages/rendering/include/rendering/ui/UINodeEditor.h @@ -84,6 +84,7 @@ namespace l::ui { mDrawVisitor.SetNGSchema(ngSchema); mLinkIOVisitor.SetNGSchema(ngSchema); mSelectVisitor.SetNGSchema(ngSchema); + mEditVisitor.SetNGSchema(ngSchema); } void Update() { @@ -100,6 +101,8 @@ namespace l::ui { if (mUIWindow.IsHovered()) { if (mUIRoot->Accept(mLinkIOVisitor, mUIInput, l::ui::UITraversalMode::DFS)) { } + else if (mUIRoot->Accept(mEditVisitor, mUIInput, l::ui::UITraversalMode::DFS)) { + } else if (mUIRoot->Accept(mSelectVisitor, mUIInput, l::ui::UITraversalMode::BFS)) { } else if (mUIRoot->Accept(mResizeVisitor, mUIInput, l::ui::UITraversalMode::DFS)) { @@ -130,6 +133,7 @@ namespace l::ui { UIDrag mDragVisitor; UIMove mMoveVisitor; UIResize mResizeVisitor; + UIEdit mEditVisitor; }; diff --git a/packages/rendering/include/rendering/ui/UIVisitors.h b/packages/rendering/include/rendering/ui/UIVisitors.h index 3e1f29e4..2dfb2899 100644 --- a/packages/rendering/include/rendering/ui/UIVisitors.h +++ b/packages/rendering/include/rendering/ui/UIVisitors.h @@ -62,6 +62,19 @@ namespace l::ui { l::nodegraph::NodeGraphSchema* mNGSchema = nullptr; }; + class UIEdit : public UIVisitor { + public: + virtual bool Visit(UIContainer& container, const InputState& input); + + void SetNGSchema(l::nodegraph::NodeGraphSchema* ngSchema) { + mNGSchema = ngSchema; + } + protected: + bool mEditing = false; + UIContainer* mSourceContainer = nullptr; + l::nodegraph::NodeGraphSchema* mNGSchema = nullptr; + }; + class UIDraw : public UIVisitor { public: UIDraw(ImDrawList* drawList = nullptr) : mDrawList(drawList) {} diff --git a/packages/rendering/source/common/ui/UICreator.cpp b/packages/rendering/source/common/ui/UICreator.cpp index 0f954fba..5001a6d2 100644 --- a/packages/rendering/source/common/ui/UICreator.cpp +++ b/packages/rendering/source/common/ui/UICreator.cpp @@ -12,13 +12,11 @@ namespace l::ui { auto numConstants = node.GetNumConstants(); auto node4 = CreateSplit(uiStorage, l::ui::UIContainer_ResizeFlag | l::ui::UIContainer_MoveFlag | l::ui::UIContainer_DrawFlag | UIContainer_SelectFlag, l::ui::UIRenderType::RectFilled, l::ui::UISplitMode::AppendV); + node4->SetColor(ImVec4(0.15f, 0.15f, 0.15f, 1.0f)); node4->SetPosition(p); node4->GetContainerArea().mMargin = 0.0f; node4->SetNodeId(node.GetId()); - ImVec2 titleSize = ImGui::CalcTextSize(node.GetName().data()); - titleSize.x += node4->GetContainerArea().mMargin * 2 + 2.0f; - node4->SetSize(ImVec2(titleSize.x < 100.0f ? 100.0f : titleSize.x, 22.0f + 19.0f * (numInputChannels > numOutputChannels ? numInputChannels : numOutputChannels))); - node4->SetColor(ImVec4(0.15f, 0.15f, 0.15f, 1.0f)); + ImVec2 sizeEstimate = ImVec2(100.0f, 22.0f + 19.0f * (numInputChannels > numOutputChannels ? numInputChannels : numOutputChannels)); { float ioSize = 4.0f; float ioOffsetV = 1.6f; @@ -30,6 +28,8 @@ namespace l::ui { { auto connector1Text = CreateContainer(uiStorage, l::ui::UIContainer_DrawFlag, l::ui::UIRenderType::Text, l::ui::UIAlignH::Center, l::ui::UIAlignV::Middle); connector1Text->SetDisplayName(node.GetName()); + auto textSize = ImGui::CalcTextSize(node.GetName().data()); + sizeEstimate.x = sizeEstimate.x < textSize.x ? textSize.x : sizeEstimate.x; connector1Text->GetContainerArea().mRender.mColor = ImColor(ImVec4(0.5f, 1.0f, 0.4f, 1.0f)); row0->Add(connector1Text); } @@ -38,6 +38,7 @@ namespace l::ui { row->GetContainerArea().mMargin = ioSize; node4->Add(row); { + float estimatedWidth = 0.0f; if (i < numInputChannels) { auto in = CreateContainer(uiStorage, l::ui::UIContainer_InputFlag | l::ui::UIContainer_DrawFlag, l::ui::UIRenderType::CircleFilled, l::ui::UIAlignH::Left); in->SetPosition(ImVec2(-ioSize, ioSize * ioOffsetV)); @@ -45,10 +46,12 @@ namespace l::ui { in->GetContainerArea().mMargin = 0.0f; in->SetNodeId(node.GetId()); in->SetChannelId(i); + estimatedWidth += ioSize; row->Add(in); auto inText = CreateContainer(uiStorage, l::ui::UIContainer_DrawFlag, l::ui::UIRenderType::Text, l::ui::UIAlignH::Left); inText->SetPosition(ImVec2(0.0f, 0.0f)); inText->SetDisplayName(node.GetInputName(i)); + estimatedWidth += ImGui::CalcTextSize(node.GetInputName(i).data()).x; row->Add(inText); } @@ -59,18 +62,21 @@ namespace l::ui { out->GetContainerArea().mMargin = 0.0f; out->SetNodeId(node.GetId()); out->SetChannelId(i); + estimatedWidth += ioSize; row->Add(out); auto outText = CreateContainer(uiStorage, l::ui::UIContainer_DrawFlag, l::ui::UIRenderType::Text, l::ui::UIAlignH::Right); outText->SetPosition(ImVec2(0.0f, 0.0f)); outText->SetDisplayName(node.GetOutputName(i)); + estimatedWidth += ImGui::CalcTextSize(node.GetOutputName(i).data()).x; row->Add(outText); } else if (i < numOutputChannels) { - auto inText = CreateContainer(uiStorage, l::ui::UIContainer_DrawFlag, l::ui::UIRenderType::NodeOutputValue, l::ui::UIAlignH::Left); + auto inText = CreateContainer(uiStorage, l::ui::UIContainer_EditFlag | l::ui::UIContainer_DrawFlag, l::ui::UIRenderType::NodeOutputValue, l::ui::UIAlignH::Left); inText->SetPosition(ImVec2(0.0f, 0.0f)); - inText->SetDisplayName(""); + inText->SetSize(ImVec2(10 * 7, 14.0f)); inText->SetNodeId(node.GetId()); inText->SetChannelId(i); + estimatedWidth += 10 * 7 + 10; row->Add(inText); auto out = CreateContainer(uiStorage, l::ui::UIContainer_OutputFlag | l::ui::UIContainer_DrawFlag, l::ui::UIRenderType::CircleFilled, l::ui::UIAlignH::Right); @@ -79,15 +85,22 @@ namespace l::ui { out->GetContainerArea().mMargin = 0.0f; out->SetNodeId(node.GetId()); out->SetChannelId(i); + estimatedWidth += ioSize; row->Add(out); auto outText = CreateContainer(uiStorage, l::ui::UIContainer_DrawFlag, l::ui::UIRenderType::Text, l::ui::UIAlignH::Right); outText->SetPosition(ImVec2(0.0f, 0.0f)); outText->SetDisplayName(node.GetOutputName(i)); + estimatedWidth += ImGui::CalcTextSize(node.GetOutputName(i).data()).x; row->Add(outText); } + sizeEstimate.x = sizeEstimate.x < estimatedWidth ? estimatedWidth : sizeEstimate.x; } } } + + sizeEstimate.x += node4->GetContainerArea().mMargin * 2 + 2.0f; + node4->SetSize(sizeEstimate); + return node4; }; diff --git a/packages/rendering/source/common/ui/UIVisitors.cpp b/packages/rendering/source/common/ui/UIVisitors.cpp index 08ec9f3a..4639d617 100644 --- a/packages/rendering/source/common/ui/UIVisitors.cpp +++ b/packages/rendering/source/common/ui/UIVisitors.cpp @@ -131,7 +131,7 @@ namespace l::ui { } else { mSourceContainer = nullptr; - container.ClearNotifications(); + container.ClearNotification(UIContainer_ResizeFlag); } } if (mResizing && mSourceContainer == &container) { @@ -142,7 +142,7 @@ namespace l::ui { if (input.mStopped) { mResizing = false; mSourceContainer = nullptr; - container.ClearNotifications(); + container.ClearNotification(UIContainer_ResizeFlag); } return mResizing; } @@ -163,13 +163,13 @@ namespace l::ui { } else { mSelectedContainers.erase(&container); - container.ClearNotifications(); + container.ClearNotification(UIContainer_SelectFlag); } } } else if (!mSelectedContainers.empty()) { for (auto it : mSelectedContainers) { - it->ClearNotifications(); + it->ClearNotification(UIContainer_SelectFlag); } mSelectedContainers.clear(); return true; @@ -190,6 +190,51 @@ namespace l::ui { return false; } + bool UIEdit::Visit(UIContainer& container, const InputState& input) { + if (!container.HasConfigFlag(UIContainer_EditFlag)) { + return false; + } + if (input.mStarted && !mEditing) { + auto& layoutArea = container.GetLayoutArea(); + if (Overlap(input.GetLocalPos(), container.GetPosition(), container.GetPositionAtSize(), layoutArea)) { + mEditing = true; + mSourceContainer = &container; + } + } + if (mEditing && mSourceContainer == &container) { + auto& layoutArea = container.GetLayoutArea(); + ImVec2 move = DragMovement(input.mPrevPos, input.mCurPos, layoutArea.mScale); + + if (mNGSchema) { + auto node = mNGSchema->GetNode(container.GetNodeId()); + auto& nodeValue = node->Get(static_cast(container.GetChannelId())); + if (!ImGui::IsKeyDown(ImGuiKey::ImGuiKey_LeftShift)) { + if (!ImGui::IsKeyDown(ImGuiKey::ImGuiKey_LeftCtrl)) { + nodeValue -= move.y / 100.0f; + } + else { + nodeValue -= move.y / 10000.0f; + } + } + else { + if (!ImGui::IsKeyDown(ImGuiKey::ImGuiKey_LeftCtrl)) { + nodeValue -= move.y; + } + else { + nodeValue -= 1000.0f * move.y; + } + } + } + + if (input.mStopped) { + mEditing = false; + mSourceContainer = nullptr; + } + return mEditing; + } + return false; + } + bool UIDraw::Visit(UIContainer& container, const InputState& input) { if (!mDebug && !container.HasConfigFlag(UIContainer_DrawFlag)) { return false; @@ -390,11 +435,11 @@ namespace l::ui { else if (mLinkContainer->GetCoParent() == &container) { LinkHandler(container.GetNodeId(), mLinkContainer->GetParent()->GetNodeId(), container.GetChannelId(), mLinkContainer->GetParent()->GetChannelId(), false); mLinkContainer->SetCoParent(nullptr); - mLinkContainer->ClearNotifications(); + mLinkContainer->ClearNotification(UIContainer_LinkFlag); } if (input.mStopped) { - mLinkContainer->ClearNotifications(); + mLinkContainer->ClearNotification(UIContainer_LinkFlag); if (mLinkContainer->GetCoParent() != nullptr) { mDragging = false; mLinkContainer.Reset(); From 6d67821a0b00ccc01d3461ce80d3e1d15d653bc0 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Sat, 31 Aug 2024 21:22:54 +0200 Subject: [PATCH 082/125] Add a human interface device package for structured keyboard input etc. Use it in node graph for key input. Add a keyboard piano ui node and a ui graphic node and hook them up to the node graph creator and the ui creator. --- CMakeLists.txt | 1 + packages/hid/CMakeLists.txt | 8 + packages/hid/include/hid/KeyState.h | 52 +++++ packages/hid/include/hid/KeyboardPiano.h | 76 +++++++ packages/hid/source/common/KeyState.cpp | 102 +++++++++ packages/hid/source/common/KeyboardPiano.cpp | 103 +++++++++ packages/hid/tests/common/HIDTest.cpp | 7 + packages/hid/tests/common/KeyStateTest.cpp | 13 ++ packages/nodegraph/CMakeLists.txt | 1 + .../nodegraph/include/nodegraph/NodeGraph.h | 94 +++++--- .../include/nodegraph/NodeGraphOperations.h | 201 +++++++++++++++--- .../include/nodegraph/NodeGraphSchema.h | 16 +- .../nodegraph/source/common/NodeGraph.cpp | 77 +++++-- .../source/common/NodeGraphOperations.cpp | 57 +++-- .../source/common/NodeGraphSchema.cpp | 18 +- .../tests/common/NodeGraphSchemaTest.cpp | 12 +- packages/rendering/CMakeLists.txt | 1 + .../include/rendering/ui/UIContainer.h | 3 +- .../include/rendering/ui/UINodeEditor.h | 1 + .../include/rendering/ui/UIVisitors.h | 4 +- .../rendering/include/rendering/ui/UIWindow.h | 1 + .../rendering/source/common/ui/UICreator.cpp | 34 ++- .../rendering/source/common/ui/UIVisitors.cpp | 16 +- 23 files changed, 767 insertions(+), 131 deletions(-) create mode 100644 packages/hid/CMakeLists.txt create mode 100644 packages/hid/include/hid/KeyState.h create mode 100644 packages/hid/include/hid/KeyboardPiano.h create mode 100644 packages/hid/source/common/KeyState.cpp create mode 100644 packages/hid/source/common/KeyboardPiano.cpp create mode 100644 packages/hid/tests/common/HIDTest.cpp create mode 100644 packages/hid/tests/common/KeyStateTest.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 2f8e41b8..18c07f0b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,6 +23,7 @@ else() testing meta memory + hid nodegraph concurrency diff --git a/packages/hid/CMakeLists.txt b/packages/hid/CMakeLists.txt new file mode 100644 index 00000000..f565fdba --- /dev/null +++ b/packages/hid/CMakeLists.txt @@ -0,0 +1,8 @@ +project (hid) + +set(deps + logging + testing +) + +bs_generate_package(hid "tier1" "${deps}" "") diff --git a/packages/hid/include/hid/KeyState.h b/packages/hid/include/hid/KeyState.h new file mode 100644 index 00000000..4addfb88 --- /dev/null +++ b/packages/hid/include/hid/KeyState.h @@ -0,0 +1,52 @@ +#pragma once + +#include "logging/LoggingAll.h" + +#include + +namespace l::hid { + + class KeyPressState { + public: + KeyPressState() = default; + + void UpdateKeyDown(); + void UpdateKeyUp(); + void UpdateFrameState(); + + bool IsReleased(); + bool IsPressed(); + bool IsReleasedNow(); + bool IsPressedNow(); + + protected: + bool pressed = false; + bool pressedPrev = false; + + bool released = true; + bool releasedPrev = true; + + bool pressedNow = false; + bool releasedNow = false; + }; + + class KeyState { + public: + KeyState() { + mActiveKeys.clear(); + } + ~KeyState() = default; + + void UpdateKeyDown(int32_t keyCode); + void UpdateKeyUp(int32_t keyCode); + bool IsReleased(int32_t keyCode); + bool IsPressed(int32_t keyCode); + bool IsReleasedNow(int32_t keyCode); + bool IsPressedNow(int32_t keyCode); + void UpdateFrameState(); + void ForEachKeyChange(std::function keyHandler); + void ForEachKey(std::function keyHandler); + protected: + std::unordered_map mActiveKeys; + }; +} diff --git a/packages/hid/include/hid/KeyboardPiano.h b/packages/hid/include/hid/KeyboardPiano.h new file mode 100644 index 00000000..dd1a88bf --- /dev/null +++ b/packages/hid/include/hid/KeyboardPiano.h @@ -0,0 +1,76 @@ +#pragma once + +#include "logging/LoggingAll.h" + +#include "hid/KeyState.h" + +#include +#include +#include + +namespace l::hid { + + class INoteProcessor { + public: + virtual ~INoteProcessor() = default; + virtual void NoteOn(int32_t) {} + virtual void NoteOff() {} + virtual void NoteOff(int32_t) {} + }; + + enum class KeyFunctionTypes { + OCTAVE_DOWN = 0, + OCTAVE_UP + }; + + class KeyboardPiano { + public: + const std::array keyMapFromNoteBUpper = {49, 81, 50, 87, 51, 69, 82, 53, 84, 54, 89, 55, 85, 73, 57, 79, 48, 80, 43}; + const std::array keyMapFromNoteBLower = {65, 90, 83, 88, 68, 67, 86, 71, 66, 72, 78, 74, 77, 44, 76, 46}; + const std::array charMapFromNoteBUpper = { '1', 'q', '2', 'w', '3', 'e', 'r', '5', 't', '6', 'y', '7', 'u', 'i', '9', 'o', '0', 'p' }; + + KeyboardPiano() : KeyboardPiano(nullptr, nullptr) {} + KeyboardPiano(l::hid::KeyState* keyState, l::hid::INoteProcessor* instrument) : + mKeyState(keyState), + mNotePlayer(instrument) + { + mKeyFunctions[KeyFunctionTypes::OCTAVE_DOWN] = 291; // F2 + mKeyFunctions[KeyFunctionTypes::OCTAVE_UP] = 292; // F3 + + int32_t i = 0; + int32_t key ; + for (i = 0; i < static_cast(keyMapFromNoteBLower.size()); i++) { + key = keyMapFromNoteBLower[i]; + mKeyCodeToNote[key] = i - 12; + } + for (i = 0; i < static_cast(keyMapFromNoteBUpper.size()); i++) { + key = keyMapFromNoteBUpper[i]; + mKeyCodeToNote[key] = i; + } + for (i = 0; i < static_cast(charMapFromNoteBUpper.size()); i++) { + char c = charMapFromNoteBUpper[i]; + mCharCodeToNote[c] = i; + } + } + + void SetKeyState(KeyState* keyState); + void SetNoteProcessor(INoteProcessor* notePlayer); + void ForEachNoteChange(std::function noteHandler); + + void Update(); + + int32_t ConvertKeyCodeToNote(int32_t keyCode, int32_t octave = 3); + int32_t ConvertCharCodeToNote(int32_t charCode, int32_t octave = 3); + + void MapKeyFunctions(KeyFunctionTypes function, int32_t keyCode); + + protected: + int32_t mOctave = 3; + std::unordered_map mKeyCodeToNote; + std::unordered_map mCharCodeToNote; + std::unordered_map mKeyFunctions; + KeyState* mKeyState = nullptr; + INoteProcessor* mNotePlayer = nullptr; + }; + +} diff --git a/packages/hid/source/common/KeyState.cpp b/packages/hid/source/common/KeyState.cpp new file mode 100644 index 00000000..d6f7d1bf --- /dev/null +++ b/packages/hid/source/common/KeyState.cpp @@ -0,0 +1,102 @@ +#include "hid/KeyState.h" + +#include + +namespace l::hid { + + void KeyPressState::UpdateKeyDown() { + pressed = true; + + releasedPrev = released; + released = false; + } + + void KeyPressState::UpdateKeyUp() { + releasedPrev = released; + released = true; + } + + void KeyPressState::UpdateFrameState() { + pressedNow = false; + releasedNow = false; + if (pressed && !pressedPrev) { + pressedNow = true; + } + if (released && !releasedPrev) { + releasedNow = true; + pressed = false; + } + pressedPrev = pressed; + releasedPrev = released; + + //if (pressedNow || releasedNow || pressed) { + // LOG(LogInfo) << "pressed now: " << pressedNow << ", released now: " << releasedNow << ", pressed: " << pressed; + //} + } + + bool KeyPressState::IsReleased() { + return !pressed; + } + + bool KeyPressState::IsPressed() { + return pressed; + } + + bool KeyPressState::IsReleasedNow() { + return releasedNow; + } + + bool KeyPressState::IsPressedNow() { + return pressedNow; + } + + void KeyState::UpdateKeyDown(int32_t keyCode) { + mActiveKeys[keyCode].UpdateKeyDown(); + } + + void KeyState::UpdateKeyUp(int32_t keyCode) { + // Up key event may happen if press was made out of focus so it may + // be null in this situation. This means the up key event is invalid. + mActiveKeys[keyCode].UpdateKeyUp(); + } + + bool KeyState::IsReleased(int32_t keyCode) { + return mActiveKeys[keyCode].IsReleased(); + } + + bool KeyState::IsPressed(int32_t keyCode) { + return mActiveKeys[keyCode].IsPressed(); + } + + bool KeyState::IsReleasedNow(int32_t keyCode) { + return mActiveKeys[keyCode].IsReleasedNow(); + } + + bool KeyState::IsPressedNow(int32_t keyCode) { + return mActiveKeys[keyCode].IsPressedNow(); + } + + void KeyState::UpdateFrameState() { + for (auto& key : mActiveKeys) { + key.second.UpdateFrameState(); + } + } + + void KeyState::ForEachKeyChange(std::function keyHandler) { + for (auto& key : mActiveKeys) { + auto& keyState = key.second; + bool pressedNow = keyState.IsPressedNow(); + bool releasedNow = keyState.IsReleasedNow(); + if (pressedNow || releasedNow) { + keyHandler(key.first, pressedNow, releasedNow); + } + } + } + + void KeyState::ForEachKey(std::function keyHandler) { + for (auto& key : mActiveKeys) { + keyHandler(key.first, key.second); + } + } + +} diff --git a/packages/hid/source/common/KeyboardPiano.cpp b/packages/hid/source/common/KeyboardPiano.cpp new file mode 100644 index 00000000..62c0c6e0 --- /dev/null +++ b/packages/hid/source/common/KeyboardPiano.cpp @@ -0,0 +1,103 @@ +#include "hid/KeyboardPiano.h" + +#include + +namespace l::hid { + + void KeyboardPiano::SetKeyState(KeyState* keyState) { + mKeyState = keyState; + } + + void KeyboardPiano::SetNoteProcessor(INoteProcessor* notePlayer) { + mNotePlayer = notePlayer; + } + + void KeyboardPiano::ForEachNoteChange(std::function noteHandler) { + mKeyState->ForEachKeyChange([&](int32_t keyCode, bool pressedNow, bool releasedNow) { + if (pressedNow) { + if (keyCode == mKeyFunctions[KeyFunctionTypes::OCTAVE_DOWN]) { + noteHandler(0, false); + mOctave -= 1; + mOctave = mOctave < -5 ? -5 : mOctave; + mOctave = mOctave > 10 ? 10 : mOctave; + } + else if (keyCode == mKeyFunctions[KeyFunctionTypes::OCTAVE_UP]) { + noteHandler(0, false); + mOctave += 1; + mOctave = mOctave < -5 ? -5 : mOctave; + mOctave = mOctave > 10 ? 10 : mOctave; + } + else { + int32_t note = ConvertKeyCodeToNote(keyCode, mOctave); + if (note > 0) { + noteHandler(note, true); + } + } + } + if (releasedNow) { + int32_t note = ConvertKeyCodeToNote(keyCode, mOctave); + if (note > 0) { + noteHandler(note, false); + } + } + }); + } + + void KeyboardPiano::Update() { + if (mKeyState == nullptr || mNotePlayer == nullptr) { + return; + } + + mKeyState->ForEachKeyChange([&](int32_t keyCode, bool pressedNow, bool releasedNow) { + if (pressedNow) { + if (keyCode == mKeyFunctions[KeyFunctionTypes::OCTAVE_DOWN]) { + mNotePlayer->NoteOff(); + mOctave -= 1; + mOctave = mOctave < -5 ? -5 : mOctave; + mOctave = mOctave > 10 ? 10 : mOctave; + } + else if (keyCode == mKeyFunctions[KeyFunctionTypes::OCTAVE_UP]) { + mNotePlayer->NoteOff(); + mOctave += 1; + mOctave = mOctave < -5 ? -5 : mOctave; + mOctave = mOctave > 10 ? 10 : mOctave; + } + else { + int32_t note = ConvertKeyCodeToNote(keyCode, mOctave); + if (note > 0) { + mNotePlayer->NoteOn(note); + } + } + } + if (releasedNow) { + int32_t note = ConvertKeyCodeToNote(keyCode, mOctave); + if (note > 0) { + mNotePlayer->NoteOff(note); + } + } + + }); + } + + int32_t KeyboardPiano::ConvertKeyCodeToNote(int32_t keyCode, int32_t octave) { + auto key = mKeyCodeToNote[keyCode]; + if (key > 0) { + return key + octave * 12; + } + return 0; + } + + int32_t KeyboardPiano::ConvertCharCodeToNote(int32_t charCode, int32_t octave) { + auto key = mCharCodeToNote[charCode]; + if (key > 0) { + return key + octave * 12; + } + return 0; + } + + void KeyboardPiano::MapKeyFunctions(KeyFunctionTypes function, int32_t keyCode) { + mKeyFunctions.emplace(function, keyCode); + } + + +} diff --git a/packages/hid/tests/common/HIDTest.cpp b/packages/hid/tests/common/HIDTest.cpp new file mode 100644 index 00000000..6d728e9e --- /dev/null +++ b/packages/hid/tests/common/HIDTest.cpp @@ -0,0 +1,7 @@ +#include "testing/Test.h" + +int main(int, char* argw[]) { + TEST_RUN(argw[0]); + + return 0; +} diff --git a/packages/hid/tests/common/KeyStateTest.cpp b/packages/hid/tests/common/KeyStateTest.cpp new file mode 100644 index 00000000..f13dd2d5 --- /dev/null +++ b/packages/hid/tests/common/KeyStateTest.cpp @@ -0,0 +1,13 @@ +#include "testing/Test.h" +#include "logging/Log.h" + +#include "hid/KeyState.h" + +using namespace l; + +TEST(KeyState, BasicFunction) { + + return 0; +} + + diff --git a/packages/nodegraph/CMakeLists.txt b/packages/nodegraph/CMakeLists.txt index ceceeb6b..9d1ec5f7 100644 --- a/packages/nodegraph/CMakeLists.txt +++ b/packages/nodegraph/CMakeLists.txt @@ -3,6 +3,7 @@ project (nodegraph) set(deps logging testing + hid ) bs_generate_package(nodegraph "tier1" "${deps}" "") diff --git a/packages/nodegraph/include/nodegraph/NodeGraph.h b/packages/nodegraph/include/nodegraph/NodeGraph.h index 1bee29e1..459a959e 100644 --- a/packages/nodegraph/include/nodegraph/NodeGraph.h +++ b/packages/nodegraph/include/nodegraph/NodeGraph.h @@ -11,6 +11,8 @@ namespace l::nodegraph { + int32_t CreateUniqueId(); + enum class DataType { FLOAT32, INT32, @@ -56,14 +58,20 @@ namespace l::nodegraph { class NodeGraphBase { public: - NodeGraphBase(std::string_view name = ""); + NodeGraphBase() : mId(CreateUniqueId()) { + mInputs.resize(1); + mOutputs.resize(1); + } + virtual ~NodeGraphBase() = default; virtual void Reset(); virtual void SetId(int32_t id) { mId = id; } virtual int32_t GetId() const { return mId; } - virtual void Update(); + void ClearProcessFlags(); + virtual void ProcessSubGraph(bool recomputeSubGraphCache = true); + virtual void Tick(float time); virtual void SetNumInputs(int8_t numInputs); virtual void SetNumOutputs(int8_t outputCount); @@ -74,6 +82,7 @@ namespace l::nodegraph { virtual int8_t GetNumConstants(); virtual float& Get(int8_t outputChannel); + virtual float GetInput(int8_t inputChannel); virtual std::string_view GetName(); virtual std::string_view GetInputName(int8_t inputChannel); @@ -89,8 +98,11 @@ namespace l::nodegraph { virtual bool SetInput(int8_t inputChannel, float* floatPtr); virtual bool RemoveInput(void* source); + + virtual bool IsDataVisible(int8_t num); + virtual bool IsDataEditable(int8_t num); + protected: - void PreUpdate(); virtual void ProcessOperation(); bool mProcessUpdateHasRun = false; @@ -105,10 +117,11 @@ namespace l::nodegraph { class NodeGraphOp { public: - NodeGraphOp(int8_t numInputs = 1, int8_t numOutputs = 1, int8_t numConstants = 0) : - mNumInputs(numInputs), - mNumOutputs(numOutputs), - mNumConstants(numConstants) + NodeGraphOp(NodeGraphBase* node, int32_t numInputs = 1, int32_t numOutputs = 1, int32_t numConstants = 0) : + mNode(node), + mNumInputs(static_cast(numInputs)), + mNumOutputs(static_cast(numOutputs)), + mNumConstants(static_cast(numConstants)) {} std::string defaultInStrings[4] = { "In 1", "In 2", "In 3", "In 4" }; @@ -116,7 +129,8 @@ namespace l::nodegraph { virtual ~NodeGraphOp() = default; virtual void Reset() {} - virtual void Process(std::vector& mInputs, std::vector& outputs) = 0; + virtual void ProcessSubGraph(std::vector& mInputs, std::vector& outputs) = 0; + virtual void Tick(float) {} virtual void SetNumInputs(int8_t numInputs); virtual void SetNumOutputs(int8_t numOutputs); @@ -125,17 +139,14 @@ namespace l::nodegraph { int8_t GetNumOutputs(); int8_t GetNumConstants(); - virtual std::string_view GetInputName(int8_t inputChannel) { - return defaultInStrings[inputChannel]; - }; - virtual std::string_view GetOutputName(int8_t outputChannel) { - return defaultOutStrings[outputChannel]; - } - virtual std::string_view GetName() { - return ""; - } + virtual bool IsDataVisible(int8_t num); + virtual bool IsDataEditable(int8_t num); + virtual std::string_view GetInputName(int8_t inputChannel); + virtual std::string_view GetOutputName(int8_t outputChannel); + virtual std::string_view GetName(); protected: + NodeGraphBase* mNode; int8_t mNumInputs = 0; int8_t mNumOutputs = 0; int8_t mNumConstants = 0; @@ -143,18 +154,21 @@ namespace l::nodegraph { class GraphDataCopy : public NodeGraphOp { public: - GraphDataCopy(int8_t) : - NodeGraphOp(0) + GraphDataCopy(NodeGraphBase* node) : + NodeGraphOp(node, 0) {} virtual ~GraphDataCopy() = default; - void Process(std::vector& inputs, std::vector& outputs) override; + void ProcessSubGraph(std::vector& inputs, std::vector& outputs) override; }; - template>> + template class NodeGraph : public NodeGraphBase { public: - NodeGraph(int8_t mode = 0) : mOperation(mode) { + NodeGraph(Params&&... params) : + NodeGraphBase(), + mOperation(this, std::forward(params)...) + { SetNumInputs(mOperation.GetNumInputs()); SetNumOutputs(mOperation.GetNumOutputs()); SetNumConstants(mOperation.GetNumConstants()); @@ -174,9 +188,17 @@ namespace l::nodegraph { NodeGraphBase::SetNumConstants(numConstants); mOperation.SetNumConstants(numConstants); for (int8_t i = mInputCount; i < mInputCount + mConstantCount; i++) { - SetInput(i, 1.0f); + SetInput(i, 0.0f); } - Update(); + ProcessSubGraph(); + } + + virtual bool IsDataVisible(int8_t num) override { + return mOperation.IsDataVisible(num); + } + + virtual bool IsDataEditable(int8_t num) override { + return mOperation.IsDataEditable(num); } virtual void Reset() override { @@ -186,7 +208,12 @@ namespace l::nodegraph { virtual void ProcessOperation() override { NodeGraphBase::ProcessOperation(); - mOperation.Process(mInputs, mOutputs); + mOperation.ProcessSubGraph(mInputs, mOutputs); + } + + virtual void Tick(float time) override { + NodeGraphBase::Tick(time); + mOperation.Tick(time); } virtual std::string_view GetInputName(int8_t inputChannel) { @@ -236,15 +263,24 @@ namespace l::nodegraph { NodeGraphBase& GetOutputNode(); NodeGraphBase* GetNode(int32_t id); + + template>> + NodeGraph* GetTypedNode(int32_t id) { + auto p = GetNode(id); + return reinterpret_cast*>(p); + } + bool RemoveNode(int32_t id); - template>> - l::nodegraph::NodeGraphBase* NewNode(int8_t mode = 0) { - mNodes.push_back(std::make_unique>(mode)); + template>, class... Params> + l::nodegraph::NodeGraphBase* NewNode(Params&&... params) { + mNodes.push_back(std::make_unique>(std::forward(params)...)); return mNodes.back().get(); } - void Update(); + void ClearProcessFlags(); + void ProcessSubGraph(bool recomputeSubGraphCache = true); + void Tick(float time); protected: NodeGraph mInputNode; NodeGraph mOutputNode; diff --git a/packages/nodegraph/include/nodegraph/NodeGraphOperations.h b/packages/nodegraph/include/nodegraph/NodeGraphOperations.h index b04919e2..7efe1ccf 100644 --- a/packages/nodegraph/include/nodegraph/NodeGraphOperations.h +++ b/packages/nodegraph/include/nodegraph/NodeGraphOperations.h @@ -2,12 +2,14 @@ #include "nodegraph/NodeGraph.h" #include "logging/LoggingAll.h" +#include "hid/KeyboardPiano.h" #include #include #include #include #include +#include namespace l::nodegraph { @@ -15,8 +17,8 @@ namespace l::nodegraph { class GraphSourceConstants : public NodeGraphOp { public: - GraphSourceConstants(int8_t mode = 0) : - NodeGraphOp(0, 1, 1) + GraphSourceConstants(NodeGraphBase* node, int32_t mode) : + NodeGraphOp(node, 0, 1, 1) { switch (mode) { case 0: @@ -39,11 +41,21 @@ namespace l::nodegraph { } virtual ~GraphSourceConstants() = default; - void Process(std::vector& inputs, std::vector& outputs) override; + void ProcessSubGraph(std::vector& inputs, std::vector& outputs) override; + virtual void Tick(float) override; + std::string_view GetName() override { return "Constant"; } + bool IsDataVisible(int8_t) override { + return true; + } + + bool IsDataEditable(int8_t) override { + return true; + } + protected: float mMax = 1.0f; float mMin = 0.0f; @@ -51,15 +63,15 @@ namespace l::nodegraph { class GraphSourceSine : public NodeGraphOp { public: - GraphSourceSine(int8_t) : - NodeGraphOp(4, 2) + GraphSourceSine(NodeGraphBase* node) : + NodeGraphOp(node, 5, 2) {} - std::string defaultInStrings[4] = { "Time", "Freq Hz", "Freq Mod", "Phase Mod"}; + std::string defaultInStrings[5] = { "Time", "Freq Hz", "Freq Mod", "Phase Mod", "Reset"}; std::string defaultOutStrings[2] = { "Sine", "Phase"}; virtual ~GraphSourceSine() = default; - void Process(std::vector& inputs, std::vector& outputs) override; + void ProcessSubGraph(std::vector& inputs, std::vector& outputs) override; void Reset() override { mPhase = 0.0f; @@ -83,13 +95,117 @@ namespace l::nodegraph { float mPrevTime = 0.0f; }; + namespace { + float GetFrequencyFromNote(float note) { + return 440.0f * powf(2.0f, (note - 49.0f) / 12.0f); + } + } + + class GraphSourceKeyboard : public NodeGraphOp, public l::hid::INoteProcessor { + public: + GraphSourceKeyboard(NodeGraphBase* node, int32_t polyphony, l::hid::KeyState* keyState) : + NodeGraphOp(node, 0, polyphony, polyphony) + { + mChannel.resize(polyphony); + mKeyboard.SetKeyState(keyState); + mKeyboard.SetNoteProcessor(this); + } + + std::string defaultOutStrings[8] = { "Note 1", "Note 2", "Note 3", "Note 4", "Note 5", "Note 6", "Note 7", "Note 8" }; + + virtual ~GraphSourceKeyboard() = default; + void ProcessSubGraph(std::vector& inputs, std::vector& outputs) override; + void Tick(float time) override; + + void Reset() override { + for (int8_t i = 0; i < GetNumInputs(); i++) { + mNode->SetInput(i, 0.0f); + } + mNode->ProcessSubGraph(); + } + + virtual std::string_view GetOutputName(int8_t outputChannel) override { + return defaultOutStrings[outputChannel]; + } + + virtual std::string_view GetName() override { + return "Keyboard"; + } + + virtual bool IsDataVisible(int8_t) override { + return true; + } + + virtual void NoteOn(int32_t note) override { + float frequency = GetFrequencyFromNote(static_cast(note)); + int8_t channel = GetNextNoteChannel(note); + mNode->SetInput(static_cast(channel), frequency); + mNode->ProcessSubGraph(); + } + virtual void NoteOff() override { + Reset(); + } + + virtual void NoteOff(int32_t note) override { + int8_t channel = ResetNoteChannel(note); + if (channel >= 0) { + mNode->SetInput(channel, 0.0f); + mNode->ProcessSubGraph(); + } + } + protected: + int8_t ResetNoteChannel(int32_t note) { + for (int8_t i = 0; i < mChannel.size(); i++) { + if (mChannel.at(i).first == note) { + mChannel.at(i).second = 0; + return i; + } + } + // It is possible to get a note off for a note not playing because the channel was taken for another newer note + return -1; + } + + int8_t GetNextNoteChannel(int32_t note) { + for (int8_t i = 0; i < mChannel.size(); i++) { + if (mChannel.at(i).first == note) { + mChannel.at(i).second = mNoteCounter++; + return i; + } + } + + for (int8_t i = 0; i < mChannel.size(); i++) { + if (mChannel.at(i).first == 0) { + mChannel.at(i).first = note; + mChannel.at(i).second = mNoteCounter++; + return i; + } + } + + int32_t lowestCount = INT32_MAX; + int8_t lowestCountIndex = 0; + for (int8_t i = 0; i < mChannel.size(); i++) { + if (lowestCount > mChannel.at(i).second) { + lowestCount = mChannel.at(i).second; + lowestCountIndex = i; + } + } + mChannel.at(lowestCountIndex).first = note; + mChannel.at(lowestCountIndex).second = mNoteCounter++; + return lowestCountIndex; + } + + int8_t mNoteCounter = 0; + std::vector> mChannel; + l::hid::KeyboardPiano mKeyboard; + }; + class GraphNumericAdd : public NodeGraphOp { public: - GraphNumericAdd(int8_t) : - NodeGraphOp(2, 1) + GraphNumericAdd(NodeGraphBase* node) : + NodeGraphOp(node, 2, 1) {} virtual ~GraphNumericAdd() = default; - void Process(std::vector& inputs, std::vector& outputs) override; + void ProcessSubGraph(std::vector& inputs, std::vector& outputs) override; std::string_view GetName() override { return "Add"; } @@ -97,12 +213,12 @@ namespace l::nodegraph { class GraphNumericMultiply : public NodeGraphOp { public: - GraphNumericMultiply(int8_t) : - NodeGraphOp(2, 1) + GraphNumericMultiply(NodeGraphBase* node) : + NodeGraphOp(node, 2, 1) {} virtual ~GraphNumericMultiply() = default; - void Process(std::vector& inputs, std::vector& outputs) override; + void ProcessSubGraph(std::vector& inputs, std::vector& outputs) override; std::string_view GetName() override { return "Multiply"; } @@ -110,11 +226,11 @@ namespace l::nodegraph { class GraphNumericSubtract : public NodeGraphOp { public: - GraphNumericSubtract(int8_t) : - NodeGraphOp(2, 1) + GraphNumericSubtract(NodeGraphBase* node) : + NodeGraphOp(node, 2, 1) {} virtual ~GraphNumericSubtract() = default; - void Process(std::vector& inputs, std::vector& outputs) override; + void ProcessSubGraph(std::vector& inputs, std::vector& outputs) override; std::string_view GetName() override { return "Subtract"; } @@ -122,12 +238,12 @@ namespace l::nodegraph { class GraphNumericNegate : public NodeGraphOp { public: - GraphNumericNegate(int8_t) : - NodeGraphOp(1, 1) + GraphNumericNegate(NodeGraphBase* node) : + NodeGraphOp(node, 1, 1) {} virtual ~GraphNumericNegate() = default; - void Process(std::vector& inputs, std::vector& outputs) override; + void ProcessSubGraph(std::vector& inputs, std::vector& outputs) override; std::string_view GetName() override { return "Negate"; } @@ -135,13 +251,13 @@ namespace l::nodegraph { class GraphNumericIntegral : public NodeGraphOp { public: - GraphNumericIntegral(int8_t) : - NodeGraphOp(1, 1) + GraphNumericIntegral(NodeGraphBase* node) : + NodeGraphOp(node, 1, 1) {} virtual ~GraphNumericIntegral() = default; void Reset() override; - void Process(std::vector& inputs, std::vector& outputs) override; + void ProcessSubGraph(std::vector& inputs, std::vector& outputs) override; std::string_view GetName() override { return "Integral"; } @@ -154,12 +270,12 @@ namespace l::nodegraph { class GraphLogicalAnd : public NodeGraphOp { public: - GraphLogicalAnd(int8_t) : - NodeGraphOp(2, 1) + GraphLogicalAnd(NodeGraphBase* node) : + NodeGraphOp(node, 2, 1) {} virtual ~GraphLogicalAnd() = default; - void Process(std::vector& inputs, std::vector& outputs) override; + void ProcessSubGraph(std::vector& inputs, std::vector& outputs) override; std::string_view GetName() override { return "And"; } @@ -167,12 +283,12 @@ namespace l::nodegraph { class GraphLogicalOr : public NodeGraphOp { public: - GraphLogicalOr(int8_t) : - NodeGraphOp(2, 1) + GraphLogicalOr(NodeGraphBase* node) : + NodeGraphOp(node, 2, 1) {} virtual ~GraphLogicalOr() = default; - void Process(std::vector& inputs, std::vector& outputs) override; + void ProcessSubGraph(std::vector& inputs, std::vector& outputs) override; std::string_view GetName() override { return "Or"; } @@ -180,12 +296,12 @@ namespace l::nodegraph { class GraphLogicalXor : public NodeGraphOp { public: - GraphLogicalXor(int8_t) : - NodeGraphOp(2, 1) + GraphLogicalXor(NodeGraphBase* node) : + NodeGraphOp(node, 2, 1) {} virtual ~GraphLogicalXor() = default; - void Process(std::vector& inputs, std::vector& outputs) override; + void ProcessSubGraph(std::vector& inputs, std::vector& outputs) override; std::string_view GetName() override { return "Xor"; } @@ -198,13 +314,13 @@ namespace l::nodegraph { std::string defaultInStrings[3] = { "Cutoff", "Resonance", "Data"}; std::string defaultOutStrings[1] = { "Out" }; - GraphFilterLowpass(int8_t) : - NodeGraphOp(3, 1) + GraphFilterLowpass(NodeGraphBase* node) : + NodeGraphOp(node, 3, 1) {} virtual ~GraphFilterLowpass() = default; void Reset() override; - void Process(std::vector& inputs, std::vector& outputs) override; + void ProcessSubGraph(std::vector& inputs, std::vector& outputs) override; std::string_view GetInputName(int8_t inputChannel) { return defaultInStrings[inputChannel]; @@ -222,6 +338,23 @@ namespace l::nodegraph { float mState1 = 0.0f; }; + class GraphGraphicDisplay : public NodeGraphOp { + public: + GraphGraphicDisplay(NodeGraphBase* node, int32_t numValueDisplays) : + NodeGraphOp(node, numValueDisplays, 0, 0) + {} + + bool IsDataVisible(int8_t) override { + return true; + } + + virtual ~GraphGraphicDisplay() = default; + void ProcessSubGraph(std::vector& inputs, std::vector& outputs) override; + virtual void Tick(float time) override; + std::string_view GetName() override { + return "Display"; + } + }; } diff --git a/packages/nodegraph/include/nodegraph/NodeGraphSchema.h b/packages/nodegraph/include/nodegraph/NodeGraphSchema.h index 387a539b..119f4f3e 100644 --- a/packages/nodegraph/include/nodegraph/NodeGraphSchema.h +++ b/packages/nodegraph/include/nodegraph/NodeGraphSchema.h @@ -36,7 +36,8 @@ namespace l::nodegraph { RegisterNodeType("Source", 1, "Value [-1,1]"); RegisterNodeType("Source", 2, "Value [0,100]"); RegisterNodeType("Source", 3, "Value [-inf,inf]"); - RegisterNodeType("Source", 4, "Sine"); + RegisterNodeType("Source", 4, "Keyboard"); + RegisterNodeType("Source", 5, "Sine"); RegisterNodeType("Numeric", 50, "Add"); RegisterNodeType("Numeric", 51, "Subtract"); RegisterNodeType("Numeric", 52, "Negate"); @@ -46,21 +47,32 @@ namespace l::nodegraph { RegisterNodeType("Logic", 101, "Or"); RegisterNodeType("Logic", 102, "Xor"); RegisterNodeType("Filter", 150, "Lowpass Filter"); + RegisterNodeType("Graphic", 200, "Display"); } ~NodeGraphSchema() = default; void SetCustomCreator(std::function customCreator); + void SetKeyState(l::hid::KeyState* keyState); + int32_t NewNode(int32_t typeId); bool RemoveNode(int32_t nodeId); NodeGraphBase* GetNode(int32_t nodeId); + + template>> + NodeGraph* GetTypedNode(int32_t id) { + return mMainNodeGraph.GetTypedNode(id); + } + void ForEachNodeType(std::function&)> cb) const; void RegisterNodeType(const std::string& typeGroup, int32_t uniqueTypeId, std::string_view typeName); - void Update(); + void ProcessSubGraph(); + void Tick(float time); protected: NodeGraphGroup mMainNodeGraph; std::function mCreateCustomNode; + l::hid::KeyState* mKeyState; std::map> mRegisteredNodeTypes; }; diff --git a/packages/nodegraph/source/common/NodeGraph.cpp b/packages/nodegraph/source/common/NodeGraph.cpp index 937119fe..b391b147 100644 --- a/packages/nodegraph/source/common/NodeGraph.cpp +++ b/packages/nodegraph/source/common/NodeGraph.cpp @@ -13,14 +13,6 @@ namespace l::nodegraph { return id++; } - NodeGraphBase::NodeGraphBase(std::string_view name) : - mId(CreateUniqueId()), - mName(name) - { - mInputs.resize(1); - mOutputs.resize(1); - } - void NodeGraphBase::SetNumInputs(int8_t numInputs) { mInputCount = numInputs; mInputs.resize(mInputCount + mConstantCount); @@ -57,17 +49,19 @@ namespace l::nodegraph { } } - void NodeGraphBase::PreUpdate() { + void NodeGraphBase::ClearProcessFlags() { mProcessUpdateHasRun = false; for (auto& link : mInputs) { if (link.mInputType == InputType::INPUT_NODE && link.mInput.mInputNode != nullptr) { - link.mInput.mInputNode->PreUpdate(); + link.mInput.mInputNode->ClearProcessFlags(); } } } - void NodeGraphBase::Update() { - PreUpdate(); + void NodeGraphBase::ProcessSubGraph(bool recomputeSubGraphCache) { + if (recomputeSubGraphCache) { + ClearProcessFlags(); + } ProcessOperation(); } @@ -83,11 +77,22 @@ namespace l::nodegraph { mProcessUpdateHasRun = true; } + void NodeGraphBase::Tick(float time) { + for (auto& link : mInputs) { + if (link.mInputType == InputType::INPUT_NODE && link.mInput.mInputNode != nullptr) { + link.mInput.mInputNode->Tick(time); + } + } + } + float& NodeGraphBase::Get(int8_t outputChannel) { - ASSERT(IsValidInOutNum(outputChannel, mOutputs.size())); return mOutputs.at(outputChannel).mOutput; } + float NodeGraphBase::GetInput(int8_t inputChannel) { + return mInputs.at(inputChannel).Get(); + } + bool NodeGraphBase::ClearInput(int8_t inputChannel) { auto& input = mInputs.at(inputChannel); if (!IsValidInOutNum(inputChannel, mInputs.size()) || !input.HasInput()) { @@ -174,6 +179,14 @@ namespace l::nodegraph { return sourceRemoved > 0 ? true : false; } + bool NodeGraphBase::IsDataVisible(int8_t) { + return false; + } + + bool NodeGraphBase::IsDataEditable(int8_t) { + return false; + } + std::string_view NodeGraphBase::GetName() { return mName; } @@ -218,7 +231,27 @@ namespace l::nodegraph { return mNumConstants; } - void GraphDataCopy::Process(std::vector& inputs, std::vector& outputs) { + bool NodeGraphOp::IsDataVisible(int8_t) { + return false; + } + + bool NodeGraphOp::IsDataEditable(int8_t) { + return false; + } + + std::string_view NodeGraphOp::GetInputName(int8_t inputChannel) { + return defaultInStrings[inputChannel]; + } + + std::string_view NodeGraphOp::GetOutputName(int8_t outputChannel) { + return defaultOutStrings[outputChannel]; + } + + std::string_view NodeGraphOp::GetName() { + return ""; + } + + void GraphDataCopy::ProcessSubGraph(std::vector& inputs, std::vector& outputs) { for (size_t i = 0; i < inputs.size() && i < outputs.size(); i++) { outputs.at(i).mOutput = inputs.at(i).Get(); } @@ -346,8 +379,20 @@ namespace l::nodegraph { return count > 0 ? true : false; } - void NodeGraphGroup::Update() { - mOutputNode.Update(); + void NodeGraphGroup::ClearProcessFlags() { + mOutputNode.ClearProcessFlags(); + } + + void NodeGraphGroup::ProcessSubGraph(bool recomputeSubGraphCache) { + if (recomputeSubGraphCache) { + mOutputNode.ClearProcessFlags(); + } + mOutputNode.ProcessSubGraph(false); } + void NodeGraphGroup::Tick(float time) { + for (auto& it : mNodes) { + it->Tick(time); + } + } } \ No newline at end of file diff --git a/packages/nodegraph/source/common/NodeGraphOperations.cpp b/packages/nodegraph/source/common/NodeGraphOperations.cpp index a156f46c..b3f67466 100644 --- a/packages/nodegraph/source/common/NodeGraphOperations.cpp +++ b/packages/nodegraph/source/common/NodeGraphOperations.cpp @@ -8,10 +8,10 @@ namespace l::nodegraph { /* Mathematical operations */ - void GraphSourceConstants::Process(std::vector& inputs, std::vector& outputs) { - ASSERT(inputs.size() == static_cast(mNumConstants)); - ASSERT(outputs.size() == static_cast(mNumConstants)); - for (int8_t i = 0; i < mNumConstants; i++) { + void GraphSourceConstants::ProcessSubGraph(std::vector& inputs, std::vector& outputs) { + ASSERT(inputs.size() == static_cast(mNumOutputs)); + ASSERT(outputs.size() == static_cast(mNumOutputs)); + for (int8_t i = 0; i < mNumOutputs; i++) { float val = inputs.at(i).Get(); val = val > mMax ? mMax : val < mMin ? mMin : val; inputs.at(i).mInput.mInputFloatConstant = val; @@ -19,7 +19,11 @@ namespace l::nodegraph { } } - void GraphSourceSine::Process(std::vector& inputs, std::vector& outputs) { + void GraphSourceConstants::Tick(float) { + mNode->ProcessSubGraph(); + } + + void GraphSourceSine::ProcessSubGraph(std::vector& inputs, std::vector& outputs) { ASSERT(inputs.size() == static_cast(mNumInputs)); ASSERT(outputs.size() == static_cast(mNumOutputs)); @@ -27,6 +31,11 @@ namespace l::nodegraph { float freq = inputs.at(1).Get(); float freqMod = inputs.at(2).Get(); float phaseMod = inputs.at(3).Get(); + float reset = inputs.at(4).Get(); + + if (reset > 0.5f) { + Reset(); + } if (mPrevTime == 0.0f) { mPrevTime = time; @@ -47,25 +56,37 @@ namespace l::nodegraph { outputs.at(1).mOutput = phase; } - void GraphNumericAdd::Process(std::vector& inputs, std::vector& outputs) { + void GraphSourceKeyboard::ProcessSubGraph(std::vector& inputs, std::vector& outputs) { + ASSERT(inputs.size() == static_cast(mNumConstants)); + ASSERT(outputs.size() == static_cast(mNumConstants)); + for (size_t i = 0; i < inputs.size();i++) { + outputs.at(i).mOutput = inputs.at(i).Get(); + } + } + + void GraphSourceKeyboard::Tick(float) { + mKeyboard.Update(); + } + + void GraphNumericAdd::ProcessSubGraph(std::vector& inputs, std::vector& outputs) { ASSERT(inputs.size() == static_cast(mNumInputs)); ASSERT(outputs.size() == static_cast(mNumOutputs)); outputs.at(0).mOutput = inputs.at(0).Get() + inputs.at(1).Get(); } - void GraphNumericMultiply::Process(std::vector& inputs, std::vector& outputs) { + void GraphNumericMultiply::ProcessSubGraph(std::vector& inputs, std::vector& outputs) { ASSERT(inputs.size() == static_cast(mNumInputs)); ASSERT(outputs.size() == static_cast(mNumOutputs)); outputs.at(0).mOutput = inputs.at(0).Get() * inputs.at(1).Get(); } - void GraphNumericSubtract::Process(std::vector& inputs, std::vector& outputs) { + void GraphNumericSubtract::ProcessSubGraph(std::vector& inputs, std::vector& outputs) { ASSERT(inputs.size() == static_cast(mNumInputs)); ASSERT(outputs.size() == static_cast(mNumOutputs)); outputs.at(0).mOutput = inputs.at(0).Get() - inputs.at(1).Get(); } - void GraphNumericNegate::Process(std::vector& inputs, std::vector& outputs) { + void GraphNumericNegate::ProcessSubGraph(std::vector& inputs, std::vector& outputs) { ASSERT(inputs.size() == static_cast(mNumInputs)); ASSERT(outputs.size() == static_cast(mNumOutputs)); outputs.at(0).mOutput = -inputs.at(0).Get(); @@ -75,7 +96,7 @@ namespace l::nodegraph { mOutput = 0.0f; } - void GraphNumericIntegral::Process(std::vector& inputs, std::vector& outputs) { + void GraphNumericIntegral::ProcessSubGraph(std::vector& inputs, std::vector& outputs) { ASSERT(inputs.size() == static_cast(mNumInputs)); ASSERT(outputs.size() == static_cast(mNumOutputs)); mOutput += inputs.at(0).Get(); @@ -84,7 +105,7 @@ namespace l::nodegraph { /* Logical operations */ - void GraphLogicalAnd::Process(std::vector& inputs, std::vector& outputs) { + void GraphLogicalAnd::ProcessSubGraph(std::vector& inputs, std::vector& outputs) { ASSERT(inputs.size() == static_cast(mNumInputs)); ASSERT(outputs.size() == static_cast(mNumOutputs)); bool input1 = inputs.at(0).Get() != 0.0f; @@ -92,7 +113,7 @@ namespace l::nodegraph { outputs.at(0).mOutput = (input1 && input2) ? 1.0f : 0.0f; } - void GraphLogicalOr::Process(std::vector& inputs, std::vector& outputs) { + void GraphLogicalOr::ProcessSubGraph(std::vector& inputs, std::vector& outputs) { ASSERT(inputs.size() == static_cast(mNumInputs)); ASSERT(outputs.size() == static_cast(mNumOutputs)); bool input1 = inputs.at(0).Get() != 0.0f; @@ -100,7 +121,7 @@ namespace l::nodegraph { outputs.at(0).mOutput = (input1 || input2) ? 1.0f : 0.0f; } - void GraphLogicalXor::Process(std::vector& inputs, std::vector& outputs) { + void GraphLogicalXor::ProcessSubGraph(std::vector& inputs, std::vector& outputs) { ASSERT(inputs.size() == static_cast(mNumInputs)); ASSERT(outputs.size() == static_cast(mNumOutputs)); bool input1 = inputs.at(0).Get() != 0.0f; @@ -115,7 +136,7 @@ namespace l::nodegraph { mState1 = 0.0f; } - void GraphFilterLowpass::Process(std::vector& inputs, std::vector& outputs) { + void GraphFilterLowpass::ProcessSubGraph(std::vector& inputs, std::vector& outputs) { ASSERT(inputs.size() == static_cast(mNumInputs)); ASSERT(outputs.size() == static_cast(mNumOutputs)); float cutoff = inputs.at(0).Get(); @@ -131,4 +152,12 @@ namespace l::nodegraph { outputs.at(0).mOutput = -mState1; } + void GraphGraphicDisplay::ProcessSubGraph(std::vector& inputs, std::vector& outputs) { + ASSERT(inputs.size() == static_cast(mNumInputs)); + ASSERT(outputs.size() == static_cast(mNumOutputs)); + } + + void GraphGraphicDisplay::Tick(float) { + mNode->ProcessSubGraph(); + } } diff --git a/packages/nodegraph/source/common/NodeGraphSchema.cpp b/packages/nodegraph/source/common/NodeGraphSchema.cpp index 2a572ad7..65721970 100644 --- a/packages/nodegraph/source/common/NodeGraphSchema.cpp +++ b/packages/nodegraph/source/common/NodeGraphSchema.cpp @@ -10,6 +10,10 @@ namespace l::nodegraph { mCreateCustomNode = customCreator; } + void NodeGraphSchema::SetKeyState(l::hid::KeyState* keyState) { + mKeyState = keyState; + } + int32_t NodeGraphSchema::NewNode(int32_t typeId) { l::nodegraph::NodeGraphBase* node = nullptr; switch (typeId) { @@ -26,6 +30,9 @@ namespace l::nodegraph { node = mMainNodeGraph.NewNode(3); break; case 4: + node = mMainNodeGraph.NewNode(3, mKeyState); + break; + case 5: node = mMainNodeGraph.NewNode(); break; case 50: @@ -55,6 +62,9 @@ namespace l::nodegraph { case 150: node = mMainNodeGraph.NewNode(); break; + case 200: + node = mMainNodeGraph.NewNode(3); + break; default: ASSERT(typeId < 10000) << "Custom node id's begin at id 1000"; ASSERT(mCreateCustomNode != nullptr) << "Custom nodes needs a handler to create them. It's missing."; @@ -86,8 +96,12 @@ namespace l::nodegraph { mRegisteredNodeTypes[typeGroup].push_back(UINodeDesc{ uniqueTypeId, std::string(typeName) }); } - void NodeGraphSchema::Update() { - mMainNodeGraph.Update(); + void NodeGraphSchema::Tick(float time) { + mMainNodeGraph.Tick(time); + } + + void NodeGraphSchema::ProcessSubGraph() { + mMainNodeGraph.ProcessSubGraph(true); } diff --git a/packages/nodegraph/tests/common/NodeGraphSchemaTest.cpp b/packages/nodegraph/tests/common/NodeGraphSchemaTest.cpp index 48c25975..af48dc14 100644 --- a/packages/nodegraph/tests/common/NodeGraphSchemaTest.cpp +++ b/packages/nodegraph/tests/common/NodeGraphSchemaTest.cpp @@ -17,7 +17,7 @@ TEST(NodeGraph, BasicFunction) { node.SetInput(0, 1.8f); node.SetInput(1, &in2); - node.Update(); + node.ProcessSubGraph(); TEST_FUZZY(node.Get(0), 4.1f, 0.0001f, ""); return 0; @@ -41,7 +41,7 @@ TEST(NodeGraph, SimpleAddNetwork) { nodeFinal.SetInput(0, node1, 0); nodeFinal.SetInput(1, node2, 0); - nodeFinal.Update(); + nodeFinal.ProcessSubGraph(); TEST_FUZZY(nodeFinal.Get(0), 12.6f, 0.0001f, ""); return 0; @@ -68,7 +68,7 @@ TEST(NodeGraph, BasicMathematicalOperations) { nodeOutput.SetInput(0, node3, 0); // - (in1 + in2) * in3 - in4 - nodeOutput.Update(); + nodeOutput.ProcessSubGraph(); TEST_FUZZY(nodeOutput.Get(0), -6.9f, 0.0001f, ""); return 0; @@ -83,7 +83,7 @@ TEST(NodeGraph, NumericIntegral) { float oneRev = 2.0f * 3.14f / 30.0f; for (int i = 0; i < 30; i++) { input = sinf(2.0f * i * oneRev); - nodeIntegral.Update(); + nodeIntegral.ProcessSubGraph(); //LOG(LogInfo) << nodeIntegral.Get(0); } @@ -106,7 +106,7 @@ TEST(NodeGraph, FilterLowpass) { float oneRev = 2.0f * 3.14f / 30.0f; for (int i = 0; i < 30; i++) { input = sinf(2.0f * i * oneRev); - nodeLowpass.Update(); + nodeLowpass.ProcessSubGraph(); //LOG(LogInfo) << nodeLowpass.Get(0); } @@ -171,7 +171,7 @@ TEST(NodeGraph, GraphGroups) { } // only update the last group/node and all dependent nodes will update in the graph to produce an output - group2.Update(); + group2.Tick(0.0f); float output1 = group2.Get(0); float output2 = group2.Get(1); diff --git a/packages/rendering/CMakeLists.txt b/packages/rendering/CMakeLists.txt index 4fff279d..90225bd1 100644 --- a/packages/rendering/CMakeLists.txt +++ b/packages/rendering/CMakeLists.txt @@ -10,6 +10,7 @@ set(deps testing memory nodegraph + hid filesystem tools diff --git a/packages/rendering/include/rendering/ui/UIContainer.h b/packages/rendering/include/rendering/ui/UIContainer.h index 4e69a800..751418c4 100644 --- a/packages/rendering/include/rendering/ui/UIContainer.h +++ b/packages/rendering/include/rendering/ui/UIContainer.h @@ -242,7 +242,8 @@ namespace l::ui { const uint32_t UIContainer_OutputFlag = 0x00000400; const uint32_t UIContainer_LinkFlag = 0x00000800; const uint32_t UIContainer_SelectFlag = 0x00001000; - const uint32_t UIContainer_EditFlag = 0x00002000; + const uint32_t UIContainer_EditFlag = 0x00002000; + const uint32_t UIContainer_ConstantsKeyboardFlag = 0x00004000; class UIDraw; diff --git a/packages/rendering/include/rendering/ui/UINodeEditor.h b/packages/rendering/include/rendering/ui/UINodeEditor.h index 6e40cf8c..b5c380be 100644 --- a/packages/rendering/include/rendering/ui/UINodeEditor.h +++ b/packages/rendering/include/rendering/ui/UINodeEditor.h @@ -88,6 +88,7 @@ namespace l::ui { } void Update() { + if (mUIWindow.IsShowing()) { ImGuiIO& io = ImGui::GetIO(); io.ConfigWindowsMoveFromTitleBarOnly = true; diff --git a/packages/rendering/include/rendering/ui/UIVisitors.h b/packages/rendering/include/rendering/ui/UIVisitors.h index 2dfb2899..f25b11fe 100644 --- a/packages/rendering/include/rendering/ui/UIVisitors.h +++ b/packages/rendering/include/rendering/ui/UIVisitors.h @@ -1,9 +1,12 @@ #pragma once #include "rendering/ui/UIContainer.h" +#include "rendering/ui/UIWindow.h" + #include "nodegraph/NodeGraphSchema.h" #include +#include namespace l::ui { @@ -132,5 +135,4 @@ namespace l::ui { l::nodegraph::NodeGraphSchema* mNGSchema = nullptr; }; - } diff --git a/packages/rendering/include/rendering/ui/UIWindow.h b/packages/rendering/include/rendering/ui/UIWindow.h index 959d5be8..f1e5c3c5 100644 --- a/packages/rendering/include/rendering/ui/UIWindow.h +++ b/packages/rendering/include/rendering/ui/UIWindow.h @@ -42,6 +42,7 @@ namespace l::ui { ImVec2 GetPosition(); ImVec2 GetSize(); void SetBgColor(ImVec4 bgColor); + protected: std::string mWindowName; std::string mPopupName; diff --git a/packages/rendering/source/common/ui/UICreator.cpp b/packages/rendering/source/common/ui/UICreator.cpp index 5001a6d2..b36cefc4 100644 --- a/packages/rendering/source/common/ui/UICreator.cpp +++ b/packages/rendering/source/common/ui/UICreator.cpp @@ -7,16 +7,18 @@ namespace l::ui { UIHandle CreateUINode(UIStorage& uiStorage, l::nodegraph::NodeGraphBase& node, ImVec2 p) { + auto numInputChannels = node.GetNumInputs(); auto numOutputChannels = node.GetNumOutputs(); auto numConstants = node.GetNumConstants(); + auto numRows = numInputChannels > numOutputChannels ? numInputChannels + numConstants : numOutputChannels; auto node4 = CreateSplit(uiStorage, l::ui::UIContainer_ResizeFlag | l::ui::UIContainer_MoveFlag | l::ui::UIContainer_DrawFlag | UIContainer_SelectFlag, l::ui::UIRenderType::RectFilled, l::ui::UISplitMode::AppendV); node4->SetColor(ImVec4(0.15f, 0.15f, 0.15f, 1.0f)); node4->SetPosition(p); node4->GetContainerArea().mMargin = 0.0f; node4->SetNodeId(node.GetId()); - ImVec2 sizeEstimate = ImVec2(100.0f, 22.0f + 19.0f * (numInputChannels > numOutputChannels ? numInputChannels : numOutputChannels)); + ImVec2 sizeEstimate = ImVec2(100.0f, 22.0f + 20.0f * numRows); { float ioSize = 4.0f; float ioOffsetV = 1.6f; @@ -33,13 +35,16 @@ namespace l::ui { connector1Text->GetContainerArea().mRender.mColor = ImColor(ImVec4(0.5f, 1.0f, 0.4f, 1.0f)); row0->Add(connector1Text); } - for (int8_t i = 0; i < numInputChannels || i < numOutputChannels; i++) { + for (int8_t i = 0; i < numInputChannels || i < numOutputChannels || i < numInputChannels + numConstants || i < numOutputChannels; i++) { auto row = CreateContainer(uiStorage, 0, l::ui::UIRenderType::Rect, l::ui::UIAlignH::Left, l::ui::UIAlignV::Top, l::ui::UILayoutH::Parent, l::ui::UILayoutV::Fixed); row->GetContainerArea().mMargin = ioSize; node4->Add(row); { + bool showsInput = i < numInputChannels; + bool showsOutput = i < numOutputChannels; + float estimatedWidth = 0.0f; - if (i < numInputChannels) { + if (showsInput) { auto in = CreateContainer(uiStorage, l::ui::UIContainer_InputFlag | l::ui::UIContainer_DrawFlag, l::ui::UIRenderType::CircleFilled, l::ui::UIAlignH::Left); in->SetPosition(ImVec2(-ioSize, ioSize * ioOffsetV)); in->SetSize(ImVec2(ioSize, ioSize)); @@ -55,30 +60,17 @@ namespace l::ui { row->Add(inText); } - if (i < numOutputChannels - numConstants) { - auto out = CreateContainer(uiStorage, l::ui::UIContainer_OutputFlag | l::ui::UIContainer_DrawFlag, l::ui::UIRenderType::CircleFilled, l::ui::UIAlignH::Right); - out->SetPosition(ImVec2(ioSize * 2.0f, ioSize * ioOffsetV)); - out->SetSize(ImVec2(ioSize, ioSize)); - out->GetContainerArea().mMargin = 0.0f; - out->SetNodeId(node.GetId()); - out->SetChannelId(i); - estimatedWidth += ioSize; - row->Add(out); - auto outText = CreateContainer(uiStorage, l::ui::UIContainer_DrawFlag, l::ui::UIRenderType::Text, l::ui::UIAlignH::Right); - outText->SetPosition(ImVec2(0.0f, 0.0f)); - outText->SetDisplayName(node.GetOutputName(i)); - estimatedWidth += ImGui::CalcTextSize(node.GetOutputName(i).data()).x; - row->Add(outText); - } - else if (i < numOutputChannels) { - auto inText = CreateContainer(uiStorage, l::ui::UIContainer_EditFlag | l::ui::UIContainer_DrawFlag, l::ui::UIRenderType::NodeOutputValue, l::ui::UIAlignH::Left); - inText->SetPosition(ImVec2(0.0f, 0.0f)); + if (node.IsDataVisible(i)) { + auto inText = CreateContainer(uiStorage, (node.IsDataEditable(i) ? l::ui::UIContainer_EditFlag : 0) | l::ui::UIContainer_DrawFlag, l::ui::UIRenderType::NodeOutputValue, l::ui::UIAlignH::Left); + inText->SetPosition(ImVec2(estimatedWidth, 0.0f)); inText->SetSize(ImVec2(10 * 7, 14.0f)); inText->SetNodeId(node.GetId()); inText->SetChannelId(i); estimatedWidth += 10 * 7 + 10; row->Add(inText); + } + if (showsOutput) { auto out = CreateContainer(uiStorage, l::ui::UIContainer_OutputFlag | l::ui::UIContainer_DrawFlag, l::ui::UIRenderType::CircleFilled, l::ui::UIAlignH::Right); out->SetPosition(ImVec2(ioSize * 2.0f, ioSize * ioOffsetV)); out->SetSize(ImVec2(ioSize, ioSize)); diff --git a/packages/rendering/source/common/ui/UIVisitors.cpp b/packages/rendering/source/common/ui/UIVisitors.cpp index 4639d617..43b985ea 100644 --- a/packages/rendering/source/common/ui/UIVisitors.cpp +++ b/packages/rendering/source/common/ui/UIVisitors.cpp @@ -1,4 +1,5 @@ #include "rendering/ui/UIVisitors.h" +#include "hid/KeyboardPiano.h" namespace l::ui { @@ -335,7 +336,13 @@ namespace l::ui { case l::ui::UIRenderType::NodeOutputValue: if (mNGSchema) { auto node = mNGSchema->GetNode(container.GetNodeId()); - auto nodeValue = node->Get(static_cast(container.GetChannelId())); + float nodeValue = 0.0f; + if (node->GetNumOutputs() > 0) { + nodeValue = node->Get(static_cast(container.GetChannelId())); + } + else { + nodeValue = node->GetInput(static_cast(container.GetChannelId())); + } auto nodeString = std::to_string(nodeValue); mDrawList->AddText(ImGui::GetDefaultFont(), 13.0f * container.GetScale() * layoutArea.mScale, p1, color, nodeString.c_str()); } @@ -400,9 +407,8 @@ namespace l::ui { ImVec2 size = container.GetCoParent()->GetSize(); ImVec2 pT = container.GetCoParent()->GetLayoutArea().Transform(pCenter); if (OverlapCircle(input.mCurPos, pT, size.x * container.GetCoParent()->GetLayoutArea().mScale)) { - DeleteContainer(mUIStorage, mLinkContainer); mLinkContainer.mContainer = &container; - LinkHandler(container.GetNodeId(), mLinkContainer->GetParent()->GetNodeId(), container.GetChannelId(), mLinkContainer->GetParent()->GetChannelId(), false); + LinkHandler(mLinkContainer->GetCoParent()->GetNodeId(), mLinkContainer->GetParent()->GetNodeId(), mLinkContainer->GetCoParent()->GetChannelId(), mLinkContainer->GetParent()->GetChannelId(), false); mDragging = true; return true; } @@ -445,14 +451,14 @@ namespace l::ui { mLinkContainer.Reset(); } else { - DeleteContainer(mUIStorage, mLinkContainer); + DeleteContainer(mUIStorage, mLinkContainer.Get()); mDragging = false; mLinkContainer.Reset(); + return true; } } } return false; } - } From 04e999985b43d76c6eb8e4ec8d31cc3aefdbc6c9 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Sat, 31 Aug 2024 21:38:19 +0200 Subject: [PATCH 083/125] Fix warnings. Fix node graph test. --- .../nodegraph/include/nodegraph/NodeGraphOperations.h | 10 +++++----- packages/nodegraph/source/common/NodeGraph.cpp | 3 ++- .../nodegraph/tests/common/NodeGraphSchemaTest.cpp | 2 +- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/packages/nodegraph/include/nodegraph/NodeGraphOperations.h b/packages/nodegraph/include/nodegraph/NodeGraphOperations.h index 7efe1ccf..96878baa 100644 --- a/packages/nodegraph/include/nodegraph/NodeGraphOperations.h +++ b/packages/nodegraph/include/nodegraph/NodeGraphOperations.h @@ -9,7 +9,7 @@ #include #include #include -#include +#include namespace l::nodegraph { @@ -155,7 +155,7 @@ namespace l::nodegraph { } protected: int8_t ResetNoteChannel(int32_t note) { - for (int8_t i = 0; i < mChannel.size(); i++) { + for (size_t i = 0; i < mChannel.size(); i++) { if (mChannel.at(i).first == note) { mChannel.at(i).second = 0; return i; @@ -166,14 +166,14 @@ namespace l::nodegraph { } int8_t GetNextNoteChannel(int32_t note) { - for (int8_t i = 0; i < mChannel.size(); i++) { + for (size_t i = 0; i < mChannel.size(); i++) { if (mChannel.at(i).first == note) { mChannel.at(i).second = mNoteCounter++; return i; } } - for (int8_t i = 0; i < mChannel.size(); i++) { + for (size_t i = 0; i < mChannel.size(); i++) { if (mChannel.at(i).first == 0) { mChannel.at(i).first = note; mChannel.at(i).second = mNoteCounter++; @@ -183,7 +183,7 @@ namespace l::nodegraph { int32_t lowestCount = INT32_MAX; int8_t lowestCountIndex = 0; - for (int8_t i = 0; i < mChannel.size(); i++) { + for (size_t i = 0; i < mChannel.size(); i++) { if (lowestCount > mChannel.at(i).second) { lowestCount = mChannel.at(i).second; lowestCountIndex = i; diff --git a/packages/nodegraph/source/common/NodeGraph.cpp b/packages/nodegraph/source/common/NodeGraph.cpp index b391b147..d9e5fe97 100644 --- a/packages/nodegraph/source/common/NodeGraph.cpp +++ b/packages/nodegraph/source/common/NodeGraph.cpp @@ -143,7 +143,7 @@ namespace l::nodegraph { } bool NodeGraphBase::SetInput(int8_t inputChannel, float constant) { - ASSERT(inputChannel >= mInputCount && inputChannel < mInputCount + mConstantCount); + ASSERT(inputChannel >= 0 && inputChannel < mInputCount + mConstantCount); auto& input = mInputs.at(inputChannel); if (!IsValidInOutNum(inputChannel, mInputs.size())) { return false; @@ -156,6 +156,7 @@ namespace l::nodegraph { } bool NodeGraphBase::SetInput(int8_t inputChannel, float* floatPtr) { + ASSERT(inputChannel >= 0 && inputChannel < mInputCount); auto& input = mInputs.at(inputChannel); if (!IsValidInOutNum(inputChannel, mInputs.size())) { return false; diff --git a/packages/nodegraph/tests/common/NodeGraphSchemaTest.cpp b/packages/nodegraph/tests/common/NodeGraphSchemaTest.cpp index af48dc14..60768c1d 100644 --- a/packages/nodegraph/tests/common/NodeGraphSchemaTest.cpp +++ b/packages/nodegraph/tests/common/NodeGraphSchemaTest.cpp @@ -171,7 +171,7 @@ TEST(NodeGraph, GraphGroups) { } // only update the last group/node and all dependent nodes will update in the graph to produce an output - group2.Tick(0.0f); + group2.ProcessSubGraph(); float output1 = group2.Get(0); float output2 = group2.Get(1); From 3eee4708cdb194d1fe60d59b1df7de8d591d45cf Mon Sep 17 00:00:00 2001 From: lnd3 Date: Sat, 31 Aug 2024 22:06:12 +0200 Subject: [PATCH 084/125] Fix some errors and warnings. --- .../include/nodegraph/NodeGraphOperations.h | 28 ++++++++++++----- .../rendering/source/common/ui/UIVisitors.cpp | 30 +++++++++++-------- 2 files changed, 37 insertions(+), 21 deletions(-) diff --git a/packages/nodegraph/include/nodegraph/NodeGraphOperations.h b/packages/nodegraph/include/nodegraph/NodeGraphOperations.h index 96878baa..ed822fc1 100644 --- a/packages/nodegraph/include/nodegraph/NodeGraphOperations.h +++ b/packages/nodegraph/include/nodegraph/NodeGraphOperations.h @@ -18,7 +18,8 @@ namespace l::nodegraph { class GraphSourceConstants : public NodeGraphOp { public: GraphSourceConstants(NodeGraphBase* node, int32_t mode) : - NodeGraphOp(node, 0, 1, 1) + NodeGraphOp(node, 0, 1, 1), + mMode(mode) { switch (mode) { case 0: @@ -34,8 +35,8 @@ namespace l::nodegraph { mMin = 0.0f; break; default: - mMax = 0.0f; - mMin = 0.0f; + mMax = 3.402823466e+38F; + mMin = -3.402823466e+38F; break; } } @@ -45,7 +46,17 @@ namespace l::nodegraph { virtual void Tick(float) override; std::string_view GetName() override { - return "Constant"; + switch (mMode) { + case 0: + return "Constant [0,1]"; + case 1: + return "Constant [-1,1]"; + case 2: + return "Constant [0,100]"; + case 3: + return "Constant [-inf,inf]"; + }; + return ""; } bool IsDataVisible(int8_t) override { @@ -57,6 +68,7 @@ namespace l::nodegraph { } protected: + int32_t mMode; float mMax = 1.0f; float mMin = 0.0f; }; @@ -158,7 +170,7 @@ namespace l::nodegraph { for (size_t i = 0; i < mChannel.size(); i++) { if (mChannel.at(i).first == note) { mChannel.at(i).second = 0; - return i; + return static_cast(i); } } // It is possible to get a note off for a note not playing because the channel was taken for another newer note @@ -169,7 +181,7 @@ namespace l::nodegraph { for (size_t i = 0; i < mChannel.size(); i++) { if (mChannel.at(i).first == note) { mChannel.at(i).second = mNoteCounter++; - return i; + return static_cast(i); } } @@ -177,7 +189,7 @@ namespace l::nodegraph { if (mChannel.at(i).first == 0) { mChannel.at(i).first = note; mChannel.at(i).second = mNoteCounter++; - return i; + return static_cast(i); } } @@ -186,7 +198,7 @@ namespace l::nodegraph { for (size_t i = 0; i < mChannel.size(); i++) { if (lowestCount > mChannel.at(i).second) { lowestCount = mChannel.at(i).second; - lowestCountIndex = i; + lowestCountIndex = static_cast(i); } } mChannel.at(lowestCountIndex).first = note; diff --git a/packages/rendering/source/common/ui/UIVisitors.cpp b/packages/rendering/source/common/ui/UIVisitors.cpp index 43b985ea..51057f61 100644 --- a/packages/rendering/source/common/ui/UIVisitors.cpp +++ b/packages/rendering/source/common/ui/UIVisitors.cpp @@ -208,22 +208,26 @@ namespace l::ui { if (mNGSchema) { auto node = mNGSchema->GetNode(container.GetNodeId()); - auto& nodeValue = node->Get(static_cast(container.GetChannelId())); - if (!ImGui::IsKeyDown(ImGuiKey::ImGuiKey_LeftShift)) { - if (!ImGui::IsKeyDown(ImGuiKey::ImGuiKey_LeftCtrl)) { - nodeValue -= move.y / 100.0f; + auto nodeChannel = static_cast(container.GetChannelId()); + if (node->IsDataEditable(nodeChannel)) { + auto nodeValue = node->Get(nodeChannel); + if (!ImGui::IsKeyDown(ImGuiKey::ImGuiKey_LeftShift)) { + if (!ImGui::IsKeyDown(ImGuiKey::ImGuiKey_LeftCtrl)) { + nodeValue -= move.y / 100.0f; + } + else { + nodeValue -= move.y / 10000.0f; + } } else { - nodeValue -= move.y / 10000.0f; - } - } - else { - if (!ImGui::IsKeyDown(ImGuiKey::ImGuiKey_LeftCtrl)) { - nodeValue -= move.y; - } - else { - nodeValue -= 1000.0f * move.y; + if (!ImGui::IsKeyDown(ImGuiKey::ImGuiKey_LeftCtrl)) { + nodeValue -= move.y; + } + else { + nodeValue -= 1000.0f * move.y; + } } + node->SetInput(nodeChannel, nodeValue); } } From 07e25711f60c5b6fa3655cc064572b34ba2c2a3c Mon Sep 17 00:00:00 2001 From: lnd3 Date: Sat, 31 Aug 2024 22:20:34 +0200 Subject: [PATCH 085/125] Move code to cpp file. --- .../include/nodegraph/NodeGraphOperations.h | 131 +++--------------- .../source/common/NodeGraphOperations.cpp | 120 +++++++++++++++- .../source/common/NodeGraphSchema.cpp | 4 +- 3 files changed, 138 insertions(+), 117 deletions(-) diff --git a/packages/nodegraph/include/nodegraph/NodeGraphOperations.h b/packages/nodegraph/include/nodegraph/NodeGraphOperations.h index ed822fc1..c133ae63 100644 --- a/packages/nodegraph/include/nodegraph/NodeGraphOperations.h +++ b/packages/nodegraph/include/nodegraph/NodeGraphOperations.h @@ -18,7 +18,7 @@ namespace l::nodegraph { class GraphSourceConstants : public NodeGraphOp { public: GraphSourceConstants(NodeGraphBase* node, int32_t mode) : - NodeGraphOp(node, 0, 1, 1), + NodeGraphOp(node, 0, 4, 4), mMode(mode) { switch (mode) { @@ -45,27 +45,9 @@ namespace l::nodegraph { void ProcessSubGraph(std::vector& inputs, std::vector& outputs) override; virtual void Tick(float) override; - std::string_view GetName() override { - switch (mMode) { - case 0: - return "Constant [0,1]"; - case 1: - return "Constant [-1,1]"; - case 2: - return "Constant [0,100]"; - case 3: - return "Constant [-inf,inf]"; - }; - return ""; - } - - bool IsDataVisible(int8_t) override { - return true; - } - - bool IsDataEditable(int8_t) override { - return true; - } + std::string_view GetName() override; + bool IsDataVisible(int8_t) override; + bool IsDataEditable(int8_t) override; protected: int32_t mMode; @@ -85,22 +67,10 @@ namespace l::nodegraph { virtual ~GraphSourceSine() = default; void ProcessSubGraph(std::vector& inputs, std::vector& outputs) override; - void Reset() override { - mPhase = 0.0f; - mPrevTime = 0.0f; - } - - std::string_view GetInputName(int8_t inputChannel) { - return defaultInStrings[inputChannel]; - } - - std::string_view GetOutputName(int8_t outputChannel) { - return defaultOutStrings[outputChannel]; - } - - std::string_view GetName() override { - return "Sine"; - } + void Reset() override; + std::string_view GetInputName(int8_t inputChannel); + std::string_view GetOutputName(int8_t outputChannel); + std::string_view GetName() override; protected: float mPhase = 0.0f; @@ -128,83 +98,16 @@ namespace l::nodegraph { virtual ~GraphSourceKeyboard() = default; void ProcessSubGraph(std::vector& inputs, std::vector& outputs) override; void Tick(float time) override; - - void Reset() override { - for (int8_t i = 0; i < GetNumInputs(); i++) { - mNode->SetInput(i, 0.0f); - } - mNode->ProcessSubGraph(); - } - - virtual std::string_view GetOutputName(int8_t outputChannel) override { - return defaultOutStrings[outputChannel]; - } - - virtual std::string_view GetName() override { - return "Keyboard"; - } - - virtual bool IsDataVisible(int8_t) override { - return true; - } - - virtual void NoteOn(int32_t note) override { - float frequency = GetFrequencyFromNote(static_cast(note)); - int8_t channel = GetNextNoteChannel(note); - mNode->SetInput(static_cast(channel), frequency); - mNode->ProcessSubGraph(); - } - virtual void NoteOff() override { - Reset(); - } - - virtual void NoteOff(int32_t note) override { - int8_t channel = ResetNoteChannel(note); - if (channel >= 0) { - mNode->SetInput(channel, 0.0f); - mNode->ProcessSubGraph(); - } - } + void Reset() override; + virtual std::string_view GetOutputName(int8_t outputChannel) override; + virtual std::string_view GetName() override; + virtual bool IsDataVisible(int8_t) override; + virtual void NoteOn(int32_t note) override; + virtual void NoteOff() override; + virtual void NoteOff(int32_t note) override; protected: - int8_t ResetNoteChannel(int32_t note) { - for (size_t i = 0; i < mChannel.size(); i++) { - if (mChannel.at(i).first == note) { - mChannel.at(i).second = 0; - return static_cast(i); - } - } - // It is possible to get a note off for a note not playing because the channel was taken for another newer note - return -1; - } - - int8_t GetNextNoteChannel(int32_t note) { - for (size_t i = 0; i < mChannel.size(); i++) { - if (mChannel.at(i).first == note) { - mChannel.at(i).second = mNoteCounter++; - return static_cast(i); - } - } - - for (size_t i = 0; i < mChannel.size(); i++) { - if (mChannel.at(i).first == 0) { - mChannel.at(i).first = note; - mChannel.at(i).second = mNoteCounter++; - return static_cast(i); - } - } - - int32_t lowestCount = INT32_MAX; - int8_t lowestCountIndex = 0; - for (size_t i = 0; i < mChannel.size(); i++) { - if (lowestCount > mChannel.at(i).second) { - lowestCount = mChannel.at(i).second; - lowestCountIndex = static_cast(i); - } - } - mChannel.at(lowestCountIndex).first = note; - mChannel.at(lowestCountIndex).second = mNoteCounter++; - return lowestCountIndex; - } + int8_t ResetNoteChannel(int32_t note); + int8_t GetNextNoteChannel(int32_t note); int8_t mNoteCounter = 0; std::vector> mChannel; diff --git a/packages/nodegraph/source/common/NodeGraphOperations.cpp b/packages/nodegraph/source/common/NodeGraphOperations.cpp index b3f67466..3d9bc939 100644 --- a/packages/nodegraph/source/common/NodeGraphOperations.cpp +++ b/packages/nodegraph/source/common/NodeGraphOperations.cpp @@ -23,6 +23,28 @@ namespace l::nodegraph { mNode->ProcessSubGraph(); } + std::string_view GraphSourceConstants::GetName() { + switch (mMode) { + case 0: + return "Constant [0,1]"; + case 1: + return "Constant [-1,1]"; + case 2: + return "Constant [0,100]"; + case 3: + return "Constant [-inf,inf]"; + }; + return ""; + } + + bool GraphSourceConstants::IsDataVisible(int8_t) { + return true; + } + + bool GraphSourceConstants::IsDataEditable(int8_t) { + return true; + } + void GraphSourceSine::ProcessSubGraph(std::vector& inputs, std::vector& outputs) { ASSERT(inputs.size() == static_cast(mNumInputs)); ASSERT(outputs.size() == static_cast(mNumOutputs)); @@ -37,11 +59,12 @@ namespace l::nodegraph { Reset(); } + float deltaTime = time - mPrevTime; if (mPrevTime == 0.0f) { mPrevTime = time; + deltaTime = time - mPrevTime; } - float deltaTime = time - mPrevTime; mPrevTime = time; float phaseDelta = deltaTime * freq; @@ -56,6 +79,23 @@ namespace l::nodegraph { outputs.at(1).mOutput = phase; } + void GraphSourceSine::Reset() { + mPhase = 0.0f; + mPrevTime = 0.0f; + } + + std::string_view GraphSourceSine::GetInputName(int8_t inputChannel) { + return defaultInStrings[inputChannel]; + } + + std::string_view GraphSourceSine::GetOutputName(int8_t outputChannel) { + return defaultOutStrings[outputChannel]; + } + + std::string_view GraphSourceSine::GetName() { + return "Sine"; + } + void GraphSourceKeyboard::ProcessSubGraph(std::vector& inputs, std::vector& outputs) { ASSERT(inputs.size() == static_cast(mNumConstants)); ASSERT(outputs.size() == static_cast(mNumConstants)); @@ -68,6 +108,84 @@ namespace l::nodegraph { mKeyboard.Update(); } + void GraphSourceKeyboard::Reset() { + for (int8_t i = 0; i < GetNumInputs(); i++) { + mNode->SetInput(i, 0.0f); + } + mNode->ProcessSubGraph(); + } + + std::string_view GraphSourceKeyboard::GetOutputName(int8_t outputChannel) { + return defaultOutStrings[outputChannel]; + } + + std::string_view GraphSourceKeyboard::GetName() { + return "Keyboard"; + } + + bool GraphSourceKeyboard::IsDataVisible(int8_t) { + return true; + } + + void GraphSourceKeyboard::NoteOn(int32_t note) { + float frequency = GetFrequencyFromNote(static_cast(note)); + int8_t channel = GetNextNoteChannel(note); + mNode->SetInput(static_cast(channel), frequency); + mNode->ProcessSubGraph(); + } + void GraphSourceKeyboard::NoteOff() { + Reset(); + } + + void GraphSourceKeyboard::NoteOff(int32_t note) { + int8_t channel = ResetNoteChannel(note); + if (channel >= 0) { + mNode->SetInput(channel, 0.0f); + mNode->ProcessSubGraph(); + } + } + + int8_t GraphSourceKeyboard::ResetNoteChannel(int32_t note) { + for (size_t i = 0; i < mChannel.size(); i++) { + if (mChannel.at(i).first == note) { + mChannel.at(i).second = 0; + return static_cast(i); + } + } + // It is possible to get a note off for a note not playing because the channel was taken for another newer note + return -1; + } + + int8_t GraphSourceKeyboard::GetNextNoteChannel(int32_t note) { + for (size_t i = 0; i < mChannel.size(); i++) { + if (mChannel.at(i).first == note) { + mChannel.at(i).second = mNoteCounter++; + return static_cast(i); + } + } + + for (size_t i = 0; i < mChannel.size(); i++) { + if (mChannel.at(i).first == 0) { + mChannel.at(i).first = note; + mChannel.at(i).second = mNoteCounter++; + return static_cast(i); + } + } + + int32_t lowestCount = INT32_MAX; + int8_t lowestCountIndex = 0; + for (size_t i = 0; i < mChannel.size(); i++) { + if (lowestCount > mChannel.at(i).second) { + lowestCount = mChannel.at(i).second; + lowestCountIndex = static_cast(i); + } + } + mChannel.at(lowestCountIndex).first = note; + mChannel.at(lowestCountIndex).second = mNoteCounter++; + return lowestCountIndex; + } + + void GraphNumericAdd::ProcessSubGraph(std::vector& inputs, std::vector& outputs) { ASSERT(inputs.size() == static_cast(mNumInputs)); ASSERT(outputs.size() == static_cast(mNumOutputs)); diff --git a/packages/nodegraph/source/common/NodeGraphSchema.cpp b/packages/nodegraph/source/common/NodeGraphSchema.cpp index 65721970..7bd47e21 100644 --- a/packages/nodegraph/source/common/NodeGraphSchema.cpp +++ b/packages/nodegraph/source/common/NodeGraphSchema.cpp @@ -30,7 +30,7 @@ namespace l::nodegraph { node = mMainNodeGraph.NewNode(3); break; case 4: - node = mMainNodeGraph.NewNode(3, mKeyState); + node = mMainNodeGraph.NewNode(4, mKeyState); break; case 5: node = mMainNodeGraph.NewNode(); @@ -63,7 +63,7 @@ namespace l::nodegraph { node = mMainNodeGraph.NewNode(); break; case 200: - node = mMainNodeGraph.NewNode(3); + node = mMainNodeGraph.NewNode(4); break; default: ASSERT(typeId < 10000) << "Custom node id's begin at id 1000"; From 977ecfc52524c25f194ce06eaae44cd2774ec75a Mon Sep 17 00:00:00 2001 From: lnd3 Date: Sat, 31 Aug 2024 22:37:07 +0200 Subject: [PATCH 086/125] Fix sine source. --- .../include/nodegraph/NodeGraphOperations.h | 4 ++-- .../source/common/NodeGraphOperations.cpp | 15 ++++++++------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/packages/nodegraph/include/nodegraph/NodeGraphOperations.h b/packages/nodegraph/include/nodegraph/NodeGraphOperations.h index c133ae63..a74ebf70 100644 --- a/packages/nodegraph/include/nodegraph/NodeGraphOperations.h +++ b/packages/nodegraph/include/nodegraph/NodeGraphOperations.h @@ -58,11 +58,11 @@ namespace l::nodegraph { class GraphSourceSine : public NodeGraphOp { public: GraphSourceSine(NodeGraphBase* node) : - NodeGraphOp(node, 5, 2) + NodeGraphOp(node, 5, 3) {} std::string defaultInStrings[5] = { "Time", "Freq Hz", "Freq Mod", "Phase Mod", "Reset"}; - std::string defaultOutStrings[2] = { "Sine", "Phase"}; + std::string defaultOutStrings[3] = { "Sine", "Phase", "Phase Mod"}; virtual ~GraphSourceSine() = default; void ProcessSubGraph(std::vector& inputs, std::vector& outputs) override; diff --git a/packages/nodegraph/source/common/NodeGraphOperations.cpp b/packages/nodegraph/source/common/NodeGraphOperations.cpp index 3d9bc939..24510bb0 100644 --- a/packages/nodegraph/source/common/NodeGraphOperations.cpp +++ b/packages/nodegraph/source/common/NodeGraphOperations.cpp @@ -51,8 +51,8 @@ namespace l::nodegraph { float time = inputs.at(0).Get(); float freq = inputs.at(1).Get(); - float freqMod = inputs.at(2).Get(); - float phaseMod = inputs.at(3).Get(); + float fMod = 1.0f + inputs.at(2).Get(); + float pMod = inputs.at(3).Get(); float reset = inputs.at(4).Get(); if (reset > 0.5f) { @@ -69,14 +69,15 @@ namespace l::nodegraph { float phaseDelta = deltaTime * freq; - mPhase += phaseDelta * freqMod; + mPhase += phaseDelta * fMod; mPhase -= floorf(mPhase); - float phase = (mPhase + phaseMod) * 0.5f; - phase -= floorf(phase); + float phaseMod = mPhase + pMod; + phaseMod -= floorf(phaseMod); - outputs.at(0).mOutput = sinf(2.0f * 3.141529f * phase); - outputs.at(1).mOutput = phase; + outputs.at(0).mOutput = sinf(3.141529f * (mPhase + phaseMod)); + outputs.at(1).mOutput = mPhase; + outputs.at(2).mOutput = phaseMod; } void GraphSourceSine::Reset() { From fffb8626c836067ab9d39b9a95f0e267bdc64efc Mon Sep 17 00:00:00 2001 From: lnd3 Date: Sun, 1 Sep 2024 00:49:26 +0200 Subject: [PATCH 087/125] Add audio package for miniaudio. --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 18c07f0b..ece516d8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,6 +31,7 @@ else() network storage tools + audio physics ecs From a84f53121c264b8ffc92bb59119f2b83389ae39e Mon Sep 17 00:00:00 2001 From: lnd3 Date: Sun, 1 Sep 2024 05:27:07 +0200 Subject: [PATCH 088/125] Some cmake config changes. --- CMakeLists.txt | 2 +- packages/audio/CMakeLists.txt | 11 +++++++++++ packages/audio/include/audio/Audio.h | 9 +++++++++ packages/audio/source/common/Audio.cpp | 16 ++++++++++++++++ packages/audio/tests/common/AudioTest.cpp | 7 +++++++ packages/audio/tests/common/MiniAudioTest.cpp | 9 +++++++++ packages/nodegraph/CMakeLists.txt | 5 +++++ .../include/nodegraph/NodeGraphOperations.h | 6 ------ .../source/common/NodeGraphOperations.cpp | 3 ++- 9 files changed, 60 insertions(+), 8 deletions(-) create mode 100644 packages/audio/CMakeLists.txt create mode 100644 packages/audio/include/audio/Audio.h create mode 100644 packages/audio/source/common/Audio.cpp create mode 100644 packages/audio/tests/common/AudioTest.cpp create mode 100644 packages/audio/tests/common/MiniAudioTest.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index ece516d8..f312e713 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,6 +24,7 @@ else() meta memory hid + audio nodegraph concurrency @@ -31,7 +32,6 @@ else() network storage tools - audio physics ecs diff --git a/packages/audio/CMakeLists.txt b/packages/audio/CMakeLists.txt new file mode 100644 index 00000000..ba1a552f --- /dev/null +++ b/packages/audio/CMakeLists.txt @@ -0,0 +1,11 @@ +project (audio) + +set(deps + various + + logging + testing + memory +) + +bs_generate_package(audio "tier1" "${deps}" "") diff --git a/packages/audio/include/audio/Audio.h b/packages/audio/include/audio/Audio.h new file mode 100644 index 00000000..e6710b39 --- /dev/null +++ b/packages/audio/include/audio/Audio.h @@ -0,0 +1,9 @@ +#pragma once + +namespace l::audio { + + float GetFrequencyFromNote(float note); + + + +} diff --git a/packages/audio/source/common/Audio.cpp b/packages/audio/source/common/Audio.cpp new file mode 100644 index 00000000..ec4d2077 --- /dev/null +++ b/packages/audio/source/common/Audio.cpp @@ -0,0 +1,16 @@ +#include "audio/Audio.h" + +#define MA_NO_DECODING +#define MA_NO_ENCODING +#define MINIAUDIO_IMPLEMENTATION + +#include "various/miniaudio.h" + + +namespace l::audio { + float GetFrequencyFromNote(float note) { + return 440.0f * powf(2.0f, (note - 49.0f) / 12.0f); + } + + +} diff --git a/packages/audio/tests/common/AudioTest.cpp b/packages/audio/tests/common/AudioTest.cpp new file mode 100644 index 00000000..6d728e9e --- /dev/null +++ b/packages/audio/tests/common/AudioTest.cpp @@ -0,0 +1,7 @@ +#include "testing/Test.h" + +int main(int, char* argw[]) { + TEST_RUN(argw[0]); + + return 0; +} diff --git a/packages/audio/tests/common/MiniAudioTest.cpp b/packages/audio/tests/common/MiniAudioTest.cpp new file mode 100644 index 00000000..d11b82d3 --- /dev/null +++ b/packages/audio/tests/common/MiniAudioTest.cpp @@ -0,0 +1,9 @@ +#include "testing/Test.h" + +#include "audio/Audio.h" + +TEST(MiniAudio, Setup) { + + + return 0; +} diff --git a/packages/nodegraph/CMakeLists.txt b/packages/nodegraph/CMakeLists.txt index 9d1ec5f7..9a4179b8 100644 --- a/packages/nodegraph/CMakeLists.txt +++ b/packages/nodegraph/CMakeLists.txt @@ -1,9 +1,14 @@ project (nodegraph) set(deps + various + logging + testing + memory hid + audio ) bs_generate_package(nodegraph "tier1" "${deps}" "") diff --git a/packages/nodegraph/include/nodegraph/NodeGraphOperations.h b/packages/nodegraph/include/nodegraph/NodeGraphOperations.h index a74ebf70..79c256f1 100644 --- a/packages/nodegraph/include/nodegraph/NodeGraphOperations.h +++ b/packages/nodegraph/include/nodegraph/NodeGraphOperations.h @@ -77,12 +77,6 @@ namespace l::nodegraph { float mPrevTime = 0.0f; }; - namespace { - float GetFrequencyFromNote(float note) { - return 440.0f * powf(2.0f, (note - 49.0f) / 12.0f); - } - } - class GraphSourceKeyboard : public NodeGraphOp, public l::hid::INoteProcessor { public: GraphSourceKeyboard(NodeGraphBase* node, int32_t polyphony, l::hid::KeyState* keyState) : diff --git a/packages/nodegraph/source/common/NodeGraphOperations.cpp b/packages/nodegraph/source/common/NodeGraphOperations.cpp index 24510bb0..1c454405 100644 --- a/packages/nodegraph/source/common/NodeGraphOperations.cpp +++ b/packages/nodegraph/source/common/NodeGraphOperations.cpp @@ -1,6 +1,7 @@ #include "nodegraph/NodeGraphOperations.h" #include "logging/Log.h" +#include "audio/Audio.h" #include @@ -129,7 +130,7 @@ namespace l::nodegraph { } void GraphSourceKeyboard::NoteOn(int32_t note) { - float frequency = GetFrequencyFromNote(static_cast(note)); + float frequency = l::audio::GetFrequencyFromNote(static_cast(note)); int8_t channel = GetNextNoteChannel(note); mNode->SetInput(static_cast(channel), frequency); mNode->ProcessSubGraph(); From 84ed168e9203a64d0f4483b78f15a1f8f30eb891 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Sun, 1 Sep 2024 05:49:39 +0200 Subject: [PATCH 089/125] Ignore warnings. --- packages/audio/source/common/Audio.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/audio/source/common/Audio.cpp b/packages/audio/source/common/Audio.cpp index ec4d2077..cac9a685 100644 --- a/packages/audio/source/common/Audio.cpp +++ b/packages/audio/source/common/Audio.cpp @@ -1,5 +1,7 @@ #include "audio/Audio.h" +__pragma(warning(push, 0)) + #define MA_NO_DECODING #define MA_NO_ENCODING #define MINIAUDIO_IMPLEMENTATION @@ -14,3 +16,5 @@ namespace l::audio { } + +__pragma(warning(pop)) From 04fb2ddd5ced31762c77b59f712b6799e164a71f Mon Sep 17 00:00:00 2001 From: lnd3 Date: Sun, 1 Sep 2024 06:21:32 +0200 Subject: [PATCH 090/125] Small audio test. --- packages/audio/include/audio/Audio.h | 2 + packages/audio/source/common/Audio.cpp | 54 +++++++++++++++++++ packages/audio/tests/common/MiniAudioTest.cpp | 13 +++++ 3 files changed, 69 insertions(+) diff --git a/packages/audio/include/audio/Audio.h b/packages/audio/include/audio/Audio.h index e6710b39..9e797c35 100644 --- a/packages/audio/include/audio/Audio.h +++ b/packages/audio/include/audio/Audio.h @@ -4,6 +4,8 @@ namespace l::audio { float GetFrequencyFromNote(float note); + bool Init(); + void Deinit(); } diff --git a/packages/audio/source/common/Audio.cpp b/packages/audio/source/common/Audio.cpp index cac9a685..76ef50d3 100644 --- a/packages/audio/source/common/Audio.cpp +++ b/packages/audio/source/common/Audio.cpp @@ -15,6 +15,60 @@ namespace l::audio { } +#define DEVICE_FORMAT ma_format_f32 +#define DEVICE_CHANNELS 2 +#define DEVICE_SAMPLE_RATE 48000 + + ma_device_config deviceConfig; + ma_device device; + + void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) + { + ma_waveform* pSineWave; + + MA_ASSERT(pDevice->playback.channels == DEVICE_CHANNELS); + + pSineWave = (ma_waveform*)pDevice->pUserData; + MA_ASSERT(pSineWave != NULL); + + ma_waveform_read_pcm_frames(pSineWave, pOutput, frameCount, NULL); + + (void)pInput; /* Unused. */ + } + + + bool Init() { + ma_waveform sineWave; + ma_waveform_config sineWaveConfig; + + deviceConfig = ma_device_config_init(ma_device_type_playback); + deviceConfig.playback.format = DEVICE_FORMAT; + deviceConfig.playback.channels = DEVICE_CHANNELS; + deviceConfig.sampleRate = DEVICE_SAMPLE_RATE; + deviceConfig.dataCallback = data_callback; + deviceConfig.pUserData = &sineWave; + + if (ma_device_init(NULL, &deviceConfig, &device) != MA_SUCCESS) { + printf("Failed to open playback device.\n"); + return false; + } + printf("Device Name: %s\n", device.playback.name); + + sineWaveConfig = ma_waveform_config_init(device.playback.format, device.playback.channels, device.sampleRate, ma_waveform_type_sine, 0.1, 13000); + ma_waveform_init(&sineWaveConfig, &sineWave); + + if (ma_device_start(&device) != MA_SUCCESS) { + printf("Failed to start playback device.\n"); + ma_device_uninit(&device); + return false; + } + return true; + } + + void Deinit() { + ma_device_uninit(&device); + } } + __pragma(warning(pop)) diff --git a/packages/audio/tests/common/MiniAudioTest.cpp b/packages/audio/tests/common/MiniAudioTest.cpp index d11b82d3..08d16108 100644 --- a/packages/audio/tests/common/MiniAudioTest.cpp +++ b/packages/audio/tests/common/MiniAudioTest.cpp @@ -2,8 +2,21 @@ #include "audio/Audio.h" +#include + + TEST(MiniAudio, Setup) { + if (!l::audio::Init()) { + return 1; + } + + + + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + + + l::audio::Deinit(); return 0; } From 199314ae8590546a3527886698683987a3f51238 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Sun, 1 Sep 2024 06:37:45 +0200 Subject: [PATCH 091/125] Start using external libraries specifically to avoid multiple linking definition errors. --- packages/audio/CMakeLists.txt | 4 +--- packages/network/CMakeLists.txt | 5 +---- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/packages/audio/CMakeLists.txt b/packages/audio/CMakeLists.txt index ba1a552f..47d63355 100644 --- a/packages/audio/CMakeLists.txt +++ b/packages/audio/CMakeLists.txt @@ -1,11 +1,9 @@ project (audio) set(deps - various - logging testing memory ) -bs_generate_package(audio "tier1" "${deps}" "") +bs_generate_package(audio "tier1" "${deps}" "various") diff --git a/packages/network/CMakeLists.txt b/packages/network/CMakeLists.txt index 96038204..40fb8e9f 100644 --- a/packages/network/CMakeLists.txt +++ b/packages/network/CMakeLists.txt @@ -1,13 +1,10 @@ project (network) set(deps - libcurl - mongoose - logging testing filesystem concurrency ) -bs_generate_package(network "tier2" "${deps}" "") +bs_generate_package(network "tier2" "${deps}" "libcurl;mongoose") From c1cba7a5ca7091be1cd4d0c01b0ebe045258234d Mon Sep 17 00:00:00 2001 From: lnd3 Date: Sun, 1 Sep 2024 06:44:59 +0200 Subject: [PATCH 092/125] Remove msvc pragmas. --- packages/audio/source/common/Audio.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/audio/source/common/Audio.cpp b/packages/audio/source/common/Audio.cpp index 76ef50d3..77b7d0e7 100644 --- a/packages/audio/source/common/Audio.cpp +++ b/packages/audio/source/common/Audio.cpp @@ -1,7 +1,5 @@ #include "audio/Audio.h" -__pragma(warning(push, 0)) - #define MA_NO_DECODING #define MA_NO_ENCODING #define MINIAUDIO_IMPLEMENTATION @@ -70,5 +68,3 @@ namespace l::audio { } } - -__pragma(warning(pop)) From 777ebd4adb97b8584af2af6bf61fb12ef24586ba Mon Sep 17 00:00:00 2001 From: lnd3 Date: Sun, 1 Sep 2024 14:53:49 +0200 Subject: [PATCH 093/125] Update ldeps. --- deps/ldeps | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/ldeps b/deps/ldeps index 97fbdeab..27874814 160000 --- a/deps/ldeps +++ b/deps/ldeps @@ -1 +1 @@ -Subproject commit 97fbdeaba768cc14ca4cb68633e0a99baba01d42 +Subproject commit 2787481461ca81206e87222e1677b3d132d41bb0 From 0024982dc5ad9c6e93808bb567d18cc8db854ab0 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Sun, 1 Sep 2024 15:16:27 +0200 Subject: [PATCH 094/125] Update deps. --- deps/ldeps | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/ldeps b/deps/ldeps index 27874814..a6a746b9 160000 --- a/deps/ldeps +++ b/deps/ldeps @@ -1 +1 @@ -Subproject commit 2787481461ca81206e87222e1677b3d132d41bb0 +Subproject commit a6a746b99d082f751710aa5958775760a427eaae From c5262574a8bbc178881a5a867fae675360946a38 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Sun, 1 Sep 2024 15:22:18 +0200 Subject: [PATCH 095/125] Ifdef miniaudio code for now. --- packages/audio/source/common/Audio.cpp | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/packages/audio/source/common/Audio.cpp b/packages/audio/source/common/Audio.cpp index 77b7d0e7..efa8219b 100644 --- a/packages/audio/source/common/Audio.cpp +++ b/packages/audio/source/common/Audio.cpp @@ -1,5 +1,8 @@ #include "audio/Audio.h" +#include + +#ifdef USE_AUDIO_MA #define MA_NO_DECODING #define MA_NO_ENCODING #define MINIAUDIO_IMPLEMENTATION @@ -68,3 +71,24 @@ namespace l::audio { } } +#else + + +namespace l::audio { + float GetFrequencyFromNote(float note) { + return 440.0f * powf(2.0f, (note - 49.0f) / 12.0f); + } + + bool Init() { + + + return true; + } + + void Deinit() { + + } + +} +#endif + From 2afef6556e40872b7bdf323ac70997a2b520ef74 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Sun, 1 Sep 2024 15:26:39 +0200 Subject: [PATCH 096/125] Update ldeps. --- deps/ldeps | 2 +- packages/audio/tests/common/MiniAudioTest.cpp | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/deps/ldeps b/deps/ldeps index a6a746b9..aa074a1f 160000 --- a/deps/ldeps +++ b/deps/ldeps @@ -1 +1 @@ -Subproject commit a6a746b99d082f751710aa5958775760a427eaae +Subproject commit aa074a1fc5e58e380c2eee116f19a19340107902 diff --git a/packages/audio/tests/common/MiniAudioTest.cpp b/packages/audio/tests/common/MiniAudioTest.cpp index 08d16108..d6b17699 100644 --- a/packages/audio/tests/common/MiniAudioTest.cpp +++ b/packages/audio/tests/common/MiniAudioTest.cpp @@ -11,8 +11,6 @@ TEST(MiniAudio, Setup) { return 1; } - - std::this_thread::sleep_for(std::chrono::milliseconds(100)); From da9f8f7d78086d294c665ea5505b99c6df2d1b3e Mon Sep 17 00:00:00 2001 From: lnd3 Date: Mon, 2 Sep 2024 00:41:09 +0200 Subject: [PATCH 097/125] Add audio package with port audio integration. --- packages/audio/CMakeLists.txt | 2 +- .../include/audio/{Audio.h => AudioUtils.h} | 6 - packages/audio/include/audio/PortAudio.h | 100 +++++++++ packages/audio/source/common/Audio.cpp | 94 -------- packages/audio/source/common/AudioUtils.cpp | 12 ++ packages/audio/source/common/PortAudio.cpp | 203 ++++++++++++++++++ packages/audio/tests/common/MiniAudioTest.cpp | 67 +++++- .../source/common/NodeGraphOperations.cpp | 2 +- 8 files changed, 376 insertions(+), 110 deletions(-) rename packages/audio/include/audio/{Audio.h => AudioUtils.h} (66%) create mode 100644 packages/audio/include/audio/PortAudio.h delete mode 100644 packages/audio/source/common/Audio.cpp create mode 100644 packages/audio/source/common/AudioUtils.cpp create mode 100644 packages/audio/source/common/PortAudio.cpp diff --git a/packages/audio/CMakeLists.txt b/packages/audio/CMakeLists.txt index 47d63355..245cfab8 100644 --- a/packages/audio/CMakeLists.txt +++ b/packages/audio/CMakeLists.txt @@ -6,4 +6,4 @@ set(deps memory ) -bs_generate_package(audio "tier1" "${deps}" "various") +bs_generate_package(audio "tier1" "${deps}" "PortAudio") diff --git a/packages/audio/include/audio/Audio.h b/packages/audio/include/audio/AudioUtils.h similarity index 66% rename from packages/audio/include/audio/Audio.h rename to packages/audio/include/audio/AudioUtils.h index 9e797c35..6660d636 100644 --- a/packages/audio/include/audio/Audio.h +++ b/packages/audio/include/audio/AudioUtils.h @@ -1,11 +1,5 @@ #pragma once namespace l::audio { - float GetFrequencyFromNote(float note); - - bool Init(); - void Deinit(); - - } diff --git a/packages/audio/include/audio/PortAudio.h b/packages/audio/include/audio/PortAudio.h new file mode 100644 index 00000000..d60d388c --- /dev/null +++ b/packages/audio/include/audio/PortAudio.h @@ -0,0 +1,100 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "../../include/portaudio.h" + +namespace l::audio { + + struct AudioStreamData { + double mDacOutputTimeAtLastCallback = 0.0; + int32_t mSampleRate = 0; + int32_t mNumBufferParts = 0; + int32_t mTotalNumFrames = 0; + int32_t mTotalBufferSize = 0; + int32_t mFramePosition = 0; + int32_t mDacFramesPerBufferPart = 0; + int32_t mNumChannels = 0; + float* mBuffer = nullptr; + std::binary_semaphore mDacWriteReady{ 0 }; + + float* GetCurrentBufferPosition() { + auto framePos = mFramePosition; + return mBuffer + framePos * mNumChannels; + } + + void NextPart() { + // go to the part just before the one being written + auto framesAhead = mDacFramesPerBufferPart * (mNumBufferParts - 1); + mFramePosition = (mFramePosition + framesAhead) % mTotalNumFrames; + } + + void NextPartCanBeWritten() { + mDacWriteReady.release(); + } + + int32_t GetPartTotalSize() { + return mDacFramesPerBufferPart * mNumChannels; + } + + int32_t GetNumFramesPerPart() { + return mDacFramesPerBufferPart; + } + + int32_t GetSampleRate() { + return mSampleRate; + } + }; + + typedef void PaStream; + + enum class ChannelMode { + MONO, + STEREO + }; + + enum class BufferingMode { + SINGLE_BUFFERING = 0, + DOUBLE_BUFFERING, + TRIPLE_BUFFERING + }; + + class AudioStream { + public: + AudioStream() = default; + ~AudioStream() = default; + + bool OpenStream(int32_t dacBufferFrames, float latency = 0.0f, BufferingMode mode = BufferingMode::DOUBLE_BUFFERING, ChannelMode channel = ChannelMode::STEREO); + bool StartStream(); + bool CanWrite(); + void Write(std::vector& out); + bool StopStream(); + int32_t GetPartTotalSize(); + int32_t GetNumFramesPerPart(); + int32_t GetSampleRate(); + protected: + PaStreamParameters mOutputParameters; + PaStream* mPaStream; + + AudioStreamData mAudioStreamData; + std::vector mBufferInterleaved; + }; + + class AudioManager { + public: + AudioManager() = default; + ~AudioManager(); + + bool Init(); + AudioStream* GetStream(std::string_view name); + void CloseStream(std::string_view name); + protected: + std::unordered_map> mStreams; + }; + +} diff --git a/packages/audio/source/common/Audio.cpp b/packages/audio/source/common/Audio.cpp deleted file mode 100644 index efa8219b..00000000 --- a/packages/audio/source/common/Audio.cpp +++ /dev/null @@ -1,94 +0,0 @@ -#include "audio/Audio.h" - -#include - -#ifdef USE_AUDIO_MA -#define MA_NO_DECODING -#define MA_NO_ENCODING -#define MINIAUDIO_IMPLEMENTATION - -#include "various/miniaudio.h" - - -namespace l::audio { - float GetFrequencyFromNote(float note) { - return 440.0f * powf(2.0f, (note - 49.0f) / 12.0f); - } - - -#define DEVICE_FORMAT ma_format_f32 -#define DEVICE_CHANNELS 2 -#define DEVICE_SAMPLE_RATE 48000 - - ma_device_config deviceConfig; - ma_device device; - - void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) - { - ma_waveform* pSineWave; - - MA_ASSERT(pDevice->playback.channels == DEVICE_CHANNELS); - - pSineWave = (ma_waveform*)pDevice->pUserData; - MA_ASSERT(pSineWave != NULL); - - ma_waveform_read_pcm_frames(pSineWave, pOutput, frameCount, NULL); - - (void)pInput; /* Unused. */ - } - - - bool Init() { - ma_waveform sineWave; - ma_waveform_config sineWaveConfig; - - deviceConfig = ma_device_config_init(ma_device_type_playback); - deviceConfig.playback.format = DEVICE_FORMAT; - deviceConfig.playback.channels = DEVICE_CHANNELS; - deviceConfig.sampleRate = DEVICE_SAMPLE_RATE; - deviceConfig.dataCallback = data_callback; - deviceConfig.pUserData = &sineWave; - - if (ma_device_init(NULL, &deviceConfig, &device) != MA_SUCCESS) { - printf("Failed to open playback device.\n"); - return false; - } - printf("Device Name: %s\n", device.playback.name); - - sineWaveConfig = ma_waveform_config_init(device.playback.format, device.playback.channels, device.sampleRate, ma_waveform_type_sine, 0.1, 13000); - ma_waveform_init(&sineWaveConfig, &sineWave); - - if (ma_device_start(&device) != MA_SUCCESS) { - printf("Failed to start playback device.\n"); - ma_device_uninit(&device); - return false; - } - return true; - } - - void Deinit() { - ma_device_uninit(&device); - } -} - -#else - - -namespace l::audio { - float GetFrequencyFromNote(float note) { - return 440.0f * powf(2.0f, (note - 49.0f) / 12.0f); - } - - bool Init() { - - - return true; - } - - void Deinit() { - - } - -} -#endif - diff --git a/packages/audio/source/common/AudioUtils.cpp b/packages/audio/source/common/AudioUtils.cpp new file mode 100644 index 00000000..a2c82fc9 --- /dev/null +++ b/packages/audio/source/common/AudioUtils.cpp @@ -0,0 +1,12 @@ +#include "audio/AudioUtils.h" + +#include "logging/LoggingAll.h" + +#include + +namespace l::audio { + + float GetFrequencyFromNote(float note) { + return 440.0f * powf(2.0f, (note - 49.0f) / 12.0f); + } +} diff --git a/packages/audio/source/common/PortAudio.cpp b/packages/audio/source/common/PortAudio.cpp new file mode 100644 index 00000000..aa3ac87d --- /dev/null +++ b/packages/audio/source/common/PortAudio.cpp @@ -0,0 +1,203 @@ +#include "audio/PortAudio.h" + +#include "logging/LoggingAll.h" + +#include +#include +#include "portaudio.h" + + +namespace l::audio { + +#define SAMPLE_RATE (44100) +#define PA_SAMPLE_TYPE paFloat32 + + static int gNumNoInputs = 0; + static int audioCallback(const void*, void* outputBuffer, + unsigned long framesPerBuffer, + const PaStreamCallbackTimeInfo* timeInfo, + PaStreamCallbackFlags statusFlags, + void* userData) + { + float* out = (float*)outputBuffer; + unsigned int i; + (void)statusFlags; + + AudioStreamData* audioStreamData = nullptr; + + if (userData == nullptr) { + for (i = 0; i < framesPerBuffer; i++) { + *out++ = 0; /* left - silent */ + *out++ = 0; /* right - silent */ + } + gNumNoInputs += 1; + } + else { + //paUserData = reinterpret_cast(userData); + audioStreamData = reinterpret_cast(userData); + if (audioStreamData != nullptr) { + audioStreamData->mDacOutputTimeAtLastCallback = timeInfo->outputBufferDacTime; + ASSERT(audioStreamData->mDacFramesPerBufferPart == static_cast(framesPerBuffer)); + + float* buffer = audioStreamData->GetCurrentBufferPosition(); + + audioStreamData->NextPart(); + audioStreamData->NextPartCanBeWritten(); + + for (i = 0; i < framesPerBuffer; i++) { + *out++ = *buffer++; + *out++ = *buffer++; + } + } + } + + return paContinue; + } + + bool AudioStream::OpenStream(int32_t dacFramesPerBufferPart, float latencyMs, BufferingMode mode, ChannelMode channel) { + + mOutputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */ + if (mOutputParameters.device == paNoDevice) { + LOG(LogError) << "Error: No default output device."; + return false; + } + + switch (channel) { + case ChannelMode::MONO: + mOutputParameters.channelCount = 1; + break; + case ChannelMode::STEREO: + default: + mOutputParameters.channelCount = 2; + break; + } + + mOutputParameters.sampleFormat = PA_SAMPLE_TYPE; + PaTime defaultLatency = Pa_GetDeviceInfo(mOutputParameters.device)->defaultLowOutputLatency; + ASSERT(latencyMs >= 0.0f && latencyMs < 1000.0f) << "Latency is allowed between 0ms to 1000ms"; + if (latencyMs > 0.0f) { + mOutputParameters.suggestedLatency = latencyMs / 1000.0f; + LOG(LogInfo) << "Port Audio set latency ms: " << static_cast(latencyMs); + } + else { + LOG(LogInfo) << "Port Audio recommended latency ms: " << static_cast(defaultLatency * 1000.0f); + mOutputParameters.suggestedLatency = defaultLatency; + } + mOutputParameters.hostApiSpecificStreamInfo = NULL; + + switch (mode) { + case BufferingMode::SINGLE_BUFFERING: + mAudioStreamData.mNumBufferParts = 1; + break; + default: + case BufferingMode::DOUBLE_BUFFERING: + mAudioStreamData.mNumBufferParts = 2; + break; + case BufferingMode::TRIPLE_BUFFERING: + mAudioStreamData.mNumBufferParts = 3; + break; + } + + mAudioStreamData.mSampleRate = SAMPLE_RATE; + mAudioStreamData.mDacFramesPerBufferPart = dacFramesPerBufferPart; + int32_t totalNumFrames = mAudioStreamData.mNumBufferParts * mAudioStreamData.mDacFramesPerBufferPart; + mAudioStreamData.mTotalNumFrames = totalNumFrames; + mAudioStreamData.mTotalBufferSize = totalNumFrames * mOutputParameters.channelCount; + mAudioStreamData.mNumChannels = static_cast(mOutputParameters.channelCount); + + mBufferInterleaved.resize(mAudioStreamData.mTotalBufferSize); + mAudioStreamData.mBuffer = mBufferInterleaved.data(); + + auto err = Pa_OpenStream( + &mPaStream, + NULL, + &mOutputParameters, + SAMPLE_RATE, + mAudioStreamData.mDacFramesPerBufferPart, + 0, /* paClipOff, */ + audioCallback, + &mAudioStreamData); + + if (err != paNoError) { + LOG(LogError) << "Failed to open stream: " << err; + return false; + } + return true; + } + + + bool AudioStream::StartStream() { + auto err = Pa_StartStream(mPaStream); + if (err != paNoError) { + LOG(LogError) << "Failed to start stream: " << err; + return false; + } + return true; + } + + bool AudioStream::CanWrite() { + return mAudioStreamData.mDacWriteReady.try_acquire(); + } + + void AudioStream::Write(std::vector& out) { + float* buffer = mAudioStreamData.GetCurrentBufferPosition(); + + float* outPtr = out.data(); + for (int i = 0; i < mAudioStreamData.mDacFramesPerBufferPart; i++) { + *buffer++ = *outPtr++; + *buffer++ = *outPtr++; + } + } + + bool AudioStream::StopStream() { + if (mPaStream == nullptr) { + return false; + } + auto err = Pa_CloseStream(mPaStream); + if (err != paNoError) { + LOG(LogError) << "Failed to close stream: " << err; + return false; + } + return true; + } + + int32_t AudioStream::GetPartTotalSize() { + return mAudioStreamData.GetPartTotalSize(); + } + + int32_t AudioStream::GetNumFramesPerPart() { + return mAudioStreamData.GetNumFramesPerPart(); + } + + int32_t AudioStream::GetSampleRate() { + return mAudioStreamData.GetSampleRate(); + } + + AudioManager::~AudioManager() { + Pa_Terminate(); + } + + bool AudioManager::Init() { + auto err = Pa_Initialize(); + if (err != paNoError) { + LOG(LogError) << "Failed to initialize port audio."; + return false; + } + return true; + } + + AudioStream* AudioManager::GetStream(std::string_view name) { + auto h = std::hash{}(name); + if (!mStreams.contains(h)) { + mStreams.insert_or_assign(h, std::make_unique()); + } + return mStreams.at(h).get(); + } + + void AudioManager::CloseStream(std::string_view name) { + auto h = std::hash{}(name); + mStreams.erase(h); + } + +} + diff --git a/packages/audio/tests/common/MiniAudioTest.cpp b/packages/audio/tests/common/MiniAudioTest.cpp index d6b17699..78aadce9 100644 --- a/packages/audio/tests/common/MiniAudioTest.cpp +++ b/packages/audio/tests/common/MiniAudioTest.cpp @@ -1,20 +1,71 @@ #include "testing/Test.h" -#include "audio/Audio.h" +#include "audio/PortAudio.h" #include -TEST(MiniAudio, Setup) { +TEST(PortAudio, Setup0) { - if (!l::audio::Init()) { - return 1; - } - std::this_thread::sleep_for(std::chrono::milliseconds(100)); + l::audio::AudioManager manager; + if (!manager.Init()) { + return 1; + } + auto stream = manager.GetStream("speaker"); - l::audio::Deinit(); + if (!stream->OpenStream(2048, 20.0f)) { + return 1; + } + if (!stream->StartStream()) { + return 1; + } - return 0; + std::vector buffer; + float sinePhase = 0.0f; + float freq = 300.0f; + float fMod = 1.0f; + float pMod = 0.0f; + float mPhase = 0.0f; + + float pModPhase = 0.0f; + + buffer.resize(stream->GetPartTotalSize()); + + float deltaTime = 1.0f / static_cast(stream->GetSampleRate()); + int32_t loops = 10; + do { + if (stream->CanWrite()) { + for (size_t i = 0; i < stream->GetNumFramesPerPart(); i++) { + + pModPhase += deltaTime * freq * (13.0f/12.0f); + pModPhase -= floorf(pModPhase); + + fMod = 0.3f * sinf(2.0f * 3.141529f * pModPhase); + + mPhase += deltaTime * freq * (fMod + 1.0f); + mPhase -= floorf(mPhase); + + float phaseMod = mPhase + pMod; + phaseMod -= floorf(phaseMod); + + buffer.at(2 * i + 0) = 0.5f * sinf(3.141529f * (mPhase + phaseMod)); + buffer.at(2 * i + 1) = 0.5f * sinf(3.141529f * (mPhase + phaseMod)); + } + stream->Write(buffer); + } + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } while (loops-- > 0); + + + if (!stream->StopStream()) { + LOG(LogError) << "Failed to stop stream"; + } + + manager.CloseStream("speaker"); + + return 0; } + + diff --git a/packages/nodegraph/source/common/NodeGraphOperations.cpp b/packages/nodegraph/source/common/NodeGraphOperations.cpp index 1c454405..c7a25e1c 100644 --- a/packages/nodegraph/source/common/NodeGraphOperations.cpp +++ b/packages/nodegraph/source/common/NodeGraphOperations.cpp @@ -1,7 +1,7 @@ #include "nodegraph/NodeGraphOperations.h" #include "logging/Log.h" -#include "audio/Audio.h" +#include "audio/AudioUtils.h" #include From f76067e69f8f0bf4b2b8d11e8bfb5b9b61a2c982 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Mon, 2 Sep 2024 00:47:53 +0200 Subject: [PATCH 098/125] Rename. --- .../common/{MiniAudioTest.cpp => PortAudioTest.cpp} | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) rename packages/audio/tests/common/{MiniAudioTest.cpp => PortAudioTest.cpp} (95%) diff --git a/packages/audio/tests/common/MiniAudioTest.cpp b/packages/audio/tests/common/PortAudioTest.cpp similarity index 95% rename from packages/audio/tests/common/MiniAudioTest.cpp rename to packages/audio/tests/common/PortAudioTest.cpp index 78aadce9..654ed2d0 100644 --- a/packages/audio/tests/common/MiniAudioTest.cpp +++ b/packages/audio/tests/common/PortAudioTest.cpp @@ -5,21 +5,20 @@ #include -TEST(PortAudio, Setup0) { - +TEST(PortAudio, Setup) { l::audio::AudioManager manager; if (!manager.Init()) { - return 1; + return 0; } auto stream = manager.GetStream("speaker"); if (!stream->OpenStream(2048, 20.0f)) { - return 1; + return 0; } if (!stream->StartStream()) { - return 1; + return 0; } std::vector buffer; From 4f64cfb1fcdbb78bc2159843df1ae7584632f934 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Mon, 2 Sep 2024 00:50:56 +0200 Subject: [PATCH 099/125] Update ldeps. --- deps/ldeps | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/ldeps b/deps/ldeps index aa074a1f..283712aa 160000 --- a/deps/ldeps +++ b/deps/ldeps @@ -1 +1 @@ -Subproject commit aa074a1fc5e58e380c2eee116f19a19340107902 +Subproject commit 283712aaabd7c7ccc333961b84fe0fbd08436969 From 55aa94804e674eaede080c2d3fd33ccf50eabee5 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Mon, 2 Sep 2024 03:58:39 +0200 Subject: [PATCH 100/125] Add port audio to relevant packages since both audio, node graph and rendering now depend on it. --- packages/audio/include/audio/PortAudio.h | 6 +- packages/audio/source/common/PortAudio.cpp | 18 +++-- packages/audio/tests/common/PortAudioTest.cpp | 5 +- packages/nodegraph/CMakeLists.txt | 2 +- .../nodegraph/include/nodegraph/NodeGraph.h | 23 ++++--- .../include/nodegraph/NodeGraphOperations.h | 61 ++++++++++++++--- .../include/nodegraph/NodeGraphSchema.h | 10 ++- .../nodegraph/source/common/NodeGraph.cpp | 24 ++++--- .../source/common/NodeGraphOperations.cpp | 66 ++++++++++--------- .../source/common/NodeGraphSchema.cpp | 46 ++++++++----- packages/rendering/CMakeLists.txt | 14 ++-- 11 files changed, 181 insertions(+), 94 deletions(-) diff --git a/packages/audio/include/audio/PortAudio.h b/packages/audio/include/audio/PortAudio.h index d60d388c..092e0246 100644 --- a/packages/audio/include/audio/PortAudio.h +++ b/packages/audio/include/audio/PortAudio.h @@ -71,8 +71,9 @@ namespace l::audio { bool OpenStream(int32_t dacBufferFrames, float latency = 0.0f, BufferingMode mode = BufferingMode::DOUBLE_BUFFERING, ChannelMode channel = ChannelMode::STEREO); bool StartStream(); + std::vector& GetWriteBuffer(); bool CanWrite(); - void Write(std::vector& out); + void Write(); bool StopStream(); int32_t GetPartTotalSize(); int32_t GetNumFramesPerPart(); @@ -82,7 +83,8 @@ namespace l::audio { PaStream* mPaStream; AudioStreamData mAudioStreamData; - std::vector mBufferInterleaved; + std::vector mOutputBufferInterleaved; + std::vector mWriteBuffer; }; class AudioManager { diff --git a/packages/audio/source/common/PortAudio.cpp b/packages/audio/source/common/PortAudio.cpp index aa3ac87d..878281b4 100644 --- a/packages/audio/source/common/PortAudio.cpp +++ b/packages/audio/source/common/PortAudio.cpp @@ -105,8 +105,8 @@ namespace l::audio { mAudioStreamData.mTotalBufferSize = totalNumFrames * mOutputParameters.channelCount; mAudioStreamData.mNumChannels = static_cast(mOutputParameters.channelCount); - mBufferInterleaved.resize(mAudioStreamData.mTotalBufferSize); - mAudioStreamData.mBuffer = mBufferInterleaved.data(); + mOutputBufferInterleaved.resize(mAudioStreamData.mTotalBufferSize); + mAudioStreamData.mBuffer = mOutputBufferInterleaved.data(); auto err = Pa_OpenStream( &mPaStream, @@ -132,17 +132,27 @@ namespace l::audio { LOG(LogError) << "Failed to start stream: " << err; return false; } + + auto bufferSize = GetPartTotalSize(); + if (mWriteBuffer.size() != bufferSize) { + mWriteBuffer.resize(bufferSize); + } + return true; } + std::vector& AudioStream::GetWriteBuffer() { + return mWriteBuffer; + } + bool AudioStream::CanWrite() { return mAudioStreamData.mDacWriteReady.try_acquire(); } - void AudioStream::Write(std::vector& out) { + void AudioStream::Write() { float* buffer = mAudioStreamData.GetCurrentBufferPosition(); - float* outPtr = out.data(); + float* outPtr = mWriteBuffer.data(); for (int i = 0; i < mAudioStreamData.mDacFramesPerBufferPart; i++) { *buffer++ = *outPtr++; *buffer++ = *outPtr++; diff --git a/packages/audio/tests/common/PortAudioTest.cpp b/packages/audio/tests/common/PortAudioTest.cpp index 654ed2d0..4e60287f 100644 --- a/packages/audio/tests/common/PortAudioTest.cpp +++ b/packages/audio/tests/common/PortAudioTest.cpp @@ -21,7 +21,6 @@ TEST(PortAudio, Setup) { return 0; } - std::vector buffer; float sinePhase = 0.0f; float freq = 300.0f; float fMod = 1.0f; @@ -30,7 +29,7 @@ TEST(PortAudio, Setup) { float pModPhase = 0.0f; - buffer.resize(stream->GetPartTotalSize()); + auto& buffer = stream->GetWriteBuffer(); float deltaTime = 1.0f / static_cast(stream->GetSampleRate()); int32_t loops = 10; @@ -52,7 +51,7 @@ TEST(PortAudio, Setup) { buffer.at(2 * i + 0) = 0.5f * sinf(3.141529f * (mPhase + phaseMod)); buffer.at(2 * i + 1) = 0.5f * sinf(3.141529f * (mPhase + phaseMod)); } - stream->Write(buffer); + stream->Write(); } std::this_thread::sleep_for(std::chrono::milliseconds(10)); } while (loops-- > 0); diff --git a/packages/nodegraph/CMakeLists.txt b/packages/nodegraph/CMakeLists.txt index 9a4179b8..e93d5698 100644 --- a/packages/nodegraph/CMakeLists.txt +++ b/packages/nodegraph/CMakeLists.txt @@ -11,4 +11,4 @@ set(deps audio ) -bs_generate_package(nodegraph "tier1" "${deps}" "") +bs_generate_package(nodegraph "tier1" "${deps}" "PortAudio") diff --git a/packages/nodegraph/include/nodegraph/NodeGraph.h b/packages/nodegraph/include/nodegraph/NodeGraph.h index 459a959e..5fb0f009 100644 --- a/packages/nodegraph/include/nodegraph/NodeGraph.h +++ b/packages/nodegraph/include/nodegraph/NodeGraph.h @@ -71,7 +71,7 @@ namespace l::nodegraph { void ClearProcessFlags(); virtual void ProcessSubGraph(bool recomputeSubGraphCache = true); - virtual void Tick(float time); + virtual void Tick(float time, float elapsed); virtual void SetNumInputs(int8_t numInputs); virtual void SetNumOutputs(int8_t outputCount); @@ -129,8 +129,8 @@ namespace l::nodegraph { virtual ~NodeGraphOp() = default; virtual void Reset() {} - virtual void ProcessSubGraph(std::vector& mInputs, std::vector& outputs) = 0; - virtual void Tick(float) {} + virtual void ProcessSubGraph(std::vector&, std::vector&) {}; + virtual void Tick(float, float) {} virtual void SetNumInputs(int8_t numInputs); virtual void SetNumOutputs(int8_t numOutputs); @@ -211,9 +211,9 @@ namespace l::nodegraph { mOperation.ProcessSubGraph(mInputs, mOutputs); } - virtual void Tick(float time) override { - NodeGraphBase::Tick(time); - mOperation.Tick(time); + virtual void Tick(float time, float elapsed) override { + NodeGraphBase::Tick(time, elapsed); + mOperation.Tick(time, elapsed); } virtual std::string_view GetInputName(int8_t inputChannel) { @@ -273,19 +273,24 @@ namespace l::nodegraph { bool RemoveNode(int32_t id); template>, class... Params> - l::nodegraph::NodeGraphBase* NewNode(Params&&... params) { + l::nodegraph::NodeGraphBase* NewNode(bool outputNode, Params&&... params) { mNodes.push_back(std::make_unique>(std::forward(params)...)); - return mNodes.back().get(); + auto nodePtr = mNodes.back().get(); + if (outputNode) { + mOutputNodes.push_back(nodePtr); + } + return nodePtr; } void ClearProcessFlags(); void ProcessSubGraph(bool recomputeSubGraphCache = true); - void Tick(float time); + void Tick(float time, float elapsed); protected: NodeGraph mInputNode; NodeGraph mOutputNode; std::vector> mNodes; + std::vector mOutputNodes; }; } diff --git a/packages/nodegraph/include/nodegraph/NodeGraphOperations.h b/packages/nodegraph/include/nodegraph/NodeGraphOperations.h index 79c256f1..5a2fc424 100644 --- a/packages/nodegraph/include/nodegraph/NodeGraphOperations.h +++ b/packages/nodegraph/include/nodegraph/NodeGraphOperations.h @@ -3,6 +3,7 @@ #include "logging/LoggingAll.h" #include "hid/KeyboardPiano.h" +#include "audio/PortAudio.h" #include #include @@ -42,8 +43,8 @@ namespace l::nodegraph { } virtual ~GraphSourceConstants() = default; - void ProcessSubGraph(std::vector& inputs, std::vector& outputs) override; - virtual void Tick(float) override; + virtual void ProcessSubGraph(std::vector& inputs, std::vector& outputs) override; + virtual void Tick(float, float) override; std::string_view GetName() override; bool IsDataVisible(int8_t) override; @@ -55,6 +56,27 @@ namespace l::nodegraph { float mMin = 0.0f; }; + class GraphSourceTime : public NodeGraphOp { + public: + GraphSourceTime(NodeGraphBase* node) : + NodeGraphOp(node, 0, 2, 0) + {} + + std::string defaultOutStrings[2] = { "Audio Time", "Frame Time"}; + + virtual ~GraphSourceTime() = default; + virtual void ProcessSubGraph(std::vector& inputs, std::vector& outputs) override; + virtual void Tick(float, float) override; + + void Reset() override; + std::string_view GetOutputName(int8_t outputChannel); + std::string_view GetName() override; + + protected: + float mAudioTime = 0.0f; + float mFrameTime = 0.0f; + }; + class GraphSourceSine : public NodeGraphOp { public: GraphSourceSine(NodeGraphBase* node) : @@ -91,7 +113,7 @@ namespace l::nodegraph { virtual ~GraphSourceKeyboard() = default; void ProcessSubGraph(std::vector& inputs, std::vector& outputs) override; - void Tick(float time) override; + void Tick(float time, float elapsed) override; void Reset() override; virtual std::string_view GetOutputName(int8_t outputChannel) override; virtual std::string_view GetName() override; @@ -247,9 +269,9 @@ namespace l::nodegraph { float mState1 = 0.0f; }; - class GraphGraphicDisplay : public NodeGraphOp { + class GraphOutputDebug : public NodeGraphOp { public: - GraphGraphicDisplay(NodeGraphBase* node, int32_t numValueDisplays) : + GraphOutputDebug(NodeGraphBase* node, int32_t numValueDisplays) : NodeGraphOp(node, numValueDisplays, 0, 0) {} @@ -257,13 +279,36 @@ namespace l::nodegraph { return true; } - virtual ~GraphGraphicDisplay() = default; + virtual ~GraphOutputDebug() = default; + + std::string_view GetName() override { + return "Debug"; + } + }; + + class GraphOutputSpeaker : public NodeGraphOp { + public: + GraphOutputSpeaker(NodeGraphBase* node, l::audio::AudioStream* stream = nullptr) : + NodeGraphOp(node, 2, 0, 0), + mAudioStream(stream), + mCurrentStereoPosition(0) + {} + + bool IsDataVisible(int8_t) override { + return true; + } + + virtual ~GraphOutputSpeaker() = default; void ProcessSubGraph(std::vector& inputs, std::vector& outputs) override; - virtual void Tick(float time) override; + virtual void Tick(float time, float elapsed) override; std::string_view GetName() override { - return "Display"; + return "Speaker"; } + + protected: + l::audio::AudioStream* mAudioStream; + int32_t mCurrentStereoPosition; }; } diff --git a/packages/nodegraph/include/nodegraph/NodeGraphSchema.h b/packages/nodegraph/include/nodegraph/NodeGraphSchema.h index 119f4f3e..39aed0cf 100644 --- a/packages/nodegraph/include/nodegraph/NodeGraphSchema.h +++ b/packages/nodegraph/include/nodegraph/NodeGraphSchema.h @@ -37,7 +37,8 @@ namespace l::nodegraph { RegisterNodeType("Source", 2, "Value [0,100]"); RegisterNodeType("Source", 3, "Value [-inf,inf]"); RegisterNodeType("Source", 4, "Keyboard"); - RegisterNodeType("Source", 5, "Sine"); + RegisterNodeType("Source", 5, "Time"); + RegisterNodeType("Source", 6, "Sine"); RegisterNodeType("Numeric", 50, "Add"); RegisterNodeType("Numeric", 51, "Subtract"); RegisterNodeType("Numeric", 52, "Negate"); @@ -47,13 +48,15 @@ namespace l::nodegraph { RegisterNodeType("Logic", 101, "Or"); RegisterNodeType("Logic", 102, "Xor"); RegisterNodeType("Filter", 150, "Lowpass Filter"); - RegisterNodeType("Graphic", 200, "Display"); + RegisterNodeType("Output", 200, "Value Debug"); + RegisterNodeType("Output", 201, "Speaker"); } ~NodeGraphSchema() = default; void SetCustomCreator(std::function customCreator); void SetKeyState(l::hid::KeyState* keyState); + void SetAudioOutput(l::audio::AudioStream* audioStream); int32_t NewNode(int32_t typeId); bool RemoveNode(int32_t nodeId); @@ -67,12 +70,13 @@ namespace l::nodegraph { void ForEachNodeType(std::function&)> cb) const; void RegisterNodeType(const std::string& typeGroup, int32_t uniqueTypeId, std::string_view typeName); void ProcessSubGraph(); - void Tick(float time); + void Tick(float time, float elapsed); protected: NodeGraphGroup mMainNodeGraph; std::function mCreateCustomNode; l::hid::KeyState* mKeyState; + l::audio::AudioStream* mAudioOutput; std::map> mRegisteredNodeTypes; }; diff --git a/packages/nodegraph/source/common/NodeGraph.cpp b/packages/nodegraph/source/common/NodeGraph.cpp index d9e5fe97..dcc04959 100644 --- a/packages/nodegraph/source/common/NodeGraph.cpp +++ b/packages/nodegraph/source/common/NodeGraph.cpp @@ -77,10 +77,10 @@ namespace l::nodegraph { mProcessUpdateHasRun = true; } - void NodeGraphBase::Tick(float time) { + void NodeGraphBase::Tick(float time, float elapsed) { for (auto& link : mInputs) { if (link.mInputType == InputType::INPUT_NODE && link.mInput.mInputNode != nullptr) { - link.mInput.mInputNode->Tick(time); + link.mInput.mInputNode->Tick(time, elapsed); } } } @@ -371,6 +371,12 @@ namespace l::nodegraph { sourceCount++; } } + std::erase_if(mOutputNodes, [&](NodeGraphBase* nodePtr) { + if (nodePtr == node) { + return true; + } + return false; + }); auto count = std::erase_if(mNodes, [&](const std::unique_ptr& node) { if (node->GetId() == id) { return true; @@ -384,16 +390,18 @@ namespace l::nodegraph { mOutputNode.ClearProcessFlags(); } - void NodeGraphGroup::ProcessSubGraph(bool recomputeSubGraphCache) { - if (recomputeSubGraphCache) { - mOutputNode.ClearProcessFlags(); + void NodeGraphGroup::ProcessSubGraph(bool) { + for (auto& it : mOutputNodes) { + it->ClearProcessFlags(); + } + for (auto& it : mOutputNodes) { + it->ProcessSubGraph(false); } - mOutputNode.ProcessSubGraph(false); } - void NodeGraphGroup::Tick(float time) { + void NodeGraphGroup::Tick(float time, float elapsed) { for (auto& it : mNodes) { - it->Tick(time); + it->Tick(time, elapsed); } } } \ No newline at end of file diff --git a/packages/nodegraph/source/common/NodeGraphOperations.cpp b/packages/nodegraph/source/common/NodeGraphOperations.cpp index c7a25e1c..aefc489e 100644 --- a/packages/nodegraph/source/common/NodeGraphOperations.cpp +++ b/packages/nodegraph/source/common/NodeGraphOperations.cpp @@ -10,8 +10,6 @@ namespace l::nodegraph { /* Mathematical operations */ void GraphSourceConstants::ProcessSubGraph(std::vector& inputs, std::vector& outputs) { - ASSERT(inputs.size() == static_cast(mNumOutputs)); - ASSERT(outputs.size() == static_cast(mNumOutputs)); for (int8_t i = 0; i < mNumOutputs; i++) { float val = inputs.at(i).Get(); val = val > mMax ? mMax : val < mMin ? mMin : val; @@ -20,7 +18,7 @@ namespace l::nodegraph { } } - void GraphSourceConstants::Tick(float) { + void GraphSourceConstants::Tick(float, float) { mNode->ProcessSubGraph(); } @@ -46,10 +44,33 @@ namespace l::nodegraph { return true; } - void GraphSourceSine::ProcessSubGraph(std::vector& inputs, std::vector& outputs) { - ASSERT(inputs.size() == static_cast(mNumInputs)); - ASSERT(outputs.size() == static_cast(mNumOutputs)); + void GraphSourceTime::ProcessSubGraph(std::vector&, std::vector& outputs) { + float rate = 44100.0f; + float phaseChange = 1.0f / rate; + mAudioTime += phaseChange; + + outputs.at(0).mOutput = mAudioTime; + outputs.at(1).mOutput = mFrameTime; + } + + void GraphSourceTime::Tick(float time, float) { + mFrameTime = time; + } + + void GraphSourceTime::Reset() { + mAudioTime = 0.0f; + mFrameTime = 0.0f; + } + std::string_view GraphSourceTime::GetOutputName(int8_t outputChannel) { + return defaultOutStrings[outputChannel]; + } + + std::string_view GraphSourceTime::GetName() { + return "Time"; + } + + void GraphSourceSine::ProcessSubGraph(std::vector& inputs, std::vector& outputs) { float time = inputs.at(0).Get(); float freq = inputs.at(1).Get(); float fMod = 1.0f + inputs.at(2).Get(); @@ -99,14 +120,12 @@ namespace l::nodegraph { } void GraphSourceKeyboard::ProcessSubGraph(std::vector& inputs, std::vector& outputs) { - ASSERT(inputs.size() == static_cast(mNumConstants)); - ASSERT(outputs.size() == static_cast(mNumConstants)); for (size_t i = 0; i < inputs.size();i++) { outputs.at(i).mOutput = inputs.at(i).Get(); } } - void GraphSourceKeyboard::Tick(float) { + void GraphSourceKeyboard::Tick(float, float) { mKeyboard.Update(); } @@ -189,26 +208,18 @@ namespace l::nodegraph { void GraphNumericAdd::ProcessSubGraph(std::vector& inputs, std::vector& outputs) { - ASSERT(inputs.size() == static_cast(mNumInputs)); - ASSERT(outputs.size() == static_cast(mNumOutputs)); outputs.at(0).mOutput = inputs.at(0).Get() + inputs.at(1).Get(); } void GraphNumericMultiply::ProcessSubGraph(std::vector& inputs, std::vector& outputs) { - ASSERT(inputs.size() == static_cast(mNumInputs)); - ASSERT(outputs.size() == static_cast(mNumOutputs)); outputs.at(0).mOutput = inputs.at(0).Get() * inputs.at(1).Get(); } void GraphNumericSubtract::ProcessSubGraph(std::vector& inputs, std::vector& outputs) { - ASSERT(inputs.size() == static_cast(mNumInputs)); - ASSERT(outputs.size() == static_cast(mNumOutputs)); outputs.at(0).mOutput = inputs.at(0).Get() - inputs.at(1).Get(); } void GraphNumericNegate::ProcessSubGraph(std::vector& inputs, std::vector& outputs) { - ASSERT(inputs.size() == static_cast(mNumInputs)); - ASSERT(outputs.size() == static_cast(mNumOutputs)); outputs.at(0).mOutput = -inputs.at(0).Get(); } @@ -217,8 +228,6 @@ namespace l::nodegraph { } void GraphNumericIntegral::ProcessSubGraph(std::vector& inputs, std::vector& outputs) { - ASSERT(inputs.size() == static_cast(mNumInputs)); - ASSERT(outputs.size() == static_cast(mNumOutputs)); mOutput += inputs.at(0).Get(); outputs.at(0).mOutput = mOutput; } @@ -226,24 +235,18 @@ namespace l::nodegraph { /* Logical operations */ void GraphLogicalAnd::ProcessSubGraph(std::vector& inputs, std::vector& outputs) { - ASSERT(inputs.size() == static_cast(mNumInputs)); - ASSERT(outputs.size() == static_cast(mNumOutputs)); bool input1 = inputs.at(0).Get() != 0.0f; bool input2 = inputs.at(1).Get() != 0.0f; outputs.at(0).mOutput = (input1 && input2) ? 1.0f : 0.0f; } void GraphLogicalOr::ProcessSubGraph(std::vector& inputs, std::vector& outputs) { - ASSERT(inputs.size() == static_cast(mNumInputs)); - ASSERT(outputs.size() == static_cast(mNumOutputs)); bool input1 = inputs.at(0).Get() != 0.0f; bool input2 = inputs.at(1).Get() != 0.0f; outputs.at(0).mOutput = (input1 || input2) ? 1.0f : 0.0f; } void GraphLogicalXor::ProcessSubGraph(std::vector& inputs, std::vector& outputs) { - ASSERT(inputs.size() == static_cast(mNumInputs)); - ASSERT(outputs.size() == static_cast(mNumOutputs)); bool input1 = inputs.at(0).Get() != 0.0f; bool input2 = inputs.at(1).Get() != 0.0f; outputs.at(0).mOutput = (input1 ^ input2) ? 1.0f : 0.0f; @@ -257,8 +260,6 @@ namespace l::nodegraph { } void GraphFilterLowpass::ProcessSubGraph(std::vector& inputs, std::vector& outputs) { - ASSERT(inputs.size() == static_cast(mNumInputs)); - ASSERT(outputs.size() == static_cast(mNumOutputs)); float cutoff = inputs.at(0).Get(); float resonance = inputs.at(1).Get(); float inputValue = inputs.at(2).Get(); @@ -272,12 +273,13 @@ namespace l::nodegraph { outputs.at(0).mOutput = -mState1; } - void GraphGraphicDisplay::ProcessSubGraph(std::vector& inputs, std::vector& outputs) { - ASSERT(inputs.size() == static_cast(mNumInputs)); - ASSERT(outputs.size() == static_cast(mNumOutputs)); + void GraphOutputSpeaker::ProcessSubGraph(std::vector& inputs, std::vector&) { + auto& buffer = mAudioStream->GetWriteBuffer(); + buffer[mCurrentStereoPosition++] = inputs.at(0).Get(); + buffer[mCurrentStereoPosition++] = inputs.at(1).Get(); + mCurrentStereoPosition %= mAudioStream->GetPartTotalSize(); } - void GraphGraphicDisplay::Tick(float) { - mNode->ProcessSubGraph(); + void GraphOutputSpeaker::Tick(float, float) { } } diff --git a/packages/nodegraph/source/common/NodeGraphSchema.cpp b/packages/nodegraph/source/common/NodeGraphSchema.cpp index 7bd47e21..6bbf09e0 100644 --- a/packages/nodegraph/source/common/NodeGraphSchema.cpp +++ b/packages/nodegraph/source/common/NodeGraphSchema.cpp @@ -14,56 +14,66 @@ namespace l::nodegraph { mKeyState = keyState; } + void NodeGraphSchema::SetAudioOutput(l::audio::AudioStream* audioOutput) { + mAudioOutput = audioOutput; + } + int32_t NodeGraphSchema::NewNode(int32_t typeId) { l::nodegraph::NodeGraphBase* node = nullptr; switch (typeId) { case 0: - node = mMainNodeGraph.NewNode(0); + node = mMainNodeGraph.NewNode(false, 0); break; case 1: - node = mMainNodeGraph.NewNode(1); + node = mMainNodeGraph.NewNode(false, 1); break; case 2: - node = mMainNodeGraph.NewNode(2); + node = mMainNodeGraph.NewNode(false, 2); break; case 3: - node = mMainNodeGraph.NewNode(3); + node = mMainNodeGraph.NewNode(false, 3); break; case 4: - node = mMainNodeGraph.NewNode(4, mKeyState); + node = mMainNodeGraph.NewNode(false, 4, mKeyState); break; case 5: - node = mMainNodeGraph.NewNode(); + node = mMainNodeGraph.NewNode(false); + break; + case 6: + node = mMainNodeGraph.NewNode(false); break; case 50: - node = mMainNodeGraph.NewNode(); + node = mMainNodeGraph.NewNode(false); break; case 51: - node = mMainNodeGraph.NewNode(); + node = mMainNodeGraph.NewNode(false); break; case 52: - node = mMainNodeGraph.NewNode(); + node = mMainNodeGraph.NewNode(false); break; case 53: - node = mMainNodeGraph.NewNode(); + node = mMainNodeGraph.NewNode(false); break; case 54: - node = mMainNodeGraph.NewNode(); + node = mMainNodeGraph.NewNode(false); break; case 100: - node = mMainNodeGraph.NewNode(); + node = mMainNodeGraph.NewNode(false); break; case 101: - node = mMainNodeGraph.NewNode(); + node = mMainNodeGraph.NewNode(false); break; case 102: - node = mMainNodeGraph.NewNode(); + node = mMainNodeGraph.NewNode(false); break; case 150: - node = mMainNodeGraph.NewNode(); + node = mMainNodeGraph.NewNode(false); break; case 200: - node = mMainNodeGraph.NewNode(4); + node = mMainNodeGraph.NewNode(true, 4); + break; + case 201: + node = mMainNodeGraph.NewNode(true, mAudioOutput); break; default: ASSERT(typeId < 10000) << "Custom node id's begin at id 1000"; @@ -96,8 +106,8 @@ namespace l::nodegraph { mRegisteredNodeTypes[typeGroup].push_back(UINodeDesc{ uniqueTypeId, std::string(typeName) }); } - void NodeGraphSchema::Tick(float time) { - mMainNodeGraph.Tick(time); + void NodeGraphSchema::Tick(float time, float deltaTime) { + mMainNodeGraph.Tick(time, deltaTime); } void NodeGraphSchema::ProcessSubGraph() { diff --git a/packages/rendering/CMakeLists.txt b/packages/rendering/CMakeLists.txt index 90225bd1..40dde48b 100644 --- a/packages/rendering/CMakeLists.txt +++ b/packages/rendering/CMakeLists.txt @@ -9,6 +9,7 @@ set(deps testing memory + audio nodegraph hid @@ -16,28 +17,29 @@ set(deps tools physics +) +set(deps_external various pugixml openfbx ufbx bc7enc + PortAudio ) -set(deps_includes) - if(TARGET glfw) - list(APPEND deps glfw glad) + list(APPEND deps_external glfw glad) endif() if(TARGET imgui) - list(APPEND deps imgui implot) + list(APPEND deps_external imgui implot) endif() find_package(Vulkan) if(TARGET Vulkan::Vulkan) list(APPEND deps Vulkan::Vulkan) - list(APPEND deps_includes "${Vulkan_INCLUDE_DIRS}") + list(APPEND deps_external "${Vulkan_INCLUDE_DIRS}") endif() -bs_generate_package(rendering "tier3" "${deps}" "${deps_includes}") +bs_generate_package(rendering "tier3" "${deps}" "${deps_external}") From 3f8fd53ac88f320152e241f53b178be299075139 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Mon, 2 Sep 2024 05:41:32 +0200 Subject: [PATCH 101/125] Fixed some bugs. --- packages/audio/source/common/PortAudio.cpp | 2 +- packages/audio/tests/common/PortAudioTest.cpp | 2 +- .../nodegraph/include/nodegraph/NodeGraph.h | 7 ++++++- .../include/nodegraph/NodeGraphOperations.h | 1 - .../source/common/NodeGraphOperations.cpp | 20 +++++++------------ .../source/common/NodeGraphSchema.cpp | 4 ++-- 6 files changed, 17 insertions(+), 19 deletions(-) diff --git a/packages/audio/source/common/PortAudio.cpp b/packages/audio/source/common/PortAudio.cpp index 878281b4..efd7b46b 100644 --- a/packages/audio/source/common/PortAudio.cpp +++ b/packages/audio/source/common/PortAudio.cpp @@ -133,7 +133,7 @@ namespace l::audio { return false; } - auto bufferSize = GetPartTotalSize(); + auto bufferSize = static_cast(GetPartTotalSize()); if (mWriteBuffer.size() != bufferSize) { mWriteBuffer.resize(bufferSize); } diff --git a/packages/audio/tests/common/PortAudioTest.cpp b/packages/audio/tests/common/PortAudioTest.cpp index 4e60287f..1177964f 100644 --- a/packages/audio/tests/common/PortAudioTest.cpp +++ b/packages/audio/tests/common/PortAudioTest.cpp @@ -14,7 +14,7 @@ TEST(PortAudio, Setup) { auto stream = manager.GetStream("speaker"); - if (!stream->OpenStream(2048, 20.0f)) { + if (!stream->OpenStream(2048, 20.0f, l::audio::BufferingMode::TRIPLE_BUFFERING)) { return 0; } if (!stream->StartStream()) { diff --git a/packages/nodegraph/include/nodegraph/NodeGraph.h b/packages/nodegraph/include/nodegraph/NodeGraph.h index 5fb0f009..93e990e4 100644 --- a/packages/nodegraph/include/nodegraph/NodeGraph.h +++ b/packages/nodegraph/include/nodegraph/NodeGraph.h @@ -190,7 +190,6 @@ namespace l::nodegraph { for (int8_t i = mInputCount; i < mInputCount + mConstantCount; i++) { SetInput(i, 0.0f); } - ProcessSubGraph(); } virtual bool IsDataVisible(int8_t num) override { @@ -207,8 +206,14 @@ namespace l::nodegraph { } virtual void ProcessOperation() override { + if (mProcessUpdateHasRun) { + return; + } + NodeGraphBase::ProcessOperation(); mOperation.ProcessSubGraph(mInputs, mOutputs); + + mProcessUpdateHasRun = true; } virtual void Tick(float time, float elapsed) override { diff --git a/packages/nodegraph/include/nodegraph/NodeGraphOperations.h b/packages/nodegraph/include/nodegraph/NodeGraphOperations.h index 5a2fc424..a0ce48b4 100644 --- a/packages/nodegraph/include/nodegraph/NodeGraphOperations.h +++ b/packages/nodegraph/include/nodegraph/NodeGraphOperations.h @@ -96,7 +96,6 @@ namespace l::nodegraph { protected: float mPhase = 0.0f; - float mPrevTime = 0.0f; }; class GraphSourceKeyboard : public NodeGraphOp, public l::hid::INoteProcessor { diff --git a/packages/nodegraph/source/common/NodeGraphOperations.cpp b/packages/nodegraph/source/common/NodeGraphOperations.cpp index aefc489e..57cb6663 100644 --- a/packages/nodegraph/source/common/NodeGraphOperations.cpp +++ b/packages/nodegraph/source/common/NodeGraphOperations.cpp @@ -71,24 +71,22 @@ namespace l::nodegraph { } void GraphSourceSine::ProcessSubGraph(std::vector& inputs, std::vector& outputs) { - float time = inputs.at(0).Get(); + //float time = inputs.at(0).Get(); float freq = inputs.at(1).Get(); float fMod = 1.0f + inputs.at(2).Get(); float pMod = inputs.at(3).Get(); float reset = inputs.at(4).Get(); - if (reset > 0.5f) { + if (freq == 0.0f) { Reset(); + outputs.at(0).mOutput = 0.0f; + return; } - - float deltaTime = time - mPrevTime; - if (mPrevTime == 0.0f) { - mPrevTime = time; - deltaTime = time - mPrevTime; + if (reset > 0.5f) { + Reset(); } - mPrevTime = time; - + float deltaTime = 1.0f / 44100.0f; float phaseDelta = deltaTime * freq; mPhase += phaseDelta * fMod; @@ -104,7 +102,6 @@ namespace l::nodegraph { void GraphSourceSine::Reset() { mPhase = 0.0f; - mPrevTime = 0.0f; } std::string_view GraphSourceSine::GetInputName(int8_t inputChannel) { @@ -133,7 +130,6 @@ namespace l::nodegraph { for (int8_t i = 0; i < GetNumInputs(); i++) { mNode->SetInput(i, 0.0f); } - mNode->ProcessSubGraph(); } std::string_view GraphSourceKeyboard::GetOutputName(int8_t outputChannel) { @@ -152,7 +148,6 @@ namespace l::nodegraph { float frequency = l::audio::GetFrequencyFromNote(static_cast(note)); int8_t channel = GetNextNoteChannel(note); mNode->SetInput(static_cast(channel), frequency); - mNode->ProcessSubGraph(); } void GraphSourceKeyboard::NoteOff() { Reset(); @@ -162,7 +157,6 @@ namespace l::nodegraph { int8_t channel = ResetNoteChannel(note); if (channel >= 0) { mNode->SetInput(channel, 0.0f); - mNode->ProcessSubGraph(); } } diff --git a/packages/nodegraph/source/common/NodeGraphSchema.cpp b/packages/nodegraph/source/common/NodeGraphSchema.cpp index 6bbf09e0..77e05267 100644 --- a/packages/nodegraph/source/common/NodeGraphSchema.cpp +++ b/packages/nodegraph/source/common/NodeGraphSchema.cpp @@ -34,7 +34,7 @@ namespace l::nodegraph { node = mMainNodeGraph.NewNode(false, 3); break; case 4: - node = mMainNodeGraph.NewNode(false, 4, mKeyState); + node = mMainNodeGraph.NewNode(false, 1, mKeyState); break; case 5: node = mMainNodeGraph.NewNode(false); @@ -70,7 +70,7 @@ namespace l::nodegraph { node = mMainNodeGraph.NewNode(false); break; case 200: - node = mMainNodeGraph.NewNode(true, 4); + node = mMainNodeGraph.NewNode(true, 1); break; case 201: node = mMainNodeGraph.NewNode(true, mAudioOutput); From d60971a8cbaa281c10da8316c8e0c45150aca42e Mon Sep 17 00:00:00 2001 From: lnd3 Date: Mon, 2 Sep 2024 07:33:14 +0200 Subject: [PATCH 102/125] In node groups add the output node to the list of group content because it will fix the issue when the group has output nodes as well as when the group has nodes that use an external output for example speakers or debug print. --- packages/nodegraph/include/nodegraph/NodeGraph.h | 1 + packages/nodegraph/include/nodegraph/NodeGraphOperations.h | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/packages/nodegraph/include/nodegraph/NodeGraph.h b/packages/nodegraph/include/nodegraph/NodeGraph.h index 93e990e4..69fce110 100644 --- a/packages/nodegraph/include/nodegraph/NodeGraph.h +++ b/packages/nodegraph/include/nodegraph/NodeGraph.h @@ -250,6 +250,7 @@ namespace l::nodegraph { NodeGraphGroup() { SetNumInputs(1); SetNumOutputs(1); + mOutputNodes.push_back(&mOutputNode); } ~NodeGraphGroup() = default; diff --git a/packages/nodegraph/include/nodegraph/NodeGraphOperations.h b/packages/nodegraph/include/nodegraph/NodeGraphOperations.h index a0ce48b4..dd54696b 100644 --- a/packages/nodegraph/include/nodegraph/NodeGraphOperations.h +++ b/packages/nodegraph/include/nodegraph/NodeGraphOperations.h @@ -287,6 +287,8 @@ namespace l::nodegraph { class GraphOutputSpeaker : public NodeGraphOp { public: + std::string defaultOutStrings[2] = { "Left", "Right"}; + GraphOutputSpeaker(NodeGraphBase* node, l::audio::AudioStream* stream = nullptr) : NodeGraphOp(node, 2, 0, 0), mAudioStream(stream), @@ -301,6 +303,10 @@ namespace l::nodegraph { void ProcessSubGraph(std::vector& inputs, std::vector& outputs) override; virtual void Tick(float time, float elapsed) override; + std::string_view GetOutputName(int8_t outputChannel) { + return defaultOutStrings[outputChannel]; + } + std::string_view GetName() override { return "Speaker"; } From 0f1b619c3b3a82e667279ad77d5480fed6ad794d Mon Sep 17 00:00:00 2001 From: lnd3 Date: Mon, 2 Sep 2024 11:19:34 +0200 Subject: [PATCH 103/125] Add reverb. --- .../include/nodegraph/NodeGraphOperations.h | 78 +++++++++++++++++++ .../include/nodegraph/NodeGraphSchema.h | 1 + .../source/common/NodeGraphOperations.cpp | 63 +++++++++++++++ .../source/common/NodeGraphSchema.cpp | 3 + .../rendering/source/common/ui/UIVisitors.cpp | 2 +- 5 files changed, 146 insertions(+), 1 deletion(-) diff --git a/packages/nodegraph/include/nodegraph/NodeGraphOperations.h b/packages/nodegraph/include/nodegraph/NodeGraphOperations.h index dd54696b..acf76fec 100644 --- a/packages/nodegraph/include/nodegraph/NodeGraphOperations.h +++ b/packages/nodegraph/include/nodegraph/NodeGraphOperations.h @@ -315,5 +315,83 @@ namespace l::nodegraph { l::audio::AudioStream* mAudioStream; int32_t mCurrentStereoPosition; }; + + class GraphEffectReverb : public NodeGraphOp { + public: + std::string defaultInStrings[11] = { "In 1", "In 2", "Mix", "Attenuation", "Room Size", "Delay 1", "Feedback 1", "Delay 2", "Feedback 2", "Delay 3", "Feedback 3" }; + std::string defaultOutStrings[2] = { "Out 1", "Out 2" }; + + uint32_t GetFramesPerRoomSize(float roomSize) { + const float metersPerFrame = 334.0f / 44100.0f; // (m/s)/(frames/s) = m/frames; + const float metersToWallPerFrame = metersPerFrame / 2.0f; // half the distance to wall for the bounced distance + const float framesPerRoom = roomSize / metersToWallPerFrame; + return static_cast(framesPerRoom); + } + + const float maxRoomSizeInMeters = 334.0f; // 334 meters large is 2 seconds of reverbation + + GraphEffectReverb(NodeGraphBase* node) : + NodeGraphOp(node, 5, 2, 6) + { + //node->SetInput(2, 0.1f); // mix + //node->SetInput(3, 0.7f); // attenuation + //node->SetInput(4, 30.0f); // room size + + //node->SetInput(5, 0.2f); // delay 1 + //node->SetInput(6, 0.5f); // feedback 1 + + //node->SetInput(7, 2.3f); // delay 2 + //node->SetInput(8, 0.79f); // feedback 2 + + //node->SetInput(9, 0.5f); // delay 3 + //node->SetInput(10, 0.96f); // feedback 3 + + uint32_t bufferSize = GetFramesPerRoomSize(maxRoomSizeInMeters); + buf0.resize(bufferSize); + buf1.resize(bufferSize); + } + + virtual ~GraphEffectReverb() = default; + void ProcessSubGraph(std::vector& inputs, std::vector& outputs) override; + + virtual bool IsDataVisible(int8_t num) override { + return num >= 2 ? true : false; + } + virtual bool IsDataEditable(int8_t num) override { + return num >= 2 ? true : false; + } + + std::string_view GetInputName(int8_t inputChannel) { + return defaultInStrings[inputChannel]; + } + std::string_view GetOutputName(int8_t outputChannel) { + return defaultOutStrings[outputChannel]; + } + + std::string_view GetName() override { + return "Reverb"; + } + float max(float value, float max) { + return value > max ? value : max; + } + float min(float value, float min) { + return value < min ? value : min; + } + + protected: + std::vector buf0; + std::vector buf1; + uint32_t bufIndex = 0; + + float mix = 0.0f; + float fb = 0.0f; + float fb0 = 0.0f; + float fb1 = 0.0f; + float fb2 = 0.0f; + float d0 = 0.0f; + float d1 = 0.0f; + float d2 = 0.0f; + }; + } diff --git a/packages/nodegraph/include/nodegraph/NodeGraphSchema.h b/packages/nodegraph/include/nodegraph/NodeGraphSchema.h index 39aed0cf..1c167c46 100644 --- a/packages/nodegraph/include/nodegraph/NodeGraphSchema.h +++ b/packages/nodegraph/include/nodegraph/NodeGraphSchema.h @@ -50,6 +50,7 @@ namespace l::nodegraph { RegisterNodeType("Filter", 150, "Lowpass Filter"); RegisterNodeType("Output", 200, "Value Debug"); RegisterNodeType("Output", 201, "Speaker"); + RegisterNodeType("Effect", 250, "Reverb"); } ~NodeGraphSchema() = default; diff --git a/packages/nodegraph/source/common/NodeGraphOperations.cpp b/packages/nodegraph/source/common/NodeGraphOperations.cpp index 57cb6663..e11e0e0e 100644 --- a/packages/nodegraph/source/common/NodeGraphOperations.cpp +++ b/packages/nodegraph/source/common/NodeGraphOperations.cpp @@ -276,4 +276,67 @@ namespace l::nodegraph { void GraphOutputSpeaker::Tick(float, float) { } + + void GraphEffectReverb::ProcessSubGraph(std::vector& inputs, std::vector& outputs) { + + // 0 - in 1 + // 1 - in 2 + //node->SetInput(2, 0.1f); // mix + //node->SetInput(3, 0.3f); // attenuation + //node->SetInput(4, 30.0f); // room size + + //node->SetInput(5, 0.2f); // delay 1 + //node->SetInput(6, 0.5f); // feedback 1 + + //node->SetInput(7, 2.3f); // delay 2 + //node->SetInput(8, 0.79f); // feedback 2 + + //node->SetInput(9, 0.5f); // delay 3 + //node->SetInput(10, 0.96f); // feedback 3 + + float wet = inputs.at(2).Get(); + + fb = 0.33f * min(1.0f - inputs.at(3).Get() , 0.0f); + float roomSize = inputs.at(4).Get(); + if (roomSize > maxRoomSizeInMeters) { + roomSize = maxRoomSizeInMeters; + mNode->SetInput(3, maxRoomSizeInMeters); + } + else if (roomSize < 0.1f) { + roomSize = 0.1f; + mNode->SetInput(3, 0.1f); + } + uint32_t bufSizeLimit = GetFramesPerRoomSize(roomSize); + + // feedback and delay + d0 = inputs.at(5).Get(); + fb0 = 0.5f * 0.45f * max(inputs.at(6).Get(), 1); + d1 = inputs.at(7).Get(); + fb1 = 0.5f * 0.45f * max(inputs.at(8).Get(), 1); + d2 = inputs.at(9).Get(); + fb2 = 0.5f * 0.45f * max(inputs.at(10).Get(), 1); + + float dry = 1.0f - wet; + + uint32_t delay0 = (int(bufIndex + d0 * bufSizeLimit)) % bufSizeLimit; + uint32_t delay1 = (int(bufIndex + d1 * bufSizeLimit)) % bufSizeLimit; + uint32_t delay2 = (int(bufIndex + d2 * bufSizeLimit)) % bufSizeLimit; + + float in0 = inputs.at(0).Get(); + float in1 = inputs.at(1).Get(); + + float out0 = fb0 * buf0[delay0] + fb1 * buf1[delay1] + fb2 * buf0[delay2]; + outputs[0].mOutput = in0 * dry + out0 * wet; + buf0[bufIndex] = (fb)*buf1[bufIndex] + in0 - out0; + + float out1 = fb0 * buf1[delay0] + fb1 * buf0[delay1] + fb2 * buf1[delay2]; + outputs[1].mOutput = in1 * dry + out1 * wet; + buf1[bufIndex] = (fb)*buf0[bufIndex] + in1 - out1; + + bufIndex = (bufIndex + 1) % bufSizeLimit; + delay0 = (delay0 + 1) % bufSizeLimit; + delay1 = (delay1 + 1) % bufSizeLimit; + delay2 = (delay2 + 1) % bufSizeLimit; + } + } diff --git a/packages/nodegraph/source/common/NodeGraphSchema.cpp b/packages/nodegraph/source/common/NodeGraphSchema.cpp index 77e05267..0cfbf8ad 100644 --- a/packages/nodegraph/source/common/NodeGraphSchema.cpp +++ b/packages/nodegraph/source/common/NodeGraphSchema.cpp @@ -75,6 +75,9 @@ namespace l::nodegraph { case 201: node = mMainNodeGraph.NewNode(true, mAudioOutput); break; + case 250: + node = mMainNodeGraph.NewNode(false); + break; default: ASSERT(typeId < 10000) << "Custom node id's begin at id 1000"; ASSERT(mCreateCustomNode != nullptr) << "Custom nodes needs a handler to create them. It's missing."; diff --git a/packages/rendering/source/common/ui/UIVisitors.cpp b/packages/rendering/source/common/ui/UIVisitors.cpp index 51057f61..9958f149 100644 --- a/packages/rendering/source/common/ui/UIVisitors.cpp +++ b/packages/rendering/source/common/ui/UIVisitors.cpp @@ -341,7 +341,7 @@ namespace l::ui { if (mNGSchema) { auto node = mNGSchema->GetNode(container.GetNodeId()); float nodeValue = 0.0f; - if (node->GetNumOutputs() > 0) { + if (node->GetNumOutputs() > 0 && container.GetChannelId() < node->GetNumOutputs()) { nodeValue = node->Get(static_cast(container.GetChannelId())); } else { From b2dad74d7b349d1148b2fe61ddbb0cd0b0570b9f Mon Sep 17 00:00:00 2001 From: lnd3 Date: Wed, 4 Sep 2024 00:36:10 +0200 Subject: [PATCH 104/125] Fix a few issues with group connections. Add another reverb. Add a envelope that retains the note during release and can fade between notes. --- .../nodegraph/include/nodegraph/NodeGraph.h | 29 ++- .../include/nodegraph/NodeGraphOperations.h | 118 +++++---- .../include/nodegraph/NodeGraphSchema.h | 1 + .../nodegraph/source/common/NodeGraph.cpp | 80 +++--- .../source/common/NodeGraphOperations.cpp | 239 ++++++++++++++---- .../source/common/NodeGraphSchema.cpp | 41 +-- .../tests/common/NodeGraphSchemaTest.cpp | 35 ++- .../rendering/source/common/ui/UIVisitors.cpp | 9 +- 8 files changed, 374 insertions(+), 178 deletions(-) diff --git a/packages/nodegraph/include/nodegraph/NodeGraph.h b/packages/nodegraph/include/nodegraph/NodeGraph.h index 69fce110..486b567b 100644 --- a/packages/nodegraph/include/nodegraph/NodeGraph.h +++ b/packages/nodegraph/include/nodegraph/NodeGraph.h @@ -27,6 +27,13 @@ namespace l::nodegraph { //INPUT_ARRAY // TODO: is it possible to process batches for example for audio processing? }; + enum class InputBound { + INPUT_UNBOUNDED, + INPUT_0_TO_1, + INPUT_NEG_1_POS_1, + INPUT_0_100 + }; + bool IsValidInOutNum(int8_t inoutNum, size_t inoutSize); struct NodeGraphOutput { @@ -48,6 +55,8 @@ namespace l::nodegraph { struct NodeGraphInput { Input mInput; InputType mInputType = InputType::INPUT_EMPTY; + InputBound mInputBound = InputBound::INPUT_UNBOUNDED; + int8_t mInputFromOutputChannel = 0; std::string mName; @@ -93,7 +102,7 @@ namespace l::nodegraph { virtual bool ClearInput(int8_t inputChannel); virtual bool SetInput(int8_t inputChannel, NodeGraphBase& source, int8_t sourceOutputChannel); - virtual bool SetInput(int8_t inputChannel, NodeGraphGroup& source, int8_t sourceOutputChannel, bool nodeIsInsideGroup); + virtual bool SetInput(int8_t inputChannel, NodeGraphGroup& source, int8_t sourceOutputChannel); virtual bool SetInput(int8_t inputChannel, float constant); virtual bool SetInput(int8_t inputChannel, float* floatPtr); @@ -129,7 +138,7 @@ namespace l::nodegraph { virtual ~NodeGraphOp() = default; virtual void Reset() {} - virtual void ProcessSubGraph(std::vector&, std::vector&) {}; + virtual void Process(std::vector&, std::vector&) {}; virtual void Tick(float, float) {} virtual void SetNumInputs(int8_t numInputs); @@ -159,7 +168,7 @@ namespace l::nodegraph { {} virtual ~GraphDataCopy() = default; - void ProcessSubGraph(std::vector& inputs, std::vector& outputs) override; + void Process(std::vector& inputs, std::vector& outputs) override; }; template @@ -211,7 +220,7 @@ namespace l::nodegraph { } NodeGraphBase::ProcessOperation(); - mOperation.ProcessSubGraph(mInputs, mOutputs); + mOperation.Process(mInputs, mOutputs); mProcessUpdateHasRun = true; } @@ -245,6 +254,11 @@ namespace l::nodegraph { T mOperation; }; + enum class OutputType { + Default, // node will be processed if it is connected to the groups output by some route + ExternalOutput, // node does not have meaningful output for other nodes but should still be processed (ex speaker output only has input) + }; + class NodeGraphGroup { public: NodeGraphGroup() { @@ -257,7 +271,7 @@ namespace l::nodegraph { void SetNumInputs(int8_t numInputs); void SetNumOutputs(int8_t outputCount); void SetInput(int8_t inputChannel, NodeGraphBase& source, int8_t sourceOutputChannel); - void SetInput(int8_t inputChannel, NodeGraphGroup& source, int8_t sourceOutputChannel, bool useSourceInternalInput = false); + void SetInput(int8_t inputChannel, NodeGraphGroup& source, int8_t sourceOutputChannel); void SetInput(int8_t inputChannel, float constant); void SetInput(int8_t inputChannel, float* floatPtr); @@ -268,6 +282,7 @@ namespace l::nodegraph { NodeGraphBase& GetInputNode(); NodeGraphBase& GetOutputNode(); + bool ContainsNode(int32_t id); NodeGraphBase* GetNode(int32_t id); template>> @@ -279,10 +294,10 @@ namespace l::nodegraph { bool RemoveNode(int32_t id); template>, class... Params> - l::nodegraph::NodeGraphBase* NewNode(bool outputNode, Params&&... params) { + l::nodegraph::NodeGraphBase* NewNode(OutputType nodeType, Params&&... params) { mNodes.push_back(std::make_unique>(std::forward(params)...)); auto nodePtr = mNodes.back().get(); - if (outputNode) { + if (nodeType == OutputType::ExternalOutput) { mOutputNodes.push_back(nodePtr); } return nodePtr; diff --git a/packages/nodegraph/include/nodegraph/NodeGraphOperations.h b/packages/nodegraph/include/nodegraph/NodeGraphOperations.h index acf76fec..15400279 100644 --- a/packages/nodegraph/include/nodegraph/NodeGraphOperations.h +++ b/packages/nodegraph/include/nodegraph/NodeGraphOperations.h @@ -11,6 +11,7 @@ #include #include #include +#include namespace l::nodegraph { @@ -43,7 +44,7 @@ namespace l::nodegraph { } virtual ~GraphSourceConstants() = default; - virtual void ProcessSubGraph(std::vector& inputs, std::vector& outputs) override; + virtual void Process(std::vector& inputs, std::vector& outputs) override; virtual void Tick(float, float) override; std::string_view GetName() override; @@ -65,7 +66,7 @@ namespace l::nodegraph { std::string defaultOutStrings[2] = { "Audio Time", "Frame Time"}; virtual ~GraphSourceTime() = default; - virtual void ProcessSubGraph(std::vector& inputs, std::vector& outputs) override; + virtual void Process(std::vector& inputs, std::vector& outputs) override; virtual void Tick(float, float) override; void Reset() override; @@ -87,7 +88,7 @@ namespace l::nodegraph { std::string defaultOutStrings[3] = { "Sine", "Phase", "Phase Mod"}; virtual ~GraphSourceSine() = default; - void ProcessSubGraph(std::vector& inputs, std::vector& outputs) override; + void Process(std::vector& inputs, std::vector& outputs) override; void Reset() override; std::string_view GetInputName(int8_t inputChannel); @@ -111,7 +112,7 @@ namespace l::nodegraph { std::string defaultOutStrings[8] = { "Note 1", "Note 2", "Note 3", "Note 4", "Note 5", "Note 6", "Note 7", "Note 8" }; virtual ~GraphSourceKeyboard() = default; - void ProcessSubGraph(std::vector& inputs, std::vector& outputs) override; + void Process(std::vector& inputs, std::vector& outputs) override; void Tick(float time, float elapsed) override; void Reset() override; virtual std::string_view GetOutputName(int8_t outputChannel) override; @@ -135,7 +136,7 @@ namespace l::nodegraph { NodeGraphOp(node, 2, 1) {} virtual ~GraphNumericAdd() = default; - void ProcessSubGraph(std::vector& inputs, std::vector& outputs) override; + void Process(std::vector& inputs, std::vector& outputs) override; std::string_view GetName() override { return "Add"; } @@ -148,7 +149,7 @@ namespace l::nodegraph { {} virtual ~GraphNumericMultiply() = default; - void ProcessSubGraph(std::vector& inputs, std::vector& outputs) override; + void Process(std::vector& inputs, std::vector& outputs) override; std::string_view GetName() override { return "Multiply"; } @@ -160,7 +161,7 @@ namespace l::nodegraph { NodeGraphOp(node, 2, 1) {} virtual ~GraphNumericSubtract() = default; - void ProcessSubGraph(std::vector& inputs, std::vector& outputs) override; + void Process(std::vector& inputs, std::vector& outputs) override; std::string_view GetName() override { return "Subtract"; } @@ -173,7 +174,7 @@ namespace l::nodegraph { {} virtual ~GraphNumericNegate() = default; - void ProcessSubGraph(std::vector& inputs, std::vector& outputs) override; + void Process(std::vector& inputs, std::vector& outputs) override; std::string_view GetName() override { return "Negate"; } @@ -187,7 +188,7 @@ namespace l::nodegraph { virtual ~GraphNumericIntegral() = default; void Reset() override; - void ProcessSubGraph(std::vector& inputs, std::vector& outputs) override; + void Process(std::vector& inputs, std::vector& outputs) override; std::string_view GetName() override { return "Integral"; } @@ -205,7 +206,7 @@ namespace l::nodegraph { {} virtual ~GraphLogicalAnd() = default; - void ProcessSubGraph(std::vector& inputs, std::vector& outputs) override; + void Process(std::vector& inputs, std::vector& outputs) override; std::string_view GetName() override { return "And"; } @@ -218,7 +219,7 @@ namespace l::nodegraph { {} virtual ~GraphLogicalOr() = default; - void ProcessSubGraph(std::vector& inputs, std::vector& outputs) override; + void Process(std::vector& inputs, std::vector& outputs) override; std::string_view GetName() override { return "Or"; } @@ -231,7 +232,7 @@ namespace l::nodegraph { {} virtual ~GraphLogicalXor() = default; - void ProcessSubGraph(std::vector& inputs, std::vector& outputs) override; + void Process(std::vector& inputs, std::vector& outputs) override; std::string_view GetName() override { return "Xor"; } @@ -250,7 +251,7 @@ namespace l::nodegraph { virtual ~GraphFilterLowpass() = default; void Reset() override; - void ProcessSubGraph(std::vector& inputs, std::vector& outputs) override; + void Process(std::vector& inputs, std::vector& outputs) override; std::string_view GetInputName(int8_t inputChannel) { return defaultInStrings[inputChannel]; @@ -268,6 +269,44 @@ namespace l::nodegraph { float mState1 = 0.0f; }; + class GraphFilterEnvelope : public NodeGraphOp { + public: + std::string defaultInStrings[5] = { "Note", "Attack ms", "Release ms", "Note Fade"}; + std::string defaultOutStrings[2] = { "Note", "Gain"}; + + GraphFilterEnvelope(NodeGraphBase* node) : + NodeGraphOp(node, 4, 2) + {} + + virtual ~GraphFilterEnvelope() = default; + void Reset() override; + void Process(std::vector& inputs, std::vector& outputs) override; + + std::string_view GetInputName(int8_t inputChannel) { + return defaultInStrings[inputChannel]; + } + + std::string_view GetOutputName(int8_t outputChannel) { + return defaultOutStrings[outputChannel]; + } + + bool IsDataVisible(int8_t channel) { + return channel >= 1 ? true : false; + } + + bool IsDataEditable(int8_t channel) { + return channel >= 1 ? true : false; + } + + std::string_view GetName() override { + return "Envelope"; + } + protected: + uint32_t mFrameCount; + float mEnvelopeTarget = 0.0f; + float mNote = 0.0f; + float mEnvelope = 0.0f; + }; class GraphOutputDebug : public NodeGraphOp { public: GraphOutputDebug(NodeGraphBase* node, int32_t numValueDisplays) : @@ -300,7 +339,7 @@ namespace l::nodegraph { } virtual ~GraphOutputSpeaker() = default; - void ProcessSubGraph(std::vector& inputs, std::vector& outputs) override; + void Process(std::vector& inputs, std::vector& outputs) override; virtual void Tick(float time, float elapsed) override; std::string_view GetOutputName(int8_t outputChannel) { @@ -318,8 +357,8 @@ namespace l::nodegraph { class GraphEffectReverb : public NodeGraphOp { public: - std::string defaultInStrings[11] = { "In 1", "In 2", "Mix", "Attenuation", "Room Size", "Delay 1", "Feedback 1", "Delay 2", "Feedback 2", "Delay 3", "Feedback 3" }; - std::string defaultOutStrings[2] = { "Out 1", "Out 2" }; + std::string defaultInStrings[12] = { "In 1", "In 2", "Mix", "Feedback", "Room Size", "Width", "First tap", "Longest tap", "Num taps", "Tap bulge", "Filter cutoff", "Filter res"}; + std::string defaultOutStrings[4] = { "Rev 1", "Rev 2", "Tap 1", "Tap 2"}; uint32_t GetFramesPerRoomSize(float roomSize) { const float metersPerFrame = 334.0f / 44100.0f; // (m/s)/(frames/s) = m/frames; @@ -331,28 +370,18 @@ namespace l::nodegraph { const float maxRoomSizeInMeters = 334.0f; // 334 meters large is 2 seconds of reverbation GraphEffectReverb(NodeGraphBase* node) : - NodeGraphOp(node, 5, 2, 6) + NodeGraphOp(node, 12, 4, 0) { - //node->SetInput(2, 0.1f); // mix - //node->SetInput(3, 0.7f); // attenuation - //node->SetInput(4, 30.0f); // room size - - //node->SetInput(5, 0.2f); // delay 1 - //node->SetInput(6, 0.5f); // feedback 1 - - //node->SetInput(7, 2.3f); // delay 2 - //node->SetInput(8, 0.79f); // feedback 2 - - //node->SetInput(9, 0.5f); // delay 3 - //node->SetInput(10, 0.96f); // feedback 3 - uint32_t bufferSize = GetFramesPerRoomSize(maxRoomSizeInMeters); - buf0.resize(bufferSize); - buf1.resize(bufferSize); + bufRev0.resize(bufferSize); + bufRev1.resize(bufferSize); + bufEarlyTap0.resize(bufferSize); + bufEarlyTap1.resize(bufferSize); } virtual ~GraphEffectReverb() = default; - void ProcessSubGraph(std::vector& inputs, std::vector& outputs) override; + virtual void Process(std::vector& inputs, std::vector& outputs) override; + virtual void Tick(float time, float elapsed) override; virtual bool IsDataVisible(int8_t num) override { return num >= 2 ? true : false; @@ -379,18 +408,21 @@ namespace l::nodegraph { } protected: - std::vector buf0; - std::vector buf1; + bool mInited = false; + + std::vector bufRev0; + std::vector bufRev1; + std::vector bufEarlyTap0; + std::vector bufEarlyTap1; uint32_t bufIndex = 0; + uint32_t bufTapIndex = 0; + + std::minstd_rand mLCG; - float mix = 0.0f; - float fb = 0.0f; - float fb0 = 0.0f; - float fb1 = 0.0f; - float fb2 = 0.0f; - float d0 = 0.0f; - float d1 = 0.0f; - float d2 = 0.0f; + float mLP0 = 0.0f; + float mLP1 = 0.0f; + float mLP2 = 0.0f; + float mLP3 = 0.0f; }; } diff --git a/packages/nodegraph/include/nodegraph/NodeGraphSchema.h b/packages/nodegraph/include/nodegraph/NodeGraphSchema.h index 1c167c46..ae4420b7 100644 --- a/packages/nodegraph/include/nodegraph/NodeGraphSchema.h +++ b/packages/nodegraph/include/nodegraph/NodeGraphSchema.h @@ -48,6 +48,7 @@ namespace l::nodegraph { RegisterNodeType("Logic", 101, "Or"); RegisterNodeType("Logic", 102, "Xor"); RegisterNodeType("Filter", 150, "Lowpass Filter"); + RegisterNodeType("Filter", 151, "Envelope"); RegisterNodeType("Output", 200, "Value Debug"); RegisterNodeType("Output", 201, "Speaker"); RegisterNodeType("Effect", 250, "Reverb"); diff --git a/packages/nodegraph/source/common/NodeGraph.cpp b/packages/nodegraph/source/common/NodeGraph.cpp index dcc04959..c713fc46 100644 --- a/packages/nodegraph/source/common/NodeGraph.cpp +++ b/packages/nodegraph/source/common/NodeGraph.cpp @@ -106,39 +106,38 @@ namespace l::nodegraph { bool NodeGraphBase::SetInput(int8_t inputChannel, NodeGraphBase& source, int8_t sourceOutputChannel) { auto& input = mInputs.at(inputChannel); - if (!IsValidInOutNum(sourceOutputChannel, source.mOutputs.size()) || - !IsValidInOutNum(inputChannel, mInputs.size()) || + if (!IsValidInOutNum(sourceOutputChannel, source.mOutputs.size()) || + !IsValidInOutNum(inputChannel, mInputs.size()) || input.HasInput()) { return false; } - - Input newInput; - newInput.mInputNode = &source; - input = NodeGraphInput{ std::move(newInput), InputType::INPUT_NODE, sourceOutputChannel, ""}; + input.mInput.mInputNode = &source; + input.mInputType = InputType::INPUT_NODE; + input.mInputFromOutputChannel = sourceOutputChannel; return true; } - bool NodeGraphBase::SetInput(int8_t inputChannel, NodeGraphGroup& source, int8_t sourceChannel, bool nodeIsInsideGroup) { - Input newInput; - if (nodeIsInsideGroup) { - newInput.mInputNode = &source.GetInputNode(); - } - else { - newInput.mInputNode = &source.GetOutputNode(); - } - + bool NodeGraphBase::SetInput(int8_t inputChannel, NodeGraphGroup& source, int8_t sourceChannel) { auto& input = mInputs.at(inputChannel); - if (!IsValidInOutNum(sourceChannel, newInput.mInputNode->mOutputs.size()) || - !IsValidInOutNum(inputChannel, mInputs.size()) || - input.HasInput()){ - return false; - } - - if (nodeIsInsideGroup) { + if (source.ContainsNode(GetId())) { + if (!IsValidInOutNum(sourceChannel, source.GetInputNode().GetNumOutputs()) || + !IsValidInOutNum(inputChannel, mInputs.size()) || + input.HasInput()) { + return false; + } + input.mInput.mInputNode = &source.GetInputNode(); source.GetInputNode().SetInputName(sourceChannel, input.mName); } - - input = NodeGraphInput{ std::move(newInput), InputType::INPUT_NODE, sourceChannel, "" }; + else { + if (!IsValidInOutNum(sourceChannel, source.GetOutputNode().GetNumOutputs()) || + !IsValidInOutNum(inputChannel, mInputs.size()) || + input.HasInput()) { + return false; + } + input.mInput.mInputNode = &source.GetOutputNode(); + } + input.mInputType = InputType::INPUT_NODE; + input.mInputFromOutputChannel = sourceChannel; return true; } @@ -149,9 +148,9 @@ namespace l::nodegraph { return false; } - Input newInput; - newInput.mInputFloatConstant = constant; - input = NodeGraphInput{ std::move(newInput), InputType::INPUT_CONSTANT, 0, "" }; + input.mInput.mInputFloatConstant = constant; + input.mInputType = InputType::INPUT_CONSTANT; + input.mInputFromOutputChannel = 0; return true; } @@ -162,9 +161,9 @@ namespace l::nodegraph { return false; } - Input newInput; - newInput.mInputFloat = floatPtr; - input = NodeGraphInput{ std::move(newInput), InputType::INPUT_VALUE, 0, "" }; + input.mInput.mInputFloat = floatPtr; + input.mInputType = InputType::INPUT_VALUE; + input.mInputFromOutputChannel = 0; return true; } @@ -252,7 +251,7 @@ namespace l::nodegraph { return ""; } - void GraphDataCopy::ProcessSubGraph(std::vector& inputs, std::vector& outputs) { + void GraphDataCopy::Process(std::vector& inputs, std::vector& outputs) { for (size_t i = 0; i < inputs.size() && i < outputs.size(); i++) { outputs.at(i).mOutput = inputs.at(i).Get(); } @@ -316,8 +315,8 @@ namespace l::nodegraph { mInputNode.SetInput(inputChannel, source, sourceOutputChannel); } - void NodeGraphGroup::SetInput(int8_t inputChannel, NodeGraphGroup& source, int8_t sourceOutputChannel, bool useSourceInternalInput) { - mInputNode.SetInput(inputChannel, source, sourceOutputChannel, useSourceInternalInput); + void NodeGraphGroup::SetInput(int8_t inputChannel, NodeGraphGroup& source, int8_t sourceOutputChannel) { + mInputNode.SetInput(inputChannel, source, sourceOutputChannel); } void NodeGraphGroup::SetInput(int8_t inputChannel, float constant) { @@ -334,7 +333,7 @@ namespace l::nodegraph { } void NodeGraphGroup::SetOutput(int8_t outputChannel, NodeGraphGroup& source, int8_t sourceOutputChannel) { - mOutputNode.SetInput(outputChannel, source, sourceOutputChannel, false); + mOutputNode.SetInput(outputChannel, source, sourceOutputChannel); mOutputNode.SetOutputName(outputChannel, source.GetOutputNode().GetOutputName(sourceOutputChannel)); } @@ -350,6 +349,19 @@ namespace l::nodegraph { return mOutputNode; } + bool NodeGraphGroup::ContainsNode(int32_t id) { + auto it = std::find_if(mNodes.begin(), mNodes.end(), [&](const std::unique_ptr& node) { + if (node->GetId() == id) { + return true; + } + return false; + }); + if (it != mNodes.end()) { + return true; + } + return false; + } + NodeGraphBase* NodeGraphGroup::GetNode(int32_t id) { auto it = std::find_if(mNodes.begin(), mNodes.end(), [&](const std::unique_ptr& node) { if (node->GetId() == id) { diff --git a/packages/nodegraph/source/common/NodeGraphOperations.cpp b/packages/nodegraph/source/common/NodeGraphOperations.cpp index e11e0e0e..0af77ce7 100644 --- a/packages/nodegraph/source/common/NodeGraphOperations.cpp +++ b/packages/nodegraph/source/common/NodeGraphOperations.cpp @@ -9,7 +9,7 @@ namespace l::nodegraph { /* Mathematical operations */ - void GraphSourceConstants::ProcessSubGraph(std::vector& inputs, std::vector& outputs) { + void GraphSourceConstants::Process(std::vector& inputs, std::vector& outputs) { for (int8_t i = 0; i < mNumOutputs; i++) { float val = inputs.at(i).Get(); val = val > mMax ? mMax : val < mMin ? mMin : val; @@ -44,7 +44,7 @@ namespace l::nodegraph { return true; } - void GraphSourceTime::ProcessSubGraph(std::vector&, std::vector& outputs) { + void GraphSourceTime::Process(std::vector&, std::vector& outputs) { float rate = 44100.0f; float phaseChange = 1.0f / rate; mAudioTime += phaseChange; @@ -70,7 +70,7 @@ namespace l::nodegraph { return "Time"; } - void GraphSourceSine::ProcessSubGraph(std::vector& inputs, std::vector& outputs) { + void GraphSourceSine::Process(std::vector& inputs, std::vector& outputs) { //float time = inputs.at(0).Get(); float freq = inputs.at(1).Get(); float fMod = 1.0f + inputs.at(2).Get(); @@ -116,7 +116,7 @@ namespace l::nodegraph { return "Sine"; } - void GraphSourceKeyboard::ProcessSubGraph(std::vector& inputs, std::vector& outputs) { + void GraphSourceKeyboard::Process(std::vector& inputs, std::vector& outputs) { for (size_t i = 0; i < inputs.size();i++) { outputs.at(i).mOutput = inputs.at(i).Get(); } @@ -201,19 +201,19 @@ namespace l::nodegraph { } - void GraphNumericAdd::ProcessSubGraph(std::vector& inputs, std::vector& outputs) { + void GraphNumericAdd::Process(std::vector& inputs, std::vector& outputs) { outputs.at(0).mOutput = inputs.at(0).Get() + inputs.at(1).Get(); } - void GraphNumericMultiply::ProcessSubGraph(std::vector& inputs, std::vector& outputs) { + void GraphNumericMultiply::Process(std::vector& inputs, std::vector& outputs) { outputs.at(0).mOutput = inputs.at(0).Get() * inputs.at(1).Get(); } - void GraphNumericSubtract::ProcessSubGraph(std::vector& inputs, std::vector& outputs) { + void GraphNumericSubtract::Process(std::vector& inputs, std::vector& outputs) { outputs.at(0).mOutput = inputs.at(0).Get() - inputs.at(1).Get(); } - void GraphNumericNegate::ProcessSubGraph(std::vector& inputs, std::vector& outputs) { + void GraphNumericNegate::Process(std::vector& inputs, std::vector& outputs) { outputs.at(0).mOutput = -inputs.at(0).Get(); } @@ -221,26 +221,26 @@ namespace l::nodegraph { mOutput = 0.0f; } - void GraphNumericIntegral::ProcessSubGraph(std::vector& inputs, std::vector& outputs) { + void GraphNumericIntegral::Process(std::vector& inputs, std::vector& outputs) { mOutput += inputs.at(0).Get(); outputs.at(0).mOutput = mOutput; } /* Logical operations */ - void GraphLogicalAnd::ProcessSubGraph(std::vector& inputs, std::vector& outputs) { + void GraphLogicalAnd::Process(std::vector& inputs, std::vector& outputs) { bool input1 = inputs.at(0).Get() != 0.0f; bool input2 = inputs.at(1).Get() != 0.0f; outputs.at(0).mOutput = (input1 && input2) ? 1.0f : 0.0f; } - void GraphLogicalOr::ProcessSubGraph(std::vector& inputs, std::vector& outputs) { + void GraphLogicalOr::Process(std::vector& inputs, std::vector& outputs) { bool input1 = inputs.at(0).Get() != 0.0f; bool input2 = inputs.at(1).Get() != 0.0f; outputs.at(0).mOutput = (input1 || input2) ? 1.0f : 0.0f; } - void GraphLogicalXor::ProcessSubGraph(std::vector& inputs, std::vector& outputs) { + void GraphLogicalXor::Process(std::vector& inputs, std::vector& outputs) { bool input1 = inputs.at(0).Get() != 0.0f; bool input2 = inputs.at(1).Get() != 0.0f; outputs.at(0).mOutput = (input1 ^ input2) ? 1.0f : 0.0f; @@ -253,13 +253,13 @@ namespace l::nodegraph { mState1 = 0.0f; } - void GraphFilterLowpass::ProcessSubGraph(std::vector& inputs, std::vector& outputs) { + void GraphFilterLowpass::Process(std::vector& inputs, std::vector& outputs) { float cutoff = inputs.at(0).Get(); float resonance = inputs.at(1).Get(); float inputValue = inputs.at(2).Get(); cutoff *= cutoff; - float rc = 1 - resonance * cutoff; + float rc = resonance * cutoff; mState0 = rc * mState0 - cutoff * (mState1 + inputValue); mState1 = rc * mState1 + cutoff * mState0; @@ -267,7 +267,55 @@ namespace l::nodegraph { outputs.at(0).mOutput = -mState1; } - void GraphOutputSpeaker::ProcessSubGraph(std::vector& inputs, std::vector&) { + void GraphFilterEnvelope::Reset() { + mEnvelope = 0.0f; + } + + void GraphFilterEnvelope::Process(std::vector& inputs, std::vector& outputs) { + float noteTarget = inputs.at(0).Get(); + float attackFrames = inputs.at(1).Get() * 44100.0f / 1000.0f; + float releaseFrames = inputs.at(2).Get() * 44100.0f / 1000.0f; + float noteFade = inputs.at(3).Get(); + + if (noteTarget != 0.0f && mFrameCount < attackFrames) { + if (mFrameCount == 0) { + mNote = noteTarget; + } + // attack + mNote = noteTarget; + mFrameCount++; + } + + if (noteTarget != 0.0f) { + if (mEnvelopeTarget < 1.0f) { + mEnvelopeTarget += 1.0f / attackFrames; + } + else { + mEnvelopeTarget = 1.0f; + } + mNote += noteFade * noteFade * (noteTarget - mNote); + } + else { + // release + if (mFrameCount > 0) { + mEnvelopeTarget -= 1.0f / releaseFrames; + if (mEnvelopeTarget < 0.0f) { + mEnvelopeTarget = 0.0f; + mFrameCount = 0; + } + } + else { + mEnvelopeTarget = 0.0f; + mFrameCount = 0; + } + } + + mEnvelope += 0.1f * (mEnvelopeTarget - mEnvelope); + outputs.at(0).mOutput = mNote; + outputs.at(1).mOutput = mEnvelope * mEnvelope; + } + + void GraphOutputSpeaker::Process(std::vector& inputs, std::vector&) { auto& buffer = mAudioStream->GetWriteBuffer(); buffer[mCurrentStereoPosition++] = inputs.at(0).Get(); buffer[mCurrentStereoPosition++] = inputs.at(1).Get(); @@ -277,27 +325,18 @@ namespace l::nodegraph { void GraphOutputSpeaker::Tick(float, float) { } - void GraphEffectReverb::ProcessSubGraph(std::vector& inputs, std::vector& outputs) { - - // 0 - in 1 - // 1 - in 2 - //node->SetInput(2, 0.1f); // mix - //node->SetInput(3, 0.3f); // attenuation - //node->SetInput(4, 30.0f); // room size - - //node->SetInput(5, 0.2f); // delay 1 - //node->SetInput(6, 0.5f); // feedback 1 - - //node->SetInput(7, 2.3f); // delay 2 - //node->SetInput(8, 0.79f); // feedback 2 - - //node->SetInput(9, 0.5f); // delay 3 - //node->SetInput(10, 0.96f); // feedback 3 - + void GraphEffectReverb::Process(std::vector& inputs, std::vector& outputs) { float wet = inputs.at(2).Get(); - - fb = 0.33f * min(1.0f - inputs.at(3).Get() , 0.0f); + float reverbFeedback = min(inputs.at(3).Get(), 1.0f); float roomSize = inputs.at(4).Get(); + float stereoWidth = min(inputs.at(5).Get(), 1.0f); + float earliestDelay = min(inputs.at(6).Get(), 5.0f); + float longestDelay = min(inputs.at(7).Get(), 5.0f); + float numDelays = max(inputs.at(8).Get(), 1.0f); + float tapBulge = 1.0f + 9.0f * min(inputs.at(9).Get(), 1.0f); + //float cutoff = min(inputs.at(10).Get(), 1.0f); + //float res = min(inputs.at(11).Get(), 1.0f); + if (roomSize > maxRoomSizeInMeters) { roomSize = maxRoomSizeInMeters; mNode->SetInput(3, maxRoomSizeInMeters); @@ -309,34 +348,128 @@ namespace l::nodegraph { uint32_t bufSizeLimit = GetFramesPerRoomSize(roomSize); // feedback and delay - d0 = inputs.at(5).Get(); - fb0 = 0.5f * 0.45f * max(inputs.at(6).Get(), 1); - d1 = inputs.at(7).Get(); - fb1 = 0.5f * 0.45f * max(inputs.at(8).Get(), 1); - d2 = inputs.at(9).Get(); - fb2 = 0.5f * 0.45f * max(inputs.at(10).Get(), 1); float dry = 1.0f - wet; - uint32_t delay0 = (int(bufIndex + d0 * bufSizeLimit)) % bufSizeLimit; - uint32_t delay1 = (int(bufIndex + d1 * bufSizeLimit)) % bufSizeLimit; - uint32_t delay2 = (int(bufIndex + d2 * bufSizeLimit)) % bufSizeLimit; - + // indexes for delays with at least one index back float in0 = inputs.at(0).Get(); float in1 = inputs.at(1).Get(); - float out0 = fb0 * buf0[delay0] + fb1 * buf1[delay1] + fb2 * buf0[delay2]; - outputs[0].mOutput = in0 * dry + out0 * wet; - buf0[bufIndex] = (fb)*buf1[bufIndex] + in0 - out0; + auto echoStrength = [](float x, float earlyBulgeStrength) { + // f(x) = E * 2 * x * pow(E, -x * 2); + return 2.72f * x * earlyBulgeStrength * powf(2.72f, -x * earlyBulgeStrength); + }; + + auto sample = [&](float* buf0, float* buf1, float earliestDelay, float longestDelay, float numDelays, float attenuation, float stereoWidth) { + float delayLength = longestDelay - earliestDelay; + float delayValue0 = 0.0f; + float delayValue1 = 0.0f; + + float attenuationSum = 0.01f; + float stereoMixing = stereoWidth; + for (int i = 0; i < static_cast(numDelays); i++) { + //float random = mLCG() / static_cast(INT32_MAX); + + float x = 0.5f / numDelays + i / numDelays; + float delay0 = earliestDelay + delayLength * x * x; + float delay1 = delay0; + + uint32_t delayIndex0 = (int(bufIndex + 2 * bufSizeLimit - 1 - delay0 * bufSizeLimit)) % bufSizeLimit; + uint32_t delayIndex1 = (int(bufIndex + 2 * bufSizeLimit - 1 - delay1 * bufSizeLimit)) % bufSizeLimit; - float out1 = fb0 * buf1[delay0] + fb1 * buf0[delay1] + fb2 * buf1[delay2]; - outputs[1].mOutput = in1 * dry + out1 * wet; - buf1[bufIndex] = (fb)*buf0[bufIndex] + in1 - out1; + float monoMixing = 1.0f - stereoMixing; + float gain = echoStrength(x, tapBulge); + delayValue0 += gain * (stereoMixing * buf0[delayIndex0] + monoMixing * buf1[delayIndex0]); + delayValue1 += gain * (stereoMixing * buf1[delayIndex1] + monoMixing * buf0[delayIndex1]); + + attenuationSum += gain; + } + + float gainAdjust = attenuation / attenuationSum; + + // normalize reverb based on input attenuation value + delayValue0 *= gainAdjust; + delayValue1 *= gainAdjust; + + return std::tuple(delayValue0, delayValue1); + }; + + + auto [delay0, delay1] = sample(bufEarlyTap0.data(), bufEarlyTap1.data(), earliestDelay, longestDelay, numDelays, reverbFeedback, stereoWidth); + + bufEarlyTap0[bufTapIndex] = reverbFeedback * (bufEarlyTap0[bufTapIndex] + delay0 + in0); + bufEarlyTap1[bufTapIndex] = reverbFeedback * (bufEarlyTap1[bufTapIndex] + delay1 + in1); + + bufTapIndex = (bufTapIndex + 1) % bufSizeLimit; + + outputs[2].mOutput = in0 * dry + bufEarlyTap0[bufTapIndex] * wet; + outputs[3].mOutput = in1 * dry + bufEarlyTap1[bufTapIndex] * wet; + + bufRev0[bufIndex] += delay0; + bufRev1[bufIndex] += delay1; + + // buffer blur of oldest sample plus next oldest sample with global attenuation and new input + auto [reverb0, reverb1] = sample(bufRev0.data(), bufRev1.data(), earliestDelay, longestDelay, numDelays, reverbFeedback, stereoWidth); + /* + cutoff *= cutoff; + float rc = 1.0f - res * cutoff; + + mLP0 = rc * mLP0 - cutoff * (mLP1 + reverb0); + mLP1 = rc * mLP1 + cutoff * mLP0; + mLP2 = rc * mLP2 - cutoff * (mLP3 + reverb1); + mLP3 = rc * mLP3 + cutoff * mLP2; + float lp0 = mLP1; + float lp1 = mLP3; + */ + bufRev0[bufIndex] = reverbFeedback * (bufRev0[bufIndex] + reverb0); + bufRev1[bufIndex] = reverbFeedback * (bufRev1[bufIndex] + reverb1); bufIndex = (bufIndex + 1) % bufSizeLimit; - delay0 = (delay0 + 1) % bufSizeLimit; - delay1 = (delay1 + 1) % bufSizeLimit; - delay2 = (delay2 + 1) % bufSizeLimit; + + outputs[0].mOutput = in0 * dry + bufRev0[bufIndex] * wet; + outputs[1].mOutput = in1 * dry + bufRev1[bufIndex] * wet; + + // cross delay mixing (delay 1 & 2) + //buf0[bufIndex] += fb1 * buf1[delay1]; + //buf1[bufIndex] += fb0 * buf0[delay0]; + + // central delay mixing (delay 3) + //float centralDelay = fb2 * (buf0[delay2] + buf1[delay2]); + //buf0[bufIndex] += centralDelay; + //buf1[bufIndex] += centralDelay; + + // + + } + + void GraphEffectReverb::Tick(float, float) { + if (!mInited) { + mNode->SetInput(2, 0.3f); + mNode->SetInput(3, 0.5f); + mNode->SetInput(4, 30.0f); + mNode->SetInput(5, 0.5f); + mNode->SetInput(6, 0.1f); + mNode->SetInput(7, 0.8f); + mNode->SetInput(8, 5.0f); + mNode->SetInput(9, 0.7f); + mNode->SetInput(10, 0.95f); + mNode->SetInput(11, 0.01f); + + /* + float wet = inputs.at(2).Get(); + float reverbFeedback = min(inputs.at(3).Get(), 1.0f); + float roomSize = inputs.at(4).Get(); + float stereoWidth = min(inputs.at(5).Get(), 1.0f); + float earliestDelay = min(inputs.at(6).Get(), 1.0f); + float longestDelay = min(inputs.at(7).Get(), 1.0f); + float numDelays = max(inputs.at(8).Get(), 1.0f); + float tapBulge = 1.0f + 9.0f * min(inputs.at(9).Get(), 1.0f); + float cutoff = min(inputs.at(10).Get(), 1.0f); + float res = min(inputs.at(11).Get(), 1.0f); + */ + mInited = true; + } } + } diff --git a/packages/nodegraph/source/common/NodeGraphSchema.cpp b/packages/nodegraph/source/common/NodeGraphSchema.cpp index 0cfbf8ad..5a19c431 100644 --- a/packages/nodegraph/source/common/NodeGraphSchema.cpp +++ b/packages/nodegraph/source/common/NodeGraphSchema.cpp @@ -22,61 +22,64 @@ namespace l::nodegraph { l::nodegraph::NodeGraphBase* node = nullptr; switch (typeId) { case 0: - node = mMainNodeGraph.NewNode(false, 0); + node = mMainNodeGraph.NewNode(OutputType::Default, 0); break; case 1: - node = mMainNodeGraph.NewNode(false, 1); + node = mMainNodeGraph.NewNode(OutputType::Default, 1); break; case 2: - node = mMainNodeGraph.NewNode(false, 2); + node = mMainNodeGraph.NewNode(OutputType::Default, 2); break; case 3: - node = mMainNodeGraph.NewNode(false, 3); + node = mMainNodeGraph.NewNode(OutputType::Default, 3); break; case 4: - node = mMainNodeGraph.NewNode(false, 1, mKeyState); + node = mMainNodeGraph.NewNode(OutputType::Default, 1, mKeyState); break; case 5: - node = mMainNodeGraph.NewNode(false); + node = mMainNodeGraph.NewNode(OutputType::Default); break; case 6: - node = mMainNodeGraph.NewNode(false); + node = mMainNodeGraph.NewNode(OutputType::Default); break; case 50: - node = mMainNodeGraph.NewNode(false); + node = mMainNodeGraph.NewNode(OutputType::Default); break; case 51: - node = mMainNodeGraph.NewNode(false); + node = mMainNodeGraph.NewNode(OutputType::Default); break; case 52: - node = mMainNodeGraph.NewNode(false); + node = mMainNodeGraph.NewNode(OutputType::Default); break; case 53: - node = mMainNodeGraph.NewNode(false); + node = mMainNodeGraph.NewNode(OutputType::Default); break; case 54: - node = mMainNodeGraph.NewNode(false); + node = mMainNodeGraph.NewNode(OutputType::Default); break; case 100: - node = mMainNodeGraph.NewNode(false); + node = mMainNodeGraph.NewNode(OutputType::Default); break; case 101: - node = mMainNodeGraph.NewNode(false); + node = mMainNodeGraph.NewNode(OutputType::Default); break; case 102: - node = mMainNodeGraph.NewNode(false); + node = mMainNodeGraph.NewNode(OutputType::Default); break; case 150: - node = mMainNodeGraph.NewNode(false); + node = mMainNodeGraph.NewNode(OutputType::Default); + break; + case 151: + node = mMainNodeGraph.NewNode(OutputType::Default); break; case 200: - node = mMainNodeGraph.NewNode(true, 1); + node = mMainNodeGraph.NewNode(OutputType::ExternalOutput, 1); break; case 201: - node = mMainNodeGraph.NewNode(true, mAudioOutput); + node = mMainNodeGraph.NewNode(OutputType::ExternalOutput, mAudioOutput); break; case 250: - node = mMainNodeGraph.NewNode(false); + node = mMainNodeGraph.NewNode(OutputType::Default); break; default: ASSERT(typeId < 10000) << "Custom node id's begin at id 1000"; diff --git a/packages/nodegraph/tests/common/NodeGraphSchemaTest.cpp b/packages/nodegraph/tests/common/NodeGraphSchemaTest.cpp index 60768c1d..d669cbab 100644 --- a/packages/nodegraph/tests/common/NodeGraphSchemaTest.cpp +++ b/packages/nodegraph/tests/common/NodeGraphSchemaTest.cpp @@ -119,9 +119,6 @@ TEST(NodeGraph, GraphGroups) { NodeGraphGroup group; - NodeGraph nodeLowpass1; - NodeGraph nodeLowpass2; - float cutoff = 0.8f; float resonance = 0.0001f; float input1 = 0.3f; @@ -136,24 +133,25 @@ TEST(NodeGraph, GraphGroups) { group.SetInput(2, &input1); group.SetInput(3, &input2); + auto nodeLowpass1 = group.NewNode(OutputType::Default); + auto nodeLowpass2 = group.NewNode(OutputType::Default); // cutoff - nodeLowpass1.SetInput(0, group, 0, true); - nodeLowpass2.SetInput(0, group, 0, true); + nodeLowpass1->SetInput(0, group, 0); + nodeLowpass2->SetInput(0, group, 0); // resonance - nodeLowpass1.SetInput(1, group, 1, true); - nodeLowpass2.SetInput(1, group, 1, true); + nodeLowpass1->SetInput(1, group, 1); + nodeLowpass2->SetInput(1, group, 1); // left, right - nodeLowpass1.SetInput(2, group, 2, true); - nodeLowpass2.SetInput(2, group, 3, true); + nodeLowpass1->SetInput(2, group, 2); + nodeLowpass2->SetInput(2, group, 3); - group.SetOutput(0, nodeLowpass1, 0); - group.SetOutput(1, nodeLowpass2, 0); + group.SetOutput(0, *nodeLowpass1, 0); + group.SetOutput(1, *nodeLowpass2, 0); } NodeGraphGroup group2; - NodeGraph copyNode; { // wire sequential group with a simple copy node group2.SetNumInputs(2); @@ -161,13 +159,14 @@ TEST(NodeGraph, GraphGroups) { group2.SetInput(0, group, 0); group2.SetInput(1, group, 1); - copyNode.SetNumInputs(2); - copyNode.SetNumOutputs(2); - copyNode.SetInput(0, group2, 0, true); - copyNode.SetInput(1, group2, 1, true); + auto copyNode = group2.NewNode(OutputType::Default); + copyNode->SetNumInputs(2); + copyNode->SetNumOutputs(2); + copyNode->SetInput(0, group2, 0); + copyNode->SetInput(1, group2, 1); - group2.SetOutput(0, copyNode, 0); - group2.SetOutput(1, copyNode, 1); + group2.SetOutput(0, *copyNode, 0); + group2.SetOutput(1, *copyNode, 1); } // only update the last group/node and all dependent nodes will update in the graph to produce an output diff --git a/packages/rendering/source/common/ui/UIVisitors.cpp b/packages/rendering/source/common/ui/UIVisitors.cpp index 9958f149..ce732aa8 100644 --- a/packages/rendering/source/common/ui/UIVisitors.cpp +++ b/packages/rendering/source/common/ui/UIVisitors.cpp @@ -210,7 +210,8 @@ namespace l::ui { auto node = mNGSchema->GetNode(container.GetNodeId()); auto nodeChannel = static_cast(container.GetChannelId()); if (node->IsDataEditable(nodeChannel)) { - auto nodeValue = node->Get(nodeChannel); + //auto nodeValue = node->Get(nodeChannel); + auto nodeValue = node->GetInput(nodeChannel); if (!ImGui::IsKeyDown(ImGuiKey::ImGuiKey_LeftShift)) { if (!ImGui::IsKeyDown(ImGuiKey::ImGuiKey_LeftCtrl)) { nodeValue -= move.y / 100.0f; @@ -341,11 +342,11 @@ namespace l::ui { if (mNGSchema) { auto node = mNGSchema->GetNode(container.GetNodeId()); float nodeValue = 0.0f; - if (node->GetNumOutputs() > 0 && container.GetChannelId() < node->GetNumOutputs()) { - nodeValue = node->Get(static_cast(container.GetChannelId())); + if (container.GetChannelId() < node->GetNumInputs()) { + nodeValue = node->GetInput(static_cast(container.GetChannelId())); } else { - nodeValue = node->GetInput(static_cast(container.GetChannelId())); + nodeValue = node->Get(static_cast(container.GetChannelId())); } auto nodeString = std::to_string(nodeValue); mDrawList->AddText(ImGui::GetDefaultFont(), 13.0f * container.GetScale() * layoutArea.mScale, p1, color, nodeString.c_str()); From 4d813c08da9a97d1d169df9eb19ecce12eec546d Mon Sep 17 00:00:00 2001 From: lnd3 Date: Wed, 4 Sep 2024 01:02:48 +0200 Subject: [PATCH 105/125] Readd previous reverb. --- .../include/nodegraph/NodeGraphOperations.h | 87 ++++++++++++++++++- .../include/nodegraph/NodeGraphSchema.h | 3 +- .../source/common/NodeGraphOperations.cpp | 67 +++++++++++++- .../source/common/NodeGraphSchema.cpp | 5 +- 4 files changed, 154 insertions(+), 8 deletions(-) diff --git a/packages/nodegraph/include/nodegraph/NodeGraphOperations.h b/packages/nodegraph/include/nodegraph/NodeGraphOperations.h index 15400279..fcebea1c 100644 --- a/packages/nodegraph/include/nodegraph/NodeGraphOperations.h +++ b/packages/nodegraph/include/nodegraph/NodeGraphOperations.h @@ -281,6 +281,14 @@ namespace l::nodegraph { virtual ~GraphFilterEnvelope() = default; void Reset() override; void Process(std::vector& inputs, std::vector& outputs) override; + virtual void Tick(float, float) { + if (!mInited) { + mNode->SetInput(1, 50.0f); + mNode->SetInput(2, 50.0f); + mNode->SetInput(3, 0.1f); + mInited = true; + } + } std::string_view GetInputName(int8_t inputChannel) { return defaultInStrings[inputChannel]; @@ -302,7 +310,8 @@ namespace l::nodegraph { return "Envelope"; } protected: - uint32_t mFrameCount; + bool mInited = false; + uint32_t mFrameCount = 0; float mEnvelopeTarget = 0.0f; float mNote = 0.0f; float mEnvelope = 0.0f; @@ -355,7 +364,77 @@ namespace l::nodegraph { int32_t mCurrentStereoPosition; }; - class GraphEffectReverb : public NodeGraphOp { + class GraphEffectReverb1 : public NodeGraphOp { + public: + std::string defaultInStrings[11] = { "In 1", "In 2", "Mix", "Attenuation", "Room Size", "Delay 1", "Feedback 1", "Delay 2", "Feedback 2", "Delay 3", "Feedback 3" }; + std::string defaultOutStrings[2] = { "Out 1", "Out 2" }; + + uint32_t GetFramesPerRoomSize(float roomSize) { + const float metersPerFrame = 334.0f / 44100.0f; // (m/s)/(frames/s) = m/frames; + const float metersToWallPerFrame = metersPerFrame / 2.0f; // half the distance to wall for the bounced distance + const float framesPerRoom = roomSize / metersToWallPerFrame; + return static_cast(framesPerRoom); + } + + const float maxRoomSizeInMeters = 334.0f; // 334 meters large is 2 seconds of reverbation + + GraphEffectReverb1(NodeGraphBase * node) : + NodeGraphOp(node, 11, 2, 0) { + uint32_t bufferSize = GetFramesPerRoomSize(maxRoomSizeInMeters); + buf0.resize(bufferSize); + buf1.resize(bufferSize); + } + + virtual ~GraphEffectReverb1() = default; + + void Process(std::vector&inputs, std::vector&outputs) override; + virtual void Tick(float time, float elapsed) override; + + virtual bool IsDataVisible(int8_t num) override { + return num >= 2 ? true : false; + + } + + virtual bool IsDataEditable(int8_t num) override { + return num >= 2 ? true : false; + } + + std::string_view GetInputName(int8_t inputChannel) { + return defaultInStrings[inputChannel]; + } + + std::string_view GetOutputName(int8_t outputChannel) { + return defaultOutStrings[outputChannel]; + } + + std::string_view GetName() override { + return "Reverb"; + } + + float max(float value, float max) { + return value > max ? value : max; + } + + float min(float value, float min) { + return value < min ? value : min; + } + protected: + bool mInited = false; + std::vector buf0; + std::vector buf1; + uint32_t bufIndex = 0; + float mix = 0.0f; + float fb = 0.0f; + float fb0 = 0.0f; + float fb1 = 0.0f; + float fb2 = 0.0f; + float d0 = 0.0f; + float d1 = 0.0f; + float d2 = 0.0f; + + }; + + class GraphEffectReverb2 : public NodeGraphOp { public: std::string defaultInStrings[12] = { "In 1", "In 2", "Mix", "Feedback", "Room Size", "Width", "First tap", "Longest tap", "Num taps", "Tap bulge", "Filter cutoff", "Filter res"}; std::string defaultOutStrings[4] = { "Rev 1", "Rev 2", "Tap 1", "Tap 2"}; @@ -369,7 +448,7 @@ namespace l::nodegraph { const float maxRoomSizeInMeters = 334.0f; // 334 meters large is 2 seconds of reverbation - GraphEffectReverb(NodeGraphBase* node) : + GraphEffectReverb2(NodeGraphBase* node) : NodeGraphOp(node, 12, 4, 0) { uint32_t bufferSize = GetFramesPerRoomSize(maxRoomSizeInMeters); @@ -379,7 +458,7 @@ namespace l::nodegraph { bufEarlyTap1.resize(bufferSize); } - virtual ~GraphEffectReverb() = default; + virtual ~GraphEffectReverb2() = default; virtual void Process(std::vector& inputs, std::vector& outputs) override; virtual void Tick(float time, float elapsed) override; diff --git a/packages/nodegraph/include/nodegraph/NodeGraphSchema.h b/packages/nodegraph/include/nodegraph/NodeGraphSchema.h index ae4420b7..64ed4558 100644 --- a/packages/nodegraph/include/nodegraph/NodeGraphSchema.h +++ b/packages/nodegraph/include/nodegraph/NodeGraphSchema.h @@ -51,7 +51,8 @@ namespace l::nodegraph { RegisterNodeType("Filter", 151, "Envelope"); RegisterNodeType("Output", 200, "Value Debug"); RegisterNodeType("Output", 201, "Speaker"); - RegisterNodeType("Effect", 250, "Reverb"); + RegisterNodeType("Effect", 250, "Reverb1"); + RegisterNodeType("Effect", 251, "Reverb2"); } ~NodeGraphSchema() = default; diff --git a/packages/nodegraph/source/common/NodeGraphOperations.cpp b/packages/nodegraph/source/common/NodeGraphOperations.cpp index 0af77ce7..93223301 100644 --- a/packages/nodegraph/source/common/NodeGraphOperations.cpp +++ b/packages/nodegraph/source/common/NodeGraphOperations.cpp @@ -325,7 +325,70 @@ namespace l::nodegraph { void GraphOutputSpeaker::Tick(float, float) { } - void GraphEffectReverb::Process(std::vector& inputs, std::vector& outputs) { + void GraphEffectReverb1::Process(std::vector&inputs, std::vector&outputs) { + float wet = inputs.at(2).Get(); + + fb = 0.33f * min(1.0f - inputs.at(3).Get(), 0.0f); + + float roomSize = inputs.at(4).Get(); + + if (roomSize > maxRoomSizeInMeters) { + roomSize = maxRoomSizeInMeters; + mNode->SetInput(3, maxRoomSizeInMeters); + } + else if (roomSize < 0.1f) { + roomSize = 0.1f; + mNode->SetInput(3, 0.1f); + } + + uint32_t bufSizeLimit = GetFramesPerRoomSize(roomSize); + + d0 = inputs.at(5).Get(); + fb0 = 0.5f * 0.45f * max(inputs.at(6).Get(), 1); + d1 = inputs.at(7).Get(); + fb1 = 0.5f * 0.45f * max(inputs.at(8).Get(), 1); + d2 = inputs.at(9).Get(); + fb2 = 0.5f * 0.45f * max(inputs.at(10).Get(), 1); + + float dry = 1.0f - wet; + + uint32_t delay0 = (int(bufIndex + d0 * bufSizeLimit)) % bufSizeLimit; + uint32_t delay1 = (int(bufIndex + d1 * bufSizeLimit)) % bufSizeLimit; + uint32_t delay2 = (int(bufIndex + d2 * bufSizeLimit)) % bufSizeLimit; + float in0 = inputs.at(0).Get(); + float in1 = inputs.at(1).Get(); + + float out0 = fb0 * buf0[delay0] + fb1 * buf1[delay1] + fb2 * buf0[delay2]; + outputs[0].mOutput = in0 * dry + out0 * wet; + buf0[bufIndex] = (fb)*buf1[bufIndex] + in0 - out0; + + float out1 = fb0 * buf1[delay0] + fb1 * buf0[delay1] + fb2 * buf1[delay2]; + outputs[1].mOutput = in1 * dry + out1 * wet; + buf1[bufIndex] = (fb)*buf0[bufIndex] + in1 - out1; + + bufIndex = (bufIndex + 1) % bufSizeLimit; + + delay0 = (delay0 + 1) % bufSizeLimit; + delay1 = (delay1 + 1) % bufSizeLimit; + delay2 = (delay2 + 1) % bufSizeLimit; + } + + void GraphEffectReverb1::Tick(float, float) { + if (!mInited) { + mNode->SetInput(2, 0.75f); + mNode->SetInput(3, 0.5f); + mNode->SetInput(4, 30.0f); + mNode->SetInput(5, 0.5f); + mNode->SetInput(6, 0.9f); + mNode->SetInput(7, 0.8f); + mNode->SetInput(8, 0.9f); + mNode->SetInput(9, 0.7f); + mNode->SetInput(10, 0.9f); + mInited = true; + } + } + + void GraphEffectReverb2::Process(std::vector& inputs, std::vector& outputs) { float wet = inputs.at(2).Get(); float reverbFeedback = min(inputs.at(3).Get(), 1.0f); float roomSize = inputs.at(4).Get(); @@ -442,7 +505,7 @@ namespace l::nodegraph { } - void GraphEffectReverb::Tick(float, float) { + void GraphEffectReverb2::Tick(float, float) { if (!mInited) { mNode->SetInput(2, 0.3f); mNode->SetInput(3, 0.5f); diff --git a/packages/nodegraph/source/common/NodeGraphSchema.cpp b/packages/nodegraph/source/common/NodeGraphSchema.cpp index 5a19c431..85994480 100644 --- a/packages/nodegraph/source/common/NodeGraphSchema.cpp +++ b/packages/nodegraph/source/common/NodeGraphSchema.cpp @@ -79,7 +79,10 @@ namespace l::nodegraph { node = mMainNodeGraph.NewNode(OutputType::ExternalOutput, mAudioOutput); break; case 250: - node = mMainNodeGraph.NewNode(OutputType::Default); + node = mMainNodeGraph.NewNode(OutputType::Default); + break; + case 251: + node = mMainNodeGraph.NewNode(OutputType::Default); break; default: ASSERT(typeId < 10000) << "Custom node id's begin at id 1000"; From 9df7efea1d2bd2d8d82e88687b9d6e68b076e78c Mon Sep 17 00:00:00 2001 From: lnd3 Date: Wed, 4 Sep 2024 01:18:21 +0200 Subject: [PATCH 106/125] Fix lp filter and test. --- packages/nodegraph/source/common/NodeGraphOperations.cpp | 4 ++-- packages/nodegraph/tests/common/NodeGraphSchemaTest.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/nodegraph/source/common/NodeGraphOperations.cpp b/packages/nodegraph/source/common/NodeGraphOperations.cpp index 93223301..b824ee61 100644 --- a/packages/nodegraph/source/common/NodeGraphOperations.cpp +++ b/packages/nodegraph/source/common/NodeGraphOperations.cpp @@ -255,11 +255,11 @@ namespace l::nodegraph { void GraphFilterLowpass::Process(std::vector& inputs, std::vector& outputs) { float cutoff = inputs.at(0).Get(); - float resonance = inputs.at(1).Get(); + float resonance = 1.0f - inputs.at(1).Get(); float inputValue = inputs.at(2).Get(); cutoff *= cutoff; - float rc = resonance * cutoff; + float rc = 1.0f - resonance * cutoff; mState0 = rc * mState0 - cutoff * (mState1 + inputValue); mState1 = rc * mState1 + cutoff * mState0; diff --git a/packages/nodegraph/tests/common/NodeGraphSchemaTest.cpp b/packages/nodegraph/tests/common/NodeGraphSchemaTest.cpp index d669cbab..3f33f159 100644 --- a/packages/nodegraph/tests/common/NodeGraphSchemaTest.cpp +++ b/packages/nodegraph/tests/common/NodeGraphSchemaTest.cpp @@ -96,7 +96,7 @@ TEST(NodeGraph, FilterLowpass) { NodeGraph nodeLowpass; float cutoff = 0.8f; - float resonance = 0.1f; + float resonance = 0.9f; float input = 1.3f; nodeLowpass.SetInput(0, &cutoff); From 9c8e24b3c17bd32326b6acf262ec5231d66bd08b Mon Sep 17 00:00:00 2001 From: lnd3 Date: Wed, 4 Sep 2024 03:46:31 +0200 Subject: [PATCH 107/125] Add math package and move some files from physics into it. Refactor dependencies. --- CMakeLists.txt | 1 + packages/audio/CMakeLists.txt | 1 + packages/audio/include/audio/AudioUtils.h | 3 ++ packages/audio/source/common/AudioUtils.cpp | 29 +++++++++++++++++ packages/math/CMakeLists.txt | 9 ++++++ .../include/math/MathAlgorithm.h} | 14 ++++++--- packages/math/include/math/MathAll.h | 6 ++++ .../include/math/MathConstants.h} | 21 ++++++++----- .../physics => math/include/math}/MathFunc.h | 31 +++++++++++-------- .../include/math}/MathSmooth.h | 4 +-- .../source/common/MathAlgorithm.cpp} | 7 ++--- packages/math/tests/common/MathTest.cpp | 7 +++++ packages/physics/CMakeLists.txt | 1 + .../physics/include/physics/CurveBezier.h | 2 -- packages/physics/include/physics/GridMap.h | 7 +++-- .../physics/include/physics/Integration.h | 14 ++++----- packages/physics/include/physics/LinkedList.h | 4 +-- packages/physics/include/physics/PhysicsAll.h | 4 --- packages/physics/source/common/BoxSweep.cpp | 3 +- .../physics/tests/common/IntegrationTest.cpp | 5 +-- .../physics/tests/common/PoleResolverTest.cpp | 1 - .../include/rendering/GeometryManip.h | 1 - .../rendering/source/common/FPSInterface.cpp | 12 +++---- .../rendering/source/common/GLFWRenderVAO.cpp | 3 +- 24 files changed, 129 insertions(+), 61 deletions(-) create mode 100644 packages/math/CMakeLists.txt rename packages/{physics/include/physics/Algorithm.h => math/include/math/MathAlgorithm.h} (93%) create mode 100644 packages/math/include/math/MathAll.h rename packages/{physics/include/physics/Constants.h => math/include/math/MathConstants.h} (64%) rename packages/{physics/include/physics => math/include/math}/MathFunc.h (95%) rename packages/{physics/include/physics => math/include/math}/MathSmooth.h (95%) rename packages/{physics/source/common/Algorithm.cpp => math/source/common/MathAlgorithm.cpp} (91%) create mode 100644 packages/math/tests/common/MathTest.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index f312e713..d74c0c8a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,6 +23,7 @@ else() testing meta memory + math hid audio nodegraph diff --git a/packages/audio/CMakeLists.txt b/packages/audio/CMakeLists.txt index 245cfab8..fd596413 100644 --- a/packages/audio/CMakeLists.txt +++ b/packages/audio/CMakeLists.txt @@ -4,6 +4,7 @@ set(deps logging testing memory + math ) bs_generate_package(audio "tier1" "${deps}" "PortAudio") diff --git a/packages/audio/include/audio/AudioUtils.h b/packages/audio/include/audio/AudioUtils.h index 6660d636..6624f801 100644 --- a/packages/audio/include/audio/AudioUtils.h +++ b/packages/audio/include/audio/AudioUtils.h @@ -1,5 +1,8 @@ #pragma once +#include + namespace l::audio { float GetFrequencyFromNote(float note); + float BatchUpdate(float updateSamples, float samplesLeft, int32_t start, int32_t end, std::function process, std::function update); } diff --git a/packages/audio/source/common/AudioUtils.cpp b/packages/audio/source/common/AudioUtils.cpp index a2c82fc9..f8b3c04e 100644 --- a/packages/audio/source/common/AudioUtils.cpp +++ b/packages/audio/source/common/AudioUtils.cpp @@ -1,6 +1,7 @@ #include "audio/AudioUtils.h" #include "logging/LoggingAll.h" +#include "math/MathAll.h" #include @@ -9,4 +10,32 @@ namespace l::audio { float GetFrequencyFromNote(float note) { return 440.0f * powf(2.0f, (note - 49.0f) / 12.0f); } + + float BatchUpdate(float updateSamples, float samplesLeft, int32_t start, int32_t end, std::function process, std::function update) { + float startNum = static_cast(start); + while (startNum < static_cast(end)) { + bool updated = false; + if (samplesLeft < 1.0f) { + samplesLeft += updateSamples; + if (update != nullptr) { + update(startNum); + } + updated = true; + } + + // Update stuff + // process samples until + // * batch max has been reached + // * end has been reached + + float samples = l::math::functions::min(static_cast(end) - startNum, samplesLeft); + if (process != nullptr) { + process(static_cast(startNum), static_cast(startNum + samples), updated); + } + startNum += samples; + samplesLeft -= samples; + } + return samplesLeft; + } + } diff --git a/packages/math/CMakeLists.txt b/packages/math/CMakeLists.txt new file mode 100644 index 00000000..dcd2c725 --- /dev/null +++ b/packages/math/CMakeLists.txt @@ -0,0 +1,9 @@ +project (math) + +set(deps + logging + testing + memory +) + +bs_generate_package(math "tier1" "${deps}" "") diff --git a/packages/physics/include/physics/Algorithm.h b/packages/math/include/math/MathAlgorithm.h similarity index 93% rename from packages/physics/include/physics/Algorithm.h rename to packages/math/include/math/MathAlgorithm.h index 8d95be2c..06408db0 100644 --- a/packages/physics/include/physics/Algorithm.h +++ b/packages/math/include/math/MathAlgorithm.h @@ -12,16 +12,23 @@ #include "logging/LoggingAll.h" #include -#include "physics/MathFunc.h" +#include "math/MathFunc.h" -namespace l { -namespace algorithm { +namespace l::math::algorithm { uint64_t pairIndex32(uint32_t i, uint32_t j); uint32_t pairIndex16(uint16_t i, uint16_t j); uint32_t encodeFloat(const float newPos); float decodeFloat(uint32_t ir); + template + T convert(U data) { + const char* srcPtr = reinterpret_cast(&data); + T dst; + memcpy(&dst, srcPtr, sizeof(T)); + return dst; + } + template bool samesign(T a, T b) { return a * b >= 0.0; @@ -151,4 +158,3 @@ namespace algorithm { } } -} diff --git a/packages/math/include/math/MathAll.h b/packages/math/include/math/MathAll.h new file mode 100644 index 00000000..3bb505b4 --- /dev/null +++ b/packages/math/include/math/MathAll.h @@ -0,0 +1,6 @@ +#pragma once + +#include "math/MathFunc.h" +#include "math/MathSmooth.h" +#include "math/MathConstants.h" +#include "math/MathAlgorithm.h" diff --git a/packages/physics/include/physics/Constants.h b/packages/math/include/math/MathConstants.h similarity index 64% rename from packages/physics/include/physics/Constants.h rename to packages/math/include/math/MathConstants.h index 89310668..a39ab187 100644 --- a/packages/physics/include/physics/Constants.h +++ b/packages/math/include/math/MathConstants.h @@ -2,19 +2,27 @@ #include -namespace l { -namespace constants { +namespace l::math::constants { // Newtonian constant of gravitation 6.67430(15)x10-11 m3 kg-1 s-2 const double G = 6.674 * std::pow(10.0, -11.0); - // speed of light in vacuum 299792458 m s-1 + // Speed of light in vacuum 299792458 m s-1 const double c = 299792458; - // Planck constant 6.62607015x10-34 J Hz-1 + // Planck's constant 6.62607015x10-34 J Hz-1 const double h = 6.62607015 * std::pow(10.0, -34.0); - const double pi = 3.14159265359; - const float pi_f = 3.14159265359f; + // PI + const double PI = 3.141592653589793; + const float PI_f = 3.141592653589793f; + + // euler's number e + const double E = 2.718281828459045; + const float E_f = 2.718281828459045f; + + // phi + const double PHI = 1.618033988749895; + const double PHI_f = 1.618033988749895f; // Minkowski space time distance https://www.youtube.com/watch?v=fvqXshyuvOg // Basically objects are 5 dimensional and the distance between objects are @@ -27,4 +35,3 @@ namespace constants { } } -} diff --git a/packages/physics/include/physics/MathFunc.h b/packages/math/include/math/MathFunc.h similarity index 95% rename from packages/physics/include/physics/MathFunc.h rename to packages/math/include/math/MathFunc.h index e68958bf..e7e56345 100644 --- a/packages/physics/include/physics/MathFunc.h +++ b/packages/math/include/math/MathFunc.h @@ -7,10 +7,24 @@ #include #include -#include "physics/Constants.h" +#include "math/MathConstants.h" -namespace l { -namespace algorithm { +namespace l::math::functions { + + template + T min(T val1, T val2) { + return val1 < val2 ? val1 : val2; + } + + template + T max(T val1, T val2) { + return val1 > val2 ? val1 : val2; + } + + template + T clamp(T val, T min, T max) { + return val < min ? min : val > max ? max : val; + } template T abs(T val) { @@ -87,14 +101,6 @@ namespace algorithm { return static_cast(0); } - template - T convert(U data) { - const char* srcPtr = reinterpret_cast(&data); - T dst; - memcpy(&dst, srcPtr, sizeof(T)); - return dst; - } - // Almost Identity(I) // Imagine you don't want to modify a signal unless it's drops to zero or close to it, in which case you want to replace the value with a small possitive constant.Then, rather than clamping the valueand introduce a discontinuity, you can smoothly blend the signal into the desired clipped value.So, let m be the threshold(anything above m stays unchanged), and n the value things will take when the signal is zero.Then, the following function does the soft clipping(in a cubic fashion): template @@ -217,9 +223,8 @@ namespace algorithm { template T sinc(T x, T k) { - const T a = l::constants::pi * (k * x - 1.0); + const T a = math::constants::PI * (k * x - 1.0); return sin(a) / a; } } -} diff --git a/packages/physics/include/physics/MathSmooth.h b/packages/math/include/math/MathSmooth.h similarity index 95% rename from packages/physics/include/physics/MathSmooth.h rename to packages/math/include/math/MathSmooth.h index 5df90f03..ad71f547 100644 --- a/packages/physics/include/physics/MathSmooth.h +++ b/packages/math/include/math/MathSmooth.h @@ -6,8 +6,7 @@ #include #include -namespace l { -namespace algorithm { +namespace l::math::smooth { // Quintic Polynomial - C2 continuity template @@ -50,4 +49,3 @@ namespace algorithm { } } -} diff --git a/packages/physics/source/common/Algorithm.cpp b/packages/math/source/common/MathAlgorithm.cpp similarity index 91% rename from packages/physics/source/common/Algorithm.cpp rename to packages/math/source/common/MathAlgorithm.cpp index 6a369f41..1caad93e 100644 --- a/packages/physics/source/common/Algorithm.cpp +++ b/packages/math/source/common/MathAlgorithm.cpp @@ -1,8 +1,8 @@ -#include "physics/Algorithm.h" +#include "math/MathAlgorithm.h" +#include "math/MathFunc.h" -namespace l { -namespace algorithm { +namespace l::math::algorithm { uint64_t pairIndex32(uint32_t i, uint32_t j) { /// Make sure index is order invariant @@ -46,4 +46,3 @@ namespace algorithm { return convert(rv); } } -} diff --git a/packages/math/tests/common/MathTest.cpp b/packages/math/tests/common/MathTest.cpp new file mode 100644 index 00000000..6d728e9e --- /dev/null +++ b/packages/math/tests/common/MathTest.cpp @@ -0,0 +1,7 @@ +#include "testing/Test.h" + +int main(int, char* argw[]) { + TEST_RUN(argw[0]); + + return 0; +} diff --git a/packages/physics/CMakeLists.txt b/packages/physics/CMakeLists.txt index 89b41de8..4a5ba9a7 100644 --- a/packages/physics/CMakeLists.txt +++ b/packages/physics/CMakeLists.txt @@ -5,6 +5,7 @@ set(deps logging testing + math tools ) diff --git a/packages/physics/include/physics/CurveBezier.h b/packages/physics/include/physics/CurveBezier.h index 32d41652..c3778dcc 100644 --- a/packages/physics/include/physics/CurveBezier.h +++ b/packages/physics/include/physics/CurveBezier.h @@ -2,8 +2,6 @@ #include #include "VecX.h" -#include "MathSmooth.h" - namespace l { namespace curves { diff --git a/packages/physics/include/physics/GridMap.h b/packages/physics/include/physics/GridMap.h index 7e26a28f..c251ee69 100644 --- a/packages/physics/include/physics/GridMap.h +++ b/packages/physics/include/physics/GridMap.h @@ -11,14 +11,15 @@ #include "logging/LoggingAll.h" #include "physics/VecX.h" -#include "physics/Constants.h" + +#include "math/MathConstants.h" namespace l::physics { template auto DistanceFromDegrees(T degree) { - auto x = cos((degree / 360.0) * 2.0 * constants::pi); - auto y = sin((degree / 360.0) * 2.0 * constants::pi); + auto x = cos((degree / 360.0) * 2.0 * math::constants::PI); + auto y = sin((degree / 360.0) * 2.0 * math::constants::PI); return static_cast(sqrt((1.0 - x) * (1.0 - x) + y * y)); } diff --git a/packages/physics/include/physics/Integration.h b/packages/physics/include/physics/Integration.h index 30cc2b4a..0e8c1638 100644 --- a/packages/physics/include/physics/Integration.h +++ b/packages/physics/include/physics/Integration.h @@ -1,8 +1,8 @@ #pragma once #include "logging/Log.h" -#include "Constants.h" -#include "Algorithm.h" + +#include "math/MathAlgorithm.h" namespace l { namespace physics { @@ -167,10 +167,10 @@ namespace physics { V freq = 2.25; V gravity = - 1.0 / (1 + pow(r * freq, power)); - V modulation = (cos(l::constants::pi * (1 + 2 * r * 0.1 * freq)) * 0.25 + 0.75); + V modulation = (cos(l::math::constants::PI * (1 + 2 * r * 0.1 * freq)) * 0.25 + 0.75); V cutoff = (1.0 - exp(-r)); - //V mod = cos(l::constants::pi * (1.0 + 2 * distance * freq)) * 0.5 + 0.5; + //V mod = cos(l::constants::PI * (1.0 + 2 * distance * freq)) * 0.5 + 0.5; //return lambda * mod; return gravity * modulation * cutoff; @@ -180,7 +180,7 @@ namespace physics { V computeForceLambda2(V r, V forceConstant, V power) { V freq = 1.0; V gravity = -power * forceConstant / (pow(1 + r, power)); - V modulation = (cos(l::constants::pi * (1 + 2 * r * freq * power)) * 0.25 + 0.75); + V modulation = (cos(l::math::constants::PI * (1 + 2 * r * freq * power)) * 0.25 + 0.75); V cutoff = (1.0 - exp(-r)); return gravity * modulation * cutoff; @@ -231,12 +231,12 @@ namespace physics { V f = abs(computeForceLambda(r, rDesired, forceConstant, type)); if (f >= 0.00001) { - auto x1 = l::algorithm::solve_quad_eq(m1 * 2.0 * v1 / f, -2.0 * m1 * s / f); + auto x1 = l::math::algorithm::solve_quad_eq(m1 * 2.0 * v1 / f, -2.0 * m1 * s / f); if (x1) { auto dt = x1->first > 0.0 ? x1->first : x1->second; v1 += f * dt / m1; } - auto x2 = l::algorithm::solve_quad_eq(m2 * 2.0 * v2 / f, -2.0 * m2 * s / f); + auto x2 = l::math::algorithm::solve_quad_eq(m2 * 2.0 * v2 / f, -2.0 * m2 * s / f); if (x2) { auto dt = x2->first > 0.0 ? x2->first : x2->second; v2 += f * dt / m2; diff --git a/packages/physics/include/physics/LinkedList.h b/packages/physics/include/physics/LinkedList.h index dfbef767..4ee57b5b 100644 --- a/packages/physics/include/physics/LinkedList.h +++ b/packages/physics/include/physics/LinkedList.h @@ -6,7 +6,7 @@ #include #include -#include "Algorithm.h" +#include "math/MathAlgorithm.h" namespace l::physics { @@ -96,7 +96,7 @@ class LinkedElements { LinkedElement e; e.mData = id; - uint32_t index = algorithm::binary_search(mElements, e, fromIndex - 10, fromIndex + 10); + uint32_t index = math::algorithm::binary_search(mElements, e, fromIndex - 10, fromIndex + 10); while (fromIndex < mElementsSize && mElements.at(fromIndex).mId != id) { fromIndex++; diff --git a/packages/physics/include/physics/PhysicsAll.h b/packages/physics/include/physics/PhysicsAll.h index 7bd5caf7..9b4d1571 100644 --- a/packages/physics/include/physics/PhysicsAll.h +++ b/packages/physics/include/physics/PhysicsAll.h @@ -1,13 +1,9 @@ #pragma once -#include "Algorithm.h" #include "BoundaryList.h" #include "BoxSweep.h" -#include "Constants.h" #include "CurveBezier.h" #include "CurveNurbs.h" #include "Integration.h" -#include "MathFunc.h" -#include "MathSmooth.h" #include "Octree.h" #include "VecX.h" diff --git a/packages/physics/source/common/BoxSweep.cpp b/packages/physics/source/common/BoxSweep.cpp index 8caec6c0..4b084da9 100644 --- a/packages/physics/source/common/BoxSweep.cpp +++ b/packages/physics/source/common/BoxSweep.cpp @@ -2,6 +2,7 @@ #include "physics/BoxSweep.h" #include "physics/VecX.h" +#include "math/MathAlgorithm.h" #include @@ -111,7 +112,7 @@ void BoxSweep::update() { }; auto updatePair = [&](uint32_t id0, uint32_t id1, bool add) { - auto id = l::algorithm::pairIndex32(id0, id1); + auto id = l::math::algorithm::pairIndex32(id0, id1); if (add) { if (IsOverlapping(CO_AXIS_Y, mBoxes.at(id0), mBoxes.at(id1))) { mOverlapPairs.emplace(id); diff --git a/packages/physics/tests/common/IntegrationTest.cpp b/packages/physics/tests/common/IntegrationTest.cpp index b895188a..e8453a6e 100644 --- a/packages/physics/tests/common/IntegrationTest.cpp +++ b/packages/physics/tests/common/IntegrationTest.cpp @@ -1,7 +1,8 @@ #include "physics/Integration.h" #include "physics/VecX.h" -#include "physics/Algorithm.h" + +#include "math/MathAlgorithm.h" #include "testing/Test.h" @@ -11,7 +12,7 @@ using namespace l; TEST(Integration, Bisect) { - double x = algorithm::bisect(0.2, 5.0, EPSILON, 10, [](double x) -> double { + double x = math::algorithm::bisect(0.2, 5.0, EPSILON, 10, [](double x) -> double { return -1.0 + (x - 1.0) * (x - 1.0); }); diff --git a/packages/physics/tests/common/PoleResolverTest.cpp b/packages/physics/tests/common/PoleResolverTest.cpp index e5b92d07..1797e92a 100644 --- a/packages/physics/tests/common/PoleResolverTest.cpp +++ b/packages/physics/tests/common/PoleResolverTest.cpp @@ -1,7 +1,6 @@ #include "physics/Integration.h" #include "physics/VecX.h" -#include "physics/Algorithm.h" #include "testing/Test.h" diff --git a/packages/rendering/include/rendering/GeometryManip.h b/packages/rendering/include/rendering/GeometryManip.h index 1792040e..de321c75 100644 --- a/packages/rendering/include/rendering/GeometryManip.h +++ b/packages/rendering/include/rendering/GeometryManip.h @@ -3,7 +3,6 @@ #include "logging/LoggingAll.h" #include "physics/GridMap.h" #include "physics/VecExt.h" -#include "physics/Algorithm.h" #include "various/linmathext.h" #include diff --git a/packages/rendering/source/common/FPSInterface.cpp b/packages/rendering/source/common/FPSInterface.cpp index 1df6241b..7969a0be 100644 --- a/packages/rendering/source/common/FPSInterface.cpp +++ b/packages/rendering/source/common/FPSInterface.cpp @@ -2,7 +2,7 @@ #include "logging/LoggingAll.h" #include "memory/Containers.h" -#include "physics/Constants.h" +#include "math/MathConstants.h" #include "logging/Macro.h" #include @@ -170,7 +170,7 @@ namespace l { if (mWindow) { auto [x, y] = mWindow->GetMouseChange(); auto [width, height] = mWindow->GetFrameBufferSize(); - float f = mSensitivity * 2.0f * constants::pi_f / static_cast(width); + float f = mSensitivity * 2.0f * math::constants::PI_f / static_cast(width); mTurnAngle += x * f; mTiltAngle += y * f; if (fabs(x) > 0.0001f && fabs(y) > 0.0001f) { @@ -178,11 +178,11 @@ namespace l { } } - if (mTiltAngle < -constants::pi_f / 2.0f + 0.001f) { - mTiltAngle = -constants::pi_f / 2.0f + 0.001f; + if (mTiltAngle < -math::constants::PI_f / 2.0f + 0.001f) { + mTiltAngle = -math::constants::PI_f / 2.0f + 0.001f; } - if (mTiltAngle > constants::pi_f / 2.0f - 0.001f) { - mTiltAngle = constants::pi_f / 2.0f - 0.001f; + if (mTiltAngle > math::constants::PI_f / 2.0f - 0.001f) { + mTiltAngle = math::constants::PI_f / 2.0f - 0.001f; } // Open Gl resource diff --git a/packages/rendering/source/common/GLFWRenderVAO.cpp b/packages/rendering/source/common/GLFWRenderVAO.cpp index a6b0c864..b844dca9 100644 --- a/packages/rendering/source/common/GLFWRenderVAO.cpp +++ b/packages/rendering/source/common/GLFWRenderVAO.cpp @@ -5,6 +5,7 @@ #include "physics/GridMap.h" #include "rendering/GeometryManip.h" #include "rendering/DataConversion.h" +#include "math/MathAlgorithm.h" #include "ufbx/ufbx.h" #include "openfbx/src/ofbx.h" @@ -327,7 +328,7 @@ namespace l { l::rendering::FindMergableIndices<3>(vertices2, 0.000001f, [&](uint32_t i, uint32_t j) { - auto id = l::algorithm::pairIndex32(i, j); + auto id = l::math::algorithm::pairIndex32(i, j); verticePairs.emplace(id); }); From 76b142d587c853d60205c2babb916f20c4cfc710 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Wed, 4 Sep 2024 07:34:40 +0200 Subject: [PATCH 108/125] Fix input bounds and clamp on get input. Fix all node operations. Tidy up. --- packages/math/include/math/MathFunc.h | 7 + .../nodegraph/include/nodegraph/NodeGraph.h | 12 +- .../include/nodegraph/NodeGraphOperations.h | 169 ++++++------ .../include/nodegraph/NodeGraphSchema.h | 6 +- .../nodegraph/source/common/NodeGraph.cpp | 78 +++++- .../source/common/NodeGraphOperations.cpp | 248 +++++++++++------- .../source/common/NodeGraphSchema.cpp | 11 +- .../tests/common/NodeGraphSchemaTest.cpp | 14 +- .../rendering/source/common/ImageSupport.cpp | 10 +- .../rendering/source/common/ui/UIVisitors.cpp | 3 +- 10 files changed, 351 insertions(+), 207 deletions(-) diff --git a/packages/math/include/math/MathFunc.h b/packages/math/include/math/MathFunc.h index e7e56345..7dfa9795 100644 --- a/packages/math/include/math/MathFunc.h +++ b/packages/math/include/math/MathFunc.h @@ -11,6 +11,13 @@ namespace l::math::functions { + template + void swap(T& val1, T& val2) { + T tmp = std::move(val1); + val1 = std::move(val2); + val2 = std::move(tmp); + } + template T min(T val1, T val2) { return val1 < val2 ? val1 : val2; diff --git a/packages/nodegraph/include/nodegraph/NodeGraph.h b/packages/nodegraph/include/nodegraph/NodeGraph.h index 486b567b..b42a996f 100644 --- a/packages/nodegraph/include/nodegraph/NodeGraph.h +++ b/packages/nodegraph/include/nodegraph/NodeGraph.h @@ -28,10 +28,12 @@ namespace l::nodegraph { }; enum class InputBound { + INPUT_DONTCHANGE, INPUT_UNBOUNDED, INPUT_0_TO_1, INPUT_NEG_1_POS_1, - INPUT_0_100 + INPUT_0_100, + INPUT_CUSTOM, }; bool IsValidInOutNum(int8_t inoutNum, size_t inoutSize); @@ -55,6 +57,9 @@ namespace l::nodegraph { struct NodeGraphInput { Input mInput; InputType mInputType = InputType::INPUT_EMPTY; + + float mBoundMin = -FLT_MAX; + float mBoundMax = FLT_MAX; InputBound mInputBound = InputBound::INPUT_UNBOUNDED; int8_t mInputFromOutputChannel = 0; @@ -90,7 +95,7 @@ namespace l::nodegraph { virtual int8_t GetNumOutputs(); virtual int8_t GetNumConstants(); - virtual float& Get(int8_t outputChannel); + virtual float& GetOutput(int8_t outputChannel); virtual float GetInput(int8_t inputChannel); virtual std::string_view GetName(); @@ -106,6 +111,7 @@ namespace l::nodegraph { virtual bool SetInput(int8_t inputChannel, float constant); virtual bool SetInput(int8_t inputChannel, float* floatPtr); + virtual bool SetInputBound(int8_t inputChannel, InputBound bound = InputBound::INPUT_DONTCHANGE, float boundMin = 0.0f, float boundMax = 0.0f); virtual bool RemoveInput(void* source); virtual bool IsDataVisible(int8_t num); @@ -278,7 +284,7 @@ namespace l::nodegraph { void SetOutput(int8_t outputChannel, NodeGraphBase& source, int8_t sourceOutputChannel); void SetOutput(int8_t outputChannel, NodeGraphGroup& source, int8_t sourceOutputChannel); - float Get(int8_t outputChannel); + float GetOutput(int8_t outputChannel); NodeGraphBase& GetInputNode(); NodeGraphBase& GetOutputNode(); diff --git a/packages/nodegraph/include/nodegraph/NodeGraphOperations.h b/packages/nodegraph/include/nodegraph/NodeGraphOperations.h index fcebea1c..806da5de 100644 --- a/packages/nodegraph/include/nodegraph/NodeGraphOperations.h +++ b/packages/nodegraph/include/nodegraph/NodeGraphOperations.h @@ -17,39 +17,22 @@ namespace l::nodegraph { /* Mathematical operations */ + /*********************************************************************/ class GraphSourceConstants : public NodeGraphOp { public: GraphSourceConstants(NodeGraphBase* node, int32_t mode) : NodeGraphOp(node, 0, 4, 4), mMode(mode) - { - switch (mode) { - case 0: - mMax = 1.0f; - mMin = 0.0f; - break; - case 1: - mMax = 1.0f; - mMin = -1.0f; - break; - case 2: - mMax = 100.0f; - mMin = 0.0f; - break; - default: - mMax = 3.402823466e+38F; - mMin = -3.402823466e+38F; - break; - } - } + {} virtual ~GraphSourceConstants() = default; + virtual void Reset(); virtual void Process(std::vector& inputs, std::vector& outputs) override; virtual void Tick(float, float) override; - std::string_view GetName() override; - bool IsDataVisible(int8_t) override; - bool IsDataEditable(int8_t) override; + virtual std::string_view GetName() override; + virtual bool IsDataVisible(int8_t) override; + virtual bool IsDataEditable(int8_t) override; protected: int32_t mMode; @@ -57,6 +40,7 @@ namespace l::nodegraph { float mMin = 0.0f; }; + /*********************************************************************/ class GraphSourceTime : public NodeGraphOp { public: GraphSourceTime(NodeGraphBase* node) : @@ -69,36 +53,39 @@ namespace l::nodegraph { virtual void Process(std::vector& inputs, std::vector& outputs) override; virtual void Tick(float, float) override; - void Reset() override; - std::string_view GetOutputName(int8_t outputChannel); - std::string_view GetName() override; + virtual void Reset() override; + virtual std::string_view GetOutputName(int8_t outputChannel); + virtual std::string_view GetName() override; protected: float mAudioTime = 0.0f; float mFrameTime = 0.0f; }; + /*********************************************************************/ class GraphSourceSine : public NodeGraphOp { public: GraphSourceSine(NodeGraphBase* node) : NodeGraphOp(node, 5, 3) {} - std::string defaultInStrings[5] = { "Time", "Freq Hz", "Freq Mod", "Phase Mod", "Reset"}; - std::string defaultOutStrings[3] = { "Sine", "Phase", "Phase Mod"}; + std::string defaultInStrings[5] = { "Note", "Vol", "Vib", "Mod", "Reset"}; + std::string defaultOutStrings[3] = { "Sine", "Phase", "Ph. Mod"}; virtual ~GraphSourceSine() = default; - void Process(std::vector& inputs, std::vector& outputs) override; - void Reset() override; - std::string_view GetInputName(int8_t inputChannel); - std::string_view GetOutputName(int8_t outputChannel); - std::string_view GetName() override; + virtual void Reset() override; + virtual void Process(std::vector& inputs, std::vector& outputs) override; + + virtual std::string_view GetInputName(int8_t inputChannel); + virtual std::string_view GetOutputName(int8_t outputChannel); + virtual std::string_view GetName() override; protected: float mPhase = 0.0f; }; + /*********************************************************************/ class GraphSourceKeyboard : public NodeGraphOp, public l::hid::INoteProcessor { public: GraphSourceKeyboard(NodeGraphBase* node, int32_t polyphony, l::hid::KeyState* keyState) : @@ -130,6 +117,7 @@ namespace l::nodegraph { l::hid::KeyboardPiano mKeyboard; }; + /*********************************************************************/ class GraphNumericAdd : public NodeGraphOp { public: GraphNumericAdd(NodeGraphBase* node) : @@ -137,11 +125,12 @@ namespace l::nodegraph { {} virtual ~GraphNumericAdd() = default; void Process(std::vector& inputs, std::vector& outputs) override; - std::string_view GetName() override { + virtual std::string_view GetName() override { return "Add"; } }; + /*********************************************************************/ class GraphNumericMultiply : public NodeGraphOp { public: GraphNumericMultiply(NodeGraphBase* node) : @@ -150,11 +139,12 @@ namespace l::nodegraph { virtual ~GraphNumericMultiply() = default; void Process(std::vector& inputs, std::vector& outputs) override; - std::string_view GetName() override { + virtual std::string_view GetName() override { return "Multiply"; } }; + /*********************************************************************/ class GraphNumericSubtract : public NodeGraphOp { public: GraphNumericSubtract(NodeGraphBase* node) : @@ -162,11 +152,12 @@ namespace l::nodegraph { {} virtual ~GraphNumericSubtract() = default; void Process(std::vector& inputs, std::vector& outputs) override; - std::string_view GetName() override { + virtual std::string_view GetName() override { return "Subtract"; } }; + /*********************************************************************/ class GraphNumericNegate : public NodeGraphOp { public: GraphNumericNegate(NodeGraphBase* node) : @@ -175,11 +166,12 @@ namespace l::nodegraph { virtual ~GraphNumericNegate() = default; void Process(std::vector& inputs, std::vector& outputs) override; - std::string_view GetName() override { + virtual std::string_view GetName() override { return "Negate"; } }; + /*********************************************************************/ class GraphNumericIntegral : public NodeGraphOp { public: GraphNumericIntegral(NodeGraphBase* node) : @@ -189,7 +181,7 @@ namespace l::nodegraph { virtual ~GraphNumericIntegral() = default; void Reset() override; void Process(std::vector& inputs, std::vector& outputs) override; - std::string_view GetName() override { + virtual std::string_view GetName() override { return "Integral"; } @@ -197,8 +189,44 @@ namespace l::nodegraph { float mOutput = 0.0f; }; + /*********************************************************************/ + class GraphNumericMultiply3 : public NodeGraphOp { + public: + GraphNumericMultiply3(NodeGraphBase* node) : + NodeGraphOp(node, 3, 1) + {} + + virtual ~GraphNumericMultiply3() = default; + void Process(std::vector& inputs, std::vector& outputs) override; + virtual std::string_view GetName() override { + return "Multiply3"; + } + }; + + /*********************************************************************/ + class GraphNumericMultiplyAndAdd : public NodeGraphOp { + public: + GraphNumericMultiplyAndAdd(NodeGraphBase* node) : + NodeGraphOp(node, 3, 1) + {} + + std::string defaultInStrings[3] = { "Factor 1", "Factor 2", "Term 1" }; + + virtual ~GraphNumericMultiplyAndAdd() = default; + void Process(std::vector& inputs, std::vector& outputs) override; + virtual std::string_view GetName() override { + return "Multiply & Add"; + } + + virtual std::string_view GetInputName(int8_t inputChannel) { + return defaultInStrings[inputChannel]; + } + }; + + /* Logical operations */ + /*********************************************************************/ class GraphLogicalAnd : public NodeGraphOp { public: GraphLogicalAnd(NodeGraphBase* node) : @@ -206,12 +234,13 @@ namespace l::nodegraph { {} virtual ~GraphLogicalAnd() = default; - void Process(std::vector& inputs, std::vector& outputs) override; + virtual void Process(std::vector& inputs, std::vector& outputs) override; std::string_view GetName() override { return "And"; } }; + /*********************************************************************/ class GraphLogicalOr : public NodeGraphOp { public: GraphLogicalOr(NodeGraphBase* node) : @@ -219,12 +248,13 @@ namespace l::nodegraph { {} virtual ~GraphLogicalOr() = default; - void Process(std::vector& inputs, std::vector& outputs) override; + virtual void Process(std::vector& inputs, std::vector& outputs) override; std::string_view GetName() override { return "Or"; } }; + /*********************************************************************/ class GraphLogicalXor : public NodeGraphOp { public: GraphLogicalXor(NodeGraphBase* node) : @@ -232,14 +262,15 @@ namespace l::nodegraph { {} virtual ~GraphLogicalXor() = default; - void Process(std::vector& inputs, std::vector& outputs) override; - std::string_view GetName() override { + virtual void Process(std::vector& inputs, std::vector& outputs) override; + virtual std::string_view GetName() override { return "Xor"; } }; /* Stateful filtering operations */ + /*********************************************************************/ class GraphFilterLowpass : public NodeGraphOp { public: std::string defaultInStrings[3] = { "Cutoff", "Resonance", "Data"}; @@ -250,18 +281,18 @@ namespace l::nodegraph { {} virtual ~GraphFilterLowpass() = default; - void Reset() override; - void Process(std::vector& inputs, std::vector& outputs) override; + virtual void Reset() override; + virtual void Process(std::vector& inputs, std::vector& outputs) override; - std::string_view GetInputName(int8_t inputChannel) { + virtual std::string_view GetInputName(int8_t inputChannel) { return defaultInStrings[inputChannel]; } - std::string_view GetOutputName(int8_t outputChannel) { + virtual std::string_view GetOutputName(int8_t outputChannel) { return defaultOutStrings[outputChannel]; } - std::string_view GetName() override { + virtual std::string_view GetName() override { return "Lowpass"; } protected: @@ -269,26 +300,20 @@ namespace l::nodegraph { float mState1 = 0.0f; }; + /*********************************************************************/ class GraphFilterEnvelope : public NodeGraphOp { public: - std::string defaultInStrings[5] = { "Note", "Attack ms", "Release ms", "Note Fade"}; - std::string defaultOutStrings[2] = { "Note", "Gain"}; + std::string defaultInStrings[5] = { "Note", "Attack", "Release", "Fade"}; + std::string defaultOutStrings[2] = { "Note", "Volume"}; GraphFilterEnvelope(NodeGraphBase* node) : NodeGraphOp(node, 4, 2) {} virtual ~GraphFilterEnvelope() = default; - void Reset() override; - void Process(std::vector& inputs, std::vector& outputs) override; - virtual void Tick(float, float) { - if (!mInited) { - mNode->SetInput(1, 50.0f); - mNode->SetInput(2, 50.0f); - mNode->SetInput(3, 0.1f); - mInited = true; - } - } + virtual void Reset() override; + virtual void Process(std::vector& inputs, std::vector& outputs) override; + virtual void Tick(float, float) {} std::string_view GetInputName(int8_t inputChannel) { return defaultInStrings[inputChannel]; @@ -310,12 +335,13 @@ namespace l::nodegraph { return "Envelope"; } protected: - bool mInited = false; uint32_t mFrameCount = 0; float mEnvelopeTarget = 0.0f; float mNote = 0.0f; float mEnvelope = 0.0f; }; + + /*********************************************************************/ class GraphOutputDebug : public NodeGraphOp { public: GraphOutputDebug(NodeGraphBase* node, int32_t numValueDisplays) : @@ -333,6 +359,7 @@ namespace l::nodegraph { } }; + /*********************************************************************/ class GraphOutputSpeaker : public NodeGraphOp { public: std::string defaultOutStrings[2] = { "Left", "Right"}; @@ -349,7 +376,6 @@ namespace l::nodegraph { virtual ~GraphOutputSpeaker() = default; void Process(std::vector& inputs, std::vector& outputs) override; - virtual void Tick(float time, float elapsed) override; std::string_view GetOutputName(int8_t outputChannel) { return defaultOutStrings[outputChannel]; @@ -364,6 +390,7 @@ namespace l::nodegraph { int32_t mCurrentStereoPosition; }; + /*********************************************************************/ class GraphEffectReverb1 : public NodeGraphOp { public: std::string defaultInStrings[11] = { "In 1", "In 2", "Mix", "Attenuation", "Room Size", "Delay 1", "Feedback 1", "Delay 2", "Feedback 2", "Delay 3", "Feedback 3" }; @@ -387,8 +414,8 @@ namespace l::nodegraph { virtual ~GraphEffectReverb1() = default; - void Process(std::vector&inputs, std::vector&outputs) override; - virtual void Tick(float time, float elapsed) override; + virtual void Reset() override; + virtual void Process(std::vector&inputs, std::vector&outputs) override; virtual bool IsDataVisible(int8_t num) override { return num >= 2 ? true : false; @@ -411,13 +438,6 @@ namespace l::nodegraph { return "Reverb"; } - float max(float value, float max) { - return value > max ? value : max; - } - - float min(float value, float min) { - return value < min ? value : min; - } protected: bool mInited = false; std::vector buf0; @@ -434,6 +454,7 @@ namespace l::nodegraph { }; + /*********************************************************************/ class GraphEffectReverb2 : public NodeGraphOp { public: std::string defaultInStrings[12] = { "In 1", "In 2", "Mix", "Feedback", "Room Size", "Width", "First tap", "Longest tap", "Num taps", "Tap bulge", "Filter cutoff", "Filter res"}; @@ -459,8 +480,8 @@ namespace l::nodegraph { } virtual ~GraphEffectReverb2() = default; + virtual void Reset() override; virtual void Process(std::vector& inputs, std::vector& outputs) override; - virtual void Tick(float time, float elapsed) override; virtual bool IsDataVisible(int8_t num) override { return num >= 2 ? true : false; @@ -479,12 +500,6 @@ namespace l::nodegraph { std::string_view GetName() override { return "Reverb"; } - float max(float value, float max) { - return value > max ? value : max; - } - float min(float value, float min) { - return value < min ? value : min; - } protected: bool mInited = false; diff --git a/packages/nodegraph/include/nodegraph/NodeGraphSchema.h b/packages/nodegraph/include/nodegraph/NodeGraphSchema.h index 64ed4558..9930d8f7 100644 --- a/packages/nodegraph/include/nodegraph/NodeGraphSchema.h +++ b/packages/nodegraph/include/nodegraph/NodeGraphSchema.h @@ -48,11 +48,11 @@ namespace l::nodegraph { RegisterNodeType("Logic", 101, "Or"); RegisterNodeType("Logic", 102, "Xor"); RegisterNodeType("Filter", 150, "Lowpass Filter"); - RegisterNodeType("Filter", 151, "Envelope"); RegisterNodeType("Output", 200, "Value Debug"); RegisterNodeType("Output", 201, "Speaker"); - RegisterNodeType("Effect", 250, "Reverb1"); - RegisterNodeType("Effect", 251, "Reverb2"); + RegisterNodeType("Effect", 250, "Envelope"); + RegisterNodeType("Effect", 251, "Reverb1"); + RegisterNodeType("Effect", 252, "Reverb2"); } ~NodeGraphSchema() = default; diff --git a/packages/nodegraph/source/common/NodeGraph.cpp b/packages/nodegraph/source/common/NodeGraph.cpp index c713fc46..0310b9b9 100644 --- a/packages/nodegraph/source/common/NodeGraph.cpp +++ b/packages/nodegraph/source/common/NodeGraph.cpp @@ -2,6 +2,8 @@ #include "logging/Log.h" +#include "math/MathFunc.h" + namespace l::nodegraph { bool IsValidInOutNum(int8_t inoutNum, size_t inoutSize) { @@ -85,7 +87,7 @@ namespace l::nodegraph { } } - float& NodeGraphBase::Get(int8_t outputChannel) { + float& NodeGraphBase::GetOutput(int8_t outputChannel) { return mOutputs.at(outputChannel).mOutput; } @@ -94,8 +96,12 @@ namespace l::nodegraph { } bool NodeGraphBase::ClearInput(int8_t inputChannel) { + ASSERT(inputChannel >= 0 && inputChannel < mInputs.size()); + if (!IsValidInOutNum(inputChannel, mInputs.size())) { + return false; + } auto& input = mInputs.at(inputChannel); - if (!IsValidInOutNum(inputChannel, mInputs.size()) || !input.HasInput()) { + if (!input.HasInput()) { return false; } input.mInputType = InputType::INPUT_EMPTY; @@ -105,6 +111,10 @@ namespace l::nodegraph { } bool NodeGraphBase::SetInput(int8_t inputChannel, NodeGraphBase& source, int8_t sourceOutputChannel) { + ASSERT(inputChannel >= 0 && inputChannel < mInputs.size()); + if (!IsValidInOutNum(inputChannel, mInputs.size())) { + return false; + } auto& input = mInputs.at(inputChannel); if (!IsValidInOutNum(sourceOutputChannel, source.mOutputs.size()) || !IsValidInOutNum(inputChannel, mInputs.size()) || @@ -118,6 +128,10 @@ namespace l::nodegraph { } bool NodeGraphBase::SetInput(int8_t inputChannel, NodeGraphGroup& source, int8_t sourceChannel) { + ASSERT(inputChannel >= 0 && inputChannel < mInputs.size()); + if (!IsValidInOutNum(inputChannel, mInputs.size())) { + return false; + } auto& input = mInputs.at(inputChannel); if (source.ContainsNode(GetId())) { if (!IsValidInOutNum(sourceChannel, source.GetInputNode().GetNumOutputs()) || @@ -142,31 +156,65 @@ namespace l::nodegraph { } bool NodeGraphBase::SetInput(int8_t inputChannel, float constant) { - ASSERT(inputChannel >= 0 && inputChannel < mInputCount + mConstantCount); - auto& input = mInputs.at(inputChannel); + ASSERT(inputChannel >= 0 && inputChannel < mInputs.size()); if (!IsValidInOutNum(inputChannel, mInputs.size())) { return false; } - + auto& input = mInputs.at(inputChannel); input.mInput.mInputFloatConstant = constant; input.mInputType = InputType::INPUT_CONSTANT; input.mInputFromOutputChannel = 0; + return true; } bool NodeGraphBase::SetInput(int8_t inputChannel, float* floatPtr) { - ASSERT(inputChannel >= 0 && inputChannel < mInputCount); - auto& input = mInputs.at(inputChannel); + ASSERT(inputChannel >= 0 && inputChannel < mInputs.size()); if (!IsValidInOutNum(inputChannel, mInputs.size())) { return false; } - + auto& input = mInputs.at(inputChannel); input.mInput.mInputFloat = floatPtr; input.mInputType = InputType::INPUT_VALUE; input.mInputFromOutputChannel = 0; return true; } + bool NodeGraphBase::SetInputBound(int8_t inputChannel, InputBound bound, float boundMin, float boundMax) { + ASSERT(inputChannel >= 0 && inputChannel < mInputs.size()); + if (!IsValidInOutNum(inputChannel, mInputs.size())) { + return false; + } + auto& input = mInputs.at(inputChannel); + input.mInputBound = bound; + switch (bound) { + case InputBound::INPUT_0_TO_1: + input.mBoundMin = 0.0f; + input.mBoundMax = 1.0f; + break; + case InputBound::INPUT_NEG_1_POS_1: + input.mBoundMin = -1.0f; + input.mBoundMax = 1.0f; + break; + case InputBound::INPUT_0_100: + input.mBoundMin = 0.0f; + input.mBoundMax = 100.0f; + break; + case InputBound::INPUT_CUSTOM: + input.mBoundMin = boundMin; + input.mBoundMax = boundMax; + break; + case InputBound::INPUT_UNBOUNDED: + input.mBoundMin = -FLT_MAX; + input.mBoundMax = FLT_MAX; + break; + case InputBound::INPUT_DONTCHANGE: + break; + } + return true; + } + + bool NodeGraphBase::RemoveInput(void* source) { int32_t sourceRemoved = 0; for (auto& it : mInputs) { @@ -207,6 +255,7 @@ namespace l::nodegraph { mOutputs.at(outputChannel).mName = name; } + void NodeGraphOp::SetNumInputs(int8_t numInputs) { mNumInputs = numInputs; } @@ -283,22 +332,23 @@ namespace l::nodegraph { } float NodeGraphInput::Get() { + float value = 0.0f; switch (mInputType) { case InputType::INPUT_NODE: if (mInput.mInputNode != nullptr) { - return mInput.mInputNode->Get(mInputFromOutputChannel); + value = mInput.mInputNode->GetOutput(mInputFromOutputChannel); } break; case InputType::INPUT_CONSTANT: - return mInput.mInputFloatConstant; + value = mInput.mInputFloatConstant; break; case InputType::INPUT_VALUE: - return *mInput.mInputFloat; + value = *mInput.mInputFloat; break; case InputType::INPUT_EMPTY: break; } - return 0.0f; + return l::math::functions::clamp(value, mBoundMin, mBoundMax); } void NodeGraphGroup::SetNumInputs(int8_t numInputs) { @@ -337,8 +387,8 @@ namespace l::nodegraph { mOutputNode.SetOutputName(outputChannel, source.GetOutputNode().GetOutputName(sourceOutputChannel)); } - float NodeGraphGroup::Get(int8_t outputChannel) { - return mOutputNode.Get(outputChannel); + float NodeGraphGroup::GetOutput(int8_t outputChannel) { + return mOutputNode.GetOutput(outputChannel); } NodeGraphBase& NodeGraphGroup::GetInputNode() { diff --git a/packages/nodegraph/source/common/NodeGraphOperations.cpp b/packages/nodegraph/source/common/NodeGraphOperations.cpp index b824ee61..c7f3f32d 100644 --- a/packages/nodegraph/source/common/NodeGraphOperations.cpp +++ b/packages/nodegraph/source/common/NodeGraphOperations.cpp @@ -3,18 +3,43 @@ #include "logging/Log.h" #include "audio/AudioUtils.h" +#include "math/MathFunc.h" + #include namespace l::nodegraph { /* Mathematical operations */ + /*********************************************************************/ + void GraphSourceConstants::Reset() { + switch (mMode) { + case 0: + for (int8_t i = 0; i < 4; i++) { + mNode->SetInputBound(i, InputBound::INPUT_0_TO_1); + } + break; + case 1: + for (int8_t i = 0; i < 4; i++) { + mNode->SetInputBound(i, InputBound::INPUT_NEG_1_POS_1); + } + break; + case 2: + for (int8_t i = 0; i < 4; i++) { + mNode->SetInputBound(i, InputBound::INPUT_0_100); + } + break; + default: + for (int8_t i = 0; i < 4; i++) { + mNode->SetInputBound(i, InputBound::INPUT_UNBOUNDED); + } + break; + } + } + void GraphSourceConstants::Process(std::vector& inputs, std::vector& outputs) { for (int8_t i = 0; i < mNumOutputs; i++) { - float val = inputs.at(i).Get(); - val = val > mMax ? mMax : val < mMin ? mMin : val; - inputs.at(i).mInput.mInputFloatConstant = val; - outputs.at(i).mOutput = val; + outputs.at(i).mOutput = inputs.at(i).Get(); } } @@ -44,6 +69,7 @@ namespace l::nodegraph { return true; } + /*********************************************************************/ void GraphSourceTime::Process(std::vector&, std::vector& outputs) { float rate = 44100.0f; float phaseChange = 1.0f / rate; @@ -70,20 +96,37 @@ namespace l::nodegraph { return "Time"; } + /*********************************************************************/ + void GraphSourceSine::Reset() { + // { "Freq Hz", "Freq Mod", "Phase Mod", "Reset"}; + mPhase = 0.0f; + mNode->SetInput(0, 0.0f); + mNode->SetInput(1, 0.0f); + mNode->SetInput(2, 0.0f); + mNode->SetInput(3, 0.0f); + mNode->SetInput(4, 0.0f); + mNode->SetInputBound(0, InputBound::INPUT_UNBOUNDED); + mNode->SetInputBound(1, InputBound::INPUT_0_TO_1); + mNode->SetInputBound(2, InputBound::INPUT_UNBOUNDED); + mNode->SetInputBound(3, InputBound::INPUT_UNBOUNDED); + mNode->SetInputBound(4, InputBound::INPUT_0_TO_1); + } + + void GraphSourceSine::Process(std::vector& inputs, std::vector& outputs) { - //float time = inputs.at(0).Get(); - float freq = inputs.at(1).Get(); + float freq = inputs.at(0).Get(); + float volume = inputs.at(1).Get(); float fMod = 1.0f + inputs.at(2).Get(); float pMod = inputs.at(3).Get(); float reset = inputs.at(4).Get(); if (freq == 0.0f) { - Reset(); + mPhase = 0.0f; outputs.at(0).mOutput = 0.0f; return; } if (reset > 0.5f) { - Reset(); + mPhase = 0.0f; } float deltaTime = 1.0f / 44100.0f; @@ -95,15 +138,11 @@ namespace l::nodegraph { float phaseMod = mPhase + pMod; phaseMod -= floorf(phaseMod); - outputs.at(0).mOutput = sinf(3.141529f * (mPhase + phaseMod)); + outputs.at(0).mOutput = volume * sinf(3.141529f * (mPhase + phaseMod)); outputs.at(1).mOutput = mPhase; outputs.at(2).mOutput = phaseMod; } - void GraphSourceSine::Reset() { - mPhase = 0.0f; - } - std::string_view GraphSourceSine::GetInputName(int8_t inputChannel) { return defaultInStrings[inputChannel]; } @@ -116,6 +155,7 @@ namespace l::nodegraph { return "Sine"; } + /*********************************************************************/ void GraphSourceKeyboard::Process(std::vector& inputs, std::vector& outputs) { for (size_t i = 0; i < inputs.size();i++) { outputs.at(i).mOutput = inputs.at(i).Get(); @@ -200,23 +240,27 @@ namespace l::nodegraph { return lowestCountIndex; } - + /*********************************************************************/ void GraphNumericAdd::Process(std::vector& inputs, std::vector& outputs) { outputs.at(0).mOutput = inputs.at(0).Get() + inputs.at(1).Get(); } + /*********************************************************************/ void GraphNumericMultiply::Process(std::vector& inputs, std::vector& outputs) { outputs.at(0).mOutput = inputs.at(0).Get() * inputs.at(1).Get(); } + /*********************************************************************/ void GraphNumericSubtract::Process(std::vector& inputs, std::vector& outputs) { outputs.at(0).mOutput = inputs.at(0).Get() - inputs.at(1).Get(); } + /*********************************************************************/ void GraphNumericNegate::Process(std::vector& inputs, std::vector& outputs) { outputs.at(0).mOutput = -inputs.at(0).Get(); } + /*********************************************************************/ void GraphNumericIntegral::Reset() { mOutput = 0.0f; } @@ -226,20 +270,33 @@ namespace l::nodegraph { outputs.at(0).mOutput = mOutput; } + /*********************************************************************/ + void GraphNumericMultiply3::Process(std::vector& inputs, std::vector& outputs) { + outputs.at(0).mOutput = inputs.at(0).Get() * inputs.at(1).Get() * inputs.at(2).Get(); + } + + /*********************************************************************/ + void GraphNumericMultiplyAndAdd::Process(std::vector& inputs, std::vector& outputs) { + outputs.at(0).mOutput = inputs.at(0).Get() * inputs.at(1).Get() + inputs.at(2).Get(); + } + /* Logical operations */ + /*********************************************************************/ void GraphLogicalAnd::Process(std::vector& inputs, std::vector& outputs) { bool input1 = inputs.at(0).Get() != 0.0f; bool input2 = inputs.at(1).Get() != 0.0f; outputs.at(0).mOutput = (input1 && input2) ? 1.0f : 0.0f; } + /*********************************************************************/ void GraphLogicalOr::Process(std::vector& inputs, std::vector& outputs) { bool input1 = inputs.at(0).Get() != 0.0f; bool input2 = inputs.at(1).Get() != 0.0f; outputs.at(0).mOutput = (input1 || input2) ? 1.0f : 0.0f; } + /*********************************************************************/ void GraphLogicalXor::Process(std::vector& inputs, std::vector& outputs) { bool input1 = inputs.at(0).Get() != 0.0f; bool input2 = inputs.at(1).Get() != 0.0f; @@ -248,15 +305,20 @@ namespace l::nodegraph { /* Stateful filtering operations */ + /*********************************************************************/ void GraphFilterLowpass::Reset() { mState0 = 0.0f; mState1 = 0.0f; + mNode->SetInput(1, 0.99f); + mNode->SetInput(2, 0.01f); + mNode->SetInputBound(1, InputBound::INPUT_0_TO_1); + mNode->SetInputBound(2, InputBound::INPUT_0_TO_1); } void GraphFilterLowpass::Process(std::vector& inputs, std::vector& outputs) { - float cutoff = inputs.at(0).Get(); - float resonance = 1.0f - inputs.at(1).Get(); - float inputValue = inputs.at(2).Get(); + float inputValue = inputs.at(0).Get(); + float cutoff = inputs.at(1).Get(); + float resonance = 1.0f - inputs.at(2).Get(); cutoff *= cutoff; float rc = 1.0f - resonance * cutoff; @@ -267,8 +329,15 @@ namespace l::nodegraph { outputs.at(0).mOutput = -mState1; } + /*********************************************************************/ void GraphFilterEnvelope::Reset() { mEnvelope = 0.0f; + mNode->SetInput(1, 50.0f); + mNode->SetInput(2, 50.0f); + mNode->SetInput(3, 0.1f); + mNode->SetInputBound(1, InputBound::INPUT_CUSTOM, 1.0f, 100000.0f); + mNode->SetInputBound(2, InputBound::INPUT_CUSTOM, 1.0f, 100000.0f); + mNode->SetInputBound(3, InputBound::INPUT_CUSTOM, 0.0001f, 1.0f); } void GraphFilterEnvelope::Process(std::vector& inputs, std::vector& outputs) { @@ -315,6 +384,7 @@ namespace l::nodegraph { outputs.at(1).mOutput = mEnvelope * mEnvelope; } + /*********************************************************************/ void GraphOutputSpeaker::Process(std::vector& inputs, std::vector&) { auto& buffer = mAudioStream->GetWriteBuffer(); buffer[mCurrentStereoPosition++] = inputs.at(0).Get(); @@ -322,33 +392,44 @@ namespace l::nodegraph { mCurrentStereoPosition %= mAudioStream->GetPartTotalSize(); } - void GraphOutputSpeaker::Tick(float, float) { + /*********************************************************************/ + void GraphEffectReverb1::Reset() { + // { "In 1", "In 2", "Mix", "Attenuation", "Room Size", "Delay 1", "Feedback 1", "Delay 2", "Feedback 2", "Delay 3", "Feedback 3" }; + + mNode->SetInput(2, 0.75f); + mNode->SetInput(3, 0.5f); + mNode->SetInput(4, 30.0f); + mNode->SetInput(5, 0.5f); + mNode->SetInput(6, 0.9f); + mNode->SetInput(7, 0.8f); + mNode->SetInput(8, 0.9f); + mNode->SetInput(9, 0.7f); + mNode->SetInput(10, 0.9f); + mNode->SetInputBound(2, InputBound::INPUT_0_TO_1); + mNode->SetInputBound(3, InputBound::INPUT_0_TO_1); + mNode->SetInputBound(4, InputBound::INPUT_CUSTOM, 0.2f, maxRoomSizeInMeters); + mNode->SetInputBound(5, InputBound::INPUT_0_TO_1); + mNode->SetInputBound(6, InputBound::INPUT_0_TO_1); + mNode->SetInputBound(7, InputBound::INPUT_0_TO_1); + mNode->SetInputBound(8, InputBound::INPUT_0_TO_1); + mNode->SetInputBound(9, InputBound::INPUT_0_TO_1); + mNode->SetInputBound(10, InputBound::INPUT_0_TO_1); } void GraphEffectReverb1::Process(std::vector&inputs, std::vector&outputs) { float wet = inputs.at(2).Get(); - fb = 0.33f * min(1.0f - inputs.at(3).Get(), 0.0f); + fb = 0.33f * (1.0f - inputs.at(3).Get()); float roomSize = inputs.at(4).Get(); - - if (roomSize > maxRoomSizeInMeters) { - roomSize = maxRoomSizeInMeters; - mNode->SetInput(3, maxRoomSizeInMeters); - } - else if (roomSize < 0.1f) { - roomSize = 0.1f; - mNode->SetInput(3, 0.1f); - } - uint32_t bufSizeLimit = GetFramesPerRoomSize(roomSize); d0 = inputs.at(5).Get(); - fb0 = 0.5f * 0.45f * max(inputs.at(6).Get(), 1); + fb0 = 0.5f * 0.5f * math::functions::max(inputs.at(6).Get(), 1.0f); d1 = inputs.at(7).Get(); - fb1 = 0.5f * 0.45f * max(inputs.at(8).Get(), 1); + fb1 = 0.5f * 0.5f * math::functions::max(inputs.at(8).Get(), 1.0f); d2 = inputs.at(9).Get(); - fb2 = 0.5f * 0.45f * max(inputs.at(10).Get(), 1); + fb2 = 0.5f * 0.5f * math::functions::max(inputs.at(10).Get(), 1.0f); float dry = 1.0f - wet; @@ -358,13 +439,11 @@ namespace l::nodegraph { float in0 = inputs.at(0).Get(); float in1 = inputs.at(1).Get(); - float out0 = fb0 * buf0[delay0] + fb1 * buf1[delay1] + fb2 * buf0[delay2]; - outputs[0].mOutput = in0 * dry + out0 * wet; - buf0[bufIndex] = (fb)*buf1[bufIndex] + in0 - out0; + outputs[0].mOutput = in0 * dry + (fb1 * buf1[delay1] + fb0 * buf0[delay0] + fb2 * buf0[delay2]) * wet; + outputs[1].mOutput = in1 * dry + (fb1 * buf0[delay1] + fb0 * buf1[delay0] + fb2 * buf1[delay2]) * wet; - float out1 = fb0 * buf1[delay0] + fb1 * buf0[delay1] + fb2 * buf1[delay2]; - outputs[1].mOutput = in1 * dry + out1 * wet; - buf1[bufIndex] = (fb)*buf0[bufIndex] + in1 - out1; + buf0[bufIndex] = fb * buf1[bufIndex] - fb1 * buf1[delay1] - fb0 * buf0[delay0] - fb2 * buf0[delay2] + in0; + buf1[bufIndex] = fb * buf0[bufIndex] - fb1 * buf0[delay1] - fb0 * buf1[delay0] - fb2 * buf1[delay2] + in1; bufIndex = (bufIndex + 1) % bufSizeLimit; @@ -373,32 +452,44 @@ namespace l::nodegraph { delay2 = (delay2 + 1) % bufSizeLimit; } - void GraphEffectReverb1::Tick(float, float) { - if (!mInited) { - mNode->SetInput(2, 0.75f); - mNode->SetInput(3, 0.5f); - mNode->SetInput(4, 30.0f); - mNode->SetInput(5, 0.5f); - mNode->SetInput(6, 0.9f); - mNode->SetInput(7, 0.8f); - mNode->SetInput(8, 0.9f); - mNode->SetInput(9, 0.7f); - mNode->SetInput(10, 0.9f); - mInited = true; - } + /*********************************************************************/ + + void GraphEffectReverb2::Reset() { + // { "In 1", "In 2", "Mix", "Feedback", "Room Size", "Width", "First tap", "Longest tap", "Num taps", "Tap bulge", "Filter cutoff", "Filter res"}; + + mNode->SetInput(2, 0.3f); + mNode->SetInput(3, 0.5f); + mNode->SetInput(4, 30.0f); + mNode->SetInput(5, 0.5f); + mNode->SetInput(6, 0.1f); + mNode->SetInput(7, 0.8f); + mNode->SetInput(8, 5.0f); + mNode->SetInput(9, 0.7f); + mNode->SetInput(10, 0.95f); + mNode->SetInput(11, 0.01f); + mNode->SetInputBound(2, InputBound::INPUT_0_TO_1); + mNode->SetInputBound(3, InputBound::INPUT_0_TO_1); + mNode->SetInputBound(4, InputBound::INPUT_CUSTOM, 1.0f, 334.0f); + mNode->SetInputBound(5, InputBound::INPUT_0_TO_1); + mNode->SetInputBound(6, InputBound::INPUT_CUSTOM, 0.0f, 10.0f); + mNode->SetInputBound(7, InputBound::INPUT_CUSTOM, 0.0f, 10.0f); + mNode->SetInputBound(8, InputBound::INPUT_CUSTOM, 1.0f, 30.0f); + mNode->SetInputBound(9, InputBound::INPUT_CUSTOM, 1.0f, 10.0f); + mNode->SetInputBound(10, InputBound::INPUT_CUSTOM, 0.001f, 0.999f); + mNode->SetInputBound(11, InputBound::INPUT_CUSTOM, 0.001f, 0.999f); } void GraphEffectReverb2::Process(std::vector& inputs, std::vector& outputs) { float wet = inputs.at(2).Get(); - float reverbFeedback = min(inputs.at(3).Get(), 1.0f); + float reverbFeedback = inputs.at(3).Get(); float roomSize = inputs.at(4).Get(); - float stereoWidth = min(inputs.at(5).Get(), 1.0f); - float earliestDelay = min(inputs.at(6).Get(), 5.0f); - float longestDelay = min(inputs.at(7).Get(), 5.0f); - float numDelays = max(inputs.at(8).Get(), 1.0f); - float tapBulge = 1.0f + 9.0f * min(inputs.at(9).Get(), 1.0f); - //float cutoff = min(inputs.at(10).Get(), 1.0f); - //float res = min(inputs.at(11).Get(), 1.0f); + float stereoWidth = inputs.at(5).Get(); + float earliestDelay = inputs.at(6).Get(); + float longestDelay = inputs.at(7).Get(); + float numDelays = inputs.at(8).Get(); + float tapBulge = inputs.at(9).Get(); + float cutoff = inputs.at(10).Get(); + float res = inputs.at(11).Get(); if (roomSize > maxRoomSizeInMeters) { roomSize = maxRoomSizeInMeters; @@ -473,7 +564,7 @@ namespace l::nodegraph { // buffer blur of oldest sample plus next oldest sample with global attenuation and new input auto [reverb0, reverb1] = sample(bufRev0.data(), bufRev1.data(), earliestDelay, longestDelay, numDelays, reverbFeedback, stereoWidth); - /* + cutoff *= cutoff; float rc = 1.0f - res * cutoff; @@ -481,11 +572,9 @@ namespace l::nodegraph { mLP1 = rc * mLP1 + cutoff * mLP0; mLP2 = rc * mLP2 - cutoff * (mLP3 + reverb1); mLP3 = rc * mLP3 + cutoff * mLP2; - float lp0 = mLP1; - float lp1 = mLP3; - */ - bufRev0[bufIndex] = reverbFeedback * (bufRev0[bufIndex] + reverb0); - bufRev1[bufIndex] = reverbFeedback * (bufRev1[bufIndex] + reverb1); + + bufRev0[bufIndex] = reverbFeedback * (bufRev0[bufIndex] + mLP1); + bufRev1[bufIndex] = reverbFeedback * (bufRev1[bufIndex] + mLP3); bufIndex = (bufIndex + 1) % bufSizeLimit; @@ -505,34 +594,7 @@ namespace l::nodegraph { } - void GraphEffectReverb2::Tick(float, float) { - if (!mInited) { - mNode->SetInput(2, 0.3f); - mNode->SetInput(3, 0.5f); - mNode->SetInput(4, 30.0f); - mNode->SetInput(5, 0.5f); - mNode->SetInput(6, 0.1f); - mNode->SetInput(7, 0.8f); - mNode->SetInput(8, 5.0f); - mNode->SetInput(9, 0.7f); - mNode->SetInput(10, 0.95f); - mNode->SetInput(11, 0.01f); - - /* - float wet = inputs.at(2).Get(); - float reverbFeedback = min(inputs.at(3).Get(), 1.0f); - float roomSize = inputs.at(4).Get(); - float stereoWidth = min(inputs.at(5).Get(), 1.0f); - float earliestDelay = min(inputs.at(6).Get(), 1.0f); - float longestDelay = min(inputs.at(7).Get(), 1.0f); - float numDelays = max(inputs.at(8).Get(), 1.0f); - float tapBulge = 1.0f + 9.0f * min(inputs.at(9).Get(), 1.0f); - float cutoff = min(inputs.at(10).Get(), 1.0f); - float res = min(inputs.at(11).Get(), 1.0f); - */ - mInited = true; - } - } + /*********************************************************************/ } diff --git a/packages/nodegraph/source/common/NodeGraphSchema.cpp b/packages/nodegraph/source/common/NodeGraphSchema.cpp index 85994480..157e99d0 100644 --- a/packages/nodegraph/source/common/NodeGraphSchema.cpp +++ b/packages/nodegraph/source/common/NodeGraphSchema.cpp @@ -69,9 +69,6 @@ namespace l::nodegraph { case 150: node = mMainNodeGraph.NewNode(OutputType::Default); break; - case 151: - node = mMainNodeGraph.NewNode(OutputType::Default); - break; case 200: node = mMainNodeGraph.NewNode(OutputType::ExternalOutput, 1); break; @@ -79,9 +76,12 @@ namespace l::nodegraph { node = mMainNodeGraph.NewNode(OutputType::ExternalOutput, mAudioOutput); break; case 250: - node = mMainNodeGraph.NewNode(OutputType::Default); + node = mMainNodeGraph.NewNode(OutputType::Default); break; case 251: + node = mMainNodeGraph.NewNode(OutputType::Default); + break; + case 252: node = mMainNodeGraph.NewNode(OutputType::Default); break; default: @@ -91,6 +91,9 @@ namespace l::nodegraph { break; }; + if (node != nullptr) { + node->Reset(); + } return node == nullptr ? 0 : node->GetId(); } diff --git a/packages/nodegraph/tests/common/NodeGraphSchemaTest.cpp b/packages/nodegraph/tests/common/NodeGraphSchemaTest.cpp index 3f33f159..07d74c32 100644 --- a/packages/nodegraph/tests/common/NodeGraphSchemaTest.cpp +++ b/packages/nodegraph/tests/common/NodeGraphSchemaTest.cpp @@ -18,7 +18,7 @@ TEST(NodeGraph, BasicFunction) { node.SetInput(1, &in2); node.ProcessSubGraph(); - TEST_FUZZY(node.Get(0), 4.1f, 0.0001f, ""); + TEST_FUZZY(node.GetOutput(0), 4.1f, 0.0001f, ""); return 0; } @@ -42,7 +42,7 @@ TEST(NodeGraph, SimpleAddNetwork) { nodeFinal.SetInput(1, node2, 0); nodeFinal.ProcessSubGraph(); - TEST_FUZZY(nodeFinal.Get(0), 12.6f, 0.0001f, ""); + TEST_FUZZY(nodeFinal.GetOutput(0), 12.6f, 0.0001f, ""); return 0; } @@ -69,7 +69,7 @@ TEST(NodeGraph, BasicMathematicalOperations) { nodeOutput.SetInput(0, node3, 0); // - (in1 + in2) * in3 - in4 nodeOutput.ProcessSubGraph(); - TEST_FUZZY(nodeOutput.Get(0), -6.9f, 0.0001f, ""); + TEST_FUZZY(nodeOutput.GetOutput(0), -6.9f, 0.0001f, ""); return 0; } @@ -87,7 +87,7 @@ TEST(NodeGraph, NumericIntegral) { //LOG(LogInfo) << nodeIntegral.Get(0); } - TEST_FUZZY(nodeIntegral.Get(0), 0.00323272f, 0.0001f, ""); + TEST_FUZZY(nodeIntegral.GetOutput(0), 0.00323272f, 0.0001f, ""); return 0; } @@ -110,7 +110,7 @@ TEST(NodeGraph, FilterLowpass) { //LOG(LogInfo) << nodeLowpass.Get(0); } - TEST_FUZZY(nodeLowpass.Get(0), -0.448589f, 0.0001f, ""); + TEST_FUZZY(nodeLowpass.GetOutput(0), -0.448589f, 0.0001f, ""); return 0; } @@ -172,8 +172,8 @@ TEST(NodeGraph, GraphGroups) { // only update the last group/node and all dependent nodes will update in the graph to produce an output group2.ProcessSubGraph(); - float output1 = group2.Get(0); - float output2 = group2.Get(1); + float output1 = group2.GetOutput(0); + float output2 = group2.GetOutput(1); TEST_FUZZY(output1, 0.122880019f, 0.0000001, ""); TEST_FUZZY(output2, 0.0819200128f, 0.0000001, ""); diff --git a/packages/rendering/source/common/ImageSupport.cpp b/packages/rendering/source/common/ImageSupport.cpp index 6be2580e..ca89aed3 100644 --- a/packages/rendering/source/common/ImageSupport.cpp +++ b/packages/rendering/source/common/ImageSupport.cpp @@ -5,6 +5,8 @@ #include #include +#include "math/MathFunc.h" + namespace l::rendering { namespace image { @@ -40,8 +42,8 @@ namespace l::rendering { image_u8 new_image(new_width, new_height); - const uint32_t w = std::min(m_width, new_width); - const uint32_t h = std::min(m_height, new_height); + const uint32_t w = math::functions::min(m_width, new_width); + const uint32_t h = math::functions::min(m_height, new_height); for (uint32_t y = 0; y < h; y++) for (uint32_t x = 0; x < w; x++) @@ -129,8 +131,8 @@ namespace l::rendering { void image_metrics::compute(const image_u8& a, const image_u8& b, uint32_t first_channel, uint32_t num_channels) { const bool average_component_error = true; - const uint32_t width = std::min(a.width(), b.width()); - const uint32_t height = std::min(a.height(), b.height()); + const uint32_t width = math::functions::min(a.width(), b.width()); + const uint32_t height = math::functions::min(a.height(), b.height()); ASSERT((first_channel < 4U) && (first_channel + num_channels <= 4U)); diff --git a/packages/rendering/source/common/ui/UIVisitors.cpp b/packages/rendering/source/common/ui/UIVisitors.cpp index ce732aa8..00d71bce 100644 --- a/packages/rendering/source/common/ui/UIVisitors.cpp +++ b/packages/rendering/source/common/ui/UIVisitors.cpp @@ -210,7 +210,6 @@ namespace l::ui { auto node = mNGSchema->GetNode(container.GetNodeId()); auto nodeChannel = static_cast(container.GetChannelId()); if (node->IsDataEditable(nodeChannel)) { - //auto nodeValue = node->Get(nodeChannel); auto nodeValue = node->GetInput(nodeChannel); if (!ImGui::IsKeyDown(ImGuiKey::ImGuiKey_LeftShift)) { if (!ImGui::IsKeyDown(ImGuiKey::ImGuiKey_LeftCtrl)) { @@ -346,7 +345,7 @@ namespace l::ui { nodeValue = node->GetInput(static_cast(container.GetChannelId())); } else { - nodeValue = node->Get(static_cast(container.GetChannelId())); + nodeValue = node->GetOutput(static_cast(container.GetChannelId())); } auto nodeString = std::to_string(nodeValue); mDrawList->AddText(ImGui::GetDefaultFont(), 13.0f * container.GetScale() * layoutArea.mScale, p1, color, nodeString.c_str()); From 48d96edef5b3ef1671ba267c97ceb721d0c13698 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Wed, 4 Sep 2024 08:01:05 +0200 Subject: [PATCH 109/125] Fix build errors. --- packages/math/include/math/MathConstants.h | 4 ++++ packages/nodegraph/include/nodegraph/NodeGraph.h | 6 ++++-- packages/nodegraph/source/common/NodeGraph.cpp | 16 ++++++++-------- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/packages/math/include/math/MathConstants.h b/packages/math/include/math/MathConstants.h index a39ab187..32ad5bf2 100644 --- a/packages/math/include/math/MathConstants.h +++ b/packages/math/include/math/MathConstants.h @@ -34,4 +34,8 @@ namespace l::math::constants { return c * c * dt * dt - w * w - dx * dx - dy * dy - dz * dz; } + const float FLTMAX = 3.402823466e+38F; + const float FLTMIN = 1.175494351e-38F; + const int32_t INTMAX = 2147483647; + const int32_t INTMIN = (-2147483647 - 1); } diff --git a/packages/nodegraph/include/nodegraph/NodeGraph.h b/packages/nodegraph/include/nodegraph/NodeGraph.h index b42a996f..9129b07f 100644 --- a/packages/nodegraph/include/nodegraph/NodeGraph.h +++ b/packages/nodegraph/include/nodegraph/NodeGraph.h @@ -9,6 +9,8 @@ #include #include +#include "math/MathConstants.h" + namespace l::nodegraph { int32_t CreateUniqueId(); @@ -58,8 +60,8 @@ namespace l::nodegraph { Input mInput; InputType mInputType = InputType::INPUT_EMPTY; - float mBoundMin = -FLT_MAX; - float mBoundMax = FLT_MAX; + float mBoundMin = -l::math::constants::FLTMAX; + float mBoundMax = l::math::constants::FLTMAX; InputBound mInputBound = InputBound::INPUT_UNBOUNDED; int8_t mInputFromOutputChannel = 0; diff --git a/packages/nodegraph/source/common/NodeGraph.cpp b/packages/nodegraph/source/common/NodeGraph.cpp index 0310b9b9..0b8b5006 100644 --- a/packages/nodegraph/source/common/NodeGraph.cpp +++ b/packages/nodegraph/source/common/NodeGraph.cpp @@ -96,7 +96,7 @@ namespace l::nodegraph { } bool NodeGraphBase::ClearInput(int8_t inputChannel) { - ASSERT(inputChannel >= 0 && inputChannel < mInputs.size()); + ASSERT(inputChannel >= 0 && static_cast(inputChannel) < mInputs.size()); if (!IsValidInOutNum(inputChannel, mInputs.size())) { return false; } @@ -111,7 +111,7 @@ namespace l::nodegraph { } bool NodeGraphBase::SetInput(int8_t inputChannel, NodeGraphBase& source, int8_t sourceOutputChannel) { - ASSERT(inputChannel >= 0 && inputChannel < mInputs.size()); + ASSERT(inputChannel >= 0 && static_cast(inputChannel) < mInputs.size()); if (!IsValidInOutNum(inputChannel, mInputs.size())) { return false; } @@ -128,7 +128,7 @@ namespace l::nodegraph { } bool NodeGraphBase::SetInput(int8_t inputChannel, NodeGraphGroup& source, int8_t sourceChannel) { - ASSERT(inputChannel >= 0 && inputChannel < mInputs.size()); + ASSERT(inputChannel >= 0 && static_cast(inputChannel) < mInputs.size()); if (!IsValidInOutNum(inputChannel, mInputs.size())) { return false; } @@ -156,7 +156,7 @@ namespace l::nodegraph { } bool NodeGraphBase::SetInput(int8_t inputChannel, float constant) { - ASSERT(inputChannel >= 0 && inputChannel < mInputs.size()); + ASSERT(inputChannel >= 0 && static_cast(inputChannel) < mInputs.size()); if (!IsValidInOutNum(inputChannel, mInputs.size())) { return false; } @@ -169,7 +169,7 @@ namespace l::nodegraph { } bool NodeGraphBase::SetInput(int8_t inputChannel, float* floatPtr) { - ASSERT(inputChannel >= 0 && inputChannel < mInputs.size()); + ASSERT(inputChannel >= 0 && static_cast(inputChannel) < mInputs.size()); if (!IsValidInOutNum(inputChannel, mInputs.size())) { return false; } @@ -181,7 +181,7 @@ namespace l::nodegraph { } bool NodeGraphBase::SetInputBound(int8_t inputChannel, InputBound bound, float boundMin, float boundMax) { - ASSERT(inputChannel >= 0 && inputChannel < mInputs.size()); + ASSERT(inputChannel >= 0 && static_cast(inputChannel) < mInputs.size()); if (!IsValidInOutNum(inputChannel, mInputs.size())) { return false; } @@ -205,8 +205,8 @@ namespace l::nodegraph { input.mBoundMax = boundMax; break; case InputBound::INPUT_UNBOUNDED: - input.mBoundMin = -FLT_MAX; - input.mBoundMax = FLT_MAX; + input.mBoundMin = -l::math::constants::FLTMAX; + input.mBoundMax = l::math::constants::FLTMAX; break; case InputBound::INPUT_DONTCHANGE: break; From a15566fe352e9df979c6fd6f1aeb69869ca39ae7 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Wed, 4 Sep 2024 14:11:47 +0200 Subject: [PATCH 110/125] Add more node types for audio processing. --- packages/math/include/math/MathFunc.h | 2 - .../include/nodegraph/NodeGraphOperations.h | 137 +++++++++++++-- .../include/nodegraph/NodeGraphSchema.h | 9 +- .../source/common/NodeGraphOperations.cpp | 157 +++++++++++++++++- .../source/common/NodeGraphSchema.cpp | 17 +- .../rendering/source/common/ui/UIVisitors.cpp | 2 +- 6 files changed, 301 insertions(+), 23 deletions(-) diff --git a/packages/math/include/math/MathFunc.h b/packages/math/include/math/MathFunc.h index 7dfa9795..448965e6 100644 --- a/packages/math/include/math/MathFunc.h +++ b/packages/math/include/math/MathFunc.h @@ -51,7 +51,6 @@ namespace l::math::functions { return llabs(val); } } - return static_cast(0); } @@ -92,7 +91,6 @@ namespace l::math::functions { return ::pow(val, exp); } } - return static_cast(0); } template diff --git a/packages/nodegraph/include/nodegraph/NodeGraphOperations.h b/packages/nodegraph/include/nodegraph/NodeGraphOperations.h index 806da5de..cf930656 100644 --- a/packages/nodegraph/include/nodegraph/NodeGraphOperations.h +++ b/packages/nodegraph/include/nodegraph/NodeGraphOperations.h @@ -66,11 +66,11 @@ namespace l::nodegraph { class GraphSourceSine : public NodeGraphOp { public: GraphSourceSine(NodeGraphBase* node) : - NodeGraphOp(node, 5, 3) + NodeGraphOp(node, 5, 2) {} - std::string defaultInStrings[5] = { "Note", "Vol", "Vib", "Mod", "Reset"}; - std::string defaultOutStrings[3] = { "Sine", "Phase", "Ph. Mod"}; + std::string defaultInStrings[5] = { "Note", "Volume", "Fmod", "Phase", "Reset"}; + std::string defaultOutStrings[2] = { "Sine", "Phase"}; virtual ~GraphSourceSine() = default; @@ -344,19 +344,28 @@ namespace l::nodegraph { /*********************************************************************/ class GraphOutputDebug : public NodeGraphOp { public: - GraphOutputDebug(NodeGraphBase* node, int32_t numValueDisplays) : - NodeGraphOp(node, numValueDisplays, 0, 0) + std::string defaultInStrings[2] = { "Debug", "Smooth" }; + GraphOutputDebug(NodeGraphBase* node) : + NodeGraphOp(node, 1, 0, 2) {} + virtual ~GraphOutputDebug() = default; - bool IsDataVisible(int8_t) override { + virtual void Reset() override; + virtual void Process(std::vector& inputs, std::vector& outputs) override; + virtual bool IsDataVisible(int8_t) override { return true; } - - virtual ~GraphOutputDebug() = default; - - std::string_view GetName() override { + virtual bool IsDataEditable(int8_t channel) override { + return channel == 1; + } + virtual std::string_view GetOutputName(int8_t outputChannel) { + return defaultInStrings[outputChannel]; + } + virtual std::string_view GetName() override { return "Debug"; } + protected: + float mValue = 0.0F; }; /*********************************************************************/ @@ -369,14 +378,13 @@ namespace l::nodegraph { mAudioStream(stream), mCurrentStereoPosition(0) {} + virtual ~GraphOutputSpeaker() = default; + + void Process(std::vector& inputs, std::vector& outputs) override; bool IsDataVisible(int8_t) override { return true; } - - virtual ~GraphOutputSpeaker() = default; - void Process(std::vector& inputs, std::vector& outputs) override; - std::string_view GetOutputName(int8_t outputChannel) { return defaultOutStrings[outputChannel]; } @@ -519,5 +527,106 @@ namespace l::nodegraph { float mLP3 = 0.0f; }; + /*********************************************************************/ + class GraphEffectLimiter : public NodeGraphOp { + public: + std::string defaultInStrings[6] = { "In 1", "In 2", "Attack", "Release", "Preamp", "Limit" }; + std::string defaultOutStrings[3] = { "Out 1", "Out 2", "Envelope"}; + + GraphEffectLimiter(NodeGraphBase* node) : + NodeGraphOp(node, 6, 3, 0) + {} + + virtual ~GraphEffectLimiter() = default; + virtual void Reset() override; + virtual void Process(std::vector& inputs, std::vector& outputs) override; + + virtual bool IsDataVisible(int8_t num) override { + return num >= 2 ? true : false; + } + virtual bool IsDataEditable(int8_t num) override { + return num >= 2 ? true : false; + } + std::string_view GetInputName(int8_t inputChannel) { + return defaultInStrings[inputChannel]; + } + std::string_view GetOutputName(int8_t outputChannel) { + return defaultOutStrings[outputChannel]; + } + std::string_view GetName() override { + return "Limiter"; + } + + protected: + float mEnvelope = 0.0f; + }; + + /*********************************************************************/ + class GraphEffectEnvelopeFollower : public NodeGraphOp { + public: + std::string defaultInStrings[6] = { "In 1", "In 2", "Attack", "Release" }; + std::string defaultOutStrings[3] = { "Envelope" }; + + GraphEffectEnvelopeFollower(NodeGraphBase* node) : + NodeGraphOp(node, 4, 1, 0) + {} + + virtual ~GraphEffectEnvelopeFollower() = default; + virtual void Reset() override; + virtual void Process(std::vector& inputs, std::vector& outputs) override; + + virtual bool IsDataVisible(int8_t num) override { + return num >= 2 ? true : false; + } + virtual bool IsDataEditable(int8_t num) override { + return num >= 2 ? true : false; + } + std::string_view GetInputName(int8_t inputChannel) { + return defaultInStrings[inputChannel]; + } + std::string_view GetOutputName(int8_t outputChannel) { + return defaultOutStrings[outputChannel]; + } + std::string_view GetName() override { + return "Envelope Follower"; + } + + protected: + float mEnvelope = 0.0f; + }; + + /*********************************************************************/ + class GraphEffectSaturator : public NodeGraphOp { + public: + std::string defaultInStrings[6] = { "In 1", "In 2", "Wet", "Preamp", "Limit", "Postamp"}; + std::string defaultOutStrings[3] = { "Out 1", "Out 2"}; + + GraphEffectSaturator(NodeGraphBase* node) : + NodeGraphOp(node, 6, 2, 0) + {} + + virtual ~GraphEffectSaturator() = default; + virtual void Reset() override; + virtual void Process(std::vector& inputs, std::vector& outputs) override; + + virtual bool IsDataVisible(int8_t num) override { + return num >= 2 ? true : false; + } + virtual bool IsDataEditable(int8_t num) override { + return num >= 2 ? true : false; + } + std::string_view GetInputName(int8_t inputChannel) { + return defaultInStrings[inputChannel]; + } + std::string_view GetOutputName(int8_t outputChannel) { + return defaultOutStrings[outputChannel]; + } + std::string_view GetName() override { + return "Saturator"; + } + + protected: + float mEnvelope = 0.0f; + }; } diff --git a/packages/nodegraph/include/nodegraph/NodeGraphSchema.h b/packages/nodegraph/include/nodegraph/NodeGraphSchema.h index 9930d8f7..f7197b1b 100644 --- a/packages/nodegraph/include/nodegraph/NodeGraphSchema.h +++ b/packages/nodegraph/include/nodegraph/NodeGraphSchema.h @@ -44,15 +44,20 @@ namespace l::nodegraph { RegisterNodeType("Numeric", 52, "Negate"); RegisterNodeType("Numeric", 53, "Multiply"); RegisterNodeType("Numeric", 54, "Integral"); + RegisterNodeType("Numeric", 55, "Multiply3"); + RegisterNodeType("Numeric", 56, "Multiply & Add"); RegisterNodeType("Logic", 100, "And"); RegisterNodeType("Logic", 101, "Or"); RegisterNodeType("Logic", 102, "Xor"); - RegisterNodeType("Filter", 150, "Lowpass Filter"); - RegisterNodeType("Output", 200, "Value Debug"); + RegisterNodeType("Filter", 150, "Lowpass"); + RegisterNodeType("Output", 200, "Debug"); RegisterNodeType("Output", 201, "Speaker"); RegisterNodeType("Effect", 250, "Envelope"); RegisterNodeType("Effect", 251, "Reverb1"); RegisterNodeType("Effect", 252, "Reverb2"); + RegisterNodeType("Effect", 253, "Limiter"); + RegisterNodeType("Effect", 254, "Envelope Follower"); + RegisterNodeType("Effect", 255, "Saturator"); } ~NodeGraphSchema() = default; diff --git a/packages/nodegraph/source/common/NodeGraphOperations.cpp b/packages/nodegraph/source/common/NodeGraphOperations.cpp index c7f3f32d..f76e5685 100644 --- a/packages/nodegraph/source/common/NodeGraphOperations.cpp +++ b/packages/nodegraph/source/common/NodeGraphOperations.cpp @@ -139,8 +139,7 @@ namespace l::nodegraph { phaseMod -= floorf(phaseMod); outputs.at(0).mOutput = volume * sinf(3.141529f * (mPhase + phaseMod)); - outputs.at(1).mOutput = mPhase; - outputs.at(2).mOutput = phaseMod; + outputs.at(1).mOutput = 0.5f * phaseMod; } std::string_view GraphSourceSine::GetInputName(int8_t inputChannel) { @@ -345,6 +344,7 @@ namespace l::nodegraph { float attackFrames = inputs.at(1).Get() * 44100.0f / 1000.0f; float releaseFrames = inputs.at(2).Get() * 44100.0f / 1000.0f; float noteFade = inputs.at(3).Get(); + //noteFade = l::math::functions::pow(0.01f, 1.0f / (1000.0f * inputs.at(3).Get() * 44100.0f * 0.001f)); if (noteTarget != 0.0f && mFrameCount < attackFrames) { if (mFrameCount == 0) { @@ -363,6 +363,7 @@ namespace l::nodegraph { mEnvelopeTarget = 1.0f; } mNote += noteFade * noteFade * (noteTarget - mNote); + //mNote = noteFade * (mEnvelope - noteTarget) + noteTarget; } else { // release @@ -384,6 +385,20 @@ namespace l::nodegraph { outputs.at(1).mOutput = mEnvelope * mEnvelope; } + /*********************************************************************/ + void GraphOutputDebug::Reset() { + mValue = 0.0; + mNode->SetInput(1, 0.5f); + mNode->SetInputBound(1, InputBound::INPUT_0_TO_1); + } + + void GraphOutputDebug::Process(std::vector& inputs, std::vector&) { + float value = inputs.at(0).Get(); + float friction = inputs.at(1).Get(); + mValue += friction * friction * (value - mValue); + inputs.at(2).mInput.mInputFloatConstant = mValue; + } + /*********************************************************************/ void GraphOutputSpeaker::Process(std::vector& inputs, std::vector&) { auto& buffer = mAudioStream->GetWriteBuffer(); @@ -456,7 +471,6 @@ namespace l::nodegraph { void GraphEffectReverb2::Reset() { // { "In 1", "In 2", "Mix", "Feedback", "Room Size", "Width", "First tap", "Longest tap", "Num taps", "Tap bulge", "Filter cutoff", "Filter res"}; - mNode->SetInput(2, 0.3f); mNode->SetInput(3, 0.5f); mNode->SetInput(4, 30.0f); @@ -591,10 +605,147 @@ namespace l::nodegraph { //buf1[bufIndex] += centralDelay; // + } + + /*********************************************************************/ + void GraphEffectLimiter::Reset() { + // { "In 1", "In 2", "Attack", "Release", "Preamp", "Limit"}; + mEnvelope = 0.0f; + mNode->SetInput(2, 5.0f); + mNode->SetInput(3, 100.0f); + mNode->SetInput(4, 1.0f); + mNode->SetInput(5, 0.95f); + mNode->SetInputBound(2, InputBound::INPUT_CUSTOM, 1.0f, 10000.0f); + mNode->SetInputBound(3, InputBound::INPUT_CUSTOM, 1.0f, 10000.0f); + mNode->SetInputBound(4, InputBound::INPUT_CUSTOM, 0.0f, 10.0f); + mNode->SetInputBound(5, InputBound::INPUT_CUSTOM, 0.0f, 10.0f); + } + + void GraphEffectLimiter::Process(std::vector& inputs, std::vector& outputs) { + float attackMs = inputs .at(2).Get(); + float releaseMs = inputs.at(3).Get(); + float attack = l::math::functions::pow(0.01f, 1.0f / (attackMs * 44100.0f * 0.001f)); + float release = l::math::functions::pow(0.01f, 1.0f / (releaseMs * 44100.0f * 0.001f)); + float preamp = inputs.at(4).Get(); + float limit = inputs.at(5).Get(); + + float in0 = inputs.at(0).Get(); + float in1 = inputs.at(1).Get(); + + float inVal0 = preamp * in0; + float inVal1 = preamp * in1; + float inVal = 0.5f * (inVal0 + inVal1); + if (inVal > mEnvelope) { + mEnvelope = attack * (mEnvelope - inVal) + inVal; + } + else { + mEnvelope = release * (mEnvelope - inVal) + inVal; + } + + float envelopeAbs = l::math::functions::abs(mEnvelope); + if (envelopeAbs > limit) { + if (envelopeAbs > 1.0f) { + outputs.at(0).mOutput = inVal0 / mEnvelope; + outputs.at(1).mOutput = inVal1 / mEnvelope; + } + else { + outputs.at(0).mOutput = inVal0 / (1.0f + mEnvelope - limit); + outputs.at(1).mOutput = inVal1 / (1.0f + mEnvelope - limit); + } + } + else { + outputs.at(0).mOutput = in0; + outputs.at(1).mOutput = in1; + } + outputs.at(2).mOutput = envelopeAbs; + } + + /*********************************************************************/ + void GraphEffectEnvelopeFollower::Reset() { + mEnvelope = 0.0f; + mNode->SetInput(2, 5.0f); + mNode->SetInput(3, 100.0f); + mNode->SetInputBound(2, InputBound::INPUT_CUSTOM, 1.0f, 10000.0f); + mNode->SetInputBound(3, InputBound::INPUT_CUSTOM, 1.0f, 10000.0f); + } + + void GraphEffectEnvelopeFollower::Process(std::vector& inputs, std::vector& outputs) { + float attackMs = inputs.at(2).Get(); + float releaseMs = inputs.at(3).Get(); + float attack = l::math::functions::pow(0.01f, 1.0f / (attackMs * 44100.0f * 0.001f)); + float release = l::math::functions::pow(0.01f, 1.0f / (releaseMs * 44100.0f * 0.001f)); + float in0 = inputs.at(0).Get(); + float in1 = inputs.at(1).Get(); + + float inVal = 0.5f * (in0 + in1); + if (inVal > mEnvelope) { + mEnvelope = attack * (mEnvelope - inVal) + inVal; + } + else { + mEnvelope = release * (mEnvelope - inVal) + inVal; + } + + float envelopeAbs = l::math::functions::abs(mEnvelope); + outputs.at(0).mOutput = envelopeAbs; } /*********************************************************************/ + void GraphEffectSaturator::Reset() { + // { "In 1", "In 2", "Wet", "Preamp", "Limit", "Postamp"}; + mEnvelope = 0.0f; + mNode->SetInput(2, 0.5f); + mNode->SetInput(3, 1.5f); + mNode->SetInput(4, 0.6f); + mNode->SetInput(5, 1.4f); + mNode->SetInputBound(2, InputBound::INPUT_0_TO_1); + mNode->SetInputBound(3, InputBound::INPUT_CUSTOM, 0.0f, 10.0f); + mNode->SetInputBound(4, InputBound::INPUT_CUSTOM, 0.0f, 10.0f); + mNode->SetInputBound(5, InputBound::INPUT_CUSTOM, 0.0f, 10.0f); + } + + void GraphEffectSaturator::Process(std::vector& inputs, std::vector& outputs) { + float wet = inputs.at(2).Get(); + float preamp = inputs.at(3).Get(); + float limit = inputs.at(4).Get(); + float postamp = inputs.at(5).Get(); + wet = postamp * wet; + float dry = postamp * (1.0f - wet); + float in0 = inputs.at(0).Get(); + float in1 = inputs.at(1).Get(); + + auto sigmoid = [](float x) { + if (x < 1.0f && x > -1.0f) { + return x * (1.5f - 0.5f * x * x); + } + else { + return x > 0 ? 1.0f : -1.0f; + } + }; + + float inPreamp0 = in0 * preamp; + float inPreamp1 = in1 * preamp; + + if (inPreamp0 >= limit || inPreamp0 <= -limit) { + if (inPreamp0 > 0.0f) { + inPreamp0 = limit + (1.0f - limit) * sigmoid((inPreamp0 - limit) / ((1.0f - limit) * 1.5f)); + } + else { + inPreamp0 = -(limit + (1.0f - limit) * sigmoid((-inPreamp0 - limit) / ((1.0f - limit) * 1.5f))); + } + } + if (inPreamp1 >= limit || inPreamp1 <= -limit) { + if (inPreamp1 > 0.0f) { + inPreamp1 = limit + (1.0f - limit) * sigmoid((inPreamp1 - limit) / ((1.0f - limit) * 1.5f)); + } + else { + inPreamp1 = -(limit + (1.0f - limit) * sigmoid((-inPreamp1 - limit) / ((1.0f - limit) * 1.5f))); + } + } + + outputs.at(0).mOutput = dry * in0 + wet * inPreamp0; + outputs.at(1).mOutput = dry * in1 + wet * inPreamp1; + } } diff --git a/packages/nodegraph/source/common/NodeGraphSchema.cpp b/packages/nodegraph/source/common/NodeGraphSchema.cpp index 157e99d0..8816a657 100644 --- a/packages/nodegraph/source/common/NodeGraphSchema.cpp +++ b/packages/nodegraph/source/common/NodeGraphSchema.cpp @@ -57,6 +57,12 @@ namespace l::nodegraph { case 54: node = mMainNodeGraph.NewNode(OutputType::Default); break; + case 55: + node = mMainNodeGraph.NewNode(OutputType::Default); + break; + case 56: + node = mMainNodeGraph.NewNode(OutputType::Default); + break; case 100: node = mMainNodeGraph.NewNode(OutputType::Default); break; @@ -70,7 +76,7 @@ namespace l::nodegraph { node = mMainNodeGraph.NewNode(OutputType::Default); break; case 200: - node = mMainNodeGraph.NewNode(OutputType::ExternalOutput, 1); + node = mMainNodeGraph.NewNode(OutputType::ExternalOutput); break; case 201: node = mMainNodeGraph.NewNode(OutputType::ExternalOutput, mAudioOutput); @@ -84,6 +90,15 @@ namespace l::nodegraph { case 252: node = mMainNodeGraph.NewNode(OutputType::Default); break; + case 253: + node = mMainNodeGraph.NewNode(OutputType::Default); + break; + case 254: + node = mMainNodeGraph.NewNode(OutputType::Default); + break; + case 255: + node = mMainNodeGraph.NewNode(OutputType::Default); + break; default: ASSERT(typeId < 10000) << "Custom node id's begin at id 1000"; ASSERT(mCreateCustomNode != nullptr) << "Custom nodes needs a handler to create them. It's missing."; diff --git a/packages/rendering/source/common/ui/UIVisitors.cpp b/packages/rendering/source/common/ui/UIVisitors.cpp index 00d71bce..dc208bcc 100644 --- a/packages/rendering/source/common/ui/UIVisitors.cpp +++ b/packages/rendering/source/common/ui/UIVisitors.cpp @@ -341,7 +341,7 @@ namespace l::ui { if (mNGSchema) { auto node = mNGSchema->GetNode(container.GetNodeId()); float nodeValue = 0.0f; - if (container.GetChannelId() < node->GetNumInputs()) { + if (container.GetChannelId() < node->GetNumInputs() + node->GetNumConstants()) { nodeValue = node->GetInput(static_cast(container.GetChannelId())); } else { From d8a4bd2c14952fc5f612219e8f593f0f78228332 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Wed, 4 Sep 2024 16:44:24 +0200 Subject: [PATCH 111/125] Prepare for batching. Fix math functions. Add limiter to speaker output. Fix other issues. --- packages/math/include/math/MathFunc.h | 55 ++++-- .../nodegraph/include/nodegraph/NodeGraph.h | 30 ++- .../include/nodegraph/NodeGraphOperations.h | 63 ++++--- .../include/nodegraph/NodeGraphSchema.h | 2 +- .../nodegraph/source/common/NodeGraph.cpp | 14 +- .../source/common/NodeGraphOperations.cpp | 172 +++++++++++++----- .../source/common/NodeGraphSchema.cpp | 4 +- .../tests/common/NodeGraphSchemaTest.cpp | 2 +- 8 files changed, 229 insertions(+), 113 deletions(-) diff --git a/packages/math/include/math/MathFunc.h b/packages/math/include/math/MathFunc.h index 448965e6..f5e3197e 100644 --- a/packages/math/include/math/MathFunc.h +++ b/packages/math/include/math/MathFunc.h @@ -65,7 +65,6 @@ namespace l::math::functions { return ::floor(val); } } - return static_cast(0); } template @@ -78,7 +77,6 @@ namespace l::math::functions { return ::sqrt(val); } } - return static_cast(0); } template @@ -103,7 +101,18 @@ namespace l::math::functions { return ::exp(val); } } - return static_cast(0); + } + + template + T sin(T val) { + if constexpr (std::is_floating_point_v) { + if constexpr (sizeof(T) == 4) { + return sinf(val); + } + else if constexpr (sizeof(T) == 8) { + return ::sin(val); + } + } } // Almost Identity(I) @@ -124,7 +133,7 @@ namespace l::math::functions { template T almostIdentity2(T x, T n) { - return sqrt(x * x + n); + return functions::sqrt(x * x + n); } // Almost Unit Identity @@ -151,7 +160,7 @@ namespace l::math::functions { T expImpulse(T x, T k) { const T h = k * x; - return h * exp(1.0 - h); + return h * functions::exp(1.0 - h); } // Polynomial Impulse @@ -159,14 +168,14 @@ namespace l::math::functions { template T quaImpulse(T k, T x) { - return 2.0 * sqrt(k) * x / (1.0 + k * x * x); + return 2.0 * functions::sqrt(k) * x / (1.0 + k * x * x); } // You can easily generalize it to other powers to get different falloff shapes, where n is the degree of the polynomial : template T polyImpulse(T k, T n, T x) { - return (n / (n - 1.0)) * pow((n - 1.0) * k, 1.0 / n) * x / (1.0 + k * pow(x, n)); + return (n / (n - 1.0)) * functions::pow((n - 1.0) * k, 1.0 / n) * x / (1.0 + k * functions::pow(x, n)); } // Sustained Impulse @@ -174,8 +183,8 @@ namespace l::math::functions { template T expSustainedImpulse(T x, T f, T k) { - T s = std::max(x - f, 0.0); - return std::min(x * x / (f * f), 1 + (2.0 / f) * s * exp(-k * s)); + T s = functions::max(x - f, 0.0); + return functions::min(x * x / (f * f), 1 + (2.0 / f) * s * functions::exp(-k * s)); } // Cubic Pulse @@ -183,7 +192,7 @@ namespace l::math::functions { template T cubicPulse(T c, T w, T x) { - x = abs(x - c); + x = functions::abs(x - c); if (x > w) return 0.0; x /= w; return 1.0 - x * x * (3.0 - 2.0 * x); @@ -194,7 +203,7 @@ namespace l::math::functions { template T expStep(T x, T k, T n) { - return exp(-k * pow(x, n)); + return functions::exp(-k * functions::pow(x, n)); } // Gain @@ -202,7 +211,7 @@ namespace l::math::functions { template T gain(T x, T k) { - const T a = 0.5 * pow(2.0 * ((x < 0.5) ? x : 1.0 - x), k); + const T a = 0.5 * functions::pow(2.0 * ((x < 0.5) ? x : 1.0 - x), k); return (x < 0.5) ? a : 1.0 - a; } @@ -211,7 +220,7 @@ namespace l::math::functions { template T parabola(T x, T k) { - return pow(4.0 * x * (1.0 - x), k); + return functions::pow(4.0 * x * (1.0 - x), k); } // Power curve @@ -219,8 +228,8 @@ namespace l::math::functions { template T pcurve(T x, T a, T b) { - const T k = pow(a + b, a + b) / (pow(a, a) * pow(b, b)); - return k * pow(x, a) * pow(1.0 - x, b); + const T k = functions::pow(a + b, a + b) / (functions::pow(a, a) * functions::pow(b, b)); + return k * functions::pow(x, a) * functions::pow(1.0 - x, b); } // Sinc curve @@ -229,7 +238,21 @@ namespace l::math::functions { T sinc(T x, T k) { const T a = math::constants::PI * (k * x - 1.0); - return sin(a) / a; + return functions::sin(a) / a; + } + + template + T sigmoid(T x) { + return static_cast(1.0 / (1.0 + functions::exp(-x))); } + template + T sigmoidFast(T x) { + if (x < 1.0 && x > -1.0) { + return static_cast(x * (1.5 - 0.5 * x * x)); + } + else { + return static_cast(x > 0.0 ? 1.0 : -1.0); + } + } } diff --git a/packages/nodegraph/include/nodegraph/NodeGraph.h b/packages/nodegraph/include/nodegraph/NodeGraph.h index 9129b07f..eb91d7c0 100644 --- a/packages/nodegraph/include/nodegraph/NodeGraph.h +++ b/packages/nodegraph/include/nodegraph/NodeGraph.h @@ -42,7 +42,21 @@ namespace l::nodegraph { struct NodeGraphOutput { float mOutput = 0.0f; + std::vector mOutputBuf; std::string mName; + + float& GetOutput(int32_t numSamples) { + if(numSamples <= 1) { + return mOutput; + } + else { + if (static_cast(mOutputBuf.size()) < numSamples) { + mOutputBuf.resize(numSamples); + } + return *mOutputBuf.data(); + } + } + }; class NodeGraphBase; @@ -86,7 +100,7 @@ namespace l::nodegraph { virtual int32_t GetId() const { return mId; } void ClearProcessFlags(); - virtual void ProcessSubGraph(bool recomputeSubGraphCache = true); + virtual void ProcessSubGraph(int32_t numSamples = 1, bool recomputeSubGraphCache = true); virtual void Tick(float time, float elapsed); virtual void SetNumInputs(int8_t numInputs); @@ -120,7 +134,7 @@ namespace l::nodegraph { virtual bool IsDataEditable(int8_t num); protected: - virtual void ProcessOperation(); + virtual void ProcessOperation(int32_t numSamples = 1); bool mProcessUpdateHasRun = false; std::vector mInputs; @@ -146,7 +160,7 @@ namespace l::nodegraph { virtual ~NodeGraphOp() = default; virtual void Reset() {} - virtual void Process(std::vector&, std::vector&) {}; + virtual void Process(int32_t, std::vector&, std::vector&) {}; virtual void Tick(float, float) {} virtual void SetNumInputs(int8_t numInputs); @@ -176,7 +190,7 @@ namespace l::nodegraph { {} virtual ~GraphDataCopy() = default; - void Process(std::vector& inputs, std::vector& outputs) override; + void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; }; template @@ -222,13 +236,13 @@ namespace l::nodegraph { mOperation.Reset(); } - virtual void ProcessOperation() override { + virtual void ProcessOperation(int32_t numSamples = 1) override { if (mProcessUpdateHasRun) { return; } - NodeGraphBase::ProcessOperation(); - mOperation.Process(mInputs, mOutputs); + NodeGraphBase::ProcessOperation(numSamples); + mOperation.Process(numSamples, mInputs, mOutputs); mProcessUpdateHasRun = true; } @@ -312,7 +326,7 @@ namespace l::nodegraph { } void ClearProcessFlags(); - void ProcessSubGraph(bool recomputeSubGraphCache = true); + void ProcessSubGraph(int32_t numSamples, bool recomputeSubGraphCache = true); void Tick(float time, float elapsed); protected: NodeGraph mInputNode; diff --git a/packages/nodegraph/include/nodegraph/NodeGraphOperations.h b/packages/nodegraph/include/nodegraph/NodeGraphOperations.h index cf930656..73ef6fd4 100644 --- a/packages/nodegraph/include/nodegraph/NodeGraphOperations.h +++ b/packages/nodegraph/include/nodegraph/NodeGraphOperations.h @@ -27,7 +27,7 @@ namespace l::nodegraph { virtual ~GraphSourceConstants() = default; virtual void Reset(); - virtual void Process(std::vector& inputs, std::vector& outputs) override; + virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; virtual void Tick(float, float) override; virtual std::string_view GetName() override; @@ -50,7 +50,7 @@ namespace l::nodegraph { std::string defaultOutStrings[2] = { "Audio Time", "Frame Time"}; virtual ~GraphSourceTime() = default; - virtual void Process(std::vector& inputs, std::vector& outputs) override; + virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; virtual void Tick(float, float) override; virtual void Reset() override; @@ -75,7 +75,7 @@ namespace l::nodegraph { virtual ~GraphSourceSine() = default; virtual void Reset() override; - virtual void Process(std::vector& inputs, std::vector& outputs) override; + virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; virtual std::string_view GetInputName(int8_t inputChannel); virtual std::string_view GetOutputName(int8_t outputChannel); @@ -99,7 +99,7 @@ namespace l::nodegraph { std::string defaultOutStrings[8] = { "Note 1", "Note 2", "Note 3", "Note 4", "Note 5", "Note 6", "Note 7", "Note 8" }; virtual ~GraphSourceKeyboard() = default; - void Process(std::vector& inputs, std::vector& outputs) override; + void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; void Tick(float time, float elapsed) override; void Reset() override; virtual std::string_view GetOutputName(int8_t outputChannel) override; @@ -124,7 +124,7 @@ namespace l::nodegraph { NodeGraphOp(node, 2, 1) {} virtual ~GraphNumericAdd() = default; - void Process(std::vector& inputs, std::vector& outputs) override; + void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; virtual std::string_view GetName() override { return "Add"; } @@ -138,7 +138,7 @@ namespace l::nodegraph { {} virtual ~GraphNumericMultiply() = default; - void Process(std::vector& inputs, std::vector& outputs) override; + void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; virtual std::string_view GetName() override { return "Multiply"; } @@ -151,7 +151,7 @@ namespace l::nodegraph { NodeGraphOp(node, 2, 1) {} virtual ~GraphNumericSubtract() = default; - void Process(std::vector& inputs, std::vector& outputs) override; + void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; virtual std::string_view GetName() override { return "Subtract"; } @@ -165,7 +165,7 @@ namespace l::nodegraph { {} virtual ~GraphNumericNegate() = default; - void Process(std::vector& inputs, std::vector& outputs) override; + void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; virtual std::string_view GetName() override { return "Negate"; } @@ -180,7 +180,7 @@ namespace l::nodegraph { virtual ~GraphNumericIntegral() = default; void Reset() override; - void Process(std::vector& inputs, std::vector& outputs) override; + void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; virtual std::string_view GetName() override { return "Integral"; } @@ -197,7 +197,7 @@ namespace l::nodegraph { {} virtual ~GraphNumericMultiply3() = default; - void Process(std::vector& inputs, std::vector& outputs) override; + void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; virtual std::string_view GetName() override { return "Multiply3"; } @@ -213,7 +213,7 @@ namespace l::nodegraph { std::string defaultInStrings[3] = { "Factor 1", "Factor 2", "Term 1" }; virtual ~GraphNumericMultiplyAndAdd() = default; - void Process(std::vector& inputs, std::vector& outputs) override; + void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; virtual std::string_view GetName() override { return "Multiply & Add"; } @@ -234,7 +234,7 @@ namespace l::nodegraph { {} virtual ~GraphLogicalAnd() = default; - virtual void Process(std::vector& inputs, std::vector& outputs) override; + virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; std::string_view GetName() override { return "And"; } @@ -248,7 +248,7 @@ namespace l::nodegraph { {} virtual ~GraphLogicalOr() = default; - virtual void Process(std::vector& inputs, std::vector& outputs) override; + virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; std::string_view GetName() override { return "Or"; } @@ -262,7 +262,7 @@ namespace l::nodegraph { {} virtual ~GraphLogicalXor() = default; - virtual void Process(std::vector& inputs, std::vector& outputs) override; + virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; virtual std::string_view GetName() override { return "Xor"; } @@ -282,7 +282,7 @@ namespace l::nodegraph { virtual ~GraphFilterLowpass() = default; virtual void Reset() override; - virtual void Process(std::vector& inputs, std::vector& outputs) override; + virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; virtual std::string_view GetInputName(int8_t inputChannel) { return defaultInStrings[inputChannel]; @@ -312,7 +312,7 @@ namespace l::nodegraph { virtual ~GraphFilterEnvelope() = default; virtual void Reset() override; - virtual void Process(std::vector& inputs, std::vector& outputs) override; + virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; virtual void Tick(float, float) {} std::string_view GetInputName(int8_t inputChannel) { @@ -351,7 +351,7 @@ namespace l::nodegraph { virtual ~GraphOutputDebug() = default; virtual void Reset() override; - virtual void Process(std::vector& inputs, std::vector& outputs) override; + virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; virtual bool IsDataVisible(int8_t) override { return true; } @@ -371,22 +371,26 @@ namespace l::nodegraph { /*********************************************************************/ class GraphOutputSpeaker : public NodeGraphOp { public: - std::string defaultOutStrings[2] = { "Left", "Right"}; + std::string defaultInStrings[3] = { "Left", "Right", "Volume"}; GraphOutputSpeaker(NodeGraphBase* node, l::audio::AudioStream* stream = nullptr) : - NodeGraphOp(node, 2, 0, 0), + NodeGraphOp(node, 3, 0, 0), mAudioStream(stream), mCurrentStereoPosition(0) {} virtual ~GraphOutputSpeaker() = default; - void Process(std::vector& inputs, std::vector& outputs) override; + void Reset() override; + void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; bool IsDataVisible(int8_t) override { return true; } - std::string_view GetOutputName(int8_t outputChannel) { - return defaultOutStrings[outputChannel]; + bool IsDataEditable(int8_t channel) override { + return channel == 2 ? true : false; + } + std::string_view GetInputName(int8_t outputChannel) { + return defaultInStrings[outputChannel]; } std::string_view GetName() override { @@ -396,6 +400,7 @@ namespace l::nodegraph { protected: l::audio::AudioStream* mAudioStream; int32_t mCurrentStereoPosition; + float mEnvelope = 0.0f; }; /*********************************************************************/ @@ -423,7 +428,7 @@ namespace l::nodegraph { virtual ~GraphEffectReverb1() = default; virtual void Reset() override; - virtual void Process(std::vector&inputs, std::vector&outputs) override; + virtual void Process(int32_t numSamples, std::vector&inputs, std::vector&outputs) override; virtual bool IsDataVisible(int8_t num) override { return num >= 2 ? true : false; @@ -443,7 +448,7 @@ namespace l::nodegraph { } std::string_view GetName() override { - return "Reverb"; + return "Reverb 1"; } protected: @@ -489,7 +494,7 @@ namespace l::nodegraph { virtual ~GraphEffectReverb2() = default; virtual void Reset() override; - virtual void Process(std::vector& inputs, std::vector& outputs) override; + virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; virtual bool IsDataVisible(int8_t num) override { return num >= 2 ? true : false; @@ -506,7 +511,7 @@ namespace l::nodegraph { } std::string_view GetName() override { - return "Reverb"; + return "Reverb 2"; } protected: @@ -539,7 +544,7 @@ namespace l::nodegraph { virtual ~GraphEffectLimiter() = default; virtual void Reset() override; - virtual void Process(std::vector& inputs, std::vector& outputs) override; + virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; virtual bool IsDataVisible(int8_t num) override { return num >= 2 ? true : false; @@ -573,7 +578,7 @@ namespace l::nodegraph { virtual ~GraphEffectEnvelopeFollower() = default; virtual void Reset() override; - virtual void Process(std::vector& inputs, std::vector& outputs) override; + virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; virtual bool IsDataVisible(int8_t num) override { return num >= 2 ? true : false; @@ -607,7 +612,7 @@ namespace l::nodegraph { virtual ~GraphEffectSaturator() = default; virtual void Reset() override; - virtual void Process(std::vector& inputs, std::vector& outputs) override; + virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; virtual bool IsDataVisible(int8_t num) override { return num >= 2 ? true : false; diff --git a/packages/nodegraph/include/nodegraph/NodeGraphSchema.h b/packages/nodegraph/include/nodegraph/NodeGraphSchema.h index f7197b1b..5cd6000e 100644 --- a/packages/nodegraph/include/nodegraph/NodeGraphSchema.h +++ b/packages/nodegraph/include/nodegraph/NodeGraphSchema.h @@ -77,7 +77,7 @@ namespace l::nodegraph { void ForEachNodeType(std::function&)> cb) const; void RegisterNodeType(const std::string& typeGroup, int32_t uniqueTypeId, std::string_view typeName); - void ProcessSubGraph(); + void ProcessSubGraph(int32_t numSamples); void Tick(float time, float elapsed); protected: NodeGraphGroup mMainNodeGraph; diff --git a/packages/nodegraph/source/common/NodeGraph.cpp b/packages/nodegraph/source/common/NodeGraph.cpp index 0b8b5006..7bd99651 100644 --- a/packages/nodegraph/source/common/NodeGraph.cpp +++ b/packages/nodegraph/source/common/NodeGraph.cpp @@ -60,20 +60,20 @@ namespace l::nodegraph { } } - void NodeGraphBase::ProcessSubGraph(bool recomputeSubGraphCache) { + void NodeGraphBase::ProcessSubGraph(int32_t numSamples, bool recomputeSubGraphCache) { if (recomputeSubGraphCache) { ClearProcessFlags(); } - ProcessOperation(); + ProcessOperation(numSamples); } - void NodeGraphBase::ProcessOperation() { + void NodeGraphBase::ProcessOperation(int32_t numSamples) { if (mProcessUpdateHasRun) { return; } for (auto& link : mInputs) { if (link.mInputType == InputType::INPUT_NODE && link.mInput.mInputNode != nullptr) { - link.mInput.mInputNode->ProcessOperation(); + link.mInput.mInputNode->ProcessOperation(numSamples); } } mProcessUpdateHasRun = true; @@ -300,7 +300,7 @@ namespace l::nodegraph { return ""; } - void GraphDataCopy::Process(std::vector& inputs, std::vector& outputs) { + void GraphDataCopy::Process(int32_t, std::vector& inputs, std::vector& outputs) { for (size_t i = 0; i < inputs.size() && i < outputs.size(); i++) { outputs.at(i).mOutput = inputs.at(i).Get(); } @@ -452,12 +452,12 @@ namespace l::nodegraph { mOutputNode.ClearProcessFlags(); } - void NodeGraphGroup::ProcessSubGraph(bool) { + void NodeGraphGroup::ProcessSubGraph(int32_t numSamples, bool) { for (auto& it : mOutputNodes) { it->ClearProcessFlags(); } for (auto& it : mOutputNodes) { - it->ProcessSubGraph(false); + it->ProcessSubGraph(numSamples, false); } } diff --git a/packages/nodegraph/source/common/NodeGraphOperations.cpp b/packages/nodegraph/source/common/NodeGraphOperations.cpp index f76e5685..60c6a56f 100644 --- a/packages/nodegraph/source/common/NodeGraphOperations.cpp +++ b/packages/nodegraph/source/common/NodeGraphOperations.cpp @@ -37,14 +37,14 @@ namespace l::nodegraph { } } - void GraphSourceConstants::Process(std::vector& inputs, std::vector& outputs) { + void GraphSourceConstants::Process(int32_t, std::vector& inputs, std::vector& outputs) { for (int8_t i = 0; i < mNumOutputs; i++) { outputs.at(i).mOutput = inputs.at(i).Get(); } } void GraphSourceConstants::Tick(float, float) { - mNode->ProcessSubGraph(); + mNode->ProcessSubGraph(1); } std::string_view GraphSourceConstants::GetName() { @@ -70,7 +70,7 @@ namespace l::nodegraph { } /*********************************************************************/ - void GraphSourceTime::Process(std::vector&, std::vector& outputs) { + void GraphSourceTime::Process(int32_t, std::vector&, std::vector& outputs) { float rate = 44100.0f; float phaseChange = 1.0f / rate; mAudioTime += phaseChange; @@ -113,7 +113,7 @@ namespace l::nodegraph { } - void GraphSourceSine::Process(std::vector& inputs, std::vector& outputs) { + void GraphSourceSine::Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) { float freq = inputs.at(0).Get(); float volume = inputs.at(1).Get(); float fMod = 1.0f + inputs.at(2).Get(); @@ -129,17 +129,22 @@ namespace l::nodegraph { mPhase = 0.0f; } - float deltaTime = 1.0f / 44100.0f; - float phaseDelta = deltaTime * freq; + float* output0 = &outputs.at(0).GetOutput(numSamples); + float* output1 = &outputs.at(1).GetOutput(numSamples); - mPhase += phaseDelta * fMod; - mPhase -= floorf(mPhase); + for (int32_t i = 0; i < numSamples; i++) { + float deltaTime = 1.0f / 44100.0f; + float phaseDelta = deltaTime * freq; - float phaseMod = mPhase + pMod; - phaseMod -= floorf(phaseMod); + mPhase += phaseDelta * fMod; + mPhase -= l::math::functions::floor(mPhase); - outputs.at(0).mOutput = volume * sinf(3.141529f * (mPhase + phaseMod)); - outputs.at(1).mOutput = 0.5f * phaseMod; + float phaseMod = mPhase + pMod; + phaseMod -= l::math::functions::floor(phaseMod); + + *output0++ = volume * sinf(3.141529f * (mPhase + phaseMod)); + *output1++ = 0.5f * phaseMod; + } } std::string_view GraphSourceSine::GetInputName(int8_t inputChannel) { @@ -155,7 +160,7 @@ namespace l::nodegraph { } /*********************************************************************/ - void GraphSourceKeyboard::Process(std::vector& inputs, std::vector& outputs) { + void GraphSourceKeyboard::Process(int32_t, std::vector& inputs, std::vector& outputs) { for (size_t i = 0; i < inputs.size();i++) { outputs.at(i).mOutput = inputs.at(i).Get(); } @@ -240,22 +245,22 @@ namespace l::nodegraph { } /*********************************************************************/ - void GraphNumericAdd::Process(std::vector& inputs, std::vector& outputs) { + void GraphNumericAdd::Process(int32_t, std::vector& inputs, std::vector& outputs) { outputs.at(0).mOutput = inputs.at(0).Get() + inputs.at(1).Get(); } /*********************************************************************/ - void GraphNumericMultiply::Process(std::vector& inputs, std::vector& outputs) { + void GraphNumericMultiply::Process(int32_t, std::vector& inputs, std::vector& outputs) { outputs.at(0).mOutput = inputs.at(0).Get() * inputs.at(1).Get(); } /*********************************************************************/ - void GraphNumericSubtract::Process(std::vector& inputs, std::vector& outputs) { + void GraphNumericSubtract::Process(int32_t, std::vector& inputs, std::vector& outputs) { outputs.at(0).mOutput = inputs.at(0).Get() - inputs.at(1).Get(); } /*********************************************************************/ - void GraphNumericNegate::Process(std::vector& inputs, std::vector& outputs) { + void GraphNumericNegate::Process(int32_t, std::vector& inputs, std::vector& outputs) { outputs.at(0).mOutput = -inputs.at(0).Get(); } @@ -264,39 +269,39 @@ namespace l::nodegraph { mOutput = 0.0f; } - void GraphNumericIntegral::Process(std::vector& inputs, std::vector& outputs) { + void GraphNumericIntegral::Process(int32_t, std::vector& inputs, std::vector& outputs) { mOutput += inputs.at(0).Get(); outputs.at(0).mOutput = mOutput; } /*********************************************************************/ - void GraphNumericMultiply3::Process(std::vector& inputs, std::vector& outputs) { + void GraphNumericMultiply3::Process(int32_t, std::vector& inputs, std::vector& outputs) { outputs.at(0).mOutput = inputs.at(0).Get() * inputs.at(1).Get() * inputs.at(2).Get(); } /*********************************************************************/ - void GraphNumericMultiplyAndAdd::Process(std::vector& inputs, std::vector& outputs) { + void GraphNumericMultiplyAndAdd::Process(int32_t, std::vector& inputs, std::vector& outputs) { outputs.at(0).mOutput = inputs.at(0).Get() * inputs.at(1).Get() + inputs.at(2).Get(); } /* Logical operations */ /*********************************************************************/ - void GraphLogicalAnd::Process(std::vector& inputs, std::vector& outputs) { + void GraphLogicalAnd::Process(int32_t, std::vector& inputs, std::vector& outputs) { bool input1 = inputs.at(0).Get() != 0.0f; bool input2 = inputs.at(1).Get() != 0.0f; outputs.at(0).mOutput = (input1 && input2) ? 1.0f : 0.0f; } /*********************************************************************/ - void GraphLogicalOr::Process(std::vector& inputs, std::vector& outputs) { + void GraphLogicalOr::Process(int32_t, std::vector& inputs, std::vector& outputs) { bool input1 = inputs.at(0).Get() != 0.0f; bool input2 = inputs.at(1).Get() != 0.0f; outputs.at(0).mOutput = (input1 || input2) ? 1.0f : 0.0f; } /*********************************************************************/ - void GraphLogicalXor::Process(std::vector& inputs, std::vector& outputs) { + void GraphLogicalXor::Process(int32_t, std::vector& inputs, std::vector& outputs) { bool input1 = inputs.at(0).Get() != 0.0f; bool input2 = inputs.at(1).Get() != 0.0f; outputs.at(0).mOutput = (input1 ^ input2) ? 1.0f : 0.0f; @@ -314,7 +319,7 @@ namespace l::nodegraph { mNode->SetInputBound(2, InputBound::INPUT_0_TO_1); } - void GraphFilterLowpass::Process(std::vector& inputs, std::vector& outputs) { + void GraphFilterLowpass::Process(int32_t, std::vector& inputs, std::vector& outputs) { float inputValue = inputs.at(0).Get(); float cutoff = inputs.at(1).Get(); float resonance = 1.0f - inputs.at(2).Get(); @@ -339,7 +344,7 @@ namespace l::nodegraph { mNode->SetInputBound(3, InputBound::INPUT_CUSTOM, 0.0001f, 1.0f); } - void GraphFilterEnvelope::Process(std::vector& inputs, std::vector& outputs) { + void GraphFilterEnvelope::Process(int32_t, std::vector& inputs, std::vector& outputs) { float noteTarget = inputs.at(0).Get(); float attackFrames = inputs.at(1).Get() * 44100.0f / 1000.0f; float releaseFrames = inputs.at(2).Get() * 44100.0f / 1000.0f; @@ -392,7 +397,7 @@ namespace l::nodegraph { mNode->SetInputBound(1, InputBound::INPUT_0_TO_1); } - void GraphOutputDebug::Process(std::vector& inputs, std::vector&) { + void GraphOutputDebug::Process(int32_t, std::vector& inputs, std::vector&) { float value = inputs.at(0).Get(); float friction = inputs.at(1).Get(); mValue += friction * friction * (value - mValue); @@ -400,10 +405,88 @@ namespace l::nodegraph { } /*********************************************************************/ - void GraphOutputSpeaker::Process(std::vector& inputs, std::vector&) { + void GraphOutputSpeaker::Reset() { + mEnvelope = 0.0f; + mNode->SetInput(2, 0.5f); + mNode->SetInputBound(2, InputBound::INPUT_0_TO_1); + } + + void GraphOutputSpeaker::Process(int32_t, std::vector& inputs, std::vector&) { auto& buffer = mAudioStream->GetWriteBuffer(); - buffer[mCurrentStereoPosition++] = inputs.at(0).Get(); - buffer[mCurrentStereoPosition++] = inputs.at(1).Get(); + + float in0 = inputs.at(0).Get(); + float in1 = inputs.at(1).Get(); + + float volume = inputs.at(2).Get(); + volume *= volume; + + if (false) { // limiter, causes distorsion + float limit = volume; + volume *= 0.95f; + + float inPreamp0 = in0 * volume; + float inPreamp1 = in1 * volume; + + if (inPreamp0 >= limit || inPreamp0 <= -limit) { + if (inPreamp0 > 0.0f) { + inPreamp0 = limit + (1.0f - limit) * l::math::functions::sigmoidFast((inPreamp0 - limit) / ((1.0f - limit) * 1.5f)); + } + else { + inPreamp0 = -(limit + (1.0f - limit) * l::math::functions::sigmoidFast((-inPreamp0 - limit) / ((1.0f - limit) * 1.5f))); + } + } + if (inPreamp1 >= limit || inPreamp1 <= -limit) { + if (inPreamp1 > 0.0f) { + inPreamp1 = limit + (1.0f - limit) * l::math::functions::sigmoidFast((inPreamp1 - limit) / ((1.0f - limit) * 1.5f)); + } + else { + inPreamp1 = -(limit + (1.0f - limit) * l::math::functions::sigmoidFast((-inPreamp1 - limit) / ((1.0f - limit) * 1.5f))); + } + } + buffer[mCurrentStereoPosition++] = inPreamp0; + buffer[mCurrentStereoPosition++] = inPreamp1; + } + else { + float attackMs = 40.0f; + float releaseMs = 40.0f; + float attack = l::math::functions::pow(0.01f, 1.0f / (attackMs * 44100.0f * 0.001f)); + float release = l::math::functions::pow(0.01f, 1.0f / (releaseMs * 44100.0f * 0.001f)); + + float limit = volume; + volume *= 0.95f; + + float inVal0 = in0 * volume; + float inVal1 = in1 * volume; + float inVal = inVal0 > inVal1 ? inVal0 : inVal1; + if (inVal > mEnvelope) { + mEnvelope = attack * (mEnvelope - inVal) + inVal; + } + else { + mEnvelope = release * (mEnvelope - inVal) + inVal; + } + + float out0 = 0.0f; + float out1 = 0.0f; + float envelopeAbs = l::math::functions::abs(mEnvelope); + if (envelopeAbs > limit) { + if (envelopeAbs > 1.0f) { + out0 = inVal0 / mEnvelope; + out1 = inVal1 / mEnvelope; + } + else { + out0 = inVal0 / (1.0f + mEnvelope - limit); + out1 = inVal1 / (1.0f + mEnvelope - limit); + } + } + else { + out0 = inVal0; + out1 = inVal1; + } + + buffer[mCurrentStereoPosition++] = out0; + buffer[mCurrentStereoPosition++] = out1; + } + mCurrentStereoPosition %= mAudioStream->GetPartTotalSize(); } @@ -431,7 +514,7 @@ namespace l::nodegraph { mNode->SetInputBound(10, InputBound::INPUT_0_TO_1); } - void GraphEffectReverb1::Process(std::vector&inputs, std::vector&outputs) { + void GraphEffectReverb1::Process(int32_t, std::vector&inputs, std::vector&outputs) { float wet = inputs.at(2).Get(); fb = 0.33f * (1.0f - inputs.at(3).Get()); @@ -493,7 +576,7 @@ namespace l::nodegraph { mNode->SetInputBound(11, InputBound::INPUT_CUSTOM, 0.001f, 0.999f); } - void GraphEffectReverb2::Process(std::vector& inputs, std::vector& outputs) { + void GraphEffectReverb2::Process(int32_t, std::vector& inputs, std::vector& outputs) { float wet = inputs.at(2).Get(); float reverbFeedback = inputs.at(3).Get(); float roomSize = inputs.at(4).Get(); @@ -621,7 +704,7 @@ namespace l::nodegraph { mNode->SetInputBound(5, InputBound::INPUT_CUSTOM, 0.0f, 10.0f); } - void GraphEffectLimiter::Process(std::vector& inputs, std::vector& outputs) { + void GraphEffectLimiter::Process(int32_t, std::vector& inputs, std::vector& outputs) { float attackMs = inputs .at(2).Get(); float releaseMs = inputs.at(3).Get(); float attack = l::math::functions::pow(0.01f, 1.0f / (attackMs * 44100.0f * 0.001f)); @@ -634,7 +717,7 @@ namespace l::nodegraph { float inVal0 = preamp * in0; float inVal1 = preamp * in1; - float inVal = 0.5f * (inVal0 + inVal1); + float inVal = inVal0 > inVal1 ? inVal0 : inVal1; if (inVal > mEnvelope) { mEnvelope = attack * (mEnvelope - inVal) + inVal; } @@ -669,7 +752,7 @@ namespace l::nodegraph { mNode->SetInputBound(3, InputBound::INPUT_CUSTOM, 1.0f, 10000.0f); } - void GraphEffectEnvelopeFollower::Process(std::vector& inputs, std::vector& outputs) { + void GraphEffectEnvelopeFollower::Process(int32_t, std::vector& inputs, std::vector& outputs) { float attackMs = inputs.at(2).Get(); float releaseMs = inputs.at(3).Get(); float attack = l::math::functions::pow(0.01f, 1.0f / (attackMs * 44100.0f * 0.001f)); @@ -678,7 +761,7 @@ namespace l::nodegraph { float in0 = inputs.at(0).Get(); float in1 = inputs.at(1).Get(); - float inVal = 0.5f * (in0 + in1); + float inVal = in0 > in1 ? in0 : in1; if (inVal > mEnvelope) { mEnvelope = attack * (mEnvelope - inVal) + inVal; } @@ -704,7 +787,7 @@ namespace l::nodegraph { mNode->SetInputBound(5, InputBound::INPUT_CUSTOM, 0.0f, 10.0f); } - void GraphEffectSaturator::Process(std::vector& inputs, std::vector& outputs) { + void GraphEffectSaturator::Process(int32_t, std::vector& inputs, std::vector& outputs) { float wet = inputs.at(2).Get(); float preamp = inputs.at(3).Get(); float limit = inputs.at(4).Get(); @@ -715,32 +798,23 @@ namespace l::nodegraph { float in0 = inputs.at(0).Get(); float in1 = inputs.at(1).Get(); - auto sigmoid = [](float x) { - if (x < 1.0f && x > -1.0f) { - return x * (1.5f - 0.5f * x * x); - } - else { - return x > 0 ? 1.0f : -1.0f; - } - }; - float inPreamp0 = in0 * preamp; float inPreamp1 = in1 * preamp; if (inPreamp0 >= limit || inPreamp0 <= -limit) { if (inPreamp0 > 0.0f) { - inPreamp0 = limit + (1.0f - limit) * sigmoid((inPreamp0 - limit) / ((1.0f - limit) * 1.5f)); + inPreamp0 = limit + (1.0f - limit) * l::math::functions::sigmoidFast((inPreamp0 - limit) / ((1.0f - limit) * 1.5f)); } else { - inPreamp0 = -(limit + (1.0f - limit) * sigmoid((-inPreamp0 - limit) / ((1.0f - limit) * 1.5f))); + inPreamp0 = -(limit + (1.0f - limit) * l::math::functions::sigmoidFast((-inPreamp0 - limit) / ((1.0f - limit) * 1.5f))); } } if (inPreamp1 >= limit || inPreamp1 <= -limit) { if (inPreamp1 > 0.0f) { - inPreamp1 = limit + (1.0f - limit) * sigmoid((inPreamp1 - limit) / ((1.0f - limit) * 1.5f)); + inPreamp1 = limit + (1.0f - limit) * l::math::functions::sigmoidFast((inPreamp1 - limit) / ((1.0f - limit) * 1.5f)); } else { - inPreamp1 = -(limit + (1.0f - limit) * sigmoid((-inPreamp1 - limit) / ((1.0f - limit) * 1.5f))); + inPreamp1 = -(limit + (1.0f - limit) * l::math::functions::sigmoidFast((-inPreamp1 - limit) / ((1.0f - limit) * 1.5f))); } } diff --git a/packages/nodegraph/source/common/NodeGraphSchema.cpp b/packages/nodegraph/source/common/NodeGraphSchema.cpp index 8816a657..d725a0f2 100644 --- a/packages/nodegraph/source/common/NodeGraphSchema.cpp +++ b/packages/nodegraph/source/common/NodeGraphSchema.cpp @@ -137,8 +137,8 @@ namespace l::nodegraph { mMainNodeGraph.Tick(time, deltaTime); } - void NodeGraphSchema::ProcessSubGraph() { - mMainNodeGraph.ProcessSubGraph(true); + void NodeGraphSchema::ProcessSubGraph(int32_t numSamples) { + mMainNodeGraph.ProcessSubGraph(numSamples, true); } diff --git a/packages/nodegraph/tests/common/NodeGraphSchemaTest.cpp b/packages/nodegraph/tests/common/NodeGraphSchemaTest.cpp index 07d74c32..e061adce 100644 --- a/packages/nodegraph/tests/common/NodeGraphSchemaTest.cpp +++ b/packages/nodegraph/tests/common/NodeGraphSchemaTest.cpp @@ -170,7 +170,7 @@ TEST(NodeGraph, GraphGroups) { } // only update the last group/node and all dependent nodes will update in the graph to produce an output - group2.ProcessSubGraph(); + group2.ProcessSubGraph(1); float output1 = group2.GetOutput(0); float output2 = group2.GetOutput(1); From bd9e25bf12485dd6c1d6772e96395008522a7c6a Mon Sep 17 00:00:00 2001 From: lnd3 Date: Wed, 4 Sep 2024 21:18:54 +0200 Subject: [PATCH 112/125] Fix an issue with recurring tick being called on the same tick due to looping over specific nodes while also looping over children. --- .../nodegraph/include/nodegraph/NodeGraph.h | 19 +++++++++++++------ .../include/nodegraph/NodeGraphOperations.h | 8 ++++---- .../include/nodegraph/NodeGraphSchema.h | 2 +- .../nodegraph/source/common/NodeGraph.cpp | 16 ++++++++++++---- .../source/common/NodeGraphOperations.cpp | 10 +++++----- .../source/common/NodeGraphSchema.cpp | 4 ++-- 6 files changed, 37 insertions(+), 22 deletions(-) diff --git a/packages/nodegraph/include/nodegraph/NodeGraph.h b/packages/nodegraph/include/nodegraph/NodeGraph.h index eb91d7c0..ad5d7de4 100644 --- a/packages/nodegraph/include/nodegraph/NodeGraph.h +++ b/packages/nodegraph/include/nodegraph/NodeGraph.h @@ -101,7 +101,7 @@ namespace l::nodegraph { void ClearProcessFlags(); virtual void ProcessSubGraph(int32_t numSamples = 1, bool recomputeSubGraphCache = true); - virtual void Tick(float time, float elapsed); + virtual void Tick(int32_t tickCount, float elapsed); virtual void SetNumInputs(int8_t numInputs); virtual void SetNumOutputs(int8_t outputCount); @@ -137,6 +137,7 @@ namespace l::nodegraph { virtual void ProcessOperation(int32_t numSamples = 1); bool mProcessUpdateHasRun = false; + int32_t mLastTickCount = 0; std::vector mInputs; std::vector mOutputs; @@ -161,7 +162,7 @@ namespace l::nodegraph { virtual ~NodeGraphOp() = default; virtual void Reset() {} virtual void Process(int32_t, std::vector&, std::vector&) {}; - virtual void Tick(float, float) {} + virtual void Tick(int32_t, float) {} virtual void SetNumInputs(int8_t numInputs); virtual void SetNumOutputs(int8_t numOutputs); @@ -247,9 +248,13 @@ namespace l::nodegraph { mProcessUpdateHasRun = true; } - virtual void Tick(float time, float elapsed) override { - NodeGraphBase::Tick(time, elapsed); - mOperation.Tick(time, elapsed); + virtual void Tick(int32_t tickCount, float elapsed) override { + if (tickCount <= mLastTickCount) { + return; + } + NodeGraphBase::Tick(tickCount, elapsed); + mOperation.Tick(tickCount, elapsed); + mLastTickCount = tickCount; } virtual std::string_view GetInputName(int8_t inputChannel) { @@ -327,13 +332,15 @@ namespace l::nodegraph { void ClearProcessFlags(); void ProcessSubGraph(int32_t numSamples, bool recomputeSubGraphCache = true); - void Tick(float time, float elapsed); + void Tick(int32_t tickCount, float elapsed); protected: NodeGraph mInputNode; NodeGraph mOutputNode; std::vector> mNodes; std::vector mOutputNodes; + + int32_t mLastTickCount = 0; }; } diff --git a/packages/nodegraph/include/nodegraph/NodeGraphOperations.h b/packages/nodegraph/include/nodegraph/NodeGraphOperations.h index 73ef6fd4..ed926cd4 100644 --- a/packages/nodegraph/include/nodegraph/NodeGraphOperations.h +++ b/packages/nodegraph/include/nodegraph/NodeGraphOperations.h @@ -28,7 +28,7 @@ namespace l::nodegraph { virtual ~GraphSourceConstants() = default; virtual void Reset(); virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; - virtual void Tick(float, float) override; + virtual void Tick(int32_t, float) override; virtual std::string_view GetName() override; virtual bool IsDataVisible(int8_t) override; @@ -51,7 +51,7 @@ namespace l::nodegraph { virtual ~GraphSourceTime() = default; virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; - virtual void Tick(float, float) override; + virtual void Tick(int32_t, float) override; virtual void Reset() override; virtual std::string_view GetOutputName(int8_t outputChannel); @@ -100,7 +100,7 @@ namespace l::nodegraph { virtual ~GraphSourceKeyboard() = default; void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; - void Tick(float time, float elapsed) override; + void Tick(int32_t tickCount, float elapsed) override; void Reset() override; virtual std::string_view GetOutputName(int8_t outputChannel) override; virtual std::string_view GetName() override; @@ -313,7 +313,7 @@ namespace l::nodegraph { virtual ~GraphFilterEnvelope() = default; virtual void Reset() override; virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; - virtual void Tick(float, float) {} + virtual void Tick(int32_t, float) {} std::string_view GetInputName(int8_t inputChannel) { return defaultInStrings[inputChannel]; diff --git a/packages/nodegraph/include/nodegraph/NodeGraphSchema.h b/packages/nodegraph/include/nodegraph/NodeGraphSchema.h index 5cd6000e..09065db3 100644 --- a/packages/nodegraph/include/nodegraph/NodeGraphSchema.h +++ b/packages/nodegraph/include/nodegraph/NodeGraphSchema.h @@ -78,7 +78,7 @@ namespace l::nodegraph { void ForEachNodeType(std::function&)> cb) const; void RegisterNodeType(const std::string& typeGroup, int32_t uniqueTypeId, std::string_view typeName); void ProcessSubGraph(int32_t numSamples); - void Tick(float time, float elapsed); + void Tick(int32_t tickCount, float elapsed); protected: NodeGraphGroup mMainNodeGraph; diff --git a/packages/nodegraph/source/common/NodeGraph.cpp b/packages/nodegraph/source/common/NodeGraph.cpp index 7bd99651..c91facb6 100644 --- a/packages/nodegraph/source/common/NodeGraph.cpp +++ b/packages/nodegraph/source/common/NodeGraph.cpp @@ -79,12 +79,16 @@ namespace l::nodegraph { mProcessUpdateHasRun = true; } - void NodeGraphBase::Tick(float time, float elapsed) { + void NodeGraphBase::Tick(int32_t tickCount, float elapsed) { + if (tickCount <= mLastTickCount) { + return; + } for (auto& link : mInputs) { if (link.mInputType == InputType::INPUT_NODE && link.mInput.mInputNode != nullptr) { - link.mInput.mInputNode->Tick(time, elapsed); + link.mInput.mInputNode->Tick(tickCount, elapsed); } } + mLastTickCount = tickCount; } float& NodeGraphBase::GetOutput(int8_t outputChannel) { @@ -461,9 +465,13 @@ namespace l::nodegraph { } } - void NodeGraphGroup::Tick(float time, float elapsed) { + void NodeGraphGroup::Tick(int32_t tickCount, float elapsed) { + if (tickCount <= mLastTickCount) { + return; + } for (auto& it : mNodes) { - it->Tick(time, elapsed); + it->Tick(tickCount, elapsed); } + mLastTickCount = tickCount; } } \ No newline at end of file diff --git a/packages/nodegraph/source/common/NodeGraphOperations.cpp b/packages/nodegraph/source/common/NodeGraphOperations.cpp index 60c6a56f..31c2fbe5 100644 --- a/packages/nodegraph/source/common/NodeGraphOperations.cpp +++ b/packages/nodegraph/source/common/NodeGraphOperations.cpp @@ -43,7 +43,7 @@ namespace l::nodegraph { } } - void GraphSourceConstants::Tick(float, float) { + void GraphSourceConstants::Tick(int32_t, float) { mNode->ProcessSubGraph(1); } @@ -79,8 +79,8 @@ namespace l::nodegraph { outputs.at(1).mOutput = mFrameTime; } - void GraphSourceTime::Tick(float time, float) { - mFrameTime = time; + void GraphSourceTime::Tick(int32_t, float deltaTime) { + mFrameTime += deltaTime; } void GraphSourceTime::Reset() { @@ -166,7 +166,7 @@ namespace l::nodegraph { } } - void GraphSourceKeyboard::Tick(float, float) { + void GraphSourceKeyboard::Tick(int32_t, float) { mKeyboard.Update(); } @@ -517,7 +517,7 @@ namespace l::nodegraph { void GraphEffectReverb1::Process(int32_t, std::vector&inputs, std::vector&outputs) { float wet = inputs.at(2).Get(); - fb = 0.33f * (1.0f - inputs.at(3).Get()); + fb = 0.2f * (1.0f - inputs.at(3).Get()); float roomSize = inputs.at(4).Get(); uint32_t bufSizeLimit = GetFramesPerRoomSize(roomSize); diff --git a/packages/nodegraph/source/common/NodeGraphSchema.cpp b/packages/nodegraph/source/common/NodeGraphSchema.cpp index d725a0f2..2ab49e81 100644 --- a/packages/nodegraph/source/common/NodeGraphSchema.cpp +++ b/packages/nodegraph/source/common/NodeGraphSchema.cpp @@ -133,8 +133,8 @@ namespace l::nodegraph { mRegisteredNodeTypes[typeGroup].push_back(UINodeDesc{ uniqueTypeId, std::string(typeName) }); } - void NodeGraphSchema::Tick(float time, float deltaTime) { - mMainNodeGraph.Tick(time, deltaTime); + void NodeGraphSchema::Tick(int32_t tickCount, float deltaTime) { + mMainNodeGraph.Tick(tickCount, deltaTime); } void NodeGraphSchema::ProcessSubGraph(int32_t numSamples) { From 622f512f6947672bb9ffe5719e4632dd07ca1f0e Mon Sep 17 00:00:00 2001 From: lnd3 Date: Wed, 4 Sep 2024 21:24:30 +0200 Subject: [PATCH 113/125] Fix parameter order. Data first, params last. --- .../include/nodegraph/NodeGraphOperations.h | 2 +- .../tests/common/NodeGraphSchemaTest.cpp | 23 ++++++++++--------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/packages/nodegraph/include/nodegraph/NodeGraphOperations.h b/packages/nodegraph/include/nodegraph/NodeGraphOperations.h index ed926cd4..c6e971c6 100644 --- a/packages/nodegraph/include/nodegraph/NodeGraphOperations.h +++ b/packages/nodegraph/include/nodegraph/NodeGraphOperations.h @@ -273,7 +273,7 @@ namespace l::nodegraph { /*********************************************************************/ class GraphFilterLowpass : public NodeGraphOp { public: - std::string defaultInStrings[3] = { "Cutoff", "Resonance", "Data"}; + std::string defaultInStrings[3] = { "Data", "Cutoff", "Resonance"}; std::string defaultOutStrings[1] = { "Out" }; GraphFilterLowpass(NodeGraphBase* node) : diff --git a/packages/nodegraph/tests/common/NodeGraphSchemaTest.cpp b/packages/nodegraph/tests/common/NodeGraphSchemaTest.cpp index e061adce..9272db0e 100644 --- a/packages/nodegraph/tests/common/NodeGraphSchemaTest.cpp +++ b/packages/nodegraph/tests/common/NodeGraphSchemaTest.cpp @@ -99,9 +99,9 @@ TEST(NodeGraph, FilterLowpass) { float resonance = 0.9f; float input = 1.3f; - nodeLowpass.SetInput(0, &cutoff); - nodeLowpass.SetInput(1, &resonance); - nodeLowpass.SetInput(2, &input); + nodeLowpass.SetInput(0, &input); + nodeLowpass.SetInput(1, &cutoff); + nodeLowpass.SetInput(2, &resonance); float oneRev = 2.0f * 3.14f / 30.0f; for (int i = 0; i < 30; i++) { @@ -135,17 +135,18 @@ TEST(NodeGraph, GraphGroups) { auto nodeLowpass1 = group.NewNode(OutputType::Default); auto nodeLowpass2 = group.NewNode(OutputType::Default); + + // left, right + nodeLowpass1->SetInput(0, group, 2); + nodeLowpass2->SetInput(0, group, 3); + // cutoff - nodeLowpass1->SetInput(0, group, 0); - nodeLowpass2->SetInput(0, group, 0); + nodeLowpass1->SetInput(1, group, 0); + nodeLowpass2->SetInput(1, group, 0); // resonance - nodeLowpass1->SetInput(1, group, 1); - nodeLowpass2->SetInput(1, group, 1); - - // left, right - nodeLowpass1->SetInput(2, group, 2); - nodeLowpass2->SetInput(2, group, 3); + nodeLowpass1->SetInput(2, group, 1); + nodeLowpass2->SetInput(2, group, 1); group.SetOutput(0, *nodeLowpass1, 0); group.SetOutput(1, *nodeLowpass2, 0); From 6162ff4317b8d38eb023226f3aa5b96cda5254ea Mon Sep 17 00:00:00 2001 From: lnd3 Date: Wed, 4 Sep 2024 21:41:20 +0200 Subject: [PATCH 114/125] Pretty up. --- packages/audio/include/audio/PortAudio.h | 4 ++++ packages/audio/source/common/PortAudio.cpp | 2 +- packages/nodegraph/source/common/NodeGraphOperations.cpp | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/audio/include/audio/PortAudio.h b/packages/audio/include/audio/PortAudio.h index 092e0246..6f864af9 100644 --- a/packages/audio/include/audio/PortAudio.h +++ b/packages/audio/include/audio/PortAudio.h @@ -38,6 +38,10 @@ namespace l::audio { mDacWriteReady.release(); } + bool CanWrite() { + return mDacWriteReady.try_acquire(); + } + int32_t GetPartTotalSize() { return mDacFramesPerBufferPart * mNumChannels; } diff --git a/packages/audio/source/common/PortAudio.cpp b/packages/audio/source/common/PortAudio.cpp index efd7b46b..9207fedf 100644 --- a/packages/audio/source/common/PortAudio.cpp +++ b/packages/audio/source/common/PortAudio.cpp @@ -146,7 +146,7 @@ namespace l::audio { } bool AudioStream::CanWrite() { - return mAudioStreamData.mDacWriteReady.try_acquire(); + return mAudioStreamData.CanWrite(); } void AudioStream::Write() { diff --git a/packages/nodegraph/source/common/NodeGraphOperations.cpp b/packages/nodegraph/source/common/NodeGraphOperations.cpp index 31c2fbe5..48952dfa 100644 --- a/packages/nodegraph/source/common/NodeGraphOperations.cpp +++ b/packages/nodegraph/source/common/NodeGraphOperations.cpp @@ -142,7 +142,7 @@ namespace l::nodegraph { float phaseMod = mPhase + pMod; phaseMod -= l::math::functions::floor(phaseMod); - *output0++ = volume * sinf(3.141529f * (mPhase + phaseMod)); + *output0++ = volume * sinf(l::math::constants::PI_f * (mPhase + phaseMod)); *output1++ = 0.5f * phaseMod; } } From deaf63e8c4a62f466279a7224e23eb50987ab3ea Mon Sep 17 00:00:00 2001 From: lnd3 Date: Thu, 5 Sep 2024 23:40:49 +0200 Subject: [PATCH 115/125] Fix the sine generators. --- packages/audio/include/audio/AudioUtils.h | 3 +- packages/audio/source/common/AudioUtils.cpp | 13 +- packages/hid/source/common/KeyboardPiano.cpp | 28 +- packages/math/include/math/MathFunc.h | 24 ++ .../include/nodegraph/NodeGraphOperations.h | 350 ++++++++++++---- .../include/nodegraph/NodeGraphSchema.h | 4 + .../source/common/NodeGraphOperations.cpp | 375 +++++++++++------- .../source/common/NodeGraphSchema.cpp | 12 + .../rendering/source/common/ui/UIVisitors.cpp | 6 +- 9 files changed, 573 insertions(+), 242 deletions(-) diff --git a/packages/audio/include/audio/AudioUtils.h b/packages/audio/include/audio/AudioUtils.h index 6624f801..6eac5491 100644 --- a/packages/audio/include/audio/AudioUtils.h +++ b/packages/audio/include/audio/AudioUtils.h @@ -4,5 +4,6 @@ namespace l::audio { float GetFrequencyFromNote(float note); - float BatchUpdate(float updateSamples, float samplesLeft, int32_t start, int32_t end, std::function process, std::function update); + double GetPhaseModifier(double note, double modifier); + float BatchUpdate(float updateSamples, float samplesLeft, int32_t start, int32_t end, std::function update, std::function process); } diff --git a/packages/audio/source/common/AudioUtils.cpp b/packages/audio/source/common/AudioUtils.cpp index f8b3c04e..ef011c65 100644 --- a/packages/audio/source/common/AudioUtils.cpp +++ b/packages/audio/source/common/AudioUtils.cpp @@ -3,22 +3,29 @@ #include "logging/LoggingAll.h" #include "math/MathAll.h" +#include "math/MathFunc.h" + #include namespace l::audio { float GetFrequencyFromNote(float note) { - return 440.0f * powf(2.0f, (note - 49.0f) / 12.0f); + return 440.0f * l::math::functions::pow(2.0f, (note - 49.0f) / 12.0f); } - float BatchUpdate(float updateSamples, float samplesLeft, int32_t start, int32_t end, std::function process, std::function update) { + double GetPhaseModifier(double note, double modifier) { + double limit = 1.0 / l::math::functions::max(note / 25.0, 1.0); + return 800.0 * modifier * modifier * limit; + } + + float BatchUpdate(float updateSamples, float samplesLeft, int32_t start, int32_t end, std::function update, std::function process) { float startNum = static_cast(start); while (startNum < static_cast(end)) { bool updated = false; if (samplesLeft < 1.0f) { samplesLeft += updateSamples; if (update != nullptr) { - update(startNum); + update(); } updated = true; } diff --git a/packages/hid/source/common/KeyboardPiano.cpp b/packages/hid/source/common/KeyboardPiano.cpp index 62c0c6e0..fbf7d3e0 100644 --- a/packages/hid/source/common/KeyboardPiano.cpp +++ b/packages/hid/source/common/KeyboardPiano.cpp @@ -18,25 +18,25 @@ namespace l::hid { if (keyCode == mKeyFunctions[KeyFunctionTypes::OCTAVE_DOWN]) { noteHandler(0, false); mOctave -= 1; - mOctave = mOctave < -5 ? -5 : mOctave; - mOctave = mOctave > 10 ? 10 : mOctave; + mOctave = mOctave < -15 ? -15 : mOctave; + mOctave = mOctave > 15 ? 15 : mOctave; } else if (keyCode == mKeyFunctions[KeyFunctionTypes::OCTAVE_UP]) { noteHandler(0, false); mOctave += 1; - mOctave = mOctave < -5 ? -5 : mOctave; - mOctave = mOctave > 10 ? 10 : mOctave; + mOctave = mOctave < -15 ? -15 : mOctave; + mOctave = mOctave > 15 ? 15 : mOctave; } else { int32_t note = ConvertKeyCodeToNote(keyCode, mOctave); - if (note > 0) { + if (note > -INT_MIN) { noteHandler(note, true); } } } if (releasedNow) { int32_t note = ConvertKeyCodeToNote(keyCode, mOctave); - if (note > 0) { + if (note > -INT_MIN) { noteHandler(note, false); } } @@ -53,25 +53,25 @@ namespace l::hid { if (keyCode == mKeyFunctions[KeyFunctionTypes::OCTAVE_DOWN]) { mNotePlayer->NoteOff(); mOctave -= 1; - mOctave = mOctave < -5 ? -5 : mOctave; - mOctave = mOctave > 10 ? 10 : mOctave; + mOctave = mOctave < -15 ? -15 : mOctave; + mOctave = mOctave > 15 ? 15 : mOctave; } else if (keyCode == mKeyFunctions[KeyFunctionTypes::OCTAVE_UP]) { mNotePlayer->NoteOff(); mOctave += 1; - mOctave = mOctave < -5 ? -5 : mOctave; - mOctave = mOctave > 10 ? 10 : mOctave; + mOctave = mOctave < -15 ? -15 : mOctave; + mOctave = mOctave > 15 ? 15 : mOctave; } else { int32_t note = ConvertKeyCodeToNote(keyCode, mOctave); - if (note > 0) { + if (note > -INT_MIN) { mNotePlayer->NoteOn(note); } } } if (releasedNow) { int32_t note = ConvertKeyCodeToNote(keyCode, mOctave); - if (note > 0) { + if (note > -INT_MIN) { mNotePlayer->NoteOff(note); } } @@ -84,7 +84,7 @@ namespace l::hid { if (key > 0) { return key + octave * 12; } - return 0; + return -INT_MIN; } int32_t KeyboardPiano::ConvertCharCodeToNote(int32_t charCode, int32_t octave) { @@ -92,7 +92,7 @@ namespace l::hid { if (key > 0) { return key + octave * 12; } - return 0; + return -INT_MIN; } void KeyboardPiano::MapKeyFunctions(KeyFunctionTypes function, int32_t keyCode) { diff --git a/packages/math/include/math/MathFunc.h b/packages/math/include/math/MathFunc.h index f5e3197e..4b9053a9 100644 --- a/packages/math/include/math/MathFunc.h +++ b/packages/math/include/math/MathFunc.h @@ -115,6 +115,30 @@ namespace l::math::functions { } } + template + T mod(T val, T mod) { + if constexpr (std::is_floating_point_v) { + if constexpr (sizeof(T) == 4) { + return modff(val, mod); + } + else if constexpr (sizeof(T) == 8) { + return fmod(val, mod); + } + } + } + + template + T round(T val) { + if constexpr (std::is_floating_point_v) { + if constexpr (sizeof(T) == 4) { + return roundf(val); + } + else if constexpr (sizeof(T) == 8) { + return roundl(val); + } + } + } + // Almost Identity(I) // Imagine you don't want to modify a signal unless it's drops to zero or close to it, in which case you want to replace the value with a small possitive constant.Then, rather than clamping the valueand introduce a discontinuity, you can smoothly blend the signal into the desired clipped value.So, let m be the threshold(anything above m stays unchanged), and n the value things will take when the signal is zero.Then, the following function does the soft clipping(in a cubic fashion): template diff --git a/packages/nodegraph/include/nodegraph/NodeGraphOperations.h b/packages/nodegraph/include/nodegraph/NodeGraphOperations.h index c6e971c6..c4b3d5ba 100644 --- a/packages/nodegraph/include/nodegraph/NodeGraphOperations.h +++ b/packages/nodegraph/include/nodegraph/NodeGraphOperations.h @@ -4,6 +4,7 @@ #include "logging/LoggingAll.h" #include "hid/KeyboardPiano.h" #include "audio/PortAudio.h" +#include "math/MathFunc.h" #include #include @@ -29,11 +30,21 @@ namespace l::nodegraph { virtual void Reset(); virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; virtual void Tick(int32_t, float) override; - - virtual std::string_view GetName() override; - virtual bool IsDataVisible(int8_t) override; - virtual bool IsDataEditable(int8_t) override; - + virtual std::string_view GetName() override { + switch (mMode) { + case 0: + return "Constant [0,1]"; + case 1: + return "Constant [-1,1]"; + case 2: + return "Constant [0,100]"; + case 3: + return "Constant [-inf,inf]"; + }; + return ""; + } + virtual bool IsDataVisible(int8_t) override {return true;} + virtual bool IsDataEditable(int8_t) override {return true;} protected: int32_t mMode; float mMax = 1.0f; @@ -50,13 +61,15 @@ namespace l::nodegraph { std::string defaultOutStrings[2] = { "Audio Time", "Frame Time"}; virtual ~GraphSourceTime() = default; + virtual void Reset() override; virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; virtual void Tick(int32_t, float) override; - - virtual void Reset() override; - virtual std::string_view GetOutputName(int8_t outputChannel); - virtual std::string_view GetName() override; - + virtual std::string_view GetOutputName(int8_t outputChannel) override { + return defaultOutStrings[outputChannel]; + } + virtual std::string_view GetName() override { + return "Time"; + } protected: float mAudioTime = 0.0f; float mFrameTime = 0.0f; @@ -66,25 +79,166 @@ namespace l::nodegraph { class GraphSourceSine : public NodeGraphOp { public: GraphSourceSine(NodeGraphBase* node) : - NodeGraphOp(node, 5, 2) + NodeGraphOp(node, 5, 1) {} std::string defaultInStrings[5] = { "Note", "Volume", "Fmod", "Phase", "Reset"}; - std::string defaultOutStrings[2] = { "Sine", "Phase"}; + std::string defaultOutStrings[1] = { "Out"}; virtual ~GraphSourceSine() = default; + virtual void Reset() override; + virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; + virtual bool IsDataVisible(int8_t) override { return true; } + virtual bool IsDataEditable(int8_t) override { return true; } + virtual std::string_view GetInputName(int8_t inputChannel) override { + return defaultInStrings[inputChannel]; + } + virtual std::string_view GetOutputName(int8_t outputChannel) override { + return defaultOutStrings[outputChannel]; + } + virtual std::string_view GetName() override { + return "Sine FM"; + } + protected: + double mNote = 0.0f; + float mVolume = 0.0f; + double mFmod = 0.0f; + double mPmod = 0.0f; + float mReset = 0.0f; + + double mWave = 0.0f; + double mDeltaTime = 0.0f; + float mVol = 0.0f; + + double mPhase = 0.0f; + double mPhaseMod = 0.0f; + float mSamplesUntilUpdate = 0.0f; + }; + + /*********************************************************************/ + class GraphSourceSineFM : public NodeGraphOp { + public: + GraphSourceSineFM(NodeGraphBase* node) : + NodeGraphOp(node, 8, 1) + {} + + std::string defaultInStrings[8] = { "Note", "Volume", "Fmod", "FmodFreq", "FmodVol", "FmodOfs", "FmodGain", "Reset"}; + std::string defaultOutStrings[1] = { "Out" }; + virtual ~GraphSourceSineFM() = default; virtual void Reset() override; virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; + virtual bool IsDataVisible(int8_t) override { return true; } + virtual bool IsDataEditable(int8_t) override { return true; } + virtual std::string_view GetInputName(int8_t inputChannel) override { + return defaultInStrings[inputChannel]; + } + virtual std::string_view GetOutputName(int8_t outputChannel) override { + return defaultOutStrings[outputChannel]; + } + virtual std::string_view GetName() override { + return "Sine FM"; + } + protected: + double mNote = 0.0; + float mVolume = 0.0f; + double mFmod = 0.0; + double mPmod = 0.0; + float mReset = 0.0f; + + double mWave = 0.0; + double mDeltaTime = 0.0; + float mVol = 0.0f; + + double mPhase = 0.0; - virtual std::string_view GetInputName(int8_t inputChannel); - virtual std::string_view GetOutputName(int8_t outputChannel); - virtual std::string_view GetName() override; + double mPhaseFmod = 0.0; + double mFmodFrq = 0.0; + double mFmodVol = 0.0; + double mFmodOfs = 0.0; + + float mSamplesUntilUpdate = 0.0f; + }; + + /*********************************************************************/ + class GraphSourceSineFM2 : public NodeGraphOp { + public: + GraphSourceSineFM2(NodeGraphBase* node) : + NodeGraphOp(node, 5, 1) + {} + + std::string defaultInStrings[5] = { "Note", "Volume", "FmodVol", "FmodFreq", "Reset" }; + std::string defaultOutStrings[1] = { "Out" }; + + virtual ~GraphSourceSineFM2() = default; + virtual void Reset() override; + virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; + virtual bool IsDataVisible(int8_t) override { return true; } + virtual bool IsDataEditable(int8_t) override { return true; } + virtual std::string_view GetInputName(int8_t inputChannel) override { + return defaultInStrings[inputChannel]; + } + virtual std::string_view GetOutputName(int8_t outputChannel) override { + return defaultOutStrings[outputChannel]; + } + virtual std::string_view GetName() override { + return "Sine FM 2"; + } protected: - float mPhase = 0.0f; + double mNote = 0.0; + float mVolume = 0.0f; + double mFmod = 0.0; + double mPmod = 0.0; + float mReset = 0.0f; + + double mWave = 0.0; + double mDeltaTime = 0.0; + double mDeltaLimit = 0.0; + float mVol = 0.0f; + + double mPhase = 0.0; + double mPhaseFmod = 0.0; + float mSamplesUntilUpdate = 0.0f; }; + /*********************************************************************/ + class GraphSourceSineFM3 : public NodeGraphOp { + public: + GraphSourceSineFM3(NodeGraphBase* node) : + NodeGraphOp(node, 4, 1) + {} + + std::string defaultInStrings[4] = { "Note", "Volume", "Fmod", "Reset" }; + std::string defaultOutStrings[1] = { "Out" }; + + virtual ~GraphSourceSineFM3() = default; + virtual void Reset() override; + virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; + virtual bool IsDataVisible(int8_t) override { return true; } + virtual bool IsDataEditable(int8_t) override { return true; } + virtual std::string_view GetInputName(int8_t inputChannel) override { + return defaultInStrings[inputChannel]; + } + virtual std::string_view GetOutputName(int8_t outputChannel) override { + return defaultOutStrings[outputChannel]; + } + virtual std::string_view GetName() override { + return "Sine FM 3"; + } + protected: + double mNote = 0.0; + float mVolume = 0.0f; + double mPhaseFmod = 0.0; + float mReset = 0.0f; + + double mWave = 0.0; + double mDeltaTime = 0.0; + double mDeltaLimit = 0.0; + float mVol = 0.0f; + double mPhase = 0.0; + float mSamplesUntilUpdate = 0.0f; + }; /*********************************************************************/ class GraphSourceKeyboard : public NodeGraphOp, public l::hid::INoteProcessor { public: @@ -99,12 +253,18 @@ namespace l::nodegraph { std::string defaultOutStrings[8] = { "Note 1", "Note 2", "Note 3", "Note 4", "Note 5", "Note 6", "Note 7", "Note 8" }; virtual ~GraphSourceKeyboard() = default; - void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; - void Tick(int32_t tickCount, float elapsed) override; - void Reset() override; - virtual std::string_view GetOutputName(int8_t outputChannel) override; - virtual std::string_view GetName() override; - virtual bool IsDataVisible(int8_t) override; + virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; + virtual void Tick(int32_t tickCount, float elapsed) override; + virtual void Reset() override; + virtual std::string_view GetOutputName(int8_t outputChannel) override { + return defaultOutStrings[outputChannel]; + } + virtual std::string_view GetName() override { + return "Keyboard"; + } + virtual bool IsDataVisible(int8_t) override { + return true; + } virtual void NoteOn(int32_t note) override; virtual void NoteOff() override; virtual void NoteOff(int32_t note) override; @@ -124,7 +284,9 @@ namespace l::nodegraph { NodeGraphOp(node, 2, 1) {} virtual ~GraphNumericAdd() = default; - void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; + virtual void Process(int32_t, std::vector& inputs, std::vector& outputs) override { + outputs.at(0).mOutput = inputs.at(0).Get() + inputs.at(1).Get(); + } virtual std::string_view GetName() override { return "Add"; } @@ -138,7 +300,9 @@ namespace l::nodegraph { {} virtual ~GraphNumericMultiply() = default; - void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; + virtual void Process(int32_t, std::vector& inputs, std::vector& outputs) override { + outputs.at(0).mOutput = inputs.at(0).Get() * inputs.at(1).Get(); + } virtual std::string_view GetName() override { return "Multiply"; } @@ -151,7 +315,9 @@ namespace l::nodegraph { NodeGraphOp(node, 2, 1) {} virtual ~GraphNumericSubtract() = default; - void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; + virtual void Process(int32_t, std::vector& inputs, std::vector& outputs) override { + outputs.at(0).mOutput = inputs.at(0).Get() - inputs.at(1).Get(); + } virtual std::string_view GetName() override { return "Subtract"; } @@ -165,7 +331,9 @@ namespace l::nodegraph { {} virtual ~GraphNumericNegate() = default; - void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; + virtual void Process(int32_t, std::vector& inputs, std::vector& outputs) override { + outputs.at(0).mOutput = -inputs.at(0).Get(); + } virtual std::string_view GetName() override { return "Negate"; } @@ -179,8 +347,14 @@ namespace l::nodegraph { {} virtual ~GraphNumericIntegral() = default; - void Reset() override; - void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; + virtual void Reset() override { + mOutput = 0.0f; + } + + virtual void Process(int32_t, std::vector& inputs, std::vector& outputs) override { + mOutput += inputs.at(0).Get(); + outputs.at(0).mOutput = mOutput; + } virtual std::string_view GetName() override { return "Integral"; } @@ -197,7 +371,9 @@ namespace l::nodegraph { {} virtual ~GraphNumericMultiply3() = default; - void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; + virtual void Process(int32_t, std::vector& inputs, std::vector& outputs) override { + outputs.at(0).mOutput = inputs.at(0).Get() * inputs.at(1).Get() * inputs.at(2).Get(); + } virtual std::string_view GetName() override { return "Multiply3"; } @@ -213,7 +389,9 @@ namespace l::nodegraph { std::string defaultInStrings[3] = { "Factor 1", "Factor 2", "Term 1" }; virtual ~GraphNumericMultiplyAndAdd() = default; - void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; + void virtual Process(int32_t, std::vector& inputs, std::vector& outputs) override { + outputs.at(0).mOutput = inputs.at(0).Get() * inputs.at(1).Get() + inputs.at(2).Get(); + } virtual std::string_view GetName() override { return "Multiply & Add"; } @@ -223,6 +401,20 @@ namespace l::nodegraph { } }; + /*********************************************************************/ + class GraphNumericRound : public NodeGraphOp { + public: + GraphNumericRound(NodeGraphBase* node) : + NodeGraphOp(node, 1, 1) + {} + virtual ~GraphNumericRound() = default; + virtual void Process(int32_t, std::vector& inputs, std::vector& outputs) override { + outputs.at(0).mOutput = l::math::functions::round(inputs.at(0).Get()); + } + virtual std::string_view GetName() override { + return "Round"; + } + }; /* Logical operations */ @@ -234,7 +426,11 @@ namespace l::nodegraph { {} virtual ~GraphLogicalAnd() = default; - virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; + virtual void Process(int32_t, std::vector& inputs, std::vector& outputs) override { + bool input1 = inputs.at(0).Get() != 0.0f; + bool input2 = inputs.at(1).Get() != 0.0f; + outputs.at(0).mOutput = (input1 && input2) ? 1.0f : 0.0f; + } std::string_view GetName() override { return "And"; } @@ -248,7 +444,11 @@ namespace l::nodegraph { {} virtual ~GraphLogicalOr() = default; - virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; + virtual void Process(int32_t, std::vector& inputs, std::vector& outputs) override { + bool input1 = inputs.at(0).Get() != 0.0f; + bool input2 = inputs.at(1).Get() != 0.0f; + outputs.at(0).mOutput = (input1 || input2) ? 1.0f : 0.0f; + } std::string_view GetName() override { return "Or"; } @@ -262,7 +462,11 @@ namespace l::nodegraph { {} virtual ~GraphLogicalXor() = default; - virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; + virtual void Process(int32_t, std::vector& inputs, std::vector& outputs) override { + bool input1 = inputs.at(0).Get() != 0.0f; + bool input2 = inputs.at(1).Get() != 0.0f; + outputs.at(0).mOutput = (input1 ^ input2) ? 1.0f : 0.0f; + } virtual std::string_view GetName() override { return "Xor"; } @@ -283,12 +487,13 @@ namespace l::nodegraph { virtual ~GraphFilterLowpass() = default; virtual void Reset() override; virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; - - virtual std::string_view GetInputName(int8_t inputChannel) { + virtual bool IsDataVisible(int8_t) override { return true; } + virtual bool IsDataEditable(int8_t channel) override { return channel > 0 ? true : false; } + virtual std::string_view GetInputName(int8_t inputChannel) override { return defaultInStrings[inputChannel]; } - virtual std::string_view GetOutputName(int8_t outputChannel) { + virtual std::string_view GetOutputName(int8_t outputChannel) override { return defaultOutStrings[outputChannel]; } @@ -314,24 +519,19 @@ namespace l::nodegraph { virtual void Reset() override; virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; virtual void Tick(int32_t, float) {} - - std::string_view GetInputName(int8_t inputChannel) { + virtual std::string_view GetInputName(int8_t inputChannel) override { return defaultInStrings[inputChannel]; } - - std::string_view GetOutputName(int8_t outputChannel) { + virtual std::string_view GetOutputName(int8_t outputChannel) override { return defaultOutStrings[outputChannel]; } - - bool IsDataVisible(int8_t channel) { + virtual bool IsDataVisible(int8_t channel) override { return channel >= 1 ? true : false; } - - bool IsDataEditable(int8_t channel) { + virtual bool IsDataEditable(int8_t channel) override { return channel >= 1 ? true : false; } - - std::string_view GetName() override { + virtual std::string_view GetName() override { return "Envelope"; } protected: @@ -358,7 +558,7 @@ namespace l::nodegraph { virtual bool IsDataEditable(int8_t channel) override { return channel == 1; } - virtual std::string_view GetOutputName(int8_t outputChannel) { + virtual std::string_view GetOutputName(int8_t outputChannel) override { return defaultInStrings[outputChannel]; } virtual std::string_view GetName() override { @@ -380,20 +580,18 @@ namespace l::nodegraph { {} virtual ~GraphOutputSpeaker() = default; - void Reset() override; - void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; - - bool IsDataVisible(int8_t) override { + virtual void Reset() override; + virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; + virtual bool IsDataVisible(int8_t) override { return true; } - bool IsDataEditable(int8_t channel) override { + virtual bool IsDataEditable(int8_t channel) override { return channel == 2 ? true : false; } - std::string_view GetInputName(int8_t outputChannel) { + virtual std::string_view GetInputName(int8_t outputChannel) override { return defaultInStrings[outputChannel]; } - - std::string_view GetName() override { + virtual std::string_view GetName() override { return "Speaker"; } @@ -429,25 +627,19 @@ namespace l::nodegraph { virtual void Reset() override; virtual void Process(int32_t numSamples, std::vector&inputs, std::vector&outputs) override; - virtual bool IsDataVisible(int8_t num) override { return num >= 2 ? true : false; - } - virtual bool IsDataEditable(int8_t num) override { return num >= 2 ? true : false; } - - std::string_view GetInputName(int8_t inputChannel) { + virtual std::string_view GetInputName(int8_t inputChannel) override { return defaultInStrings[inputChannel]; } - - std::string_view GetOutputName(int8_t outputChannel) { + virtual std::string_view GetOutputName(int8_t outputChannel) override { return defaultOutStrings[outputChannel]; } - - std::string_view GetName() override { + virtual std::string_view GetName() override { return "Reverb 1"; } @@ -495,22 +687,19 @@ namespace l::nodegraph { virtual ~GraphEffectReverb2() = default; virtual void Reset() override; virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; - virtual bool IsDataVisible(int8_t num) override { return num >= 2 ? true : false; } virtual bool IsDataEditable(int8_t num) override { return num >= 2 ? true : false; } - - std::string_view GetInputName(int8_t inputChannel) { + virtual std::string_view GetInputName(int8_t inputChannel) override { return defaultInStrings[inputChannel]; } - std::string_view GetOutputName(int8_t outputChannel) { + virtual std::string_view GetOutputName(int8_t outputChannel) override { return defaultOutStrings[outputChannel]; } - - std::string_view GetName() override { + virtual std::string_view GetName() override { return "Reverb 2"; } @@ -545,20 +734,19 @@ namespace l::nodegraph { virtual ~GraphEffectLimiter() = default; virtual void Reset() override; virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; - virtual bool IsDataVisible(int8_t num) override { return num >= 2 ? true : false; } virtual bool IsDataEditable(int8_t num) override { return num >= 2 ? true : false; } - std::string_view GetInputName(int8_t inputChannel) { + virtual std::string_view GetInputName(int8_t inputChannel) override { return defaultInStrings[inputChannel]; } - std::string_view GetOutputName(int8_t outputChannel) { + virtual std::string_view GetOutputName(int8_t outputChannel) override { return defaultOutStrings[outputChannel]; } - std::string_view GetName() override { + virtual std::string_view GetName() override { return "Limiter"; } @@ -579,20 +767,19 @@ namespace l::nodegraph { virtual ~GraphEffectEnvelopeFollower() = default; virtual void Reset() override; virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; - virtual bool IsDataVisible(int8_t num) override { return num >= 2 ? true : false; } virtual bool IsDataEditable(int8_t num) override { return num >= 2 ? true : false; } - std::string_view GetInputName(int8_t inputChannel) { + virtual std::string_view GetInputName(int8_t inputChannel) override { return defaultInStrings[inputChannel]; } - std::string_view GetOutputName(int8_t outputChannel) { + virtual std::string_view GetOutputName(int8_t outputChannel) override { return defaultOutStrings[outputChannel]; } - std::string_view GetName() override { + virtual std::string_view GetName() override { return "Envelope Follower"; } @@ -613,20 +800,19 @@ namespace l::nodegraph { virtual ~GraphEffectSaturator() = default; virtual void Reset() override; virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; - virtual bool IsDataVisible(int8_t num) override { return num >= 2 ? true : false; } virtual bool IsDataEditable(int8_t num) override { return num >= 2 ? true : false; } - std::string_view GetInputName(int8_t inputChannel) { + virtual std::string_view GetInputName(int8_t inputChannel) override { return defaultInStrings[inputChannel]; } - std::string_view GetOutputName(int8_t outputChannel) { + virtual std::string_view GetOutputName(int8_t outputChannel) override { return defaultOutStrings[outputChannel]; } - std::string_view GetName() override { + virtual std::string_view GetName() override { return "Saturator"; } diff --git a/packages/nodegraph/include/nodegraph/NodeGraphSchema.h b/packages/nodegraph/include/nodegraph/NodeGraphSchema.h index 09065db3..d0772672 100644 --- a/packages/nodegraph/include/nodegraph/NodeGraphSchema.h +++ b/packages/nodegraph/include/nodegraph/NodeGraphSchema.h @@ -39,6 +39,9 @@ namespace l::nodegraph { RegisterNodeType("Source", 4, "Keyboard"); RegisterNodeType("Source", 5, "Time"); RegisterNodeType("Source", 6, "Sine"); + RegisterNodeType("Source", 7, "Sine FM"); + RegisterNodeType("Source", 8, "Sine FM 2"); + RegisterNodeType("Source", 9, "Sine FM 3"); RegisterNodeType("Numeric", 50, "Add"); RegisterNodeType("Numeric", 51, "Subtract"); RegisterNodeType("Numeric", 52, "Negate"); @@ -46,6 +49,7 @@ namespace l::nodegraph { RegisterNodeType("Numeric", 54, "Integral"); RegisterNodeType("Numeric", 55, "Multiply3"); RegisterNodeType("Numeric", 56, "Multiply & Add"); + RegisterNodeType("Numeric", 57, "Round"); RegisterNodeType("Logic", 100, "And"); RegisterNodeType("Logic", 101, "Or"); RegisterNodeType("Logic", 102, "Xor"); diff --git a/packages/nodegraph/source/common/NodeGraphOperations.cpp b/packages/nodegraph/source/common/NodeGraphOperations.cpp index 48952dfa..43ecc67d 100644 --- a/packages/nodegraph/source/common/NodeGraphOperations.cpp +++ b/packages/nodegraph/source/common/NodeGraphOperations.cpp @@ -47,28 +47,6 @@ namespace l::nodegraph { mNode->ProcessSubGraph(1); } - std::string_view GraphSourceConstants::GetName() { - switch (mMode) { - case 0: - return "Constant [0,1]"; - case 1: - return "Constant [-1,1]"; - case 2: - return "Constant [0,100]"; - case 3: - return "Constant [-inf,inf]"; - }; - return ""; - } - - bool GraphSourceConstants::IsDataVisible(int8_t) { - return true; - } - - bool GraphSourceConstants::IsDataEditable(int8_t) { - return true; - } - /*********************************************************************/ void GraphSourceTime::Process(int32_t, std::vector&, std::vector& outputs) { float rate = 44100.0f; @@ -88,14 +66,6 @@ namespace l::nodegraph { mFrameTime = 0.0f; } - std::string_view GraphSourceTime::GetOutputName(int8_t outputChannel) { - return defaultOutStrings[outputChannel]; - } - - std::string_view GraphSourceTime::GetName() { - return "Time"; - } - /*********************************************************************/ void GraphSourceSine::Reset() { // { "Freq Hz", "Freq Mod", "Phase Mod", "Reset"}; @@ -106,59 +76,261 @@ namespace l::nodegraph { mNode->SetInput(3, 0.0f); mNode->SetInput(4, 0.0f); mNode->SetInputBound(0, InputBound::INPUT_UNBOUNDED); - mNode->SetInputBound(1, InputBound::INPUT_0_TO_1); + mNode->SetInputBound(1, InputBound::INPUT_0_100); mNode->SetInputBound(2, InputBound::INPUT_UNBOUNDED); mNode->SetInputBound(3, InputBound::INPUT_UNBOUNDED); mNode->SetInputBound(4, InputBound::INPUT_0_TO_1); } - void GraphSourceSine::Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) { - float freq = inputs.at(0).Get(); - float volume = inputs.at(1).Get(); - float fMod = 1.0f + inputs.at(2).Get(); - float pMod = inputs.at(3).Get(); - float reset = inputs.at(4).Get(); - - if (freq == 0.0f) { - mPhase = 0.0f; - outputs.at(0).mOutput = 0.0f; - return; - } - if (reset > 0.5f) { - mPhase = 0.0f; - } - float* output0 = &outputs.at(0).GetOutput(numSamples); - float* output1 = &outputs.at(1).GetOutput(numSamples); - for (int32_t i = 0; i < numSamples; i++) { - float deltaTime = 1.0f / 44100.0f; - float phaseDelta = deltaTime * freq; + mSamplesUntilUpdate = l::audio::BatchUpdate(256.0f, mSamplesUntilUpdate, 0, numSamples, + [&]() { + mNote = l::math::functions::max(static_cast(inputs.at(0).Get()), 0.0); + mVolume = inputs.at(1).Get(); + mReset = inputs.at(4).Get(); - mPhase += phaseDelta * fMod; - mPhase -= l::math::functions::floor(mPhase); + if (mNote == 0.0f) { + mVolume = 0.0f; + outputs.at(0).mOutput = 0.0f; + return; + } + if (mReset > 0.5f) { + mVolume = 0.0f; + } + mDeltaTime = 1.0 / 44100.0; - float phaseMod = mPhase + pMod; - phaseMod -= l::math::functions::floor(phaseMod); + }, + [&](int32_t start, int32_t end, bool) { - *output0++ = volume * sinf(l::math::constants::PI_f * (mPhase + phaseMod)); - *output1++ = 0.5f * phaseMod; - } + mFmod = 1.0 + static_cast(inputs.at(2).Get()); + mPmod = static_cast(inputs.at(3).Get()); + + for (int32_t i = start; i < end; i++) { + double phaseDelta = mDeltaTime * mNote; + + mPhase += phaseDelta * mFmod; + mPhase = l::math::functions::mod(mPhase, 1.0); + + double phaseMod = mPhase + mPmod; + phaseMod = l::math::functions::mod(phaseMod, 1.0); + + double waveTarget = l::math::functions::sin(l::math::constants::PI * (mPhase + phaseMod)); + + mVol += (1.0f / 256.0f) * (mVolume - mVol); + mWave += (waveTarget - mWave) * 0.5; // sample/smooth at half nyqvist frequency + + *output0++ = mVol * static_cast(mWave); + } + } + ); } - std::string_view GraphSourceSine::GetInputName(int8_t inputChannel) { - return defaultInStrings[inputChannel]; + /*********************************************************************/ + void GraphSourceSineFM::Reset() { + // { "Note", "Volume", "Fmod", "FmodFreq", "FmodVol", "FmodOfs", "Reset"} + mPhase = 0.0f; + mNode->SetInput(0, 0.0f); + mNode->SetInput(1, 0.5f); + mNode->SetInput(2, 0.0f); + mNode->SetInput(3, 0.0f); + mNode->SetInput(4, 0.0f); + mNode->SetInput(5, 0.0f); + mNode->SetInput(6, 0.0f); + mNode->SetInput(7, 0.0f); + mNode->SetInputBound(0, InputBound::INPUT_UNBOUNDED); + mNode->SetInputBound(1, InputBound::INPUT_0_100); + mNode->SetInputBound(2, InputBound::INPUT_UNBOUNDED); + mNode->SetInputBound(3, InputBound::INPUT_UNBOUNDED); + mNode->SetInputBound(4, InputBound::INPUT_UNBOUNDED); + mNode->SetInputBound(5, InputBound::INPUT_UNBOUNDED); + mNode->SetInputBound(6, InputBound::INPUT_0_TO_1); + mNode->SetInputBound(7, InputBound::INPUT_0_TO_1); } - std::string_view GraphSourceSine::GetOutputName(int8_t outputChannel) { - return defaultOutStrings[outputChannel]; + void GraphSourceSineFM::Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) { + float* output0 = &outputs.at(0).GetOutput(numSamples); + + mSamplesUntilUpdate = l::audio::BatchUpdate(256.0f, mSamplesUntilUpdate, 0, numSamples, + [&]() { + mNote = l::math::functions::max(static_cast(inputs.at(0).Get()), 0.0); + mVolume = inputs.at(1).Get(); + mReset = inputs.at(7).Get(); + + if (mReset > 0.0f || mVolume + mVol < 0.0000001f) { + mPhase = 0.0; + mPhaseFmod = 0.0; + mVolume = 0.0f; + outputs.at(0).mOutput = 0.0f; + return; + } + mDeltaTime = 1.0 / 44100.0; + + }, + [&](int32_t start, int32_t end, bool) { + + mFmod = static_cast(inputs.at(2).Get()); + mFmodFrq = static_cast(inputs.at(3).Get()); + mFmodVol = static_cast(inputs.at(4).Get()); + mFmodOfs = static_cast(inputs.at(5).Get()); + double fmodGain = static_cast(inputs.at(6).Get()); + + for (int32_t i = start; i < end; i++) { + double phaseDelta2 = mDeltaTime * mNote * mFmodFrq; + mPhaseFmod += phaseDelta2; + mPhaseFmod = l::math::functions::mod(mPhaseFmod, 1.0); + double modWave = fmodGain * l::math::functions::sin(l::math::constants::PI * mPhaseFmod * 0.5); + double fmod = (mFmodOfs + 1.0) * mFmodVol + mFmodVol * modWave; + fmod = l::math::functions::clamp(fmod, 0.0, 500.0); + + double phaseDelta = mDeltaTime * mNote * (fmod + 1.0) * (mFmod + 1.0); + phaseDelta = l::math::functions::clamp(phaseDelta, phaseDelta2, 0.5); + mPhase += phaseDelta; + mPhase = l::math::functions::mod(mPhase, 1.0); + double waveTarget = l::math::functions::sin(l::math::constants::PI * mPhase * 2.0); + + mVol += (1.0f / 256.0f) * (mVolume - mVol); + mWave += (waveTarget - mWave) * 0.5; + + *output0++ = mVol * static_cast(mWave); + } + } + ); } - std::string_view GraphSourceSine::GetName() { - return "Sine"; + /*********************************************************************/ + void GraphSourceSineFM2::Reset() { + // { "Note", "Volume", "FmodVol", "FmodOfs", "Reset"} + mPhase = 0.0f; + mNode->SetInput(0, 0.0f); + mNode->SetInput(1, 0.5f); + mNode->SetInput(2, 0.0f); + mNode->SetInput(3, 0.0f); + mNode->SetInput(4, 0.0f); + mNode->SetInputBound(0, InputBound::INPUT_UNBOUNDED); + mNode->SetInputBound(1, InputBound::INPUT_0_100); + mNode->SetInputBound(2, InputBound::INPUT_0_TO_1); + mNode->SetInputBound(3, InputBound::INPUT_0_100); + mNode->SetInputBound(4, InputBound::INPUT_0_TO_1); } + void GraphSourceSineFM2::Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) { + float* output0 = &outputs.at(0).GetOutput(numSamples); + + mSamplesUntilUpdate = l::audio::BatchUpdate(256.0f, mSamplesUntilUpdate, 0, numSamples, + [&]() { + mNote = l::math::functions::max(static_cast(inputs.at(0).Get()), 0.0); + mVolume = inputs.at(1).Get(); + mReset = inputs.at(4).Get(); + + if (mReset > 0.0f || mVolume + mVol < 0.0000001f) { + mPhase = 0.0; + mPhaseFmod = 0.0; + mVolume = 0.0f; + outputs.at(0).mOutput = 0.0f; + return; + } + mDeltaTime = 1.0 / 44100.0; + mDeltaLimit = mDeltaTime * 2.0; + + }, + [&](int32_t start, int32_t end, bool) { + + double fmMod = static_cast(inputs.at(2).Get()); + double fmFreq = static_cast(inputs.at(3).Get()); + + double limitFmMod = 1.0 / l::math::functions::max(mNote / 25.0, 1.0); + fmMod = 800.0 * fmMod * fmMod * limitFmMod; + + for (int32_t i = start; i < end; i++) { + double maxFmModulation = 1.0 / l::math::functions::max(mNote * fmFreq * mDeltaLimit, 1.0); + maxFmModulation *= maxFmModulation; + maxFmModulation *= maxFmModulation; + fmFreq = fmFreq * maxFmModulation; + + double fmNote = mNote * fmFreq; + double phaseDelta2 = mDeltaTime * fmNote; + mPhaseFmod += phaseDelta2; + mPhaseFmod = l::math::functions::mod(mPhaseFmod, 1.0); + mFmod = (fmMod + fmMod * l::math::functions::sin(l::math::constants::PI * mPhaseFmod * 2.0)); + + double phaseDelta = mDeltaTime * mNote * (mFmod + 1.0) / (fmMod + 1.0); + + mPhase += phaseDelta; + mPhase = l::math::functions::mod(mPhase, 1.0); + double waveTarget = l::math::functions::sin(l::math::constants::PI * mPhase * 2.0); + + + mVol += (1.0f / 256.0f) * (mVolume - mVol); + mWave += (waveTarget - mWave) * 0.5; + + *output0++ = mVol * static_cast(mWave); + } + } + ); + } + + /*********************************************************************/ + void GraphSourceSineFM3::Reset() { + // { "Note", "Volume", "Fmod", "Reset"} + mPhase = 0.0f; + mNode->SetInput(0, 0.0f); + mNode->SetInput(1, 0.5f); + mNode->SetInput(2, 0.0f); + mNode->SetInput(3, 0.0f); + mNode->SetInputBound(0, InputBound::INPUT_UNBOUNDED); + mNode->SetInputBound(1, InputBound::INPUT_0_100); + mNode->SetInputBound(2, InputBound::INPUT_0_TO_1); + mNode->SetInputBound(3, InputBound::INPUT_0_TO_1); + } + + void GraphSourceSineFM3::Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) { + float* output0 = &outputs.at(0).GetOutput(numSamples); + + mSamplesUntilUpdate = l::audio::BatchUpdate(256.0f, mSamplesUntilUpdate, 0, numSamples, + [&]() { + mNote = l::math::functions::max(static_cast(inputs.at(0).Get()), 0.0); + mVolume = inputs.at(1).Get(); + mReset = inputs.at(3).Get(); + + if (mReset > 0.0f || mVolume < 0.0000001f) { + mPhase = 0.0; + mPhaseFmod = 0.0; + mVolume = 0.0f; + outputs.at(0).mOutput = 0.0f; + return; + } + mDeltaTime = 1.0 / 44100.0; + mDeltaLimit = mDeltaTime * 4.0; + }, + [&](int32_t start, int32_t end, bool) { + + double fmMod = static_cast(inputs.at(2).Get()); + + double limitFmMod = 1.0 / l::math::functions::max(mNote / 25.0, 1.0); + fmMod = 800.0 * fmMod * fmMod * limitFmMod; + + for (int32_t i = start; i < end; i++) { + double phaseDelta2 = mDeltaTime * mNote; + mPhaseFmod += phaseDelta2; + mPhaseFmod = l::math::functions::mod(mPhaseFmod, 1.0); + double modulation = fmMod * l::math::functions::sin(l::math::constants::PI * mPhaseFmod * 2.0); + + double phaseDelta = mDeltaTime * mNote * modulation; + mPhase += phaseDelta; + mPhase = l::math::functions::mod(mPhase, 1.0); + double waveTarget = l::math::functions::sin(l::math::constants::PI * mPhase * 2.0); + + + mVol += (1.0f / 256.0f) * (mVolume - mVol); + mWave += (waveTarget - mWave) * 0.5; + + *output0++ = mVol * static_cast(mWave); + } + } + ); + } /*********************************************************************/ void GraphSourceKeyboard::Process(int32_t, std::vector& inputs, std::vector& outputs) { for (size_t i = 0; i < inputs.size();i++) { @@ -176,18 +348,6 @@ namespace l::nodegraph { } } - std::string_view GraphSourceKeyboard::GetOutputName(int8_t outputChannel) { - return defaultOutStrings[outputChannel]; - } - - std::string_view GraphSourceKeyboard::GetName() { - return "Keyboard"; - } - - bool GraphSourceKeyboard::IsDataVisible(int8_t) { - return true; - } - void GraphSourceKeyboard::NoteOn(int32_t note) { float frequency = l::audio::GetFrequencyFromNote(static_cast(note)); int8_t channel = GetNextNoteChannel(note); @@ -244,69 +404,6 @@ namespace l::nodegraph { return lowestCountIndex; } - /*********************************************************************/ - void GraphNumericAdd::Process(int32_t, std::vector& inputs, std::vector& outputs) { - outputs.at(0).mOutput = inputs.at(0).Get() + inputs.at(1).Get(); - } - - /*********************************************************************/ - void GraphNumericMultiply::Process(int32_t, std::vector& inputs, std::vector& outputs) { - outputs.at(0).mOutput = inputs.at(0).Get() * inputs.at(1).Get(); - } - - /*********************************************************************/ - void GraphNumericSubtract::Process(int32_t, std::vector& inputs, std::vector& outputs) { - outputs.at(0).mOutput = inputs.at(0).Get() - inputs.at(1).Get(); - } - - /*********************************************************************/ - void GraphNumericNegate::Process(int32_t, std::vector& inputs, std::vector& outputs) { - outputs.at(0).mOutput = -inputs.at(0).Get(); - } - - /*********************************************************************/ - void GraphNumericIntegral::Reset() { - mOutput = 0.0f; - } - - void GraphNumericIntegral::Process(int32_t, std::vector& inputs, std::vector& outputs) { - mOutput += inputs.at(0).Get(); - outputs.at(0).mOutput = mOutput; - } - - /*********************************************************************/ - void GraphNumericMultiply3::Process(int32_t, std::vector& inputs, std::vector& outputs) { - outputs.at(0).mOutput = inputs.at(0).Get() * inputs.at(1).Get() * inputs.at(2).Get(); - } - - /*********************************************************************/ - void GraphNumericMultiplyAndAdd::Process(int32_t, std::vector& inputs, std::vector& outputs) { - outputs.at(0).mOutput = inputs.at(0).Get() * inputs.at(1).Get() + inputs.at(2).Get(); - } - - /* Logical operations */ - - /*********************************************************************/ - void GraphLogicalAnd::Process(int32_t, std::vector& inputs, std::vector& outputs) { - bool input1 = inputs.at(0).Get() != 0.0f; - bool input2 = inputs.at(1).Get() != 0.0f; - outputs.at(0).mOutput = (input1 && input2) ? 1.0f : 0.0f; - } - - /*********************************************************************/ - void GraphLogicalOr::Process(int32_t, std::vector& inputs, std::vector& outputs) { - bool input1 = inputs.at(0).Get() != 0.0f; - bool input2 = inputs.at(1).Get() != 0.0f; - outputs.at(0).mOutput = (input1 || input2) ? 1.0f : 0.0f; - } - - /*********************************************************************/ - void GraphLogicalXor::Process(int32_t, std::vector& inputs, std::vector& outputs) { - bool input1 = inputs.at(0).Get() != 0.0f; - bool input2 = inputs.at(1).Get() != 0.0f; - outputs.at(0).mOutput = (input1 ^ input2) ? 1.0f : 0.0f; - } - /* Stateful filtering operations */ /*********************************************************************/ diff --git a/packages/nodegraph/source/common/NodeGraphSchema.cpp b/packages/nodegraph/source/common/NodeGraphSchema.cpp index 2ab49e81..cf821055 100644 --- a/packages/nodegraph/source/common/NodeGraphSchema.cpp +++ b/packages/nodegraph/source/common/NodeGraphSchema.cpp @@ -42,6 +42,15 @@ namespace l::nodegraph { case 6: node = mMainNodeGraph.NewNode(OutputType::Default); break; + case 7: + node = mMainNodeGraph.NewNode(OutputType::Default); + break; + case 8: + node = mMainNodeGraph.NewNode(OutputType::Default); + break; + case 9: + node = mMainNodeGraph.NewNode(OutputType::Default); + break; case 50: node = mMainNodeGraph.NewNode(OutputType::Default); break; @@ -63,6 +72,9 @@ namespace l::nodegraph { case 56: node = mMainNodeGraph.NewNode(OutputType::Default); break; + case 57: + node = mMainNodeGraph.NewNode(OutputType::Default); + break; case 100: node = mMainNodeGraph.NewNode(OutputType::Default); break; diff --git a/packages/rendering/source/common/ui/UIVisitors.cpp b/packages/rendering/source/common/ui/UIVisitors.cpp index dc208bcc..96a5bf81 100644 --- a/packages/rendering/source/common/ui/UIVisitors.cpp +++ b/packages/rendering/source/common/ui/UIVisitors.cpp @@ -399,7 +399,7 @@ namespace l::ui { auto& layoutArea = container.GetLayoutArea(); ImVec2 pT = layoutArea.Transform(pCenter); - if (OverlapCircle(input.mCurPos, pT, size.x * layoutArea.mScale)) { + if (OverlapCircle(input.mCurPos, pT, 2.0f * size.x * layoutArea.mScale)) { mDragging = true; mLinkContainer = CreateContainer(mUIStorage, UIContainer_LinkFlag | UIContainer_DrawFlag, UIRenderType::LinkH); container.Add(mLinkContainer); @@ -410,7 +410,7 @@ namespace l::ui { ImVec2 pCenter = container.GetCoParent()->GetPosition(); ImVec2 size = container.GetCoParent()->GetSize(); ImVec2 pT = container.GetCoParent()->GetLayoutArea().Transform(pCenter); - if (OverlapCircle(input.mCurPos, pT, size.x * container.GetCoParent()->GetLayoutArea().mScale)) { + if (OverlapCircle(input.mCurPos, pT, 2.0f * size.x * container.GetCoParent()->GetLayoutArea().mScale)) { mLinkContainer.mContainer = &container; LinkHandler(mLinkContainer->GetCoParent()->GetNodeId(), mLinkContainer->GetParent()->GetNodeId(), mLinkContainer->GetCoParent()->GetChannelId(), mLinkContainer->GetParent()->GetChannelId(), false); mDragging = true; @@ -433,7 +433,7 @@ namespace l::ui { ImVec2 pT = layoutArea.Transform(pCenter); - if (OverlapCircle(input.mCurPos, pT, size.x * layoutArea.mScale)) { + if (OverlapCircle(input.mCurPos, pT, 2.0f * size.x * layoutArea.mScale)) { if (LinkHandler(container.GetNodeId(), mLinkContainer->GetParent()->GetNodeId(), container.GetChannelId(), mLinkContainer->GetParent()->GetChannelId(), true)) { mLinkContainer->SetNotification(UIContainer_LinkFlag); mLinkContainer->SetCoParent(&container); From 123ae2d002c899f17753abed9b40ec88084b61be Mon Sep 17 00:00:00 2001 From: lnd3 Date: Thu, 5 Sep 2024 23:45:18 +0200 Subject: [PATCH 116/125] Fix some constants. --- packages/hid/CMakeLists.txt | 2 ++ packages/hid/source/common/KeyboardPiano.cpp | 14 ++++++++------ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/packages/hid/CMakeLists.txt b/packages/hid/CMakeLists.txt index f565fdba..f7f9beba 100644 --- a/packages/hid/CMakeLists.txt +++ b/packages/hid/CMakeLists.txt @@ -3,6 +3,8 @@ project (hid) set(deps logging testing + + math ) bs_generate_package(hid "tier1" "${deps}" "") diff --git a/packages/hid/source/common/KeyboardPiano.cpp b/packages/hid/source/common/KeyboardPiano.cpp index fbf7d3e0..fdb6302f 100644 --- a/packages/hid/source/common/KeyboardPiano.cpp +++ b/packages/hid/source/common/KeyboardPiano.cpp @@ -1,5 +1,7 @@ #include "hid/KeyboardPiano.h" +#include "math/MathConstants.h" + #include namespace l::hid { @@ -29,14 +31,14 @@ namespace l::hid { } else { int32_t note = ConvertKeyCodeToNote(keyCode, mOctave); - if (note > -INT_MIN) { + if (note > -l::math::constants::INTMIN) { noteHandler(note, true); } } } if (releasedNow) { int32_t note = ConvertKeyCodeToNote(keyCode, mOctave); - if (note > -INT_MIN) { + if (note > -l::math::constants::INTMIN) { noteHandler(note, false); } } @@ -64,14 +66,14 @@ namespace l::hid { } else { int32_t note = ConvertKeyCodeToNote(keyCode, mOctave); - if (note > -INT_MIN) { + if (note > -l::math::constants::INTMIN) { mNotePlayer->NoteOn(note); } } } if (releasedNow) { int32_t note = ConvertKeyCodeToNote(keyCode, mOctave); - if (note > -INT_MIN) { + if (note > -l::math::constants::INTMIN) { mNotePlayer->NoteOff(note); } } @@ -84,7 +86,7 @@ namespace l::hid { if (key > 0) { return key + octave * 12; } - return -INT_MIN; + return -l::math::constants::INTMIN; } int32_t KeyboardPiano::ConvertCharCodeToNote(int32_t charCode, int32_t octave) { @@ -92,7 +94,7 @@ namespace l::hid { if (key > 0) { return key + octave * 12; } - return -INT_MIN; + return -l::math::constants::INTMIN; } void KeyboardPiano::MapKeyFunctions(KeyFunctionTypes function, int32_t keyCode) { From 92592ec82e7aa5fa8de8091ec966aac94f66d31e Mon Sep 17 00:00:00 2001 From: lnd3 Date: Fri, 6 Sep 2024 00:37:53 +0200 Subject: [PATCH 117/125] Fix error. --- packages/hid/source/common/KeyboardPiano.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/hid/source/common/KeyboardPiano.cpp b/packages/hid/source/common/KeyboardPiano.cpp index fdb6302f..c6e2dcfb 100644 --- a/packages/hid/source/common/KeyboardPiano.cpp +++ b/packages/hid/source/common/KeyboardPiano.cpp @@ -31,14 +31,14 @@ namespace l::hid { } else { int32_t note = ConvertKeyCodeToNote(keyCode, mOctave); - if (note > -l::math::constants::INTMIN) { + if (note > l::math::constants::INTMIN) { noteHandler(note, true); } } } if (releasedNow) { int32_t note = ConvertKeyCodeToNote(keyCode, mOctave); - if (note > -l::math::constants::INTMIN) { + if (note > l::math::constants::INTMIN) { noteHandler(note, false); } } @@ -66,14 +66,14 @@ namespace l::hid { } else { int32_t note = ConvertKeyCodeToNote(keyCode, mOctave); - if (note > -l::math::constants::INTMIN) { + if (note > l::math::constants::INTMIN) { mNotePlayer->NoteOn(note); } } } if (releasedNow) { int32_t note = ConvertKeyCodeToNote(keyCode, mOctave); - if (note > -l::math::constants::INTMIN) { + if (note > l::math::constants::INTMIN) { mNotePlayer->NoteOff(note); } } @@ -86,7 +86,7 @@ namespace l::hid { if (key > 0) { return key + octave * 12; } - return -l::math::constants::INTMIN; + return l::math::constants::INTMIN; } int32_t KeyboardPiano::ConvertCharCodeToNote(int32_t charCode, int32_t octave) { @@ -94,7 +94,7 @@ namespace l::hid { if (key > 0) { return key + octave * 12; } - return -l::math::constants::INTMIN; + return l::math::constants::INTMIN; } void KeyboardPiano::MapKeyFunctions(KeyFunctionTypes function, int32_t keyCode) { From 54430fb69cb77d231a4280684c901aaa50af7778 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Fri, 6 Sep 2024 16:32:13 +0200 Subject: [PATCH 118/125] Add plot output node. --- .../nodegraph/include/nodegraph/NodeGraph.h | 74 ++++++++++++------- .../include/nodegraph/NodeGraphOperations.h | 25 +++++++ .../include/nodegraph/NodeGraphSchema.h | 3 +- .../nodegraph/source/common/NodeGraph.cpp | 55 ++++++++++++-- .../source/common/NodeGraphOperations.cpp | 44 ++++++++--- .../source/common/NodeGraphSchema.cpp | 3 + .../include/rendering/ui/UIContainer.h | 1 + .../rendering/source/common/ui/UICreator.cpp | 19 +++++ .../rendering/source/common/ui/UIVisitors.cpp | 21 ++++++ 9 files changed, 200 insertions(+), 45 deletions(-) diff --git a/packages/nodegraph/include/nodegraph/NodeGraph.h b/packages/nodegraph/include/nodegraph/NodeGraph.h index ad5d7de4..a739f401 100644 --- a/packages/nodegraph/include/nodegraph/NodeGraph.h +++ b/packages/nodegraph/include/nodegraph/NodeGraph.h @@ -38,22 +38,40 @@ namespace l::nodegraph { INPUT_CUSTOM, }; + enum class OutputType { + Default, // node will be processed if it is connected to the groups output by some route + ExternalOutput, // node does not have meaningful output for other nodes but should still be processed (ex speaker output only has input) + ExternalVisualOutput, + }; + bool IsValidInOutNum(int8_t inoutNum, size_t inoutSize); struct NodeGraphOutput { float mOutput = 0.0f; - std::vector mOutputBuf; - std::string mName; + std::unique_ptr> mOutputBuf = nullptr; + std::unique_ptr mName = nullptr; + + float& GetOutput(int32_t numSamples = 1) { + if (!mOutputBuf) { + if (numSamples <= 1) { + return mOutput; + } + else { + mOutputBuf = std::make_unique>(); + } + } + if (static_cast(mOutputBuf->size()) < numSamples) { + mOutputBuf->resize(numSamples); + } + return *mOutputBuf->data(); + } - float& GetOutput(int32_t numSamples) { - if(numSamples <= 1) { - return mOutput; + int32_t GetOutputSize() { + if (!mOutputBuf) { + return 1; } else { - if (static_cast(mOutputBuf.size()) < numSamples) { - mOutputBuf.resize(numSamples); - } - return *mOutputBuf.data(); + return static_cast(mOutputBuf->size()); } } @@ -79,16 +97,17 @@ namespace l::nodegraph { InputBound mInputBound = InputBound::INPUT_UNBOUNDED; int8_t mInputFromOutputChannel = 0; - std::string mName; + std::unique_ptr mName; void Reset(); bool HasInput(); float Get(); + float& Get(int32_t numSamples); }; class NodeGraphBase { public: - NodeGraphBase() : mId(CreateUniqueId()) { + NodeGraphBase(OutputType outputType) : mId(CreateUniqueId()), mOutputType(outputType) { mInputs.resize(1); mOutputs.resize(1); } @@ -111,9 +130,11 @@ namespace l::nodegraph { virtual int8_t GetNumOutputs(); virtual int8_t GetNumConstants(); - virtual float& GetOutput(int8_t outputChannel); + virtual float& GetOutput(int8_t outputChannel, int32_t numSamples = 1); virtual float GetInput(int8_t inputChannel); + virtual int32_t GetOutputSize(int8_t outputChannel); + virtual std::string_view GetName(); virtual std::string_view GetInputName(int8_t inputChannel); virtual std::string_view GetOutputName(int8_t outputChannel); @@ -132,6 +153,7 @@ namespace l::nodegraph { virtual bool IsDataVisible(int8_t num); virtual bool IsDataEditable(int8_t num); + virtual OutputType GetOutputType(); protected: virtual void ProcessOperation(int32_t numSamples = 1); @@ -142,6 +164,8 @@ namespace l::nodegraph { std::vector mOutputs; int32_t mId = -1; + OutputType mOutputType; + std::string mName; int8_t mInputCount = 0; int8_t mConstantCount = 0; @@ -197,8 +221,8 @@ namespace l::nodegraph { template class NodeGraph : public NodeGraphBase { public: - NodeGraph(Params&&... params) : - NodeGraphBase(), + NodeGraph(OutputType outputType = OutputType::Default, Params&&... params) : + NodeGraphBase(outputType), mOperation(this, std::forward(params)...) { SetNumInputs(mOperation.GetNumInputs()); @@ -259,16 +283,16 @@ namespace l::nodegraph { virtual std::string_view GetInputName(int8_t inputChannel) { auto& customName = mInputs.at(inputChannel).mName; - if (!customName.empty()) { - return customName; + if (customName && !customName->empty()) { + return *customName; } return mOperation.GetInputName(inputChannel); } virtual std::string_view GetOutputName(int8_t outputChannel) { auto& customName = mOutputs.at(outputChannel).mName; - if (!customName.empty()) { - return customName; + if (customName && !customName->empty()) { + return *customName; } return mOperation.GetOutputName(outputChannel); } @@ -281,14 +305,12 @@ namespace l::nodegraph { T mOperation; }; - enum class OutputType { - Default, // node will be processed if it is connected to the groups output by some route - ExternalOutput, // node does not have meaningful output for other nodes but should still be processed (ex speaker output only has input) - }; - class NodeGraphGroup { public: - NodeGraphGroup() { + NodeGraphGroup() : + mInputNode(OutputType::Default), + mOutputNode(OutputType::Default) + { SetNumInputs(1); SetNumOutputs(1); mOutputNodes.push_back(&mOutputNode); @@ -322,9 +344,9 @@ namespace l::nodegraph { template>, class... Params> l::nodegraph::NodeGraphBase* NewNode(OutputType nodeType, Params&&... params) { - mNodes.push_back(std::make_unique>(std::forward(params)...)); + mNodes.push_back(std::make_unique>(nodeType, std::forward(params)...)); auto nodePtr = mNodes.back().get(); - if (nodeType == OutputType::ExternalOutput) { + if (nodeType == OutputType::ExternalOutput || nodeType == OutputType::ExternalVisualOutput) { mOutputNodes.push_back(nodePtr); } return nodePtr; diff --git a/packages/nodegraph/include/nodegraph/NodeGraphOperations.h b/packages/nodegraph/include/nodegraph/NodeGraphOperations.h index c4b3d5ba..26dde674 100644 --- a/packages/nodegraph/include/nodegraph/NodeGraphOperations.h +++ b/packages/nodegraph/include/nodegraph/NodeGraphOperations.h @@ -601,6 +601,31 @@ namespace l::nodegraph { float mEnvelope = 0.0f; }; + /*********************************************************************/ + class GraphOutputPlot : public NodeGraphOp { + public: + std::string defaultInStrings[2] = { "Plot", "Smooth" }; + GraphOutputPlot(NodeGraphBase* node, int32_t plotSamples) : + NodeGraphOp(node, 1, 1, 0), + mPlotSamples(plotSamples) + { + mNode->GetOutput(0, mPlotSamples); + } + virtual ~GraphOutputPlot() = default; + + virtual void Reset() override; + virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; + virtual std::string_view GetOutputName(int8_t outputChannel) override { + return defaultInStrings[outputChannel]; + } + virtual std::string_view GetName() override { + return "Plot"; + } + protected: + int32_t mPlotSamples = 50; + int32_t mCurIndex = 0; + }; + /*********************************************************************/ class GraphEffectReverb1 : public NodeGraphOp { public: diff --git a/packages/nodegraph/include/nodegraph/NodeGraphSchema.h b/packages/nodegraph/include/nodegraph/NodeGraphSchema.h index d0772672..00e3b392 100644 --- a/packages/nodegraph/include/nodegraph/NodeGraphSchema.h +++ b/packages/nodegraph/include/nodegraph/NodeGraphSchema.h @@ -56,9 +56,10 @@ namespace l::nodegraph { RegisterNodeType("Filter", 150, "Lowpass"); RegisterNodeType("Output", 200, "Debug"); RegisterNodeType("Output", 201, "Speaker"); + RegisterNodeType("Output", 202, "Plot"); RegisterNodeType("Effect", 250, "Envelope"); RegisterNodeType("Effect", 251, "Reverb1"); - RegisterNodeType("Effect", 252, "Reverb2"); + //RegisterNodeType("Effect", 252, "Reverb2"); RegisterNodeType("Effect", 253, "Limiter"); RegisterNodeType("Effect", 254, "Envelope Follower"); RegisterNodeType("Effect", 255, "Saturator"); diff --git a/packages/nodegraph/source/common/NodeGraph.cpp b/packages/nodegraph/source/common/NodeGraph.cpp index c91facb6..37348dd2 100644 --- a/packages/nodegraph/source/common/NodeGraph.cpp +++ b/packages/nodegraph/source/common/NodeGraph.cpp @@ -91,8 +91,12 @@ namespace l::nodegraph { mLastTickCount = tickCount; } - float& NodeGraphBase::GetOutput(int8_t outputChannel) { - return mOutputs.at(outputChannel).mOutput; + float& NodeGraphBase::GetOutput(int8_t outputChannel, int32_t numSamples) { + return mOutputs.at(outputChannel).GetOutput(numSamples); + } + + int32_t NodeGraphBase::GetOutputSize(int8_t outputChannel) { + return mOutputs.at(outputChannel).GetOutputSize(); } float NodeGraphBase::GetInput(int8_t inputChannel) { @@ -144,7 +148,7 @@ namespace l::nodegraph { return false; } input.mInput.mInputNode = &source.GetInputNode(); - source.GetInputNode().SetInputName(sourceChannel, input.mName); + source.GetInputNode().SetInputName(sourceChannel, *input.mName); } else { if (!IsValidInOutNum(sourceChannel, source.GetOutputNode().GetNumOutputs()) || @@ -239,24 +243,44 @@ namespace l::nodegraph { return false; } + OutputType NodeGraphBase::GetOutputType() { + return mOutputType; + } + std::string_view NodeGraphBase::GetName() { return mName; } std::string_view NodeGraphBase::GetInputName(int8_t inputChannel) { - return mInputs.at(inputChannel).mName; + if (!mInputs.at(inputChannel).mName) { + return ""; + } + return *mInputs.at(inputChannel).mName; } std::string_view NodeGraphBase::GetOutputName(int8_t outputChannel) { - return mOutputs.at(outputChannel).mName; + if (!mOutputs.at(outputChannel).mName) { + return ""; + } + return *mOutputs.at(outputChannel).mName; } void NodeGraphBase::SetInputName(int8_t inputChannel, std::string_view name) { - mInputs.at(inputChannel).mName = name; + if (!mInputs.at(inputChannel).mName) { + mInputs.at(inputChannel).mName = std::make_unique(name); + } + else { + *mInputs.at(inputChannel).mName = name; + } } void NodeGraphBase::SetOutputName(int8_t outputChannel, std::string_view name) { - mOutputs.at(outputChannel).mName = name; + if (!mOutputs.at(outputChannel).mName) { + mOutputs.at(outputChannel).mName = std::make_unique(name); + } + else { + *mOutputs.at(outputChannel).mName = name; + } } @@ -355,6 +379,23 @@ namespace l::nodegraph { return l::math::functions::clamp(value, mBoundMin, mBoundMax); } + float& NodeGraphInput::Get(int32_t numSamples) { + switch (mInputType) { + case InputType::INPUT_NODE: + if (mInput.mInputNode != nullptr) { + return mInput.mInputNode->GetOutput(mInputFromOutputChannel, numSamples); + } + break; + case InputType::INPUT_CONSTANT: + return mInput.mInputFloatConstant; + case InputType::INPUT_VALUE: + return *mInput.mInputFloat; + case InputType::INPUT_EMPTY: + break; + } + return mInput.mInputFloatConstant; + } + void NodeGraphGroup::SetNumInputs(int8_t numInputs) { mInputNode.SetNumInputs(numInputs); mInputNode.SetNumOutputs(numInputs); diff --git a/packages/nodegraph/source/common/NodeGraphOperations.cpp b/packages/nodegraph/source/common/NodeGraphOperations.cpp index 43ecc67d..1f2aa598 100644 --- a/packages/nodegraph/source/common/NodeGraphOperations.cpp +++ b/packages/nodegraph/source/common/NodeGraphOperations.cpp @@ -72,7 +72,7 @@ namespace l::nodegraph { mPhase = 0.0f; mNode->SetInput(0, 0.0f); mNode->SetInput(1, 0.0f); - mNode->SetInput(2, 0.0f); + mNode->SetInput(2, 1.0f); mNode->SetInput(3, 0.0f); mNode->SetInput(4, 0.0f); mNode->SetInputBound(0, InputBound::INPUT_UNBOUNDED); @@ -104,24 +104,29 @@ namespace l::nodegraph { }, [&](int32_t start, int32_t end, bool) { - mFmod = 1.0 + static_cast(inputs.at(2).Get()); + mFmod = static_cast(inputs.at(2).Get()); mPmod = static_cast(inputs.at(3).Get()); + //double limitFmMod = 1.0 / l::math::functions::max(mNote / 25.0, 1.0); + //mPmod = 800.0 * fmMod * fmMod * limitFmMod; + + double limitFmMod = 1.0 / l::math::functions::max(mNote / 25.0, 1.0); + mFmod = 800.0 * mFmod * mFmod * limitFmMod; + for (int32_t i = start; i < end; i++) { double phaseDelta = mDeltaTime * mNote; - mPhase += phaseDelta * mFmod; + mPhase += phaseDelta * (1.0 + mFmod); mPhase = l::math::functions::mod(mPhase, 1.0); - double phaseMod = mPhase + mPmod; - phaseMod = l::math::functions::mod(phaseMod, 1.0); + mPhaseMod += mPhase + mPmod; + mPhaseMod = l::math::functions::mod(mPhaseMod, 1.0); - double waveTarget = l::math::functions::sin(l::math::constants::PI * (mPhase + phaseMod)); + double sine = l::math::functions::sin(l::math::constants::PI * (mPhase + mPhaseMod)); mVol += (1.0f / 256.0f) * (mVolume - mVol); - mWave += (waveTarget - mWave) * 0.5; // sample/smooth at half nyqvist frequency - *output0++ = mVol * static_cast(mWave); + *output0++ = mVol * static_cast(sine); } } ); @@ -309,13 +314,13 @@ namespace l::nodegraph { double fmMod = static_cast(inputs.at(2).Get()); double limitFmMod = 1.0 / l::math::functions::max(mNote / 25.0, 1.0); - fmMod = 800.0 * fmMod * fmMod * limitFmMod; + double fm = 800.0 * fmMod * fmMod * limitFmMod; for (int32_t i = start; i < end; i++) { double phaseDelta2 = mDeltaTime * mNote; - mPhaseFmod += phaseDelta2; + mPhaseFmod += 0.25 * phaseDelta2; mPhaseFmod = l::math::functions::mod(mPhaseFmod, 1.0); - double modulation = fmMod * l::math::functions::sin(l::math::constants::PI * mPhaseFmod * 2.0); + double modulation = fm * l::math::functions::sin(l::math::constants::PI * mPhaseFmod * 2.0); double phaseDelta = mDeltaTime * mNote * modulation; mPhase += phaseDelta; @@ -587,6 +592,23 @@ namespace l::nodegraph { mCurrentStereoPosition %= mAudioStream->GetPartTotalSize(); } + /*********************************************************************/ + void GraphOutputPlot::Reset() { + mNode->SetInput(0, 0.0f); + mNode->SetInputBound(0, InputBound::INPUT_NEG_1_POS_1); + } + + void GraphOutputPlot::Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) { + float* value = &inputs.at(0).Get(numSamples); + int32_t outputSize = outputs.at(0).GetOutputSize(); + float* output = &outputs.at(0).GetOutput(outputSize); + + for (int32_t i = 0; i < numSamples; i++) { + output[mCurIndex] = *value++; + mCurIndex = ++mCurIndex % outputSize; + } + } + /*********************************************************************/ void GraphEffectReverb1::Reset() { // { "In 1", "In 2", "Mix", "Attenuation", "Room Size", "Delay 1", "Feedback 1", "Delay 2", "Feedback 2", "Delay 3", "Feedback 3" }; diff --git a/packages/nodegraph/source/common/NodeGraphSchema.cpp b/packages/nodegraph/source/common/NodeGraphSchema.cpp index cf821055..8b20436e 100644 --- a/packages/nodegraph/source/common/NodeGraphSchema.cpp +++ b/packages/nodegraph/source/common/NodeGraphSchema.cpp @@ -93,6 +93,9 @@ namespace l::nodegraph { case 201: node = mMainNodeGraph.NewNode(OutputType::ExternalOutput, mAudioOutput); break; + case 202: + node = mMainNodeGraph.NewNode(OutputType::ExternalVisualOutput, 100); + break; case 250: node = mMainNodeGraph.NewNode(OutputType::Default); break; diff --git a/packages/rendering/include/rendering/ui/UIContainer.h b/packages/rendering/include/rendering/ui/UIContainer.h index 751418c4..584af8b2 100644 --- a/packages/rendering/include/rendering/ui/UIContainer.h +++ b/packages/rendering/include/rendering/ui/UIContainer.h @@ -54,6 +54,7 @@ namespace l::ui { Text = 9, Texture = 10, NodeOutputValue = 11, + NodeOutputGraph = 12, }; enum class UITraversalMode { diff --git a/packages/rendering/source/common/ui/UICreator.cpp b/packages/rendering/source/common/ui/UICreator.cpp index b36cefc4..f8050eb1 100644 --- a/packages/rendering/source/common/ui/UICreator.cpp +++ b/packages/rendering/source/common/ui/UICreator.cpp @@ -88,6 +88,25 @@ namespace l::ui { sizeEstimate.x = sizeEstimate.x < estimatedWidth ? estimatedWidth : sizeEstimate.x; } } + + if (node.GetOutputType() == l::nodegraph::OutputType::ExternalVisualOutput) { + auto row = CreateContainer(uiStorage, l::ui::UIContainer_DrawFlag, l::ui::UIRenderType::Rect, l::ui::UIAlignH::Left, l::ui::UIAlignV::Top, l::ui::UILayoutH::Parent, l::ui::UILayoutV::Parent); + row->GetContainerArea().mMargin = ioSize; + node4->Add(row); + + float estimatedWidth = 0.0f; + auto plot = CreateContainer(uiStorage, l::ui::UIContainer_DrawFlag, l::ui::UIRenderType::NodeOutputGraph, l::ui::UIAlignH::Center, l::ui::UIAlignV::Middle, l::ui::UILayoutH::Parent, l::ui::UILayoutV::Parent); + plot->SetPosition(ImVec2(estimatedWidth, 0.0f)); + plot->SetSize(ImVec2(100, 100)); + plot->SetNodeId(node.GetId()); + plot->SetChannelId(0); + estimatedWidth += 100; + row->Add(plot); + + sizeEstimate.x = sizeEstimate.x < estimatedWidth ? estimatedWidth : sizeEstimate.x; + sizeEstimate.y += 100.0f; + } + } sizeEstimate.x += node4->GetContainerArea().mMargin * 2 + 2.0f; diff --git a/packages/rendering/source/common/ui/UIVisitors.cpp b/packages/rendering/source/common/ui/UIVisitors.cpp index 96a5bf81..f229b877 100644 --- a/packages/rendering/source/common/ui/UIVisitors.cpp +++ b/packages/rendering/source/common/ui/UIVisitors.cpp @@ -351,6 +351,27 @@ namespace l::ui { mDrawList->AddText(ImGui::GetDefaultFont(), 13.0f * container.GetScale() * layoutArea.mScale, p1, color, nodeString.c_str()); } break; + case l::ui::UIRenderType::NodeOutputGraph: + if (mNGSchema) { + auto node = mNGSchema->GetNode(container.GetNodeId()); + if (container.GetChannelId() < node->GetNumOutputs()) { + int8_t channel = static_cast(container.GetChannelId()); + float* nodeValues = &node->GetOutput(channel); + int32_t nodeValueCount = node->GetOutputSize(channel); + ImVec2 size = container.GetSize(); + size.x *= layoutArea.mScale; + size.y *= layoutArea.mScale; + ImVec2 startPos = ImVec2(p1.x, p1.y + 0.5f * size.y); + for (size_t i = 0; i < nodeValueCount - 1; i++) { + float xpart1 = i / static_cast(nodeValueCount); + float xpart2 = (i+1) / static_cast(nodeValueCount); + ImVec2 graphP1 = ImVec2(startPos.x + size.x * xpart1, startPos.y + 0.5f * nodeValues[i] * size.y); + ImVec2 graphP2 = ImVec2(startPos.x + size.x * xpart2, startPos.y + 0.5f * nodeValues[i] * size.y); + mDrawList->AddLine(graphP1, graphP2, color, 2.0f * container.GetScale()); + } + } + } + break; } if (mDebug && !container.GetStringId().empty()) { From a1619cc75c0c21f5c282160b62dd92fbfc1ca0bc Mon Sep 17 00:00:00 2001 From: lnd3 Date: Fri, 6 Sep 2024 20:35:20 +0200 Subject: [PATCH 119/125] Move midi code to hid package and implement platform independent code to use it properly. --- packages/hid/include/hid/Midi.h | 58 ++++ packages/hid/include/hid/MidiDefs.h | 111 ++++++ packages/hid/source/common/Midi.cpp | 72 ++++ packages/hid/source/windows/MidiWindows.cpp | 247 ++++++++++++++ packages/hid/source/windows/MidiWindows.h | 53 +++ packages/math/include/math/MathFunc.h | 6 +- packages/math/include/math/MathSmooth.h | 1 + .../include/nodegraph/NodeGraphOperations.h | 110 ++++-- .../include/nodegraph/NodeGraphSchema.h | 12 +- .../source/common/NodeGraphOperations.cpp | 216 ++++++++---- .../source/common/NodeGraphSchema.cpp | 18 +- packages/tools/source/windows/utils/Midi.h | 317 ------------------ 12 files changed, 780 insertions(+), 441 deletions(-) create mode 100644 packages/hid/include/hid/Midi.h create mode 100644 packages/hid/include/hid/MidiDefs.h create mode 100644 packages/hid/source/common/Midi.cpp create mode 100644 packages/hid/source/windows/MidiWindows.cpp create mode 100644 packages/hid/source/windows/MidiWindows.h delete mode 100644 packages/tools/source/windows/utils/Midi.h diff --git a/packages/hid/include/hid/Midi.h b/packages/hid/include/hid/Midi.h new file mode 100644 index 00000000..864df8a7 --- /dev/null +++ b/packages/hid/include/hid/Midi.h @@ -0,0 +1,58 @@ +#pragma once + +#include "logging/Log.h" + +#include "MidiDefs.h" + +#include + +namespace l::hid::midi { + + struct MidiData { + uint32_t msg; // input message + uint32_t device; // device id + uint32_t timestamp; // Since opening the midi device + uint32_t status; // noteon=145, noteoff=129, knob=176, padon=144, padoff=128, sustain=177, + uint32_t channel; + uint32_t data1; // Key unique + uint32_t data2; // Key attack (0-127), pad attack (127), knob position (0-127) + uint32_t unused; + }; + + /* + uint32_t msg; // message + uint32_t instance; // device id + uint32_t type = (param1) & 0xff; // noteon=145, noteoff=129, knob=176, padon=144, padoff=128, sustain=177, + uint32_t key = (param1 >> 8) & 0xff; // key unique + uint32_t atc = (param1 >> 16) & 0xff; // key attack (0-127), pad attack (127), knob position (0-127) + uint32_t arg2 = (param1 >> 24) & 0xff; // unused? + uint32_t time = param2; + */ + using CallbackFunction = std::function; + + namespace details { + extern std::mutex midiCallbackMutex; + extern std::vector midiCallback; + + void HandleMidiData(uint32_t msg, uint32_t instance, uint32_t param1, uint32_t param2); + } + + class MidiManager { + public: + MidiManager() { + RegisterCallback([](MidiData data) { + LOG(LogInfo) << "listener 1: dev" << data.device << " stat " << data.status << " ch " << data.channel << " d1 " << data.data1 << " d2 " << data.data2; + }); + } + virtual ~MidiManager() = default; + + virtual void RegisterCallback(CallbackFunction cb); + virtual uint32_t GetNumDevices(); + virtual void SendToDevice(uint32_t deviceIndex, uint32_t status, uint32_t channel, uint32_t data1, uint32_t data2); + }; + + std::unique_ptr CreateMidiManager(); + +} + + diff --git a/packages/hid/include/hid/MidiDefs.h b/packages/hid/include/hid/MidiDefs.h new file mode 100644 index 00000000..4915aac3 --- /dev/null +++ b/packages/hid/include/hid/MidiDefs.h @@ -0,0 +1,111 @@ +#pragma once + +/**************************************************************************** + + Multimedia Extensions Window Messages + +****************************************************************************/ + +#define MM_JOY1MOVE 0x3A0 /* joystick */ +#define MM_JOY2MOVE 0x3A1 +#define MM_JOY1ZMOVE 0x3A2 +#define MM_JOY2ZMOVE 0x3A3 +#define MM_JOY1BUTTONDOWN 0x3B5 +#define MM_JOY2BUTTONDOWN 0x3B6 +#define MM_JOY1BUTTONUP 0x3B7 +#define MM_JOY2BUTTONUP 0x3B8 + +#define MM_MCINOTIFY 0x3B9 /* MCI */ + +#define MM_WOM_OPEN 0x3BB /* waveform output */ +#define MM_WOM_CLOSE 0x3BC +#define MM_WOM_DONE 0x3BD + +#define MM_WIM_OPEN 0x3BE /* waveform input */ +#define MM_WIM_CLOSE 0x3BF +#define MM_WIM_DATA 0x3C0 + +#define MM_MIM_OPEN 0x3C1 /* MIDI input */ +#define MM_MIM_CLOSE 0x3C2 +#define MM_MIM_DATA 0x3C3 +#define MM_MIM_LONGDATA 0x3C4 +#define MM_MIM_ERROR 0x3C5 +#define MM_MIM_LONGERROR 0x3C6 + +#define MM_MOM_OPEN 0x3C7 /* MIDI output */ +#define MM_MOM_CLOSE 0x3C8 +#define MM_MOM_DONE 0x3C9 + +/* these are also in msvideo.h */ +#ifndef MM_DRVM_OPEN +#define MM_DRVM_OPEN 0x3D0 /* installable drivers */ +#define MM_DRVM_CLOSE 0x3D1 +#define MM_DRVM_DATA 0x3D2 +#define MM_DRVM_ERROR 0x3D3 +#endif + +/* these are used by msacm.h */ +#define MM_STREAM_OPEN 0x3D4 +#define MM_STREAM_CLOSE 0x3D5 +#define MM_STREAM_DONE 0x3D6 +#define MM_STREAM_ERROR 0x3D7 + +//#if(WINVER >= 0x0400) +#define MM_MOM_POSITIONCB 0x3CA /* Callback for MEVT_POSITIONCB */ + +#ifndef MM_MCISIGNAL +#define MM_MCISIGNAL 0x3CB +#endif + +#define MM_MIM_MOREDATA 0x3CC /* MIM_DONE w/ pending events */ + +//#endif /* WINVER >= 0x0400 */ + +#define MM_MIXM_LINE_CHANGE 0x3D0 /* mixer line change notify */ +#define MM_MIXM_CONTROL_CHANGE 0x3D1 /* mixer control change notify */ + + + +// windows midi defs + +/* MIDI error return values */ +#define MIDIERR_UNPREPARED (MIDIERR_BASE + 0) /* header not prepared */ +#define MIDIERR_STILLPLAYING (MIDIERR_BASE + 1) /* still something playing */ +#define MIDIERR_NOMAP (MIDIERR_BASE + 2) /* no configured instruments */ +#define MIDIERR_NOTREADY (MIDIERR_BASE + 3) /* hardware is still busy */ +#define MIDIERR_NODEVICE (MIDIERR_BASE + 4) /* port no longer connected */ +#define MIDIERR_INVALIDSETUP (MIDIERR_BASE + 5) /* invalid MIF */ +#define MIDIERR_BADOPENMODE (MIDIERR_BASE + 6) /* operation unsupported w/ open mode */ +#define MIDIERR_DONT_CONTINUE (MIDIERR_BASE + 7) /* thru device 'eating' a message */ +#define MIDIERR_LASTERROR (MIDIERR_BASE + 7) /* last error in range */ + +/* MIDI callback messages */ +#define MIM_OPEN MM_MIM_OPEN +#define MIM_CLOSE MM_MIM_CLOSE +#define MIM_DATA MM_MIM_DATA +#define MIM_LONGDATA MM_MIM_LONGDATA +#define MIM_ERROR MM_MIM_ERROR +#define MIM_LONGERROR MM_MIM_LONGERROR +#define MOM_OPEN MM_MOM_OPEN +#define MOM_CLOSE MM_MOM_CLOSE +#define MOM_DONE MM_MOM_DONE + +//#if(WINVER >= 0x0400) +#define MIM_MOREDATA MM_MIM_MOREDATA +#define MOM_POSITIONCB MM_MOM_POSITIONCB +//#endif /* WINVER >= 0x0400 */ + +/* device ID for MIDI mapper */ +#define MIDIMAPPER ((UINT)-1) +#define MIDI_MAPPER ((UINT)-1) + +//#if(WINVER >= 0x0400) +/* flags for dwFlags parm of midiInOpen() */ +#define MIDI_IO_STATUS 0x00000020L +//#endif /* WINVER >= 0x0400 */ + +/* flags for wFlags parm of midiOutCachePatches(), midiOutCacheDrumPatches() */ +#define MIDI_CACHE_ALL 1 +#define MIDI_CACHE_BESTFIT 2 +#define MIDI_CACHE_QUERY 3 +#define MIDI_UNCACHE 4 \ No newline at end of file diff --git a/packages/hid/source/common/Midi.cpp b/packages/hid/source/common/Midi.cpp new file mode 100644 index 00000000..1f041db9 --- /dev/null +++ b/packages/hid/source/common/Midi.cpp @@ -0,0 +1,72 @@ +#pragma once + +#include "logging/Log.h" + +#include "hid/Midi.h" + +namespace l::hid::midi { + + namespace details { + std::mutex midiCallbackMutex; + std::vector midiCallback; + + void HandleMidiData(uint32_t msg, uint32_t instance, uint32_t param1, uint32_t param2) { + switch (msg) { + case MIM_OPEN: + LOG(LogInfo) << "MIM_OPEN"; + return; + case MIM_CLOSE: + LOG(LogInfo) << "MIM_CLOSE"; + return; + case MIM_LONGDATA: + LOG(LogInfo) << "Long data: " << msg << " " << instance << " " << param1 << " " << param2; + return; + case MIM_ERROR: + LOG(LogInfo) << "Error: " << msg << " " << instance << " " << param1 << " " << param2; + return; + case MIM_LONGERROR: + LOG(LogInfo) << "Long error: " << msg << " " << instance << " " << param1 << " " << param2; + return; + case MIM_MOREDATA: + LOG(LogInfo) << "More data: " << msg << " " << instance << " " << param1 << " " << param2; + return; + case MIM_DATA: + //LOG(LogInfo) << "Data: " << msg << " " << instance << " " << param1 << " " << param2; + break; + default: + LOG(LogInfo) << "default"; + break; + } + + std::lock_guard lock(midiCallbackMutex); + if (!midiCallback.empty()) { + MidiData data; + data.msg = msg; + data.device = instance; + data.timestamp = param2; + data.status = (param1 >> 4) & 0xf; + data.channel = (param1) & 0xf; + data.data1 = (param1 >> 8) & 0xff; + data.data2 = (param1 >> 16) & 0xff; + data.unused = 0; + + for (auto& cb : midiCallback) { + if (cb) { + (cb)(data); + } + } + } + } + } + + void MidiManager::RegisterCallback(CallbackFunction) { + } + + uint32_t MidiManager::GetNumDevices() { + return 0; + } + + void MidiManager::SendToDevice(uint32_t, uint32_t, uint32_t, uint32_t, uint32_t) { + } +} + diff --git a/packages/hid/source/windows/MidiWindows.cpp b/packages/hid/source/windows/MidiWindows.cpp new file mode 100644 index 00000000..cb7760ce --- /dev/null +++ b/packages/hid/source/windows/MidiWindows.cpp @@ -0,0 +1,247 @@ +#pragma once + +#include "logging/Log.h" + +#include "MidiWindows.h" + +namespace l::hid::midi { + + std::unique_ptr CreateMidiManager() { + return std::make_unique(); + } + + namespace details { + void CALLBACK MidiInProc(HMIDIIN, UINT wMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2) { + HandleMidiData(static_cast(wMsg), static_cast(dwInstance), static_cast(dwParam1), static_cast(dwParam2)); + } + + void CALLBACK MidiOutProc(HMIDIIN, UINT wMsg, DWORD, DWORD, DWORD) { + switch (wMsg) { + case MOM_OPEN: + LOG(LogInfo) << "MOM_OPEN"; + break; + case MOM_CLOSE: + LOG(LogInfo) << "MOM_CLOSE"; + break; + case MOM_DONE: + LOG(LogInfo) << "MOM_DONE"; + break; + default: + LOG(LogInfo) << "Something else.."; + break; + } + + } + } + + + + Midi::Midi() : mHeader{}, mMidiBuffer{} { + UINT nMidiDeviceNum = midiInGetNumDevs(); + if (nMidiDeviceNum == 0) { + return; + } + + UINT nMidiOutDeviceNum = midiOutGetNumDevs(); + + LOG(LogInfo) << "Number of midi in devices: " << nMidiDeviceNum; + LOG(LogInfo) << "Number of midi out devices: " << nMidiOutDeviceNum; + + for (size_t deviceId = 0; deviceId < nMidiDeviceNum; deviceId++) { + MMRESULT rv; + HMIDIIN hMidiDevice = NULL; + + rv = midiInOpen(&hMidiDevice, static_cast(deviceId), reinterpret_cast(&details::MidiInProc), 0, CALLBACK_FUNCTION); + if (rv == MMSYSERR_ALLOCATED) { + return; + } + if (rv != MMSYSERR_NOERROR) { + LOG(LogError) << "Failed to open midi in device " << deviceId; + continue; + } + + rv = midiInStart(hMidiDevice); + if (rv != MMSYSERR_NOERROR) { + LOG(LogError) << "Failed to start midi in device" << deviceId; + continue; + } + + MIDIINCAPS capsIn; + rv = midiInGetDevCaps(deviceId, &capsIn, sizeof(MIDIINCAPS)); + if (rv != MMSYSERR_NOERROR) { + LOG(LogError) << "Failed to get midi in caps on device " << deviceId; + } + + HMIDIOUT hMidiDeviceOut = nullptr; + deviceId++; + rv = midiOutOpen(&hMidiDeviceOut, static_cast(deviceId), reinterpret_cast(&details::MidiOutProc), 0, CALLBACK_FUNCTION); + if (rv == MMSYSERR_ALLOCATED) { + return; + } + if (rv != MMSYSERR_NOERROR) { + LOG(LogError) << "Failed to open midi out device " << deviceId; + } + + MIDIOUTCAPS capsOut; + rv = midiOutGetDevCaps(deviceId, &capsOut, sizeof(MIDIOUTCAPS)); + if (rv != MMSYSERR_NOERROR) { + LOG(LogError) << "Failed to get midi out caps on device " << deviceId; + } + deviceId--; + + devices.push_back(std::make_pair(hMidiDevice, hMidiDeviceOut)); + caps.push_back(std::make_pair(capsIn, capsOut)); + } + } + + Midi::~Midi() { + int deviceId = 0; + for (auto device : devices) { + MMRESULT rv; + rv = midiOutClose(device.second); + if (rv != MMSYSERR_NOERROR) { + LOG(LogError) << "Failed to close midi out device" << deviceId; + } + rv = midiInStop(device.first); + if (rv == MMSYSERR_NOERROR) { + rv = midiInClose(device.first); + } + if (rv != MMSYSERR_NOERROR) { + LOG(LogError) << "Failed to close midi in device" << deviceId; + } + deviceId++; + } + { + std::lock_guard lock(details::midiCallbackMutex); + details::midiCallback.clear(); + } + } + + void Midi::registerMidiCallback(CallbackFunction f) { + std::lock_guard lock(details::midiCallbackMutex); + details::midiCallback.push_back(f); + } + + uint32_t Midi::getNumDevices() { + return static_cast(devices.size()); + } + + void Midi::clearBuffer() { + mHeader.dwBufferLength = 0; + mHeader.dwBytesRecorded = 0; + } + + void Midi::pushBuffer(unsigned char byte) { + mMidiBuffer[mHeader.dwBufferLength] = static_cast(byte); + mHeader.dwBufferLength++; + mHeader.dwBytesRecorded++; + } + + void Midi::sendSysex2(uint32_t deviceId, uint32_t i, uint32_t data) { + if (deviceId >= devices.size()) { + return; + } + static uint32_t counter = 0; + + mHeader = { 0 }; + mHeader.lpData = mMidiBuffer; + + clearBuffer(); + + switch (i) { + case 0: + mMidiBuffer[0] = 0x7e; // + mMidiBuffer[1] = 0x00; // AKAI + mMidiBuffer[2] = 0x06; // General information + mMidiBuffer[3] = 0x01; // Identity request + mHeader.dwBufferLength = 4; + mHeader.dwBytesRecorded = 4; + break; + case 1: + pushBuffer(0xf0); + pushBuffer(0x47); + pushBuffer(0x00); + pushBuffer(0xff); // 27 + pushBuffer(0xf7); + break; + case 2: + pushBuffer(0xf0); + pushBuffer(0x47); + pushBuffer(0xff); + pushBuffer(0xff); + pushBuffer(0x60); + pushBuffer(0x00); + pushBuffer(0x04); + pushBuffer(0x40); + pushBuffer(0x01); + pushBuffer(0x00); + pushBuffer(0x00); + pushBuffer(0xf7); + break; + case 3: + mMidiBuffer[0] = 0x7f; // Real Time (7FH) + mMidiBuffer[1] = 0x47; // AKAI + mMidiBuffer[2] = 0x02; // MIDI Show Control + mMidiBuffer[3] = static_cast(data); + mHeader.dwBufferLength = 4; + mHeader.dwBytesRecorded = 4; + break; + case 4: + mMidiBuffer[0] = 0x7f; // Real Time (7FH) + mMidiBuffer[1] = 0x47; // AKAI + mMidiBuffer[2] = 0x06; // MIDI Machine Control Commands + mMidiBuffer[3] = static_cast(data); + mHeader.dwBufferLength = 4; + mHeader.dwBytesRecorded = 4; + break; + default: + + break; + } + + //HMIDIIN deviceIn = devices[deviceId].first; + HMIDIOUT deviceOut = devices[deviceId].second; + + MMRESULT rv = midiOutPrepareHeader(deviceOut, &mHeader, sizeof(MIDIHDR)); + if (rv != MMSYSERR_NOERROR) { + LOG(LogError) << "Failed to prepare midi out buffer " << deviceId << ", error " << rv; + } + + rv = midiOutLongMsg(deviceOut, &mHeader, sizeof(MIDIHDR)); + if (rv != MMSYSERR_NOERROR) { + LOG(LogError) << "Failed to send buffer to midi out device " << deviceId << ", error " << rv; + } + + rv = midiOutUnprepareHeader(deviceOut, &mHeader, sizeof(MIDIHDR)); + if (rv != MMSYSERR_NOERROR) { + LOG(LogError) << "Failed to unprepare midi out buffer " << deviceId << ", error " << rv; + } + } + + void Midi::send(uint32_t deviceId, uint32_t status, uint32_t channel, uint32_t data1, uint32_t data2) { + if (deviceId >= devices.size()) { + return; + } + HMIDIOUT device = devices[deviceId].second; + + DWORD param1 = ((data2 & 0xff) << 16) | ((data1 & 0xff) << 8) | ((status & 0xf0) | (channel & 0xf)); + + MMRESULT rv = midiOutShortMsg(device, param1); + if (rv != MMSYSERR_NOERROR) { + LOG(LogError) << "Failed to send to midi out device " << deviceId << ", error " << rv << ", data " << param1; + } + } + + void MidiManagerWindows::RegisterCallback(CallbackFunction cb) { + mMidiDevice.registerMidiCallback(cb); + } + + uint32_t MidiManagerWindows::GetNumDevices() { + return mMidiDevice.getNumDevices(); + } + + void MidiManagerWindows::SendToDevice(uint32_t deviceIndex, uint32_t status, uint32_t channel, uint32_t data1, uint32_t data2) { + mMidiDevice.send(deviceIndex, status, channel, data1, data2); + } +} + diff --git a/packages/hid/source/windows/MidiWindows.h b/packages/hid/source/windows/MidiWindows.h new file mode 100644 index 00000000..e9815954 --- /dev/null +++ b/packages/hid/source/windows/MidiWindows.h @@ -0,0 +1,53 @@ +#pragma once + +#include "logging/Log.h" + +#include "hid/Midi.h" + +#include +#include + +namespace l::hid::midi { + + + namespace details { + void CALLBACK MidiInProc(HMIDIIN, UINT wMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2); + void CALLBACK MidiOutProc(HMIDIIN, UINT wMsg, DWORD, DWORD, DWORD); + } + + class Midi { + public: + Midi(); + ~Midi(); + + void registerMidiCallback(CallbackFunction f); + uint32_t getNumDevices(); + void clearBuffer(); + void pushBuffer(unsigned char byte); + void sendSysex2(uint32_t deviceId, uint32_t i, uint32_t data); + void send(uint32_t deviceId, uint32_t status, uint32_t channel, uint32_t data1, uint32_t data2); + + private: + MIDIHDR mHeader{}; + char mMidiBuffer[16]; + + CallbackFunction mCallback; + std::vector> devices; + std::vector< std::pair> caps; + }; + + + class MidiManagerWindows : public MidiManager { + public: + MidiManagerWindows() = default; + + virtual void RegisterCallback(CallbackFunction cb) override; + virtual uint32_t GetNumDevices() override; + virtual void SendToDevice(uint32_t deviceIndex, uint32_t status, uint32_t channel, uint32_t data1, uint32_t data2) override; + protected: + Midi mMidiDevice; + }; + +} + + diff --git a/packages/math/include/math/MathFunc.h b/packages/math/include/math/MathFunc.h index 4b9053a9..ac628628 100644 --- a/packages/math/include/math/MathFunc.h +++ b/packages/math/include/math/MathFunc.h @@ -139,6 +139,8 @@ namespace l::math::functions { } } + // source https://iquilezles.org/articles/functions/ + // Almost Identity(I) // Imagine you don't want to modify a signal unless it's drops to zero or close to it, in which case you want to replace the value with a small possitive constant.Then, rather than clamping the valueand introduce a discontinuity, you can smoothly blend the signal into the desired clipped value.So, let m be the threshold(anything above m stays unchanged), and n the value things will take when the signal is zero.Then, the following function does the soft clipping(in a cubic fashion): template @@ -266,8 +268,8 @@ namespace l::math::functions { } template - T sigmoid(T x) { - return static_cast(1.0 / (1.0 + functions::exp(-x))); + T sigmoid(T x, T k) { + return static_cast(1.0 / (1.0 + functions::exp(-x * k))); } template diff --git a/packages/math/include/math/MathSmooth.h b/packages/math/include/math/MathSmooth.h index ad71f547..ae89130d 100644 --- a/packages/math/include/math/MathSmooth.h +++ b/packages/math/include/math/MathSmooth.h @@ -7,6 +7,7 @@ #include namespace l::math::smooth { + // source https://iquilezles.org/articles/smoothsteps/ // Quintic Polynomial - C2 continuity template diff --git a/packages/nodegraph/include/nodegraph/NodeGraphOperations.h b/packages/nodegraph/include/nodegraph/NodeGraphOperations.h index 26dde674..e056589d 100644 --- a/packages/nodegraph/include/nodegraph/NodeGraphOperations.h +++ b/packages/nodegraph/include/nodegraph/NodeGraphOperations.h @@ -239,43 +239,6 @@ namespace l::nodegraph { double mPhase = 0.0; float mSamplesUntilUpdate = 0.0f; }; - /*********************************************************************/ - class GraphSourceKeyboard : public NodeGraphOp, public l::hid::INoteProcessor { - public: - GraphSourceKeyboard(NodeGraphBase* node, int32_t polyphony, l::hid::KeyState* keyState) : - NodeGraphOp(node, 0, polyphony, polyphony) - { - mChannel.resize(polyphony); - mKeyboard.SetKeyState(keyState); - mKeyboard.SetNoteProcessor(this); - } - - std::string defaultOutStrings[8] = { "Note 1", "Note 2", "Note 3", "Note 4", "Note 5", "Note 6", "Note 7", "Note 8" }; - - virtual ~GraphSourceKeyboard() = default; - virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; - virtual void Tick(int32_t tickCount, float elapsed) override; - virtual void Reset() override; - virtual std::string_view GetOutputName(int8_t outputChannel) override { - return defaultOutStrings[outputChannel]; - } - virtual std::string_view GetName() override { - return "Keyboard"; - } - virtual bool IsDataVisible(int8_t) override { - return true; - } - virtual void NoteOn(int32_t note) override; - virtual void NoteOff() override; - virtual void NoteOff(int32_t note) override; - protected: - int8_t ResetNoteChannel(int32_t note); - int8_t GetNextNoteChannel(int32_t note); - - int8_t mNoteCounter = 0; - std::vector> mChannel; - l::hid::KeyboardPiano mKeyboard; - }; /*********************************************************************/ class GraphNumericAdd : public NodeGraphOp { @@ -844,5 +807,78 @@ namespace l::nodegraph { protected: float mEnvelope = 0.0f; }; + + /*********************************************************************/ + class GraphInputKeyboardPiano : public NodeGraphOp, public l::hid::INoteProcessor { + public: + GraphInputKeyboardPiano(NodeGraphBase* node, int32_t polyphony, l::hid::KeyState* keyState) : + NodeGraphOp(node, 0, polyphony, polyphony) + { + mChannel.resize(polyphony); + mKeyboard.SetKeyState(keyState); + mKeyboard.SetNoteProcessor(this); + } + + std::string defaultOutStrings[8] = { "Note 1", "Note 2", "Note 3", "Note 4", "Note 5", "Note 6", "Note 7", "Note 8" }; + + virtual ~GraphInputKeyboardPiano() = default; + virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; + virtual void Tick(int32_t tickCount, float elapsed) override; + virtual void Reset() override; + virtual std::string_view GetOutputName(int8_t outputChannel) override { + return defaultOutStrings[outputChannel]; + } + virtual std::string_view GetName() override { + return "Keyboard"; + } + virtual bool IsDataVisible(int8_t) override { + return true; + } + virtual void NoteOn(int32_t note) override; + virtual void NoteOff() override; + virtual void NoteOff(int32_t note) override; + protected: + int8_t ResetNoteChannel(int32_t note); + int8_t GetNextNoteChannel(int32_t note); + + int8_t mNoteCounter = 0; + std::vector> mChannel; + l::hid::KeyboardPiano mKeyboard; + }; + + /*********************************************************************/ + class GraphInputMidi : public NodeGraphOp, public l::hid::INoteProcessor { + public: + GraphInputMidi(NodeGraphBase* node) : + NodeGraphOp(node, 0) + { + } + + std::string defaultOutStrings[1] = { "In 1" }; + + virtual ~GraphInputMidi() = default; + virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; + virtual void Tick(int32_t tickCount, float elapsed) override; + virtual void Reset() override; + virtual std::string_view GetOutputName(int8_t outputChannel) override { + return defaultOutStrings[outputChannel]; + } + virtual std::string_view GetName() override { + return "Midi Device"; + } + virtual bool IsDataVisible(int8_t) override { + return true; + } + virtual void NoteOn(int32_t note) override; + virtual void NoteOff() override; + virtual void NoteOff(int32_t note) override; + protected: + int8_t ResetNoteChannel(int32_t note); + int8_t GetNextNoteChannel(int32_t note); + + int8_t mNoteCounter = 0; + std::vector> mChannel; + }; + } diff --git a/packages/nodegraph/include/nodegraph/NodeGraphSchema.h b/packages/nodegraph/include/nodegraph/NodeGraphSchema.h index 00e3b392..207e7292 100644 --- a/packages/nodegraph/include/nodegraph/NodeGraphSchema.h +++ b/packages/nodegraph/include/nodegraph/NodeGraphSchema.h @@ -36,12 +36,11 @@ namespace l::nodegraph { RegisterNodeType("Source", 1, "Value [-1,1]"); RegisterNodeType("Source", 2, "Value [0,100]"); RegisterNodeType("Source", 3, "Value [-inf,inf]"); - RegisterNodeType("Source", 4, "Keyboard"); - RegisterNodeType("Source", 5, "Time"); - RegisterNodeType("Source", 6, "Sine"); - RegisterNodeType("Source", 7, "Sine FM"); - RegisterNodeType("Source", 8, "Sine FM 2"); - RegisterNodeType("Source", 9, "Sine FM 3"); + RegisterNodeType("Source", 4, "Time"); + RegisterNodeType("Source", 5, "Sine"); + RegisterNodeType("Source", 6, "Sine FM"); + RegisterNodeType("Source", 7, "Sine FM 2"); + RegisterNodeType("Source", 8, "Sine FM 3"); RegisterNodeType("Numeric", 50, "Add"); RegisterNodeType("Numeric", 51, "Subtract"); RegisterNodeType("Numeric", 52, "Negate"); @@ -63,6 +62,7 @@ namespace l::nodegraph { RegisterNodeType("Effect", 253, "Limiter"); RegisterNodeType("Effect", 254, "Envelope Follower"); RegisterNodeType("Effect", 255, "Saturator"); + RegisterNodeType("Input", 300, "Keyboard"); } ~NodeGraphSchema() = default; diff --git a/packages/nodegraph/source/common/NodeGraphOperations.cpp b/packages/nodegraph/source/common/NodeGraphOperations.cpp index 1f2aa598..1252751f 100644 --- a/packages/nodegraph/source/common/NodeGraphOperations.cpp +++ b/packages/nodegraph/source/common/NodeGraphOperations.cpp @@ -336,78 +336,6 @@ namespace l::nodegraph { } ); } - /*********************************************************************/ - void GraphSourceKeyboard::Process(int32_t, std::vector& inputs, std::vector& outputs) { - for (size_t i = 0; i < inputs.size();i++) { - outputs.at(i).mOutput = inputs.at(i).Get(); - } - } - - void GraphSourceKeyboard::Tick(int32_t, float) { - mKeyboard.Update(); - } - - void GraphSourceKeyboard::Reset() { - for (int8_t i = 0; i < GetNumInputs(); i++) { - mNode->SetInput(i, 0.0f); - } - } - - void GraphSourceKeyboard::NoteOn(int32_t note) { - float frequency = l::audio::GetFrequencyFromNote(static_cast(note)); - int8_t channel = GetNextNoteChannel(note); - mNode->SetInput(static_cast(channel), frequency); - } - void GraphSourceKeyboard::NoteOff() { - Reset(); - } - - void GraphSourceKeyboard::NoteOff(int32_t note) { - int8_t channel = ResetNoteChannel(note); - if (channel >= 0) { - mNode->SetInput(channel, 0.0f); - } - } - - int8_t GraphSourceKeyboard::ResetNoteChannel(int32_t note) { - for (size_t i = 0; i < mChannel.size(); i++) { - if (mChannel.at(i).first == note) { - mChannel.at(i).second = 0; - return static_cast(i); - } - } - // It is possible to get a note off for a note not playing because the channel was taken for another newer note - return -1; - } - - int8_t GraphSourceKeyboard::GetNextNoteChannel(int32_t note) { - for (size_t i = 0; i < mChannel.size(); i++) { - if (mChannel.at(i).first == note) { - mChannel.at(i).second = mNoteCounter++; - return static_cast(i); - } - } - - for (size_t i = 0; i < mChannel.size(); i++) { - if (mChannel.at(i).first == 0) { - mChannel.at(i).first = note; - mChannel.at(i).second = mNoteCounter++; - return static_cast(i); - } - } - - int32_t lowestCount = INT32_MAX; - int8_t lowestCountIndex = 0; - for (size_t i = 0; i < mChannel.size(); i++) { - if (lowestCount > mChannel.at(i).second) { - lowestCount = mChannel.at(i).second; - lowestCountIndex = static_cast(i); - } - } - mChannel.at(lowestCountIndex).first = note; - mChannel.at(lowestCountIndex).second = mNoteCounter++; - return lowestCountIndex; - } /* Stateful filtering operations */ @@ -941,4 +869,148 @@ namespace l::nodegraph { outputs.at(1).mOutput = dry * in1 + wet * inPreamp1; } + /*********************************************************************/ + void GraphInputKeyboardPiano::Process(int32_t, std::vector& inputs, std::vector& outputs) { + for (size_t i = 0; i < inputs.size(); i++) { + outputs.at(i).mOutput = inputs.at(i).Get(); + } + } + + void GraphInputKeyboardPiano::Tick(int32_t, float) { + mKeyboard.Update(); + } + + void GraphInputKeyboardPiano::Reset() { + for (int8_t i = 0; i < GetNumInputs(); i++) { + mNode->SetInput(i, 0.0f); + } + } + + void GraphInputKeyboardPiano::NoteOn(int32_t note) { + float frequency = l::audio::GetFrequencyFromNote(static_cast(note)); + int8_t channel = GetNextNoteChannel(note); + mNode->SetInput(static_cast(channel), frequency); + } + void GraphInputKeyboardPiano::NoteOff() { + Reset(); + } + + void GraphInputKeyboardPiano::NoteOff(int32_t note) { + int8_t channel = ResetNoteChannel(note); + if (channel >= 0) { + mNode->SetInput(channel, 0.0f); + } + } + + int8_t GraphInputKeyboardPiano::ResetNoteChannel(int32_t note) { + for (size_t i = 0; i < mChannel.size(); i++) { + if (mChannel.at(i).first == note) { + mChannel.at(i).second = 0; + return static_cast(i); + } + } + // It is possible to get a note off for a note not playing because the channel was taken for another newer note + return -1; + } + + int8_t GraphInputKeyboardPiano::GetNextNoteChannel(int32_t note) { + for (size_t i = 0; i < mChannel.size(); i++) { + if (mChannel.at(i).first == note) { + mChannel.at(i).second = mNoteCounter++; + return static_cast(i); + } + } + + for (size_t i = 0; i < mChannel.size(); i++) { + if (mChannel.at(i).first == 0) { + mChannel.at(i).first = note; + mChannel.at(i).second = mNoteCounter++; + return static_cast(i); + } + } + + int32_t lowestCount = INT32_MAX; + int8_t lowestCountIndex = 0; + for (size_t i = 0; i < mChannel.size(); i++) { + if (lowestCount > mChannel.at(i).second) { + lowestCount = mChannel.at(i).second; + lowestCountIndex = static_cast(i); + } + } + mChannel.at(lowestCountIndex).first = note; + mChannel.at(lowestCountIndex).second = mNoteCounter++; + return lowestCountIndex; + } + + /*********************************************************************/ + void GraphInputMidi::Process(int32_t, std::vector& inputs, std::vector& outputs) { + for (size_t i = 0; i < inputs.size(); i++) { + outputs.at(i).mOutput = inputs.at(i).Get(); + } + } + + void GraphInputMidi::Tick(int32_t, float) { + } + + void GraphInputMidi::Reset() { + for (int8_t i = 0; i < GetNumInputs(); i++) { + mNode->SetInput(i, 0.0f); + } + } + + void GraphInputMidi::NoteOn(int32_t note) { + float frequency = l::audio::GetFrequencyFromNote(static_cast(note)); + int8_t channel = GetNextNoteChannel(note); + mNode->SetInput(static_cast(channel), frequency); + } + void GraphInputMidi::NoteOff() { + Reset(); + } + + void GraphInputMidi::NoteOff(int32_t note) { + int8_t channel = ResetNoteChannel(note); + if (channel >= 0) { + mNode->SetInput(channel, 0.0f); + } + } + + int8_t GraphInputMidi::ResetNoteChannel(int32_t note) { + for (size_t i = 0; i < mChannel.size(); i++) { + if (mChannel.at(i).first == note) { + mChannel.at(i).second = 0; + return static_cast(i); + } + } + // It is possible to get a note off for a note not playing because the channel was taken for another newer note + return -1; + } + + int8_t GraphInputMidi::GetNextNoteChannel(int32_t note) { + for (size_t i = 0; i < mChannel.size(); i++) { + if (mChannel.at(i).first == note) { + mChannel.at(i).second = mNoteCounter++; + return static_cast(i); + } + } + + for (size_t i = 0; i < mChannel.size(); i++) { + if (mChannel.at(i).first == 0) { + mChannel.at(i).first = note; + mChannel.at(i).second = mNoteCounter++; + return static_cast(i); + } + } + + int32_t lowestCount = INT32_MAX; + int8_t lowestCountIndex = 0; + for (size_t i = 0; i < mChannel.size(); i++) { + if (lowestCount > mChannel.at(i).second) { + lowestCount = mChannel.at(i).second; + lowestCountIndex = static_cast(i); + } + } + mChannel.at(lowestCountIndex).first = note; + mChannel.at(lowestCountIndex).second = mNoteCounter++; + return lowestCountIndex; + } } diff --git a/packages/nodegraph/source/common/NodeGraphSchema.cpp b/packages/nodegraph/source/common/NodeGraphSchema.cpp index 8b20436e..b7bf9c04 100644 --- a/packages/nodegraph/source/common/NodeGraphSchema.cpp +++ b/packages/nodegraph/source/common/NodeGraphSchema.cpp @@ -34,21 +34,18 @@ namespace l::nodegraph { node = mMainNodeGraph.NewNode(OutputType::Default, 3); break; case 4: - node = mMainNodeGraph.NewNode(OutputType::Default, 1, mKeyState); - break; - case 5: node = mMainNodeGraph.NewNode(OutputType::Default); break; - case 6: + case 5: node = mMainNodeGraph.NewNode(OutputType::Default); break; - case 7: + case 6: node = mMainNodeGraph.NewNode(OutputType::Default); break; - case 8: + case 7: node = mMainNodeGraph.NewNode(OutputType::Default); break; - case 9: + case 8: node = mMainNodeGraph.NewNode(OutputType::Default); break; case 50: @@ -114,6 +111,13 @@ namespace l::nodegraph { case 255: node = mMainNodeGraph.NewNode(OutputType::Default); break; + case 300: + node = mMainNodeGraph.NewNode(OutputType::Default, 1, mKeyState); + break; + case 301: + node = mMainNodeGraph.NewNode(OutputType::Default); + break; + default: ASSERT(typeId < 10000) << "Custom node id's begin at id 1000"; ASSERT(mCreateCustomNode != nullptr) << "Custom nodes needs a handler to create them. It's missing."; diff --git a/packages/tools/source/windows/utils/Midi.h b/packages/tools/source/windows/utils/Midi.h deleted file mode 100644 index 9b2f245d..00000000 --- a/packages/tools/source/windows/utils/Midi.h +++ /dev/null @@ -1,317 +0,0 @@ -#pragma once - -#include "logging/Log.h" - -#include -#include - -namespace l { -namespace x { - -namespace midi { - - struct MidiData { - uint32_t msg; // input message - uint32_t device; // device id - uint32_t timestamp; // Since opening the midi device - uint32_t status; // noteon=145, noteoff=129, knob=176, padon=144, padoff=128, sustain=177, - uint32_t channel; - uint32_t data1; // Key unique - uint32_t data2; // Key attack (0-127), pad attack (127), knob position (0-127) - uint32_t unused; - }; - - /* - uint32_t msg; // message - uint32_t instance; // device id - uint32_t type = (param1) & 0xff; // noteon=145, noteoff=129, knob=176, padon=144, padoff=128, sustain=177, - uint32_t key = (param1 >> 8) & 0xff; // key unique - uint32_t atc = (param1 >> 16) & 0xff; // key attack (0-127), pad attack (127), knob position (0-127) - uint32_t arg2 = (param1 >> 24) & 0xff; // unused? - uint32_t time = param2; - */ - using CallbackFunction = std::function; - namespace { - static std::mutex midiCallbackMutex; - static std::vector midiCallback; - } - - namespace details { - void HandleMidiData(uint32_t msg, uint32_t instance, uint32_t param1, uint32_t param2) { - switch (msg) { - case MIM_OPEN: - LOG(LogInfo) << "MIM_OPEN"; - return; - case MIM_CLOSE: - LOG(LogInfo) << "MIM_CLOSE"; - return; - case MIM_LONGDATA: - LOG(LogInfo) << "Long data: " << msg << " " << instance << " " << param1 << " " << param2; - return; - case MIM_ERROR: - LOG(LogInfo) << "Error: " << msg << " " << instance << " " << param1 << " " << param2; - return; - case MIM_LONGERROR: - LOG(LogInfo) << "Long error: " << msg << " " << instance << " " << param1 << " " << param2; - return; - case MIM_MOREDATA: - LOG(LogInfo) << "More data: " << msg << " " << instance << " " << param1 << " " << param2; - return; - case MIM_DATA: - //LOG(LogInfo) << "Data: " << msg << " " << instance << " " << param1 << " " << param2; - break; - default: - LOG(LogInfo) << "default"; - break; - } - - std::lock_guard lock(midiCallbackMutex); - if (!midiCallback.empty()) { - MidiData data; - data.msg = msg; - data.device = instance; - data.timestamp = param2; - data.status = (param1 >> 4) & 0xf; - data.channel = (param1) & 0xf; - data.data1 = (param1 >> 8) & 0xff; - data.data2 = (param1 >> 16) & 0xff; - data.unused = 0; - - for (auto cb : midiCallback) { - if (cb) { - (*cb)(data); - } - } - } - } - - void CALLBACK MidiInProc(HMIDIIN hMidiIn, UINT wMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2) { - HandleMidiData(static_cast(wMsg), static_cast(dwInstance), static_cast(dwParam1), static_cast(dwParam2)); - } - - void CALLBACK MidiOutProc(HMIDIIN hMidiIn, UINT wMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2) { - - switch (wMsg) { - case MOM_OPEN: - LOG(LogInfo) << "MOM_OPEN"; - break; - case MOM_CLOSE: - LOG(LogInfo) << "MOM_CLOSE"; - break; - case MOM_DONE: - LOG(LogInfo) << "MOM_DONE"; - break; - default: - LOG(LogInfo) << "Something else.."; - break; - } - - } - } - - class Midi { - public: - Midi(CallbackFunction f) : mHeader{}, mMidiBuffer{}, mCallback(std::move(f)) { - { - std::lock_guard lock(midiCallbackMutex); - midiCallback.push_back(&mCallback); - } - - UINT nMidiDeviceNum = midiInGetNumDevs(); - if (nMidiDeviceNum == 0) { - return; - } - UINT nMidiOutDeviceNum = midiOutGetNumDevs(); - - for (size_t deviceId = 0; deviceId < nMidiDeviceNum; deviceId++) { - MMRESULT rv; - HMIDIIN hMidiDevice = NULL; - - rv = midiInOpen(&hMidiDevice, static_cast(deviceId), reinterpret_cast(&details::MidiInProc), 0, CALLBACK_FUNCTION); - if (rv == MMSYSERR_ALLOCATED) { - return; - } - if (rv != MMSYSERR_NOERROR) { - LOG(LogError) << "Failed to open midi in device " << deviceId; - continue; - } - - rv = midiInStart(hMidiDevice); - if (rv != MMSYSERR_NOERROR) { - LOG(LogError) << "Failed to start midi in device" << deviceId; - continue; - } - - MIDIINCAPS capsIn; - rv = midiInGetDevCaps(deviceId, &capsIn, sizeof(MIDIINCAPS)); - if (rv != MMSYSERR_NOERROR) { - LOG(LogError) << "Failed to get midi in caps on device " << deviceId; - } - - HMIDIOUT hMidiDeviceOut = nullptr; - deviceId++; - rv = midiOutOpen(&hMidiDeviceOut, static_cast(deviceId), reinterpret_cast(&details::MidiOutProc), 0, CALLBACK_FUNCTION); - if (rv == MMSYSERR_ALLOCATED) { - return; - } - if (rv != MMSYSERR_NOERROR) { - LOG(LogError) << "Failed to open midi out device " << deviceId; - } - - MIDIOUTCAPS capsOut; - rv = midiOutGetDevCaps(deviceId, &capsOut, sizeof(MIDIOUTCAPS)); - if (rv != MMSYSERR_NOERROR) { - LOG(LogError) << "Failed to get midi out caps on device " << deviceId; - } - deviceId--; - - devices.push_back(std::make_pair(hMidiDevice, hMidiDeviceOut)); - caps.push_back(std::make_pair(capsIn, capsOut)); - } - } - ~Midi() { - int deviceId = 0; - for (auto device : devices) { - MMRESULT rv; - rv = midiOutClose(device.second); - if (rv != MMSYSERR_NOERROR) { - LOG(LogError) << "Failed to close midi out device" << deviceId; - } - rv = midiInStop(device.first); - if (rv == MMSYSERR_NOERROR) { - rv = midiInClose(device.first); - } - if (rv != MMSYSERR_NOERROR) { - LOG(LogError) << "Failed to close midi in device" << deviceId; - } - deviceId++; - } - { - std::lock_guard lock(midiCallbackMutex); - auto it = std::find(midiCallback.begin(), midiCallback.end(), &mCallback); - if (it != midiCallback.end()) { - midiCallback.erase(it); - } - } - } - - void clearBuffer() { - mHeader.dwBufferLength = 0; - mHeader.dwBytesRecorded = 0; - } - - void pushBuffer(unsigned char byte) { - mMidiBuffer[mHeader.dwBufferLength] = static_cast(byte); - mHeader.dwBufferLength++; - mHeader.dwBytesRecorded++; - } - - void sendSysex2(uint32_t deviceId, uint32_t i, uint32_t data) { - if (deviceId >= devices.size()) { - return; - } - static uint32_t counter = 0; - - mHeader = { 0 }; - mHeader.lpData = mMidiBuffer; - - clearBuffer(); - - switch (i) { - case 0: - mMidiBuffer[0] = 0x7e; // - mMidiBuffer[1] = 0x00; // AKAI - mMidiBuffer[2] = 0x06; // General information - mMidiBuffer[3] = 0x01; // Identity request - mHeader.dwBufferLength = 4; - mHeader.dwBytesRecorded = 4; - break; - case 1: - pushBuffer(0xf0); - pushBuffer(0x47); - pushBuffer(0x00); - pushBuffer(0xff); // 27 - pushBuffer(0xf7); - break; - case 2: - pushBuffer(0xf0); - pushBuffer(0x47); - pushBuffer(0xff); - pushBuffer(0xff); - pushBuffer(0x60); - pushBuffer(0x00); - pushBuffer(0x04); - pushBuffer(0x40); - pushBuffer(0x01); - pushBuffer(0x00); - pushBuffer(0x00); - pushBuffer(0xf7); - break; - case 3: - mMidiBuffer[0] = 0x7f; // Real Time (7FH) - mMidiBuffer[1] = 0x47; // AKAI - mMidiBuffer[2] = 0x02; // MIDI Show Control - mMidiBuffer[3] = data; - mHeader.dwBufferLength = 4; - mHeader.dwBytesRecorded = 4; - break; - case 4: - mMidiBuffer[0] = 0x7f; // Real Time (7FH) - mMidiBuffer[1] = 0x47; // AKAI - mMidiBuffer[2] = 0x06; // MIDI Machine Control Commands - mMidiBuffer[3] = data; - mHeader.dwBufferLength = 4; - mHeader.dwBytesRecorded = 4; - break; - default: - - break; - } - - HMIDIIN device = devices[deviceId].first; - HMIDIOUT deviceOut = devices[deviceId].second; - - MMRESULT rv = midiOutPrepareHeader(deviceOut, &mHeader, sizeof(MIDIHDR)); - if (rv != MMSYSERR_NOERROR) { - LOG(LogError) << "Failed to prepare midi out buffer " << deviceId << ", error " << rv; - } - - rv = midiOutLongMsg(deviceOut, &mHeader, sizeof(MIDIHDR)); - if (rv != MMSYSERR_NOERROR) { - LOG(LogError) << "Failed to send buffer to midi out device " << deviceId << ", error " << rv; - } - - rv = midiOutUnprepareHeader(deviceOut, &mHeader, sizeof(MIDIHDR)); - if (rv != MMSYSERR_NOERROR) { - LOG(LogError) << "Failed to unprepare midi out buffer " << deviceId << ", error " << rv; - } - } - - void send(uint32_t deviceId, uint32_t status, uint32_t channel, uint32_t data1, uint32_t data2) { - if (deviceId >= devices.size()) { - return; - } - HMIDIOUT device = devices[deviceId].second; - - DWORD param1 = ((data2 & 0xff) << 16) | ((data1 & 0xff) << 8) | ((status & 0xf0) | (channel & 0xf)); - - MMRESULT rv = midiOutShortMsg(device, param1); - if (rv != MMSYSERR_NOERROR) { - LOG(LogError) << "Failed to send to midi out device " << deviceId << ", error " << rv << ", data " << param1; - } - } - - private: - MIDIHDR mHeader{}; - char mMidiBuffer[16]; - - CallbackFunction mCallback; - std::vector> devices; - std::vector< std::pair> caps; - }; - -} - -} -} - From 0be7680c065cee216b1edeeac92fac77a7fce433 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Fri, 6 Sep 2024 20:38:06 +0200 Subject: [PATCH 120/125] Fix warnings. --- packages/nodegraph/source/common/NodeGraphOperations.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nodegraph/source/common/NodeGraphOperations.cpp b/packages/nodegraph/source/common/NodeGraphOperations.cpp index 1252751f..c3310e83 100644 --- a/packages/nodegraph/source/common/NodeGraphOperations.cpp +++ b/packages/nodegraph/source/common/NodeGraphOperations.cpp @@ -533,7 +533,7 @@ namespace l::nodegraph { for (int32_t i = 0; i < numSamples; i++) { output[mCurIndex] = *value++; - mCurIndex = ++mCurIndex % outputSize; + mCurIndex = (mCurIndex + 1) % outputSize; } } From 9afb124caaddab071cbf6c702eded7e41c1aad3b Mon Sep 17 00:00:00 2001 From: lnd3 Date: Fri, 6 Sep 2024 20:46:11 +0200 Subject: [PATCH 121/125] fix warnings. --- packages/rendering/source/common/ui/UIVisitors.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/rendering/source/common/ui/UIVisitors.cpp b/packages/rendering/source/common/ui/UIVisitors.cpp index f229b877..7f0bb415 100644 --- a/packages/rendering/source/common/ui/UIVisitors.cpp +++ b/packages/rendering/source/common/ui/UIVisitors.cpp @@ -362,7 +362,7 @@ namespace l::ui { size.x *= layoutArea.mScale; size.y *= layoutArea.mScale; ImVec2 startPos = ImVec2(p1.x, p1.y + 0.5f * size.y); - for (size_t i = 0; i < nodeValueCount - 1; i++) { + for (int32_t i = 0; i < nodeValueCount - 1; i++) { float xpart1 = i / static_cast(nodeValueCount); float xpart2 = (i+1) / static_cast(nodeValueCount); ImVec2 graphP1 = ImVec2(startPos.x + size.x * xpart1, startPos.y + 0.5f * nodeValues[i] * size.y); From c6e7456773ebb9cb4b9955ece0763cdceb251974 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Sat, 7 Sep 2024 02:08:23 +0200 Subject: [PATCH 122/125] Add midi keyboard and midi knob nodes. Fix some issues. --- packages/hid/include/hid/Midi.h | 4 +- .../include/nodegraph/NodeGraphOperations.h | 91 ++++++++++++++++--- .../include/nodegraph/NodeGraphSchema.h | 8 +- .../source/common/NodeGraphOperations.cpp | 80 +++++++++++----- .../source/common/NodeGraphSchema.cpp | 9 +- .../rendering/source/common/ui/UIVisitors.cpp | 2 +- 6 files changed, 156 insertions(+), 38 deletions(-) diff --git a/packages/hid/include/hid/Midi.h b/packages/hid/include/hid/Midi.h index 864df8a7..9a6e8daa 100644 --- a/packages/hid/include/hid/Midi.h +++ b/packages/hid/include/hid/Midi.h @@ -28,7 +28,7 @@ namespace l::hid::midi { uint32_t arg2 = (param1 >> 24) & 0xff; // unused? uint32_t time = param2; */ - using CallbackFunction = std::function; + using CallbackFunction = std::function; namespace details { extern std::mutex midiCallbackMutex; @@ -40,7 +40,7 @@ namespace l::hid::midi { class MidiManager { public: MidiManager() { - RegisterCallback([](MidiData data) { + RegisterCallback([](const MidiData& data) { LOG(LogInfo) << "listener 1: dev" << data.device << " stat " << data.status << " ch " << data.channel << " d1 " << data.data1 << " d2 " << data.data2; }); } diff --git a/packages/nodegraph/include/nodegraph/NodeGraphOperations.h b/packages/nodegraph/include/nodegraph/NodeGraphOperations.h index e056589d..45832af1 100644 --- a/packages/nodegraph/include/nodegraph/NodeGraphOperations.h +++ b/packages/nodegraph/include/nodegraph/NodeGraphOperations.h @@ -2,8 +2,12 @@ #include "nodegraph/NodeGraph.h" #include "logging/LoggingAll.h" + #include "hid/KeyboardPiano.h" +#include "hid/Midi.h" + #include "audio/PortAudio.h" + #include "math/MathFunc.h" #include @@ -70,6 +74,8 @@ namespace l::nodegraph { virtual std::string_view GetName() override { return "Time"; } + virtual bool IsDataVisible(int8_t) override { return true; } + virtual bool IsDataEditable(int8_t) override { return true; } protected: float mAudioTime = 0.0f; float mFrameTime = 0.0f; @@ -253,6 +259,8 @@ namespace l::nodegraph { virtual std::string_view GetName() override { return "Add"; } + virtual bool IsDataVisible(int8_t) override { return true; } + virtual bool IsDataEditable(int8_t) override { return true; } }; /*********************************************************************/ @@ -269,6 +277,8 @@ namespace l::nodegraph { virtual std::string_view GetName() override { return "Multiply"; } + virtual bool IsDataVisible(int8_t) override { return true; } + virtual bool IsDataEditable(int8_t) override { return true; } }; /*********************************************************************/ @@ -284,6 +294,8 @@ namespace l::nodegraph { virtual std::string_view GetName() override { return "Subtract"; } + virtual bool IsDataVisible(int8_t) override { return true; } + virtual bool IsDataEditable(int8_t) override { return true; } }; /*********************************************************************/ @@ -300,6 +312,8 @@ namespace l::nodegraph { virtual std::string_view GetName() override { return "Negate"; } + virtual bool IsDataVisible(int8_t) override { return true; } + virtual bool IsDataEditable(int8_t) override { return true; } }; /*********************************************************************/ @@ -321,7 +335,8 @@ namespace l::nodegraph { virtual std::string_view GetName() override { return "Integral"; } - + virtual bool IsDataVisible(int8_t) override { return true; } + virtual bool IsDataEditable(int8_t) override { return true; } protected: float mOutput = 0.0f; }; @@ -340,6 +355,8 @@ namespace l::nodegraph { virtual std::string_view GetName() override { return "Multiply3"; } + virtual bool IsDataVisible(int8_t) override { return true; } + virtual bool IsDataEditable(int8_t) override { return true; } }; /*********************************************************************/ @@ -362,6 +379,8 @@ namespace l::nodegraph { virtual std::string_view GetInputName(int8_t inputChannel) { return defaultInStrings[inputChannel]; } + virtual bool IsDataVisible(int8_t) override { return true; } + virtual bool IsDataEditable(int8_t) override { return true; } }; /*********************************************************************/ @@ -377,6 +396,8 @@ namespace l::nodegraph { virtual std::string_view GetName() override { return "Round"; } + virtual bool IsDataVisible(int8_t) override { return true; } + virtual bool IsDataEditable(int8_t) override { return true; } }; /* Logical operations */ @@ -397,6 +418,8 @@ namespace l::nodegraph { std::string_view GetName() override { return "And"; } + virtual bool IsDataVisible(int8_t) override { return true; } + virtual bool IsDataEditable(int8_t) override { return true; } }; /*********************************************************************/ @@ -415,6 +438,8 @@ namespace l::nodegraph { std::string_view GetName() override { return "Or"; } + virtual bool IsDataVisible(int8_t) override { return true; } + virtual bool IsDataEditable(int8_t) override { return true; } }; /*********************************************************************/ @@ -433,6 +458,8 @@ namespace l::nodegraph { virtual std::string_view GetName() override { return "Xor"; } + virtual bool IsDataVisible(int8_t) override { return true; } + virtual bool IsDataEditable(int8_t) override { return true; } }; /* Stateful filtering operations */ @@ -471,11 +498,11 @@ namespace l::nodegraph { /*********************************************************************/ class GraphFilterEnvelope : public NodeGraphOp { public: - std::string defaultInStrings[5] = { "Note", "Attack", "Release", "Fade"}; + std::string defaultInStrings[5] = { "Note", "Velocity", "Attack", "Release", "Fade"}; std::string defaultOutStrings[2] = { "Note", "Volume"}; GraphFilterEnvelope(NodeGraphBase* node) : - NodeGraphOp(node, 4, 2) + NodeGraphOp(node, 5, 2) {} virtual ~GraphFilterEnvelope() = default; @@ -847,18 +874,23 @@ namespace l::nodegraph { }; /*********************************************************************/ - class GraphInputMidi : public NodeGraphOp, public l::hid::INoteProcessor { + class GraphInputMidiKeyboard : public NodeGraphOp { public: - GraphInputMidi(NodeGraphBase* node) : - NodeGraphOp(node, 0) + GraphInputMidiKeyboard(NodeGraphBase* node, l::hid::midi::MidiManager* midiManager) : + NodeGraphOp(node, 0, 2, 2), + mMidiManager(midiManager) { + mChannel.resize(1); + + mMidiManager->RegisterCallback([&](l::hid::midi::MidiData data) { + MidiEvent(data); + }); } - std::string defaultOutStrings[1] = { "In 1" }; + std::string defaultOutStrings[2] = { "Note", "Velocity"}; - virtual ~GraphInputMidi() = default; + virtual ~GraphInputMidiKeyboard() = default; virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; - virtual void Tick(int32_t tickCount, float elapsed) override; virtual void Reset() override; virtual std::string_view GetOutputName(int8_t outputChannel) override { return defaultOutStrings[outputChannel]; @@ -869,16 +901,51 @@ namespace l::nodegraph { virtual bool IsDataVisible(int8_t) override { return true; } - virtual void NoteOn(int32_t note) override; - virtual void NoteOff() override; - virtual void NoteOff(int32_t note) override; + + virtual void MidiEvent(const l::hid::midi::MidiData& data); + void NoteOn(int32_t note, int32_t velocity); + void NoteOff(); + void NoteOff(int32_t note); protected: int8_t ResetNoteChannel(int32_t note); int8_t GetNextNoteChannel(int32_t note); + l::hid::midi::MidiManager* mMidiManager = nullptr; + int8_t mNoteCounter = 0; std::vector> mChannel; }; + /*********************************************************************/ + class GraphInputMidiKnobs : public NodeGraphOp { + public: + GraphInputMidiKnobs(NodeGraphBase* node, l::hid::midi::MidiManager* midiManager) : + NodeGraphOp(node, 0, 8, 8), + mMidiManager(midiManager) + { + mMidiManager->RegisterCallback([&](l::hid::midi::MidiData data) { + MidiEvent(data); + }); + } + + std::string defaultOutStrings[8] = { "Knob 1", "Knob 2", "Knob 3", "Knob 4", "Knob 5", "Knob 6", "Knob 7", "Knob 8", }; + + virtual ~GraphInputMidiKnobs() = default; + virtual void Process(int32_t numSamples, std::vector& inputs, std::vector& outputs) override; + virtual void Reset() override; + virtual std::string_view GetOutputName(int8_t outputChannel) override { + return defaultOutStrings[outputChannel]; + } + virtual std::string_view GetName() override { + return "Midi Knobs"; + } + virtual bool IsDataVisible(int8_t) override { + return true; + } + + virtual void MidiEvent(const l::hid::midi::MidiData& data); + protected: + l::hid::midi::MidiManager* mMidiManager = nullptr; + }; } diff --git a/packages/nodegraph/include/nodegraph/NodeGraphSchema.h b/packages/nodegraph/include/nodegraph/NodeGraphSchema.h index 207e7292..b818c0ff 100644 --- a/packages/nodegraph/include/nodegraph/NodeGraphSchema.h +++ b/packages/nodegraph/include/nodegraph/NodeGraphSchema.h @@ -5,6 +5,8 @@ #include "nodegraph/NodeGraph.h" #include "nodegraph/NodeGraphOperations.h" +#include "hid/Midi.h" + #include #include #include @@ -62,7 +64,9 @@ namespace l::nodegraph { RegisterNodeType("Effect", 253, "Limiter"); RegisterNodeType("Effect", 254, "Envelope Follower"); RegisterNodeType("Effect", 255, "Saturator"); - RegisterNodeType("Input", 300, "Keyboard"); + RegisterNodeType("Input", 300, "Keyboard Piano"); + RegisterNodeType("Input", 301, "Midi Keyboard"); + RegisterNodeType("Input", 302, "Midi Knobs"); } ~NodeGraphSchema() = default; @@ -70,6 +74,7 @@ namespace l::nodegraph { void SetCustomCreator(std::function customCreator); void SetKeyState(l::hid::KeyState* keyState); void SetAudioOutput(l::audio::AudioStream* audioStream); + void SetMidiManager(l::hid::midi::MidiManager* midiManager); int32_t NewNode(int32_t typeId); bool RemoveNode(int32_t nodeId); @@ -90,6 +95,7 @@ namespace l::nodegraph { std::function mCreateCustomNode; l::hid::KeyState* mKeyState; l::audio::AudioStream* mAudioOutput; + l::hid::midi::MidiManager* mMidiManager; std::map> mRegisteredNodeTypes; }; diff --git a/packages/nodegraph/source/common/NodeGraphOperations.cpp b/packages/nodegraph/source/common/NodeGraphOperations.cpp index c3310e83..acf13192 100644 --- a/packages/nodegraph/source/common/NodeGraphOperations.cpp +++ b/packages/nodegraph/source/common/NodeGraphOperations.cpp @@ -366,19 +366,22 @@ namespace l::nodegraph { /*********************************************************************/ void GraphFilterEnvelope::Reset() { mEnvelope = 0.0f; - mNode->SetInput(1, 50.0f); + mNode->SetInput(1, 0.5f); mNode->SetInput(2, 50.0f); - mNode->SetInput(3, 0.1f); - mNode->SetInputBound(1, InputBound::INPUT_CUSTOM, 1.0f, 100000.0f); + mNode->SetInput(3, 50.0f); + mNode->SetInput(4, 0.1f); + mNode->SetInputBound(1, InputBound::INPUT_0_TO_1); mNode->SetInputBound(2, InputBound::INPUT_CUSTOM, 1.0f, 100000.0f); - mNode->SetInputBound(3, InputBound::INPUT_CUSTOM, 0.0001f, 1.0f); + mNode->SetInputBound(3, InputBound::INPUT_CUSTOM, 1.0f, 100000.0f); + mNode->SetInputBound(4, InputBound::INPUT_CUSTOM, 0.0001f, 1.0f); } void GraphFilterEnvelope::Process(int32_t, std::vector& inputs, std::vector& outputs) { float noteTarget = inputs.at(0).Get(); - float attackFrames = inputs.at(1).Get() * 44100.0f / 1000.0f; - float releaseFrames = inputs.at(2).Get() * 44100.0f / 1000.0f; - float noteFade = inputs.at(3).Get(); + float velocity = l::math::functions::pow(inputs.at(1).Get(), 0.5f); + float attackFrames = inputs.at(2).Get() * 44100.0f / 1000.0f; + float releaseFrames = inputs.at(3).Get() * 44100.0f / 1000.0f; + float noteFade = inputs.at(4).Get(); //noteFade = l::math::functions::pow(0.01f, 1.0f / (1000.0f * inputs.at(3).Get() * 44100.0f * 0.001f)); if (noteTarget != 0.0f && mFrameCount < attackFrames) { @@ -391,11 +394,11 @@ namespace l::nodegraph { } if (noteTarget != 0.0f) { - if (mEnvelopeTarget < 1.0f) { - mEnvelopeTarget += 1.0f / attackFrames; + if (mEnvelopeTarget < velocity) { + mEnvelopeTarget += velocity / attackFrames; } else { - mEnvelopeTarget = 1.0f; + mEnvelopeTarget = velocity; } mNote += noteFade * noteFade * (noteTarget - mNote); //mNote = noteFade * (mEnvelope - noteTarget) + noteTarget; @@ -403,7 +406,7 @@ namespace l::nodegraph { else { // release if (mFrameCount > 0) { - mEnvelopeTarget -= 1.0f / releaseFrames; + mEnvelopeTarget -= velocity / releaseFrames; if (mEnvelopeTarget < 0.0f) { mEnvelopeTarget = 0.0f; mFrameCount = 0; @@ -943,38 +946,49 @@ namespace l::nodegraph { } /*********************************************************************/ - void GraphInputMidi::Process(int32_t, std::vector& inputs, std::vector& outputs) { + void GraphInputMidiKeyboard::Process(int32_t, std::vector& inputs, std::vector& outputs) { for (size_t i = 0; i < inputs.size(); i++) { outputs.at(i).mOutput = inputs.at(i).Get(); } } - void GraphInputMidi::Tick(int32_t, float) { - } - - void GraphInputMidi::Reset() { + void GraphInputMidiKeyboard::Reset() { for (int8_t i = 0; i < GetNumInputs(); i++) { mNode->SetInput(i, 0.0f); } } - void GraphInputMidi::NoteOn(int32_t note) { + void GraphInputMidiKeyboard::MidiEvent(const l::hid::midi::MidiData& data) { + //LOG(LogInfo) << "listener 1: dev" << data.device << " stat " << data.status << " ch " << data.channel << " d1 " << data.data1 << " d2 " << data.data2; + + if (data.status == 9) { + // note on + NoteOn(data.data1, data.data2); + } + else if (data.status == 8) { + // note off + NoteOff(data.data1); + } + } + + void GraphInputMidiKeyboard::NoteOn(int32_t note, int32_t velocity) { float frequency = l::audio::GetFrequencyFromNote(static_cast(note)); int8_t channel = GetNextNoteChannel(note); mNode->SetInput(static_cast(channel), frequency); + mNode->SetInput(static_cast(1), velocity / 128.0f); } - void GraphInputMidi::NoteOff() { + void GraphInputMidiKeyboard::NoteOff() { Reset(); } - void GraphInputMidi::NoteOff(int32_t note) { + void GraphInputMidiKeyboard::NoteOff(int32_t note) { int8_t channel = ResetNoteChannel(note); if (channel >= 0) { mNode->SetInput(channel, 0.0f); } } - int8_t GraphInputMidi::ResetNoteChannel(int32_t note) { + int8_t GraphInputMidiKeyboard::ResetNoteChannel(int32_t note) { for (size_t i = 0; i < mChannel.size(); i++) { if (mChannel.at(i).first == note) { mChannel.at(i).second = 0; @@ -985,7 +999,7 @@ namespace l::nodegraph { return -1; } - int8_t GraphInputMidi::GetNextNoteChannel(int32_t note) { + int8_t GraphInputMidiKeyboard::GetNextNoteChannel(int32_t note) { for (size_t i = 0; i < mChannel.size(); i++) { if (mChannel.at(i).first == note) { mChannel.at(i).second = mNoteCounter++; @@ -1013,4 +1027,28 @@ namespace l::nodegraph { mChannel.at(lowestCountIndex).second = mNoteCounter++; return lowestCountIndex; } + + /*********************************************************************/ + void GraphInputMidiKnobs::Process(int32_t, std::vector& inputs, std::vector& outputs) { + for (size_t i = 0; i < inputs.size(); i++) { + outputs.at(i).mOutput = inputs.at(i).Get(); + } + } + + void GraphInputMidiKnobs::Reset() { + for (int8_t i = 0; i < GetNumInputs(); i++) { + mNode->SetInput(i, 0.0f); + } + } + + void GraphInputMidiKnobs::MidiEvent(const l::hid::midi::MidiData& data) { + if (data.status == 11) { + //LOG(LogInfo) << "listener 1: dev" << data.device << " stat " << data.status << " ch " << data.channel << " d1 " << data.data1 << " d2 " << data.data2; + + if (data.data1 >= 48 && data.data1 <= 55) { + mNode->SetInput(static_cast(data.data1 - 48), data.data2 / 128.0f); + } + } + } + } diff --git a/packages/nodegraph/source/common/NodeGraphSchema.cpp b/packages/nodegraph/source/common/NodeGraphSchema.cpp index b7bf9c04..0e9efa30 100644 --- a/packages/nodegraph/source/common/NodeGraphSchema.cpp +++ b/packages/nodegraph/source/common/NodeGraphSchema.cpp @@ -18,6 +18,10 @@ namespace l::nodegraph { mAudioOutput = audioOutput; } + void NodeGraphSchema::SetMidiManager(l::hid::midi::MidiManager* midiManager) { + mMidiManager = midiManager; + } + int32_t NodeGraphSchema::NewNode(int32_t typeId) { l::nodegraph::NodeGraphBase* node = nullptr; switch (typeId) { @@ -115,7 +119,10 @@ namespace l::nodegraph { node = mMainNodeGraph.NewNode(OutputType::Default, 1, mKeyState); break; case 301: - node = mMainNodeGraph.NewNode(OutputType::Default); + node = mMainNodeGraph.NewNode(OutputType::Default, mMidiManager); + break; + case 302: + node = mMainNodeGraph.NewNode(OutputType::Default, mMidiManager); break; default: diff --git a/packages/rendering/source/common/ui/UIVisitors.cpp b/packages/rendering/source/common/ui/UIVisitors.cpp index 7f0bb415..eb6cea3a 100644 --- a/packages/rendering/source/common/ui/UIVisitors.cpp +++ b/packages/rendering/source/common/ui/UIVisitors.cpp @@ -209,7 +209,7 @@ namespace l::ui { if (mNGSchema) { auto node = mNGSchema->GetNode(container.GetNodeId()); auto nodeChannel = static_cast(container.GetChannelId()); - if (node->IsDataEditable(nodeChannel)) { + if (nodeChannel < node->GetNumInputs() && node->IsDataEditable(nodeChannel)) { auto nodeValue = node->GetInput(nodeChannel); if (!ImGui::IsKeyDown(ImGuiKey::ImGuiKey_LeftShift)) { if (!ImGui::IsKeyDown(ImGuiKey::ImGuiKey_LeftCtrl)) { From 7af6e516ca8a5f7bae1fac06a492a6b0ff30c368 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Sat, 7 Sep 2024 02:10:58 +0200 Subject: [PATCH 123/125] Fix null deref. --- packages/nodegraph/source/common/NodeGraph.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nodegraph/source/common/NodeGraph.cpp b/packages/nodegraph/source/common/NodeGraph.cpp index 37348dd2..9dbddaca 100644 --- a/packages/nodegraph/source/common/NodeGraph.cpp +++ b/packages/nodegraph/source/common/NodeGraph.cpp @@ -148,7 +148,7 @@ namespace l::nodegraph { return false; } input.mInput.mInputNode = &source.GetInputNode(); - source.GetInputNode().SetInputName(sourceChannel, *input.mName); + source.GetInputNode().SetInputName(sourceChannel, input.mName ? *input.mName : ""); } else { if (!IsValidInOutNum(sourceChannel, source.GetOutputNode().GetNumOutputs()) || From 692922ce38de3e7176cd8fa909dc77752754d984 Mon Sep 17 00:00:00 2001 From: lnd3 Date: Sat, 7 Sep 2024 02:12:28 +0200 Subject: [PATCH 124/125] Fix audio volume in audio test. --- packages/audio/tests/common/PortAudioTest.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/audio/tests/common/PortAudioTest.cpp b/packages/audio/tests/common/PortAudioTest.cpp index 1177964f..a12ca1e1 100644 --- a/packages/audio/tests/common/PortAudioTest.cpp +++ b/packages/audio/tests/common/PortAudioTest.cpp @@ -48,8 +48,8 @@ TEST(PortAudio, Setup) { float phaseMod = mPhase + pMod; phaseMod -= floorf(phaseMod); - buffer.at(2 * i + 0) = 0.5f * sinf(3.141529f * (mPhase + phaseMod)); - buffer.at(2 * i + 1) = 0.5f * sinf(3.141529f * (mPhase + phaseMod)); + buffer.at(2 * i + 0) = 0.015f * sinf(3.141529f * (mPhase + phaseMod)); + buffer.at(2 * i + 1) = 0.015f * sinf(3.141529f * (mPhase + phaseMod)); } stream->Write(); } From 02529743053f05160aa77c8c0aec23a12b313bba Mon Sep 17 00:00:00 2001 From: lnd3 Date: Sat, 7 Sep 2024 02:18:32 +0200 Subject: [PATCH 125/125] Clean up. --- packages/hid/source/common/Midi.cpp | 2 -- packages/hid/source/windows/MidiWindows.cpp | 2 -- 2 files changed, 4 deletions(-) diff --git a/packages/hid/source/common/Midi.cpp b/packages/hid/source/common/Midi.cpp index 1f041db9..9b60b598 100644 --- a/packages/hid/source/common/Midi.cpp +++ b/packages/hid/source/common/Midi.cpp @@ -1,5 +1,3 @@ -#pragma once - #include "logging/Log.h" #include "hid/Midi.h" diff --git a/packages/hid/source/windows/MidiWindows.cpp b/packages/hid/source/windows/MidiWindows.cpp index cb7760ce..20a94d0b 100644 --- a/packages/hid/source/windows/MidiWindows.cpp +++ b/packages/hid/source/windows/MidiWindows.cpp @@ -1,5 +1,3 @@ -#pragma once - #include "logging/Log.h" #include "MidiWindows.h"