diff --git a/CMakeLists.txt b/CMakeLists.txt index 09e3b55..0f2e1a4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,6 @@ cmake_minimum_required(VERSION 3.10) set(BUILD_TYPE "Debug and Release" CACHE STRING "Choose Build type") -set(CMAKE_BUILD_TYPE Debug) set(CMAKE_CONFIGURATION_TYPES Debug Release) # set the project name @@ -103,7 +102,6 @@ set(VariablesNodes_SOURCE_FILES) set(ControlFlowNodes_SOURCE_FILES) if(VISUAL_NODE_SYSTEM_BUILD_EXECUTION_FLOW_NODES) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /DVISUAL_NODE_SYSTEM_BUILD_EXECUTION_FLOW_NODES") # *************** EXECUTION_FLOW_NODES *************** list(APPEND BaseExecutionFlowNodes_SOURCE_FILES @@ -181,6 +179,8 @@ if(VISUAL_NODE_SYSTEM_BUILD_EXECUTION_FLOW_NODES) "StandardNodes/ExecutionFlowNodes/LiteralsNodes/IntegerLiteralNode.h" "StandardNodes/ExecutionFlowNodes/LiteralsNodes/IntegerLiteralNode.cpp" + "StandardNodes/ExecutionFlowNodes/LiteralsNodes/UnsignedLiteralNode.cpp" + "StandardNodes/ExecutionFlowNodes/LiteralsNodes/FloatLiteralNode.h" "StandardNodes/ExecutionFlowNodes/LiteralsNodes/FloatLiteralNode.cpp" @@ -210,6 +210,8 @@ if(VISUAL_NODE_SYSTEM_BUILD_EXECUTION_FLOW_NODES) "StandardNodes/ExecutionFlowNodes/VariablesNodes/IntegerVariableNode.h" "StandardNodes/ExecutionFlowNodes/VariablesNodes/IntegerVariableNode.cpp" + "StandardNodes/ExecutionFlowNodes/VariablesNodes/UnsignedVariableNode.cpp" + "StandardNodes/ExecutionFlowNodes/VariablesNodes/FloatVariableNode.h" "StandardNodes/ExecutionFlowNodes/VariablesNodes/FloatVariableNode.cpp" @@ -282,7 +284,10 @@ else() add_library(VisualNodeSystem STATIC ${ALL_SOURCE_FILES}) endif() -target_include_directories(VisualNodeSystem PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/ThirdParty/jsoncpp) +if(VISUAL_NODE_SYSTEM_BUILD_EXECUTION_FLOW_NODES) + #NOTE use cross platform method to set compile definitions + target_compile_definitions(VisualNodeSystem PRIVATE VISUAL_NODE_SYSTEM_BUILD_EXECUTION_FLOW_NODES) +endif() source_group("Source Files" FILES ${VisualNodeSystem_SRC}) source_group("Source Files/SubSystems/VisualNodeArea/" FILES ${VisualNodeArea_SRC}) @@ -303,6 +308,7 @@ endif() source_group("Source Files/ThirdParty/jsoncpp" FILES ${jsoncpp_SRC}) set(VISUAL_NODE_SYSTEM_THIRDPARTY_DIR ${CMAKE_CURRENT_SOURCE_DIR}/ThirdParty) +set(VISUAL_NODE_SYSTEM_THIRDPARTY_JSONCPP_DIR ${CMAKE_CURRENT_SOURCE_DIR}/ThirdParty/jsoncpp) # set the startup project set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT VisualNodeSystem) @@ -338,6 +344,7 @@ endif() include_directories( ${DEAR_IMGUI_INCLUDE_DIR} ${VISUAL_NODE_SYSTEM_THIRDPARTY_DIR} + ${VISUAL_NODE_SYSTEM_THIRDPARTY_JSONCPP_DIR} ) set(VISUAL_NODE_SYSTEM_THIRDPARTY_DIR ${CMAKE_CURRENT_SOURCE_DIR}/ThirdParty PARENT_SCOPE) @@ -355,4 +362,4 @@ add_custom_target(VisualNodeSystemPreBuild ${CMAKE_CURRENT_SOURCE_DIR}/VersionInfo/VISUAL_NODE_SYSTEM_Version.h ) -add_dependencies(VisualNodeSystem VisualNodeSystemPreBuild) \ No newline at end of file +add_dependencies(VisualNodeSystem VisualNodeSystemPreBuild) diff --git a/StandardNodes/ExecutionFlowNodes/LiteralsNodes/UnsignedLiteralNode.cpp b/StandardNodes/ExecutionFlowNodes/LiteralsNodes/UnsignedLiteralNode.cpp new file mode 100644 index 0000000..e33c3bb --- /dev/null +++ b/StandardNodes/ExecutionFlowNodes/LiteralsNodes/UnsignedLiteralNode.cpp @@ -0,0 +1,116 @@ +#include "UnsignedLiteralNode.h" +#include "imgui.h" +using namespace VisNodeSys; + +UnsignedLiteralNode::UnsignedLiteralNode() : VisNodeSys::Node() +{ + Type = "UnsignedLiteralNode"; + + SetStyle(DEFAULT); + SetName("Unsigned Literal"); + + TitleBackgroundColor = ImColor(30, 221, 170); + TitleBackgroundColorHovered = ImColor(139, 235, 199); + + AddSocket(new NodeSocket(this, "UINT", "Out", NodeSocket::SocketFlow::Output)); + + SetSize(ImVec2(170, NODE_HEIGHT_PER_SOCKET * 2)); + if (!Output.empty()) + { + Output[0]->SetFunctionToOutputData(UIntDataGetter); + Output[0]->SetCanBeDeletedByUser(false); + } +} + +UnsignedLiteralNode::UnsignedLiteralNode(const UnsignedLiteralNode& Other) : VisNodeSys::Node(Other) +{ + SetStyle(DEFAULT); + Data = Other.Data; + + // Here I am restoring the output data function. + // Because the function is not serializable, I have to set it manually. + if (!Output.empty()) + Output[0]->SetFunctionToOutputData(UIntDataGetter); +} + +Json::Value UnsignedLiteralNode::ToJson() +{ + Json::Value Result = Node::ToJson(); + Result["Value"] = Data; + return Result; +} + +bool UnsignedLiteralNode::FromJson(Json::Value Json) +{ + bool bResult = Node::FromJson(Json); + if (!bResult) + return false; + + if (!Json.isMember("Value")) + return false; + + if (!Json["Value"].isUInt()) + return false; + + Data = Json["Value"].asUInt(); + + // Here I am restoring the output data function. + // Because the function is not serializable, I have to set it manually. + if (Output.size() < 1) + return false; + + if (Output[0] == nullptr) + return false; + + Output[0]->SetFunctionToOutputData(UIntDataGetter); + + return true; +} + +void UnsignedLiteralNode::Draw() +{ + Node::Draw(); + + float Zoom = ParentArea->GetZoomFactor(); + + ImGui::SetCursorScreenPos(ImVec2(ImGui::GetCursorScreenPos().x + 30.0f * Zoom, ImGui::GetCursorScreenPos().y + 45.0f * Zoom)); + + float XPosition = ImGui::GetCursorScreenPos().x - 17.0f * Zoom; + float YPosition = ImGui::GetCursorScreenPos().y + 0.0f * Zoom; + + ImGui::SetCursorScreenPos(ImVec2(XPosition, YPosition)); + ImGui::SetNextItemWidth(100 * Zoom); + if (ImGui::InputScalar("##value", ImGuiDataType_U64, &Data)) + { + if (Output.size() > 0) + { + for (size_t i = 0; i < Output[0]->GetConnectedSockets().size(); ++i) + { + ParentArea->TriggerSocketEvent(Output[0], Output[0]->GetConnectedSockets()[i], UPDATE); + } + } + } +} + +void UnsignedLiteralNode::SocketEvent(NodeSocket* OwnSocket, NodeSocket* ConnectedSocket, NODE_SOCKET_EVENT EventType) +{ + Node::SocketEvent(OwnSocket, ConnectedSocket, EventType); +} + +bool UnsignedLiteralNode::CanConnect(NodeSocket* OwnSocket, NodeSocket* CandidateSocket, char** MsgToUser) +{ + if (!Node::CanConnect(OwnSocket, CandidateSocket, nullptr)) + return false; + + return true; +} + +unsigned int UnsignedLiteralNode::GetData() const +{ + return Data; +} + +void UnsignedLiteralNode::SetData(unsigned int NewData) +{ + Data = NewData; +} diff --git a/StandardNodes/ExecutionFlowNodes/LiteralsNodes/UnsignedLiteralNode.h b/StandardNodes/ExecutionFlowNodes/LiteralsNodes/UnsignedLiteralNode.h new file mode 100644 index 0000000..aee9943 --- /dev/null +++ b/StandardNodes/ExecutionFlowNodes/LiteralsNodes/UnsignedLiteralNode.h @@ -0,0 +1,27 @@ +#pragma once + +#include "../../../VisualNodeSystem.h" + +class UnsignedLiteralNode : public VisNodeSys::Node +{ + bool CanConnect(VisNodeSys::NodeSocket* OwnSocket, VisNodeSys::NodeSocket* CandidateSocket, char** MsgToUser); + void SocketEvent(VisNodeSys::NodeSocket* OwnSocket, VisNodeSys::NodeSocket* ConnectedSocket, VisNodeSys::NODE_SOCKET_EVENT EventType); + + uint64_t Data = 0; + + std::function UIntDataGetter = [this]() -> void* { + return &Data; + }; + +public: + UnsignedLiteralNode(); + UnsignedLiteralNode(const UnsignedLiteralNode& Other); + + Json::Value ToJson(); + bool FromJson(Json::Value Json); + + unsigned int GetData() const; + void SetData(unsigned int NewValue); + + void Draw(); +}; diff --git a/StandardNodes/ExecutionFlowNodes/OperatorNodes/Arithmetic/BaseArithmeticOperatorNode.cpp b/StandardNodes/ExecutionFlowNodes/OperatorNodes/Arithmetic/BaseArithmeticOperatorNode.cpp index c60f09f..16ffaff 100644 --- a/StandardNodes/ExecutionFlowNodes/OperatorNodes/Arithmetic/BaseArithmeticOperatorNode.cpp +++ b/StandardNodes/ExecutionFlowNodes/OperatorNodes/Arithmetic/BaseArithmeticOperatorNode.cpp @@ -2,7 +2,10 @@ #include "../../../../SubSystems/VisualNodeArea/VisualNodeArea.h" using namespace VisNodeSys; -BaseArithmeticOperatorNode::BaseArithmeticOperatorNode(std::vector AllowedTypes) : BaseExecutionFlowNode() +BaseArithmeticOperatorNode::BaseArithmeticOperatorNode( + std::vector AllowedTypes +) : BaseExecutionFlowNode(), + DefaultAllowedTypes(AllowedTypes) { Type = "BaseArithmeticOperatorNode"; @@ -113,13 +116,54 @@ std::string BaseArithmeticOperatorNode::GetActiveINDataType() void BaseArithmeticOperatorNode::SocketEvent(NodeSocket* OwnSocket, NodeSocket* ConnectedSocket, NODE_SOCKET_EVENT EventType) { Node::SocketEvent(OwnSocket, ConnectedSocket, EventType); - - if (EventType == EXECUTE) - { - Execute(); - - if (Output.size() > 0 && Output[0]->GetConnectedSockets().size() > 0) - ParentArea->TriggerSocketEvent(Output[0], Output[0]->GetConnectedSockets()[0], EXECUTE); + if (OwnSocket->GetFlowDirection() == VisNodeSys::NodeSocket::SocketFlow::Input) { + switch (EventType) { + case VisNodeSys::CONNECTED: { + // socket is connected + Execute(); + if (Output.size() > 1) { + for (size_t i = 0; i < Output[1]->GetConnectedSockets().size(); ++i) { + ParentArea->TriggerSocketEvent(Output[1], Output[1]->GetConnectedSockets()[i], UPDATE); + } + } + break; + } + case VisNodeSys::DISCONNECTED: { + // socket is disconnected + Execute(); + if (Input[0]->GetConnectedSockets().empty() && Input[1]->GetConnectedSockets().empty()) { + Output[1]->SetAllowedTypes(DefaultAllowedTypes); + } + if (Output.size() > 1) { + for (size_t i = 0; i < Output[1]->GetConnectedSockets().size(); ++i) { + ParentArea->TriggerSocketEvent(Output[1], Output[1]->GetConnectedSockets()[i], UPDATE); + } + } + break; + } + case VisNodeSys::DESTRUCTION: + // when connected node is destroyed (probably should just disconnect...) + break; + case VisNodeSys::UPDATE: + // when connected node is updated + Execute(); + if (Output.size() > 1) { + for (size_t i = 0; i < Output[1]->GetConnectedSockets().size(); ++i) { + ParentArea->TriggerSocketEvent(Output[1], Output[1]->GetConnectedSockets()[i], UPDATE); + } + } + break; + case VisNodeSys::EXECUTE: + // special operation when node operations are executed + // (dont understand why this doesnt just happen on update...) + Execute(); + if (Output.size() > 0) { + for (size_t i = 0; i < Output[0]->GetConnectedSockets().size(); ++i) { + ParentArea->TriggerSocketEvent(Output[0], Output[0]->GetConnectedSockets()[i], EXECUTE); + } + } + break; + } } } @@ -167,4 +211,106 @@ bool BaseArithmeticOperatorNode::CanConnect(NodeSocket* OwnSocket, NodeSocket* C } return true; -} \ No newline at end of file +} + +void BaseArithmeticOperatorNode::Execute() +{ + // Both A and B input sockets are required to perform the operation. + if (Input.size() <= 2) + return; + + std::string CurrentMode = GetActiveINDataType(); + if (CurrentMode.empty()) + return; + + // If we don't have both A and B inputs connected, we can't do anything. + if (Input[1]->GetConnectedSockets().empty() && + Input[2]->GetConnectedSockets().empty()) + return; + + void* AData = nullptr; + if (!Input[1]->GetConnectedSockets().empty()) + AData = Input[1]->GetConnectedSockets()[0]->GetData(); + + void* BData = nullptr; + if (!Input[2]->GetConnectedSockets().empty()) + BData = Input[2]->GetConnectedSockets()[0]->GetData(); + + if (AData == nullptr && BData == nullptr) + return; + + Output[1]->SetAllowedTypes({CurrentMode}); + // Call the appropriate operation method based on the data type. + if (CurrentMode == "INT") + { + int A = 0; + if (AData != nullptr) + A = *reinterpret_cast(AData); + + int B = 0; + if (BData != nullptr) + B = *reinterpret_cast(BData); + + LocalIntegerData = PerformOperation(A, B); + } + if (CurrentMode == "UINT") + { + unsigned int A = 0; + if (AData != nullptr) + A = *reinterpret_cast(AData); + + unsigned int B = 0; + if (BData != nullptr) + B = *reinterpret_cast(BData); + + LocalUnsignedData = PerformOperation(A, B); + } + else if (CurrentMode == "FLOAT") + { + float A = 0.0f; + if (AData != nullptr) + A = *reinterpret_cast(AData); + + float B = 0.0f; + if (BData != nullptr) + B = *reinterpret_cast(BData); + + LocalFloatData = PerformOperation(A, B); + } + else if (CurrentMode == "VEC2") + { + glm::vec2 A = glm::vec2(0.0f); + if (AData != nullptr) + A = *reinterpret_cast(AData); + + glm::vec2 B = glm::vec2(0.0f); + if (BData != nullptr) + B = *reinterpret_cast(BData); + + LocalVec2Data = PerformOperation(A, B); + } + else if (CurrentMode == "VEC3") + { + glm::vec3 A = glm::vec3(0.0f); + if (AData != nullptr) + A = *reinterpret_cast(AData); + + glm::vec3 B = glm::vec3(0.0f); + if (BData != nullptr) + B = *reinterpret_cast(BData); + + LocalVec3Data = PerformOperation(A, B); + } + else if (CurrentMode == "VEC4") + { + glm::vec4 A = glm::vec4(0.0f); + if (AData != nullptr) + A = *reinterpret_cast(AData); + + glm::vec4 B = glm::vec4(0.0f); + if (BData != nullptr) + B = *reinterpret_cast(BData); + + LocalVec4Data = PerformOperation(A, B); + } +} diff --git a/StandardNodes/ExecutionFlowNodes/OperatorNodes/Arithmetic/BaseArithmeticOperatorNode.h b/StandardNodes/ExecutionFlowNodes/OperatorNodes/Arithmetic/BaseArithmeticOperatorNode.h index 9aa404b..728d6b3 100644 --- a/StandardNodes/ExecutionFlowNodes/OperatorNodes/Arithmetic/BaseArithmeticOperatorNode.h +++ b/StandardNodes/ExecutionFlowNodes/OperatorNodes/Arithmetic/BaseArithmeticOperatorNode.h @@ -26,10 +26,12 @@ class BaseArithmeticOperatorNode : public BaseExecutionFlowNode private: // TO-DO: use std::optional to save memory. int LocalIntegerData = 0; + unsigned int LocalUnsignedData = 0; float LocalFloatData = 0.0f; glm::vec2 LocalVec2Data = glm::vec2(0.0f); glm::vec3 LocalVec3Data = glm::vec3(0.0f); glm::vec4 LocalVec4Data = glm::vec4(0.0f); + std::vector DefaultAllowedTypes; // Generic arithmetic function for basic types (int, float) template @@ -59,169 +61,7 @@ class BaseArithmeticOperatorNode : public BaseExecutionFlowNode } } - // Specialization for integer type. - template <> - int PerformOperation(const int& A, const int& B) - { - switch (OperatorType) - { - case ArithmeticOperationType::ADD: - return A + B; - case ArithmeticOperationType::SUBTRACT: - return A - B; - case ArithmeticOperationType::MULTIPLY: - return A * B; - case ArithmeticOperationType::DIVIDE: - { - if (B == 0) - return A; - if (A == INT_MIN && B == -1) - return INT_MAX; // INT_MIN / -1 overflows int; saturate like the POWER case. - return A / B; - } - case ArithmeticOperationType::MODULUS: - { - if (B == 0) - return A; - if (A == INT_MIN && B == -1) - return 0; // INT_MIN % -1 is mathematically 0. - return A % B; - } - case ArithmeticOperationType::POWER: - { - const double Result = std::pow(static_cast(A), static_cast(B)); - - if (!std::isfinite(Result)) - return 0; - - if (Result >= static_cast(INT_MAX)) - return INT_MAX; - - if (Result <= static_cast(INT_MIN)) - return INT_MIN; - - return static_cast(Result); - } - default: - return A; - } - } - - // Specialization for floating-point type - template <> - float PerformOperation(const float& A, const float& B) - { - switch (OperatorType) - { - case ArithmeticOperationType::ADD: - return A + B; - case ArithmeticOperationType::SUBTRACT: - return A - B; - case ArithmeticOperationType::MULTIPLY: - return A * B; - case ArithmeticOperationType::DIVIDE: - if (B == 0.0f) - return A; - return A / B; - case ArithmeticOperationType::POWER: - return std::pow(A, B); - case ArithmeticOperationType::MODULUS: - if (B == 0.0f) - return A; - return std::fmod(A, B); - default: - return A; - } - } - - void Execute() - { - // Both A and B input sockets are required to perform the operation. - if (Input.size() <= 2) - return; - - std::string CurrentMode = GetActiveINDataType(); - if (CurrentMode.empty()) - return; - - // If we don't have both A and B inputs connected, we can't do anything. - if (Input[1]->GetConnectedSockets().empty() && - Input[2]->GetConnectedSockets().empty()) - return; - - void* AData = nullptr; - if (!Input[1]->GetConnectedSockets().empty()) - AData = Input[1]->GetConnectedSockets()[0]->GetData(); - - void* BData = nullptr; - if (!Input[2]->GetConnectedSockets().empty()) - BData = Input[2]->GetConnectedSockets()[0]->GetData(); - - if (AData == nullptr && BData == nullptr) - return; - - // Call the appropriate operation method based on the data type. - if (CurrentMode == "INT") - { - int A = 0; - if (AData != nullptr) - A = *reinterpret_cast(AData); - - int B = 0; - if (BData != nullptr) - B = *reinterpret_cast(BData); - - LocalIntegerData = PerformOperation(A, B); - } - else if (CurrentMode == "FLOAT") - { - float A = 0.0f; - if (AData != nullptr) - A = *reinterpret_cast(AData); - - float B = 0.0f; - if (BData != nullptr) - B = *reinterpret_cast(BData); - - LocalFloatData = PerformOperation(A, B); - } - else if (CurrentMode == "VEC2") - { - glm::vec2 A = glm::vec2(0.0f); - if (AData != nullptr) - A = *reinterpret_cast(AData); - - glm::vec2 B = glm::vec2(0.0f); - if (BData != nullptr) - B = *reinterpret_cast(BData); - - LocalVec2Data = PerformOperation(A, B); - } - else if (CurrentMode == "VEC3") - { - glm::vec3 A = glm::vec3(0.0f); - if (AData != nullptr) - A = *reinterpret_cast(AData); - - glm::vec3 B = glm::vec3(0.0f); - if (BData != nullptr) - B = *reinterpret_cast(BData); - - LocalVec3Data = PerformOperation(A, B); - } - else if (CurrentMode == "VEC4") - { - glm::vec4 A = glm::vec4(0.0f); - if (AData != nullptr) - A = *reinterpret_cast(AData); - - glm::vec4 B = glm::vec4(0.0f); - if (BData != nullptr) - B = *reinterpret_cast(BData); - - LocalVec4Data = PerformOperation(A, B); - } - } + void Execute(); std::function ResultDataGetter = [this]() -> void* { std::string CurrentMode = GetActiveINDataType(); @@ -233,6 +73,10 @@ class BaseArithmeticOperatorNode : public BaseExecutionFlowNode { return &LocalIntegerData; } + if (CurrentMode == "UINT") + { + return &LocalUnsignedData; + } else if (CurrentMode == "FLOAT") { return &LocalFloatData; @@ -254,7 +98,7 @@ class BaseArithmeticOperatorNode : public BaseExecutionFlowNode }; public: - BaseArithmeticOperatorNode(std::vector AllowedTypes = { "INT", "FLOAT", "VEC2", "VEC3", "VEC4" }); + BaseArithmeticOperatorNode(std::vector AllowedTypes = { "INT", "UINT", "FLOAT", "VEC2", "VEC3", "VEC4" }); BaseArithmeticOperatorNode(const BaseArithmeticOperatorNode& Other); Json::Value ToJson(); @@ -263,4 +107,122 @@ class BaseArithmeticOperatorNode : public BaseExecutionFlowNode void Draw(); std::string GetActiveINDataType(); -}; \ No newline at end of file +}; + +// Specialization for integer type. +template <> +inline int BaseArithmeticOperatorNode::PerformOperation(const int& A, const int& B) +{ + switch (OperatorType) + { + case ArithmeticOperationType::ADD: + return A + B; + case ArithmeticOperationType::SUBTRACT: + return A - B; + case ArithmeticOperationType::MULTIPLY: + return A * B; + case ArithmeticOperationType::DIVIDE: + { + if (B == 0) + return A; + if (A == INT_MIN && B == -1) + return INT_MAX; // INT_MIN / -1 overflows int; saturate like the POWER case. + return A / B; + } + case ArithmeticOperationType::MODULUS: + { + if (B == 0) + return A; + if (A == INT_MIN && B == -1) + return 0; // INT_MIN % -1 is mathematically 0. + return A % B; + } + case ArithmeticOperationType::POWER: + { + const double Result = std::pow(static_cast(A), static_cast(B)); + + if (!std::isfinite(Result)) + return 0; + + if (Result >= static_cast(INT_MAX)) + return INT_MAX; + + if (Result <= static_cast(INT_MIN)) + return INT_MIN; + + return static_cast(Result); + } + default: + return A; + } +} +// Specialization for unsigned type. +template <> +inline unsigned int BaseArithmeticOperatorNode::PerformOperation(const unsigned int& A, const unsigned int& B) +{ + switch (OperatorType) + { + case ArithmeticOperationType::ADD: + return A + B; + case ArithmeticOperationType::SUBTRACT: + return A - B; + case ArithmeticOperationType::MULTIPLY: + return A * B; + case ArithmeticOperationType::DIVIDE: + { + if (B == 0) + return A; + return A / B; + } + case ArithmeticOperationType::MODULUS: + { + if (B == 0) + return A; + return A % B; + } + case ArithmeticOperationType::POWER: + { + const double Result = std::pow(static_cast(A), static_cast(B)); + + if (!std::isfinite(Result)) + return 0; + + if (Result >= static_cast(INT_MAX)) + return INT_MAX; + + if (Result <= static_cast(INT_MIN)) + return INT_MIN; + + return static_cast(Result); + } + default: + return A; + } +} + +// Specialization for floating-point type +template <> +inline float BaseArithmeticOperatorNode::PerformOperation(const float& A, const float& B) +{ + switch (OperatorType) + { + case ArithmeticOperationType::ADD: + return A + B; + case ArithmeticOperationType::SUBTRACT: + return A - B; + case ArithmeticOperationType::MULTIPLY: + return A * B; + case ArithmeticOperationType::DIVIDE: + if (B == 0.0f) + return A; + return A / B; + case ArithmeticOperationType::POWER: + return std::pow(A, B); + case ArithmeticOperationType::MODULUS: + if (B == 0.0f) + return A; + return std::fmod(A, B); + default: + return A; + } +} diff --git a/StandardNodes/ExecutionFlowNodes/OperatorNodes/Comparison/BaseComparisonOperatorNode.h b/StandardNodes/ExecutionFlowNodes/OperatorNodes/Comparison/BaseComparisonOperatorNode.h index 5118c54..8061a50 100644 --- a/StandardNodes/ExecutionFlowNodes/OperatorNodes/Comparison/BaseComparisonOperatorNode.h +++ b/StandardNodes/ExecutionFlowNodes/OperatorNodes/Comparison/BaseComparisonOperatorNode.h @@ -111,6 +111,13 @@ class BaseComparisonOperatorNode : public BaseExecutionFlowNode bLocalBool = CompareValues(A, B); } + else if (CurrentMode == "UINT") + { + int A = *reinterpret_cast(AData); + int B = *reinterpret_cast(BData); + + bLocalBool = CompareValues(A, B); + } else if (CurrentMode == "FLOAT") { float A = *reinterpret_cast(AData); @@ -154,6 +161,10 @@ class BaseComparisonOperatorNode : public BaseExecutionFlowNode { return &bLocalBool; } + else if (CurrentMode == "UINT") + { + return &bLocalBool; + } else if (CurrentMode == "FLOAT") { return &bLocalBool; @@ -186,4 +197,4 @@ class BaseComparisonOperatorNode : public BaseExecutionFlowNode std::string GetActiveINDataType(); std::string GetActiveOUTDataType(); -}; \ No newline at end of file +}; diff --git a/StandardNodes/ExecutionFlowNodes/VariablesNodes/UnsignedVariableNode.cpp b/StandardNodes/ExecutionFlowNodes/VariablesNodes/UnsignedVariableNode.cpp new file mode 100644 index 0000000..bf9f433 --- /dev/null +++ b/StandardNodes/ExecutionFlowNodes/VariablesNodes/UnsignedVariableNode.cpp @@ -0,0 +1,147 @@ +#include "UnsignedVariableNode.h" +#include "../OperatorNodes/Arithmetic/BaseArithmeticOperatorNode.h" +#include "../../../SubSystems/VisualNodeArea/VisualNodeArea.h" +using namespace VisNodeSys; + +UnsignedVariableNode::UnsignedVariableNode() : BaseExecutionFlowNode() +{ + Type = "UnsignedVariableNode"; + + SetStyle(DEFAULT); + SetName("Unsigned Variable"); + + TitleBackgroundColor = ImColor(30, 221, 170); + TitleBackgroundColorHovered = ImColor(139, 235, 199); + + AddSocket(new NodeSocket(this, "UINT", "Set", NodeSocket::SocketFlow::Input)); + + AddSocket(new NodeSocket(this, "EXECUTE", "", NodeSocket::SocketFlow::Output)); + AddSocket(new NodeSocket(this, "UINT", "Get", NodeSocket::SocketFlow::Output)); + + SetSize(ImVec2(220.0f, static_cast(NODE_HEIGHT_PER_SOCKET * std::max(Input.size(), Output.size())))); + if (Output.size() > 1) + Output[1]->SetFunctionToOutputData(UnsignedDataGetter); + + // All sockets are structural and must not be user deletable. + for (NodeSocket* Socket : Input) + Socket->SetCanBeDeletedByUser(false); + for (NodeSocket* Socket : Output) + Socket->SetCanBeDeletedByUser(false); +} + +UnsignedVariableNode::UnsignedVariableNode(const UnsignedVariableNode& Other) : BaseExecutionFlowNode(Other) +{ + SetStyle(DEFAULT); + Data = Other.Data; + + // Here I am restoring the output data function. + // Because the function is not serializable, I have to set it manually. + if (Output.size() > 1) + Output[1]->SetFunctionToOutputData(UnsignedDataGetter); +} + +Json::Value UnsignedVariableNode::ToJson() +{ + Json::Value Result = Node::ToJson(); + Result["Value"] = Data; + return Result; +} + +bool UnsignedVariableNode::FromJson(Json::Value Json) +{ + bool bResult = Node::FromJson(Json); + if (!bResult) + return false; + + if (!Json.isMember("Value")) + return false; + + if (!Json["Value"].isInt()) + return false; + + Data = Json["Value"].asInt(); + + // Here I am restoring the output data function. + // Because the function is not serializable, I have to set it manually. + if (Output.size() < 2) + return false; + + if (Output[1] == nullptr) + return false; + + Output[1]->SetFunctionToOutputData(UnsignedDataGetter); + + return true; +} + +void UnsignedVariableNode::Draw() +{ + Node::Draw(); + + float Zoom = ParentArea->GetZoomFactor(); + float XPosition = ImGui::GetCursorScreenPos().x + 55.0f * Zoom; + float YPosition = ImGui::GetCursorScreenPos().y + 45.0f * Zoom; + + ImGui::SetCursorScreenPos(ImVec2(XPosition, YPosition)); + ImGui::SetNextItemWidth(100 * Zoom); + if (ImGui::InputScalar("##value", ImGuiDataType_U64, &Data)) + { + if (Output.size() > 0 && Output[0]->GetConnectedSockets().size() > 0) + ParentArea->TriggerSocketEvent(Output[0], Output[0]->GetConnectedSockets()[0], UPDATE); + } +} + +void UnsignedVariableNode::SocketEvent(NodeSocket* OwnSocket, NodeSocket* ConnectedSocket, NODE_SOCKET_EVENT EventType) +{ + Node::SocketEvent(OwnSocket, ConnectedSocket, EventType); + + if (EventType == EXECUTE) + { + if (Input.size() > 1 && Input[1]->GetConnectedSockets().size() > 0) + { + void* TemporaryData = Input[1]->GetConnectedSockets()[0]->GetData(); + if (TemporaryData != nullptr) + Data = *reinterpret_cast(TemporaryData); + } + + if (Output.size() > 0 && Output[0]->GetConnectedSockets().size() > 0) + ParentArea->TriggerSocketEvent(Output[0], Output[0]->GetConnectedSockets()[0], EXECUTE); + } +} + +bool UnsignedVariableNode::CanConnect(NodeSocket* OwnSocket, NodeSocket* CandidateSocket, char** MsgToUser) +{ + if (!Node::CanConnect(OwnSocket, CandidateSocket, nullptr)) + return false; + + std::vector CandidateAllowedTypes = CandidateSocket->GetAllowedTypes(); + if (CandidateAllowedTypes.size() == 1) + return true; + + // Check if the candidate socket node is a child of BaseArithmeticOperatorNode. + if (CandidateSocket->GetParent() == nullptr) + return false; + + BaseArithmeticOperatorNode* CandidateNode = dynamic_cast(CandidateSocket->GetParent()); + if (CandidateNode == nullptr) + return false; + + std::string CandidateNodeActiveDataType = CandidateNode->GetActiveINDataType(); + if (CandidateNodeActiveDataType != OwnSocket->GetAllowedTypes()[0]) + return false; + + return true; +} + +unsigned int UnsignedVariableNode::GetData() const +{ + return Data; +} + +void UnsignedVariableNode::SetData(unsigned int NewData) +{ + Data = NewData; + + if (Output.size() > 0 && Output[0]->GetConnectedSockets().size() > 0) + ParentArea->TriggerSocketEvent(Output[0], Output[0]->GetConnectedSockets()[0], UPDATE); +} diff --git a/StandardNodes/ExecutionFlowNodes/VariablesNodes/UnsignedVariableNode.h b/StandardNodes/ExecutionFlowNodes/VariablesNodes/UnsignedVariableNode.h new file mode 100644 index 0000000..886bf9d --- /dev/null +++ b/StandardNodes/ExecutionFlowNodes/VariablesNodes/UnsignedVariableNode.h @@ -0,0 +1,27 @@ +#pragma once + +#include "../BaseExecutionFlowNode.h" + +class UnsignedVariableNode : public BaseExecutionFlowNode +{ + bool CanConnect(VisNodeSys::NodeSocket* OwnSocket, VisNodeSys::NodeSocket* CandidateSocket, char** MsgToUser); + void SocketEvent(VisNodeSys::NodeSocket* OwnSocket, VisNodeSys::NodeSocket* ConnectedSocket, VisNodeSys::NODE_SOCKET_EVENT EventType); + + unsigned int Data = 0; + + std::function UnsignedDataGetter = [this]() -> void* { + return &Data; + }; + +public: + UnsignedVariableNode(); + UnsignedVariableNode(const UnsignedVariableNode& Other); + + Json::Value ToJson(); + bool FromJson(Json::Value Json); + + unsigned int GetData() const; + void SetData(unsigned int NewValue); + + void Draw(); +}; diff --git a/SubSystems/VisualNodeArea/VisualNodeArea.cpp b/SubSystems/VisualNodeArea/VisualNodeArea.cpp index 46b20a7..e33baea 100644 --- a/SubSystems/VisualNodeArea/VisualNodeArea.cpp +++ b/SubSystems/VisualNodeArea/VisualNodeArea.cpp @@ -642,6 +642,9 @@ bool NodeArea::LoadFromFile(std::string FilePath) const std::string FileData((std::istreambuf_iterator(NodesFile)), std::istreambuf_iterator()); NodesFile.close(); + SetZoomFactor(1); + SetPosition({0,0}); + return LoadFromJson(FileData); } @@ -656,6 +659,13 @@ Node* NodeArea::GetNodeByID(std::string NodeID) const return nullptr; } +Node* NodeArea::GetNodeByIdx(size_t Idx) const +{ + if (Idx >= Nodes.size()) + return nullptr; + return Nodes[Idx]; +} + std::vector NodeArea::GetNodesByName(const std::string NodeName) const { std::vector Result; @@ -933,4 +943,4 @@ void NodeArea::SetSaveExecutedNodes(bool NewValue) { Settings.bSaveExecutedNodes = NewValue; } -#endif \ No newline at end of file +#endif diff --git a/SubSystems/VisualNodeArea/VisualNodeArea.h b/SubSystems/VisualNodeArea/VisualNodeArea.h index d576da2..10e908c 100644 --- a/SubSystems/VisualNodeArea/VisualNodeArea.h +++ b/SubSystems/VisualNodeArea/VisualNodeArea.h @@ -11,6 +11,7 @@ #include "../StandardNodes/ExecutionFlowNodes/LiteralsNodes/BoolLiteralNode.h" #include "../StandardNodes/ExecutionFlowNodes/LiteralsNodes/FloatLiteralNode.h" #include "../StandardNodes/ExecutionFlowNodes/LiteralsNodes/IntegerLiteralNode.h" +#include "../StandardNodes/ExecutionFlowNodes/LiteralsNodes/UnsignedLiteralNode.h" #include "../StandardNodes/ExecutionFlowNodes/LiteralsNodes/Vec2LiteralNode.h" #include "../StandardNodes/ExecutionFlowNodes/LiteralsNodes/BoolVec2LiteralNode.h" #include "../StandardNodes/ExecutionFlowNodes/LiteralsNodes/Vec3LiteralNode.h" @@ -22,6 +23,7 @@ #include "../StandardNodes/ExecutionFlowNodes/VariablesNodes/BoolVariableNode.h" #include "../StandardNodes/ExecutionFlowNodes/VariablesNodes/FloatVariableNode.h" #include "../StandardNodes/ExecutionFlowNodes/VariablesNodes/IntegerVariableNode.h" +#include "../StandardNodes/ExecutionFlowNodes/VariablesNodes/UnsignedVariableNode.h" #include "../StandardNodes/ExecutionFlowNodes/VariablesNodes/Vec2VariableNode.h" #include "../StandardNodes/ExecutionFlowNodes/VariablesNodes/BoolVec2VariableNode.h" #include "../StandardNodes/ExecutionFlowNodes/VariablesNodes/Vec3VariableNode.h" @@ -231,6 +233,7 @@ namespace VisNodeSys // *********************** Nodes ************************ Node* GetNodeByID(std::string NodeID) const; + Node* GetNodeByIdx(size_t Idx) const; std::vector GetNodesByName(std::string NodeName) const; std::vector GetNodesByStringType(std::string NodeType) const; template @@ -504,4 +507,4 @@ namespace VisNodeSys bool WorkOnLoadedConnection(Json::Value& Root, const Json::Value& ConnectionData, std::unordered_map& LoadedNodes); }; #include "VisualNodeArea.inl" -} \ No newline at end of file +} diff --git a/SubSystems/VisualNodeArea/VisualNodeAreaLogic.cpp b/SubSystems/VisualNodeArea/VisualNodeAreaLogic.cpp index 8d64b8b..26b9b6c 100644 --- a/SubSystems/VisualNodeArea/VisualNodeAreaLogic.cpp +++ b/SubSystems/VisualNodeArea/VisualNodeAreaLogic.cpp @@ -190,13 +190,14 @@ bool NodeArea::Delete(Connection* Connection) return true; } + bool wasErased = false; for (int i = 0; i < static_cast(Connection->In->ConnectedSockets.size()); i++) { if (Connection->In->ConnectedSockets[i] == Connection->Out) { Connection->In->ConnectedSockets.erase(Connection->In->ConnectedSockets.begin() + i, Connection->In->ConnectedSockets.begin() + i + 1); // FE_TO_DO : Add some variation of disconnected event, like DISCONNECTED_INCOMING - Connection->In->Parent->SocketEvent(Connection->In, Connection->Out, bClearing ? DESTRUCTION : DISCONNECTED); + wasErased = true; i--; } } @@ -207,10 +208,14 @@ bool NodeArea::Delete(Connection* Connection) { Connection->Out->ConnectedSockets.erase(Connection->Out->ConnectedSockets.begin() + i, Connection->Out->ConnectedSockets.begin() + i + 1); // FE_TO_DO : Add some variation of disconnected event, like DISCONNECTED_OUTGOING - Connection->Out->Parent->SocketEvent(Connection->Out, Connection->In, bClearing ? DESTRUCTION : DISCONNECTED); + wasErased = true; i--; } } + if (wasErased) + { + Connection->Out->Parent->SocketEvent(Connection->Out, Connection->In, bClearing ? DESTRUCTION : DISCONNECTED); + } for (size_t i = 0; i < Connections.size(); i++) { @@ -1385,4 +1390,4 @@ std::vector NodeArea::GetRecursiveChildren() const } return Result; -} \ No newline at end of file +} diff --git a/SubSystems/VisualNodeArea/VisualNodeAreaRendering.cpp b/SubSystems/VisualNodeArea/VisualNodeAreaRendering.cpp index b48735e..dac8764 100644 --- a/SubSystems/VisualNodeArea/VisualNodeAreaRendering.cpp +++ b/SubSystems/VisualNodeArea/VisualNodeAreaRendering.cpp @@ -1226,6 +1226,11 @@ void NodeArea::RenderDefaultMainContextMenu() NewNode = new IntegerLiteralNode(); } + if (ImGui::MenuItem("UInt")) + { + NewNode = new UnsignedLiteralNode(); + } + if (ImGui::MenuItem("Float")) { NewNode = new FloatLiteralNode(); @@ -1276,6 +1281,11 @@ void NodeArea::RenderDefaultMainContextMenu() NewNode = new IntegerVariableNode(); } + if (ImGui::MenuItem("UInt")) + { + NewNode = new UnsignedVariableNode(); + } + if (ImGui::MenuItem("Float")) { NewNode = new FloatVariableNode(); @@ -1429,4 +1439,4 @@ void NodeArea::RenderDefaultMainContextMenu() } #endif } -} \ No newline at end of file +} diff --git a/VisualNode.cpp b/VisualNode.cpp index d1b1adc..8d8e895 100644 --- a/VisualNode.cpp +++ b/VisualNode.cpp @@ -1,6 +1,7 @@ #include "VisualNode.h" #include "VisualNodeFactory.h" #include "VisualNodeSystem.h" +#include "json/value.h" using namespace VisNodeSys; Node::Node(const std::string ID) @@ -713,4 +714,4 @@ float Node::GetTitleBarAvailableWidth() const void Node::SetTitleBarAvailableWidth(float NewValue) { TitleBarAvailableWidth = NewValue; -} \ No newline at end of file +} diff --git a/VisualNodeCore.cpp b/VisualNodeCore.cpp index f2591dc..9d55eeb 100644 --- a/VisualNodeCore.cpp +++ b/VisualNodeCore.cpp @@ -3,6 +3,14 @@ #include using namespace VisNodeSys; +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include "windows.h" +#elif __linux__ +#include +#include +#endif + #ifdef VISUAL_NODE_SYSTEM_SHARED extern "C" __declspec(dllexport) void* GetNodeCore() { @@ -457,4 +465,4 @@ void NodeCore::ShowToolTip(std::string Text) const ImGui::TextUnformatted(Text.c_str()); ImGui::EndTooltip(); } -} \ No newline at end of file +} diff --git a/VisualNodeCore.h b/VisualNodeCore.h index feec0d9..75d35a1 100644 --- a/VisualNodeCore.h +++ b/VisualNodeCore.h @@ -1,14 +1,8 @@ #pragma once -#ifdef _WIN32 -#define WIN32_LEAN_AND_MEAN -#include "windows.h" -#elif __linux__ -#include -#include -#endif - +#ifndef IMGUI_DEFINE_MATH_OPERATORS #define IMGUI_DEFINE_MATH_OPERATORS +#endif #include "imgui.h" #include "imgui_internal.h" #include "misc/cpp/imgui_stdlib.h" @@ -170,4 +164,4 @@ namespace VisNodeSys #else #define NODE_CORE VisNodeSys::NodeCore::GetInstance() #endif -} \ No newline at end of file +} diff --git a/VisualNodeSystem.cpp b/VisualNodeSystem.cpp index 7816b92..1f81998 100644 --- a/VisualNodeSystem.cpp +++ b/VisualNodeSystem.cpp @@ -44,6 +44,17 @@ extern "C" __declspec(dllexport) void* GetNodeSystem() } ); + NODE_FACTORY.RegisterNodeType("UnsignedLiteralNode", + []() -> Node* { + return new UnsignedLiteralNode(); + }, + + [](const Node& CurrentNode) -> Node* { + const UnsignedLiteralNode& NodeToCopy = static_cast(CurrentNode); + return new UnsignedLiteralNode(NodeToCopy); + } + ); + NODE_FACTORY.RegisterNodeType("FloatLiteralNode", []() -> Node* { return new FloatLiteralNode(); @@ -138,6 +149,17 @@ extern "C" __declspec(dllexport) void* GetNodeSystem() } ); + NODE_FACTORY.RegisterNodeType("UnsignedVariableNode", + []() -> Node* { + return new UnsignedVariableNode(); + }, + + [](const Node& CurrentNode) -> Node* { + const UnsignedVariableNode& NodeToCopy = static_cast(CurrentNode); + return new UnsignedVariableNode(NodeToCopy); + } + ); + NODE_FACTORY.RegisterNodeType("FloatVariableNode", []() -> Node* { return new FloatVariableNode(); @@ -486,6 +508,7 @@ void NodeSystem::Initialize(bool bTestMode) AssociateSocketTypeToColor("EXECUTE", ImColor(255, 255, 255)); AssociateSocketTypeToColor("BOOL", ImColor(148, 0, 0)); AssociateSocketTypeToColor("INT", ImColor(30, 221, 170)); + AssociateSocketTypeToColor("UINT", ImColor(62, 221, 125)); AssociateSocketTypeToColor("FLOAT", ImColor(56, 210, 0)); AssociateSocketTypeToColor("VEC2", ImColor(244, 193, 34)); AssociateSocketTypeToColor("BVEC2", ImColor(125, 62, 11)); @@ -1211,7 +1234,7 @@ bool NodeSystem::UnlinkNodeAreas(const std::string& FirstAreaID, const std::stri continue; NodeAreaLinkRecord Record = LinkRecordIterator->second; - std::string RecordID = LinkRecordIterator->first; + std::string RecordIDLocal = LinkRecordIterator->first; NodeArea* InArea = GetNodeAreaByID(Record.InAreaID); if (InArea != nullptr) { @@ -1220,7 +1243,7 @@ bool NodeSystem::UnlinkNodeAreas(const std::string& FirstAreaID, const std::stri InArea->Delete(InNode); } - DeleteLinkRecord(RecordID); + DeleteLinkRecord(RecordIDLocal); } return !RecordIDsToRemove.empty(); @@ -2542,4 +2565,4 @@ std::vector NodeSystem::DeduplicateIDList(const std::vector SeenIDs(ListOfIDs.begin(), ListOfIDs.end()); return std::vector(SeenIDs.begin(), SeenIDs.end()); -} \ No newline at end of file +}