From a5f1ecde1bbb0263716e7f3d9d36252276a27c4e Mon Sep 17 00:00:00 2001 From: Saif Kandil <74428638+k0T0z@users.noreply.github.com> Date: Sun, 11 Aug 2024 14:53:33 +0300 Subject: [PATCH 01/56] fix grpc cpp plugin location Signed-off-by: Saif Kandil <74428638+k0T0z@users.noreply.github.com> --- CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3be56113f..858c68340 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -42,6 +42,8 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_AUTOUIC_SEARCH_PATHS "${CMAKE_CURRENT_SOURCE_DIR}/Dialogs") +set(GRPC_EXE "/usr/local/bin/grpc_cpp_plugin") + # Include ENIGMA things set(ENIGMA_DIR ${CMAKE_CURRENT_SOURCE_DIR}/Submodules/enigma-dev) include_directories("${CMAKE_BINARY_DIR}/Submodules/enigma-dev/shared/protos/" "${ENIGMA_DIR}/CommandLine/libEGM" "${ENIGMA_DIR}/shared") From 4562153a7a9fdedcb793eee0024ff6b8f6681d83 Mon Sep 17 00:00:00 2001 From: Saif Kandil <74428638+k0T0z@users.noreply.github.com> Date: Sun, 25 Aug 2024 18:13:14 +0300 Subject: [PATCH 02/56] merge fix-building-issues branch that has all batches that brings RGM to the light again Signed-off-by: Saif Kandil <74428638+k0T0z@users.noreply.github.com> --- .gitignore | 1 + .gitmodules | 3 + CMakeLists.txt | 96 ++++++++++---- ...ferences.cpp => KeyBindingPreferences.cpp} | 2 +- ...gpreferences.h => KeyBindingPreferences.h} | 0 Dialogs/PreferencesDialog.cpp | 2 +- Editors/RoomEditor.cpp | 71 +++++----- Editors/RoomEditor.h | 6 + Editors/VisualShaderEditor.cpp | 26 ++++ Editors/VisualShaderEditor.h | 33 +++++ MainWindow.cpp | 48 ++++--- MainWindow.h | 3 + Plugins/ServerPlugin.cpp | 122 +++++++++++++++--- Plugins/ServerPlugin.h | 8 ++ Submodules/nodeeditor | 1 + 15 files changed, 323 insertions(+), 99 deletions(-) rename Dialogs/{keybindingpreferences.cpp => KeyBindingPreferences.cpp} (94%) rename Dialogs/{keybindingpreferences.h => KeyBindingPreferences.h} (100%) create mode 100644 Editors/VisualShaderEditor.cpp create mode 100644 Editors/VisualShaderEditor.h create mode 160000 Submodules/nodeeditor diff --git a/.gitignore b/.gitignore index 25ba3af2e..256c24fc6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ /build/ /Submodules/ +/.vscode/ # C++ objects and libs *.slo diff --git a/.gitmodules b/.gitmodules index e1229a7ac..9615bb04f 100644 --- a/.gitmodules +++ b/.gitmodules @@ -2,3 +2,6 @@ path = Submodules/enigma-dev url = ../enigma-dev.git branch = master +[submodule "Submodules/nodeeditor"] + path = Submodules/nodeeditor + url = https://github.com/k0T0z/nodeeditor.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 858c68340..19a0b4413 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,9 @@ cmake_minimum_required(VERSION 3.14) project(RadialGM) +# Uncomment to give priority to the local CMake modules +# set(CMAKE_PREFIX_PATH "/usr/local/lib") + include(CMakeDependentOption) option(RGM_BUILD_EMAKE "Build Emake and the compiler." ON) @@ -9,17 +12,34 @@ option(RGM_BUILD_EMAKE "Build Emake and the compiler." ON) # since we currently don't, I'm force disabling the option on MSVC cmake_dependent_option(RGM_BUILD_STATIC "Build static libs." ON "MSVC" OFF) +# Check https://stackoverflow.com/q/33062728/14629018 for more information. +# if(MSVC) +# set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS TRUE) +# endif() + if (RGM_BUILD_STATIC) - set(LIB_TYPE STATIC) + set(LIB_TYPE STATIC CACHE STRING "Static Library type") else() - set(LIB_TYPE SHARED) + set(LIB_TYPE SHARED CACHE STRING "Shared Library type") +endif() + +# Set default build type +if(NOT CMAKE_BUILD_TYPE) + message(STATUS "Build type not set - defaulting to Debug") + set( + CMAKE_BUILD_TYPE "Debug" + CACHE + STRING + "Choose the type of build from: Debug Release RelWithDebInfo MinSizeRel." + FORCE) endif() if (CMAKE_BUILD_TYPE MATCHES "Debug") - set(EXE "RadialGM-Debug") + set(EXE "RadialGM-Debug" CACHE STRING "RGM Executable name") add_definitions(-DRGM_DEBUG) + set(CMAKE_DEBUG_POSTFIX d) else() - set(EXE "RadialGM") + set(EXE "RadialGM" CACHE STRING "RGM Executable name") endif() set(EXE_DESCRIPTION "ENIGMA IDE") @@ -40,12 +60,13 @@ set(CMAKE_AUTORCC ON) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) -set(CMAKE_AUTOUIC_SEARCH_PATHS "${CMAKE_CURRENT_SOURCE_DIR}/Dialogs") +set(CMAKE_AUTOUIC_SEARCH_PATHS "${CMAKE_CURRENT_SOURCE_DIR}/" "${CMAKE_CURRENT_SOURCE_DIR}/Dialogs" "${CMAKE_CURRENT_SOURCE_DIR}/Editors") -set(GRPC_EXE "/usr/local/bin/grpc_cpp_plugin") +# Uncomment to be able to use local grpc_cpp_plugin +# set(GRPC_EXE "/usr/local/bin/grpc_cpp_plugin") # Include ENIGMA things -set(ENIGMA_DIR ${CMAKE_CURRENT_SOURCE_DIR}/Submodules/enigma-dev) +set(ENIGMA_DIR ${CMAKE_CURRENT_SOURCE_DIR}/Submodules/enigma-dev CACHE PATH "ENIGMA directory") include_directories("${CMAKE_BINARY_DIR}/Submodules/enigma-dev/shared/protos/" "${ENIGMA_DIR}/CommandLine/libEGM" "${ENIGMA_DIR}/shared") # Populate a CMake variable with the sources @@ -83,12 +104,13 @@ set(RGM_SOURCES Editors/ScriptEditor.cpp Editors/CodeEditor.cpp Editors/TimelineEditor.cpp + Editors/VisualShaderEditor.cpp main.cpp Plugins/RGMPlugin.cpp - Plugins/ServerPlugin.cpp Dialogs/EventArgumentsDialog.cpp Dialogs/TimelineChangeMoment.cpp Dialogs/PreferencesDialog.cpp + Dialogs/KeyBindingPreferences.cpp Utils/ProtoManip.cpp Utils/FieldPath.cpp MainWindow.cpp @@ -141,13 +163,14 @@ set(RGM_HEADERS Editors/FontEditor.h Editors/SpriteEditor.h Editors/BackgroundEditor.h - Plugins/ServerPlugin.h + Editors/VisualShaderEditor.h Plugins/RGMPlugin.h MainWindow.h Dialogs/EventArgumentsDialog.h Dialogs/PreferencesDialog.h Dialogs/PreferencesKeys.h Dialogs/TimelineChangeMoment.h + Dialogs/KeyBindingPreferences.h Utils/SafeCasts.h Utils/ProtoManip.h Utils/FieldPath.h @@ -198,8 +221,8 @@ else() set(EDITOR_SOURCES Widgets/CodeWidgetScintilla.cpp) endif() - set(RGM_SOURCES ${RGM_SOURCES} Plugins/ServerPlugin.cpp) - set(RGM_HEADERS ${RGM_HEADERS} Plugins/ServerPlugin.h) +set(RGM_SOURCES ${RGM_SOURCES} Plugins/ServerPlugin.cpp) +set(RGM_HEADERS ${RGM_HEADERS} Plugins/ServerPlugin.h) # Tell CMake to create the RadialGM executable add_executable(${EXE} WIN32 ${RGM_UI} ${RGM_HEADERS} ${RGM_SOURCES} ${EDITOR_SOURCES} ${RGM_RC}) @@ -246,14 +269,13 @@ include_directories(${EXE} PRIVATE ${RAPIDJSON_INCLUDE_DIRS}) find_package(yaml-cpp CONFIG REQUIRED) target_link_libraries(${EXE} PRIVATE yaml-cpp) - #Find gRPC - find_package(gRPC CONFIG REQUIRED) - target_link_libraries(${EXE} PRIVATE gRPC::gpr gRPC::grpc gRPC::grpc++) +# Find gRPC +find_package(gRPC CONFIG REQUIRED) +target_link_libraries(${EXE} PRIVATE gRPC::gpr gRPC::grpc gRPC::grpc++) # Find Protobuf -include(FindProtobuf) -include_directories(${Protobuf_INCLUDE_DIRS}) -target_link_libraries(${EXE} PRIVATE ${Protobuf_LIBRARIES}) +find_package(Protobuf CONFIG REQUIRED) +target_link_libraries(${EXE} PRIVATE protobuf::libprotobuf) # Find OpenSSL find_package(OpenSSL REQUIRED) @@ -264,11 +286,13 @@ find_package(Qt5 COMPONENTS Core Widgets Gui PrintSupport Multimedia REQUIRED) target_link_libraries(${EXE} PRIVATE Qt5::Core Qt5::Widgets Qt5::Gui Qt5::PrintSupport Qt5::Multimedia) # LibProto -add_subdirectory(Submodules/enigma-dev/shared) +# Arrangement of these is important: shared depends on proto and emake depends on all of them +# We need to cache the library names first so we can build on top of them add_subdirectory(Submodules/enigma-dev/shared/protos) +add_subdirectory(Submodules/enigma-dev/shared) add_subdirectory(Submodules/enigma-dev/CommandLine/libEGM) -add_dependencies(${EXE} "EGM") -target_link_libraries(${EXE} PRIVATE "EGM" "Protocols" "ENIGMAShared") +add_dependencies(${EXE} ${LIB_EGM}) +target_link_libraries(${EXE} PRIVATE ${LIB_EGM} ${LIB_PROTO} ${SHARED_LIB}) # Find FreeType find_package(Freetype REQUIRED) @@ -291,6 +315,14 @@ target_link_libraries(${EXE} PRIVATE ${LIB_PCRE2}) find_library(LIB_DOUBLE_CONVERSION NAMES double-conversion) target_link_libraries(${EXE} PRIVATE ${LIB_DOUBLE_CONVERSION}) +# nodeeditor +# FIXME: In order for BUILD_DEBUG_POSTFIX_D and USE_QT6 to be set correctly, you will need to configure the project twice: https://cmake.org/cmake/help/latest/policy/CMP0077.html +if (CMAKE_BUILD_TYPE MATCHES "Debug") + set(BUILD_DEBUG_POSTFIX_D ON) +endif() +set(USE_QT6 OFF) # We use Qt5 +add_subdirectory(Submodules/nodeeditor) + if(WIN32) # Windows is a turd target_link_libraries(${EXE} PRIVATE Ws2_32 Wtsapi32 Wldap32 Crypt32 Winmm Userenv Netapi32 version Dwmapi Imm32) @@ -304,14 +336,22 @@ endif() if (RGM_BUILD_EMAKE) add_subdirectory(Submodules/enigma-dev/CompilerSource) add_subdirectory(Submodules/enigma-dev/CommandLine/emake) - if (CMAKE_BUILD_TYPE MATCHES "Debug") - set(CLI_TARGET "emake-debug") - else() - set(CLI_TARGET "emake") - endif() add_dependencies(${EXE} ${CLI_TARGET}) endif() +add_custom_command( + TARGET ${EXE} + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy + ${CMAKE_BINARY_DIR}/Submodules/enigma-dev/CommandLine/emake/${CLI_TARGET}${CMAKE_EXECUTABLE_SUFFIX} + ${CMAKE_BINARY_DIR}/Submodules/enigma-dev/CommandLine/libEGM/${CMAKE_SHARED_LIBRARY_PREFIX}${LIB_EGM}${CMAKE_DEBUG_POSTFIX}${CMAKE_SHARED_LIBRARY_SUFFIX} + ${CMAKE_BINARY_DIR}/Submodules/enigma-dev/CompilerSource/${CMAKE_SHARED_LIBRARY_PREFIX}${COMPILER_LIB}${CMAKE_DEBUG_POSTFIX}${CMAKE_SHARED_LIBRARY_SUFFIX} + ${CMAKE_BINARY_DIR}/Submodules/enigma-dev/shared/protos/${CMAKE_SHARED_LIBRARY_PREFIX}${LIB_PROTO}${CMAKE_DEBUG_POSTFIX}${CMAKE_SHARED_LIBRARY_SUFFIX} + ${CMAKE_BINARY_DIR}/Submodules/enigma-dev/shared/${CMAKE_SHARED_LIBRARY_PREFIX}${SHARED_LIB}${CMAKE_DEBUG_POSTFIX}${CMAKE_SHARED_LIBRARY_SUFFIX} + ${CMAKE_CURRENT_SOURCE_DIR}/Submodules/enigma-dev + COMMENT "Copying exes to ENIGMA's root directory" +) + install(TARGETS ${EXE} RUNTIME DESTINATION .) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${EXE}.dir/Debug/${EXE}.pdb" DESTINATION . OPTIONAL) @@ -326,9 +366,9 @@ file(TO_CMAKE_PATH ${VCPKG_ROOT} VCPKG_ROOT) set(SEARCH_PATHS "${VCPKG_ROOT}/installed/x64-windows/bin/") endif() else() - set(LIBS "${CMAKE_INSTALL_PREFIX}/${CMAKE_SHARED_LIBRARY_PREFIX}EGM${CMAKE_SHARED_LIBRARY_SUFFIX}" - "${CMAKE_INSTALL_PREFIX}/${CMAKE_SHARED_LIBRARY_PREFIX}Protocols${CMAKE_SHARED_LIBRARY_SUFFIX}" - "${CMAKE_INSTALL_PREFIX}/${CMAKE_SHARED_LIBRARY_PREFIX}ENIGMAShared${CMAKE_SHARED_LIBRARY_SUFFIX}") + set(LIBS "${CMAKE_INSTALL_PREFIX}/${CMAKE_SHARED_LIBRARY_PREFIX}${LIB_EGM}${CMAKE_SHARED_LIBRARY_SUFFIX}" + "${CMAKE_INSTALL_PREFIX}/${CMAKE_SHARED_LIBRARY_PREFIX}${LIB_PROTO}${CMAKE_SHARED_LIBRARY_SUFFIX}" + "${CMAKE_INSTALL_PREFIX}/${CMAKE_SHARED_LIBRARY_PREFIX}${SHARED_LIB}${CMAKE_SHARED_LIBRARY_SUFFIX}") endif() if (WIN32) diff --git a/Dialogs/keybindingpreferences.cpp b/Dialogs/KeyBindingPreferences.cpp similarity index 94% rename from Dialogs/keybindingpreferences.cpp rename to Dialogs/KeyBindingPreferences.cpp index 61b4e313d..957eac93d 100644 --- a/Dialogs/keybindingpreferences.cpp +++ b/Dialogs/KeyBindingPreferences.cpp @@ -1,4 +1,4 @@ -#include "KeybindingPreferences.h" +#include "KeyBindingPreferences.h" #include "ui_MainWindow.h" #include "ui_SpriteEditor.h" diff --git a/Dialogs/keybindingpreferences.h b/Dialogs/KeyBindingPreferences.h similarity index 100% rename from Dialogs/keybindingpreferences.h rename to Dialogs/KeyBindingPreferences.h diff --git a/Dialogs/PreferencesDialog.cpp b/Dialogs/PreferencesDialog.cpp index 489c8f8c2..a3954976b 100644 --- a/Dialogs/PreferencesDialog.cpp +++ b/Dialogs/PreferencesDialog.cpp @@ -2,7 +2,7 @@ #include "ui_PreferencesDialog.h" #include "PreferencesKeys.h" -#include "KeybindingPreferences.h" +#include "KeyBindingPreferences.h" #include "main.h" #include "Components/Logger.h" diff --git a/Editors/RoomEditor.cpp b/Editors/RoomEditor.cpp index cd0eda3d8..e941a6502 100644 --- a/Editors/RoomEditor.cpp +++ b/Editors/RoomEditor.cpp @@ -90,15 +90,15 @@ RoomEditor::RoomEditor(MessageModel* model, QWidget* parent) : BaseEditor(model, _ui->objectSelectButton->setMenu(objMenu); _ui->objectSelectButton->setPopupMode(QToolButton::MenuButtonPopup); - auto objects = treeProxy - ->match(treeProxy->index(0, 0), TreeModel::UserRoles::TypeCaseRole, - TypeCase::kObject, 1, Qt::MatchRecursive); - if (!objects.empty()) { - QModelIndex firstObjIdx = objects.first(); - QString firstObj = firstObjIdx.data(Qt::DisplayRole).toString(); - _ui->objectSelectButton->setIcon(firstObjIdx.data(Qt::DecorationRole).value()); - _ui->currentObject->setText(firstObj); - } + // auto objects = treeProxy + // ->match(treeProxy->index(0, 0), TreeModel::UserRoles::TypeCaseRole, + // TypeCase::kObject, 1, Qt::MatchRecursive); + // if (!objects.empty()) { + // QModelIndex firstObjIdx = objects.first(); + // QString firstObj = firstObjIdx.data(Qt::DisplayRole).toString(); + // _ui->objectSelectButton->setIcon(firstObjIdx.data(Qt::DecorationRole).value()); + // _ui->currentObject->setText(firstObj); + // } connect(objMenu, &QMenuView::triggered, [=](const QModelIndex &index) { _ui->currentObject->setText(treeProxy->data(index, Qt::DisplayRole).toString()); @@ -116,9 +116,10 @@ RoomEditor::RoomEditor(MessageModel* model, QWidget* parent) : BaseEditor(model, // This updates all the model views in the event of a sprite is changed connect(MainWindow::resourceMap, &ResourceModelMap::DataChanged, this, [this]() { - _ui->instancesListView->reset(); - _ui->tilesListView->reset(); - _ui->layersPropertiesView->reset(); + // _ui->entitiesListView->reset(); + _ui->elementsListView->reset(); + _ui->layersListView->reset(); + _ui->propertiesView->reset(); }); RoomEditor::RebindSubModels(); @@ -133,35 +134,35 @@ void RoomEditor::RebindSubModels() { RepeatedMessageModel* im = _roomModel->GetSubModel(Room::kInstancesFieldNumber); RepeatedSortFilterProxyModel* imp = new RepeatedSortFilterProxyModel(this); imp->SetSourceModel(im); - _ui->instancesListView->setModel(imp); + // _ui->instancesListView->setModel(imp); - for (int c = 0; c < im->columnCount(); ++c) { - if (c != im->FieldToColumn(Room::Instance::kNameFieldNumber) && - c != im->FieldToColumn(Room::Instance::kObjectTypeFieldNumber) && - c != im->FieldToColumn(Room::Instance::kIdFieldNumber)) - _ui->instancesListView->hideColumn(c); - else - _ui->instancesListView->resizeColumnToContents(c); - } + // for (int c = 0; c < im->columnCount(); ++c) { + // if (c != im->FieldToColumn(Room::Instance::kNameFieldNumber) && + // c != im->FieldToColumn(Room::Instance::kObjectTypeFieldNumber) && + // c != im->FieldToColumn(Room::Instance::kIdFieldNumber)) + // _ui->instancesListView->hideColumn(c); + // else + // _ui->instancesListView->resizeColumnToContents(c); + // } - _ui->instancesListView->header()->swapSections(im->FieldToColumn(Room::Instance::kNameFieldNumber), - im->FieldToColumn(Room::Instance::kObjectTypeFieldNumber)); + // _ui->instancesListView->header()->swapSections(im->FieldToColumn(Room::Instance::kNameFieldNumber), + // im->FieldToColumn(Room::Instance::kObjectTypeFieldNumber)); RepeatedMessageModel* tm = _roomModel->GetSubModel(Room::kTilesFieldNumber); RepeatedSortFilterProxyModel* tmp = new RepeatedSortFilterProxyModel(this); tmp->SetSourceModel(tm); - _ui->tilesListView->setModel(tmp); + _ui->layersListView->setModel(tmp); for (int c = 0; c < tm->columnCount(); ++c) { if (c != tm->FieldToColumn(Room::Tile::kBackgroundNameFieldNumber) && c != tm->FieldToColumn(Room::Tile::kIdFieldNumber) && c != tm->FieldToColumn(Room::Tile::kDepthFieldNumber) && c != tm->FieldToColumn(Room::Tile::kNameFieldNumber)) - _ui->tilesListView->hideColumn(c); + _ui->layersListView->hideColumn(c); else - _ui->tilesListView->resizeColumnToContents(c); + _ui->layersListView->resizeColumnToContents(c); } - _ui->tilesListView->header()->swapSections(tm->FieldToColumn(Room::Tile::kNameFieldNumber), + _ui->layersListView->header()->swapSections(tm->FieldToColumn(Room::Tile::kNameFieldNumber), tm->FieldToColumn(Room::Tile::kBackgroundNameFieldNumber)); RepeatedMessageModel* vm = _roomModel->GetSubModel(Room::kViewsFieldNumber); @@ -170,19 +171,19 @@ void RoomEditor::RebindSubModels() { connect(_ui->elementsListView->selectionModel(), &QItemSelectionModel::selectionChanged, [=](const QItemSelection& selected, const QItemSelection& /*deselected*/) { if (selected.empty()) return; - _ui->tilesListView->clearSelection(); + _ui->layersListView->clearSelection(); auto selectedIndex = selected.indexes().first(); auto currentInstanceModel = imp->GetSubModel(selectedIndex.row()); - _ui->layersPropertiesView->setModel(currentInstanceModel); + _ui->propertiesView->setModel(currentInstanceModel); }); - connect(_ui->tilesListView->selectionModel(), &QItemSelectionModel::selectionChanged, + connect(_ui->layersListView->selectionModel(), &QItemSelectionModel::selectionChanged, [=](const QItemSelection& selected, const QItemSelection& /*deselected*/) { if (selected.empty()) return; - _ui->instancesListView->clearSelection(); + // _ui->instancesListView->clearSelection(); auto selectedIndex = selected.indexes().first(); auto currentInstanceModel = tmp->GetSubModel(selectedIndex.row()); - _ui->layersPropertiesView->setModel(currentInstanceModel); + _ui->propertiesView->setModel(currentInstanceModel); }); BaseEditor::RebindSubModels(); @@ -201,7 +202,7 @@ void RoomEditor::MousePressed(Qt::MouseButton button) { if (button == Qt::MouseButton::LeftButton) { auto index = layerElements->rowCount(); layerElements->insertRow(index); - layerElements->SetData(_ui->currentObject->text(), index, Room::Instance::kObjectTypeFieldNumber); + // layerElements->SetData(_ui->currentObject->text(), index, Room::Instance::kObjectTypeFieldNumber); } } @@ -218,3 +219,7 @@ void RoomEditor::on_actionZoom_triggered() { _ui->roomPreviewBackground->ResetZo void RoomEditor::on_actionShowHideGrid_triggered() { _ui->roomPreviewBackground->SetGridVisible(_ui->actionShowHideGrid->isChecked()); } + +void RoomEditor::updateCursorPositionLabel(const QPoint& pos) { + +} diff --git a/Editors/RoomEditor.h b/Editors/RoomEditor.h index 7847f3ac4..8f399f775 100644 --- a/Editors/RoomEditor.h +++ b/Editors/RoomEditor.h @@ -21,6 +21,12 @@ class RoomEditor : public BaseEditor { void setZoom(qreal zoom); + void MouseMoved(int x, int y); + + void MousePressed(Qt::MouseButton button); + + void MouseReleased(Qt::MouseButton button); + public slots: void RebindSubModels() override; diff --git a/Editors/VisualShaderEditor.cpp b/Editors/VisualShaderEditor.cpp new file mode 100644 index 000000000..a668545ac --- /dev/null +++ b/Editors/VisualShaderEditor.cpp @@ -0,0 +1,26 @@ +/********************************************************************************\ + ** ** + ** Copyright (C) 2024 Saif Kandil (k0T0z) ** + ** ** + ** This file is a part of the ENIGMA Development Environment. ** + ** ** + ** ** + ** ENIGMA is free software: you can redistribute it and/or modify it under the ** + ** terms of the GNU General Public License as published by the Free Software ** + ** Foundation, version 3 of the license or any later version. ** + ** ** + ** This application and its source code is distributed AS-IS, WITHOUT ANY ** + ** WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS ** + ** FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ** + ** details. ** + ** ** + ** You should have recieved a copy of the GNU General Public License along ** + ** with this code. If not, see ** + ** ** + ** ENIGMA is an environment designed to create games and other programs with a ** + ** high-level, fully compilable language. Developers of ENIGMA or anything ** + ** associated with ENIGMA are in no way responsible for its users or ** + ** applications created by its users, or damages caused by the environment ** + ** or programs made in the environment. ** + ** ** + \********************************************************************************/ diff --git a/Editors/VisualShaderEditor.h b/Editors/VisualShaderEditor.h new file mode 100644 index 000000000..24822acbe --- /dev/null +++ b/Editors/VisualShaderEditor.h @@ -0,0 +1,33 @@ +/********************************************************************************\ + ** ** + ** Copyright (C) 2024 Saif Kandil (k0T0z) ** + ** ** + ** This file is a part of the ENIGMA Development Environment. ** + ** ** + ** ** + ** ENIGMA is free software: you can redistribute it and/or modify it under the ** + ** terms of the GNU General Public License as published by the Free Software ** + ** Foundation, version 3 of the license or any later version. ** + ** ** + ** This application and its source code is distributed AS-IS, WITHOUT ANY ** + ** WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS ** + ** FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ** + ** details. ** + ** ** + ** You should have recieved a copy of the GNU General Public License along ** + ** with this code. If not, see ** + ** ** + ** ENIGMA is an environment designed to create games and other programs with a ** + ** high-level, fully compilable language. Developers of ENIGMA or anything ** + ** associated with ENIGMA are in no way responsible for its users or ** + ** applications created by its users, or damages caused by the environment ** + ** or programs made in the environment. ** + ** ** + \********************************************************************************/ + +#ifndef ENIGMA_VISUAL_SHADER_EDITOR_H +#define ENIGMA_VISUAL_SHADER_EDITOR_H + +#include "ResourceTransformations/VisualShader/visual_shader.h" + +#endif // ENIGMA_VISUAL_SHADER_EDITOR_H diff --git a/MainWindow.cpp b/MainWindow.cpp index 9a9ad6985..2082a33c8 100644 --- a/MainWindow.cpp +++ b/MainWindow.cpp @@ -35,13 +35,14 @@ #undef GetMessage -QList MainWindow::EnigmaSearchPaths = {QDir::currentPath(), "./enigma-dev", "../enigma-dev", - "../RadialGM/Submodules/enigma-dev", "/opt/enigma-dev/", "/usr/lib/enigma-dev"}; +QList MainWindow::EnigmaSearchPaths = {"/opt/enigma-dev/", "/usr/lib/enigma-dev", + QDir::currentPath() + "/../Submodules/enigma-dev/"}; QFileInfo MainWindow::EnigmaRoot = MainWindow::getEnigmaRoot(); QList MainWindow::systemCache; MainWindow *MainWindow::_instance = nullptr; ResourceModelMap *MainWindow::resourceMap = nullptr; TreeModel *MainWindow::treeModel = nullptr; +MessageModel *MainWindow::protoModel = nullptr; std::unique_ptr MainWindow::_event_data; static QTextEdit *diagnosticTextEdit = nullptr; @@ -168,15 +169,14 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), _ui(new Ui::MainW settingsButton->setToolButtonStyle(Qt::ToolButtonStyle::ToolButtonTextBesideIcon); _ui->actionSettings->setMenu(_ui->menuChangeGameSettings); + ///////////////////////////// + // Create the server plugin + ///////////////////////////// + RGMPlugin *pluginServer = new ServerPlugin(*this); auto outputTextBrowser = this->_ui->outputTextBrowser; connect(pluginServer, &RGMPlugin::LogOutput, outputTextBrowser, &QTextBrowser::append); - connect(pluginServer, &RGMPlugin::CompileStatusChanged, [=](bool finished) { - _ui->outputDockWidget->show(); - _ui->actionRun->setEnabled(finished); - _ui->actionDebug->setEnabled(finished); - _ui->actionCreateExecutable->setEnabled(finished); - }); + connect(pluginServer, &RGMPlugin::CompileStatusChanged, this, &MainWindow::on_compileStatus_changed); connect(this, &MainWindow::CurrentConfigChanged, pluginServer, &RGMPlugin::SetCurrentConfig); connect(_ui->actionRun, &QAction::triggered, pluginServer, &RGMPlugin::Run); connect(_ui->actionDebug, &QAction::triggered, pluginServer, &RGMPlugin::Debug); @@ -186,6 +186,11 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), _ui(new Ui::MainW } MainWindow::~MainWindow() { + if (protoModel) delete protoModel; + if (treeModel) delete treeModel; + if (resourceMap) delete resourceMap; + if (toggleDiagnosticsAction) delete toggleDiagnosticsAction; + // if (this->pluginServer) delete this->pluginServer; diagnosticTextEdit = nullptr; delete _ui; } @@ -416,13 +421,14 @@ void MainWindow::openProject(std::unique_ptr openedProject) { treeConf.SetMessagePassthrough(); treeConf.DisableOneofReassignment(); - delete resourceMap; + if (resourceMap) delete resourceMap; resourceMap = new ResourceModelMap(this); - auto pm = new MessageModel(ProtoModel::NonProtoParent{this}, _project->mutable_game()->mutable_root()); + if (protoModel) delete protoModel; + protoModel = new MessageModel(ProtoModel::NonProtoParent{this}, _project->mutable_game()->mutable_root()); // Connect methods to auto update fields with extensions - connect(pm, &ProtoModel::ModelConstructed, [](ProtoModel *model) { + connect(protoModel, &ProtoModel::ModelConstructed, [](ProtoModel *model) { PrimitiveModel *primitive_model = model->TryCastAsPrimitiveModel(); if (primitive_model) { const FieldDescriptor *const field = primitive_model->GetFieldDescriptor(); @@ -437,13 +443,14 @@ void MainWindow::openProject(std::unique_ptr openedProject) { } }); - pm->RebuildSubModels(); + protoModel->RebuildSubModels(); + + protoModel->SetDisplayConfig(msgConf); - pm->SetDisplayConfig(msgConf); + resourceMap->TreeChanged(protoModel); - resourceMap->TreeChanged(pm); - delete treeModel; - treeModel = new TreeModel(pm, nullptr, treeConf); + if (treeModel) delete treeModel; + treeModel = new TreeModel(protoModel, nullptr, treeConf); _ui->treeView->setModel(treeModel); connect(treeModel, &TreeModel::ItemRenamed, resourceMap, @@ -451,7 +458,7 @@ void MainWindow::openProject(std::unique_ptr openedProject) { connect(treeModel, &TreeModel::TreeChanged, resourceMap, &ResourceModelMap::TreeChanged); connect(treeModel, &TreeModel::ItemRemoved, resourceMap, &ResourceModelMap::ResourceRemoved, Qt::DirectConnection); - connect(pm, &ProtoModel::dataChanged, resourceMap, &ResourceModelMap::dataChanged, + connect(protoModel, &ProtoModel::dataChanged, resourceMap, &ResourceModelMap::dataChanged, Qt::DirectConnection); connect(treeModel, &TreeModel::ModelAboutToBeDeleted, this, &MainWindow::ResourceModelDeleted, Qt::DirectConnection); @@ -717,3 +724,10 @@ void MainWindow::on_actionSortByName_triggered() { void MainWindow::on_treeView_customContextMenuRequested(const QPoint &pos) { _ui->menuEdit->exec(_ui->treeView->mapToGlobal(pos)); } + +void MainWindow::on_compileStatus_changed(bool finished) { + _ui->outputDockWidget->show(); + _ui->actionRun->setEnabled(finished); + _ui->actionDebug->setEnabled(finished); + _ui->actionCreateExecutable->setEnabled(finished); +} diff --git a/MainWindow.h b/MainWindow.h index 56cffccf9..13caf2732 100644 --- a/MainWindow.h +++ b/MainWindow.h @@ -32,6 +32,7 @@ class MainWindow : public QMainWindow { static ResourceModelMap* resourceMap; static MessageModel* resourceModel; static TreeModel* treeModel; + static MessageModel* protoModel; static QList systemCache; explicit MainWindow(QWidget *parent); @@ -109,6 +110,8 @@ class MainWindow : public QMainWindow { void on_treeView_doubleClicked(const QModelIndex &index); void on_treeView_customContextMenuRequested(const QPoint &pos); + void on_compileStatus_changed(bool finished); + private: void closeEvent(QCloseEvent *event) override; diff --git a/Plugins/ServerPlugin.cpp b/Plugins/ServerPlugin.cpp index 57a9d4452..9a8590992 100644 --- a/Plugins/ServerPlugin.cpp +++ b/Plugins/ServerPlugin.cpp @@ -252,15 +252,12 @@ ServerPlugin::ServerPlugin(MainWindow& mainWindow) : RGMPlugin(mainWindow) { // create a new child process for us to launch an emake server process = new QProcess(this); - connect(process, &QProcess::errorOccurred, [&](QProcess::ProcessError error) { - qDebug() << "QProcess error: " << error << endl; - }); - connect(process, &QProcess::readyReadStandardOutput, [&]() { - emit LogOutput(process->readAllStandardOutput()); - }); - connect(process, &QProcess::readyReadStandardError, [&]() { - emit LogOutput(process->readAllStandardError()); - }); + connect(process, &QProcess::errorOccurred, this, &ServerPlugin::onErrorOccurred); + connect(process, QOverload::of(&QProcess::finished), this, &ServerPlugin::onProcessFinished); + connect(process, &QProcess::readyReadStandardError, this, &ServerPlugin::onReadyReadStandardError); + connect(process, &QProcess::readyReadStandardOutput, this, &ServerPlugin::onReadyReadStandardOutput); + connect(process, &QProcess::started, this, &ServerPlugin::onProcessStarted); + connect(process, &QProcess::stateChanged, this, &ServerPlugin::onStateChanged); #ifdef _WIN32 //TODO: Make all this stuff configurable in IDE @@ -296,19 +293,19 @@ ServerPlugin::ServerPlugin(MainWindow& mainWindow) : RGMPlugin(mainWindow) { } if (emakeFileInfo.filePath().isEmpty()) { - qDebug() << "Error: Failed to locate emake. Compiling and syntax check will not work.\n" << "Search Paths:\n" << MainWindow::EnigmaSearchPaths; + qDebug() << "Error: Failed to locate emake. Compiling and syntax check will not work.\n" << "Search Paths:\n" << MainWindow::EnigmaSearchPaths << Qt::endl; return; } if (MainWindow::EnigmaRoot.filePath().isEmpty()) { - qDebug() << "Error: Failed to locate ENIGMA sources. Compiling and syntax check will not work.\n" << "Search Paths:\n" << MainWindow::EnigmaSearchPaths; + qDebug() << "Error: Failed to locate ENIGMA sources. Compiling and syntax check will not work.\n" << "Search Paths:\n" << MainWindow::EnigmaSearchPaths << Qt::endl; return; } // use the closest matching emake file we found and launch it in a child process - qDebug() << "Using emake exe at: " << emakeFileInfo.absolutePath(); - qDebug() << "Using ENIGMA sources at: " << MainWindow::EnigmaRoot.absolutePath(); - process->setWorkingDirectory(emakeFileInfo.absolutePath()); + qDebug() << "Using emake exe at: " << emakeFileInfo.absolutePath() << Qt::endl; + qDebug() << "Using ENIGMA sources at: " << MainWindow::EnigmaRoot.absolutePath() << Qt::endl; + process->setWorkingDirectory(emakeFileInfo.absolutePath()); // Since emake depends on other libraries in the same directory. QString program = emakeFileInfo.fileName(); QStringList arguments; arguments << "--server" @@ -321,8 +318,14 @@ ServerPlugin::ServerPlugin(MainWindow& mainWindow) : RGMPlugin(mainWindow) { qDebug() << "Running: " << program << " " << arguments; - process->start(program, arguments); - process->waitForStarted(); + process->start(emakeFileInfo.filePath(), arguments); + + // TODO: This blocks the main thread, we should probably move this to a separate thread. + if (!process->waitForStarted(-1)) { + qDebug() << "Failed to start the emake server!" << Qt::endl; + qDebug() << process->errorString() << Qt::endl; + return; + } // construct the channel and connect to the server running in the process // Note: gRPC is too dumb to resolve localhost on linux @@ -340,7 +343,15 @@ ServerPlugin::ServerPlugin(MainWindow& mainWindow) : RGMPlugin(mainWindow) { ServerPlugin::~ServerPlugin() { compilerClient->TearDown(); - process->waitForFinished(); + + if (!process->waitForFinished(-1)) { + qDebug() << "Failed to stop the emake server!" << Qt::endl; + qDebug() << process->errorString() << Qt::endl; + return; + } + + if (compilerClient) delete compilerClient; + if (process) delete process; } void ServerPlugin::Run() { compilerClient->CompileBuffer(mainWindow.Game(), CompileRequest::RUN); } @@ -352,8 +363,81 @@ void ServerPlugin::CreateExecutable() { QFileDialog::getSaveFileName(&mainWindow, tr("Create Executable"), "", tr("Executable (*.exe);;All Files (*)")); if (!fileName.isEmpty()) compilerClient->CompileBuffer(mainWindow.Game(), CompileRequest::COMPILE, fileName.toStdString()); -}; +} void ServerPlugin::SetCurrentConfig(const resources::Settings& settings) { compilerClient->SetCurrentConfig(settings); -}; +} + +void ServerPlugin::onErrorOccurred(QProcess::ProcessError error) { + qDebug() << "QProcess error: " << error << Qt::endl; + switch (error) { + case QProcess::FailedToStart: + qDebug() << "Error: The emake server failed to start. Either the invoked program is missing, or you may have insufficient permissions to invoke the program." << Qt::endl; + break; + case QProcess::Crashed: + qDebug() << "Error: The emake server crashed some time after starting successfully." << Qt::endl; + break; + case QProcess::Timedout: + qDebug() << "Error: The last waitFor...() function timed out. The state of QProcess is unchanged, and you can try calling waitFor...() again." << Qt::endl; + break; + case QProcess::WriteError: + qDebug() << "Error: An error occurred when attempting to write to the emake server. For example, the emake server may not be running, or it may have closed its input channel." << Qt::endl; + break; + case QProcess::ReadError: + qDebug() << "Error: An error occurred when attempting to read from the emake server. For example, the emake server may not be running." << Qt::endl; + break; + case QProcess::UnknownError: + qDebug() << "Error: An unknown error occurred. This is the default return value of error()." << Qt::endl; + break; + default: + qDebug() << "Error: Unrecognized error code." << Qt::endl; + break; + } +} + +void ServerPlugin::onProcessFinished(int exit_code, QProcess::ExitStatus exit_status) { + qDebug() << "Exit Code: " << exit_code << Qt::endl; + switch (exit_status) { + case QProcess::NormalExit: + qDebug() << "Exit Status: The emake server exited normally." << Qt::endl; + break; + case QProcess::CrashExit: + qDebug() << "Exit Status: The emake server crashed." << Qt::endl; + break; + default: + qDebug() << "Exit Status: Unrecognized exit status code." << Qt::endl; + break; + } +} + +void ServerPlugin::onReadyReadStandardError() { + qDebug() << "Standard Error: " << process->readAllStandardError().constData() << Qt::endl; + emit LogOutput(process->readAllStandardError()); +} + +void ServerPlugin::onReadyReadStandardOutput() { + qDebug() << "Standard Output: " << process->readAllStandardOutput().constData() << Qt::endl; + emit LogOutput(process->readAllStandardOutput()); +} + +void ServerPlugin::onProcessStarted() { + qDebug() << "The emake server started successfully!" << Qt::endl; +} + +void ServerPlugin::onStateChanged(QProcess::ProcessState state) { + switch (state) { + case QProcess::NotRunning: + qDebug() << "State: The emake server is not running." << Qt::endl; + break; + case QProcess::Starting: + qDebug() << "State: The emake server is starting, but the program has not yet been invoked." << Qt::endl; + break; + case QProcess::Running: + qDebug() << "State: The emake server is running and is ready for reading and writing." << Qt::endl; + break; + default: + qDebug() << "State: Unrecognized state code." << Qt::endl; + break; + } +} diff --git a/Plugins/ServerPlugin.h b/Plugins/ServerPlugin.h index 1f7fdd85f..2c439b64e 100644 --- a/Plugins/ServerPlugin.h +++ b/Plugins/ServerPlugin.h @@ -92,6 +92,14 @@ class ServerPlugin : public RGMPlugin { void CreateExecutable() override; void SetCurrentConfig(const buffers::resources::Settings& settings) override; + private slots: + void onErrorOccurred(QProcess::ProcessError error); + void onProcessFinished(int exit_code, QProcess::ExitStatus exit_status); + void onReadyReadStandardError(); + void onReadyReadStandardOutput(); + void onProcessStarted(); + void onStateChanged(QProcess::ProcessState state); + private: QProcess* process; CompilerClient* compilerClient; diff --git a/Submodules/nodeeditor b/Submodules/nodeeditor new file mode 160000 index 000000000..765146518 --- /dev/null +++ b/Submodules/nodeeditor @@ -0,0 +1 @@ +Subproject commit 765146518d6adc8cf3f71be2a11cf9ed9d6f1372 From b7ad0414514bfb8389bb92f285e401daa53c67ad Mon Sep 17 00:00:00 2001 From: Saif Kandil <74428638+k0T0z@users.noreply.github.com> Date: Sun, 25 Aug 2024 23:58:44 +0300 Subject: [PATCH 03/56] adding tests dir Signed-off-by: Saif Kandil <74428638+k0T0z@users.noreply.github.com> --- Tests/Editors/VisualShaderEditorTests.cpp | 26 +++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 Tests/Editors/VisualShaderEditorTests.cpp diff --git a/Tests/Editors/VisualShaderEditorTests.cpp b/Tests/Editors/VisualShaderEditorTests.cpp new file mode 100644 index 000000000..a668545ac --- /dev/null +++ b/Tests/Editors/VisualShaderEditorTests.cpp @@ -0,0 +1,26 @@ +/********************************************************************************\ + ** ** + ** Copyright (C) 2024 Saif Kandil (k0T0z) ** + ** ** + ** This file is a part of the ENIGMA Development Environment. ** + ** ** + ** ** + ** ENIGMA is free software: you can redistribute it and/or modify it under the ** + ** terms of the GNU General Public License as published by the Free Software ** + ** Foundation, version 3 of the license or any later version. ** + ** ** + ** This application and its source code is distributed AS-IS, WITHOUT ANY ** + ** WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS ** + ** FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ** + ** details. ** + ** ** + ** You should have recieved a copy of the GNU General Public License along ** + ** with this code. If not, see ** + ** ** + ** ENIGMA is an environment designed to create games and other programs with a ** + ** high-level, fully compilable language. Developers of ENIGMA or anything ** + ** associated with ENIGMA are in no way responsible for its users or ** + ** applications created by its users, or damages caused by the environment ** + ** or programs made in the environment. ** + ** ** + \********************************************************************************/ From 8292ab17f3fc78305d6d91d6cbaf6c59650c7cc2 Mon Sep 17 00:00:00 2001 From: Saif Kandil <74428638+k0T0z@users.noreply.github.com> Date: Fri, 30 Aug 2024 15:57:53 +0300 Subject: [PATCH 04/56] add VisualShader editor base code (along with https://github.com/enigma-dev/enigma-dev/pull/2399/commits/17466ab5e419f3aef2da36b03b15f5b0a93698aa) Signed-off-by: Saif Kandil <74428638+k0T0z@users.noreply.github.com> --- Editors/BaseEditor.h | 3 ++- Editors/VisualShaderEditor.cpp | 9 +++++++++ Editors/VisualShaderEditor.h | 9 +++++++++ MainWindow.cpp | 7 ++++++- MainWindow.h | 1 + MainWindow.ui | 8 +++++++- 6 files changed, 34 insertions(+), 3 deletions(-) diff --git a/Editors/BaseEditor.h b/Editors/BaseEditor.h index 0cf6de9db..5ee3822d5 100644 --- a/Editors/BaseEditor.h +++ b/Editors/BaseEditor.h @@ -13,7 +13,8 @@ static const QHash ResTypeFields = { {TypeCase::kPath, TreeNode::kPathFieldNumber}, {TypeCase::kFont, TreeNode::kFontFieldNumber}, {TypeCase::kScript, TreeNode::kScriptFieldNumber}, {TypeCase::kTimeline, TreeNode::kTimelineFieldNumber}, {TypeCase::kObject, TreeNode::kObjectFieldNumber}, {TypeCase::kRoom, TreeNode::kRoomFieldNumber}, - {TypeCase::kSettings, TreeNode::kSettingsFieldNumber}, {TypeCase::kShader, TreeNode::kShaderFieldNumber}}; + {TypeCase::kSettings, TreeNode::kSettingsFieldNumber}, {TypeCase::kShader, TreeNode::kShaderFieldNumber}, + {TypeCase::kVisualShader, TreeNode::kVisualShaderFieldNumber}}; class BaseEditor : public QWidget { Q_OBJECT diff --git a/Editors/VisualShaderEditor.cpp b/Editors/VisualShaderEditor.cpp index a668545ac..cc41ba76d 100644 --- a/Editors/VisualShaderEditor.cpp +++ b/Editors/VisualShaderEditor.cpp @@ -24,3 +24,12 @@ ** or programs made in the environment. ** ** ** \********************************************************************************/ + +#include "VisualShaderEditor.h" + +#include "VisualShader.pb.h" + +VisualShaderEditor::VisualShaderEditor(MessageModel* model, QWidget* parent) : BaseEditor(model, parent) { +// this->setWindowIcon(QIcon(":/resources/visual_shader.png")); + +} diff --git a/Editors/VisualShaderEditor.h b/Editors/VisualShaderEditor.h index 24822acbe..779985f10 100644 --- a/Editors/VisualShaderEditor.h +++ b/Editors/VisualShaderEditor.h @@ -29,5 +29,14 @@ #define ENIGMA_VISUAL_SHADER_EDITOR_H #include "ResourceTransformations/VisualShader/visual_shader.h" +#include "BaseEditor.h" + +class VisualShaderEditor : public BaseEditor { + Q_OBJECT + + public: + VisualShaderEditor(MessageModel* model, QWidget* parent = nullptr); + +}; #endif // ENIGMA_VISUAL_SHADER_EDITOR_H diff --git a/MainWindow.cpp b/MainWindow.cpp index 2082a33c8..7293e3c4a 100644 --- a/MainWindow.cpp +++ b/MainWindow.cpp @@ -12,6 +12,7 @@ #include "Editors/ScriptEditor.h" #include "Editors/SettingsEditor.h" #include "Editors/ShaderEditor.h" +#include "Editors/VisualShaderEditor.h" #include "Editors/SoundEditor.h" #include "Editors/SpriteEditor.h" #include "Editors/TimelineEditor.h" @@ -305,7 +306,7 @@ void MainWindow::openNewProject() { auto newProject = std::make_unique(); auto *root = newProject->mutable_game()->mutable_root(); QList defaultGroups = {tr("Sprites"), tr("Sounds"), tr("Backgrounds"), tr("Paths"), - tr("Scripts"), tr("Shaders"), tr("Fonts"), tr("Timelines"), + tr("Scripts"), tr("Shaders"), tr("Visual Shaders"), tr("Fonts"), tr("Timelines"), tr("Objects"), tr("Rooms"), tr("Includes"), tr("Configs")}; // We can edit the proto directly, here, since the model doesn't exist, yet. for (const auto &groupName : defaultGroups) { @@ -376,6 +377,7 @@ void MainWindow::openProject(std::unique_ptr openedProject) { treeConf.UseEditorLauncher(Launch(this)); treeConf.UseEditorLauncher(Launch(this)); treeConf.UseEditorLauncher(Launch(this)); + treeConf.UseEditorLauncher(Launch(this)); treeConf.UseEditorLauncher(Launch(this)); treeConf.UseEditorLauncher(Launch(this)); treeConf.UseEditorLauncher(Launch(this)); @@ -390,6 +392,7 @@ void MainWindow::openProject(std::unique_ptr openedProject) { msgConf.SetDefaultIcon("path"); msgConf.SetDefaultIcon("script"); msgConf.SetDefaultIcon("shader"); + msgConf.SetDefaultIcon("visual_shader"); msgConf.SetDefaultIcon("font"); msgConf.SetDefaultIcon("timeline"); msgConf.SetDefaultIcon("object"); @@ -646,6 +649,8 @@ void MainWindow::on_actionCreateScript_triggered() { CreateResource(TypeCase::kS void MainWindow::on_actionCreateShader_triggered() { CreateResource(TypeCase::kShader); } +void MainWindow::on_actionCreateVisualShader_triggered() { CreateResource(TypeCase::kVisualShader); } + void MainWindow::on_actionCreateFont_triggered() { CreateResource(TypeCase::kFont); } void MainWindow::on_actionCreateTimeline_triggered() { CreateResource(TypeCase::kTimeline); } diff --git a/MainWindow.h b/MainWindow.h index 13caf2732..c9c1606b3 100644 --- a/MainWindow.h +++ b/MainWindow.h @@ -83,6 +83,7 @@ class MainWindow : public QMainWindow { void on_actionCreatePath_triggered(); void on_actionCreateScript_triggered(); void on_actionCreateShader_triggered(); + void on_actionCreateVisualShader_triggered(); void on_actionCreateFont_triggered(); void on_actionCreateTimeline_triggered(); void on_actionCreateObject_triggered(); diff --git a/MainWindow.ui b/MainWindow.ui index b8926488a..6dec77b25 100644 --- a/MainWindow.ui +++ b/MainWindow.ui @@ -100,7 +100,7 @@ 0 0 1200 - 31 + 24 @@ -229,6 +229,7 @@ + @@ -981,6 +982,11 @@ Ctrl+Shift+G + + + Create Visual Shader + + From 4e33db8d85d96f6d650155f2899d3a1d68b5e0c0 Mon Sep 17 00:00:00 2001 From: Saif Kandil <74428638+k0T0z@users.noreply.github.com> Date: Fri, 30 Aug 2024 16:02:07 +0300 Subject: [PATCH 05/56] reverting unknown change Signed-off-by: Saif Kandil <74428638+k0T0z@users.noreply.github.com> --- MainWindow.ui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MainWindow.ui b/MainWindow.ui index 6dec77b25..2077e17eb 100644 --- a/MainWindow.ui +++ b/MainWindow.ui @@ -100,7 +100,7 @@ 0 0 1200 - 24 + 31 From dfd0414f95b0eb3f2c5683cb5076e901f5411d96 Mon Sep 17 00:00:00 2001 From: Saif Kandil <74428638+k0T0z@users.noreply.github.com> Date: Sun, 1 Sep 2024 07:17:14 +0300 Subject: [PATCH 06/56] Add TODO for improving the server plugin later --- Plugins/ServerPlugin.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Plugins/ServerPlugin.cpp b/Plugins/ServerPlugin.cpp index 9a8590992..2ff724730 100644 --- a/Plugins/ServerPlugin.cpp +++ b/Plugins/ServerPlugin.cpp @@ -249,6 +249,8 @@ void CompilerClient::UpdateLoop(void* got_tag, bool ok) { } ServerPlugin::ServerPlugin(MainWindow& mainWindow) : RGMPlugin(mainWindow) { + // TODO: Check if emake is already running and connect to it instead of starting a new one. + // create a new child process for us to launch an emake server process = new QProcess(this); From 8d1e81068d74e0aa5a37df58ca96f579fb0053f1 Mon Sep 17 00:00:00 2001 From: Saif Kandil <74428638+k0T0z@users.noreply.github.com> Date: Sun, 1 Sep 2024 15:40:51 +0300 Subject: [PATCH 07/56] fixed and improved QtNodes add_subdirectory support Signed-off-by: Saif Kandil <74428638+k0T0z@users.noreply.github.com> --- CMakeLists.txt | 8 ++------ Submodules/nodeeditor | 2 +- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 19a0b4413..6b98d1c6d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,7 +30,7 @@ if(NOT CMAKE_BUILD_TYPE) CMAKE_BUILD_TYPE "Debug" CACHE STRING - "Choose the type of build from: Debug Release RelWithDebInfo MinSizeRel." + "Choose the type of build from: Debug Release MinSizeRel RelWithDebInfo." FORCE) endif() @@ -316,12 +316,8 @@ find_library(LIB_DOUBLE_CONVERSION NAMES double-conversion) target_link_libraries(${EXE} PRIVATE ${LIB_DOUBLE_CONVERSION}) # nodeeditor -# FIXME: In order for BUILD_DEBUG_POSTFIX_D and USE_QT6 to be set correctly, you will need to configure the project twice: https://cmake.org/cmake/help/latest/policy/CMP0077.html -if (CMAKE_BUILD_TYPE MATCHES "Debug") - set(BUILD_DEBUG_POSTFIX_D ON) -endif() -set(USE_QT6 OFF) # We use Qt5 add_subdirectory(Submodules/nodeeditor) +target_link_libraries(${EXE} PRIVATE QtNodes::QtNodes) if(WIN32) # Windows is a turd diff --git a/Submodules/nodeeditor b/Submodules/nodeeditor index 765146518..f0b74fb52 160000 --- a/Submodules/nodeeditor +++ b/Submodules/nodeeditor @@ -1 +1 @@ -Subproject commit 765146518d6adc8cf3f71be2a11cf9ed9d6f1372 +Subproject commit f0b74fb52038c0fb17ceb7d718fddba4bd079299 From 5a0b5e89d8a332fbb5b7d96c54425bee7062b141 Mon Sep 17 00:00:00 2001 From: Saif Kandil <74428638+k0T0z@users.noreply.github.com> Date: Sun, 1 Sep 2024 18:42:30 +0300 Subject: [PATCH 08/56] initial prototype: integrated the simple_graph_model example Signed-off-by: Saif Kandil <74428638+k0T0z@users.noreply.github.com> --- Editors/VisualShaderEditor.cpp | 372 ++++++++++++++++++++++++++++++++- Editors/VisualShaderEditor.h | 125 +++++++++++ 2 files changed, 495 insertions(+), 2 deletions(-) diff --git a/Editors/VisualShaderEditor.cpp b/Editors/VisualShaderEditor.cpp index cc41ba76d..389383f7e 100644 --- a/Editors/VisualShaderEditor.cpp +++ b/Editors/VisualShaderEditor.cpp @@ -27,9 +27,377 @@ #include "VisualShaderEditor.h" +#include +#include +#include +#include + +#include + #include "VisualShader.pb.h" -VisualShaderEditor::VisualShaderEditor(MessageModel* model, QWidget* parent) : BaseEditor(model, parent) { +using QtNodes::GraphicsView; +using QtNodes::NodeRole; + +VisualShaderEditor::VisualShaderEditor(MessageModel* model, QWidget* parent) : BaseEditor(model, parent), graph(nullptr), scene(nullptr), layout(nullptr), view(nullptr) { // this->setWindowIcon(QIcon(":/resources/visual_shader.png")); - + + graph = new VisualShaderGraph(); + + // Initialize and connect two nodes. + { + NodeId id1 = graph->addNode(); + graph->setNodeData(id1, NodeRole::Position, QPointF(0, 0)); + + NodeId id2 = graph->addNode(); + graph->setNodeData(id2, NodeRole::Position, QPointF(300, 300)); + + graph->addConnection(ConnectionId{id1, 0, id2, 0}); + } + + scene = new BasicGraphicsScene(*graph); + + scene->setOrientation(Qt::Vertical); + + layout = new QHBoxLayout(this); + + view = new GraphicsView(scene); + + layout->addWidget(view); + + QGroupBox *groupBox = new QGroupBox("Orientation"); + + QRadioButton *radio1 = new QRadioButton("Vertical"); + QRadioButton *radio2 = new QRadioButton("Horizontal"); + + QVBoxLayout *vbl = new QVBoxLayout; + vbl->addWidget(radio1); + vbl->addWidget(radio2); + vbl->addStretch(); + groupBox->setLayout(vbl); + + QObject::connect(radio1, &QRadioButton::clicked, [this]() { + scene->setOrientation(Qt::Vertical); + }); + + QObject::connect(radio2, &QRadioButton::clicked, [this]() { + scene->setOrientation(Qt::Horizontal); + }); + + radio1->setChecked(true); + + layout->addWidget(groupBox); + + // Setup context menu for creating new nodes. + view->setContextMenuPolicy(Qt::ActionsContextMenu); + QAction createNodeAction(QStringLiteral("Create Node"), view); + QObject::connect(&createNodeAction, &QAction::triggered, [&]() { + // Mouse position in scene coordinates. + QPointF posView = view->mapToScene(view->mapFromGlobal(QCursor::pos())); + + NodeId const newId = graph->addNode(); + graph->setNodeData(newId, NodeRole::Position, posView); + }); + view->insertAction(view->actions().front(), &createNodeAction); + + this->setLayout(layout); +} + +VisualShaderEditor::~VisualShaderEditor() { + if (view) delete view; + if (layout) delete layout; + if (scene) delete scene; + if (graph) delete graph; +} + +VisualShaderGraph::VisualShaderGraph() + : _nextNodeId{0} +{} + +VisualShaderGraph::~VisualShaderGraph() +{ + +} + +std::unordered_set VisualShaderGraph::allNodeIds() const +{ + return _nodeIds; +} + +std::unordered_set VisualShaderGraph::allConnectionIds(NodeId const nodeId) const +{ + std::unordered_set result; + + std::copy_if(_connectivity.begin(), + _connectivity.end(), + std::inserter(result, std::end(result)), + [&nodeId](ConnectionId const &cid) { + return cid.inNodeId == nodeId || cid.outNodeId == nodeId; + }); + + return result; +} + +std::unordered_set VisualShaderGraph::connections(NodeId nodeId, + PortType portType, + PortIndex portIndex) const +{ + std::unordered_set result; + + std::copy_if(_connectivity.begin(), + _connectivity.end(), + std::inserter(result, std::end(result)), + [&portType, &portIndex, &nodeId](ConnectionId const &cid) { + return (getNodeId(portType, cid) == nodeId + && getPortIndex(portType, cid) == portIndex); + }); + + return result; +} + +bool VisualShaderGraph::connectionExists(ConnectionId const connectionId) const +{ + return (_connectivity.find(connectionId) != _connectivity.end()); +} + +NodeId VisualShaderGraph::addNode(QString const nodeType) +{ + NodeId newId = _nextNodeId++; + // Create new node. + _nodeIds.insert(newId); + + Q_EMIT nodeCreated(newId); + + return newId; +} + +bool VisualShaderGraph::connectionPossible(ConnectionId const connectionId) const +{ + return _connectivity.find(connectionId) == _connectivity.end(); +} + +void VisualShaderGraph::addConnection(ConnectionId const connectionId) +{ + _connectivity.insert(connectionId); + + Q_EMIT connectionCreated(connectionId); +} + +bool VisualShaderGraph::nodeExists(NodeId const nodeId) const +{ + return (_nodeIds.find(nodeId) != _nodeIds.end()); +} + +QVariant VisualShaderGraph::nodeData(NodeId nodeId, NodeRole role) const +{ + Q_UNUSED(nodeId); + + QVariant result; + + switch (role) { + case NodeRole::Type: + result = QString("Default Node Type"); + break; + + case NodeRole::Position: + result = _nodeGeometryData[nodeId].pos; + break; + + case NodeRole::Size: + result = _nodeGeometryData[nodeId].size; + break; + + case NodeRole::CaptionVisible: + result = true; + break; + + case NodeRole::Caption: + result = QString("Node"); + break; + + case NodeRole::Style: { + auto style = StyleCollection::nodeStyle(); + result = style.toJson().toVariantMap(); + } break; + + case NodeRole::InternalData: + break; + + case NodeRole::InPortCount: + result = 5u; + break; + + case NodeRole::OutPortCount: + result = 3u; + break; + + case NodeRole::Widget: + result = QVariant(); + break; + } + + return result; +} + +bool VisualShaderGraph::setNodeData(NodeId nodeId, NodeRole role, QVariant value) +{ + bool result = false; + + switch (role) { + case NodeRole::Type: + break; + case NodeRole::Position: { + _nodeGeometryData[nodeId].pos = value.value(); + + Q_EMIT nodePositionUpdated(nodeId); + + result = true; + } break; + + case NodeRole::Size: { + _nodeGeometryData[nodeId].size = value.value(); + result = true; + } break; + + case NodeRole::CaptionVisible: + break; + + case NodeRole::Caption: + break; + + case NodeRole::Style: + break; + + case NodeRole::InternalData: + break; + + case NodeRole::InPortCount: + break; + + case NodeRole::OutPortCount: + break; + + case NodeRole::Widget: + break; + } + + return result; +} + +QVariant VisualShaderGraph::portData(NodeId nodeId, + PortType portType, + PortIndex portIndex, + PortRole role) const +{ + switch (role) { + case PortRole::Data: + return QVariant(); + break; + + case PortRole::DataType: + return QVariant(); + break; + + case PortRole::ConnectionPolicyRole: + return QVariant::fromValue(ConnectionPolicy::One); + break; + + case PortRole::CaptionVisible: + return true; + break; + + case PortRole::Caption: + if (portType == PortType::In) + return QString::fromUtf8("Port In"); + else + return QString::fromUtf8("Port Out"); + + break; + } + + return QVariant(); +} + +bool VisualShaderGraph::setPortData( + NodeId nodeId, PortType portType, PortIndex portIndex, QVariant const &value, PortRole role) +{ + Q_UNUSED(nodeId); + Q_UNUSED(portType); + Q_UNUSED(portIndex); + Q_UNUSED(value); + Q_UNUSED(role); + + return false; +} + +bool VisualShaderGraph::deleteConnection(ConnectionId const connectionId) +{ + bool disconnected = false; + + auto it = _connectivity.find(connectionId); + + if (it != _connectivity.end()) { + disconnected = true; + + _connectivity.erase(it); + } + + if (disconnected) + Q_EMIT connectionDeleted(connectionId); + + return disconnected; +} + +bool VisualShaderGraph::deleteNode(NodeId const nodeId) +{ + // Delete connections to this node first. + auto connectionIds = allConnectionIds(nodeId); + for (auto &cId : connectionIds) { + deleteConnection(cId); + } + + _nodeIds.erase(nodeId); + _nodeGeometryData.erase(nodeId); + + Q_EMIT nodeDeleted(nodeId); + + return true; +} + +QJsonObject VisualShaderGraph::saveNode(NodeId const nodeId) const +{ + QJsonObject nodeJson; + + nodeJson["id"] = static_cast(nodeId); + + { + QPointF const pos = nodeData(nodeId, NodeRole::Position).value(); + + QJsonObject posJson; + posJson["x"] = pos.x(); + posJson["y"] = pos.y(); + nodeJson["position"] = posJson; + } + + return nodeJson; +} + +void VisualShaderGraph::loadNode(QJsonObject const &nodeJson) +{ + NodeId restoredNodeId = nodeJson["id"].toInt(); + + // Next NodeId must be larger that any id existing in the graph + _nextNodeId = std::max(restoredNodeId + 1, _nextNodeId); + + // Create new node. + _nodeIds.insert(restoredNodeId); + + Q_EMIT nodeCreated(restoredNodeId); + + { + QJsonObject posJson = nodeJson["position"].toObject(); + QPointF const pos(posJson["x"].toDouble(), posJson["y"].toDouble()); + + setNodeData(restoredNodeId, NodeRole::Position, pos); + } } diff --git a/Editors/VisualShaderEditor.h b/Editors/VisualShaderEditor.h index 779985f10..c85db390f 100644 --- a/Editors/VisualShaderEditor.h +++ b/Editors/VisualShaderEditor.h @@ -28,15 +28,140 @@ #ifndef ENIGMA_VISUAL_SHADER_EDITOR_H #define ENIGMA_VISUAL_SHADER_EDITOR_H +#include +#include +#include +#include + +#include +#include +#include +#include +#include + #include "ResourceTransformations/VisualShader/visual_shader.h" #include "BaseEditor.h" +using ConnectionId = QtNodes::ConnectionId; +using ConnectionPolicy = QtNodes::ConnectionPolicy; +using NodeFlag = QtNodes::NodeFlag; +using NodeId = QtNodes::NodeId; +using NodeRole = QtNodes::NodeRole; +using PortIndex = QtNodes::PortIndex; +using PortRole = QtNodes::PortRole; +using PortType = QtNodes::PortType; +using StyleCollection = QtNodes::StyleCollection; +using QtNodes::InvalidNodeId; + +using QtNodes::BasicGraphicsScene; +using QtNodes::GraphicsView; + +class VisualShaderGraph; + class VisualShaderEditor : public BaseEditor { Q_OBJECT public: VisualShaderEditor(MessageModel* model, QWidget* parent = nullptr); + ~VisualShaderEditor() override; + + private: + VisualShaderGraph* graph; + + BasicGraphicsScene* scene; + QHBoxLayout* layout; + + GraphicsView* view; +}; + +/** + * The class implements a bare minimum required to demonstrate a model-based + * graph. + */ +class VisualShaderGraph : public QtNodes::AbstractGraphModel +{ + Q_OBJECT +public: + struct NodeGeometryData + { + QSize size; + QPointF pos; + }; + +public: + VisualShaderGraph(); + + ~VisualShaderGraph() override; + + std::unordered_set allNodeIds() const override; + + std::unordered_set allConnectionIds(NodeId const nodeId) const override; + + std::unordered_set connections(NodeId nodeId, + PortType portType, + PortIndex portIndex) const override; + + bool connectionExists(ConnectionId const connectionId) const override; + + NodeId addNode(QString const nodeType = QString()) override; + + /** + * Connection is possible when graph contains no connectivity data + * in both directions `Out -> In` and `In -> Out`. + */ + bool connectionPossible(ConnectionId const connectionId) const override; + + void addConnection(ConnectionId const connectionId) override; + + bool nodeExists(NodeId const nodeId) const override; + + QVariant nodeData(NodeId nodeId, NodeRole role) const override; + + bool setNodeData(NodeId nodeId, NodeRole role, QVariant value) override; + + QVariant portData(NodeId nodeId, + PortType portType, + PortIndex portIndex, + PortRole role) const override; + + bool setPortData(NodeId nodeId, + PortType portType, + PortIndex portIndex, + QVariant const &value, + PortRole role = PortRole::Data) override; + + bool deleteConnection(ConnectionId const connectionId) override; + + bool deleteNode(NodeId const nodeId) override; + + QJsonObject saveNode(NodeId const) const override; + + /// @brief Creates a new node based on the informatoin in `nodeJson`. + /** + * @param nodeJson conains a `NodeId`, node's position, internal node + * information. + */ + void loadNode(QJsonObject const &nodeJson) override; + +private: + NodeId newNodeId() override { return _nextNodeId++; } + +private: + std::unordered_set _nodeIds; + + /// [Important] This is a user defined data structure backing your model. + /// In your case it could be anything else representing a graph, for example, a + /// table. Or a collection of structs with pointers to each other. Or an + /// abstract syntax tree, you name it. + /// + /// This data structure contains the graph connectivity information in both + /// directions, i.e. from Node1 to Node2 and from Node2 to Node1. + std::unordered_set _connectivity; + + mutable std::unordered_map _nodeGeometryData; + /// A convenience variable needed for generating unique node ids. + NodeId _nextNodeId; }; #endif // ENIGMA_VISUAL_SHADER_EDITOR_H From 0a2eb9483df169b1870d6fcc7ee5a3093d369446 Mon Sep 17 00:00:00 2001 From: Saif Kandil <74428638+k0T0z@users.noreply.github.com> Date: Sun, 1 Sep 2024 22:03:08 +0300 Subject: [PATCH 09/56] add a TODO for finalizing the project later on Signed-off-by: Saif Kandil <74428638+k0T0z@users.noreply.github.com> --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6b98d1c6d..a018c4e84 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -65,6 +65,7 @@ set(CMAKE_AUTOUIC_SEARCH_PATHS "${CMAKE_CURRENT_SOURCE_DIR}/" "${CMAKE_CURRENT_S # Uncomment to be able to use local grpc_cpp_plugin # set(GRPC_EXE "/usr/local/bin/grpc_cpp_plugin") +# TODO: I think it will be better to send this var to the C++ level and use it everywhere instead of hardcoding it each time # Include ENIGMA things set(ENIGMA_DIR ${CMAKE_CURRENT_SOURCE_DIR}/Submodules/enigma-dev CACHE PATH "ENIGMA directory") include_directories("${CMAKE_BINARY_DIR}/Submodules/enigma-dev/shared/protos/" "${ENIGMA_DIR}/CommandLine/libEGM" "${ENIGMA_DIR}/shared") From f3fadffc89dbea3ab763cabeb644d2e317fcc064 Mon Sep 17 00:00:00 2001 From: Saif Kandil <74428638+k0T0z@users.noreply.github.com> Date: Mon, 2 Sep 2024 21:30:03 +0300 Subject: [PATCH 10/56] Suppress 'QMetaObject::connectSlotsByName: No matching signal for on_compileStatus_changed(bool)' warning, improved the buildsystem by sending the ENIGMA_DIR var to the C++ level as a define, and improved the nodeeditor submodule Signed-off-by: Saif Kandil <74428638+k0T0z@users.noreply.github.com> --- CMakeLists.txt | 46 ++++++++++++++++++++++++++++--------------- MainWindow.cpp | 7 +++---- MainWindow.h | 6 +++++- Submodules/nodeeditor | 2 +- 4 files changed, 39 insertions(+), 22 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a018c4e84..041fe3be3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -32,13 +32,25 @@ if(NOT CMAKE_BUILD_TYPE) STRING "Choose the type of build from: Debug Release MinSizeRel RelWithDebInfo." FORCE) +else() + if (NOT CMAKE_BUILD_TYPE MATCHES "Debug" AND NOT CMAKE_BUILD_TYPE MATCHES "Release" AND NOT CMAKE_BUILD_TYPE MATCHES "MinSizeRel" AND NOT CMAKE_BUILD_TYPE MATCHES "RelWithDebInfo") + message(FATAL_ERROR "Invalid build type: ${CMAKE_BUILD_TYPE}") + endif() endif() +set(CMAKE_DEBUG_POSTFIX "d") +set(CMAKE_RELEASE_POSTFIX "") +set(CMAKE_MINSIZEREL_POSTFIX "s") +set(CMAKE_RELWITHDEBINFO_POSTFIX "rd") + if (CMAKE_BUILD_TYPE MATCHES "Debug") set(EXE "RadialGM-Debug" CACHE STRING "RGM Executable name") add_definitions(-DRGM_DEBUG) - set(CMAKE_DEBUG_POSTFIX d) -else() +elseif(CMAKE_BUILD_TYPE MATCHES "MinSizeRel") + set(EXE "RadialGM-MinSizeRel" CACHE STRING "RGM Executable name") +elseif(CMAKE_BUILD_TYPE MATCHES "RelWithDebInfo") + set(EXE "RadialGM-RelWithDebInfo" CACHE STRING "RGM Executable name") +else() # Release set(EXE "RadialGM" CACHE STRING "RGM Executable name") endif() @@ -65,9 +77,11 @@ set(CMAKE_AUTOUIC_SEARCH_PATHS "${CMAKE_CURRENT_SOURCE_DIR}/" "${CMAKE_CURRENT_S # Uncomment to be able to use local grpc_cpp_plugin # set(GRPC_EXE "/usr/local/bin/grpc_cpp_plugin") -# TODO: I think it will be better to send this var to the C++ level and use it everywhere instead of hardcoding it each time +set(RGM_ROOTDIR "${CMAKE_CURRENT_SOURCE_DIR}") + +# The ENIGMA_DIR is sent to the C++ code as a define # Include ENIGMA things -set(ENIGMA_DIR ${CMAKE_CURRENT_SOURCE_DIR}/Submodules/enigma-dev CACHE PATH "ENIGMA directory") +set(ENIGMA_DIR ${RGM_ROOTDIR}/Submodules/enigma-dev CACHE PATH "ENIGMA directory") include_directories("${CMAKE_BINARY_DIR}/Submodules/enigma-dev/shared/protos/" "${ENIGMA_DIR}/CommandLine/libEGM" "${ENIGMA_DIR}/shared") # Populate a CMake variable with the sources @@ -229,7 +243,7 @@ set(RGM_HEADERS ${RGM_HEADERS} Plugins/ServerPlugin.h) add_executable(${EXE} WIN32 ${RGM_UI} ${RGM_HEADERS} ${RGM_SOURCES} ${EDITOR_SOURCES} ${RGM_RC}) # we do this even in release mode for "Editor Diagnostics" -target_compile_definitions(${EXE} PUBLIC QT_MESSAGELOGCONTEXT) +target_compile_definitions(${EXE} PUBLIC QT_MESSAGELOGCONTEXT ENIGMA_DIR="${ENIGMA_DIR}") if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND NOT WIN32) set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address") @@ -289,9 +303,9 @@ target_link_libraries(${EXE} PRIVATE Qt5::Core Qt5::Widgets Qt5::Gui Qt5::PrintS # LibProto # Arrangement of these is important: shared depends on proto and emake depends on all of them # We need to cache the library names first so we can build on top of them -add_subdirectory(Submodules/enigma-dev/shared/protos) -add_subdirectory(Submodules/enigma-dev/shared) -add_subdirectory(Submodules/enigma-dev/CommandLine/libEGM) +add_subdirectory(${ENIGMA_DIR}/shared/protos) +add_subdirectory(${ENIGMA_DIR}/shared) +add_subdirectory(${ENIGMA_DIR}/CommandLine/libEGM) add_dependencies(${EXE} ${LIB_EGM}) target_link_libraries(${EXE} PRIVATE ${LIB_EGM} ${LIB_PROTO} ${SHARED_LIB}) @@ -331,8 +345,8 @@ if(MSVC) endif() if (RGM_BUILD_EMAKE) - add_subdirectory(Submodules/enigma-dev/CompilerSource) - add_subdirectory(Submodules/enigma-dev/CommandLine/emake) + add_subdirectory(${ENIGMA_DIR}/CompilerSource) + add_subdirectory(${ENIGMA_DIR}/CommandLine/emake) add_dependencies(${EXE} ${CLI_TARGET}) endif() @@ -341,12 +355,12 @@ add_custom_command( POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/Submodules/enigma-dev/CommandLine/emake/${CLI_TARGET}${CMAKE_EXECUTABLE_SUFFIX} - ${CMAKE_BINARY_DIR}/Submodules/enigma-dev/CommandLine/libEGM/${CMAKE_SHARED_LIBRARY_PREFIX}${LIB_EGM}${CMAKE_DEBUG_POSTFIX}${CMAKE_SHARED_LIBRARY_SUFFIX} - ${CMAKE_BINARY_DIR}/Submodules/enigma-dev/CompilerSource/${CMAKE_SHARED_LIBRARY_PREFIX}${COMPILER_LIB}${CMAKE_DEBUG_POSTFIX}${CMAKE_SHARED_LIBRARY_SUFFIX} - ${CMAKE_BINARY_DIR}/Submodules/enigma-dev/shared/protos/${CMAKE_SHARED_LIBRARY_PREFIX}${LIB_PROTO}${CMAKE_DEBUG_POSTFIX}${CMAKE_SHARED_LIBRARY_SUFFIX} - ${CMAKE_BINARY_DIR}/Submodules/enigma-dev/shared/${CMAKE_SHARED_LIBRARY_PREFIX}${SHARED_LIB}${CMAKE_DEBUG_POSTFIX}${CMAKE_SHARED_LIBRARY_SUFFIX} - ${CMAKE_CURRENT_SOURCE_DIR}/Submodules/enigma-dev - COMMENT "Copying exes to ENIGMA's root directory" + ${CMAKE_BINARY_DIR}/Submodules/enigma-dev/CommandLine/libEGM/${CMAKE_SHARED_LIBRARY_PREFIX}${LIB_EGM}$,${CMAKE_DEBUG_POSTFIX},$,${CMAKE_MINSIZEREL_POSTFIX},$,${CMAKE_RELWITHDEBINFO_POSTFIX},>>>${CMAKE_SHARED_LIBRARY_SUFFIX} + ${CMAKE_BINARY_DIR}/Submodules/enigma-dev/CompilerSource/${CMAKE_SHARED_LIBRARY_PREFIX}${COMPILER_LIB}$,${CMAKE_DEBUG_POSTFIX},$,${CMAKE_MINSIZEREL_POSTFIX},$,${CMAKE_RELWITHDEBINFO_POSTFIX},>>>${CMAKE_SHARED_LIBRARY_SUFFIX} + ${CMAKE_BINARY_DIR}/Submodules/enigma-dev/shared/protos/${CMAKE_SHARED_LIBRARY_PREFIX}${LIB_PROTO}$,${CMAKE_DEBUG_POSTFIX},$,${CMAKE_MINSIZEREL_POSTFIX},$,${CMAKE_RELWITHDEBINFO_POSTFIX},>>>${CMAKE_SHARED_LIBRARY_SUFFIX} + ${CMAKE_BINARY_DIR}/Submodules/enigma-dev/shared/${CMAKE_SHARED_LIBRARY_PREFIX}${SHARED_LIB}$,${CMAKE_DEBUG_POSTFIX},$,${CMAKE_MINSIZEREL_POSTFIX},$,${CMAKE_RELWITHDEBINFO_POSTFIX},>>>${CMAKE_SHARED_LIBRARY_SUFFIX} + ${ENIGMA_DIR} + COMMENT "Copying exes to ${ENIGMA_DIR}" ) install(TARGETS ${EXE} RUNTIME DESTINATION .) diff --git a/MainWindow.cpp b/MainWindow.cpp index 7293e3c4a..f46eaef8a 100644 --- a/MainWindow.cpp +++ b/MainWindow.cpp @@ -36,8 +36,7 @@ #undef GetMessage -QList MainWindow::EnigmaSearchPaths = {"/opt/enigma-dev/", "/usr/lib/enigma-dev", - QDir::currentPath() + "/../Submodules/enigma-dev/"}; +QList MainWindow::EnigmaSearchPaths = {"/opt/enigma-dev/", "/usr/lib/enigma-dev", ENIGMA_DIR}; QFileInfo MainWindow::EnigmaRoot = MainWindow::getEnigmaRoot(); QList MainWindow::systemCache; MainWindow *MainWindow::_instance = nullptr; @@ -177,7 +176,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), _ui(new Ui::MainW RGMPlugin *pluginServer = new ServerPlugin(*this); auto outputTextBrowser = this->_ui->outputTextBrowser; connect(pluginServer, &RGMPlugin::LogOutput, outputTextBrowser, &QTextBrowser::append); - connect(pluginServer, &RGMPlugin::CompileStatusChanged, this, &MainWindow::on_compileStatus_changed); + connect(pluginServer, &RGMPlugin::CompileStatusChanged, this, &MainWindow::on_compileStatusChanged); connect(this, &MainWindow::CurrentConfigChanged, pluginServer, &RGMPlugin::SetCurrentConfig); connect(_ui->actionRun, &QAction::triggered, pluginServer, &RGMPlugin::Run); connect(_ui->actionDebug, &QAction::triggered, pluginServer, &RGMPlugin::Debug); @@ -730,7 +729,7 @@ void MainWindow::on_treeView_customContextMenuRequested(const QPoint &pos) { _ui->menuEdit->exec(_ui->treeView->mapToGlobal(pos)); } -void MainWindow::on_compileStatus_changed(bool finished) { +void MainWindow::on_compileStatusChanged(bool finished) { _ui->outputDockWidget->show(); _ui->actionRun->setEnabled(finished); _ui->actionDebug->setEnabled(finished); diff --git a/MainWindow.h b/MainWindow.h index c9c1606b3..dbe9862f7 100644 --- a/MainWindow.h +++ b/MainWindow.h @@ -21,6 +21,10 @@ class MainWindow; #include #include +#ifndef ENIGMA_DIR +#error "ENIGMA_DIR not defined" +#endif + namespace Ui { class MainWindow; } @@ -111,7 +115,7 @@ class MainWindow : public QMainWindow { void on_treeView_doubleClicked(const QModelIndex &index); void on_treeView_customContextMenuRequested(const QPoint &pos); - void on_compileStatus_changed(bool finished); + void on_compileStatusChanged(bool finished); private: void closeEvent(QCloseEvent *event) override; diff --git a/Submodules/nodeeditor b/Submodules/nodeeditor index f0b74fb52..6cb2126dd 160000 --- a/Submodules/nodeeditor +++ b/Submodules/nodeeditor @@ -1 +1 @@ -Subproject commit f0b74fb52038c0fb17ceb7d718fddba4bd079299 +Subproject commit 6cb2126dd662dfd3731a1a99bd7e05331dfaedc6 From 1ad871ab169d0f0db89e82650ea53bd0236d3533 Mon Sep 17 00:00:00 2001 From: Saif Kandil <74428638+k0T0z@users.noreply.github.com> Date: Wed, 4 Sep 2024 00:56:27 +0300 Subject: [PATCH 11/56] added the floating bar and few improvements Signed-off-by: Saif Kandil <74428638+k0T0z@users.noreply.github.com> --- Editors/VisualShaderEditor.cpp | 109 +++++++++++++++++++++------------ Editors/VisualShaderEditor.h | 27 +++++--- 2 files changed, 90 insertions(+), 46 deletions(-) diff --git a/Editors/VisualShaderEditor.cpp b/Editors/VisualShaderEditor.cpp index 389383f7e..a837a103c 100644 --- a/Editors/VisualShaderEditor.cpp +++ b/Editors/VisualShaderEditor.cpp @@ -39,8 +39,27 @@ using QtNodes::GraphicsView; using QtNodes::NodeRole; -VisualShaderEditor::VisualShaderEditor(MessageModel* model, QWidget* parent) : BaseEditor(model, parent), graph(nullptr), scene(nullptr), layout(nullptr), view(nullptr) { -// this->setWindowIcon(QIcon(":/resources/visual_shader.png")); +VisualShaderEditor::VisualShaderEditor(MessageModel* model, QWidget* parent) : BaseEditor(model, parent), + layout(nullptr), + layers_stack(nullptr), + scene_layer(nullptr), + scene_layer_layout(nullptr), + graph(nullptr), + scene(nullptr), + view(nullptr), + menu_bar(nullptr) { + // Create the main layout. + layout = new QHBoxLayout(this); + + // Create the layers widget. + layers_stack = new QStackedLayout(); + layers_stack->setStackingMode(QStackedLayout::StackAll); // See https://doc.qt.io/qt-5/qstackedlayout.html#stackingMode-prop + + // Create the scene layer. + scene_layer = new QWidget(); + + // Create the scene layer layout. + scene_layer_layout = new QHBoxLayout(scene_layer); graph = new VisualShaderGraph(); @@ -56,69 +75,81 @@ VisualShaderEditor::VisualShaderEditor(MessageModel* model, QWidget* parent) : B } scene = new BasicGraphicsScene(*graph); + scene->setOrientation(Qt::Horizontal); - scene->setOrientation(Qt::Vertical); + view = new GraphicsView(scene); - layout = new QHBoxLayout(this); - - view = new GraphicsView(scene); + scene_layer_layout->addWidget(view); - layout->addWidget(view); + // Set the scene layer layout. + scene_layer->setLayout(scene_layer_layout); - QGroupBox *groupBox = new QGroupBox("Orientation"); + // Add the scene layer to the layers widget. + layers_stack->addWidget(scene_layer); // Scene layer is added first so it has index 0. - QRadioButton *radio1 = new QRadioButton("Vertical"); - QRadioButton *radio2 = new QRadioButton("Horizontal"); + // Create the menu bar layer. + top_layer = new QWidget(); - QVBoxLayout *vbl = new QVBoxLayout; - vbl->addWidget(radio1); - vbl->addWidget(radio2); - vbl->addStretch(); - groupBox->setLayout(vbl); + // Create the menu bar layout. + menu_bar = new QHBoxLayout(top_layer); - QObject::connect(radio1, &QRadioButton::clicked, [this]() { - scene->setOrientation(Qt::Vertical); - }); + // Create the add node button. + add_node_button = new QPushButton("Add Node", top_layer); + menu_bar->addWidget(add_node_button); + QObject::connect(add_node_button, &QPushButton::clicked, this, &VisualShaderEditor::create_node); - QObject::connect(radio2, &QRadioButton::clicked, [this]() { - scene->setOrientation(Qt::Horizontal); - }); + // Create the preview shader button. + preview_shader_button = new QPushButton("Preview Shader", top_layer); + menu_bar->addWidget(preview_shader_button); - radio1->setChecked(true); + // Align the buttons to the top-left corner. + menu_bar->setAlignment(Qt::AlignTop | Qt::AlignLeft); - layout->addWidget(groupBox); + // Set the top layer layout. + top_layer->setLayout(menu_bar); - // Setup context menu for creating new nodes. - view->setContextMenuPolicy(Qt::ActionsContextMenu); - QAction createNodeAction(QStringLiteral("Create Node"), view); - QObject::connect(&createNodeAction, &QAction::triggered, [&]() { - // Mouse position in scene coordinates. - QPointF posView = view->mapToScene(view->mapFromGlobal(QCursor::pos())); + // Add the top layer to the layers widget and set it as the current widget. + layers_stack->addWidget(top_layer); // Top layer is added second so it has index 1. - NodeId const newId = graph->addNode(); - graph->setNodeData(newId, NodeRole::Position, posView); - }); - view->insertAction(view->actions().front(), &createNodeAction); + layout->addLayout(layers_stack); + // Set the window title and icon. + this->setWindowTitle("Visual Shader Editor"); + // this->setWindowIcon(QIcon(":/resources/visual_shader.png")); this->setLayout(layout); } VisualShaderEditor::~VisualShaderEditor() { + if (preview_shader_button) delete preview_shader_button; + if (add_node_button) delete add_node_button; + if (menu_bar) delete menu_bar; + if (top_layer) delete top_layer; if (view) delete view; - if (layout) delete layout; if (scene) delete scene; if (graph) delete graph; + if (scene_layer_layout) delete scene_layer_layout; + if (scene_layer) delete scene_layer; + if (layers_stack) delete layers_stack; + if (layout) delete layout; } -VisualShaderGraph::VisualShaderGraph() - : _nextNodeId{0} -{} +void VisualShaderEditor::create_node() { + std::cout << "Creating node" << std::endl; + VisualShaderEditor::add_node(); +} -VisualShaderGraph::~VisualShaderGraph() -{ +void VisualShaderEditor::add_node() { } +VisualShaderGraph::VisualShaderGraph() : _nextNodeId{0} { + +} + +VisualShaderGraph::~VisualShaderGraph() { + +} + std::unordered_set VisualShaderGraph::allNodeIds() const { return _nodeIds; diff --git a/Editors/VisualShaderEditor.h b/Editors/VisualShaderEditor.h index c85db390f..04eef438e 100644 --- a/Editors/VisualShaderEditor.h +++ b/Editors/VisualShaderEditor.h @@ -32,6 +32,8 @@ #include #include #include +#include +#include #include #include @@ -65,13 +67,25 @@ class VisualShaderEditor : public BaseEditor { VisualShaderEditor(MessageModel* model, QWidget* parent = nullptr); ~VisualShaderEditor() override; - private: - VisualShaderGraph* graph; + void create_node(); - BasicGraphicsScene* scene; + void add_node(); + + private: QHBoxLayout* layout; + QStackedLayout* layers_stack; + QWidget* scene_layer; // Layer having the scene. + QHBoxLayout* scene_layer_layout; + VisualShaderGraph* graph; + BasicGraphicsScene* scene; GraphicsView* view; + + QWidget* top_layer; // Layer having the menu bar. + QHBoxLayout* menu_bar; + + QPushButton* add_node_button; + QPushButton* preview_shader_button; }; /** @@ -81,6 +95,7 @@ class VisualShaderEditor : public BaseEditor { class VisualShaderGraph : public QtNodes::AbstractGraphModel { Q_OBJECT + public: struct NodeGeometryData { @@ -88,7 +103,6 @@ class VisualShaderGraph : public QtNodes::AbstractGraphModel QPointF pos; }; -public: VisualShaderGraph(); ~VisualShaderGraph() override; @@ -143,9 +157,6 @@ class VisualShaderGraph : public QtNodes::AbstractGraphModel */ void loadNode(QJsonObject const &nodeJson) override; -private: - NodeId newNodeId() override { return _nextNodeId++; } - private: std::unordered_set _nodeIds; @@ -162,6 +173,8 @@ class VisualShaderGraph : public QtNodes::AbstractGraphModel /// A convenience variable needed for generating unique node ids. NodeId _nextNodeId; + + NodeId newNodeId() override { return _nextNodeId++; } }; #endif // ENIGMA_VISUAL_SHADER_EDITOR_H From f7f38324b6964ea44e169843f6d599ae80567425 Mon Sep 17 00:00:00 2001 From: Saif Kandil <74428638+k0T0z@users.noreply.github.com> Date: Wed, 4 Sep 2024 16:18:59 +0300 Subject: [PATCH 12/56] implemented the tree nodes dialog and switched to V layout instead of a Stacked layout as I was unable to pass the mouse events to buttons and the scene at the same time Signed-off-by: Saif Kandil <74428638+k0T0z@users.noreply.github.com> --- Editors/VisualShaderEditor.cpp | 134 ++++++++++++++++++++++++++++----- Editors/VisualShaderEditor.h | 44 ++++++++++- 2 files changed, 159 insertions(+), 19 deletions(-) diff --git a/Editors/VisualShaderEditor.cpp b/Editors/VisualShaderEditor.cpp index a837a103c..2af97476b 100644 --- a/Editors/VisualShaderEditor.cpp +++ b/Editors/VisualShaderEditor.cpp @@ -39,27 +39,47 @@ using QtNodes::GraphicsView; using QtNodes::NodeRole; +/*************************************/ +/* VisualShaderEditor */ +/*************************************/ + VisualShaderEditor::VisualShaderEditor(MessageModel* model, QWidget* parent) : BaseEditor(model, parent), layout(nullptr), - layers_stack(nullptr), + layers_layout(nullptr), scene_layer(nullptr), scene_layer_layout(nullptr), graph(nullptr), scene(nullptr), view(nullptr), - menu_bar(nullptr) { + menu_bar(nullptr), + create_node_button(nullptr), + preview_shader_button(nullptr), + create_node_dialog(nullptr) { // Create the main layout. layout = new QHBoxLayout(this); + layout->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom + layout->setSizeConstraint(QLayout::SetNoConstraint); + layout->setSpacing(0); + layout->setAlignment(Qt::AlignVCenter | Qt::AlignHCenter); // Create the layers widget. - layers_stack = new QStackedLayout(); - layers_stack->setStackingMode(QStackedLayout::StackAll); // See https://doc.qt.io/qt-5/qstackedlayout.html#stackingMode-prop + layers_layout = new QVBoxLayout(); + layers_layout->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom + layers_layout->setSizeConstraint(QLayout::SetNoConstraint); + layers_layout->setSpacing(2); + layers_layout->setAlignment(Qt::AlignTop | Qt::AlignLeft); // Create the scene layer. scene_layer = new QWidget(); + scene_layer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + scene_layer->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom // Create the scene layer layout. scene_layer_layout = new QHBoxLayout(scene_layer); + scene_layer_layout->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom + scene_layer_layout->setSpacing(0); + scene_layer_layout->setSizeConstraint(QLayout::SetNoConstraint); + scene_layer_layout->setAlignment(Qt::AlignVCenter | Qt::AlignHCenter); graph = new VisualShaderGraph(); @@ -78,40 +98,56 @@ VisualShaderEditor::VisualShaderEditor(MessageModel* model, QWidget* parent) : B scene->setOrientation(Qt::Horizontal); view = new GraphicsView(scene); + view->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom + view->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); scene_layer_layout->addWidget(view); // Set the scene layer layout. scene_layer->setLayout(scene_layer_layout); - // Add the scene layer to the layers widget. - layers_stack->addWidget(scene_layer); // Scene layer is added first so it has index 0. - // Create the menu bar layer. top_layer = new QWidget(); + top_layer->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom + top_layer->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); // Create the menu bar layout. menu_bar = new QHBoxLayout(top_layer); + menu_bar->setContentsMargins(10, 10, 10, 10); // Left, top, right, bottom + menu_bar->setSpacing(5); // Adjust spacing as needed + menu_bar->setAlignment(Qt::AlignTop | Qt::AlignLeft); + menu_bar->setSizeConstraint(QLayout::SetMinimumSize); // Create the add node button. - add_node_button = new QPushButton("Add Node", top_layer); - menu_bar->addWidget(add_node_button); - QObject::connect(add_node_button, &QPushButton::clicked, this, &VisualShaderEditor::create_node); + create_node_button = new QPushButton("Add Node", top_layer); + create_node_button->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + create_node_button->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom + menu_bar->addWidget(create_node_button); + QObject::connect(create_node_button, &QPushButton::clicked, this, &VisualShaderEditor::show_create_node_dialog); + create_node_button->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); // Create the preview shader button. preview_shader_button = new QPushButton("Preview Shader", top_layer); + preview_shader_button->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + preview_shader_button->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom menu_bar->addWidget(preview_shader_button); - - // Align the buttons to the top-left corner. - menu_bar->setAlignment(Qt::AlignTop | Qt::AlignLeft); + preview_shader_button->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); // Set the top layer layout. top_layer->setLayout(menu_bar); // Add the top layer to the layers widget and set it as the current widget. - layers_stack->addWidget(top_layer); // Top layer is added second so it has index 1. + layers_layout->addWidget(top_layer); + + // Add the scene layer to the layers widget. + layers_layout->addWidget(scene_layer); - layout->addLayout(layers_stack); + layout->addLayout(layers_layout); + + // Create the create node dialog. + create_node_dialog = new CreateNodeDialog(this); + + this->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom // Set the window title and icon. this->setWindowTitle("Visual Shader Editor"); @@ -120,8 +156,10 @@ VisualShaderEditor::VisualShaderEditor(MessageModel* model, QWidget* parent) : B } VisualShaderEditor::~VisualShaderEditor() { + // TODO: We don't need to delete the pointers as they are destroyed when the parent is destroyed. + if (create_node_dialog) delete create_node_dialog; if (preview_shader_button) delete preview_shader_button; - if (add_node_button) delete add_node_button; + if (create_node_button) delete create_node_button; if (menu_bar) delete menu_bar; if (top_layer) delete top_layer; if (view) delete view; @@ -129,7 +167,7 @@ VisualShaderEditor::~VisualShaderEditor() { if (graph) delete graph; if (scene_layer_layout) delete scene_layer_layout; if (scene_layer) delete scene_layer; - if (layers_stack) delete layers_stack; + if (layers_layout) delete layers_layout; if (layout) delete layout; } @@ -142,6 +180,68 @@ void VisualShaderEditor::add_node() { } +void VisualShaderEditor::show_create_node_dialog(const bool& custom_mouse_pos) { + int status = create_node_dialog->exec(); + switch (status) { + case QDialog::Accepted: + std::cout << "Create node dialog accepted" << std::endl; + break; + case QDialog::Rejected: + std::cout << "Create node dialog rejected" << std::endl; + break; + default: + std::cout << "Create node dialog unknown status" << std::endl; + break; + } +} + +/*************************************/ +/* CreateNodeDialog */ +/*************************************/ + +CreateNodeDialog::CreateNodeDialog(QWidget* parent) : QDialog(parent), + layout(nullptr), + buttons_layout(nullptr), + create_button(nullptr), + cancel_button(nullptr) { + layout = new QVBoxLayout(this); + + buttons_layout = new QHBoxLayout(); + + create_button = new QPushButton("Create", this); + QObject::connect(create_button, &QPushButton::clicked, this, &CreateNodeDialog::on_CreateButtonTriggered); + + cancel_button = new QPushButton("Cancel", this); + QObject::connect(cancel_button, &QPushButton::clicked, this, &CreateNodeDialog::on_CancelButtonTriggered); + + buttons_layout->addWidget(create_button); + buttons_layout->addWidget(cancel_button); + + layout->addLayout(buttons_layout); + + this->setWindowTitle("Create Shader Node"); + this->setLayout(layout); +} + +CreateNodeDialog::~CreateNodeDialog() { + if (cancel_button) delete cancel_button; + if (create_button) delete create_button; + if (buttons_layout) delete buttons_layout; + if (layout) delete layout; +} + +void CreateNodeDialog::on_CreateButtonTriggered() { + this->accept(); +} + +void CreateNodeDialog::on_CancelButtonTriggered() { + this->reject(); +} + +/*************************************/ +/* VisualShaderGraph */ +/*************************************/ + VisualShaderGraph::VisualShaderGraph() : _nextNodeId{0} { } diff --git a/Editors/VisualShaderEditor.h b/Editors/VisualShaderEditor.h index 04eef438e..e87f82cff 100644 --- a/Editors/VisualShaderEditor.h +++ b/Editors/VisualShaderEditor.h @@ -34,6 +34,9 @@ #include #include #include +#include +#include +#include #include #include @@ -59,6 +62,11 @@ using QtNodes::BasicGraphicsScene; using QtNodes::GraphicsView; class VisualShaderGraph; +class CreateNodeDialog; + +/*************************************/ +/* VisualShaderEditor */ +/*************************************/ class VisualShaderEditor : public BaseEditor { Q_OBJECT @@ -71,9 +79,11 @@ class VisualShaderEditor : public BaseEditor { void add_node(); + void show_create_node_dialog(const bool& custom_mouse_pos = false); + private: QHBoxLayout* layout; - QStackedLayout* layers_stack; + QVBoxLayout* layers_layout; QWidget* scene_layer; // Layer having the scene. QHBoxLayout* scene_layer_layout; @@ -84,10 +94,40 @@ class VisualShaderEditor : public BaseEditor { QWidget* top_layer; // Layer having the menu bar. QHBoxLayout* menu_bar; - QPushButton* add_node_button; + QPushButton* create_node_button; QPushButton* preview_shader_button; + + // Dialogs + CreateNodeDialog* create_node_dialog; +}; + +/*************************************/ +/* CreateNodeDialog */ +/*************************************/ + +class CreateNodeDialog : public QDialog { + Q_OBJECT + + public: + CreateNodeDialog(QWidget* parent = nullptr); + ~CreateNodeDialog(); + + private slots: + void on_CreateButtonTriggered(); + void on_CancelButtonTriggered(); + + private: + QVBoxLayout* layout; + + QHBoxLayout* buttons_layout; + QPushButton* create_button; + QPushButton* cancel_button; }; +/*************************************/ +/* VisualShaderGraph */ +/*************************************/ + /** * The class implements a bare minimum required to demonstrate a model-based * graph. From 06d3ea595f3f79d57d169e4773d8f44deafe3aed Mon Sep 17 00:00:00 2001 From: Saif Kandil <74428638+k0T0z@users.noreply.github.com> Date: Thu, 5 Sep 2024 23:45:39 +0300 Subject: [PATCH 13/56] added available nodes to add dialogs functionalities and deleted the V layout to switch for a tree layout (the Add Node button is now under the scene tree which is more convenient, yaay) Signed-off-by: Saif Kandil <74428638+k0T0z@users.noreply.github.com> --- Editors/VisualShaderEditor.cpp | 181 ++++++++++++++++++++++++++++----- Editors/VisualShaderEditor.h | 55 +++++++++- 2 files changed, 207 insertions(+), 29 deletions(-) diff --git a/Editors/VisualShaderEditor.cpp b/Editors/VisualShaderEditor.cpp index 2af97476b..b108836a6 100644 --- a/Editors/VisualShaderEditor.cpp +++ b/Editors/VisualShaderEditor.cpp @@ -32,6 +32,8 @@ #include #include +#include + #include #include "VisualShader.pb.h" @@ -45,12 +47,12 @@ using QtNodes::NodeRole; VisualShaderEditor::VisualShaderEditor(MessageModel* model, QWidget* parent) : BaseEditor(model, parent), layout(nullptr), - layers_layout(nullptr), - scene_layer(nullptr), scene_layer_layout(nullptr), + scene_layer(nullptr), graph(nullptr), scene(nullptr), view(nullptr), + top_layer(nullptr), menu_bar(nullptr), create_node_button(nullptr), preview_shader_button(nullptr), @@ -62,12 +64,7 @@ VisualShaderEditor::VisualShaderEditor(MessageModel* model, QWidget* parent) : B layout->setSpacing(0); layout->setAlignment(Qt::AlignVCenter | Qt::AlignHCenter); - // Create the layers widget. - layers_layout = new QVBoxLayout(); - layers_layout->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom - layers_layout->setSizeConstraint(QLayout::SetNoConstraint); - layers_layout->setSpacing(2); - layers_layout->setAlignment(Qt::AlignTop | Qt::AlignLeft); + //////////////// End of Header //////////////// // Create the scene layer. scene_layer = new QWidget(); @@ -106,15 +103,15 @@ VisualShaderEditor::VisualShaderEditor(MessageModel* model, QWidget* parent) : B // Set the scene layer layout. scene_layer->setLayout(scene_layer_layout); - // Create the menu bar layer. - top_layer = new QWidget(); + // Create the menu bar layer on top of the scene layer. + top_layer = new QWidget(view); top_layer->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom top_layer->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); // Create the menu bar layout. menu_bar = new QHBoxLayout(top_layer); menu_bar->setContentsMargins(10, 10, 10, 10); // Left, top, right, bottom - menu_bar->setSpacing(5); // Adjust spacing as needed + menu_bar->setSpacing(5); // Adjust spacing as needed menu_bar->setAlignment(Qt::AlignTop | Qt::AlignLeft); menu_bar->setSizeConstraint(QLayout::SetMinimumSize); @@ -124,28 +121,68 @@ VisualShaderEditor::VisualShaderEditor(MessageModel* model, QWidget* parent) : B create_node_button->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom menu_bar->addWidget(create_node_button); QObject::connect(create_node_button, &QPushButton::clicked, this, &VisualShaderEditor::show_create_node_dialog); - create_node_button->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); // Create the preview shader button. preview_shader_button = new QPushButton("Preview Shader", top_layer); preview_shader_button->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); preview_shader_button->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom menu_bar->addWidget(preview_shader_button); - preview_shader_button->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); // Set the top layer layout. top_layer->setLayout(menu_bar); - // Add the top layer to the layers widget and set it as the current widget. - layers_layout->addWidget(top_layer); - - // Add the scene layer to the layers widget. - layers_layout->addWidget(scene_layer); + // Add the scene layer to the main layout. + layout->addWidget(scene_layer); - layout->addLayout(layers_layout); + //////////////////////////////////// + // CreateNodeDialog Nodes Tree + //////////////////////////////////// - // Create the create node dialog. + // Create the create node dialog under the main layout. create_node_dialog = new CreateNodeDialog(this); + create_node_dialog->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom + create_node_dialog->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + + ////////////////////////////////////////// + // CreateNodeDialog Nodes Tree Children + ////////////////////////////////////////// + + const VisualShaderEditor::CreateNodeDialogNodesTreeItem* items {VisualShaderEditor::create_node_dialog_nodes_tree_items}; + + // Map to store category items + std::unordered_map category_path_map; + + int i{0}; + + while (items[i].return_type != VisualShaderNode::PortType::PORT_TYPE_ENUM_SIZE) { + const CreateNodeDialogNodesTreeItem& item {items[i]}; + + // Parse the category string into a vector of strings + std::vector categories {pasre_node_category_path(item.category_path)}; + QTreeWidgetItem* parent {nullptr}; // Start from the root + + std::string current_category_path; + // Create/find each level of categories + for (const std::string& category : categories) { + if (!current_category_path.empty()) { + current_category_path += "/"; + } + + current_category_path += category; + + parent = find_or_create_category_item(parent, category, current_category_path, create_node_dialog->get_nodes_tree(), category_path_map); + } + + // Now add the item to its corresponding parent category + QTreeWidgetItem *node_item = new QTreeWidgetItem(parent); + node_item->setText(0, QString::fromStdString(item.name)); + + i++; + } + + //////////////// Start of Footer //////////////// + + graph->set_visual_shader_editor(this); this->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom @@ -165,12 +202,19 @@ VisualShaderEditor::~VisualShaderEditor() { if (view) delete view; if (scene) delete scene; if (graph) delete graph; - if (scene_layer_layout) delete scene_layer_layout; if (scene_layer) delete scene_layer; - if (layers_layout) delete layers_layout; + if (scene_layer_layout) delete scene_layer_layout; if (layout) delete layout; } +const VisualShaderEditor::CreateNodeDialogNodesTreeItem VisualShaderEditor::create_node_dialog_nodes_tree_items[] = { + {"Color", "Input/All", "VisualShaderNodeInput", "Color", { "color" }, VisualShaderNode::PortType::PORT_TYPE_VECTOR_4D}, + {"Time", "Input/All", "VisualShaderNodeInput", "Time", { "time" }, VisualShaderNode::PortType::PORT_TYPE_SCALAR}, + {"UV", "Input/All", "VisualShaderNodeInput", "UV", { "uv" }, VisualShaderNode::PortType::PORT_TYPE_VECTOR_2D}, + + {"", "", "", "", {}, VisualShaderNode::PortType::PORT_TYPE_ENUM_SIZE}, // End of list. +}; + void VisualShaderEditor::create_node() { std::cout << "Creating node" << std::endl; VisualShaderEditor::add_node(); @@ -195,31 +239,117 @@ void VisualShaderEditor::show_create_node_dialog(const bool& custom_mouse_pos) { } } +std::vector VisualShaderEditor::pasre_node_category_path(const std::string& node_category_path) { + std::vector tokens; + std::stringstream ss(node_category_path); + std::string token; + while (std::getline(ss, token, '/')) { + tokens.push_back(token); + } + return tokens; +} + +QTreeWidgetItem* VisualShaderEditor::find_or_create_category_item(QTreeWidgetItem* parent, const std::string& category, const std::string& category_path, QTreeWidget* create_node_dialog_nodes_tree, std::unordered_map& category_path_map) { + // Check if category already exists under parent + if (category_path_map.find(category_path) != category_path_map.end()) { + return category_path_map[category_path]; + } + + // Create a new QTreeWidgetItem + QTreeWidgetItem* new_item; + + if (parent) { + new_item = new QTreeWidgetItem(parent); + } else { + new_item = new QTreeWidgetItem(create_node_dialog_nodes_tree); + } + + new_item->setText(0, QString::fromStdString(category)); + + // Add the new category to the map + category_path_map[category_path] = new_item; + + return new_item; +} + /*************************************/ /* CreateNodeDialog */ /*************************************/ CreateNodeDialog::CreateNodeDialog(QWidget* parent) : QDialog(parent), layout(nullptr), + create_node_dialog_nodes_tree_layout(nullptr), + create_node_dialog_nodes_tree(nullptr), + create_node_dialog_nodes_description(nullptr), buttons_layout(nullptr), create_button(nullptr), cancel_button(nullptr) { layout = new QVBoxLayout(this); + layout->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom + layout->setSizeConstraint(QLayout::SetNoConstraint); + layout->setSpacing(0); + layout->setAlignment(Qt::AlignVCenter | Qt::AlignHCenter); + //////////////// End of Header //////////////// + + // Create the nodes tree layout. + create_node_dialog_nodes_tree_layout = new QVBoxLayout(); + create_node_dialog_nodes_tree_layout->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom + create_node_dialog_nodes_tree_layout->setSpacing(0); // Adjust spacing as needed + create_node_dialog_nodes_tree_layout->setAlignment(Qt::AlignVCenter | Qt::AlignHCenter); + create_node_dialog_nodes_tree_layout->setSizeConstraint(QLayout::SetMinimumSize); + + // Add the nodes tree layout to the main layout. + layout->addLayout(create_node_dialog_nodes_tree_layout); + + // Create the nodes tree. + create_node_dialog_nodes_tree = new QTreeWidget(); + create_node_dialog_nodes_tree->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + create_node_dialog_nodes_tree->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom + create_node_dialog_nodes_tree->setColumnCount(1); + create_node_dialog_nodes_tree->setHeaderHidden(true); + + // Add the nodes tree to the nodes tree layout. + create_node_dialog_nodes_tree_layout->addWidget(create_node_dialog_nodes_tree, 2); // 2x the size of the nodes description. + + // Create the nodes description. + create_node_dialog_nodes_description = new QTextEdit(); + create_node_dialog_nodes_description->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + create_node_dialog_nodes_description->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom + create_node_dialog_nodes_description->setReadOnly(true); + create_node_dialog_nodes_description->setAlignment(Qt::AlignTop | Qt::AlignLeft); + + // Add the nodes description to the nodes tree layout. + create_node_dialog_nodes_tree_layout->addWidget(create_node_dialog_nodes_description, 1); + + // Create the buttons layout. buttons_layout = new QHBoxLayout(); + layout->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom + layout->setSizeConstraint(QLayout::SetNoConstraint); + layout->setSpacing(0); + layout->setAlignment(Qt::AlignVCenter | Qt::AlignHCenter); - create_button = new QPushButton("Create", this); + create_button = new QPushButton("Create"); QObject::connect(create_button, &QPushButton::clicked, this, &CreateNodeDialog::on_CreateButtonTriggered); + create_button->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + create_button->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom - cancel_button = new QPushButton("Cancel", this); + cancel_button = new QPushButton("Cancel"); QObject::connect(cancel_button, &QPushButton::clicked, this, &CreateNodeDialog::on_CancelButtonTriggered); + cancel_button->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + cancel_button->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom + // Add the buttons to the buttons layout. buttons_layout->addWidget(create_button); buttons_layout->addWidget(cancel_button); + // Add the buttons layout to the main layout. layout->addLayout(buttons_layout); + //////////////// Start of Footer //////////////// + this->setWindowTitle("Create Shader Node"); + this->setLayout(layout); } @@ -227,6 +357,9 @@ CreateNodeDialog::~CreateNodeDialog() { if (cancel_button) delete cancel_button; if (create_button) delete create_button; if (buttons_layout) delete buttons_layout; + if (create_node_dialog_nodes_tree) delete create_node_dialog_nodes_tree; + if (create_node_dialog_nodes_description) delete create_node_dialog_nodes_description; + if (create_node_dialog_nodes_tree_layout) delete create_node_dialog_nodes_tree_layout; if (layout) delete layout; } diff --git a/Editors/VisualShaderEditor.h b/Editors/VisualShaderEditor.h index e87f82cff..c7a0b1887 100644 --- a/Editors/VisualShaderEditor.h +++ b/Editors/VisualShaderEditor.h @@ -33,10 +33,13 @@ #include #include #include -#include #include #include -#include +#include +#include + +#include +#include #include #include @@ -68,6 +71,8 @@ class CreateNodeDialog; /* VisualShaderEditor */ /*************************************/ +// Add const to any function that does not modify the object. + class VisualShaderEditor : public BaseEditor { Q_OBJECT @@ -81,12 +86,14 @@ class VisualShaderEditor : public BaseEditor { void show_create_node_dialog(const bool& custom_mouse_pos = false); + std::vector pasre_node_category_path(const std::string& node_category_path); + QTreeWidgetItem* find_or_create_category_item(QTreeWidgetItem* parent, const std::string& category, const std::string& category_path, QTreeWidget* create_node_dialog_nodes_tree, std::unordered_map& category_path_map); + private: QHBoxLayout* layout; - QVBoxLayout* layers_layout; - QWidget* scene_layer; // Layer having the scene. QHBoxLayout* scene_layer_layout; + QWidget* scene_layer; // Layer having the scene. VisualShaderGraph* graph; BasicGraphicsScene* scene; GraphicsView* view; @@ -97,7 +104,34 @@ class VisualShaderEditor : public BaseEditor { QPushButton* create_node_button; QPushButton* preview_shader_button; - // Dialogs + //////////////////////////////////// + // CreateNodeDialog Nodes Tree + //////////////////////////////////// + + struct CreateNodeDialogNodesTreeItem { + std::string name; + std::string category_path; + std::string type; + std::string description; + std::vector ops; + VisualShaderNode::PortType return_type; + + CreateNodeDialogNodesTreeItem(const std::string& name = std::string(), + const std::string& category_path = std::string(), + const std::string& type = std::string(), + const std::string& description = std::string(), + const std::vector& ops = std::vector(), + const VisualShaderNode::PortType& return_type = VisualShaderNode::PortType::PORT_TYPE_ENUM_SIZE) : name(name), + category_path(category_path), + type(type), + description(description), + ops(ops), + return_type(return_type) {} + + }; + + static const VisualShaderEditor::CreateNodeDialogNodesTreeItem create_node_dialog_nodes_tree_items[]; + CreateNodeDialog* create_node_dialog; }; @@ -112,6 +146,8 @@ class CreateNodeDialog : public QDialog { CreateNodeDialog(QWidget* parent = nullptr); ~CreateNodeDialog(); + QTreeWidget* get_nodes_tree() const { return create_node_dialog_nodes_tree; } + private slots: void on_CreateButtonTriggered(); void on_CancelButtonTriggered(); @@ -119,6 +155,11 @@ class CreateNodeDialog : public QDialog { private: QVBoxLayout* layout; + QVBoxLayout* create_node_dialog_nodes_tree_layout; + + QTreeWidget* create_node_dialog_nodes_tree; + QTextEdit* create_node_dialog_nodes_description; + QHBoxLayout* buttons_layout; QPushButton* create_button; QPushButton* cancel_button; @@ -197,6 +238,8 @@ class VisualShaderGraph : public QtNodes::AbstractGraphModel */ void loadNode(QJsonObject const &nodeJson) override; + void set_visual_shader_editor(VisualShaderEditor* visual_shader_editor) { this->visual_shader_editor = visual_shader_editor; } + private: std::unordered_set _nodeIds; @@ -215,6 +258,8 @@ class VisualShaderGraph : public QtNodes::AbstractGraphModel NodeId _nextNodeId; NodeId newNodeId() override { return _nextNodeId++; } + + VisualShaderEditor* visual_shader_editor; }; #endif // ENIGMA_VISUAL_SHADER_EDITOR_H From 073017cfeb3c44bb21f9faf669ddec8c376c3b8a Mon Sep 17 00:00:00 2001 From: Saif Kandil <74428638+k0T0z@users.noreply.github.com> Date: Sun, 8 Sep 2024 22:50:54 +0300 Subject: [PATCH 14/56] few improvements and bug fixes Signed-off-by: Saif Kandil <74428638+k0T0z@users.noreply.github.com> --- Editors/VisualShaderEditor.cpp | 346 +++++++++++++++++++++------------ Editors/VisualShaderEditor.h | 104 +++++----- Tests/CMakeLists.txt | 0 3 files changed, 278 insertions(+), 172 deletions(-) create mode 100644 Tests/CMakeLists.txt diff --git a/Editors/VisualShaderEditor.cpp b/Editors/VisualShaderEditor.cpp index b108836a6..90d4c4ea0 100644 --- a/Editors/VisualShaderEditor.cpp +++ b/Editors/VisualShaderEditor.cpp @@ -37,6 +37,8 @@ #include #include "VisualShader.pb.h" +#include "ResourceTransformations/VisualShader/visual_shader_nodes.h" +#include "ResourceTransformations/VisualShader/vs_noise_nodes.h" using QtNodes::GraphicsView; using QtNodes::NodeRole; @@ -46,6 +48,7 @@ using QtNodes::NodeRole; /*************************************/ VisualShaderEditor::VisualShaderEditor(MessageModel* model, QWidget* parent) : BaseEditor(model, parent), + visual_shader(nullptr), layout(nullptr), scene_layer_layout(nullptr), scene_layer(nullptr), @@ -56,7 +59,10 @@ VisualShaderEditor::VisualShaderEditor(MessageModel* model, QWidget* parent) : B menu_bar(nullptr), create_node_button(nullptr), preview_shader_button(nullptr), + create_node_action(nullptr), create_node_dialog(nullptr) { + visual_shader = new VisualShader(); + // Create the main layout. layout = new QHBoxLayout(this); layout->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom @@ -80,17 +86,6 @@ VisualShaderEditor::VisualShaderEditor(MessageModel* model, QWidget* parent) : B graph = new VisualShaderGraph(); - // Initialize and connect two nodes. - { - NodeId id1 = graph->addNode(); - graph->setNodeData(id1, NodeRole::Position, QPointF(0, 0)); - - NodeId id2 = graph->addNode(); - graph->setNodeData(id2, NodeRole::Position, QPointF(300, 300)); - - graph->addConnection(ConnectionId{id1, 0, id2, 0}); - } - scene = new BasicGraphicsScene(*graph); scene->setOrientation(Qt::Horizontal); @@ -100,6 +95,12 @@ VisualShaderEditor::VisualShaderEditor(MessageModel* model, QWidget* parent) : B scene_layer_layout->addWidget(view); + // Setup context menu for creating new nodes. + view->setContextMenuPolicy(Qt::ActionsContextMenu); + create_node_action = new QAction(QStringLiteral("Create Node"), view); + QObject::connect(create_node_action, &QAction::triggered, this, &VisualShaderEditor::on_create_node_action_triggered); + view->insertAction(view->actions().front(), create_node_action); + // Set the scene layer layout. scene_layer->setLayout(scene_layer_layout); @@ -115,12 +116,14 @@ VisualShaderEditor::VisualShaderEditor(MessageModel* model, QWidget* parent) : B menu_bar->setAlignment(Qt::AlignTop | Qt::AlignLeft); menu_bar->setSizeConstraint(QLayout::SetMinimumSize); - // Create the add node button. - create_node_button = new QPushButton("Add Node", top_layer); + // Create the create node button. + create_node_button = new QPushButton("Create Node", top_layer); create_node_button->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); create_node_button->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom menu_bar->addWidget(create_node_button); - QObject::connect(create_node_button, &QPushButton::clicked, this, &VisualShaderEditor::show_create_node_dialog); + QObject::connect(create_node_button, &QPushButton::pressed, this, &VisualShaderEditor::on_create_node_button_pressed); + + this->connect(this, &VisualShaderEditor::on_create_node_dialog_requested, this, &VisualShaderEditor::show_create_node_dialog); // Create the preview shader button. preview_shader_button = new QPushButton("Preview Shader", top_layer); @@ -154,7 +157,7 @@ VisualShaderEditor::VisualShaderEditor(MessageModel* model, QWidget* parent) : B int i{0}; - while (items[i].return_type != VisualShaderNode::PortType::PORT_TYPE_ENUM_SIZE) { + while (!items[i].type.empty()) { const CreateNodeDialogNodesTreeItem& item {items[i]}; // Parse the category string into a vector of strings @@ -176,12 +179,15 @@ VisualShaderEditor::VisualShaderEditor(MessageModel* model, QWidget* parent) : B // Now add the item to its corresponding parent category QTreeWidgetItem *node_item = new QTreeWidgetItem(parent); node_item->setText(0, QString::fromStdString(item.name)); + node_item->setData(0, Qt::UserRole, QString::fromStdString(item.type)); + node_item->setData(0, Qt::UserRole + 1, QString::fromStdString(item.description)); i++; } //////////////// Start of Footer //////////////// + graph->register_visual_shader(visual_shader); graph->set_visual_shader_editor(this); this->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom @@ -195,6 +201,7 @@ VisualShaderEditor::VisualShaderEditor(MessageModel* model, QWidget* parent) : B VisualShaderEditor::~VisualShaderEditor() { // TODO: We don't need to delete the pointers as they are destroyed when the parent is destroyed. if (create_node_dialog) delete create_node_dialog; + if (create_node_action) delete create_node_action; if (preview_shader_button) delete preview_shader_button; if (create_node_button) delete create_node_button; if (menu_bar) delete menu_bar; @@ -205,30 +212,145 @@ VisualShaderEditor::~VisualShaderEditor() { if (scene_layer) delete scene_layer; if (scene_layer_layout) delete scene_layer_layout; if (layout) delete layout; + if (visual_shader) delete visual_shader; } const VisualShaderEditor::CreateNodeDialogNodesTreeItem VisualShaderEditor::create_node_dialog_nodes_tree_items[] = { - {"Color", "Input/All", "VisualShaderNodeInput", "Color", { "color" }, VisualShaderNode::PortType::PORT_TYPE_VECTOR_4D}, - {"Time", "Input/All", "VisualShaderNodeInput", "Time", { "time" }, VisualShaderNode::PortType::PORT_TYPE_SCALAR}, - {"UV", "Input/All", "VisualShaderNodeInput", "UV", { "uv" }, VisualShaderNode::PortType::PORT_TYPE_VECTOR_2D}, - {"", "", "", "", {}, VisualShaderNode::PortType::PORT_TYPE_ENUM_SIZE}, // End of list. + // Input + + {"Input", "Input/Basic", "VisualShaderNodeInput", "Input parameter."}, + + {"ColorConstant", "Input/Basic", "VisualShaderNodeColorConstant", "Color constant."}, + {"BooleanConstant", "Input/Basic", "VisualShaderNodeBooleanConstant", "Boolean constant."}, + {"FloatConstant", "Input/Basic", "VisualShaderNodeFloatConstant", "Scalar floating-point constant."}, + {"IntConstant", "Input/Basic", "VisualShaderNodeIntConstant", "Scalar integer constant."}, + {"UIntConstant", "Input/Basic", "VisualShaderNodeUIntConstant", "Scalar unsigned integer constant."}, + {"Vector2Constant", "Input/Basic", "VisualShaderNodeVec2Constant", "2D vector constant."}, + {"Vector3Constant", "Input/Basic", "VisualShaderNodeVec3Constant", "3D vector constant."}, + {"Vector4Constant", "Input/Basic", "VisualShaderNodeVec4Constant", "4D vector constant."}, + + // Functions + + {"FloatFunc", "Functions/Scalar", "VisualShaderNodeFloatFunc", "Float function."}, + {"IntFunc", "Functions/Scalar", "VisualShaderNodeIntFunc", "Integer function."}, + {"UIntFunc", "Functions/Scalar", "VisualShaderNodeUIntFunc", "Unsigned integer function."}, + {"VectorFunc", "Functions/Vector", "VisualShaderNodeVectorFunc", "Vector function."}, + {"DerivativeFunc", "Functions/Others", "VisualShaderNodeDerivativeFunc", "Derivative function."}, + + // Operators + + {"FloatOp", "Operators/Scalar", "VisualShaderNodeFloatOp", "Float operator."}, + {"IntOp", "Operators/Scalar", "VisualShaderNodeIntOp", "Integer operator."}, + {"UIntOp", "Operators/Scalar", "VisualShaderNodeUIntOp", "Unsigned integer operator."}, + {"VectorOp", "Operators/Vector", "VisualShaderNodeVectorOp", "Vector operator."}, + {"VectorCompose", "Operators/Vector", "VisualShaderNodeVectorCompose", "Composes vector from scalars."}, + {"VectorDecompose", "Operators/Vector", "VisualShaderNodeVectorDecompose", "Decomposes vector to scalars."}, + + // Procedural + + {"ValueNoise", "Procedural/Noise", "VisualShaderNodeValueNoise", "Generates a simple, or Value, noise based on input 'UV'. The scale of the generated noise is controlled by input 'Scale'."}, + + // Utility + + {"Compare", "Utility/Logic", "VisualShaderNodeCompare", "Returns the boolean result of the comparison between two parameters."}, + {"If", "Utility/Logic", "VisualShaderNodeIf", "Returns the value of the 'True' or 'False' input based on the value of the 'Condition' input."}, + {"Switch", "Utility/Logic", "VisualShaderNodeSwitch", "Returns an associated scalar if the provided boolean value is true or false."}, + {"Is", "Utility/Logic", "VisualShaderNodeIs", "Returns the boolean result of the comparison between INF (or NaN) and a scalar parameter."}, + + {"", "", "", ""}, }; -void VisualShaderEditor::create_node() { - std::cout << "Creating node" << std::endl; - VisualShaderEditor::add_node(); +void VisualShaderEditor::create_node(const QPointF& pos) { + QTreeWidgetItem* selected_item = create_node_dialog->get_selected_item(); + + if (!selected_item) { + return; + } + + VisualShaderEditor::add_node(selected_item, pos); } -void VisualShaderEditor::add_node() { +void VisualShaderEditor::add_node(QTreeWidgetItem* selected_item, const QPointF& pos) { + std::string type = selected_item->data(0, Qt::UserRole).toString().toStdString(); + + if (type.empty()) { + return; + } + + // Instantiate the node based on the type + std::shared_ptr node; + + if (type == "VisualShaderNodeInput") { + node = std::make_shared(); + } else if (type == "VisualShaderNodeColorConstant") { + node = std::make_shared(); + } else if (type == "VisualShaderNodeBooleanConstant") { + node = std::make_shared(); + } else if (type == "VisualShaderNodeFloatConstant") { + node = std::make_shared(); + } else if (type == "VisualShaderNodeIntConstant") { + node = std::make_shared(); + } else if (type == "VisualShaderNodeUIntConstant") { + node = std::make_shared(); + } else if (type == "VisualShaderNodeVec2Constant") { + node = std::make_shared(); + } else if (type == "VisualShaderNodeVec3Constant") { + node = std::make_shared(); + } else if (type == "VisualShaderNodeVec4Constant") { + node = std::make_shared(); + } else if (type == "VisualShaderNodeFloatFunc") { + node = std::make_shared(); + } else if (type == "VisualShaderNodeIntFunc") { + node = std::make_shared(); + } else if (type == "VisualShaderNodeUIntFunc") { + node = std::make_shared(); + } else if (type == "VisualShaderNodeDerivativeFunc") { + node = std::make_shared(); + } else if (type == "VisualShaderNodeFloatOp") { + node = std::make_shared(); + } else if (type == "VisualShaderNodeIntOp") { + node = std::make_shared(); + } else if (type == "VisualShaderNodeUIntOp") { + node = std::make_shared(); + } else if (type == "VisualShaderNodeValueNoise") { + node = std::make_shared(); + } else if (type == "VisualShaderNodeCompare") { + node = std::make_shared(); + } else if (type == "VisualShaderNodeIf") { + node = std::make_shared(); + } else if (type == "VisualShaderNodeIs") { + node = std::make_shared(); + } else if (type == "VisualShaderNodeSwitch") { + node = std::make_shared(); + } else { + std::cout << "Unknown node type: " << type << std::endl; + return; + } + + if (!node) { + std::cout << "Failed to create node of type: " << type << std::endl; + return; + } + + QPointF offset {view->mapToScene(pos.toPoint())}; // Top-left corner of the view + + // Create the node and get the new node id. + NodeId new_node_id {graph->addNode()}; + + // Add the node to the graph. + visual_shader->add_node(node, {(float)offset.x(), (float)offset.y()}, new_node_id); + graph->setNodeData(new_node_id, NodeRole::Caption, QString::fromStdString(node->get_caption())); + graph->setNodeData(new_node_id, NodeRole::Position, offset); } -void VisualShaderEditor::show_create_node_dialog(const bool& custom_mouse_pos) { - int status = create_node_dialog->exec(); +void VisualShaderEditor::show_create_node_dialog(const QPointF& pos) { + int status {create_node_dialog->exec()}; switch (status) { case QDialog::Accepted: std::cout << "Create node dialog accepted" << std::endl; + VisualShaderEditor::create_node(pos); break; case QDialog::Rejected: std::cout << "Create node dialog rejected" << std::endl; @@ -239,6 +361,15 @@ void VisualShaderEditor::show_create_node_dialog(const bool& custom_mouse_pos) { } } +void VisualShaderEditor::on_create_node_button_pressed() { + Q_EMIT on_create_node_dialog_requested(); +} + +void VisualShaderEditor::on_create_node_action_triggered() { + QPointF pos {view->mapToScene(view->mapFromGlobal(QCursor::pos()))}; + Q_EMIT on_create_node_dialog_requested(pos); +} + std::vector VisualShaderEditor::pasre_node_category_path(const std::string& node_category_path) { std::vector tokens; std::stringstream ss(node_category_path); @@ -283,7 +414,8 @@ CreateNodeDialog::CreateNodeDialog(QWidget* parent) : QDialog(parent), create_node_dialog_nodes_description(nullptr), buttons_layout(nullptr), create_button(nullptr), - cancel_button(nullptr) { + cancel_button(nullptr), + selected_item(nullptr) { layout = new QVBoxLayout(this); layout->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom layout->setSizeConstraint(QLayout::SetNoConstraint); @@ -308,6 +440,7 @@ CreateNodeDialog::CreateNodeDialog(QWidget* parent) : QDialog(parent), create_node_dialog_nodes_tree->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom create_node_dialog_nodes_tree->setColumnCount(1); create_node_dialog_nodes_tree->setHeaderHidden(true); + this->connect(create_node_dialog_nodes_tree, &QTreeWidget::itemSelectionChanged, this, &CreateNodeDialog::update_selected_item); // Add the nodes tree to the nodes tree layout. create_node_dialog_nodes_tree_layout->addWidget(create_node_dialog_nodes_tree, 2); // 2x the size of the nodes description. @@ -330,12 +463,12 @@ CreateNodeDialog::CreateNodeDialog(QWidget* parent) : QDialog(parent), layout->setAlignment(Qt::AlignVCenter | Qt::AlignHCenter); create_button = new QPushButton("Create"); - QObject::connect(create_button, &QPushButton::clicked, this, &CreateNodeDialog::on_CreateButtonTriggered); + QObject::connect(create_button, &QPushButton::pressed, this, &CreateNodeDialog::on_create_node_button_pressed); create_button->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); create_button->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom cancel_button = new QPushButton("Cancel"); - QObject::connect(cancel_button, &QPushButton::clicked, this, &CreateNodeDialog::on_CancelButtonTriggered); + QObject::connect(cancel_button, &QPushButton::pressed, this, &CreateNodeDialog::on_cancel_node_creation_button_pressed); cancel_button->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); cancel_button->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom @@ -363,19 +496,30 @@ CreateNodeDialog::~CreateNodeDialog() { if (layout) delete layout; } -void CreateNodeDialog::on_CreateButtonTriggered() { +void CreateNodeDialog::on_create_node_button_pressed() { this->accept(); } -void CreateNodeDialog::on_CancelButtonTriggered() { +void CreateNodeDialog::on_cancel_node_creation_button_pressed() { this->reject(); } +void CreateNodeDialog::update_selected_item() { + QTreeWidgetItem* item = create_node_dialog_nodes_tree->currentItem(); + if (item) { + selected_item = item; + create_node_dialog_nodes_description->setText(item->data(0, Qt::UserRole + 1).toString()); + } else { + selected_item = nullptr; + create_node_dialog_nodes_description->setText(""); + } +} + /*************************************/ /* VisualShaderGraph */ /*************************************/ -VisualShaderGraph::VisualShaderGraph() : _nextNodeId{0} { +VisualShaderGraph::VisualShaderGraph() { } @@ -385,76 +529,75 @@ VisualShaderGraph::~VisualShaderGraph() { std::unordered_set VisualShaderGraph::allNodeIds() const { - return _nodeIds; + return _node_ids; } -std::unordered_set VisualShaderGraph::allConnectionIds(NodeId const nodeId) const +std::unordered_set VisualShaderGraph::allConnectionIds(NodeId const node_id) const { std::unordered_set result; std::copy_if(_connectivity.begin(), _connectivity.end(), std::inserter(result, std::end(result)), - [&nodeId](ConnectionId const &cid) { - return cid.inNodeId == nodeId || cid.outNodeId == nodeId; + [&node_id](ConnectionId const &cid) { + return cid.inNodeId == node_id || cid.outNodeId == node_id; }); return result; } -std::unordered_set VisualShaderGraph::connections(NodeId nodeId, - PortType portType, - PortIndex portIndex) const -{ +std::unordered_set VisualShaderGraph::connections(NodeId node_id, + PortType port_type, + PortIndex port_index) const { std::unordered_set result; std::copy_if(_connectivity.begin(), _connectivity.end(), std::inserter(result, std::end(result)), - [&portType, &portIndex, &nodeId](ConnectionId const &cid) { - return (getNodeId(portType, cid) == nodeId - && getPortIndex(portType, cid) == portIndex); + [&port_type, &port_index, &node_id](ConnectionId const &cid) { + return (getNodeId(port_type, cid) == node_id + && getPortIndex(port_type, cid) == port_index); }); return result; } -bool VisualShaderGraph::connectionExists(ConnectionId const connectionId) const +bool VisualShaderGraph::connectionExists(ConnectionId const connection_id) const { - return (_connectivity.find(connectionId) != _connectivity.end()); + return (_connectivity.find(connection_id) != _connectivity.end()); } -NodeId VisualShaderGraph::addNode(QString const nodeType) +NodeId VisualShaderGraph::addNode(QString const node_type) { - NodeId newId = _nextNodeId++; + NodeId new_id {newNodeId()}; + // Create new node. - _nodeIds.insert(newId); + _node_ids.insert(new_id); - Q_EMIT nodeCreated(newId); + Q_EMIT nodeCreated(new_id); - return newId; + return new_id; } -bool VisualShaderGraph::connectionPossible(ConnectionId const connectionId) const +bool VisualShaderGraph::connectionPossible(ConnectionId const connection_id) const { - return _connectivity.find(connectionId) == _connectivity.end(); + return _connectivity.find(connection_id) == _connectivity.end(); } -void VisualShaderGraph::addConnection(ConnectionId const connectionId) +void VisualShaderGraph::addConnection(ConnectionId const connection_id) { - _connectivity.insert(connectionId); + _connectivity.insert(connection_id); - Q_EMIT connectionCreated(connectionId); + Q_EMIT connectionCreated(connection_id); } -bool VisualShaderGraph::nodeExists(NodeId const nodeId) const +bool VisualShaderGraph::nodeExists(NodeId const node_id) const { - return (_nodeIds.find(nodeId) != _nodeIds.end()); + return (_node_ids.find(node_id) != _node_ids.end()); } -QVariant VisualShaderGraph::nodeData(NodeId nodeId, NodeRole role) const -{ - Q_UNUSED(nodeId); +QVariant VisualShaderGraph::nodeData(NodeId node_id, NodeRole role) const { + Q_UNUSED(node_id); QVariant result; @@ -464,11 +607,11 @@ QVariant VisualShaderGraph::nodeData(NodeId nodeId, NodeRole role) const break; case NodeRole::Position: - result = _nodeGeometryData[nodeId].pos; + result = _node_geometry_data[node_id].pos; break; case NodeRole::Size: - result = _nodeGeometryData[nodeId].size; + result = _node_geometry_data[node_id].size; break; case NodeRole::CaptionVisible: @@ -503,23 +646,22 @@ QVariant VisualShaderGraph::nodeData(NodeId nodeId, NodeRole role) const return result; } -bool VisualShaderGraph::setNodeData(NodeId nodeId, NodeRole role, QVariant value) -{ - bool result = false; +bool VisualShaderGraph::setNodeData(NodeId node_id, NodeRole role, QVariant value) { + bool result {false}; switch (role) { case NodeRole::Type: break; case NodeRole::Position: { - _nodeGeometryData[nodeId].pos = value.value(); + _node_geometry_data[node_id].pos = value.value(); - Q_EMIT nodePositionUpdated(nodeId); + Q_EMIT nodePositionUpdated(node_id); result = true; } break; case NodeRole::Size: { - _nodeGeometryData[nodeId].size = value.value(); + _node_geometry_data[node_id].size = value.value(); result = true; } break; @@ -548,9 +690,9 @@ bool VisualShaderGraph::setNodeData(NodeId nodeId, NodeRole role, QVariant value return result; } -QVariant VisualShaderGraph::portData(NodeId nodeId, - PortType portType, - PortIndex portIndex, +QVariant VisualShaderGraph::portData(NodeId node_id, + PortType port_type, + PortIndex port_index, PortRole role) const { switch (role) { @@ -571,7 +713,7 @@ QVariant VisualShaderGraph::portData(NodeId nodeId, break; case PortRole::Caption: - if (portType == PortType::In) + if (port_type == PortType::In) return QString::fromUtf8("Port In"); else return QString::fromUtf8("Port Out"); @@ -583,22 +725,22 @@ QVariant VisualShaderGraph::portData(NodeId nodeId, } bool VisualShaderGraph::setPortData( - NodeId nodeId, PortType portType, PortIndex portIndex, QVariant const &value, PortRole role) + NodeId node_id, PortType port_type, PortIndex port_index, QVariant const &value, PortRole role) { - Q_UNUSED(nodeId); - Q_UNUSED(portType); - Q_UNUSED(portIndex); + Q_UNUSED(node_id); + Q_UNUSED(port_type); + Q_UNUSED(port_index); Q_UNUSED(value); Q_UNUSED(role); return false; } -bool VisualShaderGraph::deleteConnection(ConnectionId const connectionId) +bool VisualShaderGraph::deleteConnection(ConnectionId const connection_id) { bool disconnected = false; - auto it = _connectivity.find(connectionId); + auto it = _connectivity.find(connection_id); if (it != _connectivity.end()) { disconnected = true; @@ -607,61 +749,23 @@ bool VisualShaderGraph::deleteConnection(ConnectionId const connectionId) } if (disconnected) - Q_EMIT connectionDeleted(connectionId); + Q_EMIT connectionDeleted(connection_id); return disconnected; } -bool VisualShaderGraph::deleteNode(NodeId const nodeId) +bool VisualShaderGraph::deleteNode(NodeId const node_id) { // Delete connections to this node first. - auto connectionIds = allConnectionIds(nodeId); + auto connectionIds = allConnectionIds(node_id); for (auto &cId : connectionIds) { deleteConnection(cId); } - _nodeIds.erase(nodeId); - _nodeGeometryData.erase(nodeId); + _node_ids.erase(node_id); + _node_geometry_data.erase(node_id); - Q_EMIT nodeDeleted(nodeId); + Q_EMIT nodeDeleted(node_id); return true; } - -QJsonObject VisualShaderGraph::saveNode(NodeId const nodeId) const -{ - QJsonObject nodeJson; - - nodeJson["id"] = static_cast(nodeId); - - { - QPointF const pos = nodeData(nodeId, NodeRole::Position).value(); - - QJsonObject posJson; - posJson["x"] = pos.x(); - posJson["y"] = pos.y(); - nodeJson["position"] = posJson; - } - - return nodeJson; -} - -void VisualShaderGraph::loadNode(QJsonObject const &nodeJson) -{ - NodeId restoredNodeId = nodeJson["id"].toInt(); - - // Next NodeId must be larger that any id existing in the graph - _nextNodeId = std::max(restoredNodeId + 1, _nextNodeId); - - // Create new node. - _nodeIds.insert(restoredNodeId); - - Q_EMIT nodeCreated(restoredNodeId); - - { - QJsonObject posJson = nodeJson["position"].toObject(); - QPointF const pos(posJson["x"].toDouble(), posJson["y"].toDouble()); - - setNodeData(restoredNodeId, NodeRole::Position, pos); - } -} diff --git a/Editors/VisualShaderEditor.h b/Editors/VisualShaderEditor.h index c7a0b1887..899893345 100644 --- a/Editors/VisualShaderEditor.h +++ b/Editors/VisualShaderEditor.h @@ -80,16 +80,25 @@ class VisualShaderEditor : public BaseEditor { VisualShaderEditor(MessageModel* model, QWidget* parent = nullptr); ~VisualShaderEditor() override; - void create_node(); + void create_node(const QPointF& pos); - void add_node(); + void add_node(QTreeWidgetItem* selected_item, const QPointF& pos); - void show_create_node_dialog(const bool& custom_mouse_pos = false); + void show_create_node_dialog(const QPointF& pos); std::vector pasre_node_category_path(const std::string& node_category_path); QTreeWidgetItem* find_or_create_category_item(QTreeWidgetItem* parent, const std::string& category, const std::string& category_path, QTreeWidget* create_node_dialog_nodes_tree, std::unordered_map& category_path_map); + Q_SIGNALS: + void on_create_node_dialog_requested(const QPointF& pos = {0, 0}); // {0, 0} is the top-left corner of the scene. + + private Q_SLOTS: + void on_create_node_button_pressed(); + void on_create_node_action_triggered(); + private: + VisualShader* visual_shader; + QHBoxLayout* layout; QHBoxLayout* scene_layer_layout; @@ -104,6 +113,8 @@ class VisualShaderEditor : public BaseEditor { QPushButton* create_node_button; QPushButton* preview_shader_button; + QAction* create_node_action; + //////////////////////////////////// // CreateNodeDialog Nodes Tree //////////////////////////////////// @@ -113,20 +124,14 @@ class VisualShaderEditor : public BaseEditor { std::string category_path; std::string type; std::string description; - std::vector ops; - VisualShaderNode::PortType return_type; CreateNodeDialogNodesTreeItem(const std::string& name = std::string(), const std::string& category_path = std::string(), const std::string& type = std::string(), - const std::string& description = std::string(), - const std::vector& ops = std::vector(), - const VisualShaderNode::PortType& return_type = VisualShaderNode::PortType::PORT_TYPE_ENUM_SIZE) : name(name), - category_path(category_path), - type(type), - description(description), - ops(ops), - return_type(return_type) {} + const std::string& description = std::string()) : name(name), + category_path(category_path), + type(type), + description(description) {} }; @@ -148,9 +153,13 @@ class CreateNodeDialog : public QDialog { QTreeWidget* get_nodes_tree() const { return create_node_dialog_nodes_tree; } - private slots: - void on_CreateButtonTriggered(); - void on_CancelButtonTriggered(); + QTreeWidgetItem* get_selected_item() const { return selected_item; } + + private Q_SLOTS: + void on_create_node_button_pressed(); + void on_cancel_node_creation_button_pressed(); + + void update_selected_item(); private: QVBoxLayout* layout; @@ -163,6 +172,8 @@ class CreateNodeDialog : public QDialog { QHBoxLayout* buttons_layout; QPushButton* create_button; QPushButton* cancel_button; + + QTreeWidgetItem* selected_item; }; /*************************************/ @@ -190,58 +201,51 @@ class VisualShaderGraph : public QtNodes::AbstractGraphModel std::unordered_set allNodeIds() const override; - std::unordered_set allConnectionIds(NodeId const nodeId) const override; + std::unordered_set allConnectionIds(NodeId const node_id) const override; - std::unordered_set connections(NodeId nodeId, - PortType portType, - PortIndex portIndex) const override; + std::unordered_set connections(NodeId node_id, + PortType port_type, + PortIndex port_index) const override; - bool connectionExists(ConnectionId const connectionId) const override; + bool connectionExists(ConnectionId const connection_id) const override; - NodeId addNode(QString const nodeType = QString()) override; + NodeId addNode(QString const node_type = QString()) override; /** * Connection is possible when graph contains no connectivity data * in both directions `Out -> In` and `In -> Out`. */ - bool connectionPossible(ConnectionId const connectionId) const override; + bool connectionPossible(ConnectionId const connection_id) const override; - void addConnection(ConnectionId const connectionId) override; + void addConnection(ConnectionId const connection_id) override; - bool nodeExists(NodeId const nodeId) const override; + bool nodeExists(NodeId const node_id) const override; - QVariant nodeData(NodeId nodeId, NodeRole role) const override; + QVariant nodeData(NodeId node_id, NodeRole role) const override; - bool setNodeData(NodeId nodeId, NodeRole role, QVariant value) override; + bool setNodeData(NodeId node_id, NodeRole role, QVariant value) override; - QVariant portData(NodeId nodeId, - PortType portType, - PortIndex portIndex, + QVariant portData(NodeId node_id, + PortType port_type, + PortIndex port_index, PortRole role) const override; - bool setPortData(NodeId nodeId, - PortType portType, - PortIndex portIndex, + bool setPortData(NodeId node_id, + PortType port_type, + PortIndex port_index, QVariant const &value, PortRole role = PortRole::Data) override; - bool deleteConnection(ConnectionId const connectionId) override; + bool deleteConnection(ConnectionId const connection_id) override; - bool deleteNode(NodeId const nodeId) override; + bool deleteNode(NodeId const node_id) override; - QJsonObject saveNode(NodeId const) const override; + void register_visual_shader(VisualShader* visual_shader) const { this->visual_shader = visual_shader; } - /// @brief Creates a new node based on the informatoin in `nodeJson`. - /** - * @param nodeJson conains a `NodeId`, node's position, internal node - * information. - */ - void loadNode(QJsonObject const &nodeJson) override; - - void set_visual_shader_editor(VisualShaderEditor* visual_shader_editor) { this->visual_shader_editor = visual_shader_editor; } + void set_visual_shader_editor(VisualShaderEditor* visual_shader_editor) const { this->visual_shader_editor = visual_shader_editor; } private: - std::unordered_set _nodeIds; + std::unordered_set _node_ids; /// [Important] This is a user defined data structure backing your model. /// In your case it could be anything else representing a graph, for example, a @@ -252,14 +256,12 @@ class VisualShaderGraph : public QtNodes::AbstractGraphModel /// directions, i.e. from Node1 to Node2 and from Node2 to Node1. std::unordered_set _connectivity; - mutable std::unordered_map _nodeGeometryData; - - /// A convenience variable needed for generating unique node ids. - NodeId _nextNodeId; + mutable std::unordered_map _node_geometry_data; - NodeId newNodeId() override { return _nextNodeId++; } + NodeId newNodeId() override { return (NodeId)visual_shader->get_valid_node_id(); } - VisualShaderEditor* visual_shader_editor; + mutable VisualShader* visual_shader; + mutable VisualShaderEditor* visual_shader_editor; }; #endif // ENIGMA_VISUAL_SHADER_EDITOR_H diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt new file mode 100644 index 000000000..e69de29bb From 179fc45e55beaf21ce688f495f45b479a1d49a1d Mon Sep 17 00:00:00 2001 From: Saif Kandil <74428638+k0T0z@users.noreply.github.com> Date: Mon, 9 Sep 2024 10:38:48 +0300 Subject: [PATCH 15/56] created add_node_custom function and few other improvements Signed-off-by: Saif Kandil <74428638+k0T0z@users.noreply.github.com> --- Editors/VisualShaderEditor.cpp | 79 ++++++++++++++++------------------ Editors/VisualShaderEditor.h | 8 ++-- 2 files changed, 43 insertions(+), 44 deletions(-) diff --git a/Editors/VisualShaderEditor.cpp b/Editors/VisualShaderEditor.cpp index 90d4c4ea0..6ec66e0fe 100644 --- a/Editors/VisualShaderEditor.cpp +++ b/Editors/VisualShaderEditor.cpp @@ -325,7 +325,6 @@ void VisualShaderEditor::add_node(QTreeWidgetItem* selected_item, const QPointF& node = std::make_shared(); } else { std::cout << "Unknown node type: " << type << std::endl; - return; } if (!node) { @@ -335,14 +334,7 @@ void VisualShaderEditor::add_node(QTreeWidgetItem* selected_item, const QPointF& QPointF offset {view->mapToScene(pos.toPoint())}; // Top-left corner of the view - // Create the node and get the new node id. - NodeId new_node_id {graph->addNode()}; - - // Add the node to the graph. - visual_shader->add_node(node, {(float)offset.x(), (float)offset.y()}, new_node_id); - - graph->setNodeData(new_node_id, NodeRole::Caption, QString::fromStdString(node->get_caption())); - graph->setNodeData(new_node_id, NodeRole::Position, offset); + graph->add_node_custom(node, offset); } void VisualShaderEditor::show_create_node_dialog(const QPointF& pos) { @@ -527,13 +519,11 @@ VisualShaderGraph::~VisualShaderGraph() { } -std::unordered_set VisualShaderGraph::allNodeIds() const -{ +std::unordered_set VisualShaderGraph::allNodeIds() const { return _node_ids; } -std::unordered_set VisualShaderGraph::allConnectionIds(NodeId const node_id) const -{ +std::unordered_set VisualShaderGraph::allConnectionIds(NodeId const node_id) const { std::unordered_set result; std::copy_if(_connectivity.begin(), @@ -562,48 +552,56 @@ std::unordered_set VisualShaderGraph::connections(NodeId node_id, return result; } -bool VisualShaderGraph::connectionExists(ConnectionId const connection_id) const -{ +bool VisualShaderGraph::connectionExists(ConnectionId const connection_id) const { return (_connectivity.find(connection_id) != _connectivity.end()); } -NodeId VisualShaderGraph::addNode(QString const node_type) -{ +void VisualShaderGraph::add_node_custom(const std::shared_ptr& node, const QPointF& offset) { NodeId new_id {newNodeId()}; // Create new node. _node_ids.insert(new_id); + // Add the node to the graph. + visual_shader->add_node(node, {(float)offset.x(), (float)offset.y()}, (int)new_id); + + this->setNodeData(new_id, NodeRole::Position, offset); + Q_EMIT nodeCreated(new_id); +} - return new_id; +NodeId VisualShaderGraph::addNode(QString const node_type) { + std::cerr << "Unsupported operation: addNode" << std::endl; + return -1; } -bool VisualShaderGraph::connectionPossible(ConnectionId const connection_id) const -{ +bool VisualShaderGraph::connectionPossible(ConnectionId const connection_id) const { return _connectivity.find(connection_id) == _connectivity.end(); } -void VisualShaderGraph::addConnection(ConnectionId const connection_id) -{ +void VisualShaderGraph::addConnection(ConnectionId const connection_id) { _connectivity.insert(connection_id); Q_EMIT connectionCreated(connection_id); } -bool VisualShaderGraph::nodeExists(NodeId const node_id) const -{ +bool VisualShaderGraph::nodeExists(NodeId const node_id) const { return (_node_ids.find(node_id) != _node_ids.end()); } QVariant VisualShaderGraph::nodeData(NodeId node_id, NodeRole role) const { - Q_UNUSED(node_id); - + const std::shared_ptr n{visual_shader->get_node((int)node_id)}; + QVariant result; + // Make sure the node exists. + if (!n) { + return result; + } + switch (role) { case NodeRole::Type: - result = QString("Default Node Type"); + result = QString::fromStdString(n->get_caption()); break; case NodeRole::Position: @@ -619,7 +617,7 @@ QVariant VisualShaderGraph::nodeData(NodeId node_id, NodeRole role) const { break; case NodeRole::Caption: - result = QString("Node"); + result = QString::fromStdString(n->get_caption()); break; case NodeRole::Style: { @@ -631,11 +629,11 @@ QVariant VisualShaderGraph::nodeData(NodeId node_id, NodeRole role) const { break; case NodeRole::InPortCount: - result = 5u; + result = n->get_input_port_count(); break; case NodeRole::OutPortCount: - result = 3u; + result = n->get_output_port_count(); break; case NodeRole::Widget: @@ -691,10 +689,9 @@ bool VisualShaderGraph::setNodeData(NodeId node_id, NodeRole role, QVariant valu } QVariant VisualShaderGraph::portData(NodeId node_id, - PortType port_type, - PortIndex port_index, - PortRole role) const -{ + PortType port_type, + PortIndex port_index, + PortRole role) const { switch (role) { case PortRole::Data: return QVariant(); @@ -724,9 +721,11 @@ QVariant VisualShaderGraph::portData(NodeId node_id, return QVariant(); } -bool VisualShaderGraph::setPortData( - NodeId node_id, PortType port_type, PortIndex port_index, QVariant const &value, PortRole role) -{ +bool VisualShaderGraph::setPortData(NodeId node_id, + PortType port_type, + PortIndex port_index, + QVariant const &value, + PortRole role) { Q_UNUSED(node_id); Q_UNUSED(port_type); Q_UNUSED(port_index); @@ -736,8 +735,7 @@ bool VisualShaderGraph::setPortData( return false; } -bool VisualShaderGraph::deleteConnection(ConnectionId const connection_id) -{ +bool VisualShaderGraph::deleteConnection(ConnectionId const connection_id) { bool disconnected = false; auto it = _connectivity.find(connection_id); @@ -754,8 +752,7 @@ bool VisualShaderGraph::deleteConnection(ConnectionId const connection_id) return disconnected; } -bool VisualShaderGraph::deleteNode(NodeId const node_id) -{ +bool VisualShaderGraph::deleteNode(NodeId const node_id) { // Delete connections to this node first. auto connectionIds = allConnectionIds(node_id); for (auto &cId : connectionIds) { diff --git a/Editors/VisualShaderEditor.h b/Editors/VisualShaderEditor.h index 899893345..5f2da697b 100644 --- a/Editors/VisualShaderEditor.h +++ b/Editors/VisualShaderEditor.h @@ -209,7 +209,7 @@ class VisualShaderGraph : public QtNodes::AbstractGraphModel bool connectionExists(ConnectionId const connection_id) const override; - NodeId addNode(QString const node_type = QString()) override; + void add_node_custom(const std::shared_ptr& node, const QPointF& offset); /** * Connection is possible when graph contains no connectivity data @@ -258,10 +258,12 @@ class VisualShaderGraph : public QtNodes::AbstractGraphModel mutable std::unordered_map _node_geometry_data; - NodeId newNodeId() override { return (NodeId)visual_shader->get_valid_node_id(); } - mutable VisualShader* visual_shader; mutable VisualShaderEditor* visual_shader_editor; + + NodeId newNodeId() override { return (NodeId)visual_shader->get_valid_node_id(); } + + NodeId addNode(QString const node_type = QString()) override; }; #endif // ENIGMA_VISUAL_SHADER_EDITOR_H From 8c5b01e3e2af46cbd835e3ab8cc044aa1f687876 Mon Sep 17 00:00:00 2001 From: Saif Kandil <74428638+k0T0z@users.noreply.github.com> Date: Mon, 9 Sep 2024 23:55:59 +0300 Subject: [PATCH 16/56] fully integration between VisualShader and VisualShaderEditor is now done Signed-off-by: Saif Kandil <74428638+k0T0z@users.noreply.github.com> --- Editors/VisualShaderEditor.cpp | 270 +++++++++++++++++++++++++-------- Editors/VisualShaderEditor.h | 33 ++-- 2 files changed, 228 insertions(+), 75 deletions(-) diff --git a/Editors/VisualShaderEditor.cpp b/Editors/VisualShaderEditor.cpp index 6ec66e0fe..d1580bdb9 100644 --- a/Editors/VisualShaderEditor.cpp +++ b/Editors/VisualShaderEditor.cpp @@ -85,6 +85,7 @@ VisualShaderEditor::VisualShaderEditor(MessageModel* model, QWidget* parent) : B scene_layer_layout->setAlignment(Qt::AlignVCenter | Qt::AlignHCenter); graph = new VisualShaderGraph(); + graph->register_visual_shader(visual_shader); scene = new BasicGraphicsScene(*graph); scene->setOrientation(Qt::Horizontal); @@ -187,10 +188,10 @@ VisualShaderEditor::VisualShaderEditor(MessageModel* model, QWidget* parent) : B //////////////// Start of Footer //////////////// - graph->register_visual_shader(visual_shader); graph->set_visual_shader_editor(this); this->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom + // this->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); // Set the window title and icon. this->setWindowTitle("Visual Shader Editor"); @@ -520,18 +521,32 @@ VisualShaderGraph::~VisualShaderGraph() { } std::unordered_set VisualShaderGraph::allNodeIds() const { - return _node_ids; + std::unordered_set result; + + std::vector ids {visual_shader->get_used_ids()}; + + for (const int& id : ids) { + result.insert(id); + } + + return result; } std::unordered_set VisualShaderGraph::allConnectionIds(NodeId const node_id) const { std::unordered_set result; - std::copy_if(_connectivity.begin(), - _connectivity.end(), - std::inserter(result, std::end(result)), - [&node_id](ConnectionId const &cid) { - return cid.inNodeId == node_id || cid.outNodeId == node_id; - }); + std::vector all_connections {visual_shader->get_all_connections()}; + + for (const VisualShader::Connection& c : all_connections) { + if (c.from_node == node_id || c.to_node == node_id) { + ConnectionId cid; + cid.inNodeId = c.to_node; + cid.inPortIndex = c.to_port; + cid.outNodeId = c.from_node; + cid.outPortIndex = c.from_port; + result.insert(cid); + } + } return result; } @@ -541,52 +556,72 @@ std::unordered_set VisualShaderGraph::connections(NodeId node_id, PortIndex port_index) const { std::unordered_set result; - std::copy_if(_connectivity.begin(), - _connectivity.end(), - std::inserter(result, std::end(result)), - [&port_type, &port_index, &node_id](ConnectionId const &cid) { - return (getNodeId(port_type, cid) == node_id - && getPortIndex(port_type, cid) == port_index); - }); + std::vector all_connections {visual_shader->get_all_connections()}; + + for (const VisualShader::Connection& c : all_connections) { + switch (port_type) { + case PortType::In: + if (c.to_node == node_id && c.to_port == port_index) { + ConnectionId cid; + cid.inNodeId = c.to_node; + cid.inPortIndex = c.to_port; + cid.outNodeId = c.from_node; + cid.outPortIndex = c.from_port; + result.insert(cid); + } + break; + case PortType::Out: + if (c.from_node == node_id && c.from_port == port_index) { + ConnectionId cid; + cid.inNodeId = c.to_node; + cid.inPortIndex = c.to_port; + cid.outNodeId = c.from_node; + cid.outPortIndex = c.from_port; + result.insert(cid); + } + break; + default: + std::cout << "Unknown port type" << std::endl; + break; + } + } return result; } bool VisualShaderGraph::connectionExists(ConnectionId const connection_id) const { - return (_connectivity.find(connection_id) != _connectivity.end()); + return !visual_shader->can_connect_nodes((int)connection_id.outNodeId, (int)connection_id.outPortIndex, (int)connection_id.inNodeId, (int)connection_id.inPortIndex); } void VisualShaderGraph::add_node_custom(const std::shared_ptr& node, const QPointF& offset) { NodeId new_id {newNodeId()}; - // Create new node. - _node_ids.insert(new_id); - // Add the node to the graph. visual_shader->add_node(node, {(float)offset.x(), (float)offset.y()}, (int)new_id); this->setNodeData(new_id, NodeRole::Position, offset); + this->setNodeData(new_id, NodeRole::Size, QSize(25, 50)); Q_EMIT nodeCreated(new_id); } -NodeId VisualShaderGraph::addNode(QString const node_type) { - std::cerr << "Unsupported operation: addNode" << std::endl; - return -1; -} - bool VisualShaderGraph::connectionPossible(ConnectionId const connection_id) const { - return _connectivity.find(connection_id) == _connectivity.end(); + return visual_shader->can_connect_nodes((int)connection_id.outNodeId, (int)connection_id.outPortIndex, (int)connection_id.inNodeId, (int)connection_id.inPortIndex); } void VisualShaderGraph::addConnection(ConnectionId const connection_id) { - _connectivity.insert(connection_id); + bool status {visual_shader->connect_nodes((int)connection_id.outNodeId, (int)connection_id.outPortIndex, (int)connection_id.inNodeId, (int)connection_id.inPortIndex)}; + + if (!status) { + std::cout << "Failed to connect nodes: " << connection_id.outNodeId << " -> " << connection_id.inNodeId << std::endl; + return; + } Q_EMIT connectionCreated(connection_id); } bool VisualShaderGraph::nodeExists(NodeId const node_id) const { - return (_node_ids.find(node_id) != _node_ids.end()); + return visual_shader->get_node((int)node_id) != nullptr; } QVariant VisualShaderGraph::nodeData(NodeId node_id, NodeRole role) const { @@ -637,7 +672,8 @@ QVariant VisualShaderGraph::nodeData(NodeId node_id, NodeRole role) const { break; case NodeRole::Widget: - result = QVariant(); + NodesCustomWidget* custom_widget = new NodesCustomWidget(n); + result = QVariant::fromValue(custom_widget); break; } @@ -688,37 +724,54 @@ bool VisualShaderGraph::setNodeData(NodeId node_id, NodeRole role, QVariant valu return result; } +NodeId VisualShaderGraph::addNode(QString const node_type) { + std::cerr << "Unsupported operation: addNode" << std::endl; + return -1; +} + QVariant VisualShaderGraph::portData(NodeId node_id, PortType port_type, PortIndex port_index, PortRole role) const { - switch (role) { - case PortRole::Data: - return QVariant(); - break; + const std::shared_ptr n{visual_shader->get_node((int)node_id)}; + + QVariant result; - case PortRole::DataType: - return QVariant(); - break; + if (!n) { + return result; + } - case PortRole::ConnectionPolicyRole: - return QVariant::fromValue(ConnectionPolicy::One); - break; + switch (role) { + case PortRole::Data: + break; - case PortRole::CaptionVisible: - return true; - break; + case PortRole::DataType: + break; - case PortRole::Caption: - if (port_type == PortType::In) - return QString::fromUtf8("Port In"); - else - return QString::fromUtf8("Port Out"); + case PortRole::ConnectionPolicyRole: + result = QVariant::fromValue(ConnectionPolicy::One); + break; - break; + case PortRole::CaptionVisible: + result = true; + break; + + case PortRole::Caption: + switch (port_type) { + case PortType::In: + result = QString::fromStdString(n->get_input_port_name((int)port_index)); + break; + case PortType::Out: + result = QString::fromStdString(n->get_output_port_name((int)port_index)); + break; + default: + std::cout << "Unknown port type" << std::endl; + break; + } + break; } - return QVariant(); + return result; } bool VisualShaderGraph::setPortData(NodeId node_id, @@ -736,33 +789,122 @@ bool VisualShaderGraph::setPortData(NodeId node_id, } bool VisualShaderGraph::deleteConnection(ConnectionId const connection_id) { - bool disconnected = false; - - auto it = _connectivity.find(connection_id); - - if (it != _connectivity.end()) { - disconnected = true; + bool status {visual_shader->disconnect_nodes((int)connection_id.outNodeId, (int)connection_id.outPortIndex, (int)connection_id.inNodeId, (int)connection_id.inPortIndex)}; - _connectivity.erase(it); + if (!status) { + return status; } - if (disconnected) - Q_EMIT connectionDeleted(connection_id); + Q_EMIT connectionDeleted(connection_id); - return disconnected; + return status; } bool VisualShaderGraph::deleteNode(NodeId const node_id) { - // Delete connections to this node first. - auto connectionIds = allConnectionIds(node_id); - for (auto &cId : connectionIds) { - deleteConnection(cId); + bool status {visual_shader->remove_node((int)node_id)}; + + if (!status) { + return status; } - _node_ids.erase(node_id); _node_geometry_data.erase(node_id); Q_EMIT nodeDeleted(node_id); - return true; + return status; +} + +/*************************************/ +/* NodesCustomWidget */ +/*************************************/ + +NodesCustomWidget::NodesCustomWidget(const std::shared_ptr& node, QWidget* parent) : QWidget(parent), + layout(nullptr) { + // Create the main layout. + layout = new QVBoxLayout(this); + layout->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom + layout->setSizeConstraint(QLayout::SetMinimumSize); + layout->setSpacing(0); + layout->setAlignment(Qt::AlignVCenter | Qt::AlignHCenter); + + //////////////// End of Header //////////////// + + combo_boxes[0] = new QComboBox(); + combo_boxes[0]->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + combo_boxes[0]->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom + combo_boxes[0]->setMaximumSize(combo_boxes[0]->sizeHint()); + + // Add items to the combo box + combo_boxes[0]->addItem("Item 1"); + combo_boxes[0]->addItem("Item 2"); + combo_boxes[0]->addItem("Item 3"); + + layout->addWidget(combo_boxes[0]); + + // Connect the combo box signal to the slot + QObject::connect(combo_boxes[0], QOverload::of(&QComboBox::currentIndexChanged), this, &NodesCustomWidget::on_combo_box0_current_index_changed); + + + if (std::dynamic_pointer_cast(node)) { + + } else if (std::dynamic_pointer_cast(node)) { + + } else if (std::dynamic_pointer_cast(node)) { + + } else if (std::dynamic_pointer_cast(node)) { + + } else if (std::dynamic_pointer_cast(node)) { + + } else if (std::dynamic_pointer_cast(node)) { + + } else if (std::dynamic_pointer_cast(node)) { + + } else if (std::dynamic_pointer_cast(node)) { + + } else if (std::dynamic_pointer_cast(node)) { + + } else if (std::dynamic_pointer_cast(node)) { + + } else if (std::dynamic_pointer_cast(node)) { + + } else if (std::dynamic_pointer_cast(node)) { + + } else if (std::dynamic_pointer_cast(node)) { + + } else if (std::dynamic_pointer_cast(node)) { + + } else if (std::dynamic_pointer_cast(node)) { + + } else if (std::dynamic_pointer_cast(node)) { + + } else if (std::dynamic_pointer_cast(node)) { + + } else if (std::dynamic_pointer_cast(node)) { + + } else if (std::dynamic_pointer_cast(node)) { + + } else if (std::dynamic_pointer_cast(node)) { + + } else if (std::dynamic_pointer_cast(node)) { + + } else { + std::cout << "Unknown node type" << std::endl; + } + + //////////////// Start of Footer //////////////// + + // TODO: Set the size of this widget based on the contents. + + this->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom + // this->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + + this->setLayout(layout); +} + +NodesCustomWidget::~NodesCustomWidget() { + if (layout) delete layout; +} + +void NodesCustomWidget::on_combo_box0_current_index_changed(const int& index) { + std::cout << "Combo box index changed: " << index << std::endl; } diff --git a/Editors/VisualShaderEditor.h b/Editors/VisualShaderEditor.h index 5f2da697b..f13d5a930 100644 --- a/Editors/VisualShaderEditor.h +++ b/Editors/VisualShaderEditor.h @@ -37,6 +37,7 @@ #include #include #include +#include #include #include @@ -245,17 +246,6 @@ class VisualShaderGraph : public QtNodes::AbstractGraphModel void set_visual_shader_editor(VisualShaderEditor* visual_shader_editor) const { this->visual_shader_editor = visual_shader_editor; } private: - std::unordered_set _node_ids; - - /// [Important] This is a user defined data structure backing your model. - /// In your case it could be anything else representing a graph, for example, a - /// table. Or a collection of structs with pointers to each other. Or an - /// abstract syntax tree, you name it. - /// - /// This data structure contains the graph connectivity information in both - /// directions, i.e. from Node1 to Node2 and from Node2 to Node1. - std::unordered_set _connectivity; - mutable std::unordered_map _node_geometry_data; mutable VisualShader* visual_shader; @@ -266,4 +256,25 @@ class VisualShaderGraph : public QtNodes::AbstractGraphModel NodeId addNode(QString const node_type = QString()) override; }; +/*************************************/ +/* NodesCustomWidget */ +/*************************************/ + +class NodesCustomWidget : public QWidget +{ + Q_OBJECT + +public: + NodesCustomWidget(const std::shared_ptr& node, QWidget *parent = nullptr); + ~NodesCustomWidget(); + +private Q_SLOTS: + void on_combo_box0_current_index_changed(const int& index); + +private: + QVBoxLayout* layout; + + QComboBox* combo_boxes[2]; +}; + #endif // ENIGMA_VISUAL_SHADER_EDITOR_H From 44543bdcf1bdbf05906cb087ec53ca90ae50ea71 Mon Sep 17 00:00:00 2001 From: Saif Kandil <74428638+k0T0z@users.noreply.github.com> Date: Thu, 12 Sep 2024 14:18:47 +0300 Subject: [PATCH 17/56] Implemented the graphics view from scratch to get rid of the nodeeditor dependency --- CMakeLists.txt | 13 + Editors/VisualShaderEditor.cpp | 514 ++++++++++++++++----------------- Editors/VisualShaderEditor.h | 246 +++++++++------- Submodules/nodeeditor | 2 +- 4 files changed, 402 insertions(+), 373 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 041fe3be3..2fc3abef7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -72,6 +72,19 @@ set(CMAKE_AUTORCC ON) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) +# if(MSVC) +# string(APPEND CMAKE_CXX_FLAGS " /EHsc /wd26812") +# string(APPEND CMAKE_C_FLAGS " /EHsc /wd26812") +# endif() + +# # Disable C++ exceptions. +# if(MSVC) +# string(REGEX REPLACE "/EHsc" "/EHs-c-" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") +# add_definitions(-D_HAS_EXCEPTIONS=0) +# else() +# set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-exceptions -fno-unwind-tables -fno-asynchronous-unwind-tables") +# endif() + set(CMAKE_AUTOUIC_SEARCH_PATHS "${CMAKE_CURRENT_SOURCE_DIR}/" "${CMAKE_CURRENT_SOURCE_DIR}/Dialogs" "${CMAKE_CURRENT_SOURCE_DIR}/Editors") # Uncomment to be able to use local grpc_cpp_plugin diff --git a/Editors/VisualShaderEditor.cpp b/Editors/VisualShaderEditor.cpp index d1580bdb9..cced106a2 100644 --- a/Editors/VisualShaderEditor.cpp +++ b/Editors/VisualShaderEditor.cpp @@ -34,25 +34,25 @@ #include -#include - #include "VisualShader.pb.h" #include "ResourceTransformations/VisualShader/visual_shader_nodes.h" #include "ResourceTransformations/VisualShader/vs_noise_nodes.h" -using QtNodes::GraphicsView; -using QtNodes::NodeRole; - -/*************************************/ -/* VisualShaderEditor */ -/*************************************/ +/**********************************************************************/ +/**********************************************************************/ +/**********************************************************************/ +/***** *****/ +/***** VisualShaderEditor *****/ +/***** *****/ +/**********************************************************************/ +/**********************************************************************/ +/**********************************************************************/ VisualShaderEditor::VisualShaderEditor(MessageModel* model, QWidget* parent) : BaseEditor(model, parent), visual_shader(nullptr), layout(nullptr), scene_layer_layout(nullptr), scene_layer(nullptr), - graph(nullptr), scene(nullptr), view(nullptr), top_layer(nullptr), @@ -84,23 +84,19 @@ VisualShaderEditor::VisualShaderEditor(MessageModel* model, QWidget* parent) : B scene_layer_layout->setSizeConstraint(QLayout::SetNoConstraint); scene_layer_layout->setAlignment(Qt::AlignVCenter | Qt::AlignHCenter); - graph = new VisualShaderGraph(); - graph->register_visual_shader(visual_shader); + scene = new VisualShaderGraphicsScene(); - scene = new BasicGraphicsScene(*graph); - scene->setOrientation(Qt::Horizontal); - - view = new GraphicsView(scene); + view = new VisualShaderGraphicsView(scene, scene_layer); view->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom view->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); scene_layer_layout->addWidget(view); - // Setup context menu for creating new nodes. - view->setContextMenuPolicy(Qt::ActionsContextMenu); - create_node_action = new QAction(QStringLiteral("Create Node"), view); - QObject::connect(create_node_action, &QAction::triggered, this, &VisualShaderEditor::on_create_node_action_triggered); - view->insertAction(view->actions().front(), create_node_action); + // // Setup context menu for creating new nodes. + // view->setContextMenuPolicy(Qt::ActionsContextMenu); + // create_node_action = new QAction(QStringLiteral("Create Node"), view); + // QObject::connect(create_node_action, &QAction::triggered, this, &VisualShaderEditor::on_create_node_action_triggered); + // view->insertAction(view->actions().front(), create_node_action); // Set the scene layer layout. scene_layer->setLayout(scene_layer_layout); @@ -188,8 +184,6 @@ VisualShaderEditor::VisualShaderEditor(MessageModel* model, QWidget* parent) : B //////////////// Start of Footer //////////////// - graph->set_visual_shader_editor(this); - this->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom // this->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); @@ -209,7 +203,6 @@ VisualShaderEditor::~VisualShaderEditor() { if (top_layer) delete top_layer; if (view) delete view; if (scene) delete scene; - if (graph) delete graph; if (scene_layer) delete scene_layer; if (scene_layer_layout) delete scene_layer_layout; if (layout) delete layout; @@ -262,18 +255,18 @@ const VisualShaderEditor::CreateNodeDialogNodesTreeItem VisualShaderEditor::crea {"", "", "", ""}, }; -void VisualShaderEditor::create_node(const QPointF& pos) { - QTreeWidgetItem* selected_item = create_node_dialog->get_selected_item(); +void VisualShaderEditor::create_node(const QPointF& coordinate) { + QTreeWidgetItem* selected_item {create_node_dialog->get_selected_item()}; if (!selected_item) { return; } - VisualShaderEditor::add_node(selected_item, pos); + VisualShaderEditor::add_node(selected_item, coordinate); } -void VisualShaderEditor::add_node(QTreeWidgetItem* selected_item, const QPointF& pos) { - std::string type = selected_item->data(0, Qt::UserRole).toString().toStdString(); +void VisualShaderEditor::add_node(QTreeWidgetItem* selected_item, const QPointF& coordinate) { + std::string type {selected_item->data(0, Qt::UserRole).toString().toStdString()}; if (type.empty()) { return; @@ -333,17 +326,15 @@ void VisualShaderEditor::add_node(QTreeWidgetItem* selected_item, const QPointF& return; } - QPointF offset {view->mapToScene(pos.toPoint())}; // Top-left corner of the view - - graph->add_node_custom(node, offset); + std::cout << "END of START hahaha" << std::endl; } -void VisualShaderEditor::show_create_node_dialog(const QPointF& pos) { +void VisualShaderEditor::show_create_node_dialog(const QPointF& coordinate) { int status {create_node_dialog->exec()}; switch (status) { case QDialog::Accepted: std::cout << "Create node dialog accepted" << std::endl; - VisualShaderEditor::create_node(pos); + VisualShaderEditor::create_node(coordinate); break; case QDialog::Rejected: std::cout << "Create node dialog rejected" << std::endl; @@ -359,8 +350,8 @@ void VisualShaderEditor::on_create_node_button_pressed() { } void VisualShaderEditor::on_create_node_action_triggered() { - QPointF pos {view->mapToScene(view->mapFromGlobal(QCursor::pos()))}; - Q_EMIT on_create_node_dialog_requested(pos); + QPointF coordinate {view->mapToScene(view->mapFromGlobal(QCursor::pos()))}; + Q_EMIT on_create_node_dialog_requested(coordinate); } std::vector VisualShaderEditor::pasre_node_category_path(const std::string& node_category_path) { @@ -396,9 +387,15 @@ QTreeWidgetItem* VisualShaderEditor::find_or_create_category_item(QTreeWidgetIte return new_item; } -/*************************************/ -/* CreateNodeDialog */ -/*************************************/ +/**********************************************************************/ +/**********************************************************************/ +/**********************************************************************/ +/***** *****/ +/***** CreateNodeDialog *****/ +/***** *****/ +/**********************************************************************/ +/**********************************************************************/ +/**********************************************************************/ CreateNodeDialog::CreateNodeDialog(QWidget* parent) : QDialog(parent), layout(nullptr), @@ -498,7 +495,7 @@ void CreateNodeDialog::on_cancel_node_creation_button_pressed() { } void CreateNodeDialog::update_selected_item() { - QTreeWidgetItem* item = create_node_dialog_nodes_tree->currentItem(); + QTreeWidgetItem* item {create_node_dialog_nodes_tree->currentItem()}; if (item) { selected_item = item; create_node_dialog_nodes_description->setText(item->data(0, Qt::UserRole + 1).toString()); @@ -508,315 +505,288 @@ void CreateNodeDialog::update_selected_item() { } } -/*************************************/ -/* VisualShaderGraph */ -/*************************************/ - -VisualShaderGraph::VisualShaderGraph() { +/**********************************************************************/ +/**********************************************************************/ +/**********************************************************************/ +/***** *****/ +/***** VisualShaderGraphicsScene *****/ +/***** *****/ +/**********************************************************************/ +/**********************************************************************/ +/**********************************************************************/ +VisualShaderGraphicsScene::VisualShaderGraphicsScene(QObject *parent) : QGraphicsScene(parent) { + } -VisualShaderGraph::~VisualShaderGraph() { +/**********************************************************************/ +/**********************************************************************/ +/**********************************************************************/ +/***** *****/ +/***** VisualShaderGraphicsView *****/ +/***** *****/ +/**********************************************************************/ +/**********************************************************************/ +/**********************************************************************/ -} +////////////////////////////// +// Public functions +////////////////////////////// -std::unordered_set VisualShaderGraph::allNodeIds() const { - std::unordered_set result; +VisualShaderGraphicsView::VisualShaderGraphicsView(VisualShaderGraphicsScene *scene, QWidget *parent) : QGraphicsView(scene, parent), + context_menu(nullptr), + create_node_action(nullptr), + delete_node_action(nullptr) { + setDragMode(QGraphicsView::ScrollHandDrag); + setRenderHint(QPainter::Antialiasing); - std::vector ids {visual_shader->get_used_ids()}; + setBackgroundBrush(this->background_color); - for (const int& id : ids) { - result.insert(id); - } + setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - return result; -} + setTransformationAnchor(QGraphicsView::AnchorUnderMouse); -std::unordered_set VisualShaderGraph::allConnectionIds(NodeId const node_id) const { - std::unordered_set result; + setCacheMode(QGraphicsView::CacheBackground); + setViewportUpdateMode(QGraphicsView::BoundingRectViewportUpdate); - std::vector all_connections {visual_shader->get_all_connections()}; + // Allow dezooming 8 times from the default zoom level. + zoom_min = (1.0f / std::pow(zoom_step, 8.0f)); + // Allow zooming 4 times from the default zoom level. + zoom_max = (1.0f * std::pow(zoom_step, 4.0f)); - for (const VisualShader::Connection& c : all_connections) { - if (c.from_node == node_id || c.to_node == node_id) { - ConnectionId cid; - cid.inNodeId = c.to_node; - cid.inPortIndex = c.to_port; - cid.outNodeId = c.from_node; - cid.outPortIndex = c.from_port; - result.insert(cid); - } - } + setSceneRect(rect_x, rect_y, rect_width, rect_height); - return result; -} - -std::unordered_set VisualShaderGraph::connections(NodeId node_id, - PortType port_type, - PortIndex port_index) const { - std::unordered_set result; - - std::vector all_connections {visual_shader->get_all_connections()}; - - for (const VisualShader::Connection& c : all_connections) { - switch (port_type) { - case PortType::In: - if (c.to_node == node_id && c.to_port == port_index) { - ConnectionId cid; - cid.inNodeId = c.to_node; - cid.inPortIndex = c.to_port; - cid.outNodeId = c.from_node; - cid.outPortIndex = c.from_port; - result.insert(cid); - } - break; - case PortType::Out: - if (c.from_node == node_id && c.from_port == port_index) { - ConnectionId cid; - cid.inNodeId = c.to_node; - cid.inPortIndex = c.to_port; - cid.outNodeId = c.from_node; - cid.outPortIndex = c.from_port; - result.insert(cid); - } - break; - default: - std::cout << "Unknown port type" << std::endl; - break; - } - } + // Set the context menu + context_menu = new QMenu(this); + create_node_action = new QAction(QStringLiteral("Create Node"), context_menu); + QObject::connect(create_node_action, &QAction::triggered, this, &VisualShaderGraphicsView::on_create_node_action_triggered); + context_menu->addAction(create_node_action); - return result; + delete_node_action = new QAction(QStringLiteral("Delete Node"), context_menu); + delete_node_action->setShortcutContext(Qt::ShortcutContext::WidgetShortcut); + delete_node_action->setShortcut(QKeySequence(QKeySequence::Delete)); + QObject::connect(delete_node_action, &QAction::triggered, this, &VisualShaderGraphicsView::on_delete_node_action_triggered); + context_menu->addAction(delete_node_action); } -bool VisualShaderGraph::connectionExists(ConnectionId const connection_id) const { - return !visual_shader->can_connect_nodes((int)connection_id.outNodeId, (int)connection_id.outPortIndex, (int)connection_id.inNodeId, (int)connection_id.inPortIndex); +VisualShaderGraphicsView::~VisualShaderGraphicsView() { + if (context_menu) delete context_menu; + if (create_node_action) delete create_node_action; } -void VisualShaderGraph::add_node_custom(const std::shared_ptr& node, const QPointF& offset) { - NodeId new_id {newNodeId()}; +////////////////////////////// +// Private slots +////////////////////////////// - // Add the node to the graph. - visual_shader->add_node(node, {(float)offset.x(), (float)offset.y()}, (int)new_id); +void VisualShaderGraphicsView::setup_zoom(const float& zoom) { + float t_zoom {zoom}; - this->setNodeData(new_id, NodeRole::Position, offset); - this->setNodeData(new_id, NodeRole::Size, QSize(25, 50)); + t_zoom = std::max(zoom_min, std::min(zoom_max, t_zoom)); - Q_EMIT nodeCreated(new_id); -} + if (t_zoom <= 0.0f) + return; -bool VisualShaderGraph::connectionPossible(ConnectionId const connection_id) const { - return visual_shader->can_connect_nodes((int)connection_id.outNodeId, (int)connection_id.outPortIndex, (int)connection_id.inNodeId, (int)connection_id.inPortIndex); -} + if (t_zoom == transform().m11()) + return; -void VisualShaderGraph::addConnection(ConnectionId const connection_id) { - bool status {visual_shader->connect_nodes((int)connection_id.outNodeId, (int)connection_id.outPortIndex, (int)connection_id.inNodeId, (int)connection_id.inPortIndex)}; + QTransform matrix; + matrix.scale(t_zoom, t_zoom); + setTransform(matrix, false); - if (!status) { - std::cout << "Failed to connect nodes: " << connection_id.outNodeId << " -> " << connection_id.inNodeId << std::endl; - return; - } - - Q_EMIT connectionCreated(connection_id); + Q_EMIT zoom_changed(t_zoom); } -bool VisualShaderGraph::nodeExists(NodeId const node_id) const { - return visual_shader->get_node((int)node_id) != nullptr; +void VisualShaderGraphicsView::on_create_node_action_triggered() { + } -QVariant VisualShaderGraph::nodeData(NodeId node_id, NodeRole role) const { - const std::shared_ptr n{visual_shader->get_node((int)node_id)}; +void VisualShaderGraphicsView::on_delete_node_action_triggered() { - QVariant result; - - // Make sure the node exists. - if (!n) { - return result; - } - - switch (role) { - case NodeRole::Type: - result = QString::fromStdString(n->get_caption()); - break; - - case NodeRole::Position: - result = _node_geometry_data[node_id].pos; - break; - - case NodeRole::Size: - result = _node_geometry_data[node_id].size; - break; - - case NodeRole::CaptionVisible: - result = true; - break; +} - case NodeRole::Caption: - result = QString::fromStdString(n->get_caption()); - break; +void VisualShaderGraphicsView::zoom_in() { + const float factor {std::pow(zoom_step, 1.0f)}; - case NodeRole::Style: { - auto style = StyleCollection::nodeStyle(); - result = style.toJson().toVariantMap(); - } break; + QTransform t {transform()}; + t.scale(factor, factor); + if (t.m11() >= zoom_max) { + setup_zoom(t.m11()); + return; + } - case NodeRole::InternalData: - break; + scale(factor, factor); + Q_EMIT zoom_changed(transform().m11()); +} - case NodeRole::InPortCount: - result = n->get_input_port_count(); - break; +void VisualShaderGraphicsView::reset_zoom() { + +} - case NodeRole::OutPortCount: - result = n->get_output_port_count(); - break; +void VisualShaderGraphicsView::zoom_out() { + const float factor {std::pow(zoom_step, -1.0f)}; - case NodeRole::Widget: - NodesCustomWidget* custom_widget = new NodesCustomWidget(n); - result = QVariant::fromValue(custom_widget); - break; + QTransform t {transform()}; + t.scale(factor, factor); + if (t.m11() <= zoom_min) { + setup_zoom(t.m11()); + return; } - return result; + scale(factor, factor); + Q_EMIT zoom_changed(transform().m11()); } -bool VisualShaderGraph::setNodeData(NodeId node_id, NodeRole role, QVariant value) { - bool result {false}; - - switch (role) { - case NodeRole::Type: - break; - case NodeRole::Position: { - _node_geometry_data[node_id].pos = value.value(); +////////////////////////////// +// Private functions +////////////////////////////// - Q_EMIT nodePositionUpdated(node_id); +void VisualShaderGraphicsView::drawBackground(QPainter *painter, const QRectF &r) { + QGraphicsView::drawBackground(painter, r); - result = true; - } break; + std::function draw_grid = [&](float grid_step) { + QRect window_rect {this->rect()}; - case NodeRole::Size: { - _node_geometry_data[node_id].size = value.value(); - result = true; - } break; + QPointF tl {mapToScene(window_rect.topLeft())}; + QPointF br {mapToScene(window_rect.bottomRight())}; - case NodeRole::CaptionVisible: - break; + float left {(float)std::floor(tl.x() / grid_step - 0.5f)}; + float right {(float)std::floor(br.x() / grid_step + 1.0f)}; + float bottom {(float)std::floor(tl.y() / grid_step - 0.5f)}; + float top {(float)std::floor(br.y() / grid_step + 1.0f)}; - case NodeRole::Caption: - break; + // Vertical lines + for (int xi {(int)left}; xi <= (int)right; ++xi) { + QLineF line(xi * grid_step, bottom * grid_step, xi * grid_step, top * grid_step); + painter->drawLine(line); + } - case NodeRole::Style: - break; + // Horizontal lines + for (int yi {(int)bottom}; yi <= (int)top; ++yi) { + QLineF line(left * grid_step, yi * grid_step, right * grid_step, yi * grid_step); + painter->drawLine(line); + } + }; - case NodeRole::InternalData: - break; + QPen fine_pen(this->fine_grid_color, 1.0f); + painter->setPen(fine_pen); + draw_grid(15.0f); - case NodeRole::InPortCount: - break; + QPen coarse_pen(this->coarse_grid_color, 1.0f); + painter->setPen(coarse_pen); + draw_grid(150.0f); +} - case NodeRole::OutPortCount: - break; +void VisualShaderGraphicsView::contextMenuEvent(QContextMenuEvent *event) { + if (itemAt(event->pos())) { + QGraphicsView::contextMenuEvent(event); + return; + } - case NodeRole::Widget: - break; - } + QPointF scene_coordinate {mapToScene(event->pos())}; - return result; + context_menu->exec(event->globalPos()); } -NodeId VisualShaderGraph::addNode(QString const node_type) { - std::cerr << "Unsupported operation: addNode" << std::endl; - return -1; -} +void VisualShaderGraphicsView::wheelEvent(QWheelEvent *event) { + const QPoint delta {event->angleDelta()}; -QVariant VisualShaderGraph::portData(NodeId node_id, - PortType port_type, - PortIndex port_index, - PortRole role) const { - const std::shared_ptr n{visual_shader->get_node((int)node_id)}; - - QVariant result; + if (delta.y() == 0) { + event->ignore(); + return; + } - if (!n) { - return result; - } + if (delta.y() > 0) + zoom_in(); + else + zoom_out(); +} - switch (role) { - case PortRole::Data: - break; +void VisualShaderGraphicsView::mousePressEvent(QMouseEvent *event) { + QGraphicsView::mousePressEvent(event); - case PortRole::DataType: + switch (event->button()) { + case Qt::LeftButton: + last_click_coordinate = mapToScene(event->pos()); break; - - case PortRole::ConnectionPolicyRole: - result = QVariant::fromValue(ConnectionPolicy::One); + default: break; + } +} - case PortRole::CaptionVisible: - result = true; - break; +void VisualShaderGraphicsView::mouseMoveEvent(QMouseEvent *event) { + QGraphicsView::mouseMoveEvent(event); - case PortRole::Caption: - switch (port_type) { - case PortType::In: - result = QString::fromStdString(n->get_input_port_name((int)port_index)); - break; - case PortType::Out: - result = QString::fromStdString(n->get_output_port_name((int)port_index)); - break; - default: - std::cout << "Unknown port type" << std::endl; - break; + switch (event->buttons()) { + case Qt::LeftButton: + { + QPointF current_coordinate {mapToScene(event->pos())}; + QPointF difference {last_click_coordinate - current_coordinate}; + setSceneRect(sceneRect().translated(difference.x(), difference.y())); + last_click_coordinate = current_coordinate; } break; + default: + break; } +} - return result; +void VisualShaderGraphicsView::mouseReleaseEvent(QMouseEvent *event) { + QGraphicsView::mouseReleaseEvent(event); } -bool VisualShaderGraph::setPortData(NodeId node_id, - PortType port_type, - PortIndex port_index, - QVariant const &value, - PortRole role) { - Q_UNUSED(node_id); - Q_UNUSED(port_type); - Q_UNUSED(port_index); - Q_UNUSED(value); - Q_UNUSED(role); +void VisualShaderGraphicsView::showEvent(QShowEvent *event) { + QGraphicsView::showEvent(event); - return false; + center_scene(); } -bool VisualShaderGraph::deleteConnection(ConnectionId const connection_id) { - bool status {visual_shader->disconnect_nodes((int)connection_id.outNodeId, (int)connection_id.outPortIndex, (int)connection_id.inNodeId, (int)connection_id.inPortIndex)}; +void VisualShaderGraphicsView::center_scene() { + scene()->setSceneRect(QRectF()); - if (!status) { - return status; - } + QRectF rect {scene()->itemsBoundingRect()}; + QRectF scene_scene {QRectF(0, 0, rect.left() * 2 + rect.width(), rect.top() * 2 + rect.height())}; - Q_EMIT connectionDeleted(connection_id); + if (scene_scene.width() > this->rect().width() || scene_scene.height() > this->rect().height()) { + fitInView(scene_scene, Qt::KeepAspectRatio); + } - return status; + centerOn(scene_scene.center()); } -bool VisualShaderGraph::deleteNode(NodeId const node_id) { - bool status {visual_shader->remove_node((int)node_id)}; +/**********************************************************************/ +/**********************************************************************/ +/**********************************************************************/ +/***** *****/ +/***** VisualShaderNodeGraphicsObject *****/ +/***** *****/ +/**********************************************************************/ +/**********************************************************************/ +/**********************************************************************/ - if (!status) { - return status; - } - _node_geometry_data.erase(node_id); - Q_EMIT nodeDeleted(node_id); +/**********************************************************************/ +/**********************************************************************/ +/**********************************************************************/ +/***** *****/ +/***** VisualShaderConnectionGraphicsObject *****/ +/***** *****/ +/**********************************************************************/ +/**********************************************************************/ +/**********************************************************************/ + - return status; -} -/*************************************/ -/* NodesCustomWidget */ -/*************************************/ +/**********************************************************************/ +/**********************************************************************/ +/**********************************************************************/ +/***** *****/ +/***** NodesCustomWidget *****/ +/***** *****/ +/**********************************************************************/ +/**********************************************************************/ +/**********************************************************************/ NodesCustomWidget::NodesCustomWidget(const std::shared_ptr& node, QWidget* parent) : QWidget(parent), layout(nullptr) { @@ -887,8 +857,10 @@ NodesCustomWidget::NodesCustomWidget(const std::shared_ptr& no } else if (std::dynamic_pointer_cast(node)) { + } else if (std::dynamic_pointer_cast(node)) { + } else { - std::cout << "Unknown node type" << std::endl; + std::cout << "--- Unknown node type ---" << std::endl; } //////////////// Start of Footer //////////////// diff --git a/Editors/VisualShaderEditor.h b/Editors/VisualShaderEditor.h index f13d5a930..a2ce2cf3f 100644 --- a/Editors/VisualShaderEditor.h +++ b/Editors/VisualShaderEditor.h @@ -32,45 +32,40 @@ #include #include #include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include -#include -#include -#include -#include -#include - #include "ResourceTransformations/VisualShader/visual_shader.h" #include "BaseEditor.h" -using ConnectionId = QtNodes::ConnectionId; -using ConnectionPolicy = QtNodes::ConnectionPolicy; -using NodeFlag = QtNodes::NodeFlag; -using NodeId = QtNodes::NodeId; -using NodeRole = QtNodes::NodeRole; -using PortIndex = QtNodes::PortIndex; -using PortRole = QtNodes::PortRole; -using PortType = QtNodes::PortType; -using StyleCollection = QtNodes::StyleCollection; -using QtNodes::InvalidNodeId; - -using QtNodes::BasicGraphicsScene; -using QtNodes::GraphicsView; - -class VisualShaderGraph; +class VisualShaderGraphicsScene; +class VisualShaderGraphicsView; +class VisualShaderNodeGraphicsObject; +class VisualShaderConnectionGraphicsObject; class CreateNodeDialog; -/*************************************/ -/* VisualShaderEditor */ -/*************************************/ +/**********************************************************************/ +/**********************************************************************/ +/**********************************************************************/ +/***** *****/ +/***** VisualShaderEditor *****/ +/***** *****/ +/**********************************************************************/ +/**********************************************************************/ +/**********************************************************************/ // Add const to any function that does not modify the object. @@ -81,17 +76,17 @@ class VisualShaderEditor : public BaseEditor { VisualShaderEditor(MessageModel* model, QWidget* parent = nullptr); ~VisualShaderEditor() override; - void create_node(const QPointF& pos); + void create_node(const QPointF& coordinate); - void add_node(QTreeWidgetItem* selected_item, const QPointF& pos); + void add_node(QTreeWidgetItem* selected_item, const QPointF& coordinate); - void show_create_node_dialog(const QPointF& pos); + void show_create_node_dialog(const QPointF& coordinate); std::vector pasre_node_category_path(const std::string& node_category_path); QTreeWidgetItem* find_or_create_category_item(QTreeWidgetItem* parent, const std::string& category, const std::string& category_path, QTreeWidget* create_node_dialog_nodes_tree, std::unordered_map& category_path_map); Q_SIGNALS: - void on_create_node_dialog_requested(const QPointF& pos = {0, 0}); // {0, 0} is the top-left corner of the scene. + void on_create_node_dialog_requested(const QPointF& coordinate = {0, 0}); // {0, 0} is the top-left corner of the scene. private Q_SLOTS: void on_create_node_button_pressed(); @@ -104,9 +99,8 @@ class VisualShaderEditor : public BaseEditor { QHBoxLayout* scene_layer_layout; QWidget* scene_layer; // Layer having the scene. - VisualShaderGraph* graph; - BasicGraphicsScene* scene; - GraphicsView* view; + VisualShaderGraphicsScene* scene; + VisualShaderGraphicsView* view; QWidget* top_layer; // Layer having the menu bar. QHBoxLayout* menu_bar; @@ -141,9 +135,15 @@ class VisualShaderEditor : public BaseEditor { CreateNodeDialog* create_node_dialog; }; -/*************************************/ -/* CreateNodeDialog */ -/*************************************/ +/**********************************************************************/ +/**********************************************************************/ +/**********************************************************************/ +/***** *****/ +/***** CreateNodeDialog *****/ +/***** *****/ +/**********************************************************************/ +/**********************************************************************/ +/**********************************************************************/ class CreateNodeDialog : public QDialog { Q_OBJECT @@ -177,88 +177,132 @@ class CreateNodeDialog : public QDialog { QTreeWidgetItem* selected_item; }; -/*************************************/ -/* VisualShaderGraph */ -/*************************************/ +/**********************************************************************/ +/**********************************************************************/ +/**********************************************************************/ +/***** *****/ +/***** VisualShaderGraphicsScene *****/ +/***** *****/ +/**********************************************************************/ +/**********************************************************************/ +/**********************************************************************/ -/** - * The class implements a bare minimum required to demonstrate a model-based - * graph. - */ -class VisualShaderGraph : public QtNodes::AbstractGraphModel -{ - Q_OBJECT +class VisualShaderGraphicsScene : public QGraphicsScene { + Q_OBJECT public: - struct NodeGeometryData - { - QSize size; - QPointF pos; - }; - - VisualShaderGraph(); - - ~VisualShaderGraph() override; - - std::unordered_set allNodeIds() const override; - - std::unordered_set allConnectionIds(NodeId const node_id) const override; - - std::unordered_set connections(NodeId node_id, - PortType port_type, - PortIndex port_index) const override; + VisualShaderGraphicsScene(QObject *parent = nullptr); - bool connectionExists(ConnectionId const connection_id) const override; - - void add_node_custom(const std::shared_ptr& node, const QPointF& offset); + ~VisualShaderGraphicsScene() = default; +}; - /** - * Connection is possible when graph contains no connectivity data - * in both directions `Out -> In` and `In -> Out`. - */ - bool connectionPossible(ConnectionId const connection_id) const override; +/**********************************************************************/ +/**********************************************************************/ +/**********************************************************************/ +/***** *****/ +/***** VisualShaderGraphicsView *****/ +/***** *****/ +/**********************************************************************/ +/**********************************************************************/ +/**********************************************************************/ - void addConnection(ConnectionId const connection_id) override; +class VisualShaderGraphicsView : public QGraphicsView { + Q_OBJECT - bool nodeExists(NodeId const node_id) const override; +public: + VisualShaderGraphicsView(VisualShaderGraphicsScene *scene, QWidget *parent = nullptr); - QVariant nodeData(NodeId node_id, NodeRole role) const override; + ~VisualShaderGraphicsView(); - bool setNodeData(NodeId node_id, NodeRole role, QVariant value) override; +private Q_SLOTS: + void setup_zoom(const float& zoom); - QVariant portData(NodeId node_id, - PortType port_type, - PortIndex port_index, - PortRole role) const override; + void on_create_node_action_triggered(); + void on_delete_node_action_triggered(); - bool setPortData(NodeId node_id, - PortType port_type, - PortIndex port_index, - QVariant const &value, - PortRole role = PortRole::Data) override; + void zoom_in(); + void reset_zoom(); + void zoom_out(); - bool deleteConnection(ConnectionId const connection_id) override; +Q_SIGNALS: + void zoom_changed(const float& zoom); - bool deleteNode(NodeId const node_id) override; +private: + // Style + QColor background_color = QColor(53, 53, 53); + QColor fine_grid_color = QColor(60, 60, 60); + QColor coarse_grid_color = QColor(25, 25, 25); + + // Scene Rect + float t_size = std::numeric_limits::max(); // 32767 + float rect_x = t_size; + float rect_y = -1.0f * t_size; + float rect_width = t_size * 2.0f; + float rect_height = t_size * 2.0f; + + // Zoom + float zoom = 1.0f; + float zoom_step = 1.2f; + float zoom_min; + float zoom_max; + + QMenu* context_menu; + QAction* create_node_action; + + QAction* delete_node_action; + + QPointF last_click_coordinate; + + void drawBackground(QPainter *painter, const QRectF &r) override; + void contextMenuEvent(QContextMenuEvent *event) override; + void wheelEvent(QWheelEvent *event) override; + void mousePressEvent(QMouseEvent *event) override; + void mouseMoveEvent(QMouseEvent *event) override; + void mouseReleaseEvent(QMouseEvent *event) override; + void showEvent(QShowEvent *event) override; + + void center_scene(); +}; - void register_visual_shader(VisualShader* visual_shader) const { this->visual_shader = visual_shader; } +/**********************************************************************/ +/**********************************************************************/ +/**********************************************************************/ +/***** *****/ +/***** VisualShaderNodeGraphicsObject *****/ +/***** *****/ +/**********************************************************************/ +/**********************************************************************/ +/**********************************************************************/ - void set_visual_shader_editor(VisualShaderEditor* visual_shader_editor) const { this->visual_shader_editor = visual_shader_editor; } +class VisualShaderNodeGraphicsObject : public QGraphicsObject { + Q_OBJECT -private: - mutable std::unordered_map _node_geometry_data; +}; - mutable VisualShader* visual_shader; - mutable VisualShaderEditor* visual_shader_editor; +/**********************************************************************/ +/**********************************************************************/ +/**********************************************************************/ +/***** *****/ +/***** VisualShaderConnectionGraphicsObject *****/ +/***** *****/ +/**********************************************************************/ +/**********************************************************************/ +/**********************************************************************/ - NodeId newNodeId() override { return (NodeId)visual_shader->get_valid_node_id(); } +class VisualShaderConnectionGraphicsObject : public QGraphicsObject { + Q_OBJECT - NodeId addNode(QString const node_type = QString()) override; }; -/*************************************/ -/* NodesCustomWidget */ -/*************************************/ +/**********************************************************************/ +/**********************************************************************/ +/**********************************************************************/ +/***** *****/ +/***** NodesCustomWidget *****/ +/***** *****/ +/**********************************************************************/ +/**********************************************************************/ +/**********************************************************************/ class NodesCustomWidget : public QWidget { diff --git a/Submodules/nodeeditor b/Submodules/nodeeditor index 6cb2126dd..cb1c1c564 160000 --- a/Submodules/nodeeditor +++ b/Submodules/nodeeditor @@ -1 +1 @@ -Subproject commit 6cb2126dd662dfd3731a1a99bd7e05331dfaedc6 +Subproject commit cb1c1c564f284b02a14a08cd0836db2f7ecfc862 From 56d2878087046161c152aea30dcdfb5a868f5c07 Mon Sep 17 00:00:00 2001 From: Saif Kandil <74428638+k0T0z@users.noreply.github.com> Date: Fri, 13 Sep 2024 14:09:01 +0300 Subject: [PATCH 18/56] Few bug fixes and improvements --- CMakeLists.txt | 1 + Editors/VisualShaderEditor.cpp | 378 +++++++++++++++++++++++++++++---- Editors/VisualShaderEditor.h | 104 +++++++-- 3 files changed, 424 insertions(+), 59 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2fc3abef7..0d4a1ae70 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -72,6 +72,7 @@ set(CMAKE_AUTORCC ON) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) +# TODO: Do we need to disable exceptions in RGM like we do in ENIGMA? # if(MSVC) # string(APPEND CMAKE_CXX_FLAGS " /EHsc /wd26812") # string(APPEND CMAKE_C_FLAGS " /EHsc /wd26812") diff --git a/Editors/VisualShaderEditor.cpp b/Editors/VisualShaderEditor.cpp index cced106a2..f3ff9b150 100644 --- a/Editors/VisualShaderEditor.cpp +++ b/Editors/VisualShaderEditor.cpp @@ -60,6 +60,9 @@ VisualShaderEditor::VisualShaderEditor(MessageModel* model, QWidget* parent) : B create_node_button(nullptr), preview_shader_button(nullptr), create_node_action(nullptr), + zoom_in_button(nullptr), + reset_zoom_button(nullptr), + zoom_out_button(nullptr), create_node_dialog(nullptr) { visual_shader = new VisualShader(); @@ -84,7 +87,7 @@ VisualShaderEditor::VisualShaderEditor(MessageModel* model, QWidget* parent) : B scene_layer_layout->setSizeConstraint(QLayout::SetNoConstraint); scene_layer_layout->setAlignment(Qt::AlignVCenter | Qt::AlignHCenter); - scene = new VisualShaderGraphicsScene(); + scene = new VisualShaderGraphicsScene(visual_shader); view = new VisualShaderGraphicsView(scene, scene_layer); view->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom @@ -128,6 +131,24 @@ VisualShaderEditor::VisualShaderEditor(MessageModel* model, QWidget* parent) : B preview_shader_button->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom menu_bar->addWidget(preview_shader_button); + zoom_in_button = new QPushButton("Zoom In", scene_layer); + zoom_in_button->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + zoom_in_button->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom + menu_bar->addWidget(zoom_in_button); + QObject::connect(zoom_in_button, &QPushButton::pressed, view, &VisualShaderGraphicsView::zoom_in); + + reset_zoom_button = new QPushButton("Reset Zoom", scene_layer); + reset_zoom_button->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + reset_zoom_button->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom + menu_bar->addWidget(reset_zoom_button); + QObject::connect(reset_zoom_button, &QPushButton::pressed, view, &VisualShaderGraphicsView::reset_zoom); + + zoom_out_button = new QPushButton("Zoom Out", scene_layer); + zoom_out_button->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + zoom_out_button->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom + menu_bar->addWidget(zoom_out_button); + QObject::connect(zoom_out_button, &QPushButton::pressed, view, &VisualShaderGraphicsView::zoom_out); + // Set the top layer layout. top_layer->setLayout(menu_bar); @@ -196,6 +217,9 @@ VisualShaderEditor::VisualShaderEditor(MessageModel* model, QWidget* parent) : B VisualShaderEditor::~VisualShaderEditor() { // TODO: We don't need to delete the pointers as they are destroyed when the parent is destroyed. if (create_node_dialog) delete create_node_dialog; + if (zoom_out_button) delete zoom_out_button; + if (reset_zoom_button) delete reset_zoom_button; + if (zoom_in_button) delete zoom_in_button; if (create_node_action) delete create_node_action; if (preview_shader_button) delete preview_shader_button; if (create_node_button) delete create_node_button; @@ -272,6 +296,12 @@ void VisualShaderEditor::add_node(QTreeWidgetItem* selected_item, const QPointF& return; } + int new_node_id {visual_shader->get_valid_node_id()}; + + if (new_node_id == VisualShader::NODE_ID_INVALID) { + return; + } + // Instantiate the node based on the type std::shared_ptr node; @@ -326,7 +356,13 @@ void VisualShaderEditor::add_node(QTreeWidgetItem* selected_item, const QPointF& return; } - std::cout << "END of START hahaha" << std::endl; + bool result {visual_shader->add_node(node, {(float)coordinate.x(), (float)coordinate.y()}, new_node_id)}; + + if (!result) { + return; + } + + scene->add_node(new_node_id); } void VisualShaderEditor::show_create_node_dialog(const QPointF& coordinate) { @@ -515,8 +551,101 @@ void CreateNodeDialog::update_selected_item() { /**********************************************************************/ /**********************************************************************/ -VisualShaderGraphicsScene::VisualShaderGraphicsScene(QObject *parent) : QGraphicsScene(parent) { - +////////////////////////////// +// Public functions +////////////////////////////// + +VisualShaderGraphicsScene::VisualShaderGraphicsScene(VisualShader* vs, QObject* parent) : QGraphicsScene(parent), vs(vs) { + setItemIndexMethod(QGraphicsScene::NoIndex); // https://doc.qt.io/qt-6/qgraphicsscene.html#ItemIndexMethod-enum + + // Populate the scene with nodes + std::vector nodes {vs->get_nodes()}; + + for (const int& node_id : nodes) { + const std::shared_ptr node {vs->get_node(node_id)}; + + if (!node) { + continue; + } + + VisualShaderNodeGraphicsObject* n_o {new VisualShaderNodeGraphicsObject(vs, node_id)}; + node_graphics_objects[node_id] = n_o; + this->addItem(n_o); + this->update(); + } + + // Populate the scene with connections + std::vector connections {vs->get_connections()}; + + for (const int& connection_id : connections) { + const VisualShader::Connection connection {vs->get_connection(connection_id)}; + + VisualShaderConnectionGraphicsObject* c_o {new VisualShaderConnectionGraphicsObject()}; + connection_graphics_objects[connection_id] = c_o; + this->addItem(c_o); + this->update(); + } +} + +VisualShaderGraphicsScene::~VisualShaderGraphicsScene() { + +} + +bool VisualShaderGraphicsScene::add_node(const int& n_id) { + // Make sure the node doesn't already exist, we don't want to overwrite a node. + if (node_graphics_objects.find(n_id) != node_graphics_objects.end()) { + vs->remove_node(n_id); + return false; + } + + const std::shared_ptr node {vs->get_node(n_id)}; + + if (!node) { + vs->remove_node(n_id); + return false; + } + + VisualShaderNodeGraphicsObject* n_o {new VisualShaderNodeGraphicsObject(vs, n_id)}; + + node_graphics_objects[n_id] = n_o; + + addItem(n_o); + + return true; +} + +bool VisualShaderGraphicsScene::delete_node() { + return false; +} + +bool VisualShaderGraphicsScene::add_connection() { + return false; +} + +bool VisualShaderGraphicsScene::delete_connection() { + return false; +} + +VisualShaderNodeGraphicsObject* VisualShaderGraphicsScene::get_node_graphics_object(const int& n_id) const { + VisualShaderNodeGraphicsObject* n_o {nullptr}; + + auto it {node_graphics_objects.find(n_id)}; + if (it != node_graphics_objects.end()) { + n_o = it->second; + } + + return n_o; +} + +VisualShaderConnectionGraphicsObject* VisualShaderGraphicsScene::get_connection_graphics_object(const int& c_id) const { + VisualShaderConnectionGraphicsObject* c_o {nullptr}; + + auto it {connection_graphics_objects.find(c_id)}; + if (it != connection_graphics_objects.end()) { + c_o = it->second; + } + + return c_o; } /**********************************************************************/ @@ -536,7 +665,10 @@ VisualShaderGraphicsScene::VisualShaderGraphicsScene(QObject *parent) : QGraphic VisualShaderGraphicsView::VisualShaderGraphicsView(VisualShaderGraphicsScene *scene, QWidget *parent) : QGraphicsView(scene, parent), context_menu(nullptr), create_node_action(nullptr), - delete_node_action(nullptr) { + delete_node_action(nullptr), + zoom_in_action(nullptr), + reset_zoom_action(nullptr), + zoom_out_action(nullptr) { setDragMode(QGraphicsView::ScrollHandDrag); setRenderHint(QPainter::Antialiasing); @@ -557,6 +689,8 @@ VisualShaderGraphicsView::VisualShaderGraphicsView(VisualShaderGraphicsScene *sc setSceneRect(rect_x, rect_y, rect_width, rect_height); + reset_zoom(); + // Set the context menu context_menu = new QMenu(this); create_node_action = new QAction(QStringLiteral("Create Node"), context_menu); @@ -568,35 +702,36 @@ VisualShaderGraphicsView::VisualShaderGraphicsView(VisualShaderGraphicsScene *sc delete_node_action->setShortcut(QKeySequence(QKeySequence::Delete)); QObject::connect(delete_node_action, &QAction::triggered, this, &VisualShaderGraphicsView::on_delete_node_action_triggered); context_menu->addAction(delete_node_action); + + zoom_in_action = new QAction(QStringLiteral("Zoom In"), context_menu); + zoom_in_action->setShortcutContext(Qt::ShortcutContext::WidgetShortcut); + zoom_in_action->setShortcut(QKeySequence(QKeySequence::ZoomIn)); + QObject::connect(zoom_in_action, &QAction::triggered, this, &VisualShaderGraphicsView::zoom_in); + context_menu->addAction(zoom_in_action); + + reset_zoom_action = new QAction(QStringLiteral("Reset Zoom"), context_menu); + QObject::connect(reset_zoom_action, &QAction::triggered, this, &VisualShaderGraphicsView::reset_zoom); + context_menu->addAction(reset_zoom_action); + + zoom_out_action = new QAction(QStringLiteral("Zoom Out"), context_menu); + zoom_out_action->setShortcutContext(Qt::ShortcutContext::WidgetShortcut); + zoom_out_action->setShortcut(QKeySequence(QKeySequence::ZoomOut)); + QObject::connect(zoom_out_action, &QAction::triggered, this, &VisualShaderGraphicsView::zoom_out); + context_menu->addAction(zoom_out_action); } VisualShaderGraphicsView::~VisualShaderGraphicsView() { - if (context_menu) delete context_menu; + if (zoom_out_action) delete zoom_out_action; + if (reset_zoom_action) delete reset_zoom_action; + if (zoom_in_action) delete zoom_in_action; if (create_node_action) delete create_node_action; + if (context_menu) delete context_menu; } ////////////////////////////// // Private slots ////////////////////////////// -void VisualShaderGraphicsView::setup_zoom(const float& zoom) { - float t_zoom {zoom}; - - t_zoom = std::max(zoom_min, std::min(zoom_max, t_zoom)); - - if (t_zoom <= 0.0f) - return; - - if (t_zoom == transform().m11()) - return; - - QTransform matrix; - matrix.scale(t_zoom, t_zoom); - setTransform(matrix, false); - - Q_EMIT zoom_changed(t_zoom); -} - void VisualShaderGraphicsView::on_create_node_action_triggered() { } @@ -606,12 +741,11 @@ void VisualShaderGraphicsView::on_delete_node_action_triggered() { } void VisualShaderGraphicsView::zoom_in() { - const float factor {std::pow(zoom_step, 1.0f)}; + const float factor {std::pow(zoom_step, zoom)}; QTransform t {transform()}; t.scale(factor, factor); if (t.m11() >= zoom_max) { - setup_zoom(t.m11()); return; } @@ -620,16 +754,20 @@ void VisualShaderGraphicsView::zoom_in() { } void VisualShaderGraphicsView::reset_zoom() { - + if ((float)transform().m11() == zoom) { + return; + } + + resetTransform(); // Reset the zoom level to 1.0f + Q_EMIT zoom_changed(transform().m11()); } void VisualShaderGraphicsView::zoom_out() { - const float factor {std::pow(zoom_step, -1.0f)}; + const float factor {std::pow(zoom_step, -1.0f * zoom)}; QTransform t {transform()}; t.scale(factor, factor); if (t.m11() <= zoom_min) { - setup_zoom(t.m11()); return; } @@ -650,10 +788,10 @@ void VisualShaderGraphicsView::drawBackground(QPainter *painter, const QRectF &r QPointF tl {mapToScene(window_rect.topLeft())}; QPointF br {mapToScene(window_rect.bottomRight())}; - float left {(float)std::floor(tl.x() / grid_step - 0.5f)}; - float right {(float)std::floor(br.x() / grid_step + 1.0f)}; - float bottom {(float)std::floor(tl.y() / grid_step - 0.5f)}; - float top {(float)std::floor(br.y() / grid_step + 1.0f)}; + float left {std::floor((float)tl.x() / grid_step)}; + float right {std::ceil((float)br.x() / grid_step)}; + float bottom {std::floor((float)tl.y() / grid_step)}; + float top {std::ceil((float)br.y() / grid_step)}; // Vertical lines for (int xi {(int)left}; xi <= (int)right; ++xi) { @@ -678,8 +816,9 @@ void VisualShaderGraphicsView::drawBackground(QPainter *painter, const QRectF &r } void VisualShaderGraphicsView::contextMenuEvent(QContextMenuEvent *event) { + QGraphicsView::contextMenuEvent(event); + if (itemAt(event->pos())) { - QGraphicsView::contextMenuEvent(event); return; } @@ -689,6 +828,8 @@ void VisualShaderGraphicsView::contextMenuEvent(QContextMenuEvent *event) { } void VisualShaderGraphicsView::wheelEvent(QWheelEvent *event) { + float t_zoom {(float)transform().m11()}; + const QPoint delta {event->angleDelta()}; if (delta.y() == 0) { @@ -696,10 +837,13 @@ void VisualShaderGraphicsView::wheelEvent(QWheelEvent *event) { return; } - if (delta.y() > 0) - zoom_in(); - else - zoom_out(); + if (delta.y() > 0 && (std::abs(t_zoom - zoom_max) > std::numeric_limits::epsilon())) { + zoom_in(); + } else if (delta.y() < 0 && (std::abs(t_zoom - zoom_min) > std::numeric_limits::epsilon())) { + zoom_out(); + } else { + event->ignore(); + } } void VisualShaderGraphicsView::mousePressEvent(QMouseEvent *event) { @@ -738,20 +882,48 @@ void VisualShaderGraphicsView::mouseReleaseEvent(QMouseEvent *event) { void VisualShaderGraphicsView::showEvent(QShowEvent *event) { QGraphicsView::showEvent(event); - center_scene(); + if (!scene()) { + return; + } + + if (scene()->items().isEmpty()) { + return; + } + + std::cout << "Changing view port to fit items..." << std::endl; + + move_view_to_fit_items(); } -void VisualShaderGraphicsView::center_scene() { - scene()->setSceneRect(QRectF()); +void VisualShaderGraphicsView::move_view_to_fit_items() { + QRectF items_bounding_rect {scene()->itemsBoundingRect()}; + items_bounding_rect.adjust(-fit_in_view_margin, -fit_in_view_margin, fit_in_view_margin, fit_in_view_margin); - QRectF rect {scene()->itemsBoundingRect()}; - QRectF scene_scene {QRectF(0, 0, rect.left() * 2 + rect.width(), rect.top() * 2 + rect.height())}; + QPointF scene_tl {this->scene()->sceneRect().topLeft()}; + QPointF scene_br {this->scene()->sceneRect().bottomRight()}; + QPointF items_tl {items_bounding_rect.topLeft()}; + QPointF items_br {items_bounding_rect.bottomRight()}; + + // Make sure the items bounding rect is inside the scene rect + if (items_tl.x() < scene_tl.x()) { + items_bounding_rect.setLeft(scene_tl.x()); + } + + if (items_tl.y() > scene_tl.y()) { + items_bounding_rect.setTop(scene_tl.y()); + } - if (scene_scene.width() > this->rect().width() || scene_scene.height() > this->rect().height()) { - fitInView(scene_scene, Qt::KeepAspectRatio); + if (items_br.x() > scene_br.x()) { + items_bounding_rect.setRight(scene_br.x()); } - centerOn(scene_scene.center()); + if (items_br.y() < scene_br.y()) { + items_bounding_rect.setBottom(scene_br.y()); + } + + fitInView(items_bounding_rect, Qt::KeepAspectRatio); + + centerOn(items_bounding_rect.center()); } /**********************************************************************/ @@ -764,7 +936,107 @@ void VisualShaderGraphicsView::center_scene() { /**********************************************************************/ /**********************************************************************/ +VisualShaderNodeGraphicsObject::VisualShaderNodeGraphicsObject(VisualShader* vs, const int& n_id, QGraphicsItem* parent) : QGraphicsObject(parent), + vs(vs), + n_id(n_id) { + setFlag(QGraphicsItem::ItemDoesntPropagateOpacityToChildren, true); + setFlag(QGraphicsItem::ItemIsFocusable, true); + setFlag(QGraphicsItem::ItemIsMovable, true); + setFlag(QGraphicsItem::ItemIsSelectable, true); + setFlag(QGraphicsItem::ItemSendsScenePositionChanges, true); + + setCacheMode(QGraphicsItem::DeviceCoordinateCache); + + setVisible(true); + setOpacity(this->opacity); + setZValue(0); + + TVector2 coordinate {vs->get_node_coordinate(n_id)}; + + std::cout << "Node ID: " << n_id << " Coordinate: " << coordinate.x << ", " << coordinate.y << std::endl; + + setPos(coordinate.x, coordinate.y); +} + +VisualShaderNodeGraphicsObject::~VisualShaderNodeGraphicsObject() { + +} + +QRectF VisualShaderNodeGraphicsObject::boundingRect() const { + float w_margin {(float)size.width() * rect_margin_ratio}; + float h_margin {(float)size.height() * rect_margin_ratio}; + + QMargins margins((int)w_margin, (int)h_margin, (int)w_margin, (int)h_margin); + + QRectF r({0.0f, 0.0f}, size); + + std::cout << "Bounding rect: " << r.x() << ", " << r.y() << ", " << r.width() << ", " << r.height() << std::endl; + + return r.marginsAdded(margins); +} + +void VisualShaderNodeGraphicsObject::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { + std::cout << " ----------------- Paint method called for node ID: " << n_id << std::endl; + + const std::shared_ptr n {vs->get_node(n_id)}; + + if (!n) { + return; + } + + painter->setClipRect(option->exposedRect); + + { + // Draw Node Rect + QColor rect_color; + if (isSelected()) { + rect_color = this->normal_boundary_color; + } else { + rect_color = this->selected_boundary_color; + } + + QPen p(rect_color, this->pen_width); + painter->setPen(p); + + painter->setBrush(this->fill_color); + + QRectF boundary(0, 0, size.width(), size.height()); + + painter->drawRoundedRect(boundary, this->corner_radius, this->corner_radius); + } + + { + // Draw Caption + QString caption {QString::fromStdString(n->get_caption())}; + + QFont f {painter->font()}; + f.setBold(true); + QFontMetrics fm(f); + QRect text_rect {fm.boundingRect(caption)}; + + // Calculate the coordinate of the caption + float x {0.5f * (float)(size.width() - text_rect.width())}; + float y {0.5f * this->port_spacing + (float)text_rect.height()}; + QPointF coordinate {QPointF(x, y)}; + + painter->setFont(f); + painter->setPen(this->font_color); + painter->drawText(coordinate, caption); + + f.setBold(false); + painter->setFont(f); + } + + { + // Draw Input Ports + const int in_port_count {n->get_input_port_count()}; + } + + { + // Draw Output Ports + } +} /**********************************************************************/ /**********************************************************************/ @@ -776,7 +1048,21 @@ void VisualShaderGraphicsView::center_scene() { /**********************************************************************/ /**********************************************************************/ +VisualShaderConnectionGraphicsObject::VisualShaderConnectionGraphicsObject(QGraphicsItem* parent) : QGraphicsObject(parent) { + +} + +VisualShaderConnectionGraphicsObject::~VisualShaderConnectionGraphicsObject() { + +} +QRectF VisualShaderConnectionGraphicsObject::boundingRect() const { + return QRectF(); +} + +void VisualShaderConnectionGraphicsObject::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { + +} /**********************************************************************/ /**********************************************************************/ diff --git a/Editors/VisualShaderEditor.h b/Editors/VisualShaderEditor.h index a2ce2cf3f..9676da0c2 100644 --- a/Editors/VisualShaderEditor.h +++ b/Editors/VisualShaderEditor.h @@ -107,6 +107,9 @@ class VisualShaderEditor : public BaseEditor { QPushButton* create_node_button; QPushButton* preview_shader_button; + QPushButton* zoom_in_button; + QPushButton* reset_zoom_button; + QPushButton* zoom_out_button; QAction* create_node_action; @@ -191,9 +194,28 @@ class VisualShaderGraphicsScene : public QGraphicsScene { Q_OBJECT public: - VisualShaderGraphicsScene(QObject *parent = nullptr); + VisualShaderGraphicsScene(VisualShader* vs, QObject *parent = nullptr); - ~VisualShaderGraphicsScene() = default; + ~VisualShaderGraphicsScene(); + + bool add_node(const int& n_id); + bool delete_node(); + + bool add_connection(); + bool delete_connection(); + + VisualShaderNodeGraphicsObject* get_node_graphics_object(const int& n_id) const; + VisualShaderConnectionGraphicsObject* get_connection_graphics_object(const int& c_id) const; + + VisualShader* get_visual_shader() const { return vs; } + +private: + VisualShader* vs; + + std::unordered_map node_graphics_objects; + std::unordered_map connection_graphics_objects; + + VisualShaderConnectionGraphicsObject* one_free_end_connection; }; /**********************************************************************/ @@ -214,16 +236,15 @@ class VisualShaderGraphicsView : public QGraphicsView { ~VisualShaderGraphicsView(); -private Q_SLOTS: - void setup_zoom(const float& zoom); - - void on_create_node_action_triggered(); - void on_delete_node_action_triggered(); - +public Q_SLOTS: void zoom_in(); void reset_zoom(); void zoom_out(); +private Q_SLOTS: + void on_create_node_action_triggered(); + void on_delete_node_action_triggered(); + Q_SIGNALS: void zoom_changed(const float& zoom); @@ -235,10 +256,12 @@ private Q_SLOTS: // Scene Rect float t_size = std::numeric_limits::max(); // 32767 - float rect_x = t_size; - float rect_y = -1.0f * t_size; - float rect_width = t_size * 2.0f; - float rect_height = t_size * 2.0f; + float rect_x = -1.0f * t_size * 0.5f; + float rect_y = -1.0f * t_size * 0.5f; + float rect_width = t_size; + float rect_height = t_size; + + float fit_in_view_margin = 50.0f; // Zoom float zoom = 1.0f; @@ -251,6 +274,10 @@ private Q_SLOTS: QAction* delete_node_action; + QAction* zoom_in_action; + QAction* reset_zoom_action; + QAction* zoom_out_action; + QPointF last_click_coordinate; void drawBackground(QPainter *painter, const QRectF &r) override; @@ -261,7 +288,7 @@ private Q_SLOTS: void mouseReleaseEvent(QMouseEvent *event) override; void showEvent(QShowEvent *event) override; - void center_scene(); + void move_view_to_fit_items(); }; /**********************************************************************/ @@ -277,6 +304,49 @@ private Q_SLOTS: class VisualShaderNodeGraphicsObject : public QGraphicsObject { Q_OBJECT +public: + VisualShaderNodeGraphicsObject(VisualShader* vs, const int& n_id, QGraphicsItem *parent = nullptr); + ~VisualShaderNodeGraphicsObject(); + +private: + VisualShader* vs; + int n_id; + + // Style + QColor normal_boundary_color = QColor(255, 255, 255); + QColor selected_boundary_color = QColor(255, 165, 0); + QColor gradient_color0 = QColor(80, 80, 80); + QColor gradient_color1 = QColor(64, 64, 64); + QColor gradient_color2 = QColor(58, 58, 58); + QColor shadow_color = QColor(20, 20, 20); + QColor font_color = QColor(255, 255, 255); + QColor font_color_faded = QColor(169, 169, 169); + QColor connection_point_color = QColor(169, 169, 169); + QColor filled_connection_point_color = QColor(0, 255, 255); + QColor error_color = QColor(255, 0, 0); + QColor warning_color = QColor(128, 128, 0); + QColor fill_color = QColor(0, 0, 0, 0); + + float pen_width = 1.0f; + float hovered_pen_width = 1.5f; + + float opacity = 0.8f; + float corner_radius = 3.0f; + + float port_spacing = 10.0f; + + float rect_margin_ratio = 0.2f; + + // Ports Style + float connected_port_diameter = 8.0f; + float unconnected_port_diameter = 6.0f; + + // Size + QSizeF size = QSizeF(100.0f, 200.0f); + + QRectF boundingRect() const override; + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override; + }; /**********************************************************************/ @@ -292,6 +362,14 @@ class VisualShaderNodeGraphicsObject : public QGraphicsObject { class VisualShaderConnectionGraphicsObject : public QGraphicsObject { Q_OBJECT +public: + VisualShaderConnectionGraphicsObject(QGraphicsItem *parent = nullptr); + ~VisualShaderConnectionGraphicsObject(); + +private: + QRectF boundingRect() const override; + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override; + }; /**********************************************************************/ From 3a784aa65f7ffcd64a389d5a9e6228015a8f5c00 Mon Sep 17 00:00:00 2001 From: Saif Kandil <74428638+k0T0z@users.noreply.github.com> Date: Sun, 15 Sep 2024 00:52:53 +0300 Subject: [PATCH 19/56] completed the connection graphics rendering algorithm and large improvements --- Editors/VisualShaderEditor.cpp | 650 +++++++++++++++++++++++++++++++-- Editors/VisualShaderEditor.h | 149 +++++++- 2 files changed, 759 insertions(+), 40 deletions(-) diff --git a/Editors/VisualShaderEditor.cpp b/Editors/VisualShaderEditor.cpp index f3ff9b150..4e4713f89 100644 --- a/Editors/VisualShaderEditor.cpp +++ b/Editors/VisualShaderEditor.cpp @@ -571,7 +571,6 @@ VisualShaderGraphicsScene::VisualShaderGraphicsScene(VisualShader* vs, QObject* VisualShaderNodeGraphicsObject* n_o {new VisualShaderNodeGraphicsObject(vs, node_id)}; node_graphics_objects[node_id] = n_o; this->addItem(n_o); - this->update(); } // Populate the scene with connections @@ -580,11 +579,16 @@ VisualShaderGraphicsScene::VisualShaderGraphicsScene(VisualShader* vs, QObject* for (const int& connection_id : connections) { const VisualShader::Connection connection {vs->get_connection(connection_id)}; - VisualShaderConnectionGraphicsObject* c_o {new VisualShaderConnectionGraphicsObject()}; + VisualShaderConnectionGraphicsObject* c_o {new VisualShaderConnectionGraphicsObject(connection.from_node, connection.from_port)}; connection_graphics_objects[connection_id] = c_o; this->addItem(c_o); - this->update(); } + + QObject::connect(this, &VisualShaderGraphicsScene::node_moved, this, &VisualShaderGraphicsScene::on_node_moved); + + VisualShaderConnectionGraphicsObject* c_o {new VisualShaderConnectionGraphicsObject(1, 0)}; + connection_graphics_objects[0] = c_o; + this->addItem(c_o); } VisualShaderGraphicsScene::~VisualShaderGraphicsScene() { @@ -605,6 +609,13 @@ bool VisualShaderGraphicsScene::add_node(const int& n_id) { return false; } + if (vs->get_node_coordinate(n_id).x > this->sceneRect().bottomRight().x() || + vs->get_node_coordinate(n_id).y < this->sceneRect().bottomRight().y() || + vs->get_node_coordinate(n_id).x < this->sceneRect().topLeft().x() || + vs->get_node_coordinate(n_id).y > this->sceneRect().topLeft().y()) { + std::cout << "Node is out of scene bounds" << std::endl; + } + VisualShaderNodeGraphicsObject* n_o {new VisualShaderNodeGraphicsObject(vs, n_id)}; node_graphics_objects[n_id] = n_o; @@ -648,6 +659,11 @@ VisualShaderConnectionGraphicsObject* VisualShaderGraphicsScene::get_connection_ return c_o; } +void VisualShaderGraphicsScene::on_node_moved(const int& n_id, const QPointF& new_position) { + // Here we will move all connections that are connected to the node + // std::cout << "Node moved: " << n_id << " to " << new_position.x() << ", " << new_position.y() << std::endl; +} + /**********************************************************************/ /**********************************************************************/ /**********************************************************************/ @@ -882,6 +898,10 @@ void VisualShaderGraphicsView::mouseReleaseEvent(QMouseEvent *event) { void VisualShaderGraphicsView::showEvent(QShowEvent *event) { QGraphicsView::showEvent(event); + move_view_to_fit_items(); +} + +void VisualShaderGraphicsView::move_view_to_fit_items() { if (!scene()) { return; } @@ -892,10 +912,6 @@ void VisualShaderGraphicsView::showEvent(QShowEvent *event) { std::cout << "Changing view port to fit items..." << std::endl; - move_view_to_fit_items(); -} - -void VisualShaderGraphicsView::move_view_to_fit_items() { QRectF items_bounding_rect {scene()->itemsBoundingRect()}; items_bounding_rect.adjust(-fit_in_view_margin, -fit_in_view_margin, fit_in_view_margin, fit_in_view_margin); @@ -924,6 +940,16 @@ void VisualShaderGraphicsView::move_view_to_fit_items() { fitInView(items_bounding_rect, Qt::KeepAspectRatio); centerOn(items_bounding_rect.center()); + + if ((float)transform().m11() > zoom_max) { + std::cout << "Current zoom level is greater than the maximum zoom level." << std::endl; + std::cout << "Maybe due to having a very large distance between the nodes." << std::endl; + } + + if ((float)transform().m11() < zoom_min) { + std::cout << "Current zoom level is less than the minimum zoom level." << std::endl; + std::cout << "Maybe due to having all the nodes outside the scene bounds." << std::endl; + } } /**********************************************************************/ @@ -938,7 +964,9 @@ void VisualShaderGraphicsView::move_view_to_fit_items() { VisualShaderNodeGraphicsObject::VisualShaderNodeGraphicsObject(VisualShader* vs, const int& n_id, QGraphicsItem* parent) : QGraphicsObject(parent), vs(vs), - n_id(n_id) { + n_id(n_id), + rect_margin(0.0f), + rect_padding(0.0f) { setFlag(QGraphicsItem::ItemDoesntPropagateOpacityToChildren, true); setFlag(QGraphicsItem::ItemIsFocusable, true); setFlag(QGraphicsItem::ItemIsMovable, true); @@ -954,8 +982,6 @@ VisualShaderNodeGraphicsObject::VisualShaderNodeGraphicsObject(VisualShader* vs, TVector2 coordinate {vs->get_node_coordinate(n_id)}; - std::cout << "Node ID: " << n_id << " Coordinate: " << coordinate.x << ", " << coordinate.y << std::endl; - setPos(coordinate.x, coordinate.y); } @@ -964,21 +990,27 @@ VisualShaderNodeGraphicsObject::~VisualShaderNodeGraphicsObject() { } QRectF VisualShaderNodeGraphicsObject::boundingRect() const { - float w_margin {(float)size.width() * rect_margin_ratio}; - float h_margin {(float)size.height() * rect_margin_ratio}; + QRectF r({0.0f, 0.0f}, this->size); + + float rect_w {(float)r.width()}; + float rect_h {(float)r.height()}; - QMargins margins((int)w_margin, (int)h_margin, (int)w_margin, (int)h_margin); + float min_side {(float)qMin(rect_w, rect_h)}; - QRectF r({0.0f, 0.0f}, size); + // Calculate the margin + this->rect_margin = min_side * 0.1f; - std::cout << "Bounding rect: " << r.x() << ", " << r.y() << ", " << r.width() << ", " << r.height() << std::endl; + // Calculate the rect padding + // We add a safe area around the rect to make it easier to get an accurate coordinate of the size + this->rect_padding = min_side * 0.15f; - return r.marginsAdded(margins); + r.adjust(-rect_margin - rect_padding, -rect_margin - rect_padding, + rect_margin + rect_padding, rect_margin + rect_padding); + + return r; } void VisualShaderNodeGraphicsObject::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { - std::cout << " ----------------- Paint method called for node ID: " << n_id << std::endl; - const std::shared_ptr n {vs->get_node(n_id)}; if (!n) { @@ -987,6 +1019,10 @@ void VisualShaderNodeGraphicsObject::paint(QPainter *painter, const QStyleOption painter->setClipRect(option->exposedRect); + // Get the rect without the padding + QRectF r {this->boundingRect()}; + r.adjust(rect_padding, rect_padding, -rect_padding, -rect_padding); + { // Draw Node Rect QColor rect_color; @@ -1001,43 +1037,278 @@ void VisualShaderNodeGraphicsObject::paint(QPainter *painter, const QStyleOption painter->setBrush(this->fill_color); - QRectF boundary(0, 0, size.width(), size.height()); - - painter->drawRoundedRect(boundary, this->corner_radius, this->corner_radius); + painter->drawRoundedRect(r, this->corner_radius, this->corner_radius); } + // Add the margin to the rect + r.adjust(rect_margin, rect_margin, -rect_margin, -rect_margin); + + float rect_x {(float)r.topLeft().x()}; + float rect_y {(float)r.topLeft().y()}; + + float rect_w {(float)r.width()}; + float rect_h {(float)r.height()}; + + float min_side {(float)qMin(rect_w, rect_h)}; + + QRectF caption_rect(rect_x, rect_y, rect_w, min_side * 0.2f); + { // Draw Caption QString caption {QString::fromStdString(n->get_caption())}; - QFont f {painter->font()}; + QFont t_f {painter->font()}; + + QFont f {t_f}; f.setBold(true); + f.setPointSize(min_side * 0.1f); + painter->setFont(f); QFontMetrics fm(f); QRect text_rect {fm.boundingRect(caption)}; - // Calculate the coordinate of the caption - float x {0.5f * (float)(size.width() - text_rect.width())}; - float y {0.5f * this->port_spacing + (float)text_rect.height()}; - QPointF coordinate {QPointF(x, y)}; + // Calculate the coordinates of the caption + float x {rect_x + (float)(caption_rect.center().x() - text_rect.center().x())}; + + // Instead of subtracting, add the ascent to properly align text within the rect + float y {rect_y + (float)(caption_rect.center().y() - text_rect.center().y())}; + + QPointF coordinate {x, y}; - painter->setFont(f); painter->setPen(this->font_color); painter->drawText(coordinate, caption); - f.setBold(false); - painter->setFont(f); + painter->setFont(t_f); // Reset the font } + QPointF caption_rect_bl {caption_rect.bottomLeft()}; + QPointF first_in_port_coordinate {caption_rect_bl.x(), caption_rect_bl.y() + min_side * 0.4f}; + + // Correct X coordinate: Remove the margin + first_in_port_coordinate.setX((float)first_in_port_coordinate.x() - this->rect_margin); + { // Draw Input Ports - const int in_port_count {n->get_input_port_count()}; + int in_port_count {n->get_input_port_count()}; + + for (unsigned i {0}; i < in_port_count; ++i) { + if (in_port_graphics_objects.find(i) != in_port_graphics_objects.end()) + continue; + + QPointF port_coordinate {first_in_port_coordinate.x(), first_in_port_coordinate.y() + (min_side * 0.3f) * i}; + + QRectF port_rect(port_coordinate.x(), port_coordinate.y(), min_side * 0.1f, min_side * 0.1f); + + // Adjust the port rect to be centered + port_rect.adjust(-port_rect.width() * 0.5f, -port_rect.height() * 0.5f, -port_rect.width() * 0.5f, -port_rect.height() * 0.5f); + + QString p_n {QString::fromStdString(n->get_input_port_name(i))}; + + VisualShaderInputPortGraphicsObject* p_o {new VisualShaderInputPortGraphicsObject(p_n, r, port_rect, i, this)}; + in_port_graphics_objects[i] = p_o; + } } + QPointF caption_rect_br {caption_rect.bottomRight()}; + QPointF first_out_port_coordinate {caption_rect_br.x(), caption_rect_br.y() + min_side * 0.4f}; + + // Correct X coordinate: Remove the margin + first_out_port_coordinate.setX((float)first_out_port_coordinate.x() + this->rect_margin); + { // Draw Output Ports + int out_port_count {n->get_output_port_count()}; + + for (unsigned i {0}; i < out_port_count; ++i) { + if (out_port_graphics_objects.find(i) != out_port_graphics_objects.end()) + continue; + + QPointF port_coordinate {first_out_port_coordinate.x(), first_out_port_coordinate.y() + (min_side * 0.3f) * i}; + + QRectF port_rect(port_coordinate.x(), port_coordinate.y(), min_side * 0.1f, min_side * 0.1f); + + // Adjust the port rect to be centered + port_rect.adjust(-port_rect.width() * 0.5f, -port_rect.height() * 0.5f, -port_rect.width() * 0.5f, -port_rect.height() * 0.5f); + + QString p_n {QString::fromStdString(n->get_output_port_name(i))}; + + VisualShaderOutputPortGraphicsObject* p_o {new VisualShaderOutputPortGraphicsObject(p_n, r, port_rect, i, this)}; + out_port_graphics_objects[i] = p_o; + } + } +} + + +QVariant VisualShaderNodeGraphicsObject::itemChange(GraphicsItemChange change, const QVariant &value) { + if (scene() && change == ItemScenePositionHasChanged) { + Q_EMIT dynamic_cast(scene())->node_moved(n_id, pos()); + } + + return QGraphicsObject::itemChange(change, value); +} + +void VisualShaderNodeGraphicsObject::mousePressEvent(QGraphicsSceneMouseEvent *event) { + QGraphicsObject::mousePressEvent(event); +} + +void VisualShaderNodeGraphicsObject::mouseMoveEvent(QGraphicsSceneMouseEvent *event) { + QGraphicsObject::mouseMoveEvent(event); +} + +void VisualShaderNodeGraphicsObject::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { + QGraphicsObject::mouseReleaseEvent(event); +} + +void VisualShaderNodeGraphicsObject::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) { + QGraphicsObject::contextMenuEvent(event); +} + +VisualShaderInputPortGraphicsObject::VisualShaderInputPortGraphicsObject(const QString& name, + const QRectF& parent_node_rect, + const QRectF& rect, + const int& p_index, + QGraphicsItem* parent) : QGraphicsObject(parent), + name(name), + rect(rect), + parent_node_rect(parent_node_rect), + p_index(p_index) { + setFlag(QGraphicsItem::ItemDoesntPropagateOpacityToChildren, true); + setFlag(QGraphicsItem::ItemIsFocusable, true); + setFlag(QGraphicsItem::ItemIsSelectable, true); + + setCacheMode(QGraphicsItem::DeviceCoordinateCache); + + setVisible(true); + setOpacity(this->opacity); + + setZValue(0); +} + +VisualShaderInputPortGraphicsObject::~VisualShaderInputPortGraphicsObject() { + +} + +QRectF VisualShaderInputPortGraphicsObject::boundingRect() const { + QFont f; + QFontMetrics fm(f); + QRect text_rect {fm.boundingRect(name)}; + return rect.united(text_rect); +} + +void VisualShaderInputPortGraphicsObject::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { + painter->setClipRect(option->exposedRect); + + float parent_rect_x {(float)parent_node_rect.topLeft().x()}; + float parent_rect_y {(float)parent_node_rect.topLeft().y()}; + + float parent_rect_w {(float)parent_node_rect.width()}; + float parent_rect_h {(float)parent_node_rect.height()}; + + float parent_min_side {(float)qMin(parent_rect_w, parent_rect_h)}; + + painter->setBrush(this->connection_point_color); + + painter->drawEllipse(rect); + + { + // Draw input port name + if (!name.isEmpty()) { + QFont t_f {painter->font()}; + + QFont f {t_f}; + f.setPointSize(parent_min_side * 0.06f); + painter->setFont(f); + QFontMetrics fm(f); + QRect text_rect {fm.boundingRect(name)}; + + float x {(float)rect.x() + (float)rect.width() + parent_min_side * 0.08f}; + + float y {parent_rect_y + (float)(rect.center().y() - text_rect.center().y())}; + + QPointF coordinate {x, y}; + + painter->setPen(this->font_color); + painter->drawText(coordinate, name); + + painter->setFont(t_f); // Reset the font + } } } +VisualShaderOutputPortGraphicsObject::VisualShaderOutputPortGraphicsObject(const QString& name, + const QRectF& parent_node_rect, + const QRectF& rect, + const int& p_index, + QGraphicsItem* parent) : QGraphicsObject(parent), + name(name), + rect(rect), + parent_node_rect(parent_node_rect), + p_index(p_index) { + setFlag(QGraphicsItem::ItemDoesntPropagateOpacityToChildren, true); + setFlag(QGraphicsItem::ItemIsFocusable, true); + setFlag(QGraphicsItem::ItemIsSelectable, true); + + setCacheMode(QGraphicsItem::DeviceCoordinateCache); + + setVisible(true); + setOpacity(this->opacity); + + setZValue(0); +} + +VisualShaderOutputPortGraphicsObject::~VisualShaderOutputPortGraphicsObject() { + +} + +QRectF VisualShaderOutputPortGraphicsObject::boundingRect() const { + QFont f; + QFontMetrics fm(f); + QRect text_rect {fm.boundingRect(name)}; + return rect.united(text_rect); +} + +void VisualShaderOutputPortGraphicsObject::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { + painter->setClipRect(option->exposedRect); + + float parent_rect_x {(float)parent_node_rect.topLeft().x()}; + float parent_rect_y {(float)parent_node_rect.topLeft().y()}; + + float parent_rect_w {(float)parent_node_rect.width()}; + float parent_rect_h {(float)parent_node_rect.height()}; + + float parent_min_side {(float)qMin(parent_rect_w, parent_rect_h)}; + + painter->setBrush(this->connection_point_color); + + painter->drawEllipse(rect); + + { + // Draw output port name + if (!name.isEmpty()) { + QFont t_f {painter->font()}; + + QFont f {t_f}; + f.setPointSize(parent_min_side * 0.06f); + painter->setFont(f); + QFontMetrics fm(f); + QRect text_rect {fm.boundingRect(name)}; + + float x {(float)rect.x() + (float)rect.width() + parent_min_side * 0.08f}; + + float y {parent_rect_y + (float)(rect.center().y() - text_rect.center().y())}; + + QPointF coordinate {x, y}; + + painter->setPen(this->font_color); + painter->drawText(coordinate, name); + + painter->setFont(t_f); // Reset the font + } + + } +} + + + /**********************************************************************/ /**********************************************************************/ /**********************************************************************/ @@ -1048,8 +1319,18 @@ void VisualShaderNodeGraphicsObject::paint(QPainter *painter, const QStyleOption /**********************************************************************/ /**********************************************************************/ -VisualShaderConnectionGraphicsObject::VisualShaderConnectionGraphicsObject(QGraphicsItem* parent) : QGraphicsObject(parent) { - +VisualShaderConnectionGraphicsObject::VisualShaderConnectionGraphicsObject(const int& from_n_id, + const int& from_p_index, + QGraphicsItem* parent) : QGraphicsObject(parent), + from_n_id(from_n_id), + from_p_index(from_p_index), + start_graphics_object(nullptr), + end_graphics_object(nullptr) { + setFlag(QGraphicsItem::ItemIsMovable, true); + setFlag(QGraphicsItem::ItemIsFocusable, true); + setFlag(QGraphicsItem::ItemIsSelectable, true); + + setZValue(-1.0f); } VisualShaderConnectionGraphicsObject::~VisualShaderConnectionGraphicsObject() { @@ -1057,11 +1338,314 @@ VisualShaderConnectionGraphicsObject::~VisualShaderConnectionGraphicsObject() { } QRectF VisualShaderConnectionGraphicsObject::boundingRect() const { - return QRectF(); + QRectF r {calculate_bounding_rect_from_coordinates(start_coordinate, end_coordinate)}; + + // Calculate the rect padding + // We add a safe area around the rect to make it easier to get an accurate coordinate of the size + this->rect_padding = 10.0f; + + r.adjust(-rect_padding, -rect_padding, rect_padding, rect_padding); + + return r; } void VisualShaderConnectionGraphicsObject::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { + painter->setClipRect(option->exposedRect); + + // Get the rect without the padding + QRectF r {this->boundingRect()}; + + // { + // // Draw rect + // QColor rect_color {Qt::red}; + + // QPen p(rect_color, 3.0f); + // painter->setPen(p); + + // painter->setBrush(Qt::NoBrush); + + // painter->drawRect(r); + // } + + // Add the padding to the rect + r.adjust(rect_padding, rect_padding, -rect_padding, -rect_padding); + + float rect_x {(float)r.topLeft().x()}; + float rect_y {(float)r.topLeft().y()}; + + float rect_w {(float)r.width()}; + float rect_h {(float)r.height()}; + + float min_side {(float)qMin(rect_w, rect_h)}; + + { + QPen pen; + pen.setWidth(min_side * 0.05f); + pen.setColor(this->construction_color); + pen.setStyle(Qt::DashLine); + + painter->setPen(pen); + painter->setBrush(Qt::NoBrush); + + std::pair control_points {calculate_control_points(start_coordinate, end_coordinate)}; + + QPainterPath cubic(start_coordinate); + cubic.cubicTo(control_points.first, control_points.second, end_coordinate); + + // cubic spline + painter->drawPath(cubic); + } + + { + // draw normal line + QPen p; + p.setWidth(min_side * 0.05f); + + const bool selected {this->isSelected()}; + + std::pair control_points {calculate_control_points(start_coordinate, end_coordinate)}; + + QPainterPath cubic(start_coordinate); + cubic.cubicTo(control_points.first, control_points.second, end_coordinate); + + p.setColor(this->normal_color); + + if (selected) { + p.setColor(this->selected_color); + } + + painter->setPen(p); + painter->setBrush(Qt::NoBrush); + + painter->drawPath(cubic); + } + + { + // Draw start and end points + if (!start_graphics_object) { + QRectF start_rect(start_coordinate.x(), start_coordinate.y(), min_side * 0.2f, min_side * 0.2f); + + // Adjust the port rect to be centered + start_rect.adjust(-start_rect.width() * 0.5f, -start_rect.height() * 0.5f, -start_rect.width() * 0.5f, -start_rect.height() * 0.5f); + + VisualShaderConnectionStartGraphicsObject* s_o {new VisualShaderConnectionStartGraphicsObject(start_rect, this)}; + start_graphics_object = s_o; + } + + if (!end_graphics_object) { + QRectF end_rect(end_coordinate.x(), end_coordinate.y(), min_side * 0.2f, min_side * 0.2f); + + // Adjust the port rect to be centered + end_rect.adjust(-end_rect.width() * 0.5f, -end_rect.height() * 0.5f, -end_rect.width() * 0.5f, -end_rect.height() * 0.5f); + + VisualShaderConnectionEndGraphicsObject* e_o {new VisualShaderConnectionEndGraphicsObject(end_rect, this)}; + end_graphics_object = e_o; + } + } +} + +int VisualShaderConnectionGraphicsObject::detect_quadrant(const QPointF& reference, const QPointF& target) const { + float relative_x {(float)(target.x() - reference.x())}; + float relative_y {(float)(target.y() - reference.y())}; + + // Note that the default coordinate system in Qt is as follows: + // - X-axis: Positive to the right, negative to the left + // - Y-axis: Positive downwards, negative upwards + + // Check if the point is on an axis or the origin + if (relative_x == 0 && relative_y == 0) { + return 0; // Stack on the reference + } else if (relative_y == 0) { + return (relative_x > 0) ? 5 : 6; // On X-axis: 5 is the +ve part while 6 is the -ve one. + } else if (relative_x == 0) { + return (relative_y < 0) ? 7 : 8; // On Y-axis: 7 is the +ve part while 8 is the -ve one. + } + + // Determine the quadrant based on the relative coordinates + if (relative_x > 0 && relative_y < 0) { + return 1; // Quadrant I + } else if (relative_x < 0 && relative_y < 0) { + return 2; // Quadrant II + } else if (relative_x < 0 && relative_y > 0) { + return 3; // Quadrant III + } else if (relative_x > 0 && relative_y > 0) { + return 4; // Quadrant IV + } + + // Default case (should not reach here) + return -1; +} + +QRectF VisualShaderConnectionGraphicsObject::calculate_bounding_rect_from_coordinates(const QPointF& start_coordinate, const QPointF& end_coordinate) const { + float x1 {(float)start_coordinate.x()}; + float y1 {(float)start_coordinate.y()}; + float x2 {(float)end_coordinate.x()}; + float y2 {(float)end_coordinate.y()}; + + // Calculate the expanded rect + float min_x {(float)qMin(x1, x2)}; + float min_y {(float)qMin(y1, y2)}; + float max_x {(float)qMax(x1, x2)}; + float max_y {(float)qMax(y1, y2)}; + + QRectF r({min_x, min_y}, QSizeF(max_x - min_x, max_y - min_y)); + + bool in_abnormal_region {x2 < (x1 + min_h_distance)}; + + float a_width_expansion {((x1 + min_h_distance) - x2) * abnormal_face_to_back_control_width_expansion_factor}; + + if (in_abnormal_region) { + // The connection is not going from left to right normally + // Our control points will be outside the end_coordinate and start_coordinate bounding rect + // We will expand the bounding rect horizontally to make it easier to get an accurate coordinate of the size + r.adjust(-a_width_expansion, 0.0f, a_width_expansion, 0.0f); + } + + return r; +} + +std::pair VisualShaderConnectionGraphicsObject::calculate_control_points(const QPointF& start_coordinate, const QPointF& end_coordinated) const { + QPointF cp1; + QPointF cp2; + + float x1 {(float)start_coordinate.x()}; + float y1 {(float)start_coordinate.y()}; + float x2 {(float)end_coordinate.x()}; + float y2 {(float)end_coordinate.y()}; + + QRectF r {calculate_bounding_rect_from_coordinates(start_coordinate, end_coordinate)}; + + bool in_abnormal_region {x2 < (x1 + min_h_distance)}; + + int quadrant {detect_quadrant({x1, y1}, {x2, y2})}; + + float face_to_face_control_width_expansion_factor {0.8f}; + float face_to_face_control_height_expansion_factor {0.25f}; + + float width_expansion {(x2 - x1) * face_to_face_control_width_expansion_factor}; + + float a_width_expansion {((x1 + min_h_distance) - x2) * abnormal_face_to_back_control_width_expansion_factor}; + float a_height_expansion {a_width_expansion * abnormal_face_to_back_control_height_expansion_factor}; + + if (in_abnormal_region) { + r.adjust(-a_width_expansion, 0.0f, a_width_expansion, 0.0f); + } + + switch(quadrant) { + case 1: // Quadrant I: Normal face to back + // Find out if the connection is going from left to right normally + if (in_abnormal_region) { + // The connection is not going from left to right normally + // Our control points will be outside the end_coordinate and start_coordinate bounding rect + // We will expand the bounding rect horizontally to make it easier to get an accurate coordinate of the size + + // Here we cover cases of nodes not facing each other. + // This means we can't just send the path straight to the node. + + // Treated as inside Quadrant II + cp1 = {x1 + a_width_expansion, y1}; + cp2 = {x2 - a_width_expansion, y2}; + + } else { + // Treated as inside Quadrant I + cp1 = {x1 + width_expansion, y1}; + cp2 = {x2 - width_expansion, y2}; + } + break; + case 2: // Quadrant II: Abnormal face to back + cp1 = {x1 + a_width_expansion, y1}; + cp2 = {x2 - a_width_expansion, y2}; + break; + case 3: // Quadrant III: Abnormal face to back + cp1 = {x1 + a_width_expansion, y1 + a_height_expansion}; + cp2 = {x2 - a_width_expansion, y2 - a_height_expansion}; + break; + case 4: // Quadrant IV: Normal face to back + if (in_abnormal_region) { + // Treated as inside Quadrant III + cp1 = {x1 + a_width_expansion, y1}; + cp2 = {x2 - a_width_expansion, y2}; + } else { + // Treated as inside Quadrant IV + cp1 = {x1 + width_expansion, y1}; + cp2 = {x2 - width_expansion, y2}; + } + break; + case 5: // On +ve X-axis: Normal face to back + // Straight line + cp1 = {x1, y1}; + cp2 = {x2, y2}; + break; + case 6: // On -ve X-axis: Abnormal face to back + r.adjust(0.0f, -a_height_expansion, 0.0f, a_height_expansion); + cp1 = {x1 + a_width_expansion, y1}; + cp2 = {x2 - a_width_expansion, y2}; + break; + case 7: // On +ve Y-axis: Abnormal face to back + r.adjust(0.0f, -a_height_expansion, 0.0f, a_height_expansion); + cp1 = {x1 + a_width_expansion, y1}; + cp2 = {x2 - a_width_expansion, y2}; + break; + case 8: // On -ve Y-axis: Abnormal face to back + r.adjust(0.0f, -a_height_expansion, 0.0f, a_height_expansion); + cp1 = {x1 + a_width_expansion, y1}; + cp2 = {x2 - a_width_expansion, y2}; + break; + default: + return std::make_pair(start_coordinate, end_coordinate); + } + + return std::make_pair(cp1, cp2); +} + +VisualShaderConnectionStartGraphicsObject::VisualShaderConnectionStartGraphicsObject(const QRectF& rect, QGraphicsItem* parent) : QGraphicsObject(parent), + rect(rect) { + setFlag(QGraphicsItem::ItemIsMovable, true); + setFlag(QGraphicsItem::ItemIsFocusable, true); + setFlag(QGraphicsItem::ItemIsSelectable, true); + + setVisible(true); + setOpacity(this->opacity); + + setZValue(-1.0f); +} + +VisualShaderConnectionStartGraphicsObject::~VisualShaderConnectionStartGraphicsObject() {} + +QRectF VisualShaderConnectionStartGraphicsObject::boundingRect() const { + return rect; +} + +void VisualShaderConnectionStartGraphicsObject::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { + painter->setClipRect(option->exposedRect); + + painter->setBrush(this->connection_point_color); + painter->drawEllipse(rect); +} + +VisualShaderConnectionEndGraphicsObject::VisualShaderConnectionEndGraphicsObject(const QRectF& rect, QGraphicsItem* parent) : QGraphicsObject(parent), + rect(rect) { + setFlag(QGraphicsItem::ItemIsMovable, true); + setFlag(QGraphicsItem::ItemIsFocusable, true); + setFlag(QGraphicsItem::ItemIsSelectable, true); + + setVisible(true); + setOpacity(this->opacity); + + setZValue(-1.0f); +} + +VisualShaderConnectionEndGraphicsObject::~VisualShaderConnectionEndGraphicsObject() {} + +QRectF VisualShaderConnectionEndGraphicsObject::boundingRect() const { + return rect; +} + +void VisualShaderConnectionEndGraphicsObject::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { + painter->setClipRect(option->exposedRect); + painter->setBrush(this->connection_point_color); + painter->drawEllipse(rect); } /**********************************************************************/ diff --git a/Editors/VisualShaderEditor.h b/Editors/VisualShaderEditor.h index 9676da0c2..6b1139cfa 100644 --- a/Editors/VisualShaderEditor.h +++ b/Editors/VisualShaderEditor.h @@ -209,6 +209,12 @@ class VisualShaderGraphicsScene : public QGraphicsScene { VisualShader* get_visual_shader() const { return vs; } +Q_SIGNALS: + void node_moved(const int& n_id, const QPointF& new_position); + +private Q_SLOTS: + void on_node_moved(const int& n_id, const QPointF& new_position); + private: VisualShader* vs; @@ -237,6 +243,12 @@ class VisualShaderGraphicsView : public QGraphicsView { ~VisualShaderGraphicsView(); public Q_SLOTS: + /** + * @brief + * + * @todo If the button is pressed then zoom in from the center of the view. + * + */ void zoom_in(); void reset_zoom(); void zoom_out(); @@ -301,6 +313,9 @@ private Q_SLOTS: /**********************************************************************/ /**********************************************************************/ +class VisualShaderInputPortGraphicsObject; +class VisualShaderOutputPortGraphicsObject; + class VisualShaderNodeGraphicsObject : public QGraphicsObject { Q_OBJECT @@ -312,6 +327,9 @@ class VisualShaderNodeGraphicsObject : public QGraphicsObject { VisualShader* vs; int n_id; + std::unordered_map in_port_graphics_objects; + std::unordered_map out_port_graphics_objects; + // Style QColor normal_boundary_color = QColor(255, 255, 255); QColor selected_boundary_color = QColor(255, 165, 0); @@ -321,7 +339,6 @@ class VisualShaderNodeGraphicsObject : public QGraphicsObject { QColor shadow_color = QColor(20, 20, 20); QColor font_color = QColor(255, 255, 255); QColor font_color_faded = QColor(169, 169, 169); - QColor connection_point_color = QColor(169, 169, 169); QColor filled_connection_point_color = QColor(0, 255, 255); QColor error_color = QColor(255, 0, 0); QColor warning_color = QColor(128, 128, 0); @@ -333,20 +350,73 @@ class VisualShaderNodeGraphicsObject : public QGraphicsObject { float opacity = 0.8f; float corner_radius = 3.0f; - float port_spacing = 10.0f; - - float rect_margin_ratio = 0.2f; + mutable float rect_padding; // Calculated in boundingRect() + mutable float rect_margin; // Calculated in boundingRect() // Ports Style float connected_port_diameter = 8.0f; float unconnected_port_diameter = 6.0f; - // Size - QSizeF size = QSizeF(100.0f, 200.0f); + // Size of the node + QSizeF size = QSizeF(150.0f, 250.0f); QRectF boundingRect() const override; void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override; + QVariant itemChange(GraphicsItemChange change, const QVariant &value) override; + void mousePressEvent(QGraphicsSceneMouseEvent *event) override; + void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override; + void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override; + void contextMenuEvent(QGraphicsSceneContextMenuEvent *event) override; +}; + +class VisualShaderInputPortGraphicsObject : public QGraphicsObject { +public: + VisualShaderInputPortGraphicsObject(const QString& name, + const QRectF& parent_node_rect, + const QRectF& rect, + const int& p_index, + QGraphicsItem* parent = nullptr); + ~VisualShaderInputPortGraphicsObject(); + + QRectF boundingRect() const override; + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override; + +private: + QString name; + int p_index; + QRectF rect; + QRectF parent_node_rect; + + // Style + QColor font_color = QColor(255, 255, 255); + QColor connection_point_color = QColor(169, 169, 169); + + float opacity = 1.0f; +}; + +class VisualShaderOutputPortGraphicsObject : public QGraphicsObject { +public: + VisualShaderOutputPortGraphicsObject(const QString& name, + const QRectF& parent_node_rect, + const QRectF& rect, + const int& p_index, + QGraphicsItem* parent = nullptr); + ~VisualShaderOutputPortGraphicsObject(); + + QRectF boundingRect() const override; + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override; + +private: + QString name; + int p_index; + QRectF rect; + QRectF parent_node_rect; + + // Style + QColor font_color = QColor(255, 255, 255); + QColor connection_point_color = QColor(169, 169, 169); + float opacity = 1.0f; }; /**********************************************************************/ @@ -359,17 +429,82 @@ class VisualShaderNodeGraphicsObject : public QGraphicsObject { /**********************************************************************/ /**********************************************************************/ +class VisualShaderConnectionStartGraphicsObject; +class VisualShaderConnectionEndGraphicsObject; + class VisualShaderConnectionGraphicsObject : public QGraphicsObject { Q_OBJECT public: - VisualShaderConnectionGraphicsObject(QGraphicsItem *parent = nullptr); + VisualShaderConnectionGraphicsObject(const int& from_n_id, const int& from_p_index, QGraphicsItem *parent = nullptr); ~VisualShaderConnectionGraphicsObject(); private: + int from_n_id; + int to_n_id; + int from_p_index; + int to_p_index; + + QPointF start_coordinate = QPointF(100, 100); + QPointF end_coordinate = QPointF(200, 200); + + VisualShaderConnectionStartGraphicsObject* start_graphics_object; + VisualShaderConnectionEndGraphicsObject* end_graphics_object; + + // Style + QColor construction_color = QColor(169, 169, 169); + QColor normal_color = QColor(0, 255, 255); + QColor selected_color = QColor(100, 100, 100); + QColor selected_halo_color = QColor(255, 165, 0); + + float line_width = 3.0f; + float construction_line_width = 2.0f; + float point_diameter = 10.0f; + + mutable float rect_padding; // Calculated in boundingRect() + + float min_h_distance = 50.0f; + float abnormal_face_to_back_control_width_expansion_factor = 0.5f; + float abnormal_face_to_back_control_height_expansion_factor = 2.0f; + QRectF boundingRect() const override; void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override; + int detect_quadrant(const QPointF& reference, const QPointF& target) const; + QRectF calculate_bounding_rect_from_coordinates(const QPointF& start_coordinate, const QPointF& end_coordinate) const; + std::pair calculate_control_points(const QPointF& start_coordinate, const QPointF& end_coordinate) const; +}; + +class VisualShaderConnectionStartGraphicsObject : public QGraphicsObject { +public: + VisualShaderConnectionStartGraphicsObject(const QRectF& rect, QGraphicsItem* parent = nullptr); + ~VisualShaderConnectionStartGraphicsObject(); + + QRectF boundingRect() const override; + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override; + +private: + QRectF rect; + + // Style + QColor connection_point_color = QColor(169, 169, 169); + float opacity = 1.0f; +}; + +class VisualShaderConnectionEndGraphicsObject : public QGraphicsObject { +public: + VisualShaderConnectionEndGraphicsObject(const QRectF& rect, QGraphicsItem* parent = nullptr); + ~VisualShaderConnectionEndGraphicsObject(); + + QRectF boundingRect() const override; + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override; + +private: + QRectF rect; + + // Style + QColor connection_point_color = QColor(169, 169, 169); + float opacity = 1.0f; }; /**********************************************************************/ From 890c817ca5c4c505a4fe77d3b6844ee071022290 Mon Sep 17 00:00:00 2001 From: Saif Kandil <74428638+k0T0z@users.noreply.github.com> Date: Sun, 15 Sep 2024 12:52:23 +0300 Subject: [PATCH 20/56] Finished all nodes rendering code --- Editors/VisualShaderEditor.cpp | 230 ++++++++++++++++----------------- Editors/VisualShaderEditor.h | 60 +++++---- 2 files changed, 147 insertions(+), 143 deletions(-) diff --git a/Editors/VisualShaderEditor.cpp b/Editors/VisualShaderEditor.cpp index 4e4713f89..e6ada3c8f 100644 --- a/Editors/VisualShaderEditor.cpp +++ b/Editors/VisualShaderEditor.cpp @@ -609,11 +609,19 @@ bool VisualShaderGraphicsScene::add_node(const int& n_id) { return false; } - if (vs->get_node_coordinate(n_id).x > this->sceneRect().bottomRight().x() || - vs->get_node_coordinate(n_id).y < this->sceneRect().bottomRight().y() || - vs->get_node_coordinate(n_id).x < this->sceneRect().topLeft().x() || - vs->get_node_coordinate(n_id).y > this->sceneRect().topLeft().y()) { - std::cout << "Node is out of scene bounds" << std::endl; + QList views {this->views()}; + if (views.isEmpty()) { + std::cout << "No views available" << std::endl; + return false; + } + + VisualShaderGraphicsView* view {dynamic_cast(views.first())}; + + if (vs->get_node_coordinate(n_id).x < view->get_x() || + vs->get_node_coordinate(n_id).x > view->get_x() + view->get_width() || + vs->get_node_coordinate(n_id).y < view->get_y() || + vs->get_node_coordinate(n_id).y > view->get_y() + view->get_height()) { + std::cout << "Node is out of view bounds" << std::endl; } VisualShaderNodeGraphicsObject* n_o {new VisualShaderNodeGraphicsObject(vs, n_id)}; @@ -990,19 +998,42 @@ VisualShaderNodeGraphicsObject::~VisualShaderNodeGraphicsObject() { } QRectF VisualShaderNodeGraphicsObject::boundingRect() const { - QRectF r({0.0f, 0.0f}, this->size); + const std::shared_ptr n {vs->get_node(n_id)}; - float rect_w {(float)r.width()}; - float rect_h {(float)r.height()}; + if (!n) { + return QRectF(); + } + + QFont f("Arial", caption_font_size); + f.setBold(true); + QFontMetrics fm(f); + + QString caption {QString::fromStdString(n->get_caption())}; + + rect_width = (float)(fm.horizontalAdvance(caption, caption.length()) + 20.0f); + caption_rect_height = (float)((fm.height()) + 30.0f); + + int max_num_ports {qMax(n->get_input_port_count(), n->get_output_port_count())}; + + // Calculate the height of the node + float t_rect_h {caption_rect_height}; - float min_side {(float)qMin(rect_w, rect_h)}; + t_rect_h += body_rect_header_height; // Header + if (max_num_ports >= 0) { + t_rect_h += (float)(max_num_ports - 1) * body_rect_port_step; // Ports + } + t_rect_h += body_rect_footer_height; // Footer + + rect_height = t_rect_h; + + QRectF r({0.0f, 0.0f}, QSizeF(rect_width, rect_height)); // Calculate the margin - this->rect_margin = min_side * 0.1f; + this->rect_margin = rect_width * 0.1f; // Calculate the rect padding // We add a safe area around the rect to make it easier to get an accurate coordinate of the size - this->rect_padding = min_side * 0.15f; + this->rect_padding = rect_width * 0.15f; r.adjust(-rect_margin - rect_padding, -rect_margin - rect_padding, rect_margin + rect_padding, rect_margin + rect_padding); @@ -1049,9 +1080,9 @@ void VisualShaderNodeGraphicsObject::paint(QPainter *painter, const QStyleOption float rect_w {(float)r.width()}; float rect_h {(float)r.height()}; - float min_side {(float)qMin(rect_w, rect_h)}; + float min_side {qMin(rect_w, rect_h)}; - QRectF caption_rect(rect_x, rect_y, rect_w, min_side * 0.2f); + QRectF caption_rect(rect_x, rect_y, rect_w, caption_rect_height); { // Draw Caption @@ -1059,18 +1090,16 @@ void VisualShaderNodeGraphicsObject::paint(QPainter *painter, const QStyleOption QFont t_f {painter->font()}; - QFont f {t_f}; + QFont f("Arial", caption_font_size); f.setBold(true); - f.setPointSize(min_side * 0.1f); - painter->setFont(f); QFontMetrics fm(f); - QRect text_rect {fm.boundingRect(caption)}; + painter->setFont(f); // Calculate the coordinates of the caption - float x {rect_x + (float)(caption_rect.center().x() - text_rect.center().x())}; + float x {(float)(caption_rect.center().x() - (float)fm.horizontalAdvance(caption) * 0.5f)}; // Instead of subtracting, add the ascent to properly align text within the rect - float y {rect_y + (float)(caption_rect.center().y() - text_rect.center().y())}; + float y {(float)(caption_rect.center().y() + (float)fm.ascent() * 0.5f)}; QPointF coordinate {x, y}; @@ -1081,7 +1110,7 @@ void VisualShaderNodeGraphicsObject::paint(QPainter *painter, const QStyleOption } QPointF caption_rect_bl {caption_rect.bottomLeft()}; - QPointF first_in_port_coordinate {caption_rect_bl.x(), caption_rect_bl.y() + min_side * 0.4f}; + QPointF first_in_port_coordinate {caption_rect_bl.x(), caption_rect_bl.y() + body_rect_header_height}; // Correct X coordinate: Remove the margin first_in_port_coordinate.setX((float)first_in_port_coordinate.x() - this->rect_margin); @@ -1091,25 +1120,46 @@ void VisualShaderNodeGraphicsObject::paint(QPainter *painter, const QStyleOption int in_port_count {n->get_input_port_count()}; for (unsigned i {0}; i < in_port_count; ++i) { - if (in_port_graphics_objects.find(i) != in_port_graphics_objects.end()) - continue; - - QPointF port_coordinate {first_in_port_coordinate.x(), first_in_port_coordinate.y() + (min_side * 0.3f) * i}; + QPointF port_coordinate {first_in_port_coordinate.x(), first_in_port_coordinate.y() + body_rect_port_step * i}; QRectF port_rect(port_coordinate.x(), port_coordinate.y(), min_side * 0.1f, min_side * 0.1f); // Adjust the port rect to be centered port_rect.adjust(-port_rect.width() * 0.5f, -port_rect.height() * 0.5f, -port_rect.width() * 0.5f, -port_rect.height() * 0.5f); + // Draw caption QString p_n {QString::fromStdString(n->get_input_port_name(i))}; - VisualShaderInputPortGraphicsObject* p_o {new VisualShaderInputPortGraphicsObject(p_n, r, port_rect, i, this)}; + if (!p_n.isEmpty()) { + QFont t_f {painter->font()}; + + QFont f("Arial", port_caption_font_size); + QFontMetrics fm(f); + painter->setFont(f); + + float x {rect_x + 5.0f}; + + float y {(float)(port_rect.center().y()) + (float)fm.ascent() * 0.5f}; + + QPointF coordinate {x, y}; + + painter->setPen(this->font_color); + painter->drawText(coordinate, p_n); + + painter->setFont(t_f); // Reset the font + } + + if (in_port_graphics_objects.find(i) != in_port_graphics_objects.end()) + continue; + + // Draw the port + VisualShaderInputPortGraphicsObject* p_o {new VisualShaderInputPortGraphicsObject(port_rect, i, this)}; in_port_graphics_objects[i] = p_o; } } QPointF caption_rect_br {caption_rect.bottomRight()}; - QPointF first_out_port_coordinate {caption_rect_br.x(), caption_rect_br.y() + min_side * 0.4f}; + QPointF first_out_port_coordinate {caption_rect_br.x(), caption_rect_br.y() + body_rect_header_height}; // Correct X coordinate: Remove the margin first_out_port_coordinate.setX((float)first_out_port_coordinate.x() + this->rect_margin); @@ -1119,19 +1169,40 @@ void VisualShaderNodeGraphicsObject::paint(QPainter *painter, const QStyleOption int out_port_count {n->get_output_port_count()}; for (unsigned i {0}; i < out_port_count; ++i) { - if (out_port_graphics_objects.find(i) != out_port_graphics_objects.end()) - continue; - - QPointF port_coordinate {first_out_port_coordinate.x(), first_out_port_coordinate.y() + (min_side * 0.3f) * i}; + QPointF port_coordinate {first_out_port_coordinate.x(), first_out_port_coordinate.y() + body_rect_port_step * i}; QRectF port_rect(port_coordinate.x(), port_coordinate.y(), min_side * 0.1f, min_side * 0.1f); // Adjust the port rect to be centered port_rect.adjust(-port_rect.width() * 0.5f, -port_rect.height() * 0.5f, -port_rect.width() * 0.5f, -port_rect.height() * 0.5f); + // Draw caption QString p_n {QString::fromStdString(n->get_output_port_name(i))}; - VisualShaderOutputPortGraphicsObject* p_o {new VisualShaderOutputPortGraphicsObject(p_n, r, port_rect, i, this)}; + if (!p_n.isEmpty()) { + QFont t_f {painter->font()}; + + QFont f("Arial", port_caption_font_size); + QFontMetrics fm(f); + painter->setFont(f); + + float x {rect_x + rect_w - (float)fm.horizontalAdvance(p_n, p_n.length()) - 5.0f}; + + float y {(float)(port_rect.center().y()) + (float)fm.ascent() * 0.5f}; + + QPointF coordinate {x, y}; + + painter->setPen(this->font_color); + painter->drawText(coordinate, p_n); + + painter->setFont(t_f); // Reset the font + } + + if (out_port_graphics_objects.find(i) != out_port_graphics_objects.end()) + continue; + + // Draw the port + VisualShaderOutputPortGraphicsObject* p_o {new VisualShaderOutputPortGraphicsObject(port_rect, i, this)}; out_port_graphics_objects[i] = p_o; } } @@ -1162,14 +1233,10 @@ void VisualShaderNodeGraphicsObject::contextMenuEvent(QGraphicsSceneContextMenuE QGraphicsObject::contextMenuEvent(event); } -VisualShaderInputPortGraphicsObject::VisualShaderInputPortGraphicsObject(const QString& name, - const QRectF& parent_node_rect, - const QRectF& rect, +VisualShaderInputPortGraphicsObject::VisualShaderInputPortGraphicsObject(const QRectF& rect, const int& p_index, QGraphicsItem* parent) : QGraphicsObject(parent), - name(name), rect(rect), - parent_node_rect(parent_node_rect), p_index(p_index) { setFlag(QGraphicsItem::ItemDoesntPropagateOpacityToChildren, true); setFlag(QGraphicsItem::ItemIsFocusable, true); @@ -1188,60 +1255,21 @@ VisualShaderInputPortGraphicsObject::~VisualShaderInputPortGraphicsObject() { } QRectF VisualShaderInputPortGraphicsObject::boundingRect() const { - QFont f; - QFontMetrics fm(f); - QRect text_rect {fm.boundingRect(name)}; - return rect.united(text_rect); + return rect; } void VisualShaderInputPortGraphicsObject::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { painter->setClipRect(option->exposedRect); - - float parent_rect_x {(float)parent_node_rect.topLeft().x()}; - float parent_rect_y {(float)parent_node_rect.topLeft().y()}; - - float parent_rect_w {(float)parent_node_rect.width()}; - float parent_rect_h {(float)parent_node_rect.height()}; - - float parent_min_side {(float)qMin(parent_rect_w, parent_rect_h)}; painter->setBrush(this->connection_point_color); painter->drawEllipse(rect); - - { - // Draw input port name - if (!name.isEmpty()) { - QFont t_f {painter->font()}; - - QFont f {t_f}; - f.setPointSize(parent_min_side * 0.06f); - painter->setFont(f); - QFontMetrics fm(f); - QRect text_rect {fm.boundingRect(name)}; - - float x {(float)rect.x() + (float)rect.width() + parent_min_side * 0.08f}; - - float y {parent_rect_y + (float)(rect.center().y() - text_rect.center().y())}; - - QPointF coordinate {x, y}; - - painter->setPen(this->font_color); - painter->drawText(coordinate, name); - - painter->setFont(t_f); // Reset the font - } - } } -VisualShaderOutputPortGraphicsObject::VisualShaderOutputPortGraphicsObject(const QString& name, - const QRectF& parent_node_rect, - const QRectF& rect, +VisualShaderOutputPortGraphicsObject::VisualShaderOutputPortGraphicsObject(const QRectF& rect, const int& p_index, QGraphicsItem* parent) : QGraphicsObject(parent), - name(name), rect(rect), - parent_node_rect(parent_node_rect), p_index(p_index) { setFlag(QGraphicsItem::ItemDoesntPropagateOpacityToChildren, true); setFlag(QGraphicsItem::ItemIsFocusable, true); @@ -1260,51 +1288,15 @@ VisualShaderOutputPortGraphicsObject::~VisualShaderOutputPortGraphicsObject() { } QRectF VisualShaderOutputPortGraphicsObject::boundingRect() const { - QFont f; - QFontMetrics fm(f); - QRect text_rect {fm.boundingRect(name)}; - return rect.united(text_rect); + return rect; } void VisualShaderOutputPortGraphicsObject::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { painter->setClipRect(option->exposedRect); - - float parent_rect_x {(float)parent_node_rect.topLeft().x()}; - float parent_rect_y {(float)parent_node_rect.topLeft().y()}; - - float parent_rect_w {(float)parent_node_rect.width()}; - float parent_rect_h {(float)parent_node_rect.height()}; - - float parent_min_side {(float)qMin(parent_rect_w, parent_rect_h)}; painter->setBrush(this->connection_point_color); painter->drawEllipse(rect); - - { - // Draw output port name - if (!name.isEmpty()) { - QFont t_f {painter->font()}; - - QFont f {t_f}; - f.setPointSize(parent_min_side * 0.06f); - painter->setFont(f); - QFontMetrics fm(f); - QRect text_rect {fm.boundingRect(name)}; - - float x {(float)rect.x() + (float)rect.width() + parent_min_side * 0.08f}; - - float y {parent_rect_y + (float)(rect.center().y() - text_rect.center().y())}; - - QPointF coordinate {x, y}; - - painter->setPen(this->font_color); - painter->drawText(coordinate, name); - - painter->setFont(t_f); // Reset the font - } - - } } @@ -1376,7 +1368,7 @@ void VisualShaderConnectionGraphicsObject::paint(QPainter *painter, const QStyle float rect_w {(float)r.width()}; float rect_h {(float)r.height()}; - float min_side {(float)qMin(rect_w, rect_h)}; + float min_side {qMin(rect_w, rect_h)}; { QPen pen; @@ -1483,10 +1475,10 @@ QRectF VisualShaderConnectionGraphicsObject::calculate_bounding_rect_from_coordi float y2 {(float)end_coordinate.y()}; // Calculate the expanded rect - float min_x {(float)qMin(x1, x2)}; - float min_y {(float)qMin(y1, y2)}; - float max_x {(float)qMax(x1, x2)}; - float max_y {(float)qMax(y1, y2)}; + float min_x {qMin(x1, x2)}; + float min_y {qMin(y1, y2)}; + float max_x {qMax(x1, x2)}; + float max_y {qMax(y1, y2)}; QRectF r({min_x, min_y}, QSizeF(max_x - min_x, max_y - min_y)); diff --git a/Editors/VisualShaderEditor.h b/Editors/VisualShaderEditor.h index 6b1139cfa..ab114e038 100644 --- a/Editors/VisualShaderEditor.h +++ b/Editors/VisualShaderEditor.h @@ -242,6 +242,11 @@ class VisualShaderGraphicsView : public QGraphicsView { ~VisualShaderGraphicsView(); + float get_x() const { return rect_x; } + float get_y() const { return rect_y; } + float get_width() const { return rect_width; } + float get_height() const { return rect_height; } + public Q_SLOTS: /** * @brief @@ -350,6 +355,14 @@ class VisualShaderNodeGraphicsObject : public QGraphicsObject { float opacity = 0.8f; float corner_radius = 3.0f; + mutable float rect_width; // Calculated in boundingRect() + mutable float caption_rect_height; // Calculated in boundingRect() + + mutable float rect_height; // Calculated in boundingRect() + float body_rect_header_height = 30.0f; + float body_rect_port_step = 35.0f; + float body_rect_footer_height = 30.0f; + mutable float rect_padding; // Calculated in boundingRect() mutable float rect_margin; // Calculated in boundingRect() @@ -357,8 +370,9 @@ class VisualShaderNodeGraphicsObject : public QGraphicsObject { float connected_port_diameter = 8.0f; float unconnected_port_diameter = 6.0f; - // Size of the node - QSizeF size = QSizeF(150.0f, 250.0f); + // Caption + float caption_font_size = 18.0f; + float port_caption_font_size = 8.0f; QRectF boundingRect() const override; void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override; @@ -371,52 +385,44 @@ class VisualShaderNodeGraphicsObject : public QGraphicsObject { class VisualShaderInputPortGraphicsObject : public QGraphicsObject { public: - VisualShaderInputPortGraphicsObject(const QString& name, - const QRectF& parent_node_rect, - const QRectF& rect, + VisualShaderInputPortGraphicsObject(const QRectF& rect, const int& p_index, QGraphicsItem* parent = nullptr); ~VisualShaderInputPortGraphicsObject(); - QRectF boundingRect() const override; - void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override; - private: - QString name; int p_index; QRectF rect; - QRectF parent_node_rect; // Style QColor font_color = QColor(255, 255, 255); QColor connection_point_color = QColor(169, 169, 169); float opacity = 1.0f; + + QRectF boundingRect() const override; + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override; }; class VisualShaderOutputPortGraphicsObject : public QGraphicsObject { public: - VisualShaderOutputPortGraphicsObject(const QString& name, - const QRectF& parent_node_rect, - const QRectF& rect, + VisualShaderOutputPortGraphicsObject(const QRectF& rect, const int& p_index, QGraphicsItem* parent = nullptr); ~VisualShaderOutputPortGraphicsObject(); - QRectF boundingRect() const override; - void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override; - private: - QString name; int p_index; QRectF rect; - QRectF parent_node_rect; // Style QColor font_color = QColor(255, 255, 255); QColor connection_point_color = QColor(169, 169, 169); float opacity = 1.0f; + + QRectF boundingRect() const override; + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override; }; /**********************************************************************/ @@ -480,15 +486,18 @@ class VisualShaderConnectionStartGraphicsObject : public QGraphicsObject { VisualShaderConnectionStartGraphicsObject(const QRectF& rect, QGraphicsItem* parent = nullptr); ~VisualShaderConnectionStartGraphicsObject(); - QRectF boundingRect() const override; - void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override; - private: + int n_id; + int p_index; // This is an output port index + QRectF rect; // Style QColor connection_point_color = QColor(169, 169, 169); float opacity = 1.0f; + + QRectF boundingRect() const override; + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override; }; class VisualShaderConnectionEndGraphicsObject : public QGraphicsObject { @@ -496,15 +505,18 @@ class VisualShaderConnectionEndGraphicsObject : public QGraphicsObject { VisualShaderConnectionEndGraphicsObject(const QRectF& rect, QGraphicsItem* parent = nullptr); ~VisualShaderConnectionEndGraphicsObject(); - QRectF boundingRect() const override; - void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override; - private: + int n_id; + int p_index; // This is an input port index + QRectF rect; // Style QColor connection_point_color = QColor(169, 169, 169); float opacity = 1.0f; + + QRectF boundingRect() const override; + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override; }; /**********************************************************************/ From eb1684fde6e6487522ccaff1f4dfa55c1ae7346b Mon Sep 17 00:00:00 2001 From: Saif Kandil <74428638+k0T0z@users.noreply.github.com> Date: Sun, 15 Sep 2024 14:16:32 +0300 Subject: [PATCH 21/56] Fixed the text alignment --- Editors/VisualShaderEditor.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Editors/VisualShaderEditor.cpp b/Editors/VisualShaderEditor.cpp index e6ada3c8f..a6e9a1ba1 100644 --- a/Editors/VisualShaderEditor.cpp +++ b/Editors/VisualShaderEditor.cpp @@ -1099,7 +1099,7 @@ void VisualShaderNodeGraphicsObject::paint(QPainter *painter, const QStyleOption float x {(float)(caption_rect.center().x() - (float)fm.horizontalAdvance(caption) * 0.5f)}; // Instead of subtracting, add the ascent to properly align text within the rect - float y {(float)(caption_rect.center().y() + (float)fm.ascent() * 0.5f)}; + float y {(float)(caption_rect.center().y() + (float)((fm.ascent() + fm.descent()) * 0.5f - fm.descent()))}; QPointF coordinate {x, y}; @@ -1139,7 +1139,7 @@ void VisualShaderNodeGraphicsObject::paint(QPainter *painter, const QStyleOption float x {rect_x + 5.0f}; - float y {(float)(port_rect.center().y()) + (float)fm.ascent() * 0.5f}; + float y {(float)(port_rect.center().y()) + (float)((fm.ascent() + fm.descent()) * 0.5f - fm.descent())}; QPointF coordinate {x, y}; @@ -1186,9 +1186,9 @@ void VisualShaderNodeGraphicsObject::paint(QPainter *painter, const QStyleOption QFontMetrics fm(f); painter->setFont(f); - float x {rect_x + rect_w - (float)fm.horizontalAdvance(p_n, p_n.length()) - 5.0f}; + float x {rect_x + rect_w - (float)fm.horizontalAdvance(p_n) - 5.0f}; - float y {(float)(port_rect.center().y()) + (float)fm.ascent() * 0.5f}; + float y {(float)(port_rect.center().y()) + (float)((fm.ascent() + fm.descent()) * 0.5f - fm.descent())}; QPointF coordinate {x, y}; From ae9433a0d229a23b96490af3091c080086bc59f2 Mon Sep 17 00:00:00 2001 From: Saif Kandil <74428638+k0T0z@users.noreply.github.com> Date: Sun, 15 Sep 2024 19:18:11 +0300 Subject: [PATCH 22/56] almost done --- Editors/VisualShaderEditor.cpp | 302 ++++++++++++++++++++++++++------- Editors/VisualShaderEditor.h | 126 +++++++++++--- 2 files changed, 344 insertions(+), 84 deletions(-) diff --git a/Editors/VisualShaderEditor.cpp b/Editors/VisualShaderEditor.cpp index a6e9a1ba1..1174fcd7e 100644 --- a/Editors/VisualShaderEditor.cpp +++ b/Editors/VisualShaderEditor.cpp @@ -555,7 +555,9 @@ void CreateNodeDialog::update_selected_item() { // Public functions ////////////////////////////// -VisualShaderGraphicsScene::VisualShaderGraphicsScene(VisualShader* vs, QObject* parent) : QGraphicsScene(parent), vs(vs) { +VisualShaderGraphicsScene::VisualShaderGraphicsScene(VisualShader* vs, QObject* parent) : QGraphicsScene(parent), + vs(vs), + one_free_end_connection(nullptr) { setItemIndexMethod(QGraphicsScene::NoIndex); // https://doc.qt.io/qt-6/qgraphicsscene.html#ItemIndexMethod-enum // Populate the scene with nodes @@ -579,16 +581,14 @@ VisualShaderGraphicsScene::VisualShaderGraphicsScene(VisualShader* vs, QObject* for (const int& connection_id : connections) { const VisualShader::Connection connection {vs->get_connection(connection_id)}; - VisualShaderConnectionGraphicsObject* c_o {new VisualShaderConnectionGraphicsObject(connection.from_node, connection.from_port)}; + VisualShaderNodeGraphicsObject* n_o {get_node_graphics_object(connection.from_node)}; + + VisualShaderConnectionGraphicsObject* c_o {new VisualShaderConnectionGraphicsObject(connection.from_node, connection.from_port, n_o->find_output_port_coordinate(connection.from_port))}; connection_graphics_objects[connection_id] = c_o; this->addItem(c_o); } QObject::connect(this, &VisualShaderGraphicsScene::node_moved, this, &VisualShaderGraphicsScene::on_node_moved); - - VisualShaderConnectionGraphicsObject* c_o {new VisualShaderConnectionGraphicsObject(1, 0)}; - connection_graphics_objects[0] = c_o; - this->addItem(c_o); } VisualShaderGraphicsScene::~VisualShaderGraphicsScene() { @@ -667,9 +667,132 @@ VisualShaderConnectionGraphicsObject* VisualShaderGraphicsScene::get_connection_ return c_o; } -void VisualShaderGraphicsScene::on_node_moved(const int& n_id, const QPointF& new_position) { - // Here we will move all connections that are connected to the node - // std::cout << "Node moved: " << n_id << " to " << new_position.x() << ", " << new_position.y() << std::endl; +void VisualShaderGraphicsScene::on_node_moved(const int& n_id, const QPointF& new_coordinate) { + const std::shared_ptr n {vs->get_node(n_id)}; + + if (!n) { + return; + } + + // Update the node's coordinate in the VisualShader + vs->set_node_coordinate(n_id, {(float)new_coordinate.x(), (float)new_coordinate.y()}); + + VisualShaderNodeGraphicsObject* n_o {get_node_graphics_object(n_id)}; + + if (!n_o) { + return; + } + + // Update input ports + for (int i {0}; i < n->get_input_port_count(); i++) { + VisualShaderInputPortGraphicsObject* i_p_o {n_o->get_input_port_graphics_object(i)}; + + if (!i_p_o) { + continue; + } + + i_p_o->set_coordinate(n_o->find_input_port_coordinate(i)); + } + + // Update output ports + for (int i {0}; i < n->get_output_port_count(); i++) { + VisualShaderOutputPortGraphicsObject* o_p_o {n_o->get_output_port_graphics_object(i)}; + + if (!o_p_o) { + continue; + } + + o_p_o->set_coordinate(n_o->find_output_port_coordinate(i)); + } +} + +void VisualShaderGraphicsScene::on_port_dragged(QGraphicsObject* port, const QPointF& coordinate) { + VisualShaderOutputPortGraphicsObject* o_port {dynamic_cast(port)}; + + if (!o_port) { + VisualShaderInputPortGraphicsObject* i_port {dynamic_cast(port)}; + + if (!i_port) { + return; + } + + // If it is not connected, then we don't need to do anything + // If it is connected, however, we will convert the complete connection to a one free end connection + if (!i_port->is_connected()) { + return; + } + + return; + } + + if (!one_free_end_connection) { + // Create a One Free End Connection + QPointF start_coord {o_port->get_coordinate()}; + one_free_end_connection = new VisualShaderConnectionGraphicsObject(o_port->get_node_id(), o_port->get_port_index(), start_coord); + addItem(one_free_end_connection); + } + + one_free_end_connection->set_end_coordinate(coordinate); +} + +void VisualShaderGraphicsScene::on_port_dropped(QGraphicsObject* port, const QPointF& coordinate) { + if (!one_free_end_connection) { + return; + } + + // Find all items under the coordinate + QList items_at_coordinate {this->items(coordinate)}; + + // Iterate through the items and check if an input port is under the mouse + VisualShaderInputPortGraphicsObject* in_p_o {nullptr}; + for (QGraphicsItem* item : items_at_coordinate) { + // Check if the item is an input port + in_p_o = dynamic_cast(item); + + if (in_p_o) { + break; + } + } + + if (!in_p_o) { + // Delete the connection + this->removeItem(one_free_end_connection); + delete one_free_end_connection; + one_free_end_connection = nullptr; + return; + } + + bool result {vs->can_connect_nodes(one_free_end_connection->get_from_node_id(), one_free_end_connection->get_from_port_index(), in_p_o->get_node_id(), in_p_o->get_port_index())}; + + if (!result) { + // Delete the connection + this->removeItem(one_free_end_connection); + delete one_free_end_connection; + one_free_end_connection = nullptr; + std::cout << "Failed to connect nodes" << std::endl; + return; + } + + // Get a connection id before connecting + int c_id {vs->get_valid_connection_id()}; + + // Connect the nodes + result = vs->connect_nodes(one_free_end_connection->get_from_node_id(), one_free_end_connection->get_from_port_index(), in_p_o->get_node_id(), in_p_o->get_port_index()); + + if (!result) { + // Delete the connection + this->removeItem(one_free_end_connection); + delete one_free_end_connection; + one_free_end_connection = nullptr; + std::cout << "Failed to connect nodes" << std::endl; + return; + } + + // Add the connection to the scene + connection_graphics_objects[c_id] = one_free_end_connection; + + // Reset the one free end connection + one_free_end_connection = nullptr; } /**********************************************************************/ @@ -997,6 +1120,38 @@ VisualShaderNodeGraphicsObject::~VisualShaderNodeGraphicsObject() { } +VisualShaderInputPortGraphicsObject* VisualShaderNodeGraphicsObject::get_input_port_graphics_object(const int& p_index) const { + if (in_port_graphics_objects.find(p_index) != in_port_graphics_objects.end()) { + return in_port_graphics_objects.at(p_index); + } + + return nullptr; +} + +VisualShaderOutputPortGraphicsObject* VisualShaderNodeGraphicsObject::get_output_port_graphics_object(const int& p_index) const { + if (out_port_graphics_objects.find(p_index) != out_port_graphics_objects.end()) { + return out_port_graphics_objects.at(p_index); + } + + return nullptr; +} + +QPointF VisualShaderNodeGraphicsObject::find_input_port_coordinate(const int& p_index) const { + if (in_port_graphics_objects.find(p_index) != in_port_graphics_objects.end()) { + return in_port_graphics_objects.at(p_index)->get_coordinate(); + } + + return QPointF(); +} + +QPointF VisualShaderNodeGraphicsObject::find_output_port_coordinate(const int& p_index) const { + if (out_port_graphics_objects.find(p_index) != out_port_graphics_objects.end()) { + return out_port_graphics_objects.at(p_index)->get_coordinate(); + } + + return QPointF(); +} + QRectF VisualShaderNodeGraphicsObject::boundingRect() const { const std::shared_ptr n {vs->get_node(n_id)}; @@ -1048,6 +1203,12 @@ void VisualShaderNodeGraphicsObject::paint(QPainter *painter, const QStyleOption return; } + int n_id {vs->find_node_id(n)}; + + if (n_id == VisualShader::NODE_ID_INVALID) { + return; + } + painter->setClipRect(option->exposedRect); // Get the rect without the padding @@ -1153,8 +1314,12 @@ void VisualShaderNodeGraphicsObject::paint(QPainter *painter, const QStyleOption continue; // Draw the port - VisualShaderInputPortGraphicsObject* p_o {new VisualShaderInputPortGraphicsObject(port_rect, i, this)}; + VisualShaderInputPortGraphicsObject* p_o {new VisualShaderInputPortGraphicsObject(port_rect, n_id, i, this)}; in_port_graphics_objects[i] = p_o; + + // Connect the signals + QObject::connect(p_o, &VisualShaderInputPortGraphicsObject::port_dragged, dynamic_cast(scene()), &VisualShaderGraphicsScene::on_port_dragged); + QObject::connect(p_o, &VisualShaderInputPortGraphicsObject::port_dropped, dynamic_cast(scene()), &VisualShaderGraphicsScene::on_port_dropped); } } @@ -1202,8 +1367,12 @@ void VisualShaderNodeGraphicsObject::paint(QPainter *painter, const QStyleOption continue; // Draw the port - VisualShaderOutputPortGraphicsObject* p_o {new VisualShaderOutputPortGraphicsObject(port_rect, i, this)}; + VisualShaderOutputPortGraphicsObject* p_o {new VisualShaderOutputPortGraphicsObject(port_rect, n_id, i, this)}; out_port_graphics_objects[i] = p_o; + + // Connect the signals + QObject::connect(p_o, &VisualShaderOutputPortGraphicsObject::port_dragged, dynamic_cast(scene()), &VisualShaderGraphicsScene::on_port_dragged); + QObject::connect(p_o, &VisualShaderOutputPortGraphicsObject::port_dropped, dynamic_cast(scene()), &VisualShaderGraphicsScene::on_port_dropped); } } } @@ -1217,31 +1386,19 @@ QVariant VisualShaderNodeGraphicsObject::itemChange(GraphicsItemChange change, c return QGraphicsObject::itemChange(change, value); } -void VisualShaderNodeGraphicsObject::mousePressEvent(QGraphicsSceneMouseEvent *event) { - QGraphicsObject::mousePressEvent(event); -} - -void VisualShaderNodeGraphicsObject::mouseMoveEvent(QGraphicsSceneMouseEvent *event) { - QGraphicsObject::mouseMoveEvent(event); -} - -void VisualShaderNodeGraphicsObject::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { - QGraphicsObject::mouseReleaseEvent(event); -} - -void VisualShaderNodeGraphicsObject::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) { - QGraphicsObject::contextMenuEvent(event); -} - VisualShaderInputPortGraphicsObject::VisualShaderInputPortGraphicsObject(const QRectF& rect, + const int& n_id, const int& p_index, QGraphicsItem* parent) : QGraphicsObject(parent), rect(rect), + n_id(n_id), p_index(p_index) { setFlag(QGraphicsItem::ItemDoesntPropagateOpacityToChildren, true); setFlag(QGraphicsItem::ItemIsFocusable, true); setFlag(QGraphicsItem::ItemIsSelectable, true); + setCursor(Qt::PointingHandCursor); + setCacheMode(QGraphicsItem::DeviceCoordinateCache); setVisible(true); @@ -1255,26 +1412,55 @@ VisualShaderInputPortGraphicsObject::~VisualShaderInputPortGraphicsObject() { } QRectF VisualShaderInputPortGraphicsObject::boundingRect() const { + // rect.adjust(-padding, -padding, padding, padding); + return rect; } void VisualShaderInputPortGraphicsObject::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { painter->setClipRect(option->exposedRect); + + // rect.adjust(padding, padding, -padding, -padding); painter->setBrush(this->connection_point_color); painter->drawEllipse(rect); } +void VisualShaderInputPortGraphicsObject::mousePressEvent(QGraphicsSceneMouseEvent *event) { + is_dragging = true; + QGraphicsObject::mousePressEvent(event); +} + +void VisualShaderInputPortGraphicsObject::mouseMoveEvent(QGraphicsSceneMouseEvent *event) { + if (is_dragging) { + emit port_dragged(this, event->scenePos()); + } + QGraphicsObject::mouseMoveEvent(event); +} + +void VisualShaderInputPortGraphicsObject::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { + if (is_dragging) { + emit port_dropped(this, event->scenePos()); + is_dragging = false; + } + QGraphicsObject::mouseReleaseEvent(event); +} + VisualShaderOutputPortGraphicsObject::VisualShaderOutputPortGraphicsObject(const QRectF& rect, + const int& n_id, const int& p_index, QGraphicsItem* parent) : QGraphicsObject(parent), rect(rect), - p_index(p_index) { + n_id(n_id), + p_index(p_index), + start_graphics_object(nullptr) { setFlag(QGraphicsItem::ItemDoesntPropagateOpacityToChildren, true); setFlag(QGraphicsItem::ItemIsFocusable, true); setFlag(QGraphicsItem::ItemIsSelectable, true); + setCursor(Qt::PointingHandCursor); + setCacheMode(QGraphicsItem::DeviceCoordinateCache); setVisible(true); @@ -1288,18 +1474,40 @@ VisualShaderOutputPortGraphicsObject::~VisualShaderOutputPortGraphicsObject() { } QRectF VisualShaderOutputPortGraphicsObject::boundingRect() const { + // rect.adjust(-padding, -padding, padding, padding); + return rect; } void VisualShaderOutputPortGraphicsObject::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { painter->setClipRect(option->exposedRect); + + // rect.adjust(padding, padding, -padding, -padding); painter->setBrush(this->connection_point_color); painter->drawEllipse(rect); } +void VisualShaderOutputPortGraphicsObject::mousePressEvent(QGraphicsSceneMouseEvent *event) { + is_dragging = true; + QGraphicsObject::mousePressEvent(event); +} + +void VisualShaderOutputPortGraphicsObject::mouseMoveEvent(QGraphicsSceneMouseEvent *event) { + if (is_dragging) { + emit port_dragged(this, event->scenePos()); + } + QGraphicsObject::mouseMoveEvent(event); +} +void VisualShaderOutputPortGraphicsObject::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { + if (is_dragging) { + emit port_dropped(this, event->scenePos()); + is_dragging = false; + } + QGraphicsObject::mouseReleaseEvent(event); +} /**********************************************************************/ /**********************************************************************/ @@ -1313,12 +1521,14 @@ void VisualShaderOutputPortGraphicsObject::paint(QPainter *painter, const QStyle VisualShaderConnectionGraphicsObject::VisualShaderConnectionGraphicsObject(const int& from_n_id, const int& from_p_index, + const QPointF& start_coordinate, QGraphicsItem* parent) : QGraphicsObject(parent), from_n_id(from_n_id), from_p_index(from_p_index), + start_coordinate(start_coordinate), + end_coordinate(start_coordinate), start_graphics_object(nullptr), end_graphics_object(nullptr) { - setFlag(QGraphicsItem::ItemIsMovable, true); setFlag(QGraphicsItem::ItemIsFocusable, true); setFlag(QGraphicsItem::ItemIsSelectable, true); @@ -1344,35 +1554,9 @@ QRectF VisualShaderConnectionGraphicsObject::boundingRect() const { void VisualShaderConnectionGraphicsObject::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { painter->setClipRect(option->exposedRect); - // Get the rect without the padding - QRectF r {this->boundingRect()}; - - // { - // // Draw rect - // QColor rect_color {Qt::red}; - - // QPen p(rect_color, 3.0f); - // painter->setPen(p); - - // painter->setBrush(Qt::NoBrush); - - // painter->drawRect(r); - // } - - // Add the padding to the rect - r.adjust(rect_padding, rect_padding, -rect_padding, -rect_padding); - - float rect_x {(float)r.topLeft().x()}; - float rect_y {(float)r.topLeft().y()}; - - float rect_w {(float)r.width()}; - float rect_h {(float)r.height()}; - - float min_side {qMin(rect_w, rect_h)}; - { QPen pen; - pen.setWidth(min_side * 0.05f); + pen.setWidth(this->line_width); pen.setColor(this->construction_color); pen.setStyle(Qt::DashLine); @@ -1391,7 +1575,7 @@ void VisualShaderConnectionGraphicsObject::paint(QPainter *painter, const QStyle { // draw normal line QPen p; - p.setWidth(min_side * 0.05f); + p.setWidth(this->line_width); const bool selected {this->isSelected()}; @@ -1415,7 +1599,7 @@ void VisualShaderConnectionGraphicsObject::paint(QPainter *painter, const QStyle { // Draw start and end points if (!start_graphics_object) { - QRectF start_rect(start_coordinate.x(), start_coordinate.y(), min_side * 0.2f, min_side * 0.2f); + QRectF start_rect(start_coordinate.x(), start_coordinate.y(), this->point_diameter, this->point_diameter); // Adjust the port rect to be centered start_rect.adjust(-start_rect.width() * 0.5f, -start_rect.height() * 0.5f, -start_rect.width() * 0.5f, -start_rect.height() * 0.5f); @@ -1425,7 +1609,7 @@ void VisualShaderConnectionGraphicsObject::paint(QPainter *painter, const QStyle } if (!end_graphics_object) { - QRectF end_rect(end_coordinate.x(), end_coordinate.y(), min_side * 0.2f, min_side * 0.2f); + QRectF end_rect(end_coordinate.x(), end_coordinate.y(), this->point_diameter, this->point_diameter); // Adjust the port rect to be centered end_rect.adjust(-end_rect.width() * 0.5f, -end_rect.height() * 0.5f, -end_rect.width() * 0.5f, -end_rect.height() * 0.5f); diff --git a/Editors/VisualShaderEditor.h b/Editors/VisualShaderEditor.h index ab114e038..00714d7d6 100644 --- a/Editors/VisualShaderEditor.h +++ b/Editors/VisualShaderEditor.h @@ -44,6 +44,7 @@ #include #include #include +#include #include #include @@ -209,11 +210,15 @@ class VisualShaderGraphicsScene : public QGraphicsScene { VisualShader* get_visual_shader() const { return vs; } +public Q_SLOTS: + void on_port_dragged(QGraphicsObject* port, const QPointF& coordinate); + void on_port_dropped(QGraphicsObject* port, const QPointF& coordinate); + Q_SIGNALS: - void node_moved(const int& n_id, const QPointF& new_position); + void node_moved(const int& n_id, const QPointF& new_coordinate); private Q_SLOTS: - void on_node_moved(const int& n_id, const QPointF& new_position); + void on_node_moved(const int& n_id, const QPointF& new_coordinate); private: VisualShader* vs; @@ -328,6 +333,12 @@ class VisualShaderNodeGraphicsObject : public QGraphicsObject { VisualShaderNodeGraphicsObject(VisualShader* vs, const int& n_id, QGraphicsItem *parent = nullptr); ~VisualShaderNodeGraphicsObject(); + VisualShaderInputPortGraphicsObject* get_input_port_graphics_object(const int& p_index) const; + VisualShaderOutputPortGraphicsObject* get_output_port_graphics_object(const int& p_index) const; + + QPointF find_input_port_coordinate(const int& p_index) const; + QPointF find_output_port_coordinate(const int& p_index) const; + private: VisualShader* vs; int n_id; @@ -338,19 +349,10 @@ class VisualShaderNodeGraphicsObject : public QGraphicsObject { // Style QColor normal_boundary_color = QColor(255, 255, 255); QColor selected_boundary_color = QColor(255, 165, 0); - QColor gradient_color0 = QColor(80, 80, 80); - QColor gradient_color1 = QColor(64, 64, 64); - QColor gradient_color2 = QColor(58, 58, 58); - QColor shadow_color = QColor(20, 20, 20); QColor font_color = QColor(255, 255, 255); - QColor font_color_faded = QColor(169, 169, 169); - QColor filled_connection_point_color = QColor(0, 255, 255); - QColor error_color = QColor(255, 0, 0); - QColor warning_color = QColor(128, 128, 0); QColor fill_color = QColor(0, 0, 0, 0); float pen_width = 1.0f; - float hovered_pen_width = 1.5f; float opacity = 0.8f; float corner_radius = 3.0f; @@ -360,7 +362,7 @@ class VisualShaderNodeGraphicsObject : public QGraphicsObject { mutable float rect_height; // Calculated in boundingRect() float body_rect_header_height = 30.0f; - float body_rect_port_step = 35.0f; + float body_rect_port_step = 40.0f; float body_rect_footer_height = 30.0f; mutable float rect_padding; // Calculated in boundingRect() @@ -377,22 +379,41 @@ class VisualShaderNodeGraphicsObject : public QGraphicsObject { QRectF boundingRect() const override; void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override; QVariant itemChange(GraphicsItemChange change, const QVariant &value) override; - void mousePressEvent(QGraphicsSceneMouseEvent *event) override; - void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override; - void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override; - void contextMenuEvent(QGraphicsSceneContextMenuEvent *event) override; }; class VisualShaderInputPortGraphicsObject : public QGraphicsObject { + Q_OBJECT + public: VisualShaderInputPortGraphicsObject(const QRectF& rect, + const int& n_id, const int& p_index, QGraphicsItem* parent = nullptr); ~VisualShaderInputPortGraphicsObject(); + int get_node_id() const { return n_id; } + int get_port_index() const { return p_index; } + QPointF get_coordinate() const { return rect.center(); } + void set_coordinate(const QPointF& coordinate) const { rect.moveCenter(coordinate); } + + bool is_connected() const { return end_graphics_object != nullptr; } + VisualShaderConnectionEndGraphicsObject* get_end_graphics_object() const { return end_graphics_object; } + void set_end_graphics_object(VisualShaderConnectionEndGraphicsObject* end_graphics_object) const { this->end_graphics_object = end_graphics_object; } + +Q_SIGNALS: + void port_dragged(VisualShaderInputPortGraphicsObject* port, const QPointF& pos); + void port_dropped(VisualShaderInputPortGraphicsObject* port, const QPointF& pos); + private: + int n_id; int p_index; - QRectF rect; + mutable QRectF rect; + + float padding = 0.5f; + + mutable VisualShaderConnectionEndGraphicsObject* end_graphics_object; + + float is_dragging = false; // Style QColor font_color = QColor(255, 255, 255); @@ -402,18 +423,44 @@ class VisualShaderInputPortGraphicsObject : public QGraphicsObject { QRectF boundingRect() const override; void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override; + void mousePressEvent(QGraphicsSceneMouseEvent *event) override; + void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override; + void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override; }; class VisualShaderOutputPortGraphicsObject : public QGraphicsObject { + Q_OBJECT + public: VisualShaderOutputPortGraphicsObject(const QRectF& rect, + const int& n_id, const int& p_index, QGraphicsItem* parent = nullptr); ~VisualShaderOutputPortGraphicsObject(); + int get_node_id() const { return n_id; } + int get_port_index() const { return p_index; } + QPointF get_coordinate() const { return rect.center(); } + void set_coordinate(const QPointF& coordinate) const { rect.moveCenter(coordinate); } + + bool is_connected() const { return start_graphics_object != nullptr; } + VisualShaderConnectionStartGraphicsObject* get_start_graphics_object() const { return start_graphics_object; } + void set_start_graphics_object(VisualShaderConnectionStartGraphicsObject* start_graphics_object) const { this->start_graphics_object = start_graphics_object; } + +Q_SIGNALS: + void port_dragged(VisualShaderOutputPortGraphicsObject* port, const QPointF& pos); + void port_dropped(VisualShaderOutputPortGraphicsObject* port, const QPointF& pos); + private: + int n_id; int p_index; - QRectF rect; + mutable QRectF rect; + + float padding = 0.5f; + + mutable VisualShaderConnectionStartGraphicsObject* start_graphics_object; + + float is_dragging = false; // Style QColor font_color = QColor(255, 255, 255); @@ -423,6 +470,9 @@ class VisualShaderOutputPortGraphicsObject : public QGraphicsObject { QRectF boundingRect() const override; void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override; + void mousePressEvent(QGraphicsSceneMouseEvent *event) override; + void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override; + void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override; }; /**********************************************************************/ @@ -442,17 +492,33 @@ class VisualShaderConnectionGraphicsObject : public QGraphicsObject { Q_OBJECT public: - VisualShaderConnectionGraphicsObject(const int& from_n_id, const int& from_p_index, QGraphicsItem *parent = nullptr); + VisualShaderConnectionGraphicsObject(const int& from_n_id, + const int& from_p_index, + const QPointF& start_coordinate, + QGraphicsItem *parent = nullptr); ~VisualShaderConnectionGraphicsObject(); + int get_from_node_id() const { return from_n_id; } + int get_from_port_index() const { return from_p_index; } + + void set_to_node_id(const int& to_n_id) const { this->to_n_id = to_n_id; } + void set_to_port_index(const int& to_p_index) const { this->to_p_index = to_p_index; } + + QPointF get_start_coordinate() const { return start_coordinate; } + + void set_end_coordinate(const QPointF& end_coordinate) const { this->end_coordinate = end_coordinate; } + + VisualShaderConnectionStartGraphicsObject* get_start_graphics_object() const { return start_graphics_object; } + VisualShaderConnectionEndGraphicsObject* get_end_graphics_object() const { return end_graphics_object; } + private: int from_n_id; - int to_n_id; + mutable int to_n_id; int from_p_index; - int to_p_index; + mutable int to_p_index; - QPointF start_coordinate = QPointF(100, 100); - QPointF end_coordinate = QPointF(200, 200); + QPointF start_coordinate; + mutable QPointF end_coordinate; VisualShaderConnectionStartGraphicsObject* start_graphics_object; VisualShaderConnectionEndGraphicsObject* end_graphics_object; @@ -486,11 +552,16 @@ class VisualShaderConnectionStartGraphicsObject : public QGraphicsObject { VisualShaderConnectionStartGraphicsObject(const QRectF& rect, QGraphicsItem* parent = nullptr); ~VisualShaderConnectionStartGraphicsObject(); + int get_node_id() const { return n_id; } + int get_port_index() const { return p_index; } + + void set_coordinate(const QPointF& coordinate) const { rect.moveCenter(coordinate); } + private: int n_id; int p_index; // This is an output port index - QRectF rect; + mutable QRectF rect; // Style QColor connection_point_color = QColor(169, 169, 169); @@ -505,11 +576,16 @@ class VisualShaderConnectionEndGraphicsObject : public QGraphicsObject { VisualShaderConnectionEndGraphicsObject(const QRectF& rect, QGraphicsItem* parent = nullptr); ~VisualShaderConnectionEndGraphicsObject(); + int get_node_id() const { return n_id; } + int get_port_index() const { return p_index; } + + void set_coordinate(const QPointF& coordinate) const { rect.moveCenter(coordinate); } + private: int n_id; int p_index; // This is an input port index - QRectF rect; + mutable QRectF rect; // Style QColor connection_point_color = QColor(169, 169, 169); From 749c6a03a2d7114e5622add0e0871e32a53d9148 Mon Sep 17 00:00:00 2001 From: Saif Kandil <74428638+k0T0z@users.noreply.github.com> Date: Tue, 17 Sep 2024 23:01:09 +0300 Subject: [PATCH 23/56] bug fixes and few improvements --- Editors/VisualShaderEditor.cpp | 492 +++++++++++++++++---------------- Editors/VisualShaderEditor.h | 123 +++------ 2 files changed, 290 insertions(+), 325 deletions(-) diff --git a/Editors/VisualShaderEditor.cpp b/Editors/VisualShaderEditor.cpp index 1174fcd7e..de82ac174 100644 --- a/Editors/VisualShaderEditor.cpp +++ b/Editors/VisualShaderEditor.cpp @@ -296,73 +296,61 @@ void VisualShaderEditor::add_node(QTreeWidgetItem* selected_item, const QPointF& return; } - int new_node_id {visual_shader->get_valid_node_id()}; - - if (new_node_id == VisualShader::NODE_ID_INVALID) { - return; - } - // Instantiate the node based on the type - std::shared_ptr node; + std::shared_ptr n; if (type == "VisualShaderNodeInput") { - node = std::make_shared(); + n = std::make_shared(); } else if (type == "VisualShaderNodeColorConstant") { - node = std::make_shared(); + n = std::make_shared(); } else if (type == "VisualShaderNodeBooleanConstant") { - node = std::make_shared(); + n = std::make_shared(); } else if (type == "VisualShaderNodeFloatConstant") { - node = std::make_shared(); + n = std::make_shared(); } else if (type == "VisualShaderNodeIntConstant") { - node = std::make_shared(); + n = std::make_shared(); } else if (type == "VisualShaderNodeUIntConstant") { - node = std::make_shared(); + n = std::make_shared(); } else if (type == "VisualShaderNodeVec2Constant") { - node = std::make_shared(); + n = std::make_shared(); } else if (type == "VisualShaderNodeVec3Constant") { - node = std::make_shared(); + n = std::make_shared(); } else if (type == "VisualShaderNodeVec4Constant") { - node = std::make_shared(); + n = std::make_shared(); } else if (type == "VisualShaderNodeFloatFunc") { - node = std::make_shared(); + n = std::make_shared(); } else if (type == "VisualShaderNodeIntFunc") { - node = std::make_shared(); + n = std::make_shared(); } else if (type == "VisualShaderNodeUIntFunc") { - node = std::make_shared(); + n = std::make_shared(); } else if (type == "VisualShaderNodeDerivativeFunc") { - node = std::make_shared(); + n = std::make_shared(); } else if (type == "VisualShaderNodeFloatOp") { - node = std::make_shared(); + n = std::make_shared(); } else if (type == "VisualShaderNodeIntOp") { - node = std::make_shared(); + n = std::make_shared(); } else if (type == "VisualShaderNodeUIntOp") { - node = std::make_shared(); + n = std::make_shared(); } else if (type == "VisualShaderNodeValueNoise") { - node = std::make_shared(); + n = std::make_shared(); } else if (type == "VisualShaderNodeCompare") { - node = std::make_shared(); + n = std::make_shared(); } else if (type == "VisualShaderNodeIf") { - node = std::make_shared(); + n = std::make_shared(); } else if (type == "VisualShaderNodeIs") { - node = std::make_shared(); + n = std::make_shared(); } else if (type == "VisualShaderNodeSwitch") { - node = std::make_shared(); + n = std::make_shared(); } else { std::cout << "Unknown node type: " << type << std::endl; } - if (!node) { + if (!n) { std::cout << "Failed to create node of type: " << type << std::endl; return; } - bool result {visual_shader->add_node(node, {(float)coordinate.x(), (float)coordinate.y()}, new_node_id)}; - - if (!result) { - return; - } - - scene->add_node(new_node_id); + scene->add_node(n, coordinate); } void VisualShaderEditor::show_create_node_dialog(const QPointF& coordinate) { @@ -556,37 +544,11 @@ void CreateNodeDialog::update_selected_item() { ////////////////////////////// VisualShaderGraphicsScene::VisualShaderGraphicsScene(VisualShader* vs, QObject* parent) : QGraphicsScene(parent), - vs(vs), - one_free_end_connection(nullptr) { + vs(vs), + temporary_connection_graphics_object(nullptr) { setItemIndexMethod(QGraphicsScene::NoIndex); // https://doc.qt.io/qt-6/qgraphicsscene.html#ItemIndexMethod-enum - // Populate the scene with nodes - std::vector nodes {vs->get_nodes()}; - - for (const int& node_id : nodes) { - const std::shared_ptr node {vs->get_node(node_id)}; - - if (!node) { - continue; - } - - VisualShaderNodeGraphicsObject* n_o {new VisualShaderNodeGraphicsObject(vs, node_id)}; - node_graphics_objects[node_id] = n_o; - this->addItem(n_o); - } - - // Populate the scene with connections - std::vector connections {vs->get_connections()}; - - for (const int& connection_id : connections) { - const VisualShader::Connection connection {vs->get_connection(connection_id)}; - - VisualShaderNodeGraphicsObject* n_o {get_node_graphics_object(connection.from_node)}; - - VisualShaderConnectionGraphicsObject* c_o {new VisualShaderConnectionGraphicsObject(connection.from_node, connection.from_port, n_o->find_output_port_coordinate(connection.from_port))}; - connection_graphics_objects[connection_id] = c_o; - this->addItem(c_o); - } + // Populate the scene with nodes from the VisualShader QObject::connect(this, &VisualShaderGraphicsScene::node_moved, this, &VisualShaderGraphicsScene::on_node_moved); } @@ -595,17 +557,15 @@ VisualShaderGraphicsScene::~VisualShaderGraphicsScene() { } -bool VisualShaderGraphicsScene::add_node(const int& n_id) { - // Make sure the node doesn't already exist, we don't want to overwrite a node. - if (node_graphics_objects.find(n_id) != node_graphics_objects.end()) { - vs->remove_node(n_id); +bool VisualShaderGraphicsScene::add_node(const std::shared_ptr& node, const QPointF& coordinate) { + int n_id {vs->get_valid_node_id()}; + + if (n_id == VisualShader::NODE_ID_INVALID) { return false; } - const std::shared_ptr node {vs->get_node(n_id)}; - - if (!node) { - vs->remove_node(n_id); + // Make sure the node doesn't already exist, we don't want to overwrite a node. + if (node_graphics_objects.find(n_id) != node_graphics_objects.end()) { return false; } @@ -615,6 +575,12 @@ bool VisualShaderGraphicsScene::add_node(const int& n_id) { return false; } + bool result {vs->add_node(node, {(float)coordinate.x(), (float)coordinate.y()}, n_id)}; + + if (!result) { + return false; + } + VisualShaderGraphicsView* view {dynamic_cast(views.first())}; if (vs->get_node_coordinate(n_id).x < view->get_x() || @@ -656,17 +622,6 @@ VisualShaderNodeGraphicsObject* VisualShaderGraphicsScene::get_node_graphics_obj return n_o; } -VisualShaderConnectionGraphicsObject* VisualShaderGraphicsScene::get_connection_graphics_object(const int& c_id) const { - VisualShaderConnectionGraphicsObject* c_o {nullptr}; - - auto it {connection_graphics_objects.find(c_id)}; - if (it != connection_graphics_objects.end()) { - c_o = it->second; - } - - return c_o; -} - void VisualShaderGraphicsScene::on_node_moved(const int& n_id, const QPointF& new_coordinate) { const std::shared_ptr n {vs->get_node(n_id)}; @@ -677,36 +632,47 @@ void VisualShaderGraphicsScene::on_node_moved(const int& n_id, const QPointF& ne // Update the node's coordinate in the VisualShader vs->set_node_coordinate(n_id, {(float)new_coordinate.x(), (float)new_coordinate.y()}); - VisualShaderNodeGraphicsObject* n_o {get_node_graphics_object(n_id)}; + // Update coordinates of all connected connections + VisualShaderNodeGraphicsObject* n_o {this->get_node_graphics_object(n_id)}; - if (!n_o) { - return; - } + for (int i{0}; i < n->get_input_port_count(); i++) { + VisualShaderInputPortGraphicsObject* i_port {n_o->get_input_port_graphics_object(i)}; - // Update input ports - for (int i {0}; i < n->get_input_port_count(); i++) { - VisualShaderInputPortGraphicsObject* i_p_o {n_o->get_input_port_graphics_object(i)}; + if (!i_port || !i_port->is_connected()) { + continue; + } - if (!i_p_o) { + VisualShaderConnectionGraphicsObject* c_o {i_port->get_connection_graphics_object()}; + + if (!c_o) { continue; } - i_p_o->set_coordinate(n_o->find_input_port_coordinate(i)); + c_o->set_end_coordinate(i_port->get_global_coordinate()); } + + for (int i{0}; i < n->get_output_port_count(); i++) { + VisualShaderOutputPortGraphicsObject* o_port {n_o->get_output_port_graphics_object(i)}; - // Update output ports - for (int i {0}; i < n->get_output_port_count(); i++) { - VisualShaderOutputPortGraphicsObject* o_p_o {n_o->get_output_port_graphics_object(i)}; + if (!o_port || !o_port->is_connected()) { + continue; + } + + VisualShaderConnectionGraphicsObject* c_o {o_port->get_connection_graphics_object()}; - if (!o_p_o) { + if (!c_o) { continue; } - o_p_o->set_coordinate(n_o->find_output_port_coordinate(i)); + c_o->set_start_coordinate(o_port->get_global_coordinate()); } } +void VisualShaderGraphicsScene::on_port_pressed(QGraphicsObject* port, const QPointF& coordinate) {} + void VisualShaderGraphicsScene::on_port_dragged(QGraphicsObject* port, const QPointF& coordinate) { + VisualShaderConnectionGraphicsObject* c_o {nullptr}; + VisualShaderOutputPortGraphicsObject* o_port {dynamic_cast(port)}; if (!o_port) { @@ -716,27 +682,54 @@ void VisualShaderGraphicsScene::on_port_dragged(QGraphicsObject* port, const QPo return; } - // If it is not connected, then we don't need to do anything - // If it is connected, however, we will convert the complete connection to a one free end connection - if (!i_port->is_connected()) { - return; + if (i_port->is_connected() && !temporary_connection_graphics_object) { + c_o = i_port->get_connection_graphics_object(); + temporary_connection_graphics_object = c_o; // Store the connection object for access in the next drag call + i_port->detach_connection(); + c_o->set_to_node_id(VisualShader::NODE_ID_INVALID); + c_o->set_to_port_index(VisualShader::PORT_INDEX_INVALID); + } else if (!i_port->is_connected() && temporary_connection_graphics_object) { + c_o = temporary_connection_graphics_object; } + c_o->set_end_coordinate(coordinate); + return; } - if (!one_free_end_connection) { - // Create a One Free End Connection - QPointF start_coord {o_port->get_coordinate()}; - one_free_end_connection = new VisualShaderConnectionGraphicsObject(o_port->get_node_id(), o_port->get_port_index(), start_coord); - addItem(one_free_end_connection); + if (!o_port->is_connected() && !temporary_connection_graphics_object) { + c_o = new VisualShaderConnectionGraphicsObject(o_port->get_node_id(), o_port->get_port_index(), o_port->get_global_coordinate()); + temporary_connection_graphics_object = c_o; // Store the connection object for access in the next drag call + o_port->connect(c_o); + addItem(c_o); + } else if (o_port->is_connected() && temporary_connection_graphics_object) { + c_o = temporary_connection_graphics_object; + } else if (o_port->is_connected() && !temporary_connection_graphics_object) { + c_o = o_port->get_connection_graphics_object(); + temporary_connection_graphics_object = c_o; // Store the connection object for access in the next drag call + + // Detach the connection from the input port + VisualShaderNodeGraphicsObject* n_o {this->get_node_graphics_object(c_o->get_to_node_id())}; + if (!n_o) { + return; + } + VisualShaderInputPortGraphicsObject* i_port {n_o->get_input_port_graphics_object(c_o->get_to_port_index())}; + if (!i_port) { + return; + } + i_port->detach_connection(); + c_o->set_to_node_id(VisualShader::NODE_ID_INVALID); + c_o->set_to_port_index(VisualShader::PORT_INDEX_INVALID); } - one_free_end_connection->set_end_coordinate(coordinate); + c_o->set_end_coordinate(coordinate); } void VisualShaderGraphicsScene::on_port_dropped(QGraphicsObject* port, const QPointF& coordinate) { - if (!one_free_end_connection) { + VisualShaderConnectionGraphicsObject* c_o {temporary_connection_graphics_object}; + temporary_connection_graphics_object = nullptr; // Reset the temporary connection object + + if (!c_o) { return; } @@ -745,54 +738,155 @@ void VisualShaderGraphicsScene::on_port_dropped(QGraphicsObject* port, const QPo // Iterate through the items and check if an input port is under the mouse VisualShaderInputPortGraphicsObject* in_p_o {nullptr}; + VisualShaderOutputPortGraphicsObject* out_p_o {nullptr}; for (QGraphicsItem* item : items_at_coordinate) { // Check if the item is an input port in_p_o = dynamic_cast(item); + out_p_o = dynamic_cast(item); - if (in_p_o) { + if (in_p_o || out_p_o) { break; } } + + VisualShaderOutputPortGraphicsObject* o_port {dynamic_cast(port)}; - if (!in_p_o) { - // Delete the connection - this->removeItem(one_free_end_connection); - delete one_free_end_connection; - one_free_end_connection = nullptr; + if (!o_port) { + VisualShaderInputPortGraphicsObject* i_port {dynamic_cast(port)}; + + if (!i_port) { + return; + } + + if (!in_p_o && !out_p_o) { + // Get the output port of the connection object and detach it + VisualShaderNodeGraphicsObject* n_o {this->get_node_graphics_object(c_o->get_from_node_id())}; + if (!n_o) { + return; + } + VisualShaderOutputPortGraphicsObject* o_port {n_o->get_output_port_graphics_object(c_o->get_from_port_index())}; + if (!o_port) { + return; + } + + o_port->detach_connection(); + + // Delete the connection + this->removeItem(c_o); + delete c_o; + return; // Return because we dragging an input port and dropped on nothing + } else if (out_p_o) { + // Get the output port of the connection object and detach it + VisualShaderNodeGraphicsObject* n_o {this->get_node_graphics_object(c_o->get_from_node_id())}; + if (!n_o) { + return; + } + VisualShaderOutputPortGraphicsObject* o_port {n_o->get_output_port_graphics_object(c_o->get_from_port_index())}; + if (!o_port) { + return; + } + + o_port->detach_connection(); + + // Delete the connection + this->removeItem(c_o); + delete c_o; + return; // Return because we dragging an input port and dropped on an output port + } + + bool result {vs->can_connect_nodes(c_o->get_from_node_id(), c_o->get_from_port_index(), in_p_o->get_node_id(), in_p_o->get_port_index())}; + + if (!result) { + // Get the output port of the connection object and detach it + VisualShaderNodeGraphicsObject* n_o {this->get_node_graphics_object(c_o->get_from_node_id())}; + if (!n_o) { + return; + } + VisualShaderOutputPortGraphicsObject* o_port {n_o->get_output_port_graphics_object(c_o->get_from_port_index())}; + if (!o_port) { + return; + } + + o_port->detach_connection(); + + // Delete the connection + this->removeItem(c_o); + delete c_o; + return; + } + + // Connect the nodes + result = vs->connect_nodes(c_o->get_from_node_id(), c_o->get_from_port_index(), in_p_o->get_node_id(), in_p_o->get_port_index()); + + if (!result) { + // Get the output port of the connection object and detach it + VisualShaderNodeGraphicsObject* n_o {this->get_node_graphics_object(c_o->get_from_node_id())}; + if (!n_o) { + return; + } + VisualShaderOutputPortGraphicsObject* o_port {n_o->get_output_port_graphics_object(c_o->get_from_port_index())}; + if (!o_port) { + return; + } + + o_port->detach_connection(); + + // Delete the connection + this->removeItem(c_o); + delete c_o; + return; + } + + c_o->set_to_node_id(in_p_o->get_node_id()); + c_o->set_to_port_index(in_p_o->get_port_index()); + c_o->set_end_coordinate(in_p_o->get_global_coordinate()); + in_p_o->connect(c_o); return; } - bool result {vs->can_connect_nodes(one_free_end_connection->get_from_node_id(), one_free_end_connection->get_from_port_index(), in_p_o->get_node_id(), in_p_o->get_port_index())}; + if (!in_p_o && !out_p_o) { + o_port->detach_connection(); + + // Delete the connection + this->removeItem(c_o); + delete c_o; + return; // Return because we dragging an input port and dropped on nothing + } else if (out_p_o) { + o_port->detach_connection(); + + // Delete the connection + this->removeItem(c_o); + delete c_o; + return; // Return because we dragging an input port and dropped on an output port + } + + bool result {vs->can_connect_nodes(c_o->get_from_node_id(), c_o->get_from_port_index(), in_p_o->get_node_id(), in_p_o->get_port_index())}; if (!result) { + o_port->detach_connection(); + // Delete the connection - this->removeItem(one_free_end_connection); - delete one_free_end_connection; - one_free_end_connection = nullptr; - std::cout << "Failed to connect nodes" << std::endl; + this->removeItem(c_o); + delete c_o; return; } - // Get a connection id before connecting - int c_id {vs->get_valid_connection_id()}; - // Connect the nodes - result = vs->connect_nodes(one_free_end_connection->get_from_node_id(), one_free_end_connection->get_from_port_index(), in_p_o->get_node_id(), in_p_o->get_port_index()); + result = vs->connect_nodes(c_o->get_from_node_id(), c_o->get_from_port_index(), in_p_o->get_node_id(), in_p_o->get_port_index()); if (!result) { + o_port->detach_connection(); + // Delete the connection - this->removeItem(one_free_end_connection); - delete one_free_end_connection; - one_free_end_connection = nullptr; - std::cout << "Failed to connect nodes" << std::endl; + this->removeItem(c_o); + delete c_o; return; } - // Add the connection to the scene - connection_graphics_objects[c_id] = one_free_end_connection; - - // Reset the one free end connection - one_free_end_connection = nullptr; + c_o->set_to_node_id(in_p_o->get_node_id()); + c_o->set_to_port_index(in_p_o->get_port_index()); + c_o->set_end_coordinate(in_p_o->get_global_coordinate()); + in_p_o->connect(c_o); } /**********************************************************************/ @@ -1096,6 +1190,9 @@ void VisualShaderGraphicsView::move_view_to_fit_items() { VisualShaderNodeGraphicsObject::VisualShaderNodeGraphicsObject(VisualShader* vs, const int& n_id, QGraphicsItem* parent) : QGraphicsObject(parent), vs(vs), n_id(n_id), + rect_width(0.0f), + caption_rect_height(0.0f), + rect_height(0.0f), rect_margin(0.0f), rect_padding(0.0f) { setFlag(QGraphicsItem::ItemDoesntPropagateOpacityToChildren, true); @@ -1136,22 +1233,6 @@ VisualShaderOutputPortGraphicsObject* VisualShaderNodeGraphicsObject::get_output return nullptr; } -QPointF VisualShaderNodeGraphicsObject::find_input_port_coordinate(const int& p_index) const { - if (in_port_graphics_objects.find(p_index) != in_port_graphics_objects.end()) { - return in_port_graphics_objects.at(p_index)->get_coordinate(); - } - - return QPointF(); -} - -QPointF VisualShaderNodeGraphicsObject::find_output_port_coordinate(const int& p_index) const { - if (out_port_graphics_objects.find(p_index) != out_port_graphics_objects.end()) { - return out_port_graphics_objects.at(p_index)->get_coordinate(); - } - - return QPointF(); -} - QRectF VisualShaderNodeGraphicsObject::boundingRect() const { const std::shared_ptr n {vs->get_node(n_id)}; @@ -1318,6 +1399,7 @@ void VisualShaderNodeGraphicsObject::paint(QPainter *painter, const QStyleOption in_port_graphics_objects[i] = p_o; // Connect the signals + // QObject::connect(p_o, &VisualShaderInputPortGraphicsObject::port_pressed, dynamic_cast(scene()), &VisualShaderGraphicsScene::on_port_pressed); QObject::connect(p_o, &VisualShaderInputPortGraphicsObject::port_dragged, dynamic_cast(scene()), &VisualShaderGraphicsScene::on_port_dragged); QObject::connect(p_o, &VisualShaderInputPortGraphicsObject::port_dropped, dynamic_cast(scene()), &VisualShaderGraphicsScene::on_port_dropped); } @@ -1371,6 +1453,7 @@ void VisualShaderNodeGraphicsObject::paint(QPainter *painter, const QStyleOption out_port_graphics_objects[i] = p_o; // Connect the signals + // QObject::connect(p_o, &VisualShaderOutputPortGraphicsObject::port_pressed, dynamic_cast(scene()), &VisualShaderGraphicsScene::on_port_pressed); QObject::connect(p_o, &VisualShaderOutputPortGraphicsObject::port_dragged, dynamic_cast(scene()), &VisualShaderGraphicsScene::on_port_dragged); QObject::connect(p_o, &VisualShaderOutputPortGraphicsObject::port_dropped, dynamic_cast(scene()), &VisualShaderGraphicsScene::on_port_dropped); } @@ -1392,7 +1475,8 @@ VisualShaderInputPortGraphicsObject::VisualShaderInputPortGraphicsObject(const Q QGraphicsItem* parent) : QGraphicsObject(parent), rect(rect), n_id(n_id), - p_index(p_index) { + p_index(p_index), + connection_graphics_object(nullptr) { setFlag(QGraphicsItem::ItemDoesntPropagateOpacityToChildren, true); setFlag(QGraphicsItem::ItemIsFocusable, true); setFlag(QGraphicsItem::ItemIsSelectable, true); @@ -1428,22 +1512,17 @@ void VisualShaderInputPortGraphicsObject::paint(QPainter *painter, const QStyleO } void VisualShaderInputPortGraphicsObject::mousePressEvent(QGraphicsSceneMouseEvent *event) { - is_dragging = true; + emit port_pressed(this, event->scenePos()); QGraphicsObject::mousePressEvent(event); } void VisualShaderInputPortGraphicsObject::mouseMoveEvent(QGraphicsSceneMouseEvent *event) { - if (is_dragging) { - emit port_dragged(this, event->scenePos()); - } + emit port_dragged(this, event->scenePos()); QGraphicsObject::mouseMoveEvent(event); } void VisualShaderInputPortGraphicsObject::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { - if (is_dragging) { - emit port_dropped(this, event->scenePos()); - is_dragging = false; - } + emit port_dropped(this, event->scenePos()); QGraphicsObject::mouseReleaseEvent(event); } @@ -1454,7 +1533,7 @@ VisualShaderOutputPortGraphicsObject::VisualShaderOutputPortGraphicsObject(const rect(rect), n_id(n_id), p_index(p_index), - start_graphics_object(nullptr) { + connection_graphics_object(nullptr) { setFlag(QGraphicsItem::ItemDoesntPropagateOpacityToChildren, true); setFlag(QGraphicsItem::ItemIsFocusable, true); setFlag(QGraphicsItem::ItemIsSelectable, true); @@ -1490,22 +1569,17 @@ void VisualShaderOutputPortGraphicsObject::paint(QPainter *painter, const QStyle } void VisualShaderOutputPortGraphicsObject::mousePressEvent(QGraphicsSceneMouseEvent *event) { - is_dragging = true; + emit port_pressed(this, event->scenePos()); QGraphicsObject::mousePressEvent(event); } void VisualShaderOutputPortGraphicsObject::mouseMoveEvent(QGraphicsSceneMouseEvent *event) { - if (is_dragging) { - emit port_dragged(this, event->scenePos()); - } + emit port_dragged(this, event->scenePos()); QGraphicsObject::mouseMoveEvent(event); } void VisualShaderOutputPortGraphicsObject::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { - if (is_dragging) { - emit port_dropped(this, event->scenePos()); - is_dragging = false; - } + emit port_dropped(this, event->scenePos()); QGraphicsObject::mouseReleaseEvent(event); } @@ -1525,10 +1599,11 @@ VisualShaderConnectionGraphicsObject::VisualShaderConnectionGraphicsObject(const QGraphicsItem* parent) : QGraphicsObject(parent), from_n_id(from_n_id), from_p_index(from_p_index), + to_n_id((int)VisualShader::NODE_ID_INVALID), + to_p_index((int)VisualShader::PORT_INDEX_INVALID), start_coordinate(start_coordinate), end_coordinate(start_coordinate), - start_graphics_object(nullptr), - end_graphics_object(nullptr) { + rect_padding(0.0f) { setFlag(QGraphicsItem::ItemIsFocusable, true); setFlag(QGraphicsItem::ItemIsSelectable, true); @@ -1596,27 +1671,26 @@ void VisualShaderConnectionGraphicsObject::paint(QPainter *painter, const QStyle painter->drawPath(cubic); } + painter->setBrush(this->connection_point_color); + { - // Draw start and end points - if (!start_graphics_object) { - QRectF start_rect(start_coordinate.x(), start_coordinate.y(), this->point_diameter, this->point_diameter); + // Draw start point + QRectF start_rect(start_coordinate.x(), start_coordinate.y(), this->point_diameter, this->point_diameter); - // Adjust the port rect to be centered - start_rect.adjust(-start_rect.width() * 0.5f, -start_rect.height() * 0.5f, -start_rect.width() * 0.5f, -start_rect.height() * 0.5f); + // Adjust the port rect to be centered + start_rect.adjust(-start_rect.width() * 0.5f, -start_rect.height() * 0.5f, -start_rect.width() * 0.5f, -start_rect.height() * 0.5f); - VisualShaderConnectionStartGraphicsObject* s_o {new VisualShaderConnectionStartGraphicsObject(start_rect, this)}; - start_graphics_object = s_o; - } + painter->drawEllipse(start_rect); + } - if (!end_graphics_object) { - QRectF end_rect(end_coordinate.x(), end_coordinate.y(), this->point_diameter, this->point_diameter); + { + // Draw end point + QRectF end_rect(end_coordinate.x(), end_coordinate.y(), this->point_diameter, this->point_diameter); - // Adjust the port rect to be centered - end_rect.adjust(-end_rect.width() * 0.5f, -end_rect.height() * 0.5f, -end_rect.width() * 0.5f, -end_rect.height() * 0.5f); + // Adjust the port rect to be centered + end_rect.adjust(-end_rect.width() * 0.5f, -end_rect.height() * 0.5f, -end_rect.width() * 0.5f, -end_rect.height() * 0.5f); - VisualShaderConnectionEndGraphicsObject* e_o {new VisualShaderConnectionEndGraphicsObject(end_rect, this)}; - end_graphics_object = e_o; - } + painter->drawEllipse(end_rect); } } @@ -1774,56 +1848,6 @@ std::pair VisualShaderConnectionGraphicsObject::calculate_cont return std::make_pair(cp1, cp2); } -VisualShaderConnectionStartGraphicsObject::VisualShaderConnectionStartGraphicsObject(const QRectF& rect, QGraphicsItem* parent) : QGraphicsObject(parent), - rect(rect) { - setFlag(QGraphicsItem::ItemIsMovable, true); - setFlag(QGraphicsItem::ItemIsFocusable, true); - setFlag(QGraphicsItem::ItemIsSelectable, true); - - setVisible(true); - setOpacity(this->opacity); - - setZValue(-1.0f); -} - -VisualShaderConnectionStartGraphicsObject::~VisualShaderConnectionStartGraphicsObject() {} - -QRectF VisualShaderConnectionStartGraphicsObject::boundingRect() const { - return rect; -} - -void VisualShaderConnectionStartGraphicsObject::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { - painter->setClipRect(option->exposedRect); - - painter->setBrush(this->connection_point_color); - painter->drawEllipse(rect); -} - -VisualShaderConnectionEndGraphicsObject::VisualShaderConnectionEndGraphicsObject(const QRectF& rect, QGraphicsItem* parent) : QGraphicsObject(parent), - rect(rect) { - setFlag(QGraphicsItem::ItemIsMovable, true); - setFlag(QGraphicsItem::ItemIsFocusable, true); - setFlag(QGraphicsItem::ItemIsSelectable, true); - - setVisible(true); - setOpacity(this->opacity); - - setZValue(-1.0f); -} - -VisualShaderConnectionEndGraphicsObject::~VisualShaderConnectionEndGraphicsObject() {} - -QRectF VisualShaderConnectionEndGraphicsObject::boundingRect() const { - return rect; -} - -void VisualShaderConnectionEndGraphicsObject::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { - painter->setClipRect(option->exposedRect); - - painter->setBrush(this->connection_point_color); - painter->drawEllipse(rect); -} - /**********************************************************************/ /**********************************************************************/ /**********************************************************************/ diff --git a/Editors/VisualShaderEditor.h b/Editors/VisualShaderEditor.h index 00714d7d6..06e5050ef 100644 --- a/Editors/VisualShaderEditor.h +++ b/Editors/VisualShaderEditor.h @@ -57,6 +57,8 @@ class VisualShaderGraphicsView; class VisualShaderNodeGraphicsObject; class VisualShaderConnectionGraphicsObject; class CreateNodeDialog; +class VisualShaderInputPortGraphicsObject; +class VisualShaderOutputPortGraphicsObject; /**********************************************************************/ /**********************************************************************/ @@ -199,18 +201,18 @@ class VisualShaderGraphicsScene : public QGraphicsScene { ~VisualShaderGraphicsScene(); - bool add_node(const int& n_id); + bool add_node(const std::shared_ptr& node, const QPointF& coordinate); bool delete_node(); bool add_connection(); bool delete_connection(); VisualShaderNodeGraphicsObject* get_node_graphics_object(const int& n_id) const; - VisualShaderConnectionGraphicsObject* get_connection_graphics_object(const int& c_id) const; VisualShader* get_visual_shader() const { return vs; } public Q_SLOTS: + void on_port_pressed(QGraphicsObject* port, const QPointF& coordinate); void on_port_dragged(QGraphicsObject* port, const QPointF& coordinate); void on_port_dropped(QGraphicsObject* port, const QPointF& coordinate); @@ -224,9 +226,8 @@ private Q_SLOTS: VisualShader* vs; std::unordered_map node_graphics_objects; - std::unordered_map connection_graphics_objects; - VisualShaderConnectionGraphicsObject* one_free_end_connection; + VisualShaderConnectionGraphicsObject* temporary_connection_graphics_object; }; /**********************************************************************/ @@ -323,9 +324,6 @@ private Q_SLOTS: /**********************************************************************/ /**********************************************************************/ -class VisualShaderInputPortGraphicsObject; -class VisualShaderOutputPortGraphicsObject; - class VisualShaderNodeGraphicsObject : public QGraphicsObject { Q_OBJECT @@ -336,9 +334,6 @@ class VisualShaderNodeGraphicsObject : public QGraphicsObject { VisualShaderInputPortGraphicsObject* get_input_port_graphics_object(const int& p_index) const; VisualShaderOutputPortGraphicsObject* get_output_port_graphics_object(const int& p_index) const; - QPointF find_input_port_coordinate(const int& p_index) const; - QPointF find_output_port_coordinate(const int& p_index) const; - private: VisualShader* vs; int n_id; @@ -391,29 +386,29 @@ class VisualShaderInputPortGraphicsObject : public QGraphicsObject { QGraphicsItem* parent = nullptr); ~VisualShaderInputPortGraphicsObject(); + QPointF get_global_coordinate() const { return mapToScene(rect.center()); } + int get_node_id() const { return n_id; } int get_port_index() const { return p_index; } - QPointF get_coordinate() const { return rect.center(); } - void set_coordinate(const QPointF& coordinate) const { rect.moveCenter(coordinate); } - bool is_connected() const { return end_graphics_object != nullptr; } - VisualShaderConnectionEndGraphicsObject* get_end_graphics_object() const { return end_graphics_object; } - void set_end_graphics_object(VisualShaderConnectionEndGraphicsObject* end_graphics_object) const { this->end_graphics_object = end_graphics_object; } + VisualShaderConnectionGraphicsObject* get_connection_graphics_object() const { return connection_graphics_object; } + void connect(VisualShaderConnectionGraphicsObject* c_g_o) const { this->connection_graphics_object = c_g_o; } + void detach_connection() const { this->connection_graphics_object = nullptr; } + bool is_connected() const { return connection_graphics_object != nullptr; } Q_SIGNALS: + void port_pressed(VisualShaderInputPortGraphicsObject* port, const QPointF& pos); void port_dragged(VisualShaderInputPortGraphicsObject* port, const QPointF& pos); void port_dropped(VisualShaderInputPortGraphicsObject* port, const QPointF& pos); private: int n_id; int p_index; - mutable QRectF rect; + QRectF rect; - float padding = 0.5f; - - mutable VisualShaderConnectionEndGraphicsObject* end_graphics_object; + mutable VisualShaderConnectionGraphicsObject* connection_graphics_object; - float is_dragging = false; + float padding = 0.5f; // Style QColor font_color = QColor(255, 255, 255); @@ -438,29 +433,29 @@ class VisualShaderOutputPortGraphicsObject : public QGraphicsObject { QGraphicsItem* parent = nullptr); ~VisualShaderOutputPortGraphicsObject(); + QPointF get_global_coordinate() const { return mapToScene(rect.center()); } + int get_node_id() const { return n_id; } int get_port_index() const { return p_index; } - QPointF get_coordinate() const { return rect.center(); } - void set_coordinate(const QPointF& coordinate) const { rect.moveCenter(coordinate); } - bool is_connected() const { return start_graphics_object != nullptr; } - VisualShaderConnectionStartGraphicsObject* get_start_graphics_object() const { return start_graphics_object; } - void set_start_graphics_object(VisualShaderConnectionStartGraphicsObject* start_graphics_object) const { this->start_graphics_object = start_graphics_object; } + VisualShaderConnectionGraphicsObject* get_connection_graphics_object() const { return connection_graphics_object; } + void connect(VisualShaderConnectionGraphicsObject* c_o) const { this->connection_graphics_object = c_o; } + void detach_connection() const { this->connection_graphics_object = nullptr; } + bool is_connected() const { return connection_graphics_object != nullptr; } Q_SIGNALS: + void port_pressed(VisualShaderOutputPortGraphicsObject* port, const QPointF& pos); void port_dragged(VisualShaderOutputPortGraphicsObject* port, const QPointF& pos); void port_dropped(VisualShaderOutputPortGraphicsObject* port, const QPointF& pos); private: int n_id; int p_index; - mutable QRectF rect; + QRectF rect; - float padding = 0.5f; - - mutable VisualShaderConnectionStartGraphicsObject* start_graphics_object; + mutable VisualShaderConnectionGraphicsObject* connection_graphics_object; - float is_dragging = false; + float padding = 0.5f; // Style QColor font_color = QColor(255, 255, 255); @@ -485,9 +480,6 @@ class VisualShaderOutputPortGraphicsObject : public QGraphicsObject { /**********************************************************************/ /**********************************************************************/ -class VisualShaderConnectionStartGraphicsObject; -class VisualShaderConnectionEndGraphicsObject; - class VisualShaderConnectionGraphicsObject : public QGraphicsObject { Q_OBJECT @@ -501,15 +493,14 @@ class VisualShaderConnectionGraphicsObject : public QGraphicsObject { int get_from_node_id() const { return from_n_id; } int get_from_port_index() const { return from_p_index; } + int get_to_node_id() const { return to_n_id; } + int get_to_port_index() const { return to_p_index; } + void set_to_node_id(const int& to_n_id) const { this->to_n_id = to_n_id; } void set_to_port_index(const int& to_p_index) const { this->to_p_index = to_p_index; } - - QPointF get_start_coordinate() const { return start_coordinate; } - void set_end_coordinate(const QPointF& end_coordinate) const { this->end_coordinate = end_coordinate; } - - VisualShaderConnectionStartGraphicsObject* get_start_graphics_object() const { return start_graphics_object; } - VisualShaderConnectionEndGraphicsObject* get_end_graphics_object() const { return end_graphics_object; } + void set_start_coordinate(const QPointF& start_coordinate) { this->start_coordinate = start_coordinate; update(); } + void set_end_coordinate(const QPointF& end_coordinate) { this->end_coordinate = end_coordinate; update(); } private: int from_n_id; @@ -518,16 +509,14 @@ class VisualShaderConnectionGraphicsObject : public QGraphicsObject { mutable int to_p_index; QPointF start_coordinate; - mutable QPointF end_coordinate; - - VisualShaderConnectionStartGraphicsObject* start_graphics_object; - VisualShaderConnectionEndGraphicsObject* end_graphics_object; + QPointF end_coordinate; // Style QColor construction_color = QColor(169, 169, 169); QColor normal_color = QColor(0, 255, 255); QColor selected_color = QColor(100, 100, 100); QColor selected_halo_color = QColor(255, 165, 0); + QColor connection_point_color = QColor(169, 169, 169); float line_width = 3.0f; float construction_line_width = 2.0f; @@ -547,54 +536,6 @@ class VisualShaderConnectionGraphicsObject : public QGraphicsObject { std::pair calculate_control_points(const QPointF& start_coordinate, const QPointF& end_coordinate) const; }; -class VisualShaderConnectionStartGraphicsObject : public QGraphicsObject { -public: - VisualShaderConnectionStartGraphicsObject(const QRectF& rect, QGraphicsItem* parent = nullptr); - ~VisualShaderConnectionStartGraphicsObject(); - - int get_node_id() const { return n_id; } - int get_port_index() const { return p_index; } - - void set_coordinate(const QPointF& coordinate) const { rect.moveCenter(coordinate); } - -private: - int n_id; - int p_index; // This is an output port index - - mutable QRectF rect; - - // Style - QColor connection_point_color = QColor(169, 169, 169); - float opacity = 1.0f; - - QRectF boundingRect() const override; - void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override; -}; - -class VisualShaderConnectionEndGraphicsObject : public QGraphicsObject { -public: - VisualShaderConnectionEndGraphicsObject(const QRectF& rect, QGraphicsItem* parent = nullptr); - ~VisualShaderConnectionEndGraphicsObject(); - - int get_node_id() const { return n_id; } - int get_port_index() const { return p_index; } - - void set_coordinate(const QPointF& coordinate) const { rect.moveCenter(coordinate); } - -private: - int n_id; - int p_index; // This is an input port index - - mutable QRectF rect; - - // Style - QColor connection_point_color = QColor(169, 169, 169); - float opacity = 1.0f; - - QRectF boundingRect() const override; - void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override; -}; - /**********************************************************************/ /**********************************************************************/ /**********************************************************************/ From 78cbc4215cfc0a4b4df915378ef5b350d3ec0a29 Mon Sep 17 00:00:00 2001 From: Saif Kandil <74428638+k0T0z@users.noreply.github.com> Date: Tue, 17 Sep 2024 23:06:06 +0300 Subject: [PATCH 24/56] fixed a crash due to unset pointer --- Editors/VisualShaderEditor.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Editors/VisualShaderEditor.cpp b/Editors/VisualShaderEditor.cpp index de82ac174..6f8c7bcf1 100644 --- a/Editors/VisualShaderEditor.cpp +++ b/Editors/VisualShaderEditor.cpp @@ -690,6 +690,8 @@ void VisualShaderGraphicsScene::on_port_dragged(QGraphicsObject* port, const QPo c_o->set_to_port_index(VisualShader::PORT_INDEX_INVALID); } else if (!i_port->is_connected() && temporary_connection_graphics_object) { c_o = temporary_connection_graphics_object; + } else { + return; } c_o->set_end_coordinate(coordinate); @@ -720,6 +722,8 @@ void VisualShaderGraphicsScene::on_port_dragged(QGraphicsObject* port, const QPo i_port->detach_connection(); c_o->set_to_node_id(VisualShader::NODE_ID_INVALID); c_o->set_to_port_index(VisualShader::PORT_INDEX_INVALID); + } else { + return; } c_o->set_end_coordinate(coordinate); From 4429aa9b8d184144560a496f7e2e187cf8f7a8bb Mon Sep 17 00:00:00 2001 From: Saif Kandil <74428638+k0T0z@users.noreply.github.com> Date: Thu, 19 Sep 2024 09:43:16 +0300 Subject: [PATCH 25/56] Bug fixes and few improvements --- CMakeLists.txt | 5 + Editors/VisualShaderEditor.cpp | 424 ++++++++++++++++++++++++--------- Editors/VisualShaderEditor.h | 35 ++- Tests/CMakeLists.txt | 27 +++ 4 files changed, 372 insertions(+), 119 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0d4a1ae70..e05fe41d0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,6 +7,7 @@ project(RadialGM) include(CMakeDependentOption) option(RGM_BUILD_EMAKE "Build Emake and the compiler." ON) +option(RGM_BUILD_TESTS "Build tests." OFF) # FIXME: MSVC dynamic linking requires US TO DLLEXPORT our funcs # since we currently don't, I'm force disabling the option on MSVC @@ -348,6 +349,10 @@ target_link_libraries(${EXE} PRIVATE ${LIB_DOUBLE_CONVERSION}) add_subdirectory(Submodules/nodeeditor) target_link_libraries(${EXE} PRIVATE QtNodes::QtNodes) +if(RGM_BUILD_TESTS) + add_subdirectory(Tests) +endif() + if(WIN32) # Windows is a turd target_link_libraries(${EXE} PRIVATE Ws2_32 Wtsapi32 Wldap32 Crypt32 Winmm Userenv Netapi32 version Dwmapi Imm32) diff --git a/Editors/VisualShaderEditor.cpp b/Editors/VisualShaderEditor.cpp index 6f8c7bcf1..0ff29f1ee 100644 --- a/Editors/VisualShaderEditor.cpp +++ b/Editors/VisualShaderEditor.cpp @@ -25,7 +25,7 @@ ** ** \********************************************************************************/ -#include "VisualShaderEditor.h" +#include "Editors/VisualShaderEditor.h" #include #include @@ -350,7 +350,13 @@ void VisualShaderEditor::add_node(QTreeWidgetItem* selected_item, const QPointF& return; } - scene->add_node(n, coordinate); + int n_id {visual_shader->get_valid_node_id()}; + + if (n_id == (int)VisualShader::NODE_ID_INVALID) { + return; + } + + scene->add_node(n_id, n, coordinate); } void VisualShaderEditor::show_create_node_dialog(const QPointF& coordinate) { @@ -548,22 +554,12 @@ VisualShaderGraphicsScene::VisualShaderGraphicsScene(VisualShader* vs, QObject* temporary_connection_graphics_object(nullptr) { setItemIndexMethod(QGraphicsScene::NoIndex); // https://doc.qt.io/qt-6/qgraphicsscene.html#ItemIndexMethod-enum - // Populate the scene with nodes from the VisualShader - QObject::connect(this, &VisualShaderGraphicsScene::node_moved, this, &VisualShaderGraphicsScene::on_node_moved); } -VisualShaderGraphicsScene::~VisualShaderGraphicsScene() { - -} - -bool VisualShaderGraphicsScene::add_node(const std::shared_ptr& node, const QPointF& coordinate) { - int n_id {vs->get_valid_node_id()}; - - if (n_id == VisualShader::NODE_ID_INVALID) { - return false; - } +VisualShaderGraphicsScene::~VisualShaderGraphicsScene() {} +bool VisualShaderGraphicsScene::add_node(const int& n_id, const std::shared_ptr& node, const QPointF& coordinate) { // Make sure the node doesn't already exist, we don't want to overwrite a node. if (node_graphics_objects.find(n_id) != node_graphics_objects.end()) { return false; @@ -575,10 +571,13 @@ bool VisualShaderGraphicsScene::add_node(const std::shared_ptr return false; } - bool result {vs->add_node(node, {(float)coordinate.x(), (float)coordinate.y()}, n_id)}; + // The output node cannot be removed or added by the user + if (n_id >= VisualShader::NODE_ID_OUTPUT + 1) { + bool result {vs->add_node(node, {(float)coordinate.x(), (float)coordinate.y()}, n_id)}; - if (!result) { - return false; + if (!result) { + return false; + } } VisualShaderGraphicsView* view {dynamic_cast(views.first())}; @@ -599,16 +598,234 @@ bool VisualShaderGraphicsScene::add_node(const std::shared_ptr return true; } -bool VisualShaderGraphicsScene::delete_node() { - return false; +bool VisualShaderGraphicsScene::delete_node(const int& n_id) { + bool result {vs->remove_node(n_id)}; + + if (!result) { + return false; + } + + const std::shared_ptr n {vs->get_node(n_id)}; + + if (!n) { + return false; + } + + VisualShaderNodeGraphicsObject* n_o {this->get_node_graphics_object(n_id)}; + + if (!n_o) { + return false; + } + + // Remove all connections to the node + for (int i{0}; i < n->get_input_port_count(); i++) { + VisualShaderInputPortGraphicsObject* i_port {n_o->get_input_port_graphics_object(i)}; + + if (!i_port || !i_port->is_connected()) { + continue; + } + + // Get the output port of the connection + VisualShaderConnectionGraphicsObject* c_o {i_port->get_connection_graphics_object()}; + + if (!c_o) { + continue; + } + + VisualShaderNodeGraphicsObject* from_n_o {this->get_node_graphics_object(c_o->get_from_node_id())}; + + if (!from_n_o) { + continue; + } + + VisualShaderOutputPortGraphicsObject* o_port {from_n_o->get_output_port_graphics_object(c_o->get_from_port_index())}; + + if (!o_port) { + continue; + } + + bool result {vs->disconnect_nodes(c_o->get_from_node_id(), c_o->get_from_port_index(), n_id, i)}; + + if (!result) { + std::cout << "Failed to disconnect nodes" << std::endl; + continue; + } + + result = this->delete_connection(c_o->get_from_node_id(), c_o->get_from_port_index(), n_id, i); + + if (!result) { + std::cout << "Failed to delete connection" << std::endl; + continue; + } + } + + for (int i{0}; i < n->get_output_port_count(); i++) { + VisualShaderOutputPortGraphicsObject* o_port {n_o->get_output_port_graphics_object(i)}; + + if (!o_port || !o_port->is_connected()) { + continue; + } + + // Get the input port of the connection + VisualShaderConnectionGraphicsObject* c_o {o_port->get_connection_graphics_object()}; + + if (!c_o) { + continue; + } + + VisualShaderNodeGraphicsObject* to_n_o {this->get_node_graphics_object(c_o->get_to_node_id())}; + + if (!to_n_o) { + continue; + } + + VisualShaderInputPortGraphicsObject* i_port {to_n_o->get_input_port_graphics_object(c_o->get_to_port_index())}; + + if (!i_port) { + continue; + } + + bool result {vs->disconnect_nodes(n_id, i, c_o->get_to_node_id(), c_o->get_to_port_index())}; + + if (!result) { + std::cout << "Failed to disconnect nodes" << std::endl; + continue; + } + + result = this->delete_connection(c_o->get_from_node_id(), c_o->get_from_port_index(), n_id, i); + + if (!result) { + std::cout << "Failed to delete connection" << std::endl; + continue; + } + } + + // Remove the node from the scene + removeItem(n_o); + delete n_o; // Make sure to delete the node object to delete all child ports + + return true; } -bool VisualShaderGraphicsScene::add_connection() { - return false; +bool VisualShaderGraphicsScene::add_connection(const int& from_node_id, const int& from_port_index, const int& to_node_id, const int& to_port_index) { + QList views {this->views()}; + if (views.isEmpty()) { + std::cout << "No views available" << std::endl; + return false; + } + + // Create the connection and set its start + VisualShaderNodeGraphicsObject* from_n_o {this->get_node_graphics_object(from_node_id)}; + + if (!from_n_o) { + return false; + } + + VisualShaderOutputPortGraphicsObject* from_o_port {from_n_o->get_output_port_graphics_object(from_port_index)}; + + if (!from_o_port) { + return false; + } + + VisualShaderGraphicsView* view {dynamic_cast(views.first())}; + + if (from_o_port->get_global_coordinate().x() < view->get_x() || + from_o_port->get_global_coordinate().x() > view->get_x() + view->get_width()) { + std::cout << "Start of connection is out of view bounds" << std::endl; + } + + this->temporary_connection_graphics_object = new VisualShaderConnectionGraphicsObject(from_node_id, from_port_index, from_o_port->get_global_coordinate()); + from_o_port->connect(this->temporary_connection_graphics_object); + addItem(this->temporary_connection_graphics_object); + + if (to_node_id != (int)VisualShader::NODE_ID_INVALID && to_port_index != (int)VisualShader::PORT_INDEX_INVALID) { + // Set the end of the connection + VisualShaderNodeGraphicsObject* to_n_o {this->get_node_graphics_object(to_node_id)}; + + if (!to_n_o) { + return false; + } + + VisualShaderInputPortGraphicsObject* to_i_port {to_n_o->get_input_port_graphics_object(to_port_index)}; + + if (!to_i_port) { + return false; + } + + if (to_i_port->get_global_coordinate().y() < view->get_y() || + to_i_port->get_global_coordinate().y() > view->get_y() + view->get_height()) { + std::cout << "End of connection is out of view bounds" << std::endl; + } + + this->temporary_connection_graphics_object->set_end_coordinate(to_i_port->get_global_coordinate()); + to_i_port->connect(this->temporary_connection_graphics_object); + this->temporary_connection_graphics_object->set_to_node_id(to_node_id); + this->temporary_connection_graphics_object->set_to_port_index(to_port_index); + this->temporary_connection_graphics_object = nullptr; // Make sure to reset the temporary connection object + + // Connect the nodes in the VisualShader + bool result {vs->can_connect_nodes(from_node_id, from_port_index, to_node_id, to_port_index)}; + if (!result) { + std::cout << "Can't connect nodes" << std::endl; + return false; + } + + result = vs->connect_nodes(from_node_id, from_port_index, to_node_id, to_port_index); + if (!result) { + std::cout << "Failed to connect nodes" << std::endl; + return false; + } + } + + return true; } -bool VisualShaderGraphicsScene::delete_connection() { - return false; +bool VisualShaderGraphicsScene::delete_connection(const int& from_node_id, const int& from_port_index, const int& to_node_id, const int& to_port_index) { + VisualShaderNodeGraphicsObject* from_n_o {this->get_node_graphics_object(from_node_id)}; + + if (!from_n_o) { + return false; + } + + VisualShaderOutputPortGraphicsObject* from_o_port {from_n_o->get_output_port_graphics_object(from_port_index)}; + + if (!from_o_port) { + return false; + } + + VisualShaderConnectionGraphicsObject* c_o {from_o_port->get_connection_graphics_object()}; + + if (!c_o) { + return false; + } + + if (to_node_id != (int)VisualShader::NODE_ID_INVALID && to_port_index != (int)VisualShader::PORT_INDEX_INVALID) { + VisualShaderNodeGraphicsObject* to_n_o {this->get_node_graphics_object(to_node_id)}; + + if (!to_n_o) { + return false; + } + + VisualShaderInputPortGraphicsObject* to_i_port {to_n_o->get_input_port_graphics_object(to_port_index)}; + + if (!to_i_port) { + return false; + } + + bool result {vs->disconnect_nodes(from_node_id, from_port_index, to_node_id, to_port_index)}; + + if (!result) { + return false; + } + + to_i_port->detach_connection(); + } + + from_o_port->detach_connection(); + removeItem(c_o); + delete c_o; + + return true; } VisualShaderNodeGraphicsObject* VisualShaderGraphicsScene::get_node_graphics_object(const int& n_id) const { @@ -685,9 +902,12 @@ void VisualShaderGraphicsScene::on_port_dragged(QGraphicsObject* port, const QPo if (i_port->is_connected() && !temporary_connection_graphics_object) { c_o = i_port->get_connection_graphics_object(); temporary_connection_graphics_object = c_o; // Store the connection object for access in the next drag call + bool result {vs->disconnect_nodes(c_o->get_from_node_id(), c_o->get_from_port_index(), c_o->get_to_node_id(), c_o->get_to_port_index())}; + if (!result) { + std::cout << "Failed to disconnect nodes" << std::endl; + } i_port->detach_connection(); - c_o->set_to_node_id(VisualShader::NODE_ID_INVALID); - c_o->set_to_port_index(VisualShader::PORT_INDEX_INVALID); + c_o->detach_end(); } else if (!i_port->is_connected() && temporary_connection_graphics_object) { c_o = temporary_connection_graphics_object; } else { @@ -700,10 +920,12 @@ void VisualShaderGraphicsScene::on_port_dragged(QGraphicsObject* port, const QPo } if (!o_port->is_connected() && !temporary_connection_graphics_object) { - c_o = new VisualShaderConnectionGraphicsObject(o_port->get_node_id(), o_port->get_port_index(), o_port->get_global_coordinate()); - temporary_connection_graphics_object = c_o; // Store the connection object for access in the next drag call - o_port->connect(c_o); - addItem(c_o); + bool result {this->add_connection(o_port->get_node_id(), o_port->get_port_index())}; + if (!result) { + std::cout << "Failed to add connection" << std::endl; + return; + } + c_o = temporary_connection_graphics_object; } else if (o_port->is_connected() && temporary_connection_graphics_object) { c_o = temporary_connection_graphics_object; } else if (o_port->is_connected() && !temporary_connection_graphics_object) { @@ -719,9 +941,12 @@ void VisualShaderGraphicsScene::on_port_dragged(QGraphicsObject* port, const QPo if (!i_port) { return; } + bool result {vs->disconnect_nodes(c_o->get_from_node_id(), c_o->get_from_port_index(), c_o->get_to_node_id(), c_o->get_to_port_index())}; + if (!result) { + std::cout << "Failed to disconnect nodes" << std::endl; + } i_port->detach_connection(); - c_o->set_to_node_id(VisualShader::NODE_ID_INVALID); - c_o->set_to_port_index(VisualShader::PORT_INDEX_INVALID); + c_o->detach_end(); } else { return; } @@ -742,13 +967,11 @@ void VisualShaderGraphicsScene::on_port_dropped(QGraphicsObject* port, const QPo // Iterate through the items and check if an input port is under the mouse VisualShaderInputPortGraphicsObject* in_p_o {nullptr}; - VisualShaderOutputPortGraphicsObject* out_p_o {nullptr}; for (QGraphicsItem* item : items_at_coordinate) { // Check if the item is an input port in_p_o = dynamic_cast(item); - out_p_o = dynamic_cast(item); - if (in_p_o || out_p_o) { + if (in_p_o) { break; } } @@ -762,83 +985,39 @@ void VisualShaderGraphicsScene::on_port_dropped(QGraphicsObject* port, const QPo return; } - if (!in_p_o && !out_p_o) { - // Get the output port of the connection object and detach it - VisualShaderNodeGraphicsObject* n_o {this->get_node_graphics_object(c_o->get_from_node_id())}; - if (!n_o) { - return; - } - VisualShaderOutputPortGraphicsObject* o_port {n_o->get_output_port_graphics_object(c_o->get_from_port_index())}; - if (!o_port) { - return; - } - - o_port->detach_connection(); + if (!in_p_o) { + bool result {this->delete_connection(c_o->get_from_node_id(), c_o->get_from_port_index())}; - // Delete the connection - this->removeItem(c_o); - delete c_o; - return; // Return because we dragging an input port and dropped on nothing - } else if (out_p_o) { - // Get the output port of the connection object and detach it - VisualShaderNodeGraphicsObject* n_o {this->get_node_graphics_object(c_o->get_from_node_id())}; - if (!n_o) { - return; + if (!result) { + std::cout << "Failed to delete connection" << std::endl; } - VisualShaderOutputPortGraphicsObject* o_port {n_o->get_output_port_graphics_object(c_o->get_from_port_index())}; - if (!o_port) { - return; - } - - o_port->detach_connection(); - // Delete the connection - this->removeItem(c_o); - delete c_o; - return; // Return because we dragging an input port and dropped on an output port + return; // Return because we dragging an input port and dropped on nothing } bool result {vs->can_connect_nodes(c_o->get_from_node_id(), c_o->get_from_port_index(), in_p_o->get_node_id(), in_p_o->get_port_index())}; if (!result) { - // Get the output port of the connection object and detach it - VisualShaderNodeGraphicsObject* n_o {this->get_node_graphics_object(c_o->get_from_node_id())}; - if (!n_o) { - return; - } - VisualShaderOutputPortGraphicsObject* o_port {n_o->get_output_port_graphics_object(c_o->get_from_port_index())}; - if (!o_port) { - return; + bool result {this->delete_connection(c_o->get_from_node_id(), c_o->get_from_port_index())}; + + if (!result) { + std::cout << "Failed to delete connection" << std::endl; } - - o_port->detach_connection(); - // Delete the connection - this->removeItem(c_o); - delete c_o; - return; + return; // Return because we dragging an input port and dropped on nothing } // Connect the nodes result = vs->connect_nodes(c_o->get_from_node_id(), c_o->get_from_port_index(), in_p_o->get_node_id(), in_p_o->get_port_index()); if (!result) { - // Get the output port of the connection object and detach it - VisualShaderNodeGraphicsObject* n_o {this->get_node_graphics_object(c_o->get_from_node_id())}; - if (!n_o) { - return; - } - VisualShaderOutputPortGraphicsObject* o_port {n_o->get_output_port_graphics_object(c_o->get_from_port_index())}; - if (!o_port) { - return; + bool result {this->delete_connection(c_o->get_from_node_id(), c_o->get_from_port_index())}; + + if (!result) { + std::cout << "Failed to delete connection" << std::endl; } - - o_port->detach_connection(); - // Delete the connection - this->removeItem(c_o); - delete c_o; - return; + return; // Return because we dragging an input port and dropped on nothing } c_o->set_to_node_id(in_p_o->get_node_id()); @@ -848,30 +1027,25 @@ void VisualShaderGraphicsScene::on_port_dropped(QGraphicsObject* port, const QPo return; } - if (!in_p_o && !out_p_o) { - o_port->detach_connection(); + if (!in_p_o) { + bool result {this->delete_connection(c_o->get_from_node_id(), c_o->get_from_port_index())}; - // Delete the connection - this->removeItem(c_o); - delete c_o; - return; // Return because we dragging an input port and dropped on nothing - } else if (out_p_o) { - o_port->detach_connection(); + if (!result) { + std::cout << "Failed to delete connection" << std::endl; + } - // Delete the connection - this->removeItem(c_o); - delete c_o; - return; // Return because we dragging an input port and dropped on an output port + return; // Return because we dragging an output port and dropped on nothing } bool result {vs->can_connect_nodes(c_o->get_from_node_id(), c_o->get_from_port_index(), in_p_o->get_node_id(), in_p_o->get_port_index())}; if (!result) { - o_port->detach_connection(); + bool result {this->delete_connection(c_o->get_from_node_id(), c_o->get_from_port_index())}; + + if (!result) { + std::cout << "Failed to delete connection" << std::endl; + } - // Delete the connection - this->removeItem(c_o); - delete c_o; return; } @@ -879,11 +1053,12 @@ void VisualShaderGraphicsScene::on_port_dropped(QGraphicsObject* port, const QPo result = vs->connect_nodes(c_o->get_from_node_id(), c_o->get_from_port_index(), in_p_o->get_node_id(), in_p_o->get_port_index()); if (!result) { - o_port->detach_connection(); + bool result {this->delete_connection(c_o->get_from_node_id(), c_o->get_from_port_index())}; + + if (!result) { + std::cout << "Failed to delete connection" << std::endl; + } - // Delete the connection - this->removeItem(c_o); - delete c_o; return; } @@ -963,6 +1138,27 @@ VisualShaderGraphicsView::VisualShaderGraphicsView(VisualShaderGraphicsScene *sc zoom_out_action->setShortcut(QKeySequence(QKeySequence::ZoomOut)); QObject::connect(zoom_out_action, &QAction::triggered, this, &VisualShaderGraphicsView::zoom_out); context_menu->addAction(zoom_out_action); + + VisualShader* vs {scene->get_visual_shader()}; + + // Load the nodes and connections from the VisualShader + std::vector ns {vs->get_nodes()}; + for (const int& n_id : ns) { + const std::shared_ptr n {vs->get_node(n_id)}; + + if (!n) { + continue; + } + + TVector2 c {vs->get_node_coordinate(n_id)}; + + scene->add_node(n_id, n, {c.x, c.y}); + } + + std::vector cs {vs->get_connections()}; + for (const VisualShader::Connection& c : cs) { + scene->add_connection(c.from_node, c.from_port, c.to_node, c.to_port); + } } VisualShaderGraphicsView::~VisualShaderGraphicsView() { @@ -1290,7 +1486,7 @@ void VisualShaderNodeGraphicsObject::paint(QPainter *painter, const QStyleOption int n_id {vs->find_node_id(n)}; - if (n_id == VisualShader::NODE_ID_INVALID) { + if (n_id == (int)VisualShader::NODE_ID_INVALID) { return; } diff --git a/Editors/VisualShaderEditor.h b/Editors/VisualShaderEditor.h index 06e5050ef..0809dcebc 100644 --- a/Editors/VisualShaderEditor.h +++ b/Editors/VisualShaderEditor.h @@ -50,7 +50,7 @@ #include #include "ResourceTransformations/VisualShader/visual_shader.h" -#include "BaseEditor.h" +#include "Editors/BaseEditor.h" class VisualShaderGraphicsScene; class VisualShaderGraphicsView; @@ -201,11 +201,34 @@ class VisualShaderGraphicsScene : public QGraphicsScene { ~VisualShaderGraphicsScene(); - bool add_node(const std::shared_ptr& node, const QPointF& coordinate); - bool delete_node(); + bool add_node(const int& n_id, const std::shared_ptr& node, const QPointF& coordinate); + bool delete_node(const int& n_id); - bool add_connection(); - bool delete_connection(); + /** + * @brief + * + * @note This function sets the @c temporary_connection_graphics_object if + * we have a valid @c from_node_id and @c from_port_index only. Then it + * resets it again if we have a valid @c to_node_id and @c to_port_index and + * this is important because inside the drag and drop event, we need to know + * if we have a valid temporary connection or not. + * + * @param from_node_id + * @param from_port_index + * @param to_node_id + * @param to_port_index + * @return true + * @return false + */ + bool add_connection(const int& from_node_id, + const int& from_port_index, + const int& to_node_id = (int)VisualShader::NODE_ID_INVALID, + const int& to_port_index = (int)VisualShader::PORT_INDEX_INVALID); + + bool delete_connection(const int& from_node_id, + const int& from_port_index, + const int& to_node_id = (int)VisualShader::NODE_ID_INVALID, + const int& to_port_index = (int)VisualShader::PORT_INDEX_INVALID); VisualShaderNodeGraphicsObject* get_node_graphics_object(const int& n_id) const; @@ -496,6 +519,8 @@ class VisualShaderConnectionGraphicsObject : public QGraphicsObject { int get_to_node_id() const { return to_n_id; } int get_to_port_index() const { return to_p_index; } + void detach_end() const { this->set_to_node_id((int)VisualShader::NODE_ID_INVALID); this->set_to_port_index((int)VisualShader::PORT_INDEX_INVALID); } + void set_to_node_id(const int& to_n_id) const { this->to_n_id = to_n_id; } void set_to_port_index(const int& to_p_index) const { this->to_p_index = to_p_index; } diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt index e69de29bb..5775c6448 100644 --- a/Tests/CMakeLists.txt +++ b/Tests/CMakeLists.txt @@ -0,0 +1,27 @@ +set(RGM_TESTS "RGM-Tests" CACHE STRING "RGM Tests") + +project(${RGM_TESTS} LANGUAGES CXX) + +file(GLOB RGM_TESTS_SRC_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/Editors/VisualShaderEditorTests.cpp +) + +set(CMAKE_INCLUDE_CURRENT_DIR ON) + +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTOUIC ON) +set(CMAKE_AUTORCC ON) + +enable_testing() + +add_executable(${RGM_TESTS} ${RGM_TESTS_SRC_FILES}) + +find_package(Qt5Test REQUIRED) +target_link_libraries(${RGM_TESTS} PRIVATE Qt5::Test) + +find_package(Qt5 COMPONENTS Core Widgets Gui PrintSupport Multimedia REQUIRED) +target_link_libraries(${RGM_TESTS} PRIVATE Qt5::Core Qt5::Widgets Qt5::Gui Qt5::PrintSupport Qt5::Multimedia) + +target_link_libraries(${RGM_TESTS} PRIVATE ${LIB_PROTO} ${SHARED_LIB}) + +add_test(NAME ${RGM_TESTS} COMMAND ${RGM_TESTS}) From f33b2adcf965402b5ae51b9e8babd15fa2db6ccc Mon Sep 17 00:00:00 2001 From: Saif Kandil <74428638+k0T0z@users.noreply.github.com> Date: Thu, 19 Sep 2024 15:32:12 +0300 Subject: [PATCH 26/56] Fixed clang-format double key --- .clang-format | 1 - 1 file changed, 1 deletion(-) diff --git a/.clang-format b/.clang-format index 4897bcf18..ee0c924b3 100644 --- a/.clang-format +++ b/.clang-format @@ -18,7 +18,6 @@ AlwaysBreakAfterDefinitionReturnType: None AlwaysBreakAfterReturnType: None AlwaysBreakBeforeMultilineStrings: true AlwaysBreakTemplateDeclarations: true -AllowShortCaseLabelsOnASingleLine: true BinPackArguments: true BinPackParameters: true BraceWrapping: From f78d473afe1949832e595561d67d6d1045416b09 Mon Sep 17 00:00:00 2001 From: Saif Kandil <74428638+k0T0z@users.noreply.github.com> Date: Fri, 20 Sep 2024 23:50:30 +0300 Subject: [PATCH 27/56] Added RGM-Tests target, improved the VisualShaderEditor, and fixed many bugs --- CMakeLists.txt | 29 +- Dialogs/PreferencesDialog.cpp | 1 - Dialogs/PreferencesKeys.cpp | 30 + Dialogs/PreferencesKeys.h | 5 + Editors/BaseEditor.cpp | 2 + Editors/BaseEditor.h | 1 + Editors/VisualShaderEditor.cpp | 3232 +++++++++++---------- Editors/VisualShaderEditor.h | 788 +++-- Tests/CMakeLists.txt | 208 +- Tests/Editors/VisualShaderEditorTests.cpp | 72 +- Tests/Editors/VisualShaderEditorTests.h | 49 + Tests/MainWindowTests.cpp | 46 + Tests/MainWindowTests.h | 49 + Tests/tests_main.cpp | 49 + main.cpp | 3 - main.h | 11 - 16 files changed, 2604 insertions(+), 1971 deletions(-) create mode 100644 Dialogs/PreferencesKeys.cpp create mode 100644 Tests/Editors/VisualShaderEditorTests.h create mode 100644 Tests/MainWindowTests.cpp create mode 100644 Tests/MainWindowTests.h create mode 100644 Tests/tests_main.cpp delete mode 100644 main.h diff --git a/CMakeLists.txt b/CMakeLists.txt index e05fe41d0..ba2ecc5ca 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,3 +1,30 @@ +################################################################################### +## ## +## Copyright (C) 2024 Saif Kandil (k0T0z) ## +## ## +## This file is a part of the ENIGMA Development Environment. ## +## ## +## ## +## ENIGMA is free software: you can redistribute it and/or modify it under the ## +## terms of the GNU General Public License as published by the Free Software ## +## Foundation, version 3 of the license or any later version. ## +## ## +## This application and its source code is distributed AS-IS, WITHOUT ANY ## +## WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS ## +## FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## +## details. ## +## ## +## You should have recieved a copy of the GNU General Public License along ## +## with this code. If not, see ## +## ## +## ENIGMA is an environment designed to create games and other programs with a ## +## high-level, fully compilable language. Developers of ENIGMA or anything ## +## associated with ENIGMA are in no way responsible for its users or ## +## applications created by its users, or damages caused by the environment ## +## or programs made in the environment. ## +## ## +################################################################################### + cmake_minimum_required(VERSION 3.14) project(RadialGM) @@ -140,6 +167,7 @@ set(RGM_SOURCES Dialogs/EventArgumentsDialog.cpp Dialogs/TimelineChangeMoment.cpp Dialogs/PreferencesDialog.cpp + Dialogs/PreferencesKeys.cpp Dialogs/KeyBindingPreferences.cpp Utils/ProtoManip.cpp Utils/FieldPath.cpp @@ -171,7 +199,6 @@ set(RGM_HEADERS Models/ModelMapper.h Models/RepeatedSortFilterProxyModel.h Models/TreeSortFilterProxyModel.h - main.h Components/RecentFiles.h Components/QMenuView_p.h Components/Utility.h diff --git a/Dialogs/PreferencesDialog.cpp b/Dialogs/PreferencesDialog.cpp index a3954976b..a25cacec0 100644 --- a/Dialogs/PreferencesDialog.cpp +++ b/Dialogs/PreferencesDialog.cpp @@ -3,7 +3,6 @@ #include "PreferencesKeys.h" #include "KeyBindingPreferences.h" -#include "main.h" #include "Components/Logger.h" #include diff --git a/Dialogs/PreferencesKeys.cpp b/Dialogs/PreferencesKeys.cpp new file mode 100644 index 000000000..f023935d4 --- /dev/null +++ b/Dialogs/PreferencesKeys.cpp @@ -0,0 +1,30 @@ +/*********************************************************************************/ +/* */ +/* Copyright (C) 2024 Saif Kandil (k0T0z) */ +/* */ +/* This file is a part of the ENIGMA Development Environment. */ +/* */ +/* */ +/* ENIGMA is free software: you can redistribute it and/or modify it under the */ +/* terms of the GNU General Public License as published by the Free Software */ +/* Foundation, version 3 of the license or any later version. */ +/* */ +/* This application and its source code is distributed AS-IS, WITHOUT ANY */ +/* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS */ +/* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more */ +/* details. */ +/* */ +/* You should have recieved a copy of the GNU General Public License along */ +/* with this code. If not, see */ +/* */ +/* ENIGMA is an environment designed to create games and other programs with a */ +/* high-level, fully compilable language. Developers of ENIGMA or anything */ +/* associated with ENIGMA are in no way responsible for its users or */ +/* applications created by its users, or damages caused by the environment */ +/* or programs made in the environment. */ +/* */ +/*********************************************************************************/ + +#include "Dialogs/PreferencesKeys.h" + +QString defaultStyle = ""; diff --git a/Dialogs/PreferencesKeys.h b/Dialogs/PreferencesKeys.h index 46e574c97..4ab196514 100644 --- a/Dialogs/PreferencesKeys.h +++ b/Dialogs/PreferencesKeys.h @@ -3,6 +3,11 @@ #include +// Qt doesn't have a way of getting the default style +// so we store it for later when the user restores +// default settings so we can apply it again +extern QString defaultStyle; + // these are settings key helpers to prevent typos and promote // consistency, or at least turn such mistakes into compile-time // errors and not difficult-to-diagnose issues at runtime diff --git a/Editors/BaseEditor.cpp b/Editors/BaseEditor.cpp index a411d3666..1ac76e056 100644 --- a/Editors/BaseEditor.cpp +++ b/Editors/BaseEditor.cpp @@ -5,6 +5,8 @@ #include #include +BaseEditor::BaseEditor(QWidget* parent) : QWidget(parent) {} + BaseEditor::BaseEditor(MessageModel* resource_model, QWidget* parent) : QWidget(parent), _model(resource_model->GetParentModel()), _nodeMapper(new ModelMapper(_model, this)) { _resMapper = new ModelMapper(resource_model, this); diff --git a/Editors/BaseEditor.h b/Editors/BaseEditor.h index 5ee3822d5..7c4165b2d 100644 --- a/Editors/BaseEditor.h +++ b/Editors/BaseEditor.h @@ -20,6 +20,7 @@ class BaseEditor : public QWidget { Q_OBJECT public: + explicit BaseEditor(QWidget *parent = nullptr); explicit BaseEditor(MessageModel *treeNodeModel, QWidget *parent); ~BaseEditor(); void ReplaceBuffer(google::protobuf::Message *buffer); diff --git a/Editors/VisualShaderEditor.cpp b/Editors/VisualShaderEditor.cpp index 0ff29f1ee..39298bbd2 100644 --- a/Editors/VisualShaderEditor.cpp +++ b/Editors/VisualShaderEditor.cpp @@ -1,29 +1,29 @@ -/********************************************************************************\ - ** ** - ** Copyright (C) 2024 Saif Kandil (k0T0z) ** - ** ** - ** This file is a part of the ENIGMA Development Environment. ** - ** ** - ** ** - ** ENIGMA is free software: you can redistribute it and/or modify it under the ** - ** terms of the GNU General Public License as published by the Free Software ** - ** Foundation, version 3 of the license or any later version. ** - ** ** - ** This application and its source code is distributed AS-IS, WITHOUT ANY ** - ** WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS ** - ** FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ** - ** details. ** - ** ** - ** You should have recieved a copy of the GNU General Public License along ** - ** with this code. If not, see ** - ** ** - ** ENIGMA is an environment designed to create games and other programs with a ** - ** high-level, fully compilable language. Developers of ENIGMA or anything ** - ** associated with ENIGMA are in no way responsible for its users or ** - ** applications created by its users, or damages caused by the environment ** - ** or programs made in the environment. ** - ** ** - \********************************************************************************/ +/*********************************************************************************/ +/* */ +/* Copyright (C) 2024 Saif Kandil (k0T0z) */ +/* */ +/* This file is a part of the ENIGMA Development Environment. */ +/* */ +/* */ +/* ENIGMA is free software: you can redistribute it and/or modify it under the */ +/* terms of the GNU General Public License as published by the Free Software */ +/* Foundation, version 3 of the license or any later version. */ +/* */ +/* This application and its source code is distributed AS-IS, WITHOUT ANY */ +/* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS */ +/* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more */ +/* details. */ +/* */ +/* You should have recieved a copy of the GNU General Public License along */ +/* with this code. If not, see */ +/* */ +/* ENIGMA is an environment designed to create games and other programs with a */ +/* high-level, fully compilable language. Developers of ENIGMA or anything */ +/* associated with ENIGMA are in no way responsible for its users or */ +/* applications created by its users, or damages caused by the environment */ +/* or programs made in the environment. */ +/* */ +/*********************************************************************************/ #include "Editors/VisualShaderEditor.h" @@ -34,9 +34,9 @@ #include -#include "VisualShader.pb.h" #include "ResourceTransformations/VisualShader/visual_shader_nodes.h" #include "ResourceTransformations/VisualShader/vs_noise_nodes.h" +#include "VisualShader.pb.h" /**********************************************************************/ /**********************************************************************/ @@ -48,373 +48,384 @@ /**********************************************************************/ /**********************************************************************/ -VisualShaderEditor::VisualShaderEditor(MessageModel* model, QWidget* parent) : BaseEditor(model, parent), - visual_shader(nullptr), - layout(nullptr), - scene_layer_layout(nullptr), - scene_layer(nullptr), - scene(nullptr), - view(nullptr), - top_layer(nullptr), - menu_bar(nullptr), - create_node_button(nullptr), - preview_shader_button(nullptr), - create_node_action(nullptr), - zoom_in_button(nullptr), - reset_zoom_button(nullptr), - zoom_out_button(nullptr), - create_node_dialog(nullptr) { - visual_shader = new VisualShader(); - - // Create the main layout. - layout = new QHBoxLayout(this); - layout->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom - layout->setSizeConstraint(QLayout::SetNoConstraint); - layout->setSpacing(0); - layout->setAlignment(Qt::AlignVCenter | Qt::AlignHCenter); - - //////////////// End of Header //////////////// - - // Create the scene layer. - scene_layer = new QWidget(); - scene_layer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); - scene_layer->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom - - // Create the scene layer layout. - scene_layer_layout = new QHBoxLayout(scene_layer); - scene_layer_layout->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom - scene_layer_layout->setSpacing(0); - scene_layer_layout->setSizeConstraint(QLayout::SetNoConstraint); - scene_layer_layout->setAlignment(Qt::AlignVCenter | Qt::AlignHCenter); - - scene = new VisualShaderGraphicsScene(visual_shader); - - view = new VisualShaderGraphicsView(scene, scene_layer); - view->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom - view->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); - - scene_layer_layout->addWidget(view); - - // // Setup context menu for creating new nodes. - // view->setContextMenuPolicy(Qt::ActionsContextMenu); - // create_node_action = new QAction(QStringLiteral("Create Node"), view); - // QObject::connect(create_node_action, &QAction::triggered, this, &VisualShaderEditor::on_create_node_action_triggered); - // view->insertAction(view->actions().front(), create_node_action); - - // Set the scene layer layout. - scene_layer->setLayout(scene_layer_layout); - - // Create the menu bar layer on top of the scene layer. - top_layer = new QWidget(view); - top_layer->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom - top_layer->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); - - // Create the menu bar layout. - menu_bar = new QHBoxLayout(top_layer); - menu_bar->setContentsMargins(10, 10, 10, 10); // Left, top, right, bottom - menu_bar->setSpacing(5); // Adjust spacing as needed - menu_bar->setAlignment(Qt::AlignTop | Qt::AlignLeft); - menu_bar->setSizeConstraint(QLayout::SetMinimumSize); - - // Create the create node button. - create_node_button = new QPushButton("Create Node", top_layer); - create_node_button->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); - create_node_button->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom - menu_bar->addWidget(create_node_button); - QObject::connect(create_node_button, &QPushButton::pressed, this, &VisualShaderEditor::on_create_node_button_pressed); - - this->connect(this, &VisualShaderEditor::on_create_node_dialog_requested, this, &VisualShaderEditor::show_create_node_dialog); - - // Create the preview shader button. - preview_shader_button = new QPushButton("Preview Shader", top_layer); - preview_shader_button->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); - preview_shader_button->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom - menu_bar->addWidget(preview_shader_button); - - zoom_in_button = new QPushButton("Zoom In", scene_layer); - zoom_in_button->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); - zoom_in_button->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom - menu_bar->addWidget(zoom_in_button); - QObject::connect(zoom_in_button, &QPushButton::pressed, view, &VisualShaderGraphicsView::zoom_in); - - reset_zoom_button = new QPushButton("Reset Zoom", scene_layer); - reset_zoom_button->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); - reset_zoom_button->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom - menu_bar->addWidget(reset_zoom_button); - QObject::connect(reset_zoom_button, &QPushButton::pressed, view, &VisualShaderGraphicsView::reset_zoom); - - zoom_out_button = new QPushButton("Zoom Out", scene_layer); - zoom_out_button->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); - zoom_out_button->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom - menu_bar->addWidget(zoom_out_button); - QObject::connect(zoom_out_button, &QPushButton::pressed, view, &VisualShaderGraphicsView::zoom_out); - - // Set the top layer layout. - top_layer->setLayout(menu_bar); - - // Add the scene layer to the main layout. - layout->addWidget(scene_layer); - - //////////////////////////////////// - // CreateNodeDialog Nodes Tree - //////////////////////////////////// - - // Create the create node dialog under the main layout. - create_node_dialog = new CreateNodeDialog(this); - create_node_dialog->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom - create_node_dialog->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); - - ////////////////////////////////////////// - // CreateNodeDialog Nodes Tree Children - ////////////////////////////////////////// - - const VisualShaderEditor::CreateNodeDialogNodesTreeItem* items {VisualShaderEditor::create_node_dialog_nodes_tree_items}; - - // Map to store category items - std::unordered_map category_path_map; - - int i{0}; - - while (!items[i].type.empty()) { - const CreateNodeDialogNodesTreeItem& item {items[i]}; - - // Parse the category string into a vector of strings - std::vector categories {pasre_node_category_path(item.category_path)}; - QTreeWidgetItem* parent {nullptr}; // Start from the root - - std::string current_category_path; - // Create/find each level of categories - for (const std::string& category : categories) { - if (!current_category_path.empty()) { - current_category_path += "/"; - } - - current_category_path += category; - - parent = find_or_create_category_item(parent, category, current_category_path, create_node_dialog->get_nodes_tree(), category_path_map); - } - - // Now add the item to its corresponding parent category - QTreeWidgetItem *node_item = new QTreeWidgetItem(parent); - node_item->setText(0, QString::fromStdString(item.name)); - node_item->setData(0, Qt::UserRole, QString::fromStdString(item.type)); - node_item->setData(0, Qt::UserRole + 1, QString::fromStdString(item.description)); - - i++; +VisualShaderEditor::VisualShaderEditor(QWidget* parent) : BaseEditor(parent) { + VisualShaderEditor::init(); +} + +VisualShaderEditor::VisualShaderEditor(MessageModel* model, QWidget* parent) + : BaseEditor(model, parent), + visual_shader(nullptr), + layout(nullptr), + scene_layer_layout(nullptr), + scene_layer(nullptr), + scene(nullptr), + view(nullptr), + top_layer(nullptr), + menu_bar(nullptr), + create_node_button(nullptr), + preview_shader_button(nullptr), + create_node_action(nullptr), + zoom_in_button(nullptr), + reset_zoom_button(nullptr), + zoom_out_button(nullptr), + load_image_button(nullptr), + match_image_button(nullptr), + create_node_dialog(nullptr), + code_previewer_dialog(nullptr), + code_previewer_layout(nullptr), + code_previewer(nullptr) { + VisualShaderEditor::init(); +} + +VisualShaderEditor::~VisualShaderEditor() { + if (visual_shader) delete visual_shader; +} + +void VisualShaderEditor::init() { + visual_shader = new VisualShader(); + + // Create the main layout. + layout = new QHBoxLayout(this); + layout->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom + layout->setSizeConstraint(QLayout::SetNoConstraint); + layout->setSpacing(0); + layout->setAlignment(Qt::AlignVCenter | Qt::AlignHCenter); + + //////////////// End of Header //////////////// + + // Create the scene layer. + scene_layer = new QWidget(); + scene_layer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + scene_layer->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom + + // Create the scene layer layout. + scene_layer_layout = new QHBoxLayout(scene_layer); + scene_layer_layout->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom + scene_layer_layout->setSpacing(0); + scene_layer_layout->setSizeConstraint(QLayout::SetNoConstraint); + scene_layer_layout->setAlignment(Qt::AlignVCenter | Qt::AlignHCenter); + + scene = new VisualShaderGraphicsScene(visual_shader); + scene->set_editor(this); + + view = new VisualShaderGraphicsView(scene, scene_layer); + view->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom + view->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + + VisualShaderEditor::init_graph(); // Must be called after a scene and a view are created. + + scene_layer_layout->addWidget(view); + + // Set the scene layer layout. + scene_layer->setLayout(scene_layer_layout); + + // Create the menu bar layer on top of the scene layer. + top_layer = new QWidget(view); + top_layer->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom + top_layer->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + + // Create the menu bar layout. + menu_bar = new QHBoxLayout(top_layer); + menu_bar->setContentsMargins(10, 10, 10, 10); // Left, top, right, bottom + menu_bar->setSpacing(5); // Adjust spacing as needed + menu_bar->setAlignment(Qt::AlignTop | Qt::AlignLeft); + menu_bar->setSizeConstraint(QLayout::SetMinimumSize); + + // Create the create node button. + create_node_button = new QPushButton("Create Node", top_layer); + create_node_button->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + create_node_button->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom + menu_bar->addWidget(create_node_button); + QObject::connect(create_node_button, &QPushButton::pressed, this, &VisualShaderEditor::on_create_node_button_pressed); + + this->connect(this, &VisualShaderEditor::on_create_node_dialog_requested, this, + &VisualShaderEditor::show_create_node_dialog); + + // Create the preview shader button. + preview_shader_button = new QPushButton("Preview Shader", top_layer); + preview_shader_button->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + preview_shader_button->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom + menu_bar->addWidget(preview_shader_button); + QObject::connect(preview_shader_button, &QPushButton::pressed, this, &VisualShaderEditor::on_preview_shader_button_pressed); + + zoom_in_button = new QPushButton("Zoom In", scene_layer); + zoom_in_button->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + zoom_in_button->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom + menu_bar->addWidget(zoom_in_button); + QObject::connect(zoom_in_button, &QPushButton::pressed, view, &VisualShaderGraphicsView::zoom_in); + + reset_zoom_button = new QPushButton("Reset Zoom", scene_layer); + reset_zoom_button->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + reset_zoom_button->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom + menu_bar->addWidget(reset_zoom_button); + QObject::connect(reset_zoom_button, &QPushButton::pressed, view, &VisualShaderGraphicsView::reset_zoom); + + zoom_out_button = new QPushButton("Zoom Out", scene_layer); + zoom_out_button->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + zoom_out_button->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom + menu_bar->addWidget(zoom_out_button); + QObject::connect(zoom_out_button, &QPushButton::pressed, view, &VisualShaderGraphicsView::zoom_out); + + load_image_button = new QPushButton("Load Image", scene_layer); + load_image_button->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + load_image_button->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom + menu_bar->addWidget(load_image_button); + + match_image_button = new QPushButton("Match Image", scene_layer); + match_image_button->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + match_image_button->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom + menu_bar->addWidget(match_image_button); + + // Set the top layer layout. + top_layer->setLayout(menu_bar); + + // Add the scene layer to the main layout. + layout->addWidget(scene_layer); + + //////////////////////////////////// + // Code Previewer + //////////////////////////////////// + + code_previewer_dialog = new QDialog(this); + code_previewer_dialog->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom + code_previewer_dialog->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + + code_previewer_layout = new QVBoxLayout(code_previewer_dialog); + code_previewer_layout->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom + code_previewer_layout->setSpacing(0); // Adjust spacing as needed + code_previewer_layout->setAlignment(Qt::AlignTop | Qt::AlignLeft); + code_previewer_layout->setSizeConstraint(QLayout::SetMinimumSize); + + code_previewer = new QPlainTextEdit(code_previewer_dialog); + code_previewer->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom + code_previewer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + code_previewer->setReadOnly(true); + code_previewer->setLineWrapMode(QPlainTextEdit::NoWrap); + code_previewer->setWordWrapMode(QTextOption::NoWrap); + code_previewer->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); + code_previewer->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + code_previewer->setTabChangesFocus(true); + code_previewer->setMinimumSize(800, 600); + + code_previewer_layout->addWidget(code_previewer); + + code_previewer_dialog->setLayout(code_previewer_layout); + + //////////////////////////////////// + // CreateNodeDialog Nodes Tree + //////////////////////////////////// + + // Create the create node dialog under the main layout. + create_node_dialog = new CreateNodeDialog(this); + create_node_dialog->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom + create_node_dialog->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + + ////////////////////////////////////////// + // CreateNodeDialog Nodes Tree Children + ////////////////////////////////////////// + + const VisualShaderEditor::CreateNodeDialogNodesTreeItem* items{ + VisualShaderEditor::create_node_dialog_nodes_tree_items}; + + // Map to store category items + std::unordered_map category_path_map; + + int i{0}; + + while (!items[i].type.empty()) { + const CreateNodeDialogNodesTreeItem& item{items[i]}; + + // Parse the category string into a vector of strings + std::vector categories{parse_node_category_path(item.category_path)}; + QTreeWidgetItem* parent{nullptr}; // Start from the root + + std::string current_category_path; + // Create/find each level of categories + for (const std::string& category : categories) { + if (!current_category_path.empty()) { + current_category_path += "/"; + } + + current_category_path += category; + + parent = find_or_create_category_item(parent, category, current_category_path, + create_node_dialog->get_nodes_tree(), category_path_map); } - //////////////// Start of Footer //////////////// + // Now add the item to its corresponding parent category + QTreeWidgetItem* node_item = new QTreeWidgetItem(parent); + node_item->setText(0, QString::fromStdString(item.name)); + node_item->setData(0, Qt::UserRole, QString::fromStdString(item.type)); + node_item->setData(0, Qt::UserRole + 1, QString::fromStdString(item.description)); + + i++; + } - this->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom - // this->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + //////////////// Start of Footer //////////////// - // Set the window title and icon. - this->setWindowTitle("Visual Shader Editor"); - // this->setWindowIcon(QIcon(":/resources/visual_shader.png")); - this->setLayout(layout); + this->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom + // this->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + + // Set the window title and icon. + this->setWindowTitle("Visual Shader Editor"); + // this->setWindowIcon(QIcon(":/resources/visual_shader.png")); + this->setLayout(layout); } -VisualShaderEditor::~VisualShaderEditor() { - // TODO: We don't need to delete the pointers as they are destroyed when the parent is destroyed. - if (create_node_dialog) delete create_node_dialog; - if (zoom_out_button) delete zoom_out_button; - if (reset_zoom_button) delete reset_zoom_button; - if (zoom_in_button) delete zoom_in_button; - if (create_node_action) delete create_node_action; - if (preview_shader_button) delete preview_shader_button; - if (create_node_button) delete create_node_button; - if (menu_bar) delete menu_bar; - if (top_layer) delete top_layer; - if (view) delete view; - if (scene) delete scene; - if (scene_layer) delete scene_layer; - if (scene_layer_layout) delete scene_layer_layout; - if (layout) delete layout; - if (visual_shader) delete visual_shader; +void VisualShaderEditor::init_graph() { + // Load the nodes and connections from the VisualShader + std::vector ns{visual_shader->get_nodes()}; + for (const int& n_id : ns) { + const std::shared_ptr n{visual_shader->get_node(n_id)}; + + if (!n) { + continue; + } + + TVector2 c{visual_shader->get_node_coordinate(n_id)}; + + scene->add_node(n_id, n, {c.x, c.y}); + } + + std::vector cs{visual_shader->get_connections()}; + for (const VisualShader::Connection& c : cs) { + scene->add_connection(c.from_node, c.from_port, c.to_node, c.to_port); + } } const VisualShaderEditor::CreateNodeDialogNodesTreeItem VisualShaderEditor::create_node_dialog_nodes_tree_items[] = { - // Input + // Input {"Input", "Input/Basic", "VisualShaderNodeInput", "Input parameter."}, - {"ColorConstant", "Input/Basic", "VisualShaderNodeColorConstant", "Color constant."}, - {"BooleanConstant", "Input/Basic", "VisualShaderNodeBooleanConstant", "Boolean constant."}, - {"FloatConstant", "Input/Basic", "VisualShaderNodeFloatConstant", "Scalar floating-point constant."}, - {"IntConstant", "Input/Basic", "VisualShaderNodeIntConstant", "Scalar integer constant."}, - {"UIntConstant", "Input/Basic", "VisualShaderNodeUIntConstant", "Scalar unsigned integer constant."}, - {"Vector2Constant", "Input/Basic", "VisualShaderNodeVec2Constant", "2D vector constant."}, - {"Vector3Constant", "Input/Basic", "VisualShaderNodeVec3Constant", "3D vector constant."}, - {"Vector4Constant", "Input/Basic", "VisualShaderNodeVec4Constant", "4D vector constant."}, + {"ColorConstant", "Input/Basic", "VisualShaderNodeColorConstant", "Color constant."}, + {"BooleanConstant", "Input/Basic", "VisualShaderNodeBooleanConstant", "Boolean constant."}, + {"FloatConstant", "Input/Basic", "VisualShaderNodeFloatConstant", "Scalar floating-point constant."}, + {"IntConstant", "Input/Basic", "VisualShaderNodeIntConstant", "Scalar integer constant."}, + {"UIntConstant", "Input/Basic", "VisualShaderNodeUIntConstant", "Scalar unsigned integer constant."}, + {"Vector2Constant", "Input/Basic", "VisualShaderNodeVec2Constant", "2D vector constant."}, + {"Vector3Constant", "Input/Basic", "VisualShaderNodeVec3Constant", "3D vector constant."}, + {"Vector4Constant", "Input/Basic", "VisualShaderNodeVec4Constant", "4D vector constant."}, - // Functions + // Functions - {"FloatFunc", "Functions/Scalar", "VisualShaderNodeFloatFunc", "Float function."}, - {"IntFunc", "Functions/Scalar", "VisualShaderNodeIntFunc", "Integer function."}, - {"UIntFunc", "Functions/Scalar", "VisualShaderNodeUIntFunc", "Unsigned integer function."}, - {"VectorFunc", "Functions/Vector", "VisualShaderNodeVectorFunc", "Vector function."}, - {"DerivativeFunc", "Functions/Others", "VisualShaderNodeDerivativeFunc", "Derivative function."}, + {"FloatFunc", "Functions/Scalar", "VisualShaderNodeFloatFunc", "Float function."}, + {"IntFunc", "Functions/Scalar", "VisualShaderNodeIntFunc", "Integer function."}, + {"UIntFunc", "Functions/Scalar", "VisualShaderNodeUIntFunc", "Unsigned integer function."}, + {"VectorFunc", "Functions/Vector", "VisualShaderNodeVectorFunc", "Vector function."}, + {"DerivativeFunc", "Functions/Others", "VisualShaderNodeDerivativeFunc", "Derivative function."}, - // Operators + // Operators - {"FloatOp", "Operators/Scalar", "VisualShaderNodeFloatOp", "Float operator."}, - {"IntOp", "Operators/Scalar", "VisualShaderNodeIntOp", "Integer operator."}, - {"UIntOp", "Operators/Scalar", "VisualShaderNodeUIntOp", "Unsigned integer operator."}, - {"VectorOp", "Operators/Vector", "VisualShaderNodeVectorOp", "Vector operator."}, - {"VectorCompose", "Operators/Vector", "VisualShaderNodeVectorCompose", "Composes vector from scalars."}, - {"VectorDecompose", "Operators/Vector", "VisualShaderNodeVectorDecompose", "Decomposes vector to scalars."}, + {"FloatOp", "Operators/Scalar", "VisualShaderNodeFloatOp", "Float operator."}, + {"IntOp", "Operators/Scalar", "VisualShaderNodeIntOp", "Integer operator."}, + {"UIntOp", "Operators/Scalar", "VisualShaderNodeUIntOp", "Unsigned integer operator."}, + {"VectorOp", "Operators/Vector", "VisualShaderNodeVectorOp", "Vector operator."}, + {"VectorCompose", "Operators/Vector", "VisualShaderNodeVectorCompose", "Composes vector from scalars."}, + {"VectorDecompose", "Operators/Vector", "VisualShaderNodeVectorDecompose", "Decomposes vector to scalars."}, - // Procedural + // Procedural - {"ValueNoise", "Procedural/Noise", "VisualShaderNodeValueNoise", "Generates a simple, or Value, noise based on input 'UV'. The scale of the generated noise is controlled by input 'Scale'."}, + {"ValueNoise", "Procedural/Noise", "VisualShaderNodeValueNoise", + "Generates a simple, or Value, noise based on input 'UV'. The scale of the generated noise is controlled by input " + "'Scale'."}, // Utility - {"Compare", "Utility/Logic", "VisualShaderNodeCompare", "Returns the boolean result of the comparison between two parameters."}, - {"If", "Utility/Logic", "VisualShaderNodeIf", "Returns the value of the 'True' or 'False' input based on the value of the 'Condition' input."}, - {"Switch", "Utility/Logic", "VisualShaderNodeSwitch", "Returns an associated scalar if the provided boolean value is true or false."}, - {"Is", "Utility/Logic", "VisualShaderNodeIs", "Returns the boolean result of the comparison between INF (or NaN) and a scalar parameter."}, + {"Compare", "Utility/Logic", "VisualShaderNodeCompare", + "Returns the boolean result of the comparison between two parameters."}, + {"If", "Utility/Logic", "VisualShaderNodeIf", + "Returns the value of the 'True' or 'False' input based on the value of the 'Condition' input."}, + {"Switch", "Utility/Logic", "VisualShaderNodeSwitch", + "Returns an associated scalar if the provided boolean value is true or false."}, + {"Is", "Utility/Logic", "VisualShaderNodeIs", + "Returns the boolean result of the comparison between INF (or NaN) and a scalar parameter."}, {"", "", "", ""}, }; void VisualShaderEditor::create_node(const QPointF& coordinate) { - QTreeWidgetItem* selected_item {create_node_dialog->get_selected_item()}; + QTreeWidgetItem* selected_item{create_node_dialog->get_selected_item()}; - if (!selected_item) { - return; - } + if (!selected_item) { + return; + } - VisualShaderEditor::add_node(selected_item, coordinate); + VisualShaderEditor::add_node(selected_item, coordinate); } void VisualShaderEditor::add_node(QTreeWidgetItem* selected_item, const QPointF& coordinate) { - std::string type {selected_item->data(0, Qt::UserRole).toString().toStdString()}; + std::string type{selected_item->data(0, Qt::UserRole).toString().toStdString()}; - if (type.empty()) { - return; - } + if (type.empty()) { + return; + } - // Instantiate the node based on the type - std::shared_ptr n; - - if (type == "VisualShaderNodeInput") { - n = std::make_shared(); - } else if (type == "VisualShaderNodeColorConstant") { - n = std::make_shared(); - } else if (type == "VisualShaderNodeBooleanConstant") { - n = std::make_shared(); - } else if (type == "VisualShaderNodeFloatConstant") { - n = std::make_shared(); - } else if (type == "VisualShaderNodeIntConstant") { - n = std::make_shared(); - } else if (type == "VisualShaderNodeUIntConstant") { - n = std::make_shared(); - } else if (type == "VisualShaderNodeVec2Constant") { - n = std::make_shared(); - } else if (type == "VisualShaderNodeVec3Constant") { - n = std::make_shared(); - } else if (type == "VisualShaderNodeVec4Constant") { - n = std::make_shared(); - } else if (type == "VisualShaderNodeFloatFunc") { - n = std::make_shared(); - } else if (type == "VisualShaderNodeIntFunc") { - n = std::make_shared(); - } else if (type == "VisualShaderNodeUIntFunc") { - n = std::make_shared(); - } else if (type == "VisualShaderNodeDerivativeFunc") { - n = std::make_shared(); - } else if (type == "VisualShaderNodeFloatOp") { - n = std::make_shared(); - } else if (type == "VisualShaderNodeIntOp") { - n = std::make_shared(); - } else if (type == "VisualShaderNodeUIntOp") { - n = std::make_shared(); - } else if (type == "VisualShaderNodeValueNoise") { - n = std::make_shared(); - } else if (type == "VisualShaderNodeCompare") { - n = std::make_shared(); - } else if (type == "VisualShaderNodeIf") { - n = std::make_shared(); - } else if (type == "VisualShaderNodeIs") { - n = std::make_shared(); - } else if (type == "VisualShaderNodeSwitch") { - n = std::make_shared(); - } else { - std::cout << "Unknown node type: " << type << std::endl; - } - - if (!n) { - std::cout << "Failed to create node of type: " << type << std::endl; - return; - } - - int n_id {visual_shader->get_valid_node_id()}; - - if (n_id == (int)VisualShader::NODE_ID_INVALID) { - return; - } - - scene->add_node(n_id, n, coordinate); + scene->add_node(type, coordinate); } void VisualShaderEditor::show_create_node_dialog(const QPointF& coordinate) { - int status {create_node_dialog->exec()}; - switch (status) { - case QDialog::Accepted: - std::cout << "Create node dialog accepted" << std::endl; - VisualShaderEditor::create_node(coordinate); - break; - case QDialog::Rejected: - std::cout << "Create node dialog rejected" << std::endl; - break; - default: - std::cout << "Create node dialog unknown status" << std::endl; - break; - } -} - -void VisualShaderEditor::on_create_node_button_pressed() { - Q_EMIT on_create_node_dialog_requested(); -} - -void VisualShaderEditor::on_create_node_action_triggered() { - QPointF coordinate {view->mapToScene(view->mapFromGlobal(QCursor::pos()))}; - Q_EMIT on_create_node_dialog_requested(coordinate); -} - -std::vector VisualShaderEditor::pasre_node_category_path(const std::string& node_category_path) { - std::vector tokens; - std::stringstream ss(node_category_path); - std::string token; - while (std::getline(ss, token, '/')) { - tokens.push_back(token); - } - return tokens; -} - -QTreeWidgetItem* VisualShaderEditor::find_or_create_category_item(QTreeWidgetItem* parent, const std::string& category, const std::string& category_path, QTreeWidget* create_node_dialog_nodes_tree, std::unordered_map& category_path_map) { - // Check if category already exists under parent - if (category_path_map.find(category_path) != category_path_map.end()) { - return category_path_map[category_path]; - } - - // Create a new QTreeWidgetItem - QTreeWidgetItem* new_item; - - if (parent) { - new_item = new QTreeWidgetItem(parent); - } else { - new_item = new QTreeWidgetItem(create_node_dialog_nodes_tree); - } - - new_item->setText(0, QString::fromStdString(category)); - - // Add the new category to the map - category_path_map[category_path] = new_item; - - return new_item; + int status{create_node_dialog->exec()}; + switch (status) { + case QDialog::Accepted: + std::cout << "Create node dialog accepted" << std::endl; + VisualShaderEditor::create_node(coordinate); + break; + case QDialog::Rejected: + std::cout << "Create node dialog rejected" << std::endl; + break; + default: + std::cout << "Create node dialog unknown status" << std::endl; + break; + } +} + +void VisualShaderEditor::on_create_node_button_pressed() { Q_EMIT on_create_node_dialog_requested(); } + +void VisualShaderEditor::on_preview_shader_button_pressed() { + bool result{visual_shader->generate_shader()}; + if (!result) { + std::cout << "Failed to generate shader" << std::endl; + return; + } + code_previewer->setPlainText(QString::fromStdString(visual_shader->get_code())); + code_previewer_dialog->exec(); +} + +std::vector VisualShaderEditor::parse_node_category_path(const std::string& node_category_path) { + std::vector tokens; + std::stringstream ss(node_category_path); + std::string token; + while (std::getline(ss, token, '/')) { + tokens.push_back(token); + } + return tokens; +} + +QTreeWidgetItem* VisualShaderEditor::find_or_create_category_item( + QTreeWidgetItem* parent, const std::string& category, const std::string& category_path, + QTreeWidget* create_node_dialog_nodes_tree, std::unordered_map& category_path_map) { + // Check if category already exists under parent + if (category_path_map.find(category_path) != category_path_map.end()) { + return category_path_map[category_path]; + } + + // Create a new QTreeWidgetItem + QTreeWidgetItem* new_item; + + if (parent) { + new_item = new QTreeWidgetItem(parent); + } else { + new_item = new QTreeWidgetItem(create_node_dialog_nodes_tree); + } + + new_item->setText(0, QString::fromStdString(category)); + + // Add the new category to the map + category_path_map[category_path] = new_item; + + return new_item; } /**********************************************************************/ @@ -427,112 +438,104 @@ QTreeWidgetItem* VisualShaderEditor::find_or_create_category_item(QTreeWidgetIte /**********************************************************************/ /**********************************************************************/ -CreateNodeDialog::CreateNodeDialog(QWidget* parent) : QDialog(parent), - layout(nullptr), - create_node_dialog_nodes_tree_layout(nullptr), - create_node_dialog_nodes_tree(nullptr), - create_node_dialog_nodes_description(nullptr), - buttons_layout(nullptr), - create_button(nullptr), - cancel_button(nullptr), - selected_item(nullptr) { - layout = new QVBoxLayout(this); - layout->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom - layout->setSizeConstraint(QLayout::SetNoConstraint); - layout->setSpacing(0); - layout->setAlignment(Qt::AlignVCenter | Qt::AlignHCenter); - - //////////////// End of Header //////////////// - - // Create the nodes tree layout. - create_node_dialog_nodes_tree_layout = new QVBoxLayout(); - create_node_dialog_nodes_tree_layout->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom - create_node_dialog_nodes_tree_layout->setSpacing(0); // Adjust spacing as needed - create_node_dialog_nodes_tree_layout->setAlignment(Qt::AlignVCenter | Qt::AlignHCenter); - create_node_dialog_nodes_tree_layout->setSizeConstraint(QLayout::SetMinimumSize); - - // Add the nodes tree layout to the main layout. - layout->addLayout(create_node_dialog_nodes_tree_layout); - - // Create the nodes tree. - create_node_dialog_nodes_tree = new QTreeWidget(); - create_node_dialog_nodes_tree->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); - create_node_dialog_nodes_tree->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom - create_node_dialog_nodes_tree->setColumnCount(1); - create_node_dialog_nodes_tree->setHeaderHidden(true); - this->connect(create_node_dialog_nodes_tree, &QTreeWidget::itemSelectionChanged, this, &CreateNodeDialog::update_selected_item); - - // Add the nodes tree to the nodes tree layout. - create_node_dialog_nodes_tree_layout->addWidget(create_node_dialog_nodes_tree, 2); // 2x the size of the nodes description. - - // Create the nodes description. - create_node_dialog_nodes_description = new QTextEdit(); - create_node_dialog_nodes_description->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); - create_node_dialog_nodes_description->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom - create_node_dialog_nodes_description->setReadOnly(true); - create_node_dialog_nodes_description->setAlignment(Qt::AlignTop | Qt::AlignLeft); - - // Add the nodes description to the nodes tree layout. - create_node_dialog_nodes_tree_layout->addWidget(create_node_dialog_nodes_description, 1); - - // Create the buttons layout. - buttons_layout = new QHBoxLayout(); - layout->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom - layout->setSizeConstraint(QLayout::SetNoConstraint); - layout->setSpacing(0); - layout->setAlignment(Qt::AlignVCenter | Qt::AlignHCenter); - - create_button = new QPushButton("Create"); - QObject::connect(create_button, &QPushButton::pressed, this, &CreateNodeDialog::on_create_node_button_pressed); - create_button->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); - create_button->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom - - cancel_button = new QPushButton("Cancel"); - QObject::connect(cancel_button, &QPushButton::pressed, this, &CreateNodeDialog::on_cancel_node_creation_button_pressed); - cancel_button->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); - cancel_button->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom - - // Add the buttons to the buttons layout. - buttons_layout->addWidget(create_button); - buttons_layout->addWidget(cancel_button); - - // Add the buttons layout to the main layout. - layout->addLayout(buttons_layout); - - //////////////// Start of Footer //////////////// - - this->setWindowTitle("Create Shader Node"); - - this->setLayout(layout); -} - -CreateNodeDialog::~CreateNodeDialog() { - if (cancel_button) delete cancel_button; - if (create_button) delete create_button; - if (buttons_layout) delete buttons_layout; - if (create_node_dialog_nodes_tree) delete create_node_dialog_nodes_tree; - if (create_node_dialog_nodes_description) delete create_node_dialog_nodes_description; - if (create_node_dialog_nodes_tree_layout) delete create_node_dialog_nodes_tree_layout; - if (layout) delete layout; -} - -void CreateNodeDialog::on_create_node_button_pressed() { - this->accept(); -} - -void CreateNodeDialog::on_cancel_node_creation_button_pressed() { - this->reject(); -} +CreateNodeDialog::CreateNodeDialog(QWidget* parent) + : QDialog(parent), + layout(nullptr), + create_node_dialog_nodes_tree_layout(nullptr), + create_node_dialog_nodes_tree(nullptr), + create_node_dialog_nodes_description(nullptr), + buttons_layout(nullptr), + create_button(nullptr), + cancel_button(nullptr), + selected_item(nullptr) { + layout = new QVBoxLayout(this); + layout->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom + layout->setSizeConstraint(QLayout::SetNoConstraint); + layout->setSpacing(0); + layout->setAlignment(Qt::AlignVCenter | Qt::AlignHCenter); + + //////////////// End of Header //////////////// + + // Create the nodes tree layout. + create_node_dialog_nodes_tree_layout = new QVBoxLayout(); + create_node_dialog_nodes_tree_layout->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom + create_node_dialog_nodes_tree_layout->setSpacing(0); // Adjust spacing as needed + create_node_dialog_nodes_tree_layout->setAlignment(Qt::AlignVCenter | Qt::AlignHCenter); + create_node_dialog_nodes_tree_layout->setSizeConstraint(QLayout::SetMinimumSize); + + // Add the nodes tree layout to the main layout. + layout->addLayout(create_node_dialog_nodes_tree_layout); + + // Create the nodes tree. + create_node_dialog_nodes_tree = new QTreeWidget(); + create_node_dialog_nodes_tree->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + create_node_dialog_nodes_tree->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom + create_node_dialog_nodes_tree->setColumnCount(1); + create_node_dialog_nodes_tree->setHeaderHidden(true); + this->connect(create_node_dialog_nodes_tree, &QTreeWidget::itemSelectionChanged, this, + &CreateNodeDialog::update_selected_item); + + // Add the nodes tree to the nodes tree layout. + create_node_dialog_nodes_tree_layout->addWidget(create_node_dialog_nodes_tree, + 2); // 2x the size of the nodes description. + + // Create the nodes description. + create_node_dialog_nodes_description = new QTextEdit(); + create_node_dialog_nodes_description->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + create_node_dialog_nodes_description->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom + create_node_dialog_nodes_description->setReadOnly(true); + create_node_dialog_nodes_description->setAlignment(Qt::AlignTop | Qt::AlignLeft); + + // Add the nodes description to the nodes tree layout. + create_node_dialog_nodes_tree_layout->addWidget(create_node_dialog_nodes_description, 1); + + // Create the buttons layout. + buttons_layout = new QHBoxLayout(); + layout->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom + layout->setSizeConstraint(QLayout::SetNoConstraint); + layout->setSpacing(0); + layout->setAlignment(Qt::AlignVCenter | Qt::AlignHCenter); + + create_button = new QPushButton("Create"); + QObject::connect(create_button, &QPushButton::pressed, this, &CreateNodeDialog::on_create_node_button_pressed); + create_button->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + create_button->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom + + cancel_button = new QPushButton("Cancel"); + QObject::connect(cancel_button, &QPushButton::pressed, this, + &CreateNodeDialog::on_cancel_node_creation_button_pressed); + cancel_button->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + cancel_button->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom + + // Add the buttons to the buttons layout. + buttons_layout->addWidget(create_button); + buttons_layout->addWidget(cancel_button); + + // Add the buttons layout to the main layout. + layout->addLayout(buttons_layout); + + //////////////// Start of Footer //////////////// + + this->setWindowTitle("Create Shader Node"); + + this->setLayout(layout); +} + +CreateNodeDialog::~CreateNodeDialog() {} + +void CreateNodeDialog::on_create_node_button_pressed() { this->accept(); } + +void CreateNodeDialog::on_cancel_node_creation_button_pressed() { this->reject(); } void CreateNodeDialog::update_selected_item() { - QTreeWidgetItem* item {create_node_dialog_nodes_tree->currentItem()}; - if (item) { - selected_item = item; - create_node_dialog_nodes_description->setText(item->data(0, Qt::UserRole + 1).toString()); - } else { - selected_item = nullptr; - create_node_dialog_nodes_description->setText(""); - } + QTreeWidgetItem* item{create_node_dialog_nodes_tree->currentItem()}; + if (item) { + selected_item = item; + create_node_dialog_nodes_description->setText(item->data(0, Qt::UserRole + 1).toString()); + } else { + selected_item = nullptr; + create_node_dialog_nodes_description->setText(""); + } } /**********************************************************************/ @@ -549,523 +552,610 @@ void CreateNodeDialog::update_selected_item() { // Public functions ////////////////////////////// -VisualShaderGraphicsScene::VisualShaderGraphicsScene(VisualShader* vs, QObject* parent) : QGraphicsScene(parent), - vs(vs), - temporary_connection_graphics_object(nullptr) { - setItemIndexMethod(QGraphicsScene::NoIndex); // https://doc.qt.io/qt-6/qgraphicsscene.html#ItemIndexMethod-enum +VisualShaderGraphicsScene::VisualShaderGraphicsScene(VisualShader* vs, QObject* parent) + : QGraphicsScene(parent), vs(vs), temporary_connection_graphics_object(nullptr) { + setItemIndexMethod(QGraphicsScene::NoIndex); // https://doc.qt.io/qt-6/qgraphicsscene.html#ItemIndexMethod-enum - QObject::connect(this, &VisualShaderGraphicsScene::node_moved, this, &VisualShaderGraphicsScene::on_node_moved); + QObject::connect(this, &VisualShaderGraphicsScene::node_moved, this, &VisualShaderGraphicsScene::on_node_moved); } VisualShaderGraphicsScene::~VisualShaderGraphicsScene() {} -bool VisualShaderGraphicsScene::add_node(const int& n_id, const std::shared_ptr& node, const QPointF& coordinate) { - // Make sure the node doesn't already exist, we don't want to overwrite a node. - if (node_graphics_objects.find(n_id) != node_graphics_objects.end()) { - return false; - } +bool VisualShaderGraphicsScene::add_node(const std::string& type, const QPointF& coordinate) { + // Instantiate the node based on the type + std::shared_ptr n; + + if (type == "VisualShaderNodeInput") { + n = std::make_shared(); + } else if (type == "VisualShaderNodeColorConstant") { + n = std::make_shared(); + } else if (type == "VisualShaderNodeBooleanConstant") { + n = std::make_shared(); + } else if (type == "VisualShaderNodeFloatConstant") { + n = std::make_shared(); + } else if (type == "VisualShaderNodeIntConstant") { + n = std::make_shared(); + } else if (type == "VisualShaderNodeUIntConstant") { + n = std::make_shared(); + } else if (type == "VisualShaderNodeVec2Constant") { + n = std::make_shared(); + } else if (type == "VisualShaderNodeVec3Constant") { + n = std::make_shared(); + } else if (type == "VisualShaderNodeVec4Constant") { + n = std::make_shared(); + } else if (type == "VisualShaderNodeFloatFunc") { + n = std::make_shared(); + } else if (type == "VisualShaderNodeIntFunc") { + n = std::make_shared(); + } else if (type == "VisualShaderNodeUIntFunc") { + n = std::make_shared(); + } else if (type == "VisualShaderNodeDerivativeFunc") { + n = std::make_shared(); + } else if (type == "VisualShaderNodeFloatOp") { + n = std::make_shared(); + } else if (type == "VisualShaderNodeIntOp") { + n = std::make_shared(); + } else if (type == "VisualShaderNodeUIntOp") { + n = std::make_shared(); + } else if (type == "VisualShaderNodeValueNoise") { + n = std::make_shared(); + } else if (type == "VisualShaderNodeCompare") { + n = std::make_shared(); + } else if (type == "VisualShaderNodeIf") { + n = std::make_shared(); + } else if (type == "VisualShaderNodeIs") { + n = std::make_shared(); + } else if (type == "VisualShaderNodeSwitch") { + n = std::make_shared(); + } else { + std::cout << "Unknown node type: " << type << std::endl; + } + + if (!n) { + std::cout << "Failed to create node of type: " << type << std::endl; + return false; + } + + int n_id{vs->get_valid_node_id()}; + + if (n_id == (int)VisualShader::NODE_ID_INVALID) { + return false; + } + + return VisualShaderGraphicsScene::add_node(n_id, n, coordinate); +} + +bool VisualShaderGraphicsScene::add_node(const int& n_id, const std::shared_ptr& n, const QPointF& coordinate) { + // Make sure the node doesn't already exist, we don't want to overwrite a node. + if (node_graphics_objects.find(n_id) != node_graphics_objects.end()) { + return false; + } + + QList views{this->views()}; + if (views.isEmpty()) { + std::cout << "No views available" << std::endl; + return false; + } + + // The output node cannot be removed or added by the user + if (n_id >= VisualShader::NODE_ID_OUTPUT + 1) { + bool result{vs->add_node(n, {(float)coordinate.x(), (float)coordinate.y()}, n_id)}; - QList views {this->views()}; - if (views.isEmpty()) { - std::cout << "No views available" << std::endl; - return false; + if (!result) { + return false; } + } - // The output node cannot be removed or added by the user - if (n_id >= VisualShader::NODE_ID_OUTPUT + 1) { - bool result {vs->add_node(node, {(float)coordinate.x(), (float)coordinate.y()}, n_id)}; + VisualShaderGraphicsView* view{dynamic_cast(views.first())}; - if (!result) { - return false; - } - } + if (vs->get_node_coordinate(n_id).x < view->get_x() || + vs->get_node_coordinate(n_id).x > view->get_x() + view->get_width() || + vs->get_node_coordinate(n_id).y < view->get_y() || + vs->get_node_coordinate(n_id).y > view->get_y() + view->get_height()) { + std::cout << "Node is out of view bounds" << std::endl; + } - VisualShaderGraphicsView* view {dynamic_cast(views.first())}; + VisualShaderNodeGraphicsObject* n_o{new VisualShaderNodeGraphicsObject(n_id, coordinate, n)}; - if (vs->get_node_coordinate(n_id).x < view->get_x() || - vs->get_node_coordinate(n_id).x > view->get_x() + view->get_width() || - vs->get_node_coordinate(n_id).y < view->get_y() || - vs->get_node_coordinate(n_id).y > view->get_y() + view->get_height()) { - std::cout << "Node is out of view bounds" << std::endl; - } + QObject::connect(n_o, &VisualShaderNodeGraphicsObject::node_deleted, this, &VisualShaderGraphicsScene::on_node_deleted); - VisualShaderNodeGraphicsObject* n_o {new VisualShaderNodeGraphicsObject(vs, n_id)}; + node_graphics_objects[n_id] = n_o; - node_graphics_objects[n_id] = n_o; + addItem(n_o); - addItem(n_o); - - return true; + return true; } bool VisualShaderGraphicsScene::delete_node(const int& n_id) { - bool result {vs->remove_node(n_id)}; + const std::shared_ptr n{vs->get_node(n_id)}; - if (!result) { - return false; + if (!n) { + return false; + } + + bool result{vs->remove_node(n_id)}; + + if (!result) { + return false; + } + + VisualShaderNodeGraphicsObject* n_o{this->get_node_graphics_object(n_id)}; + + if (!n_o) { + return false; + } + + // Remove all connections to the node + for (int i{0}; i < n->get_input_port_count(); i++) { + VisualShaderInputPortGraphicsObject* i_port{n_o->get_input_port_graphics_object(i)}; + + if (!i_port || !i_port->is_connected()) { + continue; } - const std::shared_ptr n {vs->get_node(n_id)}; + // Get the output port of the connection + VisualShaderConnectionGraphicsObject* c_o{i_port->get_connection_graphics_object()}; - if (!n) { - return false; + if (!c_o) { + continue; } - VisualShaderNodeGraphicsObject* n_o {this->get_node_graphics_object(n_id)}; + VisualShaderNodeGraphicsObject* from_n_o{this->get_node_graphics_object(c_o->get_from_node_id())}; - if (!n_o) { - return false; + if (!from_n_o) { + continue; } - // Remove all connections to the node - for (int i{0}; i < n->get_input_port_count(); i++) { - VisualShaderInputPortGraphicsObject* i_port {n_o->get_input_port_graphics_object(i)}; + VisualShaderOutputPortGraphicsObject* o_port{from_n_o->get_output_port_graphics_object(c_o->get_from_port_index())}; - if (!i_port || !i_port->is_connected()) { - continue; - } + if (!o_port) { + continue; + } - // Get the output port of the connection - VisualShaderConnectionGraphicsObject* c_o {i_port->get_connection_graphics_object()}; + bool result{vs->disconnect_nodes(c_o->get_from_node_id(), c_o->get_from_port_index(), n_id, i)}; - if (!c_o) { - continue; - } + if (!result) { + std::cout << "Failed to disconnect nodes" << std::endl; + continue; + } - VisualShaderNodeGraphicsObject* from_n_o {this->get_node_graphics_object(c_o->get_from_node_id())}; + result = this->delete_connection(c_o->get_from_node_id(), c_o->get_from_port_index(), n_id, i); - if (!from_n_o) { - continue; - } + if (!result) { + std::cout << "Failed to delete connection" << std::endl; + continue; + } + } - VisualShaderOutputPortGraphicsObject* o_port {from_n_o->get_output_port_graphics_object(c_o->get_from_port_index())}; + for (int i{0}; i < n->get_output_port_count(); i++) { + VisualShaderOutputPortGraphicsObject* o_port{n_o->get_output_port_graphics_object(i)}; - if (!o_port) { - continue; - } + if (!o_port || !o_port->is_connected()) { + continue; + } - bool result {vs->disconnect_nodes(c_o->get_from_node_id(), c_o->get_from_port_index(), n_id, i)}; + // Get the input port of the connection + VisualShaderConnectionGraphicsObject* c_o{o_port->get_connection_graphics_object()}; - if (!result) { - std::cout << "Failed to disconnect nodes" << std::endl; - continue; - } + if (!c_o) { + continue; + } - result = this->delete_connection(c_o->get_from_node_id(), c_o->get_from_port_index(), n_id, i); + VisualShaderNodeGraphicsObject* to_n_o{this->get_node_graphics_object(c_o->get_to_node_id())}; - if (!result) { - std::cout << "Failed to delete connection" << std::endl; - continue; - } + if (!to_n_o) { + continue; } - for (int i{0}; i < n->get_output_port_count(); i++) { - VisualShaderOutputPortGraphicsObject* o_port {n_o->get_output_port_graphics_object(i)}; + VisualShaderInputPortGraphicsObject* i_port{to_n_o->get_input_port_graphics_object(c_o->get_to_port_index())}; - if (!o_port || !o_port->is_connected()) { - continue; - } + if (!i_port) { + continue; + } - // Get the input port of the connection - VisualShaderConnectionGraphicsObject* c_o {o_port->get_connection_graphics_object()}; + bool result{vs->disconnect_nodes(n_id, i, c_o->get_to_node_id(), c_o->get_to_port_index())}; - if (!c_o) { - continue; - } + if (!result) { + std::cout << "Failed to disconnect nodes" << std::endl; + continue; + } - VisualShaderNodeGraphicsObject* to_n_o {this->get_node_graphics_object(c_o->get_to_node_id())}; + result = this->delete_connection(c_o->get_from_node_id(), c_o->get_from_port_index(), n_id, i); - if (!to_n_o) { - continue; - } + if (!result) { + std::cout << "Failed to delete connection" << std::endl; + continue; + } + } - VisualShaderInputPortGraphicsObject* i_port {to_n_o->get_input_port_graphics_object(c_o->get_to_port_index())}; - if (!i_port) { - continue; - } + // Remove the node from the scene + removeItem(n_o); + this->node_graphics_objects.erase(n_id); - bool result {vs->disconnect_nodes(n_id, i, c_o->get_to_node_id(), c_o->get_to_port_index())}; + delete n_o; // Make sure to delete the node object to delete all child ports - if (!result) { - std::cout << "Failed to disconnect nodes" << std::endl; - continue; - } + return true; +} - result = this->delete_connection(c_o->get_from_node_id(), c_o->get_from_port_index(), n_id, i); +bool VisualShaderGraphicsScene::add_connection(const int& from_node_id, const int& from_port_index, + const int& to_node_id, const int& to_port_index) { + QList views{this->views()}; + if (views.isEmpty()) { + std::cout << "No views available" << std::endl; + return false; + } - if (!result) { - std::cout << "Failed to delete connection" << std::endl; - continue; - } - } + // Create the connection and set its start + VisualShaderNodeGraphicsObject* from_n_o{this->get_node_graphics_object(from_node_id)}; - // Remove the node from the scene - removeItem(n_o); - delete n_o; // Make sure to delete the node object to delete all child ports + if (!from_n_o) { + return false; + } - return true; -} + VisualShaderOutputPortGraphicsObject* from_o_port{from_n_o->get_output_port_graphics_object(from_port_index)}; -bool VisualShaderGraphicsScene::add_connection(const int& from_node_id, const int& from_port_index, const int& to_node_id, const int& to_port_index) { - QList views {this->views()}; - if (views.isEmpty()) { - std::cout << "No views available" << std::endl; - return false; - } - - // Create the connection and set its start - VisualShaderNodeGraphicsObject* from_n_o {this->get_node_graphics_object(from_node_id)}; + if (!from_o_port) { + return false; + } - if (!from_n_o) { - return false; - } + VisualShaderGraphicsView* view{dynamic_cast(views.first())}; + + if (from_o_port->get_global_coordinate().x() < view->get_x() || + from_o_port->get_global_coordinate().x() > view->get_x() + view->get_width()) { + std::cout << "Start of connection is out of view bounds" << std::endl; + } + + this->temporary_connection_graphics_object = + new VisualShaderConnectionGraphicsObject(from_node_id, from_port_index, from_o_port->get_global_coordinate()); + from_o_port->connect(this->temporary_connection_graphics_object); + addItem(this->temporary_connection_graphics_object); - VisualShaderOutputPortGraphicsObject* from_o_port {from_n_o->get_output_port_graphics_object(from_port_index)}; + if (to_node_id != (int)VisualShader::NODE_ID_INVALID && to_port_index != (int)VisualShader::PORT_INDEX_INVALID) { + // Set the end of the connection + VisualShaderNodeGraphicsObject* to_n_o{this->get_node_graphics_object(to_node_id)}; - if (!from_o_port) { - return false; + if (!to_n_o) { + return false; } - VisualShaderGraphicsView* view {dynamic_cast(views.first())}; + VisualShaderInputPortGraphicsObject* to_i_port{to_n_o->get_input_port_graphics_object(to_port_index)}; - if (from_o_port->get_global_coordinate().x() < view->get_x() || - from_o_port->get_global_coordinate().x() > view->get_x() + view->get_width()) { - std::cout << "Start of connection is out of view bounds" << std::endl; + if (!to_i_port) { + return false; } - this->temporary_connection_graphics_object = new VisualShaderConnectionGraphicsObject(from_node_id, from_port_index, from_o_port->get_global_coordinate()); - from_o_port->connect(this->temporary_connection_graphics_object); - addItem(this->temporary_connection_graphics_object); - - if (to_node_id != (int)VisualShader::NODE_ID_INVALID && to_port_index != (int)VisualShader::PORT_INDEX_INVALID) { - // Set the end of the connection - VisualShaderNodeGraphicsObject* to_n_o {this->get_node_graphics_object(to_node_id)}; - - if (!to_n_o) { - return false; - } - - VisualShaderInputPortGraphicsObject* to_i_port {to_n_o->get_input_port_graphics_object(to_port_index)}; - - if (!to_i_port) { - return false; - } - - if (to_i_port->get_global_coordinate().y() < view->get_y() || - to_i_port->get_global_coordinate().y() > view->get_y() + view->get_height()) { - std::cout << "End of connection is out of view bounds" << std::endl; - } - - this->temporary_connection_graphics_object->set_end_coordinate(to_i_port->get_global_coordinate()); - to_i_port->connect(this->temporary_connection_graphics_object); - this->temporary_connection_graphics_object->set_to_node_id(to_node_id); - this->temporary_connection_graphics_object->set_to_port_index(to_port_index); - this->temporary_connection_graphics_object = nullptr; // Make sure to reset the temporary connection object - - // Connect the nodes in the VisualShader - bool result {vs->can_connect_nodes(from_node_id, from_port_index, to_node_id, to_port_index)}; - if (!result) { - std::cout << "Can't connect nodes" << std::endl; - return false; - } - - result = vs->connect_nodes(from_node_id, from_port_index, to_node_id, to_port_index); - if (!result) { - std::cout << "Failed to connect nodes" << std::endl; - return false; - } + if (to_i_port->get_global_coordinate().y() < view->get_y() || + to_i_port->get_global_coordinate().y() > view->get_y() + view->get_height()) { + std::cout << "End of connection is out of view bounds" << std::endl; } - return true; -} + this->temporary_connection_graphics_object->set_end_coordinate(to_i_port->get_global_coordinate()); + to_i_port->connect(this->temporary_connection_graphics_object); + this->temporary_connection_graphics_object->set_to_node_id(to_node_id); + this->temporary_connection_graphics_object->set_to_port_index(to_port_index); + this->temporary_connection_graphics_object = nullptr; // Make sure to reset the temporary connection object -bool VisualShaderGraphicsScene::delete_connection(const int& from_node_id, const int& from_port_index, const int& to_node_id, const int& to_port_index) { - VisualShaderNodeGraphicsObject* from_n_o {this->get_node_graphics_object(from_node_id)}; + // Connect the nodes in the VisualShader + bool result{vs->can_connect_nodes(from_node_id, from_port_index, to_node_id, to_port_index)}; + if (!result) { + std::cout << "Can't connect nodes" << std::endl; + return false; + } - if (!from_n_o) { - return false; + result = vs->connect_nodes(from_node_id, from_port_index, to_node_id, to_port_index); + if (!result) { + std::cout << "Failed to connect nodes" << std::endl; + return false; } + } - VisualShaderOutputPortGraphicsObject* from_o_port {from_n_o->get_output_port_graphics_object(from_port_index)}; + return true; +} - if (!from_o_port) { - return false; - } +bool VisualShaderGraphicsScene::delete_connection(const int& from_node_id, const int& from_port_index, + const int& to_node_id, const int& to_port_index) { + VisualShaderNodeGraphicsObject* from_n_o{this->get_node_graphics_object(from_node_id)}; - VisualShaderConnectionGraphicsObject* c_o {from_o_port->get_connection_graphics_object()}; + if (!from_n_o) { + return false; + } - if (!c_o) { - return false; - } + VisualShaderOutputPortGraphicsObject* from_o_port{from_n_o->get_output_port_graphics_object(from_port_index)}; - if (to_node_id != (int)VisualShader::NODE_ID_INVALID && to_port_index != (int)VisualShader::PORT_INDEX_INVALID) { - VisualShaderNodeGraphicsObject* to_n_o {this->get_node_graphics_object(to_node_id)}; + if (!from_o_port) { + return false; + } - if (!to_n_o) { - return false; - } + VisualShaderConnectionGraphicsObject* c_o{from_o_port->get_connection_graphics_object()}; - VisualShaderInputPortGraphicsObject* to_i_port {to_n_o->get_input_port_graphics_object(to_port_index)}; + if (!c_o) { + return false; + } - if (!to_i_port) { - return false; - } + if (to_node_id != (int)VisualShader::NODE_ID_INVALID && to_port_index != (int)VisualShader::PORT_INDEX_INVALID) { + VisualShaderNodeGraphicsObject* to_n_o{this->get_node_graphics_object(to_node_id)}; - bool result {vs->disconnect_nodes(from_node_id, from_port_index, to_node_id, to_port_index)}; + if (!to_n_o) { + return false; + } - if (!result) { - return false; - } + VisualShaderInputPortGraphicsObject* to_i_port{to_n_o->get_input_port_graphics_object(to_port_index)}; - to_i_port->detach_connection(); + if (!to_i_port) { + return false; } - from_o_port->detach_connection(); - removeItem(c_o); - delete c_o; + bool result{vs->disconnect_nodes(from_node_id, from_port_index, to_node_id, to_port_index)}; - return true; + if (!result) { + return false; + } + + to_i_port->detach_connection(); + } + + from_o_port->detach_connection(); + removeItem(c_o); + delete c_o; + + return true; } VisualShaderNodeGraphicsObject* VisualShaderGraphicsScene::get_node_graphics_object(const int& n_id) const { - VisualShaderNodeGraphicsObject* n_o {nullptr}; + VisualShaderNodeGraphicsObject* n_o{nullptr}; - auto it {node_graphics_objects.find(n_id)}; - if (it != node_graphics_objects.end()) { - n_o = it->second; - } + auto it{node_graphics_objects.find(n_id)}; + if (it != node_graphics_objects.end()) { + n_o = it->second; + } - return n_o; + return n_o; } void VisualShaderGraphicsScene::on_node_moved(const int& n_id, const QPointF& new_coordinate) { - const std::shared_ptr n {vs->get_node(n_id)}; + const std::shared_ptr n{vs->get_node(n_id)}; - if (!n) { - return; - } - - // Update the node's coordinate in the VisualShader - vs->set_node_coordinate(n_id, {(float)new_coordinate.x(), (float)new_coordinate.y()}); + if (!n) { + return; + } - // Update coordinates of all connected connections - VisualShaderNodeGraphicsObject* n_o {this->get_node_graphics_object(n_id)}; + // Update the node's coordinate in the VisualShader + vs->set_node_coordinate(n_id, {(float)new_coordinate.x(), (float)new_coordinate.y()}); - for (int i{0}; i < n->get_input_port_count(); i++) { - VisualShaderInputPortGraphicsObject* i_port {n_o->get_input_port_graphics_object(i)}; + // Update coordinates of all connected connections + VisualShaderNodeGraphicsObject* n_o{this->get_node_graphics_object(n_id)}; - if (!i_port || !i_port->is_connected()) { - continue; - } + for (int i{0}; i < n->get_input_port_count(); i++) { + VisualShaderInputPortGraphicsObject* i_port{n_o->get_input_port_graphics_object(i)}; - VisualShaderConnectionGraphicsObject* c_o {i_port->get_connection_graphics_object()}; + if (!i_port || !i_port->is_connected()) { + continue; + } - if (!c_o) { - continue; - } + VisualShaderConnectionGraphicsObject* c_o{i_port->get_connection_graphics_object()}; - c_o->set_end_coordinate(i_port->get_global_coordinate()); + if (!c_o) { + continue; } - - for (int i{0}; i < n->get_output_port_count(); i++) { - VisualShaderOutputPortGraphicsObject* o_port {n_o->get_output_port_graphics_object(i)}; - if (!o_port || !o_port->is_connected()) { - continue; - } + c_o->set_end_coordinate(i_port->get_global_coordinate()); + } - VisualShaderConnectionGraphicsObject* c_o {o_port->get_connection_graphics_object()}; + for (int i{0}; i < n->get_output_port_count(); i++) { + VisualShaderOutputPortGraphicsObject* o_port{n_o->get_output_port_graphics_object(i)}; - if (!c_o) { - continue; - } + if (!o_port || !o_port->is_connected()) { + continue; + } + + VisualShaderConnectionGraphicsObject* c_o{o_port->get_connection_graphics_object()}; - c_o->set_start_coordinate(o_port->get_global_coordinate()); + if (!c_o) { + continue; } + + c_o->set_start_coordinate(o_port->get_global_coordinate()); + } } -void VisualShaderGraphicsScene::on_port_pressed(QGraphicsObject* port, const QPointF& coordinate) {} +void VisualShaderGraphicsScene::on_node_deleted(const int& n_id) { + bool result{this->delete_node(n_id)}; + + if (!result) { + std::cout << "Failed to delete node" << std::endl; + } +} + +void VisualShaderGraphicsScene::on_port_pressed(QGraphicsObject* port, const QPointF& coordinate) { + this->temporary_connection_graphics_object = nullptr; // Reset the temporary connection object +} void VisualShaderGraphicsScene::on_port_dragged(QGraphicsObject* port, const QPointF& coordinate) { - VisualShaderConnectionGraphicsObject* c_o {nullptr}; - - VisualShaderOutputPortGraphicsObject* o_port {dynamic_cast(port)}; + VisualShaderConnectionGraphicsObject* c_o{nullptr}; - if (!o_port) { - VisualShaderInputPortGraphicsObject* i_port {dynamic_cast(port)}; - - if (!i_port) { - return; - } - - if (i_port->is_connected() && !temporary_connection_graphics_object) { - c_o = i_port->get_connection_graphics_object(); - temporary_connection_graphics_object = c_o; // Store the connection object for access in the next drag call - bool result {vs->disconnect_nodes(c_o->get_from_node_id(), c_o->get_from_port_index(), c_o->get_to_node_id(), c_o->get_to_port_index())}; - if (!result) { - std::cout << "Failed to disconnect nodes" << std::endl; - } - i_port->detach_connection(); - c_o->detach_end(); - } else if (!i_port->is_connected() && temporary_connection_graphics_object) { - c_o = temporary_connection_graphics_object; - } else { - return; - } - - c_o->set_end_coordinate(coordinate); - - return; + VisualShaderOutputPortGraphicsObject* o_port{dynamic_cast(port)}; + + if (!o_port) { + VisualShaderInputPortGraphicsObject* i_port{dynamic_cast(port)}; + + if (!i_port) { + return; } - if (!o_port->is_connected() && !temporary_connection_graphics_object) { - bool result {this->add_connection(o_port->get_node_id(), o_port->get_port_index())}; - if (!result) { - std::cout << "Failed to add connection" << std::endl; - return; - } - c_o = temporary_connection_graphics_object; - } else if (o_port->is_connected() && temporary_connection_graphics_object) { - c_o = temporary_connection_graphics_object; - } else if (o_port->is_connected() && !temporary_connection_graphics_object) { - c_o = o_port->get_connection_graphics_object(); - temporary_connection_graphics_object = c_o; // Store the connection object for access in the next drag call - - // Detach the connection from the input port - VisualShaderNodeGraphicsObject* n_o {this->get_node_graphics_object(c_o->get_to_node_id())}; - if (!n_o) { - return; - } - VisualShaderInputPortGraphicsObject* i_port {n_o->get_input_port_graphics_object(c_o->get_to_port_index())}; - if (!i_port) { - return; - } - bool result {vs->disconnect_nodes(c_o->get_from_node_id(), c_o->get_from_port_index(), c_o->get_to_node_id(), c_o->get_to_port_index())}; - if (!result) { - std::cout << "Failed to disconnect nodes" << std::endl; - } - i_port->detach_connection(); - c_o->detach_end(); + if (i_port->is_connected() && !temporary_connection_graphics_object) { + c_o = i_port->get_connection_graphics_object(); + temporary_connection_graphics_object = c_o; // Store the connection object for access in the next drag call + bool result{vs->disconnect_nodes(c_o->get_from_node_id(), c_o->get_from_port_index(), c_o->get_to_node_id(), + c_o->get_to_port_index())}; + if (!result) { + std::cout << "Failed to disconnect nodes" << std::endl; + } + i_port->detach_connection(); + c_o->detach_end(); + } else if (!i_port->is_connected() && temporary_connection_graphics_object) { + c_o = temporary_connection_graphics_object; } else { - return; + return; } c_o->set_end_coordinate(coordinate); + + return; + } + + if (!o_port->is_connected() && !temporary_connection_graphics_object) { + bool result{this->add_connection(o_port->get_node_id(), o_port->get_port_index())}; + if (!result) { + std::cout << "Failed to add connection" << std::endl; + return; + } + c_o = temporary_connection_graphics_object; + } else if (o_port->is_connected() && temporary_connection_graphics_object) { + c_o = temporary_connection_graphics_object; + } else if (o_port->is_connected() && !temporary_connection_graphics_object) { + c_o = o_port->get_connection_graphics_object(); + temporary_connection_graphics_object = c_o; // Store the connection object for access in the next drag call + + // Detach the connection from the input port + VisualShaderNodeGraphicsObject* n_o{this->get_node_graphics_object(c_o->get_to_node_id())}; + if (!n_o) { + return; + } + VisualShaderInputPortGraphicsObject* i_port{n_o->get_input_port_graphics_object(c_o->get_to_port_index())}; + if (!i_port) { + return; + } + bool result{vs->disconnect_nodes(c_o->get_from_node_id(), c_o->get_from_port_index(), c_o->get_to_node_id(), + c_o->get_to_port_index())}; + if (!result) { + std::cout << "Failed to disconnect nodes" << std::endl; + } + i_port->detach_connection(); + c_o->detach_end(); + } else { + return; + } + + c_o->set_end_coordinate(coordinate); } void VisualShaderGraphicsScene::on_port_dropped(QGraphicsObject* port, const QPointF& coordinate) { - VisualShaderConnectionGraphicsObject* c_o {temporary_connection_graphics_object}; - temporary_connection_graphics_object = nullptr; // Reset the temporary connection object + VisualShaderConnectionGraphicsObject* c_o{temporary_connection_graphics_object}; + temporary_connection_graphics_object = nullptr; // Reset the temporary connection object - if (!c_o) { - return; - } + if (!c_o) { + return; + } - // Find all items under the coordinate - QList items_at_coordinate {this->items(coordinate)}; + // Find all items under the coordinate + QList items_at_coordinate{this->items(coordinate)}; - // Iterate through the items and check if an input port is under the mouse - VisualShaderInputPortGraphicsObject* in_p_o {nullptr}; - for (QGraphicsItem* item : items_at_coordinate) { - // Check if the item is an input port - in_p_o = dynamic_cast(item); + // Iterate through the items and check if an input port is under the mouse + VisualShaderInputPortGraphicsObject* in_p_o{nullptr}; + for (QGraphicsItem* item : items_at_coordinate) { + // Check if the item is an input port + in_p_o = dynamic_cast(item); - if (in_p_o) { - break; - } + if (in_p_o) { + break; } - - VisualShaderOutputPortGraphicsObject* o_port {dynamic_cast(port)}; + } - if (!o_port) { - VisualShaderInputPortGraphicsObject* i_port {dynamic_cast(port)}; + VisualShaderOutputPortGraphicsObject* o_port{dynamic_cast(port)}; - if (!i_port) { - return; - } + if (!o_port) { + VisualShaderInputPortGraphicsObject* i_port{dynamic_cast(port)}; - if (!in_p_o) { - bool result {this->delete_connection(c_o->get_from_node_id(), c_o->get_from_port_index())}; + if (!i_port) { + return; + } - if (!result) { - std::cout << "Failed to delete connection" << std::endl; - } + if (!in_p_o) { + bool result{this->delete_connection(c_o->get_from_node_id(), c_o->get_from_port_index())}; - return; // Return because we dragging an input port and dropped on nothing - } + if (!result) { + std::cout << "Failed to delete connection" << std::endl; + } - bool result {vs->can_connect_nodes(c_o->get_from_node_id(), c_o->get_from_port_index(), in_p_o->get_node_id(), in_p_o->get_port_index())}; + return; // Return because we dragging an input port and dropped on nothing + } - if (!result) { - bool result {this->delete_connection(c_o->get_from_node_id(), c_o->get_from_port_index())}; + bool result{vs->can_connect_nodes(c_o->get_from_node_id(), c_o->get_from_port_index(), in_p_o->get_node_id(), + in_p_o->get_port_index())}; - if (!result) { - std::cout << "Failed to delete connection" << std::endl; - } + if (!result) { + bool result{this->delete_connection(c_o->get_from_node_id(), c_o->get_from_port_index())}; - return; // Return because we dragging an input port and dropped on nothing - } + if (!result) { + std::cout << "Failed to delete connection" << std::endl; + } - // Connect the nodes - result = vs->connect_nodes(c_o->get_from_node_id(), c_o->get_from_port_index(), in_p_o->get_node_id(), in_p_o->get_port_index()); + return; // Return because we dragging an input port and dropped on nothing + } - if (!result) { - bool result {this->delete_connection(c_o->get_from_node_id(), c_o->get_from_port_index())}; + // Connect the nodes + result = vs->connect_nodes(c_o->get_from_node_id(), c_o->get_from_port_index(), in_p_o->get_node_id(), + in_p_o->get_port_index()); - if (!result) { - std::cout << "Failed to delete connection" << std::endl; - } + if (!result) { + bool result{this->delete_connection(c_o->get_from_node_id(), c_o->get_from_port_index())}; - return; // Return because we dragging an input port and dropped on nothing - } + if (!result) { + std::cout << "Failed to delete connection" << std::endl; + } - c_o->set_to_node_id(in_p_o->get_node_id()); - c_o->set_to_port_index(in_p_o->get_port_index()); - c_o->set_end_coordinate(in_p_o->get_global_coordinate()); - in_p_o->connect(c_o); - return; + return; // Return because we dragging an input port and dropped on nothing } - if (!in_p_o) { - bool result {this->delete_connection(c_o->get_from_node_id(), c_o->get_from_port_index())}; + c_o->set_to_node_id(in_p_o->get_node_id()); + c_o->set_to_port_index(in_p_o->get_port_index()); + c_o->set_end_coordinate(in_p_o->get_global_coordinate()); + in_p_o->connect(c_o); + return; + } - if (!result) { - std::cout << "Failed to delete connection" << std::endl; - } + if (!in_p_o) { + bool result{this->delete_connection(c_o->get_from_node_id(), c_o->get_from_port_index())}; - return; // Return because we dragging an output port and dropped on nothing + if (!result) { + std::cout << "Failed to delete connection" << std::endl; } - bool result {vs->can_connect_nodes(c_o->get_from_node_id(), c_o->get_from_port_index(), in_p_o->get_node_id(), in_p_o->get_port_index())}; + return; // Return because we dragging an output port and dropped on nothing + } - if (!result) { - bool result {this->delete_connection(c_o->get_from_node_id(), c_o->get_from_port_index())}; + bool result{vs->can_connect_nodes(c_o->get_from_node_id(), c_o->get_from_port_index(), in_p_o->get_node_id(), + in_p_o->get_port_index())}; - if (!result) { - std::cout << "Failed to delete connection" << std::endl; - } + if (!result) { + bool result{this->delete_connection(c_o->get_from_node_id(), c_o->get_from_port_index())}; - return; + if (!result) { + std::cout << "Failed to delete connection" << std::endl; } - // Connect the nodes - result = vs->connect_nodes(c_o->get_from_node_id(), c_o->get_from_port_index(), in_p_o->get_node_id(), in_p_o->get_port_index()); + return; + } - if (!result) { - bool result {this->delete_connection(c_o->get_from_node_id(), c_o->get_from_port_index())}; + // Connect the nodes + result = vs->connect_nodes(c_o->get_from_node_id(), c_o->get_from_port_index(), in_p_o->get_node_id(), + in_p_o->get_port_index()); - if (!result) { - std::cout << "Failed to delete connection" << std::endl; - } + if (!result) { + bool result{this->delete_connection(c_o->get_from_node_id(), c_o->get_from_port_index())}; - return; + if (!result) { + std::cout << "Failed to delete connection" << std::endl; } - c_o->set_to_node_id(in_p_o->get_node_id()); - c_o->set_to_port_index(in_p_o->get_port_index()); - c_o->set_end_coordinate(in_p_o->get_global_coordinate()); - in_p_o->connect(c_o); + return; + } + + c_o->set_to_node_id(in_p_o->get_node_id()); + c_o->set_to_port_index(in_p_o->get_port_index()); + c_o->set_end_coordinate(in_p_o->get_global_coordinate()); + in_p_o->connect(c_o); } /**********************************************************************/ @@ -1082,299 +1172,263 @@ void VisualShaderGraphicsScene::on_port_dropped(QGraphicsObject* port, const QPo // Public functions ////////////////////////////// -VisualShaderGraphicsView::VisualShaderGraphicsView(VisualShaderGraphicsScene *scene, QWidget *parent) : QGraphicsView(scene, parent), - context_menu(nullptr), - create_node_action(nullptr), - delete_node_action(nullptr), - zoom_in_action(nullptr), - reset_zoom_action(nullptr), - zoom_out_action(nullptr) { - setDragMode(QGraphicsView::ScrollHandDrag); - setRenderHint(QPainter::Antialiasing); - - setBackgroundBrush(this->background_color); - - setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); +VisualShaderGraphicsView::VisualShaderGraphicsView(VisualShaderGraphicsScene* scene, QWidget* parent) + : QGraphicsView(scene, parent), + scene(scene), + context_menu(nullptr), + create_node_action(nullptr), + zoom_in_action(nullptr), + reset_zoom_action(nullptr), + zoom_out_action(nullptr) { + setDragMode(QGraphicsView::ScrollHandDrag); + setRenderHint(QPainter::Antialiasing); - setTransformationAnchor(QGraphicsView::AnchorUnderMouse); + setBackgroundBrush(this->background_color); - setCacheMode(QGraphicsView::CacheBackground); - setViewportUpdateMode(QGraphicsView::BoundingRectViewportUpdate); + setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - // Allow dezooming 8 times from the default zoom level. - zoom_min = (1.0f / std::pow(zoom_step, 8.0f)); - // Allow zooming 4 times from the default zoom level. - zoom_max = (1.0f * std::pow(zoom_step, 4.0f)); + setTransformationAnchor(QGraphicsView::AnchorUnderMouse); - setSceneRect(rect_x, rect_y, rect_width, rect_height); + setCacheMode(QGraphicsView::CacheBackground); + setViewportUpdateMode(QGraphicsView::BoundingRectViewportUpdate); - reset_zoom(); + // Allow dezooming 8 times from the default zoom level. + zoom_min = (1.0f / std::pow(zoom_step, 8.0f)); + // Allow zooming 4 times from the default zoom level. + zoom_max = (1.0f * std::pow(zoom_step, 4.0f)); - // Set the context menu - context_menu = new QMenu(this); - create_node_action = new QAction(QStringLiteral("Create Node"), context_menu); - QObject::connect(create_node_action, &QAction::triggered, this, &VisualShaderGraphicsView::on_create_node_action_triggered); - context_menu->addAction(create_node_action); + setSceneRect(rect_x, rect_y, rect_width, rect_height); - delete_node_action = new QAction(QStringLiteral("Delete Node"), context_menu); - delete_node_action->setShortcutContext(Qt::ShortcutContext::WidgetShortcut); - delete_node_action->setShortcut(QKeySequence(QKeySequence::Delete)); - QObject::connect(delete_node_action, &QAction::triggered, this, &VisualShaderGraphicsView::on_delete_node_action_triggered); - context_menu->addAction(delete_node_action); + reset_zoom(); - zoom_in_action = new QAction(QStringLiteral("Zoom In"), context_menu); - zoom_in_action->setShortcutContext(Qt::ShortcutContext::WidgetShortcut); - zoom_in_action->setShortcut(QKeySequence(QKeySequence::ZoomIn)); - QObject::connect(zoom_in_action, &QAction::triggered, this, &VisualShaderGraphicsView::zoom_in); - context_menu->addAction(zoom_in_action); + // Set the context menu + context_menu = new QMenu(this); + create_node_action = new QAction(QStringLiteral("Create Node"), context_menu); + QObject::connect(create_node_action, &QAction::triggered, this, + &VisualShaderGraphicsView::on_create_node_action_triggered); + context_menu->addAction(create_node_action); - reset_zoom_action = new QAction(QStringLiteral("Reset Zoom"), context_menu); - QObject::connect(reset_zoom_action, &QAction::triggered, this, &VisualShaderGraphicsView::reset_zoom); - context_menu->addAction(reset_zoom_action); + zoom_in_action = new QAction(QStringLiteral("Zoom In"), context_menu); + zoom_in_action->setShortcutContext(Qt::ShortcutContext::WidgetShortcut); + zoom_in_action->setShortcut(QKeySequence(QKeySequence::ZoomIn)); + QObject::connect(zoom_in_action, &QAction::triggered, this, &VisualShaderGraphicsView::zoom_in); + context_menu->addAction(zoom_in_action); - zoom_out_action = new QAction(QStringLiteral("Zoom Out"), context_menu); - zoom_out_action->setShortcutContext(Qt::ShortcutContext::WidgetShortcut); - zoom_out_action->setShortcut(QKeySequence(QKeySequence::ZoomOut)); - QObject::connect(zoom_out_action, &QAction::triggered, this, &VisualShaderGraphicsView::zoom_out); - context_menu->addAction(zoom_out_action); + reset_zoom_action = new QAction(QStringLiteral("Reset Zoom"), context_menu); + QObject::connect(reset_zoom_action, &QAction::triggered, this, &VisualShaderGraphicsView::reset_zoom); + context_menu->addAction(reset_zoom_action); - VisualShader* vs {scene->get_visual_shader()}; - - // Load the nodes and connections from the VisualShader - std::vector ns {vs->get_nodes()}; - for (const int& n_id : ns) { - const std::shared_ptr n {vs->get_node(n_id)}; - - if (!n) { - continue; - } - - TVector2 c {vs->get_node_coordinate(n_id)}; - - scene->add_node(n_id, n, {c.x, c.y}); - } - - std::vector cs {vs->get_connections()}; - for (const VisualShader::Connection& c : cs) { - scene->add_connection(c.from_node, c.from_port, c.to_node, c.to_port); - } + zoom_out_action = new QAction(QStringLiteral("Zoom Out"), context_menu); + zoom_out_action->setShortcutContext(Qt::ShortcutContext::WidgetShortcut); + zoom_out_action->setShortcut(QKeySequence(QKeySequence::ZoomOut)); + QObject::connect(zoom_out_action, &QAction::triggered, this, &VisualShaderGraphicsView::zoom_out); + context_menu->addAction(zoom_out_action); } -VisualShaderGraphicsView::~VisualShaderGraphicsView() { - if (zoom_out_action) delete zoom_out_action; - if (reset_zoom_action) delete reset_zoom_action; - if (zoom_in_action) delete zoom_in_action; - if (create_node_action) delete create_node_action; - if (context_menu) delete context_menu; -} +VisualShaderGraphicsView::~VisualShaderGraphicsView() {} ////////////////////////////// // Private slots ////////////////////////////// void VisualShaderGraphicsView::on_create_node_action_triggered() { - -} + VisualShaderEditor* editor {scene->get_editor()}; -void VisualShaderGraphicsView::on_delete_node_action_triggered() { - + Q_EMIT editor->on_create_node_dialog_requested(this->last_context_menu_coordinate); } void VisualShaderGraphicsView::zoom_in() { - const float factor {std::pow(zoom_step, zoom)}; + const float factor{std::pow(zoom_step, zoom)}; - QTransform t {transform()}; - t.scale(factor, factor); - if (t.m11() >= zoom_max) { - return; - } + QTransform t{transform()}; + t.scale(factor, factor); + if (t.m11() >= zoom_max) { + return; + } - scale(factor, factor); - Q_EMIT zoom_changed(transform().m11()); + scale(factor, factor); + Q_EMIT zoom_changed(transform().m11()); } void VisualShaderGraphicsView::reset_zoom() { - if ((float)transform().m11() == zoom) { - return; - } + if ((float)transform().m11() == zoom) { + return; + } - resetTransform(); // Reset the zoom level to 1.0f - Q_EMIT zoom_changed(transform().m11()); + resetTransform(); // Reset the zoom level to 1.0f + Q_EMIT zoom_changed(transform().m11()); } void VisualShaderGraphicsView::zoom_out() { - const float factor {std::pow(zoom_step, -1.0f * zoom)}; + const float factor{std::pow(zoom_step, -1.0f * zoom)}; - QTransform t {transform()}; - t.scale(factor, factor); - if (t.m11() <= zoom_min) { - return; - } + QTransform t{transform()}; + t.scale(factor, factor); + if (t.m11() <= zoom_min) { + return; + } - scale(factor, factor); - Q_EMIT zoom_changed(transform().m11()); + scale(factor, factor); + Q_EMIT zoom_changed(transform().m11()); } ////////////////////////////// // Private functions ////////////////////////////// -void VisualShaderGraphicsView::drawBackground(QPainter *painter, const QRectF &r) { - QGraphicsView::drawBackground(painter, r); +void VisualShaderGraphicsView::drawBackground(QPainter* painter, const QRectF& r) { + QGraphicsView::drawBackground(painter, r); - std::function draw_grid = [&](float grid_step) { - QRect window_rect {this->rect()}; + std::function draw_grid = [&](float grid_step) { + QRect window_rect{this->rect()}; - QPointF tl {mapToScene(window_rect.topLeft())}; - QPointF br {mapToScene(window_rect.bottomRight())}; + QPointF tl{mapToScene(window_rect.topLeft())}; + QPointF br{mapToScene(window_rect.bottomRight())}; - float left {std::floor((float)tl.x() / grid_step)}; - float right {std::ceil((float)br.x() / grid_step)}; - float bottom {std::floor((float)tl.y() / grid_step)}; - float top {std::ceil((float)br.y() / grid_step)}; + float left{std::floor((float)tl.x() / grid_step)}; + float right{std::ceil((float)br.x() / grid_step)}; + float bottom{std::floor((float)tl.y() / grid_step)}; + float top{std::ceil((float)br.y() / grid_step)}; - // Vertical lines - for (int xi {(int)left}; xi <= (int)right; ++xi) { - QLineF line(xi * grid_step, bottom * grid_step, xi * grid_step, top * grid_step); - painter->drawLine(line); - } + // Vertical lines + for (int xi{(int)left}; xi <= (int)right; ++xi) { + QLineF line(xi * grid_step, bottom * grid_step, xi * grid_step, top * grid_step); + painter->drawLine(line); + } - // Horizontal lines - for (int yi {(int)bottom}; yi <= (int)top; ++yi) { - QLineF line(left * grid_step, yi * grid_step, right * grid_step, yi * grid_step); - painter->drawLine(line); - } - }; + // Horizontal lines + for (int yi{(int)bottom}; yi <= (int)top; ++yi) { + QLineF line(left * grid_step, yi * grid_step, right * grid_step, yi * grid_step); + painter->drawLine(line); + } + }; - QPen fine_pen(this->fine_grid_color, 1.0f); - painter->setPen(fine_pen); - draw_grid(15.0f); + QPen fine_pen(this->fine_grid_color, 1.0f); + painter->setPen(fine_pen); + draw_grid(15.0f); - QPen coarse_pen(this->coarse_grid_color, 1.0f); - painter->setPen(coarse_pen); - draw_grid(150.0f); + QPen coarse_pen(this->coarse_grid_color, 1.0f); + painter->setPen(coarse_pen); + draw_grid(150.0f); } -void VisualShaderGraphicsView::contextMenuEvent(QContextMenuEvent *event) { - QGraphicsView::contextMenuEvent(event); +void VisualShaderGraphicsView::contextMenuEvent(QContextMenuEvent* event) { + QGraphicsView::contextMenuEvent(event); - if (itemAt(event->pos())) { - return; - } + // اتاكد انها مش node + if (itemAt(event->pos())) { + return; + } - QPointF scene_coordinate {mapToScene(event->pos())}; + this->last_context_menu_coordinate = (QPointF)event->globalPos(); - context_menu->exec(event->globalPos()); + context_menu->exec({(int)last_context_menu_coordinate.x(), (int)last_context_menu_coordinate.y()}); } -void VisualShaderGraphicsView::wheelEvent(QWheelEvent *event) { - float t_zoom {(float)transform().m11()}; +void VisualShaderGraphicsView::wheelEvent(QWheelEvent* event) { + float t_zoom{(float)transform().m11()}; - const QPoint delta {event->angleDelta()}; + const QPoint delta{event->angleDelta()}; - if (delta.y() == 0) { - event->ignore(); - return; - } + if (delta.y() == 0) { + event->ignore(); + return; + } - if (delta.y() > 0 && (std::abs(t_zoom - zoom_max) > std::numeric_limits::epsilon())) { - zoom_in(); - } else if (delta.y() < 0 && (std::abs(t_zoom - zoom_min) > std::numeric_limits::epsilon())) { - zoom_out(); - } else { - event->ignore(); - } + if (delta.y() > 0 && (std::abs(t_zoom - zoom_max) > std::numeric_limits::epsilon())) { + zoom_in(); + } else if (delta.y() < 0 && (std::abs(t_zoom - zoom_min) > std::numeric_limits::epsilon())) { + zoom_out(); + } else { + event->ignore(); + } } -void VisualShaderGraphicsView::mousePressEvent(QMouseEvent *event) { - QGraphicsView::mousePressEvent(event); +void VisualShaderGraphicsView::mousePressEvent(QMouseEvent* event) { + QGraphicsView::mousePressEvent(event); - switch (event->button()) { - case Qt::LeftButton: - last_click_coordinate = mapToScene(event->pos()); - break; - default: - break; - } + switch (event->button()) { + case Qt::LeftButton: + last_click_coordinate = mapToScene(event->pos()); + break; + default: + break; + } } -void VisualShaderGraphicsView::mouseMoveEvent(QMouseEvent *event) { - QGraphicsView::mouseMoveEvent(event); - - switch (event->buttons()) { - case Qt::LeftButton: - { - QPointF current_coordinate {mapToScene(event->pos())}; - QPointF difference {last_click_coordinate - current_coordinate}; - setSceneRect(sceneRect().translated(difference.x(), difference.y())); - last_click_coordinate = current_coordinate; - } - break; - default: - break; - } -} +void VisualShaderGraphicsView::mouseMoveEvent(QMouseEvent* event) { + QGraphicsView::mouseMoveEvent(event); -void VisualShaderGraphicsView::mouseReleaseEvent(QMouseEvent *event) { - QGraphicsView::mouseReleaseEvent(event); + switch (event->buttons()) { + case Qt::LeftButton: { + QPointF current_coordinate{mapToScene(event->pos())}; + QPointF difference{last_click_coordinate - current_coordinate}; + setSceneRect(sceneRect().translated(difference.x(), difference.y())); + last_click_coordinate = current_coordinate; + } break; + default: + break; + } } -void VisualShaderGraphicsView::showEvent(QShowEvent *event) { - QGraphicsView::showEvent(event); +void VisualShaderGraphicsView::mouseReleaseEvent(QMouseEvent* event) { QGraphicsView::mouseReleaseEvent(event); } - move_view_to_fit_items(); +void VisualShaderGraphicsView::showEvent(QShowEvent* event) { + QGraphicsView::showEvent(event); + + move_view_to_fit_items(); } void VisualShaderGraphicsView::move_view_to_fit_items() { - if (!scene()) { - return; - } + if (!scene) { + return; + } - if (scene()->items().isEmpty()) { - return; - } + if (scene->items().isEmpty()) { + return; + } - std::cout << "Changing view port to fit items..." << std::endl; + std::cout << "Changing view port to fit items..." << std::endl; - QRectF items_bounding_rect {scene()->itemsBoundingRect()}; - items_bounding_rect.adjust(-fit_in_view_margin, -fit_in_view_margin, fit_in_view_margin, fit_in_view_margin); + QRectF items_bounding_rect{scene->itemsBoundingRect()}; + items_bounding_rect.adjust(-fit_in_view_margin, -fit_in_view_margin, fit_in_view_margin, fit_in_view_margin); - QPointF scene_tl {this->scene()->sceneRect().topLeft()}; - QPointF scene_br {this->scene()->sceneRect().bottomRight()}; - QPointF items_tl {items_bounding_rect.topLeft()}; - QPointF items_br {items_bounding_rect.bottomRight()}; + QPointF scene_tl{scene->sceneRect().topLeft()}; + QPointF scene_br{scene->sceneRect().bottomRight()}; + QPointF items_tl{items_bounding_rect.topLeft()}; + QPointF items_br{items_bounding_rect.bottomRight()}; - // Make sure the items bounding rect is inside the scene rect - if (items_tl.x() < scene_tl.x()) { - items_bounding_rect.setLeft(scene_tl.x()); - } + // Make sure the items bounding rect is inside the scene rect + if (items_tl.x() < scene_tl.x()) { + items_bounding_rect.setLeft(scene_tl.x()); + } - if (items_tl.y() > scene_tl.y()) { - items_bounding_rect.setTop(scene_tl.y()); - } + if (items_tl.y() > scene_tl.y()) { + items_bounding_rect.setTop(scene_tl.y()); + } - if (items_br.x() > scene_br.x()) { - items_bounding_rect.setRight(scene_br.x()); - } + if (items_br.x() > scene_br.x()) { + items_bounding_rect.setRight(scene_br.x()); + } - if (items_br.y() < scene_br.y()) { - items_bounding_rect.setBottom(scene_br.y()); - } + if (items_br.y() < scene_br.y()) { + items_bounding_rect.setBottom(scene_br.y()); + } - fitInView(items_bounding_rect, Qt::KeepAspectRatio); + fitInView(items_bounding_rect, Qt::KeepAspectRatio); - centerOn(items_bounding_rect.center()); + centerOn(items_bounding_rect.center()); - if ((float)transform().m11() > zoom_max) { - std::cout << "Current zoom level is greater than the maximum zoom level." << std::endl; - std::cout << "Maybe due to having a very large distance between the nodes." << std::endl; - } + if ((float)transform().m11() > zoom_max) { + std::cout << "Current zoom level is greater than the maximum zoom level." << std::endl; + std::cout << "Maybe due to having a very large distance between the nodes." << std::endl; + } - if ((float)transform().m11() < zoom_min) { - std::cout << "Current zoom level is less than the minimum zoom level." << std::endl; - std::cout << "Maybe due to having all the nodes outside the scene bounds." << std::endl; - } + if ((float)transform().m11() < zoom_min) { + std::cout << "Current zoom level is less than the minimum zoom level." << std::endl; + std::cout << "Maybe due to having all the nodes outside the scene bounds." << std::endl; + } } /**********************************************************************/ @@ -1387,400 +1441,398 @@ void VisualShaderGraphicsView::move_view_to_fit_items() { /**********************************************************************/ /**********************************************************************/ -VisualShaderNodeGraphicsObject::VisualShaderNodeGraphicsObject(VisualShader* vs, const int& n_id, QGraphicsItem* parent) : QGraphicsObject(parent), - vs(vs), - n_id(n_id), - rect_width(0.0f), - caption_rect_height(0.0f), - rect_height(0.0f), - rect_margin(0.0f), - rect_padding(0.0f) { - setFlag(QGraphicsItem::ItemDoesntPropagateOpacityToChildren, true); - setFlag(QGraphicsItem::ItemIsFocusable, true); - setFlag(QGraphicsItem::ItemIsMovable, true); - setFlag(QGraphicsItem::ItemIsSelectable, true); - setFlag(QGraphicsItem::ItemSendsScenePositionChanges, true); - - setCacheMode(QGraphicsItem::DeviceCoordinateCache); - - setVisible(true); - setOpacity(this->opacity); - - setZValue(0); - - TVector2 coordinate {vs->get_node_coordinate(n_id)}; - - setPos(coordinate.x, coordinate.y); +VisualShaderNodeGraphicsObject::VisualShaderNodeGraphicsObject(const int& n_id, + const QPointF& coordinate, + const std::shared_ptr& node, + QGraphicsItem* parent) : QGraphicsObject(parent), + n_id(n_id), + coordinate(coordinate), + node(node), + context_menu(nullptr), + delete_node_action(nullptr), + rect_width(0.0f), + caption_rect_height(0.0f), + rect_height(0.0f), + rect_margin(0.0f), + rect_padding(0.0f) { + setFlag(QGraphicsItem::ItemDoesntPropagateOpacityToChildren, true); + setFlag(QGraphicsItem::ItemIsFocusable, true); + setFlag(QGraphicsItem::ItemIsMovable, true); + setFlag(QGraphicsItem::ItemIsSelectable, true); + setFlag(QGraphicsItem::ItemSendsScenePositionChanges, true); + + setCacheMode(QGraphicsItem::DeviceCoordinateCache); + + setVisible(true); + setOpacity(this->opacity); + + setZValue(0); + + setPos(coordinate.x(), coordinate.y()); + + // Set the context menu + context_menu = new QMenu(); + delete_node_action = new QAction(QStringLiteral("Delete Node"), context_menu); + delete_node_action->setShortcutContext(Qt::ShortcutContext::WidgetShortcut); + delete_node_action->setShortcut(QKeySequence(QKeySequence::Delete)); + QObject::connect(delete_node_action, &QAction::triggered, this, + &VisualShaderNodeGraphicsObject::on_delete_node_action_triggered); + context_menu->addAction(delete_node_action); } VisualShaderNodeGraphicsObject::~VisualShaderNodeGraphicsObject() { - + if (context_menu) delete context_menu; } -VisualShaderInputPortGraphicsObject* VisualShaderNodeGraphicsObject::get_input_port_graphics_object(const int& p_index) const { - if (in_port_graphics_objects.find(p_index) != in_port_graphics_objects.end()) { - return in_port_graphics_objects.at(p_index); - } - - return nullptr; +void VisualShaderNodeGraphicsObject::on_delete_node_action_triggered() { + Q_EMIT node_deleted(n_id); } -VisualShaderOutputPortGraphicsObject* VisualShaderNodeGraphicsObject::get_output_port_graphics_object(const int& p_index) const { - if (out_port_graphics_objects.find(p_index) != out_port_graphics_objects.end()) { - return out_port_graphics_objects.at(p_index); - } +VisualShaderInputPortGraphicsObject* VisualShaderNodeGraphicsObject::get_input_port_graphics_object( + const int& p_index) const { + if (in_port_graphics_objects.find(p_index) != in_port_graphics_objects.end()) { + return in_port_graphics_objects.at(p_index); + } - return nullptr; + return nullptr; } -QRectF VisualShaderNodeGraphicsObject::boundingRect() const { - const std::shared_ptr n {vs->get_node(n_id)}; +VisualShaderOutputPortGraphicsObject* VisualShaderNodeGraphicsObject::get_output_port_graphics_object( + const int& p_index) const { + if (out_port_graphics_objects.find(p_index) != out_port_graphics_objects.end()) { + return out_port_graphics_objects.at(p_index); + } - if (!n) { - return QRectF(); - } + return nullptr; +} - QFont f("Arial", caption_font_size); - f.setBold(true); - QFontMetrics fm(f); +QRectF VisualShaderNodeGraphicsObject::boundingRect() const { + QFont f("Arial", caption_font_size); + f.setBold(true); + QFontMetrics fm(f); - QString caption {QString::fromStdString(n->get_caption())}; + QString caption{QString::fromStdString(node->get_caption())}; - rect_width = (float)(fm.horizontalAdvance(caption, caption.length()) + 20.0f); - caption_rect_height = (float)((fm.height()) + 30.0f); + rect_width = (float)(fm.horizontalAdvance(caption, caption.length()) + 20.0f); + caption_rect_height = (float)((fm.height()) + 30.0f); - int max_num_ports {qMax(n->get_input_port_count(), n->get_output_port_count())}; + int max_num_ports{qMax(node->get_input_port_count(), node->get_output_port_count())}; - // Calculate the height of the node - float t_rect_h {caption_rect_height}; + // Calculate the height of the node + float t_rect_h{caption_rect_height}; - t_rect_h += body_rect_header_height; // Header - if (max_num_ports >= 0) { - t_rect_h += (float)(max_num_ports - 1) * body_rect_port_step; // Ports - } - t_rect_h += body_rect_footer_height; // Footer + t_rect_h += body_rect_header_height; // Header + if (max_num_ports >= 0) { + t_rect_h += (float)(max_num_ports - 1) * body_rect_port_step; // Ports + } + t_rect_h += body_rect_footer_height; // Footer - rect_height = t_rect_h; + rect_height = t_rect_h; - QRectF r({0.0f, 0.0f}, QSizeF(rect_width, rect_height)); + QRectF r({0.0f, 0.0f}, QSizeF(rect_width, rect_height)); - // Calculate the margin - this->rect_margin = rect_width * 0.1f; + // Calculate the margin + this->rect_margin = rect_width * 0.1f; - // Calculate the rect padding - // We add a safe area around the rect to make it easier to get an accurate coordinate of the size - this->rect_padding = rect_width * 0.15f; + // Calculate the rect padding + // We add a safe area around the rect to make it easier to get an accurate coordinate of the size + this->rect_padding = rect_width * 0.15f; - r.adjust(-rect_margin - rect_padding, -rect_margin - rect_padding, - rect_margin + rect_padding, rect_margin + rect_padding); + r.adjust(-rect_margin - rect_padding, -rect_margin - rect_padding, rect_margin + rect_padding, + rect_margin + rect_padding); - return r; + return r; } -void VisualShaderNodeGraphicsObject::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { - const std::shared_ptr n {vs->get_node(n_id)}; - - if (!n) { - return; - } +void VisualShaderNodeGraphicsObject::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) { + painter->setClipRect(option->exposedRect); - int n_id {vs->find_node_id(n)}; + // Get the rect without the padding + QRectF r{this->boundingRect()}; + r.adjust(rect_padding, rect_padding, -rect_padding, -rect_padding); - if (n_id == (int)VisualShader::NODE_ID_INVALID) { - return; + { + // Draw Node Rect + QColor rect_color; + if (isSelected()) { + rect_color = this->normal_boundary_color; + } else { + rect_color = this->selected_boundary_color; } - painter->setClipRect(option->exposedRect); - - // Get the rect without the padding - QRectF r {this->boundingRect()}; - r.adjust(rect_padding, rect_padding, -rect_padding, -rect_padding); - - { - // Draw Node Rect - QColor rect_color; - if (isSelected()) { - rect_color = this->normal_boundary_color; - } else { - rect_color = this->selected_boundary_color; - } + QPen p(rect_color, this->pen_width); + painter->setPen(p); - QPen p(rect_color, this->pen_width); - painter->setPen(p); + painter->setBrush(this->fill_color); - painter->setBrush(this->fill_color); - - painter->drawRoundedRect(r, this->corner_radius, this->corner_radius); - } + painter->drawRoundedRect(r, this->corner_radius, this->corner_radius); + } - // Add the margin to the rect - r.adjust(rect_margin, rect_margin, -rect_margin, -rect_margin); + // Add the margin to the rect + r.adjust(rect_margin, rect_margin, -rect_margin, -rect_margin); - float rect_x {(float)r.topLeft().x()}; - float rect_y {(float)r.topLeft().y()}; + float rect_x{(float)r.topLeft().x()}; + float rect_y{(float)r.topLeft().y()}; - float rect_w {(float)r.width()}; - float rect_h {(float)r.height()}; + float rect_w{(float)r.width()}; + float rect_h{(float)r.height()}; - float min_side {qMin(rect_w, rect_h)}; + float min_side{qMin(rect_w, rect_h)}; - QRectF caption_rect(rect_x, rect_y, rect_w, caption_rect_height); + QRectF caption_rect(rect_x, rect_y, rect_w, caption_rect_height); - { - // Draw Caption - QString caption {QString::fromStdString(n->get_caption())}; + { + // Draw Caption + QString caption{QString::fromStdString(node->get_caption())}; - QFont t_f {painter->font()}; + QFont t_f{painter->font()}; - QFont f("Arial", caption_font_size); - f.setBold(true); - QFontMetrics fm(f); - painter->setFont(f); + QFont f("Arial", caption_font_size); + f.setBold(true); + QFontMetrics fm(f); + painter->setFont(f); - // Calculate the coordinates of the caption - float x {(float)(caption_rect.center().x() - (float)fm.horizontalAdvance(caption) * 0.5f)}; + // Calculate the coordinates of the caption + float x{(float)(caption_rect.center().x() - (float)fm.horizontalAdvance(caption) * 0.5f)}; - // Instead of subtracting, add the ascent to properly align text within the rect - float y {(float)(caption_rect.center().y() + (float)((fm.ascent() + fm.descent()) * 0.5f - fm.descent()))}; + // Instead of subtracting, add the ascent to properly align text within the rect + float y{(float)(caption_rect.center().y() + (float)((fm.ascent() + fm.descent()) * 0.5f - fm.descent()))}; - QPointF coordinate {x, y}; + QPointF coordinate{x, y}; - painter->setPen(this->font_color); - painter->drawText(coordinate, caption); + painter->setPen(this->font_color); + painter->drawText(coordinate, caption); - painter->setFont(t_f); // Reset the font - } + painter->setFont(t_f); // Reset the font + } - QPointF caption_rect_bl {caption_rect.bottomLeft()}; - QPointF first_in_port_coordinate {caption_rect_bl.x(), caption_rect_bl.y() + body_rect_header_height}; + QPointF caption_rect_bl{caption_rect.bottomLeft()}; + QPointF first_in_port_coordinate{caption_rect_bl.x(), caption_rect_bl.y() + body_rect_header_height}; - // Correct X coordinate: Remove the margin - first_in_port_coordinate.setX((float)first_in_port_coordinate.x() - this->rect_margin); + // Correct X coordinate: Remove the margin + first_in_port_coordinate.setX((float)first_in_port_coordinate.x() - this->rect_margin); - { - // Draw Input Ports - int in_port_count {n->get_input_port_count()}; + { + // Draw Input Ports + int in_port_count{node->get_input_port_count()}; - for (unsigned i {0}; i < in_port_count; ++i) { - QPointF port_coordinate {first_in_port_coordinate.x(), first_in_port_coordinate.y() + body_rect_port_step * i}; + for (unsigned i{0}; i < in_port_count; ++i) { + QPointF port_coordinate{first_in_port_coordinate.x(), first_in_port_coordinate.y() + body_rect_port_step * i}; - QRectF port_rect(port_coordinate.x(), port_coordinate.y(), min_side * 0.1f, min_side * 0.1f); + QRectF port_rect(port_coordinate.x(), port_coordinate.y(), min_side * 0.1f, min_side * 0.1f); - // Adjust the port rect to be centered - port_rect.adjust(-port_rect.width() * 0.5f, -port_rect.height() * 0.5f, -port_rect.width() * 0.5f, -port_rect.height() * 0.5f); + // Adjust the port rect to be centered + port_rect.adjust(-port_rect.width() * 0.5f, -port_rect.height() * 0.5f, -port_rect.width() * 0.5f, + -port_rect.height() * 0.5f); - // Draw caption - QString p_n {QString::fromStdString(n->get_input_port_name(i))}; + // Draw caption + QString p_n{QString::fromStdString(node->get_input_port_name(i))}; - if (!p_n.isEmpty()) { - QFont t_f {painter->font()}; + if (!p_n.isEmpty()) { + QFont t_f{painter->font()}; - QFont f("Arial", port_caption_font_size); - QFontMetrics fm(f); - painter->setFont(f); + QFont f("Arial", port_caption_font_size); + QFontMetrics fm(f); + painter->setFont(f); - float x {rect_x + 5.0f}; + float x{rect_x + 5.0f}; - float y {(float)(port_rect.center().y()) + (float)((fm.ascent() + fm.descent()) * 0.5f - fm.descent())}; + float y{(float)(port_rect.center().y()) + (float)((fm.ascent() + fm.descent()) * 0.5f - fm.descent())}; - QPointF coordinate {x, y}; + QPointF coordinate{x, y}; - painter->setPen(this->font_color); - painter->drawText(coordinate, p_n); + painter->setPen(this->font_color); + painter->drawText(coordinate, p_n); - painter->setFont(t_f); // Reset the font - } + painter->setFont(t_f); // Reset the font + } - if (in_port_graphics_objects.find(i) != in_port_graphics_objects.end()) - continue; + if (in_port_graphics_objects.find(i) != in_port_graphics_objects.end()) continue; - // Draw the port - VisualShaderInputPortGraphicsObject* p_o {new VisualShaderInputPortGraphicsObject(port_rect, n_id, i, this)}; - in_port_graphics_objects[i] = p_o; + // Draw the port + VisualShaderInputPortGraphicsObject* p_o{new VisualShaderInputPortGraphicsObject(port_rect, n_id, i, this)}; + in_port_graphics_objects[i] = p_o; - // Connect the signals - // QObject::connect(p_o, &VisualShaderInputPortGraphicsObject::port_pressed, dynamic_cast(scene()), &VisualShaderGraphicsScene::on_port_pressed); - QObject::connect(p_o, &VisualShaderInputPortGraphicsObject::port_dragged, dynamic_cast(scene()), &VisualShaderGraphicsScene::on_port_dragged); - QObject::connect(p_o, &VisualShaderInputPortGraphicsObject::port_dropped, dynamic_cast(scene()), &VisualShaderGraphicsScene::on_port_dropped); - } + // Connect the signals + QObject::connect(p_o, &VisualShaderInputPortGraphicsObject::port_pressed, dynamic_cast(scene()), &VisualShaderGraphicsScene::on_port_pressed); + QObject::connect(p_o, &VisualShaderInputPortGraphicsObject::port_dragged, + dynamic_cast(scene()), &VisualShaderGraphicsScene::on_port_dragged); + QObject::connect(p_o, &VisualShaderInputPortGraphicsObject::port_dropped, + dynamic_cast(scene()), &VisualShaderGraphicsScene::on_port_dropped); } + } - QPointF caption_rect_br {caption_rect.bottomRight()}; - QPointF first_out_port_coordinate {caption_rect_br.x(), caption_rect_br.y() + body_rect_header_height}; + QPointF caption_rect_br{caption_rect.bottomRight()}; + QPointF first_out_port_coordinate{caption_rect_br.x(), caption_rect_br.y() + body_rect_header_height}; - // Correct X coordinate: Remove the margin - first_out_port_coordinate.setX((float)first_out_port_coordinate.x() + this->rect_margin); + // Correct X coordinate: Remove the margin + first_out_port_coordinate.setX((float)first_out_port_coordinate.x() + this->rect_margin); - { - // Draw Output Ports - int out_port_count {n->get_output_port_count()}; + { + // Draw Output Ports + int out_port_count{node->get_output_port_count()}; - for (unsigned i {0}; i < out_port_count; ++i) { - QPointF port_coordinate {first_out_port_coordinate.x(), first_out_port_coordinate.y() + body_rect_port_step * i}; + for (unsigned i{0}; i < out_port_count; ++i) { + QPointF port_coordinate{first_out_port_coordinate.x(), first_out_port_coordinate.y() + body_rect_port_step * i}; - QRectF port_rect(port_coordinate.x(), port_coordinate.y(), min_side * 0.1f, min_side * 0.1f); + QRectF port_rect(port_coordinate.x(), port_coordinate.y(), min_side * 0.1f, min_side * 0.1f); - // Adjust the port rect to be centered - port_rect.adjust(-port_rect.width() * 0.5f, -port_rect.height() * 0.5f, -port_rect.width() * 0.5f, -port_rect.height() * 0.5f); + // Adjust the port rect to be centered + port_rect.adjust(-port_rect.width() * 0.5f, -port_rect.height() * 0.5f, -port_rect.width() * 0.5f, + -port_rect.height() * 0.5f); - // Draw caption - QString p_n {QString::fromStdString(n->get_output_port_name(i))}; + // Draw caption + QString p_n{QString::fromStdString(node->get_output_port_name(i))}; - if (!p_n.isEmpty()) { - QFont t_f {painter->font()}; + if (!p_n.isEmpty()) { + QFont t_f{painter->font()}; - QFont f("Arial", port_caption_font_size); - QFontMetrics fm(f); - painter->setFont(f); + QFont f("Arial", port_caption_font_size); + QFontMetrics fm(f); + painter->setFont(f); - float x {rect_x + rect_w - (float)fm.horizontalAdvance(p_n) - 5.0f}; + float x{rect_x + rect_w - (float)fm.horizontalAdvance(p_n) - 5.0f}; - float y {(float)(port_rect.center().y()) + (float)((fm.ascent() + fm.descent()) * 0.5f - fm.descent())}; + float y{(float)(port_rect.center().y()) + (float)((fm.ascent() + fm.descent()) * 0.5f - fm.descent())}; - QPointF coordinate {x, y}; + QPointF coordinate{x, y}; - painter->setPen(this->font_color); - painter->drawText(coordinate, p_n); + painter->setPen(this->font_color); + painter->drawText(coordinate, p_n); - painter->setFont(t_f); // Reset the font - } + painter->setFont(t_f); // Reset the font + } - if (out_port_graphics_objects.find(i) != out_port_graphics_objects.end()) - continue; + if (out_port_graphics_objects.find(i) != out_port_graphics_objects.end()) continue; - // Draw the port - VisualShaderOutputPortGraphicsObject* p_o {new VisualShaderOutputPortGraphicsObject(port_rect, n_id, i, this)}; - out_port_graphics_objects[i] = p_o; + // Draw the port + VisualShaderOutputPortGraphicsObject* p_o{new VisualShaderOutputPortGraphicsObject(port_rect, n_id, i, this)}; + out_port_graphics_objects[i] = p_o; - // Connect the signals - // QObject::connect(p_o, &VisualShaderOutputPortGraphicsObject::port_pressed, dynamic_cast(scene()), &VisualShaderGraphicsScene::on_port_pressed); - QObject::connect(p_o, &VisualShaderOutputPortGraphicsObject::port_dragged, dynamic_cast(scene()), &VisualShaderGraphicsScene::on_port_dragged); - QObject::connect(p_o, &VisualShaderOutputPortGraphicsObject::port_dropped, dynamic_cast(scene()), &VisualShaderGraphicsScene::on_port_dropped); - } - } + // Connect the signals + QObject::connect(p_o, &VisualShaderOutputPortGraphicsObject::port_pressed, dynamic_cast(scene()), &VisualShaderGraphicsScene::on_port_pressed); + QObject::connect(p_o, &VisualShaderOutputPortGraphicsObject::port_dragged, + dynamic_cast(scene()), &VisualShaderGraphicsScene::on_port_dragged); + QObject::connect(p_o, &VisualShaderOutputPortGraphicsObject::port_dropped, + dynamic_cast(scene()), &VisualShaderGraphicsScene::on_port_dropped); + } + } } +QVariant VisualShaderNodeGraphicsObject::itemChange(GraphicsItemChange change, const QVariant& value) { + if (scene() && change == ItemScenePositionHasChanged) { + Q_EMIT dynamic_cast(scene())->node_moved(n_id, pos()); + } -QVariant VisualShaderNodeGraphicsObject::itemChange(GraphicsItemChange change, const QVariant &value) { - if (scene() && change == ItemScenePositionHasChanged) { - Q_EMIT dynamic_cast(scene())->node_moved(n_id, pos()); - } + return QGraphicsObject::itemChange(change, value); +} + +void VisualShaderNodeGraphicsObject::contextMenuEvent(QGraphicsSceneContextMenuEvent* event) { + QGraphicsObject::contextMenuEvent(event); - return QGraphicsObject::itemChange(change, value); + context_menu->exec(event->screenPos()); } -VisualShaderInputPortGraphicsObject::VisualShaderInputPortGraphicsObject(const QRectF& rect, - const int& n_id, - const int& p_index, - QGraphicsItem* parent) : QGraphicsObject(parent), - rect(rect), - n_id(n_id), - p_index(p_index), - connection_graphics_object(nullptr) { - setFlag(QGraphicsItem::ItemDoesntPropagateOpacityToChildren, true); - setFlag(QGraphicsItem::ItemIsFocusable, true); - setFlag(QGraphicsItem::ItemIsSelectable, true); +VisualShaderInputPortGraphicsObject::VisualShaderInputPortGraphicsObject(const QRectF& rect, const int& n_id, + const int& p_index, QGraphicsItem* parent) + : QGraphicsObject(parent), rect(rect), n_id(n_id), p_index(p_index), connection_graphics_object(nullptr) { + setFlag(QGraphicsItem::ItemDoesntPropagateOpacityToChildren, true); + setFlag(QGraphicsItem::ItemIsFocusable, true); + setFlag(QGraphicsItem::ItemIsSelectable, true); - setCursor(Qt::PointingHandCursor); + setCursor(Qt::PointingHandCursor); - setCacheMode(QGraphicsItem::DeviceCoordinateCache); + setCacheMode(QGraphicsItem::DeviceCoordinateCache); - setVisible(true); - setOpacity(this->opacity); + setVisible(true); + setOpacity(this->opacity); - setZValue(0); + setZValue(0); } -VisualShaderInputPortGraphicsObject::~VisualShaderInputPortGraphicsObject() { - -} +VisualShaderInputPortGraphicsObject::~VisualShaderInputPortGraphicsObject() {} QRectF VisualShaderInputPortGraphicsObject::boundingRect() const { - // rect.adjust(-padding, -padding, padding, padding); + // rect.adjust(-padding, -padding, padding, padding); - return rect; + return rect; } -void VisualShaderInputPortGraphicsObject::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { - painter->setClipRect(option->exposedRect); +void VisualShaderInputPortGraphicsObject::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, + QWidget* widget) { + painter->setClipRect(option->exposedRect); - // rect.adjust(padding, padding, -padding, -padding); - - painter->setBrush(this->connection_point_color); + // rect.adjust(padding, padding, -padding, -padding); - painter->drawEllipse(rect); + painter->setBrush(this->connection_point_color); + + painter->drawEllipse(rect); } -void VisualShaderInputPortGraphicsObject::mousePressEvent(QGraphicsSceneMouseEvent *event) { - emit port_pressed(this, event->scenePos()); - QGraphicsObject::mousePressEvent(event); +void VisualShaderInputPortGraphicsObject::mousePressEvent(QGraphicsSceneMouseEvent* event) { + emit port_pressed(this, event->scenePos()); + QGraphicsObject::mousePressEvent(event); } -void VisualShaderInputPortGraphicsObject::mouseMoveEvent(QGraphicsSceneMouseEvent *event) { - emit port_dragged(this, event->scenePos()); - QGraphicsObject::mouseMoveEvent(event); +void VisualShaderInputPortGraphicsObject::mouseMoveEvent(QGraphicsSceneMouseEvent* event) { + emit port_dragged(this, event->scenePos()); + QGraphicsObject::mouseMoveEvent(event); } -void VisualShaderInputPortGraphicsObject::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { - emit port_dropped(this, event->scenePos()); - QGraphicsObject::mouseReleaseEvent(event); +void VisualShaderInputPortGraphicsObject::mouseReleaseEvent(QGraphicsSceneMouseEvent* event) { + emit port_dropped(this, event->scenePos()); + QGraphicsObject::mouseReleaseEvent(event); } -VisualShaderOutputPortGraphicsObject::VisualShaderOutputPortGraphicsObject(const QRectF& rect, - const int& n_id, - const int& p_index, - QGraphicsItem* parent) : QGraphicsObject(parent), - rect(rect), - n_id(n_id), - p_index(p_index), - connection_graphics_object(nullptr) { - setFlag(QGraphicsItem::ItemDoesntPropagateOpacityToChildren, true); - setFlag(QGraphicsItem::ItemIsFocusable, true); - setFlag(QGraphicsItem::ItemIsSelectable, true); +VisualShaderOutputPortGraphicsObject::VisualShaderOutputPortGraphicsObject(const QRectF& rect, const int& n_id, + const int& p_index, QGraphicsItem* parent) + : QGraphicsObject(parent), rect(rect), n_id(n_id), p_index(p_index), connection_graphics_object(nullptr) { + setFlag(QGraphicsItem::ItemDoesntPropagateOpacityToChildren, true); + setFlag(QGraphicsItem::ItemIsFocusable, true); + setFlag(QGraphicsItem::ItemIsSelectable, true); - setCursor(Qt::PointingHandCursor); + setCursor(Qt::PointingHandCursor); - setCacheMode(QGraphicsItem::DeviceCoordinateCache); + setCacheMode(QGraphicsItem::DeviceCoordinateCache); - setVisible(true); - setOpacity(this->opacity); + setVisible(true); + setOpacity(this->opacity); - setZValue(0); + setZValue(0); } -VisualShaderOutputPortGraphicsObject::~VisualShaderOutputPortGraphicsObject() { - -} +VisualShaderOutputPortGraphicsObject::~VisualShaderOutputPortGraphicsObject() {} QRectF VisualShaderOutputPortGraphicsObject::boundingRect() const { - // rect.adjust(-padding, -padding, padding, padding); + // rect.adjust(-padding, -padding, padding, padding); - return rect; + return rect; } -void VisualShaderOutputPortGraphicsObject::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { - painter->setClipRect(option->exposedRect); +void VisualShaderOutputPortGraphicsObject::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, + QWidget* widget) { + painter->setClipRect(option->exposedRect); - // rect.adjust(padding, padding, -padding, -padding); - - painter->setBrush(this->connection_point_color); + // rect.adjust(padding, padding, -padding, -padding); - painter->drawEllipse(rect); + painter->setBrush(this->connection_point_color); + + painter->drawEllipse(rect); } -void VisualShaderOutputPortGraphicsObject::mousePressEvent(QGraphicsSceneMouseEvent *event) { - emit port_pressed(this, event->scenePos()); - QGraphicsObject::mousePressEvent(event); +void VisualShaderOutputPortGraphicsObject::mousePressEvent(QGraphicsSceneMouseEvent* event) { + emit port_pressed(this, event->scenePos()); + QGraphicsObject::mousePressEvent(event); } -void VisualShaderOutputPortGraphicsObject::mouseMoveEvent(QGraphicsSceneMouseEvent *event) { - emit port_dragged(this, event->scenePos()); - QGraphicsObject::mouseMoveEvent(event); +void VisualShaderOutputPortGraphicsObject::mouseMoveEvent(QGraphicsSceneMouseEvent* event) { + emit port_dragged(this, event->scenePos()); + QGraphicsObject::mouseMoveEvent(event); } -void VisualShaderOutputPortGraphicsObject::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { - emit port_dropped(this, event->scenePos()); - QGraphicsObject::mouseReleaseEvent(event); +void VisualShaderOutputPortGraphicsObject::mouseReleaseEvent(QGraphicsSceneMouseEvent* event) { + emit port_dropped(this, event->scenePos()); + QGraphicsObject::mouseReleaseEvent(event); } /**********************************************************************/ @@ -1793,259 +1845,263 @@ void VisualShaderOutputPortGraphicsObject::mouseReleaseEvent(QGraphicsSceneMouse /**********************************************************************/ /**********************************************************************/ -VisualShaderConnectionGraphicsObject::VisualShaderConnectionGraphicsObject(const int& from_n_id, - const int& from_p_index, - const QPointF& start_coordinate, - QGraphicsItem* parent) : QGraphicsObject(parent), - from_n_id(from_n_id), - from_p_index(from_p_index), - to_n_id((int)VisualShader::NODE_ID_INVALID), - to_p_index((int)VisualShader::PORT_INDEX_INVALID), - start_coordinate(start_coordinate), - end_coordinate(start_coordinate), - rect_padding(0.0f) { - setFlag(QGraphicsItem::ItemIsFocusable, true); - setFlag(QGraphicsItem::ItemIsSelectable, true); +VisualShaderConnectionGraphicsObject::VisualShaderConnectionGraphicsObject(const int& from_n_id, + const int& from_p_index, + const QPointF& start_coordinate, + QGraphicsItem* parent) + : QGraphicsObject(parent), + from_n_id(from_n_id), + from_p_index(from_p_index), + to_n_id((int)VisualShader::NODE_ID_INVALID), + to_p_index((int)VisualShader::PORT_INDEX_INVALID), + start_coordinate(start_coordinate), + end_coordinate(start_coordinate), + rect_padding(0.0f) { + setFlag(QGraphicsItem::ItemIsFocusable, true); + setFlag(QGraphicsItem::ItemIsSelectable, true); - setZValue(-1.0f); + setZValue(-1.0f); } -VisualShaderConnectionGraphicsObject::~VisualShaderConnectionGraphicsObject() { - -} +VisualShaderConnectionGraphicsObject::~VisualShaderConnectionGraphicsObject() {} QRectF VisualShaderConnectionGraphicsObject::boundingRect() const { - QRectF r {calculate_bounding_rect_from_coordinates(start_coordinate, end_coordinate)}; + QRectF r{calculate_bounding_rect_from_coordinates(start_coordinate, end_coordinate)}; - // Calculate the rect padding - // We add a safe area around the rect to make it easier to get an accurate coordinate of the size - this->rect_padding = 10.0f; + // Calculate the rect padding + // We add a safe area around the rect to make it easier to get an accurate coordinate of the size + this->rect_padding = 10.0f; - r.adjust(-rect_padding, -rect_padding, rect_padding, rect_padding); + r.adjust(-rect_padding, -rect_padding, rect_padding, rect_padding); - return r; + return r; } -void VisualShaderConnectionGraphicsObject::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { - painter->setClipRect(option->exposedRect); +void VisualShaderConnectionGraphicsObject::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, + QWidget* widget) { + painter->setClipRect(option->exposedRect); - { - QPen pen; - pen.setWidth(this->line_width); - pen.setColor(this->construction_color); - pen.setStyle(Qt::DashLine); + { + QPen pen; + pen.setWidth(this->line_width); + pen.setColor(this->construction_color); + pen.setStyle(Qt::DashLine); - painter->setPen(pen); - painter->setBrush(Qt::NoBrush); + painter->setPen(pen); + painter->setBrush(Qt::NoBrush); - std::pair control_points {calculate_control_points(start_coordinate, end_coordinate)}; + std::pair control_points{calculate_control_points(start_coordinate, end_coordinate)}; - QPainterPath cubic(start_coordinate); - cubic.cubicTo(control_points.first, control_points.second, end_coordinate); + QPainterPath cubic(start_coordinate); + cubic.cubicTo(control_points.first, control_points.second, end_coordinate); - // cubic spline - painter->drawPath(cubic); - } + // cubic spline + painter->drawPath(cubic); + } - { - // draw normal line - QPen p; - p.setWidth(this->line_width); + { + // draw normal line + QPen p; + p.setWidth(this->line_width); - const bool selected {this->isSelected()}; + const bool selected{this->isSelected()}; - std::pair control_points {calculate_control_points(start_coordinate, end_coordinate)}; + std::pair control_points{calculate_control_points(start_coordinate, end_coordinate)}; - QPainterPath cubic(start_coordinate); - cubic.cubicTo(control_points.first, control_points.second, end_coordinate); + QPainterPath cubic(start_coordinate); + cubic.cubicTo(control_points.first, control_points.second, end_coordinate); - p.setColor(this->normal_color); + p.setColor(this->normal_color); - if (selected) { - p.setColor(this->selected_color); - } + if (selected) { + p.setColor(this->selected_color); + } - painter->setPen(p); - painter->setBrush(Qt::NoBrush); + painter->setPen(p); + painter->setBrush(Qt::NoBrush); - painter->drawPath(cubic); - } + painter->drawPath(cubic); + } - painter->setBrush(this->connection_point_color); - - { - // Draw start point - QRectF start_rect(start_coordinate.x(), start_coordinate.y(), this->point_diameter, this->point_diameter); + painter->setBrush(this->connection_point_color); - // Adjust the port rect to be centered - start_rect.adjust(-start_rect.width() * 0.5f, -start_rect.height() * 0.5f, -start_rect.width() * 0.5f, -start_rect.height() * 0.5f); + { + // Draw start point + QRectF start_rect(start_coordinate.x(), start_coordinate.y(), this->point_diameter, this->point_diameter); - painter->drawEllipse(start_rect); - } + // Adjust the port rect to be centered + start_rect.adjust(-start_rect.width() * 0.5f, -start_rect.height() * 0.5f, -start_rect.width() * 0.5f, + -start_rect.height() * 0.5f); - { - // Draw end point - QRectF end_rect(end_coordinate.x(), end_coordinate.y(), this->point_diameter, this->point_diameter); + painter->drawEllipse(start_rect); + } - // Adjust the port rect to be centered - end_rect.adjust(-end_rect.width() * 0.5f, -end_rect.height() * 0.5f, -end_rect.width() * 0.5f, -end_rect.height() * 0.5f); + { + // Draw end point + QRectF end_rect(end_coordinate.x(), end_coordinate.y(), this->point_diameter, this->point_diameter); - painter->drawEllipse(end_rect); - } + // Adjust the port rect to be centered + end_rect.adjust(-end_rect.width() * 0.5f, -end_rect.height() * 0.5f, -end_rect.width() * 0.5f, + -end_rect.height() * 0.5f); + + painter->drawEllipse(end_rect); + } } int VisualShaderConnectionGraphicsObject::detect_quadrant(const QPointF& reference, const QPointF& target) const { - float relative_x {(float)(target.x() - reference.x())}; - float relative_y {(float)(target.y() - reference.y())}; - - // Note that the default coordinate system in Qt is as follows: - // - X-axis: Positive to the right, negative to the left - // - Y-axis: Positive downwards, negative upwards - - // Check if the point is on an axis or the origin - if (relative_x == 0 && relative_y == 0) { - return 0; // Stack on the reference - } else if (relative_y == 0) { - return (relative_x > 0) ? 5 : 6; // On X-axis: 5 is the +ve part while 6 is the -ve one. - } else if (relative_x == 0) { - return (relative_y < 0) ? 7 : 8; // On Y-axis: 7 is the +ve part while 8 is the -ve one. - } + float relative_x{(float)(target.x() - reference.x())}; + float relative_y{(float)(target.y() - reference.y())}; - // Determine the quadrant based on the relative coordinates - if (relative_x > 0 && relative_y < 0) { - return 1; // Quadrant I - } else if (relative_x < 0 && relative_y < 0) { - return 2; // Quadrant II - } else if (relative_x < 0 && relative_y > 0) { - return 3; // Quadrant III - } else if (relative_x > 0 && relative_y > 0) { - return 4; // Quadrant IV - } + // Note that the default coordinate system in Qt is as follows: + // - X-axis: Positive to the right, negative to the left + // - Y-axis: Positive downwards, negative upwards - // Default case (should not reach here) - return -1; + // Check if the point is on an axis or the origin + if (relative_x == 0 && relative_y == 0) { + return 0; // Stack on the reference + } else if (relative_y == 0) { + return (relative_x > 0) ? 5 : 6; // On X-axis: 5 is the +ve part while 6 is the -ve one. + } else if (relative_x == 0) { + return (relative_y < 0) ? 7 : 8; // On Y-axis: 7 is the +ve part while 8 is the -ve one. + } + + // Determine the quadrant based on the relative coordinates + if (relative_x > 0 && relative_y < 0) { + return 1; // Quadrant I + } else if (relative_x < 0 && relative_y < 0) { + return 2; // Quadrant II + } else if (relative_x < 0 && relative_y > 0) { + return 3; // Quadrant III + } else if (relative_x > 0 && relative_y > 0) { + return 4; // Quadrant IV + } + + // Default case (should not reach here) + return -1; } -QRectF VisualShaderConnectionGraphicsObject::calculate_bounding_rect_from_coordinates(const QPointF& start_coordinate, const QPointF& end_coordinate) const { - float x1 {(float)start_coordinate.x()}; - float y1 {(float)start_coordinate.y()}; - float x2 {(float)end_coordinate.x()}; - float y2 {(float)end_coordinate.y()}; - - // Calculate the expanded rect - float min_x {qMin(x1, x2)}; - float min_y {qMin(y1, y2)}; - float max_x {qMax(x1, x2)}; - float max_y {qMax(y1, y2)}; +QRectF VisualShaderConnectionGraphicsObject::calculate_bounding_rect_from_coordinates( + const QPointF& start_coordinate, const QPointF& end_coordinate) const { + float x1{(float)start_coordinate.x()}; + float y1{(float)start_coordinate.y()}; + float x2{(float)end_coordinate.x()}; + float y2{(float)end_coordinate.y()}; - QRectF r({min_x, min_y}, QSizeF(max_x - min_x, max_y - min_y)); + // Calculate the expanded rect + float min_x{qMin(x1, x2)}; + float min_y{qMin(y1, y2)}; + float max_x{qMax(x1, x2)}; + float max_y{qMax(y1, y2)}; - bool in_abnormal_region {x2 < (x1 + min_h_distance)}; + QRectF r({min_x, min_y}, QSizeF(max_x - min_x, max_y - min_y)); - float a_width_expansion {((x1 + min_h_distance) - x2) * abnormal_face_to_back_control_width_expansion_factor}; + bool in_abnormal_region{x2 < (x1 + min_h_distance)}; - if (in_abnormal_region) { - // The connection is not going from left to right normally - // Our control points will be outside the end_coordinate and start_coordinate bounding rect - // We will expand the bounding rect horizontally to make it easier to get an accurate coordinate of the size - r.adjust(-a_width_expansion, 0.0f, a_width_expansion, 0.0f); - } + float a_width_expansion{((x1 + min_h_distance) - x2) * abnormal_face_to_back_control_width_expansion_factor}; + + if (in_abnormal_region) { + // The connection is not going from left to right normally + // Our control points will be outside the end_coordinate and start_coordinate bounding rect + // We will expand the bounding rect horizontally to make it easier to get an accurate coordinate of the size + r.adjust(-a_width_expansion, 0.0f, a_width_expansion, 0.0f); + } - return r; + return r; } -std::pair VisualShaderConnectionGraphicsObject::calculate_control_points(const QPointF& start_coordinate, const QPointF& end_coordinated) const { - QPointF cp1; - QPointF cp2; - - float x1 {(float)start_coordinate.x()}; - float y1 {(float)start_coordinate.y()}; - float x2 {(float)end_coordinate.x()}; - float y2 {(float)end_coordinate.y()}; - - QRectF r {calculate_bounding_rect_from_coordinates(start_coordinate, end_coordinate)}; +std::pair VisualShaderConnectionGraphicsObject::calculate_control_points( + const QPointF& start_coordinate, const QPointF& end_coordinated) const { + QPointF cp1; + QPointF cp2; - bool in_abnormal_region {x2 < (x1 + min_h_distance)}; + float x1{(float)start_coordinate.x()}; + float y1{(float)start_coordinate.y()}; + float x2{(float)end_coordinate.x()}; + float y2{(float)end_coordinate.y()}; - int quadrant {detect_quadrant({x1, y1}, {x2, y2})}; + QRectF r{calculate_bounding_rect_from_coordinates(start_coordinate, end_coordinate)}; - float face_to_face_control_width_expansion_factor {0.8f}; - float face_to_face_control_height_expansion_factor {0.25f}; + bool in_abnormal_region{x2 < (x1 + min_h_distance)}; - float width_expansion {(x2 - x1) * face_to_face_control_width_expansion_factor}; + int quadrant{detect_quadrant({x1, y1}, {x2, y2})}; - float a_width_expansion {((x1 + min_h_distance) - x2) * abnormal_face_to_back_control_width_expansion_factor}; - float a_height_expansion {a_width_expansion * abnormal_face_to_back_control_height_expansion_factor}; + float face_to_face_control_width_expansion_factor{0.8f}; + float face_to_face_control_height_expansion_factor{0.25f}; - if (in_abnormal_region) { - r.adjust(-a_width_expansion, 0.0f, a_width_expansion, 0.0f); - } + float width_expansion{(x2 - x1) * face_to_face_control_width_expansion_factor}; - switch(quadrant) { - case 1: // Quadrant I: Normal face to back - // Find out if the connection is going from left to right normally - if (in_abnormal_region) { - // The connection is not going from left to right normally - // Our control points will be outside the end_coordinate and start_coordinate bounding rect - // We will expand the bounding rect horizontally to make it easier to get an accurate coordinate of the size - - // Here we cover cases of nodes not facing each other. - // This means we can't just send the path straight to the node. - - // Treated as inside Quadrant II - cp1 = {x1 + a_width_expansion, y1}; - cp2 = {x2 - a_width_expansion, y2}; - - } else { - // Treated as inside Quadrant I - cp1 = {x1 + width_expansion, y1}; - cp2 = {x2 - width_expansion, y2}; - } - break; - case 2: // Quadrant II: Abnormal face to back - cp1 = {x1 + a_width_expansion, y1}; - cp2 = {x2 - a_width_expansion, y2}; - break; - case 3: // Quadrant III: Abnormal face to back - cp1 = {x1 + a_width_expansion, y1 + a_height_expansion}; - cp2 = {x2 - a_width_expansion, y2 - a_height_expansion}; - break; - case 4: // Quadrant IV: Normal face to back - if (in_abnormal_region) { - // Treated as inside Quadrant III - cp1 = {x1 + a_width_expansion, y1}; - cp2 = {x2 - a_width_expansion, y2}; - } else { - // Treated as inside Quadrant IV - cp1 = {x1 + width_expansion, y1}; - cp2 = {x2 - width_expansion, y2}; - } - break; - case 5: // On +ve X-axis: Normal face to back - // Straight line - cp1 = {x1, y1}; - cp2 = {x2, y2}; - break; - case 6: // On -ve X-axis: Abnormal face to back - r.adjust(0.0f, -a_height_expansion, 0.0f, a_height_expansion); - cp1 = {x1 + a_width_expansion, y1}; - cp2 = {x2 - a_width_expansion, y2}; - break; - case 7: // On +ve Y-axis: Abnormal face to back - r.adjust(0.0f, -a_height_expansion, 0.0f, a_height_expansion); - cp1 = {x1 + a_width_expansion, y1}; - cp2 = {x2 - a_width_expansion, y2}; - break; - case 8: // On -ve Y-axis: Abnormal face to back - r.adjust(0.0f, -a_height_expansion, 0.0f, a_height_expansion); - cp1 = {x1 + a_width_expansion, y1}; - cp2 = {x2 - a_width_expansion, y2}; - break; - default: - return std::make_pair(start_coordinate, end_coordinate); - } + float a_width_expansion{((x1 + min_h_distance) - x2) * abnormal_face_to_back_control_width_expansion_factor}; + float a_height_expansion{a_width_expansion * abnormal_face_to_back_control_height_expansion_factor}; + + if (in_abnormal_region) { + r.adjust(-a_width_expansion, 0.0f, a_width_expansion, 0.0f); + } + + switch (quadrant) { + case 1: // Quadrant I: Normal face to back + // Find out if the connection is going from left to right normally + if (in_abnormal_region) { + // The connection is not going from left to right normally + // Our control points will be outside the end_coordinate and start_coordinate bounding rect + // We will expand the bounding rect horizontally to make it easier to get an accurate coordinate of the size - return std::make_pair(cp1, cp2); + // Here we cover cases of nodes not facing each other. + // This means we can't just send the path straight to the node. + + // Treated as inside Quadrant II + cp1 = {x1 + a_width_expansion, y1}; + cp2 = {x2 - a_width_expansion, y2}; + + } else { + // Treated as inside Quadrant I + cp1 = {x1 + width_expansion, y1}; + cp2 = {x2 - width_expansion, y2}; + } + break; + case 2: // Quadrant II: Abnormal face to back + cp1 = {x1 + a_width_expansion, y1}; + cp2 = {x2 - a_width_expansion, y2}; + break; + case 3: // Quadrant III: Abnormal face to back + cp1 = {x1 + a_width_expansion, y1 + a_height_expansion}; + cp2 = {x2 - a_width_expansion, y2 - a_height_expansion}; + break; + case 4: // Quadrant IV: Normal face to back + if (in_abnormal_region) { + // Treated as inside Quadrant III + cp1 = {x1 + a_width_expansion, y1}; + cp2 = {x2 - a_width_expansion, y2}; + } else { + // Treated as inside Quadrant IV + cp1 = {x1 + width_expansion, y1}; + cp2 = {x2 - width_expansion, y2}; + } + break; + case 5: // On +ve X-axis: Normal face to back + // Straight line + cp1 = {x1, y1}; + cp2 = {x2, y2}; + break; + case 6: // On -ve X-axis: Abnormal face to back + r.adjust(0.0f, -a_height_expansion, 0.0f, a_height_expansion); + cp1 = {x1 + a_width_expansion, y1}; + cp2 = {x2 - a_width_expansion, y2}; + break; + case 7: // On +ve Y-axis: Abnormal face to back + r.adjust(0.0f, -a_height_expansion, 0.0f, a_height_expansion); + cp1 = {x1 + a_width_expansion, y1}; + cp2 = {x2 - a_width_expansion, y2}; + break; + case 8: // On -ve Y-axis: Abnormal face to back + r.adjust(0.0f, -a_height_expansion, 0.0f, a_height_expansion); + cp1 = {x1 + a_width_expansion, y1}; + cp2 = {x2 - a_width_expansion, y2}; + break; + default: + return std::make_pair(start_coordinate, end_coordinate); + } + + return std::make_pair(cp1, cp2); } /**********************************************************************/ @@ -2058,95 +2114,71 @@ std::pair VisualShaderConnectionGraphicsObject::calculate_cont /**********************************************************************/ /**********************************************************************/ -NodesCustomWidget::NodesCustomWidget(const std::shared_ptr& node, QWidget* parent) : QWidget(parent), - layout(nullptr) { - // Create the main layout. - layout = new QVBoxLayout(this); - layout->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom - layout->setSizeConstraint(QLayout::SetMinimumSize); - layout->setSpacing(0); - layout->setAlignment(Qt::AlignVCenter | Qt::AlignHCenter); - - //////////////// End of Header //////////////// - - combo_boxes[0] = new QComboBox(); - combo_boxes[0]->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); - combo_boxes[0]->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom - combo_boxes[0]->setMaximumSize(combo_boxes[0]->sizeHint()); - - // Add items to the combo box - combo_boxes[0]->addItem("Item 1"); - combo_boxes[0]->addItem("Item 2"); - combo_boxes[0]->addItem("Item 3"); - - layout->addWidget(combo_boxes[0]); - - // Connect the combo box signal to the slot - QObject::connect(combo_boxes[0], QOverload::of(&QComboBox::currentIndexChanged), this, &NodesCustomWidget::on_combo_box0_current_index_changed); - - - if (std::dynamic_pointer_cast(node)) { - - } else if (std::dynamic_pointer_cast(node)) { - - } else if (std::dynamic_pointer_cast(node)) { - - } else if (std::dynamic_pointer_cast(node)) { - - } else if (std::dynamic_pointer_cast(node)) { - - } else if (std::dynamic_pointer_cast(node)) { - - } else if (std::dynamic_pointer_cast(node)) { - - } else if (std::dynamic_pointer_cast(node)) { - - } else if (std::dynamic_pointer_cast(node)) { - - } else if (std::dynamic_pointer_cast(node)) { - - } else if (std::dynamic_pointer_cast(node)) { - - } else if (std::dynamic_pointer_cast(node)) { - - } else if (std::dynamic_pointer_cast(node)) { - - } else if (std::dynamic_pointer_cast(node)) { - - } else if (std::dynamic_pointer_cast(node)) { - - } else if (std::dynamic_pointer_cast(node)) { - - } else if (std::dynamic_pointer_cast(node)) { - - } else if (std::dynamic_pointer_cast(node)) { - - } else if (std::dynamic_pointer_cast(node)) { - - } else if (std::dynamic_pointer_cast(node)) { - - } else if (std::dynamic_pointer_cast(node)) { - - } else if (std::dynamic_pointer_cast(node)) { - - } else { - std::cout << "--- Unknown node type ---" << std::endl; - } - - //////////////// Start of Footer //////////////// - - // TODO: Set the size of this widget based on the contents. - - this->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom - // this->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); - - this->setLayout(layout); -} - -NodesCustomWidget::~NodesCustomWidget() { - if (layout) delete layout; -} +NodesCustomWidget::NodesCustomWidget(const std::shared_ptr& node, QWidget* parent) + : QWidget(parent), layout(nullptr) { + // Create the main layout. + layout = new QVBoxLayout(this); + layout->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom + layout->setSizeConstraint(QLayout::SetMinimumSize); + layout->setSpacing(0); + layout->setAlignment(Qt::AlignVCenter | Qt::AlignHCenter); + + //////////////// End of Header //////////////// + + combo_boxes[0] = new QComboBox(); + combo_boxes[0]->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + combo_boxes[0]->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom + combo_boxes[0]->setMaximumSize(combo_boxes[0]->sizeHint()); + + // Add items to the combo box + combo_boxes[0]->addItem("Item 1"); + combo_boxes[0]->addItem("Item 2"); + combo_boxes[0]->addItem("Item 3"); + + layout->addWidget(combo_boxes[0]); + + // Connect the combo box signal to the slot + QObject::connect(combo_boxes[0], QOverload::of(&QComboBox::currentIndexChanged), this, + &NodesCustomWidget::on_combo_box0_current_index_changed); + + if (std::dynamic_pointer_cast(node)) { + } else if (std::dynamic_pointer_cast(node)) { + } else if (std::dynamic_pointer_cast(node)) { + } else if (std::dynamic_pointer_cast(node)) { + } else if (std::dynamic_pointer_cast(node)) { + } else if (std::dynamic_pointer_cast(node)) { + } else if (std::dynamic_pointer_cast(node)) { + } else if (std::dynamic_pointer_cast(node)) { + } else if (std::dynamic_pointer_cast(node)) { + } else if (std::dynamic_pointer_cast(node)) { + } else if (std::dynamic_pointer_cast(node)) { + } else if (std::dynamic_pointer_cast(node)) { + } else if (std::dynamic_pointer_cast(node)) { + } else if (std::dynamic_pointer_cast(node)) { + } else if (std::dynamic_pointer_cast(node)) { + } else if (std::dynamic_pointer_cast(node)) { + } else if (std::dynamic_pointer_cast(node)) { + } else if (std::dynamic_pointer_cast(node)) { + } else if (std::dynamic_pointer_cast(node)) { + } else if (std::dynamic_pointer_cast(node)) { + } else if (std::dynamic_pointer_cast(node)) { + } else if (std::dynamic_pointer_cast(node)) { + } else { + std::cout << "--- Unknown node type ---" << std::endl; + } + + //////////////// Start of Footer //////////////// + + // TODO: Set the size of this widget based on the contents. + + this->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom + // this->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + + this->setLayout(layout); +} + +NodesCustomWidget::~NodesCustomWidget() {} void NodesCustomWidget::on_combo_box0_current_index_changed(const int& index) { - std::cout << "Combo box index changed: " << index << std::endl; + std::cout << "Combo box index changed: " << index << std::endl; } diff --git a/Editors/VisualShaderEditor.h b/Editors/VisualShaderEditor.h index 0809dcebc..5e0c5b68e 100644 --- a/Editors/VisualShaderEditor.h +++ b/Editors/VisualShaderEditor.h @@ -1,56 +1,58 @@ -/********************************************************************************\ - ** ** - ** Copyright (C) 2024 Saif Kandil (k0T0z) ** - ** ** - ** This file is a part of the ENIGMA Development Environment. ** - ** ** - ** ** - ** ENIGMA is free software: you can redistribute it and/or modify it under the ** - ** terms of the GNU General Public License as published by the Free Software ** - ** Foundation, version 3 of the license or any later version. ** - ** ** - ** This application and its source code is distributed AS-IS, WITHOUT ANY ** - ** WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS ** - ** FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ** - ** details. ** - ** ** - ** You should have recieved a copy of the GNU General Public License along ** - ** with this code. If not, see ** - ** ** - ** ENIGMA is an environment designed to create games and other programs with a ** - ** high-level, fully compilable language. Developers of ENIGMA or anything ** - ** associated with ENIGMA are in no way responsible for its users or ** - ** applications created by its users, or damages caused by the environment ** - ** or programs made in the environment. ** - ** ** - \********************************************************************************/ +/*********************************************************************************/ +/* */ +/* Copyright (C) 2024 Saif Kandil (k0T0z) */ +/* */ +/* This file is a part of the ENIGMA Development Environment. */ +/* */ +/* */ +/* ENIGMA is free software: you can redistribute it and/or modify it under the */ +/* terms of the GNU General Public License as published by the Free Software */ +/* Foundation, version 3 of the license or any later version. */ +/* */ +/* This application and its source code is distributed AS-IS, WITHOUT ANY */ +/* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS */ +/* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more */ +/* details. */ +/* */ +/* You should have recieved a copy of the GNU General Public License along */ +/* with this code. If not, see */ +/* */ +/* ENIGMA is an environment designed to create games and other programs with a */ +/* high-level, fully compilable language. Developers of ENIGMA or anything */ +/* associated with ENIGMA are in no way responsible for its users or */ +/* applications created by its users, or damages caused by the environment */ +/* or programs made in the environment. */ +/* */ +/*********************************************************************************/ #ifndef ENIGMA_VISUAL_SHADER_EDITOR_H #define ENIGMA_VISUAL_SHADER_EDITOR_H +#include +#include #include #include #include -#include -#include -#include -#include -#include -#include +#include #include +#include +#include #include #include -#include +#include #include -#include -#include -#include +#include +#include +#include +#include +#include #include #include #include "ResourceTransformations/VisualShader/visual_shader.h" #include "Editors/BaseEditor.h" +#include "Editors/CodeEditor.h" class VisualShaderGraphicsScene; class VisualShaderGraphicsView; @@ -73,27 +75,41 @@ class VisualShaderOutputPortGraphicsObject; // Add const to any function that does not modify the object. class VisualShaderEditor : public BaseEditor { - Q_OBJECT + Q_OBJECT public: + VisualShaderEditor(QWidget* parent = nullptr); VisualShaderEditor(MessageModel* model, QWidget* parent = nullptr); ~VisualShaderEditor() override; - void create_node(const QPointF& coordinate); - - void add_node(QTreeWidgetItem* selected_item, const QPointF& coordinate); - - void show_create_node_dialog(const QPointF& coordinate); - - std::vector pasre_node_category_path(const std::string& node_category_path); - QTreeWidgetItem* find_or_create_category_item(QTreeWidgetItem* parent, const std::string& category, const std::string& category_path, QTreeWidget* create_node_dialog_nodes_tree, std::unordered_map& category_path_map); - Q_SIGNALS: - void on_create_node_dialog_requested(const QPointF& coordinate = {0, 0}); // {0, 0} is the top-left corner of the scene. + /** + * @brief Request the dialog that has all kinds of nodes we can + * create. + * + * @note This signal is emitted from two sources: + * @c VisualShaderEditor::on_create_node_button_pressed and + * @c VisualShaderGraphicsView::on_create_node_action_triggered slots + * and it is connected to the @c VisualShaderEditor::show_create_node_dialog + * function. + * + * @param coordinate + */ + void on_create_node_dialog_requested(const QPointF& coordinate = {0, + 0}); // {0, 0} is the top-left corner of the scene. private Q_SLOTS: + /** + * @brief Called when @c VisualShaderEditor::create_node_button is pressed. + * + * @note Connected in @c VisualShaderEditor::init function + * to @c QPushButton::pressed signal. + * + * @note EMITS @c VisualShaderEditor::on_create_node_dialog_requested signal. + * + */ void on_create_node_button_pressed(); - void on_create_node_action_triggered(); + void on_preview_shader_button_pressed(); private: VisualShader* visual_shader; @@ -101,11 +117,11 @@ class VisualShaderEditor : public BaseEditor { QHBoxLayout* layout; QHBoxLayout* scene_layer_layout; - QWidget* scene_layer; // Layer having the scene. + QWidget* scene_layer; // Layer having the scene. VisualShaderGraphicsScene* scene; VisualShaderGraphicsView* view; - QWidget* top_layer; // Layer having the menu bar. + QWidget* top_layer; // Layer having the menu bar. QHBoxLayout* menu_bar; QPushButton* create_node_button; @@ -114,8 +130,19 @@ class VisualShaderEditor : public BaseEditor { QPushButton* reset_zoom_button; QPushButton* zoom_out_button; + QPushButton* load_image_button; + QPushButton* match_image_button; + QAction* create_node_action; + //////////////////////////////////// + // Code Previewer + //////////////////////////////////// + + QDialog* code_previewer_dialog; + QVBoxLayout* code_previewer_layout; + QPlainTextEdit* code_previewer; + //////////////////////////////////// // CreateNodeDialog Nodes Tree //////////////////////////////////// @@ -126,19 +153,38 @@ class VisualShaderEditor : public BaseEditor { std::string type; std::string description; - CreateNodeDialogNodesTreeItem(const std::string& name = std::string(), - const std::string& category_path = std::string(), - const std::string& type = std::string(), - const std::string& description = std::string()) : name(name), - category_path(category_path), - type(type), - description(description) {} - + CreateNodeDialogNodesTreeItem(const std::string& name = std::string(), + const std::string& category_path = std::string(), + const std::string& type = std::string(), + const std::string& description = std::string()) + : name(name), category_path(category_path), type(type), description(description) {} }; static const VisualShaderEditor::CreateNodeDialogNodesTreeItem create_node_dialog_nodes_tree_items[]; CreateNodeDialog* create_node_dialog; + + /** + * @brief Initializes the UI + * + * @note To be called from different constructors. + * + */ + void init(); + + void init_graph(); + + void create_node(const QPointF& coordinate); + + void add_node(QTreeWidgetItem* selected_item, const QPointF& coordinate); + + void show_create_node_dialog(const QPointF& coordinate); + + std::vector parse_node_category_path(const std::string& node_category_path); + QTreeWidgetItem* find_or_create_category_item(QTreeWidgetItem* parent, const std::string& category, + const std::string& category_path, + QTreeWidget* create_node_dialog_nodes_tree, + std::unordered_map& category_path_map); }; /**********************************************************************/ @@ -152,7 +198,7 @@ class VisualShaderEditor : public BaseEditor { /**********************************************************************/ class CreateNodeDialog : public QDialog { - Q_OBJECT + Q_OBJECT public: CreateNodeDialog(QWidget* parent = nullptr); @@ -194,17 +240,21 @@ class CreateNodeDialog : public QDialog { /**********************************************************************/ class VisualShaderGraphicsScene : public QGraphicsScene { - Q_OBJECT + Q_OBJECT + + public: + VisualShaderGraphicsScene(VisualShader* vs, QObject* parent = nullptr); -public: - VisualShaderGraphicsScene(VisualShader* vs, QObject *parent = nullptr); + ~VisualShaderGraphicsScene(); - ~VisualShaderGraphicsScene(); + bool add_node(const std::string& type, const QPointF& coordinate); + bool add_node(const int& n_id, const std::shared_ptr& n, const QPointF& coordinate); + bool delete_node(const int& n_id); - bool add_node(const int& n_id, const std::shared_ptr& node, const QPointF& coordinate); - bool delete_node(const int& n_id); + VisualShaderEditor* get_editor() const { return editor; } + void set_editor(VisualShaderEditor* editor) const { this->editor = editor; } - /** + /** * @brief * * @note This function sets the @c temporary_connection_graphics_object if @@ -220,37 +270,73 @@ class VisualShaderGraphicsScene : public QGraphicsScene { * @return true * @return false */ - bool add_connection(const int& from_node_id, - const int& from_port_index, - const int& to_node_id = (int)VisualShader::NODE_ID_INVALID, - const int& to_port_index = (int)VisualShader::PORT_INDEX_INVALID); - - bool delete_connection(const int& from_node_id, - const int& from_port_index, - const int& to_node_id = (int)VisualShader::NODE_ID_INVALID, - const int& to_port_index = (int)VisualShader::PORT_INDEX_INVALID); - - VisualShaderNodeGraphicsObject* get_node_graphics_object(const int& n_id) const; - - VisualShader* get_visual_shader() const { return vs; } + bool add_connection(const int& from_node_id, const int& from_port_index, + const int& to_node_id = (int)VisualShader::NODE_ID_INVALID, + const int& to_port_index = (int)VisualShader::PORT_INDEX_INVALID); + + bool delete_connection(const int& from_node_id, const int& from_port_index, + const int& to_node_id = (int)VisualShader::NODE_ID_INVALID, + const int& to_port_index = (int)VisualShader::PORT_INDEX_INVALID); + + VisualShaderNodeGraphicsObject* get_node_graphics_object(const int& n_id) const; + + public Q_SLOTS: + /** + * @brief Called when an interaction with a port is made. + * + * @note Connected in @c VisualShaderNodeGraphicsObject::paint function + * to @c VisualShaderInputPortGraphicsObject::port_* signals. + * + * @param port + * @param coordinate + */ + void on_port_pressed(QGraphicsObject* port, const QPointF& coordinate); + void on_port_dragged(QGraphicsObject* port, const QPointF& coordinate); + void on_port_dropped(QGraphicsObject* port, const QPointF& coordinate); -public Q_SLOTS: - void on_port_pressed(QGraphicsObject* port, const QPointF& coordinate); - void on_port_dragged(QGraphicsObject* port, const QPointF& coordinate); - void on_port_dropped(QGraphicsObject* port, const QPointF& coordinate); - -Q_SIGNALS: - void node_moved(const int& n_id, const QPointF& new_coordinate); + Q_SIGNALS: + /** + * @brief Notify the scene that a node has been moved. + * + * @note EMITTED from @c VisualShaderNodeGraphicsObject::itemChange function. + * + * @note Connected to @c VisualShaderGraphicsScene::on_node_moved slot in + * @c VisualShaderGraphicsScene::VisualShaderGraphicsScene constructor. + * + * @param n_id + * @param new_coordinate + */ + void node_moved(const int& n_id, const QPointF& new_coordinate); -private Q_SLOTS: - void on_node_moved(const int& n_id, const QPointF& new_coordinate); + private Q_SLOTS: + /** + * @brief Called when a node is moved. + * + * @note Connected in @c VisualShaderGraphicsScene::VisualShaderGraphicsScene constructor + * to @c VisualShaderGraphicsScene::node_moved signal. + * + * @param n_id + * @param new_coordinate + */ + void on_node_moved(const int& n_id, const QPointF& new_coordinate); + + /** + * @brief Called when a delete node action is triggered. + * + * @note Connected in @c VisualShaderGraphicsScene::add_node function + * to @c VisualShaderNodeGraphicsObject::node_deleted signal. + * + * @param n_id + */ + void on_node_deleted(const int& n_id); -private: - VisualShader* vs; + private: + VisualShader* vs; + mutable VisualShaderEditor* editor; - std::unordered_map node_graphics_objects; + std::unordered_map node_graphics_objects; - VisualShaderConnectionGraphicsObject* temporary_connection_graphics_object; + VisualShaderConnectionGraphicsObject* temporary_connection_graphics_object; }; /**********************************************************************/ @@ -264,77 +350,83 @@ private Q_SLOTS: /**********************************************************************/ class VisualShaderGraphicsView : public QGraphicsView { - Q_OBJECT + Q_OBJECT -public: - VisualShaderGraphicsView(VisualShaderGraphicsScene *scene, QWidget *parent = nullptr); + public: + VisualShaderGraphicsView(VisualShaderGraphicsScene* scene, QWidget* parent = nullptr); - ~VisualShaderGraphicsView(); + ~VisualShaderGraphicsView(); - float get_x() const { return rect_x; } - float get_y() const { return rect_y; } - float get_width() const { return rect_width; } - float get_height() const { return rect_height; } + float get_x() const { return rect_x; } + float get_y() const { return rect_y; } + float get_width() const { return rect_width; } + float get_height() const { return rect_height; } -public Q_SLOTS: - /** - * @brief - * - * @todo If the button is pressed then zoom in from the center of the view. - * - */ - void zoom_in(); - void reset_zoom(); - void zoom_out(); - -private Q_SLOTS: - void on_create_node_action_triggered(); - void on_delete_node_action_triggered(); - -Q_SIGNALS: - void zoom_changed(const float& zoom); - -private: - // Style - QColor background_color = QColor(53, 53, 53); - QColor fine_grid_color = QColor(60, 60, 60); - QColor coarse_grid_color = QColor(25, 25, 25); - - // Scene Rect - float t_size = std::numeric_limits::max(); // 32767 - float rect_x = -1.0f * t_size * 0.5f; - float rect_y = -1.0f * t_size * 0.5f; - float rect_width = t_size; - float rect_height = t_size; - - float fit_in_view_margin = 50.0f; - - // Zoom - float zoom = 1.0f; - float zoom_step = 1.2f; - float zoom_min; - float zoom_max; - - QMenu* context_menu; - QAction* create_node_action; - - QAction* delete_node_action; - - QAction* zoom_in_action; - QAction* reset_zoom_action; - QAction* zoom_out_action; - - QPointF last_click_coordinate; - - void drawBackground(QPainter *painter, const QRectF &r) override; - void contextMenuEvent(QContextMenuEvent *event) override; - void wheelEvent(QWheelEvent *event) override; - void mousePressEvent(QMouseEvent *event) override; - void mouseMoveEvent(QMouseEvent *event) override; - void mouseReleaseEvent(QMouseEvent *event) override; - void showEvent(QShowEvent *event) override; - - void move_view_to_fit_items(); + public Q_SLOTS: + /** + * @todo If the button is pressed then zoom in from the center of the view. + */ + void zoom_in(); + void reset_zoom(); + void zoom_out(); + + private Q_SLOTS: + /** + * @brief Called when @c VisualShaderGraphicsView::create_node_action is triggered. + * + * @note Connected in @c VisualShaderGraphicsView::VisualShaderGraphicsView constructor + * to @c QAction::triggered signal. + * + * @note EMITS @c VisualShaderEditor::on_create_node_dialog_requested signal. + * + */ + void on_create_node_action_triggered(); + + Q_SIGNALS: + void zoom_changed(const float& zoom); + + private: + VisualShaderGraphicsScene* scene; + + // Style + QColor background_color = QColor(53, 53, 53); + QColor fine_grid_color = QColor(60, 60, 60); + QColor coarse_grid_color = QColor(25, 25, 25); + + // Scene Rect + float t_size = std::numeric_limits::max(); // 32767 + float rect_x = -1.0f * t_size * 0.5f; + float rect_y = -1.0f * t_size * 0.5f; + float rect_width = t_size; + float rect_height = t_size; + + float fit_in_view_margin = 50.0f; + + // Zoom + float zoom = 1.0f; + float zoom_step = 1.2f; + float zoom_min; + float zoom_max; + + QMenu* context_menu; + QPointF last_context_menu_coordinate = {0, 0}; + QAction* create_node_action; + + QAction* zoom_in_action; + QAction* reset_zoom_action; + QAction* zoom_out_action; + + QPointF last_click_coordinate; + + void drawBackground(QPainter* painter, const QRectF& r) override; + void contextMenuEvent(QContextMenuEvent* event) override; + void wheelEvent(QWheelEvent* event) override; + void mousePressEvent(QMouseEvent* event) override; + void mouseMoveEvent(QMouseEvent* event) override; + void mouseReleaseEvent(QMouseEvent* event) override; + void showEvent(QShowEvent* event) override; + + void move_view_to_fit_items(); }; /**********************************************************************/ @@ -348,149 +440,196 @@ private Q_SLOTS: /**********************************************************************/ class VisualShaderNodeGraphicsObject : public QGraphicsObject { - Q_OBJECT + Q_OBJECT -public: - VisualShaderNodeGraphicsObject(VisualShader* vs, const int& n_id, QGraphicsItem *parent = nullptr); - ~VisualShaderNodeGraphicsObject(); + public: + VisualShaderNodeGraphicsObject(const int& n_id, + const QPointF& coordinate, + const std::shared_ptr& node, + QGraphicsItem* parent = nullptr); + ~VisualShaderNodeGraphicsObject(); - VisualShaderInputPortGraphicsObject* get_input_port_graphics_object(const int& p_index) const; - VisualShaderOutputPortGraphicsObject* get_output_port_graphics_object(const int& p_index) const; + VisualShaderInputPortGraphicsObject* get_input_port_graphics_object(const int& p_index) const; + VisualShaderOutputPortGraphicsObject* get_output_port_graphics_object(const int& p_index) const; -private: - VisualShader* vs; - int n_id; + Q_SIGNALS: + /** + * @brief Send a request to delete a node. + * + * @note EMITTED from @c VisualShaderNodeGraphicsObject::on_delete_node_action_triggered slot. + * + * @note Connected in @c VisualShaderGraphicsScene::add_node function to + * @c VisualShaderGraphicsScene::on_node_deleted slot. + * + * @param n_id + */ + void node_deleted(const int& n_id); + + private Q_SLOTS: + /** + * @brief Called when @c VisualShaderNodeGraphicsObject::delete_node_action is triggered. + * + * @note Connected in @c VisualShaderNodeGraphicsObject::VisualShaderNodeGraphicsObject constructor + * to @c QAction::triggered signal. + * + * @note EMITS @c VisualShaderNodeGraphicsObject::node_deleted signal. + * + */ + void on_delete_node_action_triggered(); + + private: + int n_id; + QPointF coordinate; + std::shared_ptr node; + + QMenu* context_menu; + QAction* delete_node_action; - std::unordered_map in_port_graphics_objects; - std::unordered_map out_port_graphics_objects; + std::unordered_map in_port_graphics_objects; + std::unordered_map out_port_graphics_objects; - // Style - QColor normal_boundary_color = QColor(255, 255, 255); - QColor selected_boundary_color = QColor(255, 165, 0); - QColor font_color = QColor(255, 255, 255); - QColor fill_color = QColor(0, 0, 0, 0); + // Style + QColor normal_boundary_color = QColor(255, 255, 255); + QColor selected_boundary_color = QColor(255, 165, 0); + QColor font_color = QColor(255, 255, 255); + QColor fill_color = QColor(0, 0, 0, 0); - float pen_width = 1.0f; + float pen_width = 1.0f; - float opacity = 0.8f; - float corner_radius = 3.0f; + float opacity = 0.8f; + float corner_radius = 3.0f; - mutable float rect_width; // Calculated in boundingRect() - mutable float caption_rect_height; // Calculated in boundingRect() + mutable float rect_width; // Calculated in boundingRect() + mutable float caption_rect_height; // Calculated in boundingRect() - mutable float rect_height; // Calculated in boundingRect() - float body_rect_header_height = 30.0f; - float body_rect_port_step = 40.0f; - float body_rect_footer_height = 30.0f; + mutable float rect_height; // Calculated in boundingRect() + float body_rect_header_height = 30.0f; + float body_rect_port_step = 40.0f; + float body_rect_footer_height = 30.0f; - mutable float rect_padding; // Calculated in boundingRect() - mutable float rect_margin; // Calculated in boundingRect() + mutable float rect_padding; // Calculated in boundingRect() + mutable float rect_margin; // Calculated in boundingRect() - // Ports Style - float connected_port_diameter = 8.0f; - float unconnected_port_diameter = 6.0f; + // Ports Style + float connected_port_diameter = 8.0f; + float unconnected_port_diameter = 6.0f; - // Caption - float caption_font_size = 18.0f; - float port_caption_font_size = 8.0f; + // Caption + float caption_font_size = 18.0f; + float port_caption_font_size = 8.0f; - QRectF boundingRect() const override; - void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override; - QVariant itemChange(GraphicsItemChange change, const QVariant &value) override; + QRectF boundingRect() const override; + void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget = nullptr) override; + QVariant itemChange(GraphicsItemChange change, const QVariant& value) override; + void contextMenuEvent(QGraphicsSceneContextMenuEvent *event) override; }; class VisualShaderInputPortGraphicsObject : public QGraphicsObject { - Q_OBJECT + Q_OBJECT -public: - VisualShaderInputPortGraphicsObject(const QRectF& rect, - const int& n_id, - const int& p_index, - QGraphicsItem* parent = nullptr); - ~VisualShaderInputPortGraphicsObject(); + public: + VisualShaderInputPortGraphicsObject(const QRectF& rect, const int& n_id, const int& p_index, + QGraphicsItem* parent = nullptr); + ~VisualShaderInputPortGraphicsObject(); - QPointF get_global_coordinate() const { return mapToScene(rect.center()); } + QPointF get_global_coordinate() const { return mapToScene(rect.center()); } - int get_node_id() const { return n_id; } - int get_port_index() const { return p_index; } + int get_node_id() const { return n_id; } + int get_port_index() const { return p_index; } - VisualShaderConnectionGraphicsObject* get_connection_graphics_object() const { return connection_graphics_object; } - void connect(VisualShaderConnectionGraphicsObject* c_g_o) const { this->connection_graphics_object = c_g_o; } - void detach_connection() const { this->connection_graphics_object = nullptr; } - bool is_connected() const { return connection_graphics_object != nullptr; } + VisualShaderConnectionGraphicsObject* get_connection_graphics_object() const { return connection_graphics_object; } + void connect(VisualShaderConnectionGraphicsObject* c_g_o) const { this->connection_graphics_object = c_g_o; } + void detach_connection() const { this->connection_graphics_object = nullptr; } + bool is_connected() const { return connection_graphics_object != nullptr; } -Q_SIGNALS: - void port_pressed(VisualShaderInputPortGraphicsObject* port, const QPointF& pos); - void port_dragged(VisualShaderInputPortGraphicsObject* port, const QPointF& pos); - void port_dropped(VisualShaderInputPortGraphicsObject* port, const QPointF& pos); + Q_SIGNALS: + /** + * @brief Called when the an interaction with the port is made. + * + * @note Connected in @c VisualShaderNodeGraphicsObject::paint function to + * @c VisualShaderGraphicsScene::on_port_* slots. + * + * @param port + * @param pos + */ + void port_pressed(VisualShaderInputPortGraphicsObject* port, const QPointF& pos); + void port_dragged(VisualShaderInputPortGraphicsObject* port, const QPointF& pos); + void port_dropped(VisualShaderInputPortGraphicsObject* port, const QPointF& pos); -private: - int n_id; - int p_index; - QRectF rect; + private: + int n_id; + int p_index; + QRectF rect; - mutable VisualShaderConnectionGraphicsObject* connection_graphics_object; + mutable VisualShaderConnectionGraphicsObject* connection_graphics_object; - float padding = 0.5f; + float padding = 0.5f; - // Style - QColor font_color = QColor(255, 255, 255); - QColor connection_point_color = QColor(169, 169, 169); + // Style + QColor font_color = QColor(255, 255, 255); + QColor connection_point_color = QColor(169, 169, 169); - float opacity = 1.0f; + float opacity = 1.0f; - QRectF boundingRect() const override; - void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override; - void mousePressEvent(QGraphicsSceneMouseEvent *event) override; - void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override; - void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override; + QRectF boundingRect() const override; + void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) override; + void mousePressEvent(QGraphicsSceneMouseEvent* event) override; + void mouseMoveEvent(QGraphicsSceneMouseEvent* event) override; + void mouseReleaseEvent(QGraphicsSceneMouseEvent* event) override; }; class VisualShaderOutputPortGraphicsObject : public QGraphicsObject { - Q_OBJECT + Q_OBJECT -public: - VisualShaderOutputPortGraphicsObject(const QRectF& rect, - const int& n_id, - const int& p_index, - QGraphicsItem* parent = nullptr); - ~VisualShaderOutputPortGraphicsObject(); + public: + VisualShaderOutputPortGraphicsObject(const QRectF& rect, const int& n_id, const int& p_index, + QGraphicsItem* parent = nullptr); + ~VisualShaderOutputPortGraphicsObject(); - QPointF get_global_coordinate() const { return mapToScene(rect.center()); } + QPointF get_global_coordinate() const { return mapToScene(rect.center()); } - int get_node_id() const { return n_id; } - int get_port_index() const { return p_index; } + int get_node_id() const { return n_id; } + int get_port_index() const { return p_index; } - VisualShaderConnectionGraphicsObject* get_connection_graphics_object() const { return connection_graphics_object; } - void connect(VisualShaderConnectionGraphicsObject* c_o) const { this->connection_graphics_object = c_o; } - void detach_connection() const { this->connection_graphics_object = nullptr; } - bool is_connected() const { return connection_graphics_object != nullptr; } + VisualShaderConnectionGraphicsObject* get_connection_graphics_object() const { return connection_graphics_object; } + void connect(VisualShaderConnectionGraphicsObject* c_o) const { this->connection_graphics_object = c_o; } + void detach_connection() const { this->connection_graphics_object = nullptr; } + bool is_connected() const { return connection_graphics_object != nullptr; } -Q_SIGNALS: - void port_pressed(VisualShaderOutputPortGraphicsObject* port, const QPointF& pos); - void port_dragged(VisualShaderOutputPortGraphicsObject* port, const QPointF& pos); - void port_dropped(VisualShaderOutputPortGraphicsObject* port, const QPointF& pos); + Q_SIGNALS: + /** + * @brief Called when the an interaction with the port is made. + * + * @note Connected in @c VisualShaderNodeGraphicsObject::paint function to + * @c VisualShaderGraphicsScene::on_port_* slots. + * + * @param port + * @param pos + */ + void port_pressed(VisualShaderOutputPortGraphicsObject* port, const QPointF& pos); + void port_dragged(VisualShaderOutputPortGraphicsObject* port, const QPointF& pos); + void port_dropped(VisualShaderOutputPortGraphicsObject* port, const QPointF& pos); -private: - int n_id; - int p_index; - QRectF rect; + private: + int n_id; + int p_index; + QRectF rect; - mutable VisualShaderConnectionGraphicsObject* connection_graphics_object; + mutable VisualShaderConnectionGraphicsObject* connection_graphics_object; - float padding = 0.5f; + float padding = 0.5f; - // Style - QColor font_color = QColor(255, 255, 255); - QColor connection_point_color = QColor(169, 169, 169); + // Style + QColor font_color = QColor(255, 255, 255); + QColor connection_point_color = QColor(169, 169, 169); - float opacity = 1.0f; + float opacity = 1.0f; - QRectF boundingRect() const override; - void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override; - void mousePressEvent(QGraphicsSceneMouseEvent *event) override; - void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override; - void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override; + QRectF boundingRect() const override; + void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) override; + void mousePressEvent(QGraphicsSceneMouseEvent* event) override; + void mouseMoveEvent(QGraphicsSceneMouseEvent* event) override; + void mouseReleaseEvent(QGraphicsSceneMouseEvent* event) override; }; /**********************************************************************/ @@ -504,61 +643,69 @@ class VisualShaderOutputPortGraphicsObject : public QGraphicsObject { /**********************************************************************/ class VisualShaderConnectionGraphicsObject : public QGraphicsObject { - Q_OBJECT + Q_OBJECT -public: - VisualShaderConnectionGraphicsObject(const int& from_n_id, - const int& from_p_index, - const QPointF& start_coordinate, - QGraphicsItem *parent = nullptr); - ~VisualShaderConnectionGraphicsObject(); - - int get_from_node_id() const { return from_n_id; } - int get_from_port_index() const { return from_p_index; } - - int get_to_node_id() const { return to_n_id; } - int get_to_port_index() const { return to_p_index; } - - void detach_end() const { this->set_to_node_id((int)VisualShader::NODE_ID_INVALID); this->set_to_port_index((int)VisualShader::PORT_INDEX_INVALID); } - - void set_to_node_id(const int& to_n_id) const { this->to_n_id = to_n_id; } - void set_to_port_index(const int& to_p_index) const { this->to_p_index = to_p_index; } - - void set_start_coordinate(const QPointF& start_coordinate) { this->start_coordinate = start_coordinate; update(); } - void set_end_coordinate(const QPointF& end_coordinate) { this->end_coordinate = end_coordinate; update(); } - -private: - int from_n_id; - mutable int to_n_id; - int from_p_index; - mutable int to_p_index; - - QPointF start_coordinate; - QPointF end_coordinate; + public: + VisualShaderConnectionGraphicsObject(const int& from_n_id, const int& from_p_index, const QPointF& start_coordinate, + QGraphicsItem* parent = nullptr); + ~VisualShaderConnectionGraphicsObject(); - // Style - QColor construction_color = QColor(169, 169, 169); - QColor normal_color = QColor(0, 255, 255); - QColor selected_color = QColor(100, 100, 100); - QColor selected_halo_color = QColor(255, 165, 0); - QColor connection_point_color = QColor(169, 169, 169); + int get_from_node_id() const { return from_n_id; } + int get_from_port_index() const { return from_p_index; } - float line_width = 3.0f; - float construction_line_width = 2.0f; - float point_diameter = 10.0f; + int get_to_node_id() const { return to_n_id; } + int get_to_port_index() const { return to_p_index; } - mutable float rect_padding; // Calculated in boundingRect() + void detach_end() const { + this->set_to_node_id((int)VisualShader::NODE_ID_INVALID); + this->set_to_port_index((int)VisualShader::PORT_INDEX_INVALID); + } - float min_h_distance = 50.0f; - float abnormal_face_to_back_control_width_expansion_factor = 0.5f; - float abnormal_face_to_back_control_height_expansion_factor = 2.0f; + void set_to_node_id(const int& to_n_id) const { this->to_n_id = to_n_id; } + void set_to_port_index(const int& to_p_index) const { this->to_p_index = to_p_index; } - QRectF boundingRect() const override; - void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override; + void set_start_coordinate(const QPointF& start_coordinate) { + this->start_coordinate = start_coordinate; + update(); + } + void set_end_coordinate(const QPointF& end_coordinate) { + this->end_coordinate = end_coordinate; + update(); + } - int detect_quadrant(const QPointF& reference, const QPointF& target) const; - QRectF calculate_bounding_rect_from_coordinates(const QPointF& start_coordinate, const QPointF& end_coordinate) const; - std::pair calculate_control_points(const QPointF& start_coordinate, const QPointF& end_coordinate) const; + private: + int from_n_id; + mutable int to_n_id; + int from_p_index; + mutable int to_p_index; + + QPointF start_coordinate; + QPointF end_coordinate; + + // Style + QColor construction_color = QColor(169, 169, 169); + QColor normal_color = QColor(0, 255, 255); + QColor selected_color = QColor(100, 100, 100); + QColor selected_halo_color = QColor(255, 165, 0); + QColor connection_point_color = QColor(169, 169, 169); + + float line_width = 3.0f; + float construction_line_width = 2.0f; + float point_diameter = 10.0f; + + mutable float rect_padding; // Calculated in boundingRect() + + float min_h_distance = 50.0f; + float abnormal_face_to_back_control_width_expansion_factor = 0.5f; + float abnormal_face_to_back_control_height_expansion_factor = 2.0f; + + QRectF boundingRect() const override; + void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget = nullptr) override; + + int detect_quadrant(const QPointF& reference, const QPointF& target) const; + QRectF calculate_bounding_rect_from_coordinates(const QPointF& start_coordinate, const QPointF& end_coordinate) const; + std::pair calculate_control_points(const QPointF& start_coordinate, + const QPointF& end_coordinate) const; }; /**********************************************************************/ @@ -571,21 +718,20 @@ class VisualShaderConnectionGraphicsObject : public QGraphicsObject { /**********************************************************************/ /**********************************************************************/ -class NodesCustomWidget : public QWidget -{ - Q_OBJECT +class NodesCustomWidget : public QWidget { + Q_OBJECT -public: - NodesCustomWidget(const std::shared_ptr& node, QWidget *parent = nullptr); - ~NodesCustomWidget(); + public: + NodesCustomWidget(const std::shared_ptr& node, QWidget* parent = nullptr); + ~NodesCustomWidget(); -private Q_SLOTS: - void on_combo_box0_current_index_changed(const int& index); + private Q_SLOTS: + void on_combo_box0_current_index_changed(const int& index); -private: - QVBoxLayout* layout; + private: + QVBoxLayout* layout; - QComboBox* combo_boxes[2]; + QComboBox* combo_boxes[2]; }; -#endif // ENIGMA_VISUAL_SHADER_EDITOR_H +#endif // ENIGMA_VISUAL_SHADER_EDITOR_H diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt index 5775c6448..683dfab9c 100644 --- a/Tests/CMakeLists.txt +++ b/Tests/CMakeLists.txt @@ -1,27 +1,219 @@ +################################################################################### +## ## +## Copyright (C) 2024 Saif Kandil (k0T0z) ## +## ## +## This file is a part of the ENIGMA Development Environment. ## +## ## +## ## +## ENIGMA is free software: you can redistribute it and/or modify it under the ## +## terms of the GNU General Public License as published by the Free Software ## +## Foundation, version 3 of the license or any later version. ## +## ## +## This application and its source code is distributed AS-IS, WITHOUT ANY ## +## WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS ## +## FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ## +## details. ## +## ## +## You should have recieved a copy of the GNU General Public License along ## +## with this code. If not, see ## +## ## +## ENIGMA is an environment designed to create games and other programs with a ## +## high-level, fully compilable language. Developers of ENIGMA or anything ## +## associated with ENIGMA are in no way responsible for its users or ## +## applications created by its users, or damages caused by the environment ## +## or programs made in the environment. ## +## ## +################################################################################### + set(RGM_TESTS "RGM-Tests" CACHE STRING "RGM Tests") project(${RGM_TESTS} LANGUAGES CXX) -file(GLOB RGM_TESTS_SRC_FILES - ${CMAKE_CURRENT_SOURCE_DIR}/Editors/VisualShaderEditorTests.cpp -) - set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTOUIC ON) set(CMAKE_AUTORCC ON) +set(CMAKE_AUTOUIC_SEARCH_PATHS "${RGM_ROOTDIR}/" "${RGM_ROOTDIR}/Dialogs" "${RGM_ROOTDIR}/Editors") + +file(GLOB RGM_TESTS_SRC_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/tests_main.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/MainWindowTests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/Editors/VisualShaderEditorTests.cpp +) + +file(GLOB RGM_TESTS_H_FILES + ${RGM_ROOTDIR}/MainWindow.h + ${RGM_ROOTDIR}/Models/MessageModel.h + ${RGM_ROOTDIR}/Models/ProtoModel.h + ${RGM_ROOTDIR}/Models/TreeModel.h + ${RGM_ROOTDIR}/Models/PrimitiveModel.h + ${RGM_ROOTDIR}/Models/RepeatedMessageModel.h + ${RGM_ROOTDIR}/Models/EventTypesListSortFilterProxyModel.h + ${RGM_ROOTDIR}/Models/ResourceModelMap.h + ${RGM_ROOTDIR}/Models/EventTypesListModel.h + ${RGM_ROOTDIR}/Models/ImmediateMapper.h + ${RGM_ROOTDIR}/Models/RepeatedModel.h + ${RGM_ROOTDIR}/Models/EventsListModel.h + ${RGM_ROOTDIR}/Models/ModelMapper.h + ${RGM_ROOTDIR}/Models/RepeatedSortFilterProxyModel.h + ${RGM_ROOTDIR}/Models/TreeSortFilterProxyModel.h + ${RGM_ROOTDIR}/Components/RecentFiles.h + ${RGM_ROOTDIR}/Components/QMenuView_p.h + ${RGM_ROOTDIR}/Components/Utility.h + ${RGM_ROOTDIR}/Components/QMenuView.h + ${RGM_ROOTDIR}/Components/Logger.h + ${RGM_ROOTDIR}/Components/ArtManager.h + ${RGM_ROOTDIR}/Editors/ObjectEditor.h + ${RGM_ROOTDIR}/Editors/PathEditor.h + ${RGM_ROOTDIR}/Editors/ScriptEditor.h + ${RGM_ROOTDIR}/Editors/CodeEditor.h + ${RGM_ROOTDIR}/Editors/SoundEditor.h + ${RGM_ROOTDIR}/Editors/IncludeEditor.h + ${RGM_ROOTDIR}/Editors/InformationEditor.h + ${RGM_ROOTDIR}/Editors/TimelineEditor.h + ${RGM_ROOTDIR}/Editors/SettingsEditor.h + ${RGM_ROOTDIR}/Editors/ShaderEditor.h + ${RGM_ROOTDIR}/Editors/RoomEditor.h + ${RGM_ROOTDIR}/Editors/BaseEditor.h + ${RGM_ROOTDIR}/Editors/FontEditor.h + ${RGM_ROOTDIR}/Editors/SpriteEditor.h + ${RGM_ROOTDIR}/Editors/BackgroundEditor.h + ${RGM_ROOTDIR}/Editors/VisualShaderEditor.h + ${RGM_ROOTDIR}/Plugins/RGMPlugin.h + ${RGM_ROOTDIR}/Plugins/ServerPlugin.h + ${RGM_ROOTDIR}/Dialogs/EventArgumentsDialog.h + ${RGM_ROOTDIR}/Dialogs/PreferencesDialog.h + ${RGM_ROOTDIR}/Dialogs/PreferencesKeys.h + ${RGM_ROOTDIR}/Dialogs/TimelineChangeMoment.h + ${RGM_ROOTDIR}/Dialogs/KeyBindingPreferences.h + ${RGM_ROOTDIR}/Utils/SafeCasts.h + ${RGM_ROOTDIR}/Utils/ProtoManip.h + ${RGM_ROOTDIR}/Utils/FieldPath.h + ${RGM_ROOTDIR}/Utils/QBoilerplate.h + ${RGM_ROOTDIR}/Widgets/BackgroundView.h + ${RGM_ROOTDIR}/Widgets/CodeWidget.h + ${RGM_ROOTDIR}/Widgets/ResourceSelector.h + ${RGM_ROOTDIR}/Widgets/ColorPicker.h + ${RGM_ROOTDIR}/Widgets/AssetScrollArea.h + ${RGM_ROOTDIR}/Widgets/SpriteView.h + ${RGM_ROOTDIR}/Widgets/AssetView.h + ${RGM_ROOTDIR}/Widgets/PathView.h + ${RGM_ROOTDIR}/Widgets/StackedCodeWidget.h + ${RGM_ROOTDIR}/Widgets/SpriteSubimageListView.h + ${RGM_ROOTDIR}/Widgets/AssetScrollAreaBackground.h + ${RGM_ROOTDIR}/Widgets/RoomView.h +) + +file(GLOB RGM_TESTS_DEPS_FILES + ${RGM_ROOTDIR}/MainWindow.cpp + ${RGM_ROOTDIR}/Models/RepeatedMessageModel.cpp + ${RGM_ROOTDIR}/Models/TreeSortFilterProxyModel.cpp + ${RGM_ROOTDIR}/Models/PrimitiveModel.cpp + ${RGM_ROOTDIR}/Models/EventsListModel.cpp + ${RGM_ROOTDIR}/Models/ModelMapper.cpp + ${RGM_ROOTDIR}/Models/RepeatedModel.cpp + ${RGM_ROOTDIR}/Models/MessageModel.cpp + ${RGM_ROOTDIR}/Models/TreeModel.cpp + ${RGM_ROOTDIR}/Models/EventTypesListModel.cpp + ${RGM_ROOTDIR}/Models/ResourceModelMap.cpp + ${RGM_ROOTDIR}/Models/ImmediateMapper.cpp + ${RGM_ROOTDIR}/Models/ProtoModel.cpp + ${RGM_ROOTDIR}/Models/EventTypesListSortFilterProxyModel.cpp + ${RGM_ROOTDIR}/Models/RepeatedSortFilterProxyModel.cpp + ${RGM_ROOTDIR}/Components/Utility.cpp + ${RGM_ROOTDIR}/Components/RecentFiles.cpp + ${RGM_ROOTDIR}/Components/QMenuView.cpp + ${RGM_ROOTDIR}/Components/ArtManager.cpp + ${RGM_ROOTDIR}/Editors/VisualShaderEditor.cpp + ${RGM_ROOTDIR}/Editors/PathEditor.cpp + ${RGM_ROOTDIR}/Editors/RoomEditor.cpp + ${RGM_ROOTDIR}/Editors/ObjectEditor.cpp + ${RGM_ROOTDIR}/Editors/FontEditor.cpp + ${RGM_ROOTDIR}/Editors/SpriteEditor.cpp + ${RGM_ROOTDIR}/Editors/BackgroundEditor.cpp + ${RGM_ROOTDIR}/Editors/SettingsEditor.cpp + ${RGM_ROOTDIR}/Editors/ShaderEditor.cpp + ${RGM_ROOTDIR}/Editors/SoundEditor.cpp + ${RGM_ROOTDIR}/Editors/IncludeEditor.cpp + ${RGM_ROOTDIR}/Editors/InformationEditor.cpp + ${RGM_ROOTDIR}/Editors/BaseEditor.cpp + ${RGM_ROOTDIR}/Editors/ScriptEditor.cpp + ${RGM_ROOTDIR}/Editors/CodeEditor.cpp + ${RGM_ROOTDIR}/Editors/TimelineEditor.cpp + ${RGM_ROOTDIR}/Editors/VisualShaderEditor.cpp + ${RGM_ROOTDIR}/Plugins/RGMPlugin.cpp + ${RGM_ROOTDIR}/Plugins/ServerPlugin.cpp + ${RGM_ROOTDIR}/Dialogs/EventArgumentsDialog.cpp + ${RGM_ROOTDIR}/Dialogs/TimelineChangeMoment.cpp + ${RGM_ROOTDIR}/Dialogs/PreferencesDialog.cpp + ${RGM_ROOTDIR}/Dialogs/PreferencesKeys.cpp + ${RGM_ROOTDIR}/Dialogs/KeyBindingPreferences.cpp + ${RGM_ROOTDIR}/Utils/ProtoManip.cpp + ${RGM_ROOTDIR}/Utils/FieldPath.cpp + ${RGM_ROOTDIR}/Widgets/BackgroundView.cpp + ${RGM_ROOTDIR}/Widgets/ColorPicker.cpp + ${RGM_ROOTDIR}/Widgets/AssetView.cpp + ${RGM_ROOTDIR}/Widgets/PathView.cpp + ${RGM_ROOTDIR}/Widgets/RoomView.cpp + ${RGM_ROOTDIR}/Widgets/SpriteView.cpp + ${RGM_ROOTDIR}/Widgets/CodeWidget.cpp + ${RGM_ROOTDIR}/Widgets/CodeWidgetPlain.cpp # Ignore checking for QScintilla + ${RGM_ROOTDIR}/Widgets/SpriteSubimageListView.cpp + ${RGM_ROOTDIR}/Widgets/StackedCodeWidget.cpp + ${RGM_ROOTDIR}/Widgets/AssetScrollAreaBackground.cpp +) + +file(GLOB RGM_TESTS_UI_FILES + ${RGM_ROOTDIR}/MainWindow.ui + ${RGM_ROOTDIR}/Dialogs/TimelineChangeMoment.ui + ${RGM_ROOTDIR}/Dialogs/PreferencesDialog.ui + ${RGM_ROOTDIR}/Dialogs/AddImageDialog.ui + ${RGM_ROOTDIR}/Editors/CodeEditor.ui + ${RGM_ROOTDIR}/Editors/SoundEditor.ui + ${RGM_ROOTDIR}/Editors/BackgroundEditor.ui + ${RGM_ROOTDIR}/Editors/SpriteEditor.ui + ${RGM_ROOTDIR}/Editors/PathEditor.ui + ${RGM_ROOTDIR}/Editors/SettingsEditor.ui + ${RGM_ROOTDIR}/Editors/RoomEditor.ui + ${RGM_ROOTDIR}/Editors/FontEditor.ui + ${RGM_ROOTDIR}/Editors/TimelineEditor.ui + ${RGM_ROOTDIR}/Editors/ObjectEditor.ui + ${RGM_ROOTDIR}/Editors/InformationEditor.ui + ${RGM_ROOTDIR}/Editors/IncludeEditor.ui +) + +file(GLOB RGM_TESTS_QRC_FILES + ${RGM_ROOTDIR}/images.qrc + ${RGM_ROOTDIR}/resources.rc +) + enable_testing() -add_executable(${RGM_TESTS} ${RGM_TESTS_SRC_FILES}) +add_executable(${RGM_TESTS} ${RGM_TESTS_H_FILES} ${RGM_TESTS_SRC_FILES} ${RGM_TESTS_DEPS_FILES} ${RGM_TESTS_UI_FILES} ${RGM_TESTS_QRC_FILES}) -find_package(Qt5Test REQUIRED) -target_link_libraries(${RGM_TESTS} PRIVATE Qt5::Test) +# Find Protobuf +find_package(Protobuf CONFIG REQUIRED) +target_link_libraries(${RGM_TESTS} PRIVATE protobuf::libprotobuf) + +# Find gRPC +find_package(gRPC CONFIG REQUIRED) +target_link_libraries(${RGM_TESTS} PRIVATE gRPC::gpr gRPC::grpc gRPC::grpc++) find_package(Qt5 COMPONENTS Core Widgets Gui PrintSupport Multimedia REQUIRED) target_link_libraries(${RGM_TESTS} PRIVATE Qt5::Core Qt5::Widgets Qt5::Gui Qt5::PrintSupport Qt5::Multimedia) -target_link_libraries(${RGM_TESTS} PRIVATE ${LIB_PROTO} ${SHARED_LIB}) +find_package(Qt5Test REQUIRED) +target_link_libraries(${RGM_TESTS} PRIVATE Qt5::Test) + +target_include_directories(${RGM_TESTS} PRIVATE + "${CMAKE_CURRENT_SOURCE_DIR}/" + "${RGM_ROOTDIR}/" +) + +target_compile_definitions(${RGM_TESTS} PRIVATE QT_MESSAGELOGCONTEXT ENIGMA_DIR="${ENIGMA_DIR}") + +target_link_libraries(${RGM_TESTS} PRIVATE ${LIB_EGM} ${LIB_PROTO} ${SHARED_LIB}) add_test(NAME ${RGM_TESTS} COMMAND ${RGM_TESTS}) diff --git a/Tests/Editors/VisualShaderEditorTests.cpp b/Tests/Editors/VisualShaderEditorTests.cpp index a668545ac..46ebba05a 100644 --- a/Tests/Editors/VisualShaderEditorTests.cpp +++ b/Tests/Editors/VisualShaderEditorTests.cpp @@ -1,26 +1,46 @@ -/********************************************************************************\ - ** ** - ** Copyright (C) 2024 Saif Kandil (k0T0z) ** - ** ** - ** This file is a part of the ENIGMA Development Environment. ** - ** ** - ** ** - ** ENIGMA is free software: you can redistribute it and/or modify it under the ** - ** terms of the GNU General Public License as published by the Free Software ** - ** Foundation, version 3 of the license or any later version. ** - ** ** - ** This application and its source code is distributed AS-IS, WITHOUT ANY ** - ** WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS ** - ** FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ** - ** details. ** - ** ** - ** You should have recieved a copy of the GNU General Public License along ** - ** with this code. If not, see ** - ** ** - ** ENIGMA is an environment designed to create games and other programs with a ** - ** high-level, fully compilable language. Developers of ENIGMA or anything ** - ** associated with ENIGMA are in no way responsible for its users or ** - ** applications created by its users, or damages caused by the environment ** - ** or programs made in the environment. ** - ** ** - \********************************************************************************/ +/*********************************************************************************/ +/* */ +/* Copyright (C) 2024 Saif Kandil (k0T0z) */ +/* */ +/* This file is a part of the ENIGMA Development Environment. */ +/* */ +/* */ +/* ENIGMA is free software: you can redistribute it and/or modify it under the */ +/* terms of the GNU General Public License as published by the Free Software */ +/* Foundation, version 3 of the license or any later version. */ +/* */ +/* This application and its source code is distributed AS-IS, WITHOUT ANY */ +/* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS */ +/* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more */ +/* details. */ +/* */ +/* You should have recieved a copy of the GNU General Public License along */ +/* with this code. If not, see */ +/* */ +/* ENIGMA is an environment designed to create games and other programs with a */ +/* high-level, fully compilable language. Developers of ENIGMA or anything */ +/* associated with ENIGMA are in no way responsible for its users or */ +/* applications created by its users, or damages caused by the environment */ +/* or programs made in the environment. */ +/* */ +/*********************************************************************************/ + +#include "Editors/VisualShaderEditorTests.h" + +#include +#include + +void TestVisualShaderEditor::initTestCase() { + editor = new VisualShaderEditor(); +} +void TestVisualShaderEditor::init() { } + +void TestVisualShaderEditor::cleanupTestCase() { delete editor; } +void TestVisualShaderEditor::cleanup() { } + +void TestVisualShaderEditor::testCreateFullGraph() { + editor->show(); + + // Wait for the editor to be shown + QVERIFY(QTest::qWaitForWindowExposed(editor)); +} diff --git a/Tests/Editors/VisualShaderEditorTests.h b/Tests/Editors/VisualShaderEditorTests.h new file mode 100644 index 000000000..e127d3572 --- /dev/null +++ b/Tests/Editors/VisualShaderEditorTests.h @@ -0,0 +1,49 @@ +/*********************************************************************************/ +/* */ +/* Copyright (C) 2024 Saif Kandil (k0T0z) */ +/* */ +/* This file is a part of the ENIGMA Development Environment. */ +/* */ +/* */ +/* ENIGMA is free software: you can redistribute it and/or modify it under the */ +/* terms of the GNU General Public License as published by the Free Software */ +/* Foundation, version 3 of the license or any later version. */ +/* */ +/* This application and its source code is distributed AS-IS, WITHOUT ANY */ +/* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS */ +/* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more */ +/* details. */ +/* */ +/* You should have recieved a copy of the GNU General Public License along */ +/* with this code. If not, see */ +/* */ +/* ENIGMA is an environment designed to create games and other programs with a */ +/* high-level, fully compilable language. Developers of ENIGMA or anything */ +/* associated with ENIGMA are in no way responsible for its users or */ +/* applications created by its users, or damages caused by the environment */ +/* or programs made in the environment. */ +/* */ +/*********************************************************************************/ + +#ifndef VISUAL_SHADER_EDITOR_TESTS_H +#define VISUAL_SHADER_EDITOR_TESTS_H + +#include "Editors/VisualShaderEditor.h" + +class TestVisualShaderEditor : public QObject { + Q_OBJECT + + private slots: + void initTestCase(); // Will be called before the first test function is executed. + void init(); // Will be called before each test function is executed. + + void cleanupTestCase(); // Will be called after the last test function was executed. + void cleanup(); // Will be called after every test function. + + void testCreateFullGraph(); + + private: + VisualShaderEditor* editor = nullptr; +}; + +#endif // VISUAL_SHADER_EDITOR_TESTS_H diff --git a/Tests/MainWindowTests.cpp b/Tests/MainWindowTests.cpp new file mode 100644 index 000000000..500a8609a --- /dev/null +++ b/Tests/MainWindowTests.cpp @@ -0,0 +1,46 @@ +/*********************************************************************************/ +/* */ +/* Copyright (C) 2024 Saif Kandil (k0T0z) */ +/* */ +/* This file is a part of the ENIGMA Development Environment. */ +/* */ +/* */ +/* ENIGMA is free software: you can redistribute it and/or modify it under the */ +/* terms of the GNU General Public License as published by the Free Software */ +/* Foundation, version 3 of the license or any later version. */ +/* */ +/* This application and its source code is distributed AS-IS, WITHOUT ANY */ +/* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS */ +/* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more */ +/* details. */ +/* */ +/* You should have recieved a copy of the GNU General Public License along */ +/* with this code. If not, see */ +/* */ +/* ENIGMA is an environment designed to create games and other programs with a */ +/* high-level, fully compilable language. Developers of ENIGMA or anything */ +/* associated with ENIGMA are in no way responsible for its users or */ +/* applications created by its users, or damages caused by the environment */ +/* or programs made in the environment. */ +/* */ +/*********************************************************************************/ + +#include "MainWindowTests.h" + +#include +#include + +void TestMainWindow::initTestCase() { + mw = new MainWindow(nullptr); +} +void TestMainWindow::init() { } + +void TestMainWindow::cleanupTestCase() { delete mw; } +void TestMainWindow::cleanup() { } + +void TestMainWindow::testVisualShaderEditor() { + mw->show(); + + // Wait for the window to be shown + QTest::qWait(1000); +} diff --git a/Tests/MainWindowTests.h b/Tests/MainWindowTests.h new file mode 100644 index 000000000..a10e8c004 --- /dev/null +++ b/Tests/MainWindowTests.h @@ -0,0 +1,49 @@ +/*********************************************************************************/ +/* */ +/* Copyright (C) 2024 Saif Kandil (k0T0z) */ +/* */ +/* This file is a part of the ENIGMA Development Environment. */ +/* */ +/* */ +/* ENIGMA is free software: you can redistribute it and/or modify it under the */ +/* terms of the GNU General Public License as published by the Free Software */ +/* Foundation, version 3 of the license or any later version. */ +/* */ +/* This application and its source code is distributed AS-IS, WITHOUT ANY */ +/* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS */ +/* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more */ +/* details. */ +/* */ +/* You should have recieved a copy of the GNU General Public License along */ +/* with this code. If not, see */ +/* */ +/* ENIGMA is an environment designed to create games and other programs with a */ +/* high-level, fully compilable language. Developers of ENIGMA or anything */ +/* associated with ENIGMA are in no way responsible for its users or */ +/* applications created by its users, or damages caused by the environment */ +/* or programs made in the environment. */ +/* */ +/*********************************************************************************/ + +#ifndef MAIN_WINDOW_TESTS_H +#define MAIN_WINDOW_TESTS_H + +#include "MainWindow.h" + +class TestMainWindow : public QObject { + Q_OBJECT + + private slots: + void initTestCase(); // Will be called before the first test function is executed. + void init(); // Will be called before each test function is executed. + + void cleanupTestCase(); // Will be called after the last test function was executed. + void cleanup(); // Will be called after every test function. + + void testVisualShaderEditor(); + + private: + MainWindow* mw; +}; + +#endif // MAIN_WINDOW_TESTS_H diff --git a/Tests/tests_main.cpp b/Tests/tests_main.cpp new file mode 100644 index 000000000..2e1c53d1d --- /dev/null +++ b/Tests/tests_main.cpp @@ -0,0 +1,49 @@ +/*********************************************************************************/ +/* */ +/* Copyright (C) 2024 Saif Kandil (k0T0z) */ +/* */ +/* This file is a part of the ENIGMA Development Environment. */ +/* */ +/* */ +/* ENIGMA is free software: you can redistribute it and/or modify it under the */ +/* terms of the GNU General Public License as published by the Free Software */ +/* Foundation, version 3 of the license or any later version. */ +/* */ +/* This application and its source code is distributed AS-IS, WITHOUT ANY */ +/* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS */ +/* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more */ +/* details. */ +/* */ +/* You should have recieved a copy of the GNU General Public License along */ +/* with this code. If not, see */ +/* */ +/* ENIGMA is an environment designed to create games and other programs with a */ +/* high-level, fully compilable language. Developers of ENIGMA or anything */ +/* associated with ENIGMA are in no way responsible for its users or */ +/* applications created by its users, or damages caused by the environment */ +/* or programs made in the environment. */ +/* */ +/*********************************************************************************/ + +#include + +#include "MainWindowTests.h" +#include "Editors/VisualShaderEditorTests.h" + +#define RUN_TEST(CLASS_NAME) \ + { \ + ::QTest::Internal::callInitMain(); \ + CLASS_NAME t; \ + int r {QTest::qExec(&t, argc, argv)}; \ + if (r != 0) return 1; \ + } + +int main(int argc, char* argv[]) { + QApplication app(argc, argv); + app.setAttribute(Qt::AA_Use96Dpi, true); + + RUN_TEST(TestVisualShaderEditor); + // RUN_TEST(TestMainWindow); + + return 0; +} diff --git a/main.cpp b/main.cpp index 5694ab251..35868ddd4 100644 --- a/main.cpp +++ b/main.cpp @@ -1,4 +1,3 @@ -#include "main.h" #include "MainWindow.h" #include "Dialogs/PreferencesKeys.h" @@ -8,8 +7,6 @@ #include #include -QString defaultStyle = ""; - int main(int argc, char *argv[]) { // must enable high DPI before QApplication constructor QApplication::setAttribute(Qt::AA_EnableHighDpiScaling); diff --git a/main.h b/main.h deleted file mode 100644 index 62bf1da0e..000000000 --- a/main.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef MAIN_H -#define MAIN_H - -#include - -// Qt doesn't have a way of getting the default style -// so we store it for later when the user restores -// default settings so we can apply it again -extern QString defaultStyle; - -#endif // MAIN_H From 85a5e51ec2de77da04df0186bc10921619248e60 Mon Sep 17 00:00:00 2001 From: Saif Kandil <74428638+k0T0z@users.noreply.github.com> Date: Sat, 21 Sep 2024 15:19:05 +0300 Subject: [PATCH 28/56] Fixed how we calculate the connection cubic path and improved colors and themes --- Editors/VisualShaderEditor.cpp | 130 +++++++++++++++++---------------- Editors/VisualShaderEditor.h | 29 ++++---- 2 files changed, 83 insertions(+), 76 deletions(-) diff --git a/Editors/VisualShaderEditor.cpp b/Editors/VisualShaderEditor.cpp index 39298bbd2..de46a52c9 100644 --- a/Editors/VisualShaderEditor.cpp +++ b/Editors/VisualShaderEditor.cpp @@ -655,6 +655,9 @@ bool VisualShaderGraphicsScene::add_node(const int& n_id, const std::shared_ptr< std::cout << "Node is out of view bounds" << std::endl; } + // Create the embedded widgets + // TODO + VisualShaderNodeGraphicsObject* n_o{new VisualShaderNodeGraphicsObject(n_id, coordinate, n)}; QObject::connect(n_o, &VisualShaderNodeGraphicsObject::node_deleted, this, &VisualShaderGraphicsScene::on_node_deleted); @@ -1536,7 +1539,7 @@ QRectF VisualShaderNodeGraphicsObject::boundingRect() const { // Calculate the rect padding // We add a safe area around the rect to make it easier to get an accurate coordinate of the size - this->rect_padding = rect_width * 0.15f; + this->rect_padding = rect_width * 0.08f; r.adjust(-rect_margin - rect_padding, -rect_margin - rect_padding, rect_margin + rect_padding, rect_margin + rect_padding); @@ -1753,17 +1756,15 @@ VisualShaderInputPortGraphicsObject::VisualShaderInputPortGraphicsObject(const Q VisualShaderInputPortGraphicsObject::~VisualShaderInputPortGraphicsObject() {} QRectF VisualShaderInputPortGraphicsObject::boundingRect() const { - // rect.adjust(-padding, -padding, padding, padding); - - return rect; + QRectF t_rect{rect}; + t_rect.adjust(-padding, -padding, padding, padding); + return t_rect; } void VisualShaderInputPortGraphicsObject::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) { painter->setClipRect(option->exposedRect); - // rect.adjust(padding, padding, -padding, -padding); - painter->setBrush(this->connection_point_color); painter->drawEllipse(rect); @@ -1804,17 +1805,15 @@ VisualShaderOutputPortGraphicsObject::VisualShaderOutputPortGraphicsObject(const VisualShaderOutputPortGraphicsObject::~VisualShaderOutputPortGraphicsObject() {} QRectF VisualShaderOutputPortGraphicsObject::boundingRect() const { - // rect.adjust(-padding, -padding, padding, padding); - - return rect; + QRectF t_rect{rect}; + t_rect.adjust(-padding, -padding, padding, padding); + return t_rect; } void VisualShaderOutputPortGraphicsObject::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) { painter->setClipRect(option->exposedRect); - // rect.adjust(padding, padding, -padding, -padding); - painter->setBrush(this->connection_point_color); painter->drawEllipse(rect); @@ -1859,6 +1858,7 @@ VisualShaderConnectionGraphicsObject::VisualShaderConnectionGraphicsObject(const rect_padding(0.0f) { setFlag(QGraphicsItem::ItemIsFocusable, true); setFlag(QGraphicsItem::ItemIsSelectable, true); + setAcceptedMouseButtons(Qt::NoButton); setZValue(-1.0f); } @@ -1870,7 +1870,7 @@ QRectF VisualShaderConnectionGraphicsObject::boundingRect() const { // Calculate the rect padding // We add a safe area around the rect to make it easier to get an accurate coordinate of the size - this->rect_padding = 10.0f; + this->rect_padding = this->point_diameter; r.adjust(-rect_padding, -rect_padding, rect_padding, rect_padding); @@ -1982,30 +1982,40 @@ int VisualShaderConnectionGraphicsObject::detect_quadrant(const QPointF& referen QRectF VisualShaderConnectionGraphicsObject::calculate_bounding_rect_from_coordinates( const QPointF& start_coordinate, const QPointF& end_coordinate) const { - float x1{(float)start_coordinate.x()}; - float y1{(float)start_coordinate.y()}; - float x2{(float)end_coordinate.x()}; - float y2{(float)end_coordinate.y()}; + const float x1{(float)start_coordinate.x()}; + const float y1{(float)start_coordinate.y()}; + const float x2{(float)end_coordinate.x()}; + const float y2{(float)end_coordinate.y()}; // Calculate the expanded rect - float min_x{qMin(x1, x2)}; - float min_y{qMin(y1, y2)}; - float max_x{qMax(x1, x2)}; - float max_y{qMax(y1, y2)}; + const float min_x{qMin(x1, x2)}; + const float min_y{qMin(y1, y2)}; + const float max_x{qMax(x1, x2)}; + const float max_y{qMax(y1, y2)}; QRectF r({min_x, min_y}, QSizeF(max_x - min_x, max_y - min_y)); - bool in_abnormal_region{x2 < (x1 + min_h_distance)}; + const bool in_abnormal_region{x2 < (x1 + abnormal_offset)}; + + const int quadrant{detect_quadrant({x1, y1}, {x2, y2})}; - float a_width_expansion{((x1 + min_h_distance) - x2) * abnormal_face_to_back_control_width_expansion_factor}; + // We will expand the bounding rect horizontally so that our connection don't get cut off + const float a_width_expansion{((x1 + abnormal_offset) - x2) * abnormal_face_to_back_control_width_expansion_factor}; + const float a_height_expansion{a_width_expansion * abnormal_face_to_back_control_height_expansion_factor}; if (in_abnormal_region) { - // The connection is not going from left to right normally - // Our control points will be outside the end_coordinate and start_coordinate bounding rect - // We will expand the bounding rect horizontally to make it easier to get an accurate coordinate of the size r.adjust(-a_width_expansion, 0.0f, a_width_expansion, 0.0f); } + switch (quadrant) { + case 6: // On -ve X-axis: Abnormal face to back + // Elipse like curve + r.adjust(0.0f, -a_height_expansion, 0.0f, a_height_expansion); + break; + default: + break; + } + return r; } @@ -2014,28 +2024,31 @@ std::pair VisualShaderConnectionGraphicsObject::calculate_cont QPointF cp1; QPointF cp2; - float x1{(float)start_coordinate.x()}; - float y1{(float)start_coordinate.y()}; - float x2{(float)end_coordinate.x()}; - float y2{(float)end_coordinate.y()}; + const float x1{(float)start_coordinate.x()}; + const float y1{(float)start_coordinate.y()}; + const float x2{(float)end_coordinate.x()}; + const float y2{(float)end_coordinate.y()}; QRectF r{calculate_bounding_rect_from_coordinates(start_coordinate, end_coordinate)}; - bool in_abnormal_region{x2 < (x1 + min_h_distance)}; + const bool in_abnormal_region{x2 < (x1 + abnormal_offset)}; - int quadrant{detect_quadrant({x1, y1}, {x2, y2})}; + const int quadrant{detect_quadrant({x1, y1}, {x2, y2})}; - float face_to_face_control_width_expansion_factor{0.8f}; - float face_to_face_control_height_expansion_factor{0.25f}; + // We will expand the bounding rect horizontally so that our connection don't get cut off + const float a_width_expansion{((x1 + abnormal_offset) - x2) * abnormal_face_to_back_control_width_expansion_factor}; + const float a_height_expansion{a_width_expansion * abnormal_face_to_back_control_height_expansion_factor}; - float width_expansion{(x2 - x1) * face_to_face_control_width_expansion_factor}; + const float cp_x_delta_factor{0.8f}; + const float cp_y_delta_factor{0.25f}; - float a_width_expansion{((x1 + min_h_distance) - x2) * abnormal_face_to_back_control_width_expansion_factor}; - float a_height_expansion{a_width_expansion * abnormal_face_to_back_control_height_expansion_factor}; + // Normal region control points deltas + const float cp_x_delta{(float)r.width() * cp_x_delta_factor}; + const float cp_y_delta{(float)r.height() * cp_y_delta_factor}; - if (in_abnormal_region) { - r.adjust(-a_width_expansion, 0.0f, a_width_expansion, 0.0f); - } + // Abnormal region control points deltas + const float a_cp_x_delta{((float)r.width() - a_width_expansion) * cp_x_delta_factor}; + const float a_cp_y_delta{((float)r.height() - a_height_expansion) * cp_y_delta_factor}; switch (quadrant) { case 1: // Quadrant I: Normal face to back @@ -2049,32 +2062,32 @@ std::pair VisualShaderConnectionGraphicsObject::calculate_cont // This means we can't just send the path straight to the node. // Treated as inside Quadrant II - cp1 = {x1 + a_width_expansion, y1}; - cp2 = {x2 - a_width_expansion, y2}; + cp1 = {x1 + a_cp_x_delta, y1}; + cp2 = {x2 - a_cp_x_delta, y2}; } else { // Treated as inside Quadrant I - cp1 = {x1 + width_expansion, y1}; - cp2 = {x2 - width_expansion, y2}; + cp1 = {x1 + cp_x_delta, y1 - cp_y_delta}; + cp2 = {x2 - cp_x_delta, y2 + cp_y_delta}; } break; case 2: // Quadrant II: Abnormal face to back - cp1 = {x1 + a_width_expansion, y1}; - cp2 = {x2 - a_width_expansion, y2}; + cp1 = {x1 + a_cp_x_delta, y1}; + cp2 = {x2 - a_cp_x_delta, y2}; break; case 3: // Quadrant III: Abnormal face to back - cp1 = {x1 + a_width_expansion, y1 + a_height_expansion}; - cp2 = {x2 - a_width_expansion, y2 - a_height_expansion}; + cp1 = {x1 + a_width_expansion, y1}; + cp2 = {x2 - a_width_expansion, y2}; break; case 4: // Quadrant IV: Normal face to back if (in_abnormal_region) { // Treated as inside Quadrant III - cp1 = {x1 + a_width_expansion, y1}; - cp2 = {x2 - a_width_expansion, y2}; + cp1 = {x1 + a_cp_x_delta, y1}; + cp2 = {x2 - a_cp_x_delta, y2}; } else { // Treated as inside Quadrant IV - cp1 = {x1 + width_expansion, y1}; - cp2 = {x2 - width_expansion, y2}; + cp1 = {x1 + cp_x_delta, y1 + cp_y_delta}; + cp2 = {x2 - cp_x_delta, y2 - cp_y_delta}; } break; case 5: // On +ve X-axis: Normal face to back @@ -2083,19 +2096,14 @@ std::pair VisualShaderConnectionGraphicsObject::calculate_cont cp2 = {x2, y2}; break; case 6: // On -ve X-axis: Abnormal face to back - r.adjust(0.0f, -a_height_expansion, 0.0f, a_height_expansion); - cp1 = {x1 + a_width_expansion, y1}; - cp2 = {x2 - a_width_expansion, y2}; + // Elipse like curve + cp1 = {x1 + a_cp_x_delta, y1 - a_cp_y_delta}; + cp2 = {x2 - a_cp_x_delta, y2 - a_cp_y_delta}; break; case 7: // On +ve Y-axis: Abnormal face to back - r.adjust(0.0f, -a_height_expansion, 0.0f, a_height_expansion); - cp1 = {x1 + a_width_expansion, y1}; - cp2 = {x2 - a_width_expansion, y2}; - break; case 8: // On -ve Y-axis: Abnormal face to back - r.adjust(0.0f, -a_height_expansion, 0.0f, a_height_expansion); - cp1 = {x1 + a_width_expansion, y1}; - cp2 = {x2 - a_width_expansion, y2}; + cp1 = {x1 + a_cp_x_delta, y1}; + cp2 = {x2 - a_cp_x_delta, y2}; break; default: return std::make_pair(start_coordinate, end_coordinate); diff --git a/Editors/VisualShaderEditor.h b/Editors/VisualShaderEditor.h index 5e0c5b68e..0e9fc4055 100644 --- a/Editors/VisualShaderEditor.h +++ b/Editors/VisualShaderEditor.h @@ -389,9 +389,9 @@ class VisualShaderGraphicsView : public QGraphicsView { VisualShaderGraphicsScene* scene; // Style - QColor background_color = QColor(53, 53, 53); - QColor fine_grid_color = QColor(60, 60, 60); - QColor coarse_grid_color = QColor(25, 25, 25); + QColor background_color = QColor(40, 40, 40); // Dark Charcoal + QColor fine_grid_color = QColor(50, 50, 50); // Soft Dark Gray + QColor coarse_grid_color = QColor(30, 30, 30); // Muted Deep Gray // Scene Rect float t_size = std::numeric_limits::max(); // 32767 @@ -489,10 +489,10 @@ class VisualShaderNodeGraphicsObject : public QGraphicsObject { std::unordered_map out_port_graphics_objects; // Style - QColor normal_boundary_color = QColor(255, 255, 255); - QColor selected_boundary_color = QColor(255, 165, 0); - QColor font_color = QColor(255, 255, 255); - QColor fill_color = QColor(0, 0, 0, 0); + QColor normal_boundary_color = QColor(220, 20, 60); // Crimson Red + QColor selected_boundary_color = QColor(255, 69, 0); // Red-Orange + QColor font_color = QColor(255, 255, 255); // Pure White + QColor fill_color = QColor(40, 40, 40, 200); // Semi-transparent Dark Gray float pen_width = 1.0f; @@ -567,7 +567,7 @@ class VisualShaderInputPortGraphicsObject : public QGraphicsObject { // Style QColor font_color = QColor(255, 255, 255); - QColor connection_point_color = QColor(169, 169, 169); + QColor connection_point_color = QColor(220, 20, 60); // Crimson float opacity = 1.0f; @@ -621,7 +621,7 @@ class VisualShaderOutputPortGraphicsObject : public QGraphicsObject { // Style QColor font_color = QColor(255, 255, 255); - QColor connection_point_color = QColor(169, 169, 169); + QColor connection_point_color = QColor(220, 20, 60); // Crimson float opacity = 1.0f; @@ -683,11 +683,10 @@ class VisualShaderConnectionGraphicsObject : public QGraphicsObject { QPointF end_coordinate; // Style - QColor construction_color = QColor(169, 169, 169); - QColor normal_color = QColor(0, 255, 255); - QColor selected_color = QColor(100, 100, 100); - QColor selected_halo_color = QColor(255, 165, 0); - QColor connection_point_color = QColor(169, 169, 169); + QColor construction_color = QColor(139, 0, 0); // Dark Red + QColor normal_color = QColor(178, 34, 34); // Firebrick Red + QColor selected_color = QColor(55, 55, 55); // Dark Gray + QColor connection_point_color = QColor(211, 211, 211); float line_width = 3.0f; float construction_line_width = 2.0f; @@ -695,7 +694,7 @@ class VisualShaderConnectionGraphicsObject : public QGraphicsObject { mutable float rect_padding; // Calculated in boundingRect() - float min_h_distance = 50.0f; + float abnormal_offset = 50.0f; float abnormal_face_to_back_control_width_expansion_factor = 0.5f; float abnormal_face_to_back_control_height_expansion_factor = 2.0f; From 497406e8e26d62c10c3bc6404a546023be55af19 Mon Sep 17 00:00:00 2001 From: Saif Kandil <74428638+k0T0z@users.noreply.github.com> Date: Sun, 22 Sep 2024 00:46:28 +0300 Subject: [PATCH 29/56] Few improvements and bug fixes --- Editors/VisualShaderEditor.cpp | 260 +++++++++++++++++++-------------- Editors/VisualShaderEditor.h | 32 ++-- 2 files changed, 170 insertions(+), 122 deletions(-) diff --git a/Editors/VisualShaderEditor.cpp b/Editors/VisualShaderEditor.cpp index de46a52c9..2dea5fcfe 100644 --- a/Editors/VisualShaderEditor.cpp +++ b/Editors/VisualShaderEditor.cpp @@ -28,9 +28,6 @@ #include "Editors/VisualShaderEditor.h" #include -#include -#include -#include #include @@ -564,9 +561,11 @@ VisualShaderGraphicsScene::~VisualShaderGraphicsScene() {} bool VisualShaderGraphicsScene::add_node(const std::string& type, const QPointF& coordinate) { // Instantiate the node based on the type std::shared_ptr n; + QWidget* embed_widget {nullptr}; if (type == "VisualShaderNodeInput") { n = std::make_shared(); + embed_widget = new VisualShaderNodeInputEmbedWidget(std::dynamic_pointer_cast(n)); } else if (type == "VisualShaderNodeColorConstant") { n = std::make_shared(); } else if (type == "VisualShaderNodeBooleanConstant") { @@ -622,10 +621,10 @@ bool VisualShaderGraphicsScene::add_node(const std::string& type, const QPointF& return false; } - return VisualShaderGraphicsScene::add_node(n_id, n, coordinate); + return VisualShaderGraphicsScene::add_node(n_id, n, coordinate, embed_widget); } -bool VisualShaderGraphicsScene::add_node(const int& n_id, const std::shared_ptr& n, const QPointF& coordinate) { +bool VisualShaderGraphicsScene::add_node(const int& n_id, const std::shared_ptr& n, const QPointF& coordinate, QWidget* embed_widget) { // Make sure the node doesn't already exist, we don't want to overwrite a node. if (node_graphics_objects.find(n_id) != node_graphics_objects.end()) { return false; @@ -653,23 +652,30 @@ bool VisualShaderGraphicsScene::add_node(const int& n_id, const std::shared_ptr< vs->get_node_coordinate(n_id).y < view->get_y() || vs->get_node_coordinate(n_id).y > view->get_y() + view->get_height()) { std::cout << "Node is out of view bounds" << std::endl; - } - - // Create the embedded widgets - // TODO + } VisualShaderNodeGraphicsObject* n_o{new VisualShaderNodeGraphicsObject(n_id, coordinate, n)}; + if (embed_widget) { + QGraphicsProxyWidget* embed_widget_proxy{new QGraphicsProxyWidget(n_o)}; + embed_widget_proxy->setWidget(embed_widget); + n_o->set_embed_widget(embed_widget); + } + QObject::connect(n_o, &VisualShaderNodeGraphicsObject::node_deleted, this, &VisualShaderGraphicsScene::on_node_deleted); node_graphics_objects[n_id] = n_o; - + addItem(n_o); return true; } bool VisualShaderGraphicsScene::delete_node(const int& n_id) { + if (node_graphics_objects.find(n_id) == node_graphics_objects.end()) { + return false; + } + const std::shared_ptr n{vs->get_node(n_id)}; if (!n) { @@ -1457,7 +1463,8 @@ VisualShaderNodeGraphicsObject::VisualShaderNodeGraphicsObject(const int& n_id, caption_rect_height(0.0f), rect_height(0.0f), rect_margin(0.0f), - rect_padding(0.0f) { + rect_padding(0.0f), + embed_widget(nullptr) { setFlag(QGraphicsItem::ItemDoesntPropagateOpacityToChildren, true); setFlag(QGraphicsItem::ItemIsFocusable, true); setFlag(QGraphicsItem::ItemIsMovable, true); @@ -1516,8 +1523,9 @@ QRectF VisualShaderNodeGraphicsObject::boundingRect() const { QString caption{QString::fromStdString(node->get_caption())}; - rect_width = (float)(fm.horizontalAdvance(caption, caption.length()) + 20.0f); - caption_rect_height = (float)((fm.height()) + 30.0f); + rect_width = (float)(fm.horizontalAdvance(caption, caption.length()) + caption_h_padding * 2.0f); + + caption_rect_height = (float)((fm.height()) + caption_v_padding * 2.0f); int max_num_ports{qMax(node->get_input_port_count(), node->get_output_port_count())}; @@ -1532,14 +1540,72 @@ QRectF VisualShaderNodeGraphicsObject::boundingRect() const { rect_height = t_rect_h; + // Correct node rect if it has an embed widget (if needed). + if (embed_widget) { + float embed_widget_width{(float)embed_widget->width()}; + + // Find biggest horizontal length of input port names + int in_p_count{node->get_input_port_count()}; + float max_in_p_width{0.0f}; + for (unsigned i{0}; i < in_p_count; ++i) { + QString p_n{QString::fromStdString(node->get_input_port_name(i))}; + + if (!p_n.isEmpty()) { + QFont f("Arial", port_caption_font_size); + QFontMetrics fm(f); + + float w{(float)fm.horizontalAdvance(p_n)}; + + if (w > max_in_p_width) { + max_in_p_width = w; + } + } + } + + // Find biggest horizontal length of output port names + int out_p_count{node->get_output_port_count()}; + float max_out_p_width{0.0f}; + for (unsigned i{0}; i < out_p_count; ++i) { + QString p_n{QString::fromStdString(node->get_output_port_name(i))}; + + if (!p_n.isEmpty()) { + QFont f("Arial", port_caption_font_size); + QFontMetrics fm(f); + + float w{(float)fm.horizontalAdvance(p_n)}; + + if (w > max_out_p_width) { + max_out_p_width = w; + } + } + } + + float calculated_rect {max_in_p_width + embed_widget_width + max_out_p_width + embed_widget_padding * 2.0f}; + + if (calculated_rect > rect_width) { + rect_width = calculated_rect; + } + + // Check the height + float calculated_height{caption_rect_height + + body_rect_header_height + + embed_widget->height() + + body_rect_footer_height + + embed_widget_padding * 2.0f}; + + if (calculated_height > rect_height) { + rect_height = calculated_height; + } + } + QRectF r({0.0f, 0.0f}, QSizeF(rect_width, rect_height)); // Calculate the margin - this->rect_margin = rect_width * 0.1f; + this->rect_margin = port_diameter * 0.5f + 5.0f; // 5.0f is the margin between the ports and the port names // Calculate the rect padding - // We add a safe area around the rect to make it easier to get an accurate coordinate of the size - this->rect_padding = rect_width * 0.08f; + // We add a safe area around the rect to prevent the ports from being cut off + this->rect_padding = port_diameter * 0.5f; r.adjust(-rect_margin - rect_padding, -rect_margin - rect_padding, rect_margin + rect_padding, rect_margin + rect_padding); @@ -1622,7 +1688,7 @@ void VisualShaderNodeGraphicsObject::paint(QPainter* painter, const QStyleOption for (unsigned i{0}; i < in_port_count; ++i) { QPointF port_coordinate{first_in_port_coordinate.x(), first_in_port_coordinate.y() + body_rect_port_step * i}; - QRectF port_rect(port_coordinate.x(), port_coordinate.y(), min_side * 0.1f, min_side * 0.1f); + QRectF port_rect(port_coordinate.x(), port_coordinate.y(), port_diameter, port_diameter); // Adjust the port rect to be centered port_rect.adjust(-port_rect.width() * 0.5f, -port_rect.height() * 0.5f, -port_rect.width() * 0.5f, @@ -1678,7 +1744,7 @@ void VisualShaderNodeGraphicsObject::paint(QPainter* painter, const QStyleOption for (unsigned i{0}; i < out_port_count; ++i) { QPointF port_coordinate{first_out_port_coordinate.x(), first_out_port_coordinate.y() + body_rect_port_step * i}; - QRectF port_rect(port_coordinate.x(), port_coordinate.y(), min_side * 0.1f, min_side * 0.1f); + QRectF port_rect(port_coordinate.x(), port_coordinate.y(), port_diameter, port_diameter); // Adjust the port rect to be centered port_rect.adjust(-port_rect.width() * 0.5f, -port_rect.height() * 0.5f, -port_rect.width() * 0.5f, @@ -1720,6 +1786,16 @@ void VisualShaderNodeGraphicsObject::paint(QPainter* painter, const QStyleOption dynamic_cast(scene()), &VisualShaderGraphicsScene::on_port_dropped); } } + + { + // Correct the position of the embed widget + if (embed_widget) { + float embed_widget_x{rect_x + rect_w * 0.5f - embed_widget->width() * 0.5f}; + float embed_widget_y{rect_y + caption_rect_height + body_rect_header_height + embed_widget_padding}; + + embed_widget->setGeometry(embed_widget_x, embed_widget_y, embed_widget->width(), embed_widget->height()); + } + } } QVariant VisualShaderNodeGraphicsObject::itemChange(GraphicsItemChange change, const QVariant& value) { @@ -1869,7 +1945,8 @@ QRectF VisualShaderConnectionGraphicsObject::boundingRect() const { QRectF r{calculate_bounding_rect_from_coordinates(start_coordinate, end_coordinate)}; // Calculate the rect padding - // We add a safe area around the rect to make it easier to get an accurate coordinate of the size + // We add a safe area around the rect to prevent the ports from being cut off + // Due to inaccuracy in the calculation of the bounding rect we use the point diameter not the radius this->rect_padding = this->point_diameter; r.adjust(-rect_padding, -rect_padding, rect_padding, rect_padding); @@ -1882,25 +1959,7 @@ void VisualShaderConnectionGraphicsObject::paint(QPainter* painter, const QStyle painter->setClipRect(option->exposedRect); { - QPen pen; - pen.setWidth(this->line_width); - pen.setColor(this->construction_color); - pen.setStyle(Qt::DashLine); - - painter->setPen(pen); - painter->setBrush(Qt::NoBrush); - - std::pair control_points{calculate_control_points(start_coordinate, end_coordinate)}; - - QPainterPath cubic(start_coordinate); - cubic.cubicTo(control_points.first, control_points.second, end_coordinate); - - // cubic spline - painter->drawPath(cubic); - } - - { - // draw normal line + // Draw the connection QPen p; p.setWidth(this->line_width); @@ -1995,22 +2054,27 @@ QRectF VisualShaderConnectionGraphicsObject::calculate_bounding_rect_from_coordi QRectF r({min_x, min_y}, QSizeF(max_x - min_x, max_y - min_y)); - const bool in_abnormal_region{x2 < (x1 + abnormal_offset)}; + const bool in_h_abnormal_region{x2 < (x1 + h_abnormal_offset)}; + const bool in_v_abnormal_region{std::abs(y2 - y1) < v_abnormal_offset}; const int quadrant{detect_quadrant({x1, y1}, {x2, y2})}; // We will expand the bounding rect horizontally so that our connection don't get cut off - const float a_width_expansion{((x1 + abnormal_offset) - x2) * abnormal_face_to_back_control_width_expansion_factor}; + const float a_width_expansion{((x1 + h_abnormal_offset) - x2) * abnormal_face_to_back_control_width_expansion_factor}; const float a_height_expansion{a_width_expansion * abnormal_face_to_back_control_height_expansion_factor}; - if (in_abnormal_region) { + if (in_h_abnormal_region) { r.adjust(-a_width_expansion, 0.0f, a_width_expansion, 0.0f); } switch (quadrant) { + case 2: // Quadrant II: Abnormal face to back + case 3: // Quadrant III: Abnormal face to back case 6: // On -ve X-axis: Abnormal face to back // Elipse like curve - r.adjust(0.0f, -a_height_expansion, 0.0f, a_height_expansion); + if (in_v_abnormal_region) { + r.adjust(0.0f, -a_height_expansion, 0.0f, a_height_expansion); + } break; default: break; @@ -2031,12 +2095,13 @@ std::pair VisualShaderConnectionGraphicsObject::calculate_cont QRectF r{calculate_bounding_rect_from_coordinates(start_coordinate, end_coordinate)}; - const bool in_abnormal_region{x2 < (x1 + abnormal_offset)}; + const bool in_h_abnormal_region{x2 < (x1 + h_abnormal_offset)}; + const bool in_v_abnormal_region{std::abs(y2 - y1) < v_abnormal_offset}; const int quadrant{detect_quadrant({x1, y1}, {x2, y2})}; // We will expand the bounding rect horizontally so that our connection don't get cut off - const float a_width_expansion{((x1 + abnormal_offset) - x2) * abnormal_face_to_back_control_width_expansion_factor}; + const float a_width_expansion{((x1 + h_abnormal_offset) - x2) * abnormal_face_to_back_control_width_expansion_factor}; const float a_height_expansion{a_width_expansion * abnormal_face_to_back_control_height_expansion_factor}; const float cp_x_delta_factor{0.8f}; @@ -2053,7 +2118,7 @@ std::pair VisualShaderConnectionGraphicsObject::calculate_cont switch (quadrant) { case 1: // Quadrant I: Normal face to back // Find out if the connection is going from left to right normally - if (in_abnormal_region) { + if (in_h_abnormal_region) { // The connection is not going from left to right normally // Our control points will be outside the end_coordinate and start_coordinate bounding rect // We will expand the bounding rect horizontally to make it easier to get an accurate coordinate of the size @@ -2064,7 +2129,6 @@ std::pair VisualShaderConnectionGraphicsObject::calculate_cont // Treated as inside Quadrant II cp1 = {x1 + a_cp_x_delta, y1}; cp2 = {x2 - a_cp_x_delta, y2}; - } else { // Treated as inside Quadrant I cp1 = {x1 + cp_x_delta, y1 - cp_y_delta}; @@ -2072,15 +2136,25 @@ std::pair VisualShaderConnectionGraphicsObject::calculate_cont } break; case 2: // Quadrant II: Abnormal face to back - cp1 = {x1 + a_cp_x_delta, y1}; - cp2 = {x2 - a_cp_x_delta, y2}; + if (in_v_abnormal_region) { + cp1 = {x1 + a_cp_x_delta, y1 - a_cp_y_delta}; + cp2 = {x2 - a_cp_x_delta, y2 - a_cp_y_delta}; + } else { + cp1 = {x1 + a_cp_x_delta, y1}; + cp2 = {x2 - a_cp_x_delta, y2}; + } break; case 3: // Quadrant III: Abnormal face to back - cp1 = {x1 + a_width_expansion, y1}; - cp2 = {x2 - a_width_expansion, y2}; + if (in_v_abnormal_region) { + cp1 = {x1 + a_cp_x_delta, y1 - a_cp_y_delta}; + cp2 = {x2 - a_cp_x_delta, y2 - a_cp_y_delta}; + } else { + cp1 = {x1 + a_width_expansion, y1}; + cp2 = {x2 - a_width_expansion, y2}; + } break; case 4: // Quadrant IV: Normal face to back - if (in_abnormal_region) { + if (in_h_abnormal_region) { // Treated as inside Quadrant III cp1 = {x1 + a_cp_x_delta, y1}; cp2 = {x2 - a_cp_x_delta, y2}; @@ -2116,77 +2190,41 @@ std::pair VisualShaderConnectionGraphicsObject::calculate_cont /**********************************************************************/ /**********************************************************************/ /***** *****/ -/***** NodesCustomWidget *****/ +/***** Embed Widgets *****/ /***** *****/ /**********************************************************************/ /**********************************************************************/ /**********************************************************************/ -NodesCustomWidget::NodesCustomWidget(const std::shared_ptr& node, QWidget* parent) - : QWidget(parent), layout(nullptr) { - // Create the main layout. - layout = new QVBoxLayout(this); - layout->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom - layout->setSizeConstraint(QLayout::SetMinimumSize); - layout->setSpacing(0); - layout->setAlignment(Qt::AlignVCenter | Qt::AlignHCenter); +VisualShaderNodeInputEmbedWidget::VisualShaderNodeInputEmbedWidget(const std::shared_ptr& node) : QComboBox(), + node(node) { + setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom - //////////////// End of Header //////////////// + // Add the default item + addItem(QString::fromStdString(node->get_input_name()), ""); - combo_boxes[0] = new QComboBox(); - combo_boxes[0]->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); - combo_boxes[0]->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom - combo_boxes[0]->setMaximumSize(combo_boxes[0]->sizeHint()); - - // Add items to the combo box - combo_boxes[0]->addItem("Item 1"); - combo_boxes[0]->addItem("Item 2"); - combo_boxes[0]->addItem("Item 3"); - - layout->addWidget(combo_boxes[0]); - - // Connect the combo box signal to the slot - QObject::connect(combo_boxes[0], QOverload::of(&QComboBox::currentIndexChanged), this, - &NodesCustomWidget::on_combo_box0_current_index_changed); - - if (std::dynamic_pointer_cast(node)) { - } else if (std::dynamic_pointer_cast(node)) { - } else if (std::dynamic_pointer_cast(node)) { - } else if (std::dynamic_pointer_cast(node)) { - } else if (std::dynamic_pointer_cast(node)) { - } else if (std::dynamic_pointer_cast(node)) { - } else if (std::dynamic_pointer_cast(node)) { - } else if (std::dynamic_pointer_cast(node)) { - } else if (std::dynamic_pointer_cast(node)) { - } else if (std::dynamic_pointer_cast(node)) { - } else if (std::dynamic_pointer_cast(node)) { - } else if (std::dynamic_pointer_cast(node)) { - } else if (std::dynamic_pointer_cast(node)) { - } else if (std::dynamic_pointer_cast(node)) { - } else if (std::dynamic_pointer_cast(node)) { - } else if (std::dynamic_pointer_cast(node)) { - } else if (std::dynamic_pointer_cast(node)) { - } else if (std::dynamic_pointer_cast(node)) { - } else if (std::dynamic_pointer_cast(node)) { - } else if (std::dynamic_pointer_cast(node)) { - } else if (std::dynamic_pointer_cast(node)) { - } else if (std::dynamic_pointer_cast(node)) { - } else { - std::cout << "--- Unknown node type ---" << std::endl; - } + const VisualShaderNodeInput::Port* ps {VisualShaderNodeInput::get_ports()}; - //////////////// Start of Footer //////////////// - - // TODO: Set the size of this widget based on the contents. + int i{0}; - this->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom - // this->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + while (ps[i].type != VisualShaderNode::PortType::PORT_TYPE_ENUM_SIZE) { + addItem(QString::fromStdString(ps[i].name)); + i++; + } - this->setLayout(layout); + QObject::connect(this, + QOverload::of(&QComboBox::currentIndexChanged), + this, + &VisualShaderNodeInputEmbedWidget::on_current_index_changed); } -NodesCustomWidget::~NodesCustomWidget() {} +VisualShaderNodeInputEmbedWidget::~VisualShaderNodeInputEmbedWidget() {} -void NodesCustomWidget::on_combo_box0_current_index_changed(const int& index) { - std::cout << "Combo box index changed: " << index << std::endl; +void VisualShaderNodeInputEmbedWidget::on_current_index_changed(const int& index) { + const std::shared_ptr n{std::dynamic_pointer_cast(node)}; + if (!n) { + return; + } + n->set_input_name(itemText(index).toStdString()); } diff --git a/Editors/VisualShaderEditor.h b/Editors/VisualShaderEditor.h index 0e9fc4055..e231f008a 100644 --- a/Editors/VisualShaderEditor.h +++ b/Editors/VisualShaderEditor.h @@ -46,6 +46,7 @@ #include #include #include +#include #include #include @@ -61,6 +62,7 @@ class VisualShaderConnectionGraphicsObject; class CreateNodeDialog; class VisualShaderInputPortGraphicsObject; class VisualShaderOutputPortGraphicsObject; +class VisualShaderNodeInputEmbedWidget; /**********************************************************************/ /**********************************************************************/ @@ -248,7 +250,7 @@ class VisualShaderGraphicsScene : public QGraphicsScene { ~VisualShaderGraphicsScene(); bool add_node(const std::string& type, const QPointF& coordinate); - bool add_node(const int& n_id, const std::shared_ptr& n, const QPointF& coordinate); + bool add_node(const int& n_id, const std::shared_ptr& n, const QPointF& coordinate, QWidget* embed_widget = nullptr); bool delete_node(const int& n_id); VisualShaderEditor* get_editor() const { return editor; } @@ -452,6 +454,8 @@ class VisualShaderNodeGraphicsObject : public QGraphicsObject { VisualShaderInputPortGraphicsObject* get_input_port_graphics_object(const int& p_index) const; VisualShaderOutputPortGraphicsObject* get_output_port_graphics_object(const int& p_index) const; + void set_embed_widget(QWidget* embed_widget) { this->embed_widget = embed_widget; } + Q_SIGNALS: /** * @brief Send a request to delete a node. @@ -498,6 +502,10 @@ class VisualShaderNodeGraphicsObject : public QGraphicsObject { float opacity = 0.8f; float corner_radius = 3.0f; + float port_diameter = 8.0f; + + float caption_h_padding = 10.0f; + float caption_v_padding = 15.0f; mutable float rect_width; // Calculated in boundingRect() mutable float caption_rect_height; // Calculated in boundingRect() @@ -518,6 +526,9 @@ class VisualShaderNodeGraphicsObject : public QGraphicsObject { float caption_font_size = 18.0f; float port_caption_font_size = 8.0f; + QWidget* embed_widget; + float embed_widget_padding = 5.0f; + QRectF boundingRect() const override; void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget = nullptr) override; QVariant itemChange(GraphicsItemChange change, const QVariant& value) override; @@ -694,9 +705,10 @@ class VisualShaderConnectionGraphicsObject : public QGraphicsObject { mutable float rect_padding; // Calculated in boundingRect() - float abnormal_offset = 50.0f; + float h_abnormal_offset = 50.0f; + float v_abnormal_offset = 40.0f; float abnormal_face_to_back_control_width_expansion_factor = 0.5f; - float abnormal_face_to_back_control_height_expansion_factor = 2.0f; + float abnormal_face_to_back_control_height_expansion_factor = 1.0f; QRectF boundingRect() const override; void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget = nullptr) override; @@ -711,26 +723,24 @@ class VisualShaderConnectionGraphicsObject : public QGraphicsObject { /**********************************************************************/ /**********************************************************************/ /***** *****/ -/***** NodesCustomWidget *****/ +/***** Embed Widgets *****/ /***** *****/ /**********************************************************************/ /**********************************************************************/ /**********************************************************************/ -class NodesCustomWidget : public QWidget { +class VisualShaderNodeInputEmbedWidget : public QComboBox { Q_OBJECT public: - NodesCustomWidget(const std::shared_ptr& node, QWidget* parent = nullptr); - ~NodesCustomWidget(); + VisualShaderNodeInputEmbedWidget(const std::shared_ptr& node); + ~VisualShaderNodeInputEmbedWidget(); private Q_SLOTS: - void on_combo_box0_current_index_changed(const int& index); + void on_current_index_changed(const int& index); private: - QVBoxLayout* layout; - - QComboBox* combo_boxes[2]; + std::shared_ptr node; }; #endif // ENIGMA_VISUAL_SHADER_EDITOR_H From b0eb611e4b15340eddd502cc5d350f3b9b035e2f Mon Sep 17 00:00:00 2001 From: Saif Kandil <74428638+k0T0z@users.noreply.github.com> Date: Sun, 22 Sep 2024 12:20:09 +0300 Subject: [PATCH 30/56] Added drop down to set the functions and operations and few improvements and bug fixes --- Editors/VisualShaderEditor.cpp | 317 +++++++++++++++++++++++++++++++-- Editors/VisualShaderEditor.h | 154 +++++++++++++++- 2 files changed, 455 insertions(+), 16 deletions(-) diff --git a/Editors/VisualShaderEditor.cpp b/Editors/VisualShaderEditor.cpp index 2dea5fcfe..65d0dc35b 100644 --- a/Editors/VisualShaderEditor.cpp +++ b/Editors/VisualShaderEditor.cpp @@ -31,8 +31,6 @@ #include -#include "ResourceTransformations/VisualShader/visual_shader_nodes.h" -#include "ResourceTransformations/VisualShader/vs_noise_nodes.h" #include "VisualShader.pb.h" /**********************************************************************/ @@ -584,18 +582,24 @@ bool VisualShaderGraphicsScene::add_node(const std::string& type, const QPointF& n = std::make_shared(); } else if (type == "VisualShaderNodeFloatFunc") { n = std::make_shared(); + embed_widget = new VisualShaderNodeFloatFuncEmbedWidget(std::dynamic_pointer_cast(n)); } else if (type == "VisualShaderNodeIntFunc") { n = std::make_shared(); + embed_widget = new VisualShaderNodeIntFuncEmbedWidget(std::dynamic_pointer_cast(n)); } else if (type == "VisualShaderNodeUIntFunc") { n = std::make_shared(); + embed_widget = new VisualShaderNodeUIntFuncEmbedWidget(std::dynamic_pointer_cast(n)); } else if (type == "VisualShaderNodeDerivativeFunc") { n = std::make_shared(); } else if (type == "VisualShaderNodeFloatOp") { n = std::make_shared(); + embed_widget = new VisualShaderNodeFloatOpEmbedWidget(std::dynamic_pointer_cast(n)); } else if (type == "VisualShaderNodeIntOp") { n = std::make_shared(); + embed_widget = new VisualShaderNodeIntOpEmbedWidget(std::dynamic_pointer_cast(n)); } else if (type == "VisualShaderNodeUIntOp") { n = std::make_shared(); + embed_widget = new VisualShaderNodeUIntOpEmbedWidget(std::dynamic_pointer_cast(n)); } else if (type == "VisualShaderNodeValueNoise") { n = std::make_shared(); } else if (type == "VisualShaderNodeCompare") { @@ -1580,7 +1584,7 @@ QRectF VisualShaderNodeGraphicsObject::boundingRect() const { } } - float calculated_rect {max_in_p_width + embed_widget_width + max_out_p_width + embed_widget_padding * 2.0f}; + float calculated_rect {max_in_p_width + embed_widget_width + max_out_p_width + embed_widget_h_padding * 2.0f}; if (calculated_rect > rect_width) { rect_width = calculated_rect; @@ -1591,7 +1595,7 @@ QRectF VisualShaderNodeGraphicsObject::boundingRect() const { body_rect_header_height + embed_widget->height() + body_rect_footer_height + - embed_widget_padding * 2.0f}; + embed_widget_v_padding * 2.0f}; if (calculated_height > rect_height) { rect_height = calculated_height; @@ -1704,7 +1708,7 @@ void VisualShaderNodeGraphicsObject::paint(QPainter* painter, const QStyleOption QFontMetrics fm(f); painter->setFont(f); - float x{rect_x + 5.0f}; + float x{rect_x}; float y{(float)(port_rect.center().y()) + (float)((fm.ascent() + fm.descent()) * 0.5f - fm.descent())}; @@ -1760,7 +1764,7 @@ void VisualShaderNodeGraphicsObject::paint(QPainter* painter, const QStyleOption QFontMetrics fm(f); painter->setFont(f); - float x{rect_x + rect_w - (float)fm.horizontalAdvance(p_n) - 5.0f}; + float x{rect_x + rect_w - (float)fm.horizontalAdvance(p_n)}; float y{(float)(port_rect.center().y()) + (float)((fm.ascent() + fm.descent()) * 0.5f - fm.descent())}; @@ -1791,7 +1795,7 @@ void VisualShaderNodeGraphicsObject::paint(QPainter* painter, const QStyleOption // Correct the position of the embed widget if (embed_widget) { float embed_widget_x{rect_x + rect_w * 0.5f - embed_widget->width() * 0.5f}; - float embed_widget_y{rect_y + caption_rect_height + body_rect_header_height + embed_widget_padding}; + float embed_widget_y{rect_y + caption_rect_height + body_rect_header_height + embed_widget_v_padding}; embed_widget->setGeometry(embed_widget_x, embed_widget_y, embed_widget->width(), embed_widget->height()); } @@ -2222,9 +2226,298 @@ VisualShaderNodeInputEmbedWidget::VisualShaderNodeInputEmbedWidget(const std::sh VisualShaderNodeInputEmbedWidget::~VisualShaderNodeInputEmbedWidget() {} void VisualShaderNodeInputEmbedWidget::on_current_index_changed(const int& index) { - const std::shared_ptr n{std::dynamic_pointer_cast(node)}; - if (!n) { - return; - } - n->set_input_name(itemText(index).toStdString()); + node->set_input_name(itemText(index).toStdString()); +} + +/*************************************/ +/* Float Op Node */ +/*************************************/ + +VisualShaderNodeFloatOpEmbedWidget::VisualShaderNodeFloatOpEmbedWidget(const std::shared_ptr& node) : QComboBox(), + node(node) { + setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom + + addItem("Add", (int)VisualShaderNodeFloatOp::OP_ADD); + addItem("Subtract", (int)VisualShaderNodeFloatOp::OP_SUB); + addItem("Multiply", (int)VisualShaderNodeFloatOp::OP_MUL); + addItem("Divide", (int)VisualShaderNodeFloatOp::OP_DIV); + addItem("Modulus", (int)VisualShaderNodeFloatOp::OP_MOD); + addItem("Power", (int)VisualShaderNodeFloatOp::OP_POW); + addItem("Maximum", (int)VisualShaderNodeFloatOp::OP_MAX); + addItem("Minimum", (int)VisualShaderNodeFloatOp::OP_MIN); + addItem("Arc Tangent 2", (int)VisualShaderNodeFloatOp::OP_ATAN2); + addItem("Step", (int)VisualShaderNodeFloatOp::OP_STEP); + + QObject::connect(this, + QOverload::of(&QComboBox::currentIndexChanged), + this, + &VisualShaderNodeFloatOpEmbedWidget::on_current_index_changed); +} + +VisualShaderNodeFloatOpEmbedWidget::~VisualShaderNodeFloatOpEmbedWidget() {} + +void VisualShaderNodeFloatOpEmbedWidget::on_current_index_changed(const int& index) { + node->set_operator((VisualShaderNodeFloatOp::Operator)itemData(index).toInt()); +} + +/*************************************/ +/* Int Op Node */ +/*************************************/ + +VisualShaderNodeIntOpEmbedWidget::VisualShaderNodeIntOpEmbedWidget(const std::shared_ptr& node) : QComboBox(), + node(node) { + setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom + + addItem("Add", (int)VisualShaderNodeIntOp::OP_ADD); + addItem("Subtract", (int)VisualShaderNodeIntOp::OP_SUB); + addItem("Multiply", (int)VisualShaderNodeIntOp::OP_MUL); + addItem("Divide", (int)VisualShaderNodeIntOp::OP_DIV); + addItem("Modulus", (int)VisualShaderNodeIntOp::OP_MOD); + addItem("Maximum", (int)VisualShaderNodeIntOp::OP_MAX); + addItem("Minimum", (int)VisualShaderNodeIntOp::OP_MIN); + addItem("Bitwise AND", (int)VisualShaderNodeIntOp::OP_BITWISE_AND); + addItem("Bitwise OR", (int)VisualShaderNodeIntOp::OP_BITWISE_OR); + addItem("Bitwise XOR", (int)VisualShaderNodeIntOp::OP_BITWISE_XOR); + addItem("Bitwise Left Shift", (int)VisualShaderNodeIntOp::OP_BITWISE_LEFT_SHIFT); + addItem("Bitwise Right Shift", (int)VisualShaderNodeIntOp::OP_BITWISE_RIGHT_SHIFT); + + QObject::connect(this, + QOverload::of(&QComboBox::currentIndexChanged), + this, + &VisualShaderNodeIntOpEmbedWidget::on_current_index_changed); +} + +VisualShaderNodeIntOpEmbedWidget::~VisualShaderNodeIntOpEmbedWidget() {} + +void VisualShaderNodeIntOpEmbedWidget::on_current_index_changed(const int& index) { + node->set_operator((VisualShaderNodeIntOp::Operator)itemData(index).toInt()); +} + +/*************************************/ +/* UInt Op Node */ +/*************************************/ + +VisualShaderNodeUIntOpEmbedWidget::VisualShaderNodeUIntOpEmbedWidget(const std::shared_ptr& node) : QComboBox(), + node(node) { + setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom + + addItem("Add", (int)VisualShaderNodeUIntOp::OP_ADD); + addItem("Subtract", (int)VisualShaderNodeUIntOp::OP_SUB); + addItem("Multiply", (int)VisualShaderNodeUIntOp::OP_MUL); + addItem("Divide", (int)VisualShaderNodeUIntOp::OP_DIV); + addItem("Modulus", (int)VisualShaderNodeUIntOp::OP_MOD); + addItem("Maximum", (int)VisualShaderNodeUIntOp::OP_MAX); + addItem("Minimum", (int)VisualShaderNodeUIntOp::OP_MIN); + addItem("Bitwise AND", (int)VisualShaderNodeUIntOp::OP_BITWISE_AND); + addItem("Bitwise OR", (int)VisualShaderNodeUIntOp::OP_BITWISE_OR); + addItem("Bitwise XOR", (int)VisualShaderNodeUIntOp::OP_BITWISE_XOR); + addItem("Bitwise Left Shift", (int)VisualShaderNodeUIntOp::OP_BITWISE_LEFT_SHIFT); + addItem("Bitwise Right Shift", (int)VisualShaderNodeUIntOp::OP_BITWISE_RIGHT_SHIFT); + + QObject::connect(this, + QOverload::of(&QComboBox::currentIndexChanged), + this, + &VisualShaderNodeUIntOpEmbedWidget::on_current_index_changed); +} + +VisualShaderNodeUIntOpEmbedWidget::~VisualShaderNodeUIntOpEmbedWidget() {} + +void VisualShaderNodeUIntOpEmbedWidget::on_current_index_changed(const int& index) { + node->set_operator((VisualShaderNodeUIntOp::Operator)itemData(index).toInt()); +} + +/*************************************/ +/* Vector Op Node */ +/*************************************/ + +VisualShaderNodeVectorOpEmbedWidget::VisualShaderNodeVectorOpEmbedWidget(const std::shared_ptr& node) : QComboBox(), + node(node) { + setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom + + addItem("Add", (int)VisualShaderNodeVectorOp::OP_ADD); + addItem("Subtract", (int)VisualShaderNodeVectorOp::OP_SUB); + addItem("Multiply", (int)VisualShaderNodeVectorOp::OP_MUL); + addItem("Divide", (int)VisualShaderNodeVectorOp::OP_DIV); + addItem("Modulus", (int)VisualShaderNodeVectorOp::OP_MOD); + addItem("Power", (int)VisualShaderNodeVectorOp::OP_POW); + addItem("Maximum", (int)VisualShaderNodeVectorOp::OP_MAX); + addItem("Minimum", (int)VisualShaderNodeVectorOp::OP_MIN); + addItem("Cross Product", (int)VisualShaderNodeVectorOp::OP_CROSS); + addItem("Arc Tangent 2", (int)VisualShaderNodeVectorOp::OP_ATAN2); + addItem("Reflect", (int)VisualShaderNodeVectorOp::OP_REFLECT); + addItem("Step", (int)VisualShaderNodeVectorOp::OP_STEP); + + QObject::connect(this, + QOverload::of(&QComboBox::currentIndexChanged), + this, + &VisualShaderNodeVectorOpEmbedWidget::on_current_index_changed); +} + +VisualShaderNodeVectorOpEmbedWidget::~VisualShaderNodeVectorOpEmbedWidget() {} + +void VisualShaderNodeVectorOpEmbedWidget::on_current_index_changed(const int& index) { + node->set_operator((VisualShaderNodeVectorOp::Operator)itemData(index).toInt()); +} + +/*************************************/ +/* Float Funcs Node */ +/*************************************/ + +VisualShaderNodeFloatFuncEmbedWidget::VisualShaderNodeFloatFuncEmbedWidget(const std::shared_ptr& node) : QComboBox(), + node(node) { + setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom + + addItem("Sin", (int)VisualShaderNodeFloatFunc::FUNC_SIN); + addItem("Cos", (int)VisualShaderNodeFloatFunc::FUNC_COS); + addItem("Tan", (int)VisualShaderNodeFloatFunc::FUNC_TAN); + addItem("Arc Sine", (int)VisualShaderNodeFloatFunc::FUNC_ASIN); + addItem("Arc Cosine", (int)VisualShaderNodeFloatFunc::FUNC_ACOS); + addItem("Arc Tangent", (int)VisualShaderNodeFloatFunc::FUNC_ATAN); + addItem("Sine Hyperbolic", (int)VisualShaderNodeFloatFunc::FUNC_SINH); + addItem("Cosine Hyperbolic", (int)VisualShaderNodeFloatFunc::FUNC_COSH); + addItem("Tangent Hyperbolic", (int)VisualShaderNodeFloatFunc::FUNC_TANH); + addItem("Logarithm", (int)VisualShaderNodeFloatFunc::FUNC_LOG); + addItem("Exponential", (int)VisualShaderNodeFloatFunc::FUNC_EXP); + addItem("Square Root", (int)VisualShaderNodeFloatFunc::FUNC_SQRT); + addItem("Absolute", (int)VisualShaderNodeFloatFunc::FUNC_ABS); + addItem("Sign", (int)VisualShaderNodeFloatFunc::FUNC_SIGN); + addItem("Floor", (int)VisualShaderNodeFloatFunc::FUNC_FLOOR); + addItem("Round", (int)VisualShaderNodeFloatFunc::FUNC_ROUND); + addItem("Ceil", (int)VisualShaderNodeFloatFunc::FUNC_CEIL); + addItem("Fraction", (int)VisualShaderNodeFloatFunc::FUNC_FRACT); + addItem("Saturate", (int)VisualShaderNodeFloatFunc::FUNC_SATURATE); + addItem("Negate", (int)VisualShaderNodeFloatFunc::FUNC_NEGATE); + addItem("Arc Cosine Hyperbolic", (int)VisualShaderNodeFloatFunc::FUNC_ACOSH); + addItem("Arc Sine Hyperbolic", (int)VisualShaderNodeFloatFunc::FUNC_ASINH); + addItem("Arc Tangent Hyperbolic", (int)VisualShaderNodeFloatFunc::FUNC_ATANH); + addItem("Degrees", (int)VisualShaderNodeFloatFunc::FUNC_DEGREES); + addItem("Exponential 2", (int)VisualShaderNodeFloatFunc::FUNC_EXP2); + addItem("Inverse Square Root", (int)VisualShaderNodeFloatFunc::FUNC_INVERSE_SQRT); + addItem("Logarithm 2", (int)VisualShaderNodeFloatFunc::FUNC_LOG2); + addItem("Radians", (int)VisualShaderNodeFloatFunc::FUNC_RADIANS); + addItem("Reciprocal", (int)VisualShaderNodeFloatFunc::FUNC_RECIPROCAL); + addItem("Round Even", (int)VisualShaderNodeFloatFunc::FUNC_ROUNDEVEN); + addItem("Truncate", (int)VisualShaderNodeFloatFunc::FUNC_TRUNC); + addItem("One Minus", (int)VisualShaderNodeFloatFunc::FUNC_ONEMINUS); + + QObject::connect(this, + QOverload::of(&QComboBox::currentIndexChanged), + this, + &VisualShaderNodeFloatFuncEmbedWidget::on_current_index_changed); +} + +VisualShaderNodeFloatFuncEmbedWidget::~VisualShaderNodeFloatFuncEmbedWidget() {} + +void VisualShaderNodeFloatFuncEmbedWidget::on_current_index_changed(const int& index) { + node->set_function((VisualShaderNodeFloatFunc::Function)itemData(index).toInt()); +} + +/*************************************/ +/* Int Funcs Node */ +/*************************************/ + +VisualShaderNodeIntFuncEmbedWidget::VisualShaderNodeIntFuncEmbedWidget(const std::shared_ptr& node) : QComboBox(), + node(node) { + setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom + + addItem("Abs", (int)VisualShaderNodeIntFunc::FUNC_ABS); + addItem("Negate", (int)VisualShaderNodeIntFunc::FUNC_NEGATE); + addItem("Sign", (int)VisualShaderNodeIntFunc::FUNC_SIGN); + addItem("Bitwise NOT", (int)VisualShaderNodeIntFunc::FUNC_BITWISE_NOT); + + QObject::connect(this, + QOverload::of(&QComboBox::currentIndexChanged), + this, + &VisualShaderNodeIntFuncEmbedWidget::on_current_index_changed); +} + +VisualShaderNodeIntFuncEmbedWidget::~VisualShaderNodeIntFuncEmbedWidget() {} + +void VisualShaderNodeIntFuncEmbedWidget::on_current_index_changed(const int& index) { + node->set_function((VisualShaderNodeIntFunc::Function)itemData(index).toInt()); +} + +/*************************************/ +/* UInt Funcs Node */ +/*************************************/ + +VisualShaderNodeUIntFuncEmbedWidget::VisualShaderNodeUIntFuncEmbedWidget(const std::shared_ptr& node) : QComboBox(), + node(node) { + setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom + + addItem("Negate", (int)VisualShaderNodeUIntFunc::FUNC_NEGATE); + addItem("Bitwise NOT", (int)VisualShaderNodeUIntFunc::FUNC_BITWISE_NOT); + + QObject::connect(this, + QOverload::of(&QComboBox::currentIndexChanged), + this, + &VisualShaderNodeUIntFuncEmbedWidget::on_current_index_changed); +} + +VisualShaderNodeUIntFuncEmbedWidget::~VisualShaderNodeUIntFuncEmbedWidget() {} + +void VisualShaderNodeUIntFuncEmbedWidget::on_current_index_changed(const int& index) { + node->set_function((VisualShaderNodeUIntFunc::Function)itemData(index).toInt()); +} + +/*************************************/ +/* Vector Funcs Node */ +/*************************************/ + +VisualShaderNodeVectorFuncEmbedWidget::VisualShaderNodeVectorFuncEmbedWidget(const std::shared_ptr& node) : QComboBox(), + node(node) { + setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom + + addItem("Normalize", (int)VisualShaderNodeVectorFunc::FUNC_NORMALIZE); + addItem("Saturate", (int)VisualShaderNodeVectorFunc::FUNC_SATURATE); + addItem("Negate", (int)VisualShaderNodeVectorFunc::FUNC_NEGATE); + addItem("Reciprocal", (int)VisualShaderNodeVectorFunc::FUNC_RECIPROCAL); + addItem("Abs", (int)VisualShaderNodeVectorFunc::FUNC_ABS); + addItem("Arc Cosine", (int)VisualShaderNodeVectorFunc::FUNC_ACOS); + addItem("Arc Cosine Hyperbolic", (int)VisualShaderNodeVectorFunc::FUNC_ACOSH); + addItem("Arc Sine", (int)VisualShaderNodeVectorFunc::FUNC_ASIN); + addItem("Arc Sine Hyperbolic", (int)VisualShaderNodeVectorFunc::FUNC_ASINH); + addItem("Arc Tangent", (int)VisualShaderNodeVectorFunc::FUNC_ATAN); + addItem("Arc Tangent Hyperbolic", (int)VisualShaderNodeVectorFunc::FUNC_ATANH); + addItem("Ceil", (int)VisualShaderNodeVectorFunc::FUNC_CEIL); + addItem("Cos", (int)VisualShaderNodeVectorFunc::FUNC_COS); + addItem("Cosine Hyperbolic", (int)VisualShaderNodeVectorFunc::FUNC_COSH); + addItem("Degrees", (int)VisualShaderNodeVectorFunc::FUNC_DEGREES); + addItem("Exp", (int)VisualShaderNodeVectorFunc::FUNC_EXP); + addItem("Exp2", (int)VisualShaderNodeVectorFunc::FUNC_EXP2); + addItem("Floor", (int)VisualShaderNodeVectorFunc::FUNC_FLOOR); + addItem("Fraction", (int)VisualShaderNodeVectorFunc::FUNC_FRACT); + addItem("Inverse Square Root", (int)VisualShaderNodeVectorFunc::FUNC_INVERSE_SQRT); + addItem("Log", (int)VisualShaderNodeVectorFunc::FUNC_LOG); + addItem("Log2", (int)VisualShaderNodeVectorFunc::FUNC_LOG2); + addItem("Radians", (int)VisualShaderNodeVectorFunc::FUNC_RADIANS); + addItem("Round", (int)VisualShaderNodeVectorFunc::FUNC_ROUND); + addItem("Round Even", (int)VisualShaderNodeVectorFunc::FUNC_ROUNDEVEN); + addItem("Sign", (int)VisualShaderNodeVectorFunc::FUNC_SIGN); + addItem("Sin", (int)VisualShaderNodeVectorFunc::FUNC_SIN); + addItem("Sine Hyperbolic", (int)VisualShaderNodeVectorFunc::FUNC_SINH); + addItem("Sqrt", (int)VisualShaderNodeVectorFunc::FUNC_SQRT); + addItem("Tan", (int)VisualShaderNodeVectorFunc::FUNC_TAN); + addItem("Tangent Hyperbolic", (int)VisualShaderNodeVectorFunc::FUNC_TANH); + addItem("Truncate", (int)VisualShaderNodeVectorFunc::FUNC_TRUNC); + addItem("One Minus", (int)VisualShaderNodeVectorFunc::FUNC_ONEMINUS); + + QObject::connect(this, + QOverload::of(&QComboBox::currentIndexChanged), + this, + &VisualShaderNodeVectorFuncEmbedWidget::on_current_index_changed); +} + +VisualShaderNodeVectorFuncEmbedWidget::~VisualShaderNodeVectorFuncEmbedWidget() {} + +void VisualShaderNodeVectorFuncEmbedWidget::on_current_index_changed(const int& index) { + node->set_function((VisualShaderNodeVectorFunc::Function)itemData(index).toInt()); } diff --git a/Editors/VisualShaderEditor.h b/Editors/VisualShaderEditor.h index e231f008a..9c43db959 100644 --- a/Editors/VisualShaderEditor.h +++ b/Editors/VisualShaderEditor.h @@ -52,6 +52,8 @@ #include #include "ResourceTransformations/VisualShader/visual_shader.h" +#include "ResourceTransformations/VisualShader/visual_shader_nodes.h" +#include "ResourceTransformations/VisualShader/vs_noise_nodes.h" #include "Editors/BaseEditor.h" #include "Editors/CodeEditor.h" @@ -62,7 +64,6 @@ class VisualShaderConnectionGraphicsObject; class CreateNodeDialog; class VisualShaderInputPortGraphicsObject; class VisualShaderOutputPortGraphicsObject; -class VisualShaderNodeInputEmbedWidget; /**********************************************************************/ /**********************************************************************/ @@ -505,7 +506,7 @@ class VisualShaderNodeGraphicsObject : public QGraphicsObject { float port_diameter = 8.0f; float caption_h_padding = 10.0f; - float caption_v_padding = 15.0f; + float caption_v_padding = 8.0f; mutable float rect_width; // Calculated in boundingRect() mutable float caption_rect_height; // Calculated in boundingRect() @@ -527,7 +528,8 @@ class VisualShaderNodeGraphicsObject : public QGraphicsObject { float port_caption_font_size = 8.0f; QWidget* embed_widget; - float embed_widget_padding = 5.0f; + float embed_widget_h_padding = 10.0f; + float embed_widget_v_padding = 5.0f; QRectF boundingRect() const override; void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget = nullptr) override; @@ -740,7 +742,151 @@ class VisualShaderNodeInputEmbedWidget : public QComboBox { void on_current_index_changed(const int& index); private: - std::shared_ptr node; + std::shared_ptr node; +}; + +/*************************************/ +/* Float Op Node */ +/*************************************/ + +class VisualShaderNodeFloatOpEmbedWidget : public QComboBox { + Q_OBJECT + + public: + VisualShaderNodeFloatOpEmbedWidget(const std::shared_ptr& node); + ~VisualShaderNodeFloatOpEmbedWidget(); + + private Q_SLOTS: + void on_current_index_changed(const int& index); + + private: + std::shared_ptr node; +}; + +/*************************************/ +/* Int Op Node */ +/*************************************/ + +class VisualShaderNodeIntOpEmbedWidget : public QComboBox { + Q_OBJECT + + public: + VisualShaderNodeIntOpEmbedWidget(const std::shared_ptr& node); + ~VisualShaderNodeIntOpEmbedWidget(); + + private Q_SLOTS: + void on_current_index_changed(const int& index); + + private: + std::shared_ptr node; +}; + +/*************************************/ +/* UInt Op Node */ +/*************************************/ + +class VisualShaderNodeUIntOpEmbedWidget : public QComboBox { + Q_OBJECT + + public: + VisualShaderNodeUIntOpEmbedWidget(const std::shared_ptr& node); + ~VisualShaderNodeUIntOpEmbedWidget(); + + private Q_SLOTS: + void on_current_index_changed(const int& index); + + private: + std::shared_ptr node; +}; + +/*************************************/ +/* Vector Op Node */ +/*************************************/ + +class VisualShaderNodeVectorOpEmbedWidget : public QComboBox { + Q_OBJECT + + public: + VisualShaderNodeVectorOpEmbedWidget(const std::shared_ptr& node); + ~VisualShaderNodeVectorOpEmbedWidget(); + + private Q_SLOTS: + void on_current_index_changed(const int& index); + + private: + std::shared_ptr node; +}; + +/*************************************/ +/* Float Funcs Node */ +/*************************************/ + +class VisualShaderNodeFloatFuncEmbedWidget : public QComboBox { + Q_OBJECT + + public: + VisualShaderNodeFloatFuncEmbedWidget(const std::shared_ptr& node); + ~VisualShaderNodeFloatFuncEmbedWidget(); + + private Q_SLOTS: + void on_current_index_changed(const int& index); + + private: + std::shared_ptr node; +}; + +/*************************************/ +/* Int Funcs Node */ +/*************************************/ + +class VisualShaderNodeIntFuncEmbedWidget : public QComboBox { + Q_OBJECT + + public: + VisualShaderNodeIntFuncEmbedWidget(const std::shared_ptr& node); + ~VisualShaderNodeIntFuncEmbedWidget(); + + private Q_SLOTS: + void on_current_index_changed(const int& index); + + private: + std::shared_ptr node; +}; + +/*************************************/ +/* UInt Funcs Node */ +/*************************************/ + +class VisualShaderNodeUIntFuncEmbedWidget : public QComboBox { + Q_OBJECT + + public: + VisualShaderNodeUIntFuncEmbedWidget(const std::shared_ptr& node); + ~VisualShaderNodeUIntFuncEmbedWidget(); + + private Q_SLOTS: + void on_current_index_changed(const int& index); + + private: + std::shared_ptr node; +}; + +/*************************************/ +/* Vector Funcs Node */ +/*************************************/ + +class VisualShaderNodeVectorFuncEmbedWidget : public QComboBox { + Q_OBJECT + + public: + VisualShaderNodeVectorFuncEmbedWidget(const std::shared_ptr& node); + ~VisualShaderNodeVectorFuncEmbedWidget(); + + private Q_SLOTS: + void on_current_index_changed(const int& index); + + private: + std::shared_ptr node; }; #endif // ENIGMA_VISUAL_SHADER_EDITOR_H From 6a8c6ea9cd7af9b093300323e85a8defde327196 Mon Sep 17 00:00:00 2001 From: Saif Kandil <74428638+k0T0z@users.noreply.github.com> Date: Mon, 23 Sep 2024 16:00:21 +0300 Subject: [PATCH 31/56] Remove the nodeeditor package --- .gitmodules | 3 --- CMakeLists.txt | 4 ---- Submodules/nodeeditor | 1 - 3 files changed, 8 deletions(-) delete mode 160000 Submodules/nodeeditor diff --git a/.gitmodules b/.gitmodules index 9615bb04f..e1229a7ac 100644 --- a/.gitmodules +++ b/.gitmodules @@ -2,6 +2,3 @@ path = Submodules/enigma-dev url = ../enigma-dev.git branch = master -[submodule "Submodules/nodeeditor"] - path = Submodules/nodeeditor - url = https://github.com/k0T0z/nodeeditor.git diff --git a/CMakeLists.txt b/CMakeLists.txt index ba2ecc5ca..2a4295745 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -372,10 +372,6 @@ target_link_libraries(${EXE} PRIVATE ${LIB_PCRE2}) find_library(LIB_DOUBLE_CONVERSION NAMES double-conversion) target_link_libraries(${EXE} PRIVATE ${LIB_DOUBLE_CONVERSION}) -# nodeeditor -add_subdirectory(Submodules/nodeeditor) -target_link_libraries(${EXE} PRIVATE QtNodes::QtNodes) - if(RGM_BUILD_TESTS) add_subdirectory(Tests) endif() diff --git a/Submodules/nodeeditor b/Submodules/nodeeditor deleted file mode 160000 index cb1c1c564..000000000 --- a/Submodules/nodeeditor +++ /dev/null @@ -1 +0,0 @@ -Subproject commit cb1c1c564f284b02a14a08cd0836db2f7ecfc862 From 1568bafe42240f2f75847b50cd926f9ff9e48bb2 Mon Sep 17 00:00:00 2001 From: Saif Kandil <74428638+k0T0z@users.noreply.github.com> Date: Wed, 25 Sep 2024 17:44:40 +0300 Subject: [PATCH 32/56] Changed the name of the Visual Shader message so that it doesnt conflict with the VisualShader class --- Editors/VisualShaderEditor.cpp | 96 ++++++++++++++++++++++++++++++++-- Editors/VisualShaderEditor.h | 16 ++++-- MainWindow.cpp | 4 +- 3 files changed, 105 insertions(+), 11 deletions(-) diff --git a/Editors/VisualShaderEditor.cpp b/Editors/VisualShaderEditor.cpp index 65d0dc35b..ceba62a63 100644 --- a/Editors/VisualShaderEditor.cpp +++ b/Editors/VisualShaderEditor.cpp @@ -27,11 +27,9 @@ #include "Editors/VisualShaderEditor.h" -#include - #include -#include "VisualShader.pb.h" +#include "EVisualShader.pb.h" /**********************************************************************/ /**********************************************************************/ @@ -43,7 +41,32 @@ /**********************************************************************/ /**********************************************************************/ -VisualShaderEditor::VisualShaderEditor(QWidget* parent) : BaseEditor(parent) { +VisualShaderEditor::VisualShaderEditor(QWidget* parent) + : BaseEditor(parent), + visual_shader(nullptr), + layout(nullptr), + side_widget(nullptr), + side_layout(nullptr), + name_edit(nullptr), + scene_layer_layout(nullptr), + scene_layer(nullptr), + scene(nullptr), + view(nullptr), + top_layer(nullptr), + menu_bar(nullptr), + menu_button(nullptr), + create_node_button(nullptr), + preview_shader_button(nullptr), + create_node_action(nullptr), + zoom_in_button(nullptr), + reset_zoom_button(nullptr), + zoom_out_button(nullptr), + load_image_button(nullptr), + match_image_button(nullptr), + create_node_dialog(nullptr), + code_previewer_dialog(nullptr), + code_previewer_layout(nullptr), + code_previewer(nullptr) { VisualShaderEditor::init(); } @@ -51,12 +74,16 @@ VisualShaderEditor::VisualShaderEditor(MessageModel* model, QWidget* parent) : BaseEditor(model, parent), visual_shader(nullptr), layout(nullptr), + side_widget(nullptr), + side_layout(nullptr), + name_edit(nullptr), scene_layer_layout(nullptr), scene_layer(nullptr), scene(nullptr), view(nullptr), top_layer(nullptr), menu_bar(nullptr), + menu_button(nullptr), create_node_button(nullptr), preview_shader_button(nullptr), create_node_action(nullptr), @@ -70,6 +97,9 @@ VisualShaderEditor::VisualShaderEditor(MessageModel* model, QWidget* parent) code_previewer_layout(nullptr), code_previewer(nullptr) { VisualShaderEditor::init(); + + _nodeMapper->addMapping(name_edit, TreeNode::kNameFieldNumber); + // visual_shader_model = _model->GetSubModel(TreeNode::kVisualShaderFieldNumber); } VisualShaderEditor::~VisualShaderEditor() { @@ -88,6 +118,36 @@ void VisualShaderEditor::init() { //////////////// End of Header //////////////// + // Create the side widget + side_widget = new QWidget(); + side_widget->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + side_widget->setContentsMargins(10, 10, 10, 10); // Left, top, right, bottom + side_widget->setVisible(false); + + // Add the vertical layout + side_layout = new QVBoxLayout(side_widget); + side_layout->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom + side_layout->setSpacing(5); + side_layout->setAlignment(Qt::AlignTop | Qt::AlignLeft); + side_layout->setSizeConstraint(QLayout::SetNoConstraint); + + // Fill in the left layout + QHBoxLayout* name_layout = new QHBoxLayout(); + name_layout->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom + name_layout->setSpacing(5); + name_layout->setAlignment(Qt::AlignTop | Qt::AlignLeft); + name_layout->setSizeConstraint(QLayout::SetNoConstraint); + + QLabel* name_label = new QLabel("Name"); + name_layout->addWidget(name_label, 1); + + name_edit = new QLineEdit(); + name_layout->addWidget(name_edit, 4); + + side_layout->addLayout(name_layout); + + side_widget->setLayout(side_layout); + // Create the scene layer. scene_layer = new QWidget(); scene_layer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); @@ -126,10 +186,19 @@ void VisualShaderEditor::init() { menu_bar->setAlignment(Qt::AlignTop | Qt::AlignLeft); menu_bar->setSizeConstraint(QLayout::SetMinimumSize); + // Create the menu button + menu_button = new QPushButton("Show Menu", top_layer); + menu_button->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + menu_button->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom + menu_button->setToolTip("Toggle Menu"); + menu_bar->addWidget(menu_button); + QObject::connect(menu_button, &QPushButton::pressed, this, &VisualShaderEditor::on_menu_button_pressed); + // Create the create node button. create_node_button = new QPushButton("Create Node", top_layer); create_node_button->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); create_node_button->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom + create_node_button->setToolTip("Create a new node"); menu_bar->addWidget(create_node_button); QObject::connect(create_node_button, &QPushButton::pressed, this, &VisualShaderEditor::on_create_node_button_pressed); @@ -140,42 +209,51 @@ void VisualShaderEditor::init() { preview_shader_button = new QPushButton("Preview Shader", top_layer); preview_shader_button->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); preview_shader_button->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom + preview_shader_button->setToolTip("Preview the expected generated shader code"); menu_bar->addWidget(preview_shader_button); QObject::connect(preview_shader_button, &QPushButton::pressed, this, &VisualShaderEditor::on_preview_shader_button_pressed); zoom_in_button = new QPushButton("Zoom In", scene_layer); zoom_in_button->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); zoom_in_button->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom + zoom_in_button->setToolTip("Zoom In"); menu_bar->addWidget(zoom_in_button); QObject::connect(zoom_in_button, &QPushButton::pressed, view, &VisualShaderGraphicsView::zoom_in); reset_zoom_button = new QPushButton("Reset Zoom", scene_layer); reset_zoom_button->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); reset_zoom_button->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom + reset_zoom_button->setToolTip("Reset Zoom"); menu_bar->addWidget(reset_zoom_button); QObject::connect(reset_zoom_button, &QPushButton::pressed, view, &VisualShaderGraphicsView::reset_zoom); zoom_out_button = new QPushButton("Zoom Out", scene_layer); zoom_out_button->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); zoom_out_button->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom + zoom_out_button->setToolTip("Zoom Out"); menu_bar->addWidget(zoom_out_button); QObject::connect(zoom_out_button, &QPushButton::pressed, view, &VisualShaderGraphicsView::zoom_out); load_image_button = new QPushButton("Load Image", scene_layer); load_image_button->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); load_image_button->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom + load_image_button->setToolTip("Load an image to match"); menu_bar->addWidget(load_image_button); match_image_button = new QPushButton("Match Image", scene_layer); match_image_button->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); match_image_button->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom + match_image_button->setToolTip("Match the shader to the loaded image"); menu_bar->addWidget(match_image_button); // Set the top layer layout. top_layer->setLayout(menu_bar); + // Add the left layout + layout->addWidget(side_widget, 1); + // Add the scene layer to the main layout. - layout->addWidget(scene_layer); + layout->addWidget(scene_layer, 4); //////////////////////////////////// // Code Previewer @@ -388,6 +466,12 @@ void VisualShaderEditor::on_preview_shader_button_pressed() { code_previewer_dialog->exec(); } +void VisualShaderEditor::on_menu_button_pressed() { + bool is_visible{side_widget->isVisible()}; + side_widget->setVisible(!is_visible); + menu_button->setText(!is_visible ? "Hide Menu" : "Show Menu"); +} + std::vector VisualShaderEditor::parse_node_category_path(const std::string& node_category_path) { std::vector tokens; std::stringstream ss(node_category_path); @@ -495,12 +579,14 @@ CreateNodeDialog::CreateNodeDialog(QWidget* parent) QObject::connect(create_button, &QPushButton::pressed, this, &CreateNodeDialog::on_create_node_button_pressed); create_button->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); create_button->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom + create_button->setToolTip("Create the selected node."); cancel_button = new QPushButton("Cancel"); QObject::connect(cancel_button, &QPushButton::pressed, this, &CreateNodeDialog::on_cancel_node_creation_button_pressed); cancel_button->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); cancel_button->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom + cancel_button->setToolTip("Cancel the node creation."); // Add the buttons to the buttons layout. buttons_layout->addWidget(create_button); diff --git a/Editors/VisualShaderEditor.h b/Editors/VisualShaderEditor.h index 9c43db959..a572ba4b7 100644 --- a/Editors/VisualShaderEditor.h +++ b/Editors/VisualShaderEditor.h @@ -30,10 +30,8 @@ #include #include -#include #include #include -#include #include #include #include @@ -47,6 +45,8 @@ #include #include #include +#include +#include #include #include @@ -55,7 +55,6 @@ #include "ResourceTransformations/VisualShader/visual_shader_nodes.h" #include "ResourceTransformations/VisualShader/vs_noise_nodes.h" #include "Editors/BaseEditor.h" -#include "Editors/CodeEditor.h" class VisualShaderGraphicsScene; class VisualShaderGraphicsView; @@ -114,11 +113,18 @@ class VisualShaderEditor : public BaseEditor { void on_create_node_button_pressed(); void on_preview_shader_button_pressed(); + void on_menu_button_pressed(); + private: VisualShader* visual_shader; + MessageModel* visual_shader_model; QHBoxLayout* layout; + QWidget* side_widget; + QVBoxLayout* side_layout; + QLineEdit* name_edit; + QHBoxLayout* scene_layer_layout; QWidget* scene_layer; // Layer having the scene. VisualShaderGraphicsScene* scene; @@ -127,6 +133,7 @@ class VisualShaderEditor : public BaseEditor { QWidget* top_layer; // Layer having the menu bar. QHBoxLayout* menu_bar; + QPushButton* menu_button; QPushButton* create_node_button; QPushButton* preview_shader_button; QPushButton* zoom_in_button; @@ -170,7 +177,8 @@ class VisualShaderEditor : public BaseEditor { /** * @brief Initializes the UI * - * @note To be called from different constructors. + * @note To be called from different constructors. This function shouldn't contain + * any code related to MessageModel class as this will break the tests. * */ void init(); diff --git a/MainWindow.cpp b/MainWindow.cpp index f46eaef8a..c80ababbe 100644 --- a/MainWindow.cpp +++ b/MainWindow.cpp @@ -376,7 +376,7 @@ void MainWindow::openProject(std::unique_ptr openedProject) { treeConf.UseEditorLauncher(Launch(this)); treeConf.UseEditorLauncher(Launch(this)); treeConf.UseEditorLauncher(Launch(this)); - treeConf.UseEditorLauncher(Launch(this)); + treeConf.UseEditorLauncher(Launch(this)); treeConf.UseEditorLauncher(Launch(this)); treeConf.UseEditorLauncher(Launch(this)); treeConf.UseEditorLauncher(Launch(this)); @@ -391,7 +391,7 @@ void MainWindow::openProject(std::unique_ptr openedProject) { msgConf.SetDefaultIcon("path"); msgConf.SetDefaultIcon("script"); msgConf.SetDefaultIcon("shader"); - msgConf.SetDefaultIcon("visual_shader"); + msgConf.SetDefaultIcon("visual_shader"); msgConf.SetDefaultIcon("font"); msgConf.SetDefaultIcon("timeline"); msgConf.SetDefaultIcon("object"); From 88e810e7c410be864be3feede1c5f646a4d58097 Mon Sep 17 00:00:00 2001 From: Saif Kandil <74428638+k0T0z@users.noreply.github.com> Date: Wed, 25 Sep 2024 18:55:21 +0300 Subject: [PATCH 33/56] add the save button --- Editors/VisualShaderEditor.cpp | 27 ++++++++++++++++++++++++--- Editors/VisualShaderEditor.h | 2 ++ 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/Editors/VisualShaderEditor.cpp b/Editors/VisualShaderEditor.cpp index ceba62a63..c765d6ce6 100644 --- a/Editors/VisualShaderEditor.cpp +++ b/Editors/VisualShaderEditor.cpp @@ -46,8 +46,10 @@ VisualShaderEditor::VisualShaderEditor(QWidget* parent) visual_shader(nullptr), layout(nullptr), side_widget(nullptr), + side_outer_layout(nullptr), side_layout(nullptr), name_edit(nullptr), + save_button(nullptr), scene_layer_layout(nullptr), scene_layer(nullptr), scene(nullptr), @@ -75,8 +77,10 @@ VisualShaderEditor::VisualShaderEditor(MessageModel* model, QWidget* parent) visual_shader(nullptr), layout(nullptr), side_widget(nullptr), + side_outer_layout(nullptr), side_layout(nullptr), name_edit(nullptr), + save_button(nullptr), scene_layer_layout(nullptr), scene_layer(nullptr), scene(nullptr), @@ -99,6 +103,7 @@ VisualShaderEditor::VisualShaderEditor(MessageModel* model, QWidget* parent) VisualShaderEditor::init(); _nodeMapper->addMapping(name_edit, TreeNode::kNameFieldNumber); + QObject::connect(save_button, &QAbstractButton::pressed, this, &BaseEditor::OnSave); // visual_shader_model = _model->GetSubModel(TreeNode::kVisualShaderFieldNumber); } @@ -124,8 +129,15 @@ void VisualShaderEditor::init() { side_widget->setContentsMargins(10, 10, 10, 10); // Left, top, right, bottom side_widget->setVisible(false); - // Add the vertical layout - side_layout = new QVBoxLayout(side_widget); + // Create the side outer layout + side_outer_layout = new QVBoxLayout(side_widget); + side_outer_layout->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom + side_outer_layout->setSpacing(5); + side_outer_layout->setAlignment(Qt::AlignTop | Qt::AlignLeft); + side_outer_layout->setSizeConstraint(QLayout::SetNoConstraint); + + // Add the side inner layout + side_layout = new QVBoxLayout(); side_layout->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom side_layout->setSpacing(5); side_layout->setAlignment(Qt::AlignTop | Qt::AlignLeft); @@ -146,7 +158,16 @@ void VisualShaderEditor::init() { side_layout->addLayout(name_layout); - side_widget->setLayout(side_layout); + side_outer_layout->addLayout(side_layout); + + save_button = new QPushButton("Save"); + save_button->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + save_button->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom + save_button->setToolTip("Save editor changes including the graph"); + save_button->setIcon(QIcon(":/actions/accept.png")); + side_outer_layout->addWidget(save_button); + + side_widget->setLayout(side_outer_layout); // Create the scene layer. scene_layer = new QWidget(); diff --git a/Editors/VisualShaderEditor.h b/Editors/VisualShaderEditor.h index a572ba4b7..01dc63489 100644 --- a/Editors/VisualShaderEditor.h +++ b/Editors/VisualShaderEditor.h @@ -122,8 +122,10 @@ class VisualShaderEditor : public BaseEditor { QHBoxLayout* layout; QWidget* side_widget; + QVBoxLayout* side_outer_layout; QVBoxLayout* side_layout; QLineEdit* name_edit; + QPushButton* save_button; QHBoxLayout* scene_layer_layout; QWidget* scene_layer; // Layer having the scene. From 0a80517a19584a3f94c1c16b2017b083ef280bc7 Mon Sep 17 00:00:00 2001 From: Saif Kandil <74428638+k0T0z@users.noreply.github.com> Date: Fri, 27 Sep 2024 09:24:09 +0300 Subject: [PATCH 34/56] Fixed some delete nodes bugs and improved tests --- Editors/VisualShaderEditor.cpp | 181 +++++++--------------- Editors/VisualShaderEditor.h | 22 +++ Tests/Editors/VisualShaderEditorTests.cpp | 126 ++++++++++++++- Tests/Editors/VisualShaderEditorTests.h | 2 +- 4 files changed, 202 insertions(+), 129 deletions(-) diff --git a/Editors/VisualShaderEditor.cpp b/Editors/VisualShaderEditor.cpp index c765d6ce6..2298412a4 100644 --- a/Editors/VisualShaderEditor.cpp +++ b/Editors/VisualShaderEditor.cpp @@ -118,7 +118,7 @@ void VisualShaderEditor::init() { layout = new QHBoxLayout(this); layout->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom layout->setSizeConstraint(QLayout::SetNoConstraint); - layout->setSpacing(0); + layout->setSpacing(5); layout->setAlignment(Qt::AlignVCenter | Qt::AlignHCenter); //////////////// End of Header //////////////// @@ -783,22 +783,12 @@ bool VisualShaderGraphicsScene::add_node(const int& n_id, const std::shared_ptr< } bool VisualShaderGraphicsScene::delete_node(const int& n_id) { - if (node_graphics_objects.find(n_id) == node_graphics_objects.end()) { - return false; - } - const std::shared_ptr n{vs->get_node(n_id)}; if (!n) { return false; } - bool result{vs->remove_node(n_id)}; - - if (!result) { - return false; - } - VisualShaderNodeGraphicsObject* n_o{this->get_node_graphics_object(n_id)}; if (!n_o) { @@ -820,26 +810,7 @@ bool VisualShaderGraphicsScene::delete_node(const int& n_id) { continue; } - VisualShaderNodeGraphicsObject* from_n_o{this->get_node_graphics_object(c_o->get_from_node_id())}; - - if (!from_n_o) { - continue; - } - - VisualShaderOutputPortGraphicsObject* o_port{from_n_o->get_output_port_graphics_object(c_o->get_from_port_index())}; - - if (!o_port) { - continue; - } - - bool result{vs->disconnect_nodes(c_o->get_from_node_id(), c_o->get_from_port_index(), n_id, i)}; - - if (!result) { - std::cout << "Failed to disconnect nodes" << std::endl; - continue; - } - - result = this->delete_connection(c_o->get_from_node_id(), c_o->get_from_port_index(), n_id, i); + bool result{this->delete_connection(c_o->get_from_node_id(), c_o->get_from_port_index(), n_id, i)}; if (!result) { std::cout << "Failed to delete connection" << std::endl; @@ -861,26 +832,7 @@ bool VisualShaderGraphicsScene::delete_node(const int& n_id) { continue; } - VisualShaderNodeGraphicsObject* to_n_o{this->get_node_graphics_object(c_o->get_to_node_id())}; - - if (!to_n_o) { - continue; - } - - VisualShaderInputPortGraphicsObject* i_port{to_n_o->get_input_port_graphics_object(c_o->get_to_port_index())}; - - if (!i_port) { - continue; - } - - bool result{vs->disconnect_nodes(n_id, i, c_o->get_to_node_id(), c_o->get_to_port_index())}; - - if (!result) { - std::cout << "Failed to disconnect nodes" << std::endl; - continue; - } - - result = this->delete_connection(c_o->get_from_node_id(), c_o->get_from_port_index(), n_id, i); + bool result{this->delete_connection(n_id, i, c_o->get_to_node_id(), c_o->get_to_port_index())}; if (!result) { std::cout << "Failed to delete connection" << std::endl; @@ -888,6 +840,11 @@ bool VisualShaderGraphicsScene::delete_node(const int& n_id) { } } + bool result{vs->remove_node(n_id)}; + + if (!result) { + return false; + } // Remove the node from the scene removeItem(n_o); @@ -926,10 +883,12 @@ bool VisualShaderGraphicsScene::add_connection(const int& from_node_id, const in std::cout << "Start of connection is out of view bounds" << std::endl; } - this->temporary_connection_graphics_object = - new VisualShaderConnectionGraphicsObject(from_node_id, from_port_index, from_o_port->get_global_coordinate()); - from_o_port->connect(this->temporary_connection_graphics_object); - addItem(this->temporary_connection_graphics_object); + if (!this->temporary_connection_graphics_object) { + this->temporary_connection_graphics_object = + new VisualShaderConnectionGraphicsObject(from_node_id, from_port_index, from_o_port->get_global_coordinate()); + from_o_port->connect(this->temporary_connection_graphics_object); + addItem(this->temporary_connection_graphics_object); + } if (to_node_id != (int)VisualShader::NODE_ID_INVALID && to_port_index != (int)VisualShader::PORT_INDEX_INVALID) { // Set the end of the connection @@ -950,12 +909,6 @@ bool VisualShaderGraphicsScene::add_connection(const int& from_node_id, const in std::cout << "End of connection is out of view bounds" << std::endl; } - this->temporary_connection_graphics_object->set_end_coordinate(to_i_port->get_global_coordinate()); - to_i_port->connect(this->temporary_connection_graphics_object); - this->temporary_connection_graphics_object->set_to_node_id(to_node_id); - this->temporary_connection_graphics_object->set_to_port_index(to_port_index); - this->temporary_connection_graphics_object = nullptr; // Make sure to reset the temporary connection object - // Connect the nodes in the VisualShader bool result{vs->can_connect_nodes(from_node_id, from_port_index, to_node_id, to_port_index)}; if (!result) { @@ -968,6 +921,12 @@ bool VisualShaderGraphicsScene::add_connection(const int& from_node_id, const in std::cout << "Failed to connect nodes" << std::endl; return false; } + + this->temporary_connection_graphics_object->set_end_coordinate(to_i_port->get_global_coordinate()); + to_i_port->connect(this->temporary_connection_graphics_object); + this->temporary_connection_graphics_object->set_to_node_id(to_node_id); + this->temporary_connection_graphics_object->set_to_port_index(to_port_index); + this->temporary_connection_graphics_object = nullptr; // Make sure to reset the temporary connection object } return true; @@ -986,14 +945,15 @@ bool VisualShaderGraphicsScene::delete_connection(const int& from_node_id, const if (!from_o_port) { return false; } + + // If we have a complete connection, then we can disconnect the nodes + if (to_node_id != (int)VisualShader::NODE_ID_INVALID && to_port_index != (int)VisualShader::PORT_INDEX_INVALID && !this->temporary_connection_graphics_object) { + VisualShaderConnectionGraphicsObject* c_o{from_o_port->get_connection_graphics_object()}; - VisualShaderConnectionGraphicsObject* c_o{from_o_port->get_connection_graphics_object()}; - - if (!c_o) { - return false; - } - - if (to_node_id != (int)VisualShader::NODE_ID_INVALID && to_port_index != (int)VisualShader::PORT_INDEX_INVALID) { + if (!c_o) { + return false; + } + VisualShaderNodeGraphicsObject* to_n_o{this->get_node_graphics_object(to_node_id)}; if (!to_n_o) { @@ -1013,13 +973,19 @@ bool VisualShaderGraphicsScene::delete_connection(const int& from_node_id, const } to_i_port->detach_connection(); + from_o_port->detach_connection(); + removeItem(c_o); + delete c_o; + return true; + } else if (to_node_id != (int)VisualShader::NODE_ID_INVALID && to_port_index != (int)VisualShader::PORT_INDEX_INVALID) { + from_o_port->detach_connection(); + removeItem(this->temporary_connection_graphics_object); + delete this->temporary_connection_graphics_object; + this->temporary_connection_graphics_object = nullptr; + return true; } - from_o_port->detach_connection(); - removeItem(c_o); - delete c_o; - - return true; + return false; } VisualShaderNodeGraphicsObject* VisualShaderGraphicsScene::get_node_graphics_object(const int& n_id) const { @@ -1161,13 +1127,6 @@ void VisualShaderGraphicsScene::on_port_dragged(QGraphicsObject* port, const QPo } void VisualShaderGraphicsScene::on_port_dropped(QGraphicsObject* port, const QPointF& coordinate) { - VisualShaderConnectionGraphicsObject* c_o{temporary_connection_graphics_object}; - temporary_connection_graphics_object = nullptr; // Reset the temporary connection object - - if (!c_o) { - return; - } - // Find all items under the coordinate QList items_at_coordinate{this->items(coordinate)}; @@ -1192,20 +1151,8 @@ void VisualShaderGraphicsScene::on_port_dropped(QGraphicsObject* port, const QPo } if (!in_p_o) { - bool result{this->delete_connection(c_o->get_from_node_id(), c_o->get_from_port_index())}; - - if (!result) { - std::cout << "Failed to delete connection" << std::endl; - } - - return; // Return because we dragging an input port and dropped on nothing - } - - bool result{vs->can_connect_nodes(c_o->get_from_node_id(), c_o->get_from_port_index(), in_p_o->get_node_id(), - in_p_o->get_port_index())}; - - if (!result) { - bool result{this->delete_connection(c_o->get_from_node_id(), c_o->get_from_port_index())}; + bool result{this->delete_connection(temporary_connection_graphics_object->get_from_node_id(), + temporary_connection_graphics_object->get_from_port_index())}; if (!result) { std::cout << "Failed to delete connection" << std::endl; @@ -1214,12 +1161,14 @@ void VisualShaderGraphicsScene::on_port_dropped(QGraphicsObject* port, const QPo return; // Return because we dragging an input port and dropped on nothing } - // Connect the nodes - result = vs->connect_nodes(c_o->get_from_node_id(), c_o->get_from_port_index(), in_p_o->get_node_id(), - in_p_o->get_port_index()); + bool result {add_connection(temporary_connection_graphics_object->get_from_node_id(), + temporary_connection_graphics_object->get_from_port_index(), + in_p_o->get_node_id(), + in_p_o->get_port_index())}; if (!result) { - bool result{this->delete_connection(c_o->get_from_node_id(), c_o->get_from_port_index())}; + bool result{this->delete_connection(temporary_connection_graphics_object->get_from_node_id(), + temporary_connection_graphics_object->get_from_port_index())}; if (!result) { std::cout << "Failed to delete connection" << std::endl; @@ -1228,15 +1177,12 @@ void VisualShaderGraphicsScene::on_port_dropped(QGraphicsObject* port, const QPo return; // Return because we dragging an input port and dropped on nothing } - c_o->set_to_node_id(in_p_o->get_node_id()); - c_o->set_to_port_index(in_p_o->get_port_index()); - c_o->set_end_coordinate(in_p_o->get_global_coordinate()); - in_p_o->connect(c_o); return; } if (!in_p_o) { - bool result{this->delete_connection(c_o->get_from_node_id(), c_o->get_from_port_index())}; + bool result{this->delete_connection(temporary_connection_graphics_object->get_from_node_id(), + temporary_connection_graphics_object->get_from_port_index())}; if (!result) { std::cout << "Failed to delete connection" << std::endl; @@ -1244,26 +1190,12 @@ void VisualShaderGraphicsScene::on_port_dropped(QGraphicsObject* port, const QPo return; // Return because we dragging an output port and dropped on nothing } - - bool result{vs->can_connect_nodes(c_o->get_from_node_id(), c_o->get_from_port_index(), in_p_o->get_node_id(), - in_p_o->get_port_index())}; - - if (!result) { - bool result{this->delete_connection(c_o->get_from_node_id(), c_o->get_from_port_index())}; - - if (!result) { - std::cout << "Failed to delete connection" << std::endl; - } - - return; - } - - // Connect the nodes - result = vs->connect_nodes(c_o->get_from_node_id(), c_o->get_from_port_index(), in_p_o->get_node_id(), - in_p_o->get_port_index()); + + bool result{add_connection(o_port->get_node_id(), o_port->get_port_index(), in_p_o->get_node_id(), in_p_o->get_port_index())}; if (!result) { - bool result{this->delete_connection(c_o->get_from_node_id(), c_o->get_from_port_index())}; + bool result{this->delete_connection(temporary_connection_graphics_object->get_from_node_id(), + temporary_connection_graphics_object->get_from_port_index())}; if (!result) { std::cout << "Failed to delete connection" << std::endl; @@ -1271,11 +1203,6 @@ void VisualShaderGraphicsScene::on_port_dropped(QGraphicsObject* port, const QPo return; } - - c_o->set_to_node_id(in_p_o->get_node_id()); - c_o->set_to_port_index(in_p_o->get_port_index()); - c_o->set_end_coordinate(in_p_o->get_global_coordinate()); - in_p_o->connect(c_o); } /**********************************************************************/ @@ -1436,7 +1363,7 @@ void VisualShaderGraphicsView::drawBackground(QPainter* painter, const QRectF& r void VisualShaderGraphicsView::contextMenuEvent(QContextMenuEvent* event) { QGraphicsView::contextMenuEvent(event); - // اتاكد انها مش node + // TODO: Make sure to not show the context menu if an item is under the mouse if (itemAt(event->pos())) { return; } diff --git a/Editors/VisualShaderEditor.h b/Editors/VisualShaderEditor.h index 01dc63489..428c2c5d0 100644 --- a/Editors/VisualShaderEditor.h +++ b/Editors/VisualShaderEditor.h @@ -84,6 +84,9 @@ class VisualShaderEditor : public BaseEditor { VisualShaderEditor(MessageModel* model, QWidget* parent = nullptr); ~VisualShaderEditor() override; + VisualShaderGraphicsScene* get_scene() const { return scene; } + VisualShaderGraphicsView* get_view() const { return view; } + Q_SIGNALS: /** * @brief Request the dialog that has all kinds of nodes we can @@ -465,6 +468,7 @@ class VisualShaderNodeGraphicsObject : public QGraphicsObject { VisualShaderInputPortGraphicsObject* get_input_port_graphics_object(const int& p_index) const; VisualShaderOutputPortGraphicsObject* get_output_port_graphics_object(const int& p_index) const; + QWidget* get_embed_widget() const { return embed_widget; } void set_embed_widget(QWidget* embed_widget) { this->embed_widget = embed_widget; } Q_SIGNALS: @@ -748,6 +752,8 @@ class VisualShaderNodeInputEmbedWidget : public QComboBox { VisualShaderNodeInputEmbedWidget(const std::shared_ptr& node); ~VisualShaderNodeInputEmbedWidget(); + void set_current_index(const int& index) { this->setCurrentIndex(index); } + private Q_SLOTS: void on_current_index_changed(const int& index); @@ -766,6 +772,8 @@ class VisualShaderNodeFloatOpEmbedWidget : public QComboBox { VisualShaderNodeFloatOpEmbedWidget(const std::shared_ptr& node); ~VisualShaderNodeFloatOpEmbedWidget(); + void set_current_index(const int& index) { this->setCurrentIndex(index); } + private Q_SLOTS: void on_current_index_changed(const int& index); @@ -784,6 +792,8 @@ class VisualShaderNodeIntOpEmbedWidget : public QComboBox { VisualShaderNodeIntOpEmbedWidget(const std::shared_ptr& node); ~VisualShaderNodeIntOpEmbedWidget(); + void set_current_index(const int& index) { this->setCurrentIndex(index); } + private Q_SLOTS: void on_current_index_changed(const int& index); @@ -802,6 +812,8 @@ class VisualShaderNodeUIntOpEmbedWidget : public QComboBox { VisualShaderNodeUIntOpEmbedWidget(const std::shared_ptr& node); ~VisualShaderNodeUIntOpEmbedWidget(); + void set_current_index(const int& index) { this->setCurrentIndex(index); } + private Q_SLOTS: void on_current_index_changed(const int& index); @@ -820,6 +832,8 @@ class VisualShaderNodeVectorOpEmbedWidget : public QComboBox { VisualShaderNodeVectorOpEmbedWidget(const std::shared_ptr& node); ~VisualShaderNodeVectorOpEmbedWidget(); + void set_current_index(const int& index) { this->setCurrentIndex(index); } + private Q_SLOTS: void on_current_index_changed(const int& index); @@ -838,6 +852,8 @@ class VisualShaderNodeFloatFuncEmbedWidget : public QComboBox { VisualShaderNodeFloatFuncEmbedWidget(const std::shared_ptr& node); ~VisualShaderNodeFloatFuncEmbedWidget(); + void set_current_index(const int& index) { this->setCurrentIndex(index); } + private Q_SLOTS: void on_current_index_changed(const int& index); @@ -856,6 +872,8 @@ class VisualShaderNodeIntFuncEmbedWidget : public QComboBox { VisualShaderNodeIntFuncEmbedWidget(const std::shared_ptr& node); ~VisualShaderNodeIntFuncEmbedWidget(); + void set_current_index(const int& index) { this->setCurrentIndex(index); } + private Q_SLOTS: void on_current_index_changed(const int& index); @@ -874,6 +892,8 @@ class VisualShaderNodeUIntFuncEmbedWidget : public QComboBox { VisualShaderNodeUIntFuncEmbedWidget(const std::shared_ptr& node); ~VisualShaderNodeUIntFuncEmbedWidget(); + void set_current_index(const int& index) { this->setCurrentIndex(index); } + private Q_SLOTS: void on_current_index_changed(const int& index); @@ -892,6 +912,8 @@ class VisualShaderNodeVectorFuncEmbedWidget : public QComboBox { VisualShaderNodeVectorFuncEmbedWidget(const std::shared_ptr& node); ~VisualShaderNodeVectorFuncEmbedWidget(); + void set_current_index(const int& index) { this->setCurrentIndex(index); } + private Q_SLOTS: void on_current_index_changed(const int& index); diff --git a/Tests/Editors/VisualShaderEditorTests.cpp b/Tests/Editors/VisualShaderEditorTests.cpp index 46ebba05a..ef6283b2b 100644 --- a/Tests/Editors/VisualShaderEditorTests.cpp +++ b/Tests/Editors/VisualShaderEditorTests.cpp @@ -32,15 +32,139 @@ void TestVisualShaderEditor::initTestCase() { editor = new VisualShaderEditor(); + + // Get the scene + VisualShaderGraphicsScene* scene {editor->get_scene()}; + QVERIFY(scene != nullptr); + + { + // Add an input UV node + bool result {scene->add_node("VisualShaderNodeInput", {-200, 0})}; + QVERIFY(result); + } + + { + // Add an input TIME node + bool result {scene->add_node("VisualShaderNodeInput", {-200, 350})}; + QVERIFY(result); + } + + { + // Add a value noise node + bool result {scene->add_node("VisualShaderNodeValueNoise", {-50, 0})}; + QVERIFY(result); + } + + { + // Add a float function node: sin + bool result {scene->add_node("VisualShaderNodeFloatFunc", {-50, 350})}; + QVERIFY(result); + } + + { + // Add a divide operator node + bool result {scene->add_node("VisualShaderNodeFloatOp", {150, 150})}; + QVERIFY(result); + } } void TestVisualShaderEditor::init() { } void TestVisualShaderEditor::cleanupTestCase() { delete editor; } void TestVisualShaderEditor::cleanup() { } -void TestVisualShaderEditor::testCreateFullGraph() { +void TestVisualShaderEditor::test_create_full_graph() { editor->show(); // Wait for the editor to be shown QVERIFY(QTest::qWaitForWindowExposed(editor)); + + // Get the scene + VisualShaderGraphicsScene* scene {editor->get_scene()}; + QVERIFY(scene != nullptr); + VisualShaderGraphicsView* view {editor->get_view()}; + QVERIFY(view != nullptr); + + VisualShaderOutputPortGraphicsObject* o_p_uv {nullptr}; + + { + VisualShaderNodeGraphicsObject* uv_node {scene->get_node_graphics_object(1)}; // 1 is the node id + QVERIFY(uv_node != nullptr); + o_p_uv = uv_node->get_output_port_graphics_object(0); // The only output port + QVERIFY(o_p_uv != nullptr); + QWidget* embed_widget {uv_node->get_embed_widget()}; + QVERIFY(embed_widget != nullptr); + VisualShaderNodeInputEmbedWidget* uv_embed_widget {dynamic_cast(embed_widget)}; + QVERIFY(uv_embed_widget != nullptr); + uv_embed_widget->set_current_index(1); // 1 is UV + } + + VisualShaderOutputPortGraphicsObject* o_p_time {nullptr}; + + { + VisualShaderNodeGraphicsObject* uv_node {scene->get_node_graphics_object(2)}; // 2 is the node id + QVERIFY(uv_node != nullptr); + o_p_time = uv_node->get_output_port_graphics_object(0); // The only output port + QVERIFY(o_p_time != nullptr); + QWidget* embed_widget {uv_node->get_embed_widget()}; + QVERIFY(embed_widget != nullptr); + VisualShaderNodeInputEmbedWidget* uv_embed_widget {dynamic_cast(embed_widget)}; + QVERIFY(uv_embed_widget != nullptr); + uv_embed_widget->set_current_index(2); // 2 is TIME + } + + VisualShaderInputPortGraphicsObject* i_p_noise {nullptr}; + VisualShaderOutputPortGraphicsObject* o_p_noise {nullptr}; + + { + VisualShaderNodeGraphicsObject* noise_node {scene->get_node_graphics_object(3)}; // 3 is the node id + QVERIFY(noise_node != nullptr); + i_p_noise = noise_node->get_input_port_graphics_object(0); // The only input port + QVERIFY(i_p_noise != nullptr); + o_p_noise = noise_node->get_output_port_graphics_object(0); // The only output port + QVERIFY(o_p_noise != nullptr); + } + + VisualShaderInputPortGraphicsObject* i_p_sin {nullptr}; + VisualShaderOutputPortGraphicsObject* o_p_sin {nullptr}; + + { + VisualShaderNodeGraphicsObject* sin_node {scene->get_node_graphics_object(4)}; // 4 is the node id + QVERIFY(sin_node != nullptr); + i_p_sin = sin_node->get_input_port_graphics_object(0); // The only input port + QVERIFY(i_p_sin != nullptr); + o_p_sin = sin_node->get_output_port_graphics_object(0); // The only output port + QVERIFY(o_p_sin != nullptr); + QWidget* embed_widget {sin_node->get_embed_widget()}; + QVERIFY(embed_widget != nullptr); + VisualShaderNodeFloatFuncEmbedWidget* sin_embed_widget {dynamic_cast(embed_widget)}; + QVERIFY(sin_embed_widget != nullptr); + sin_embed_widget->set_current_index(0); // 0 is sin + } + + VisualShaderInputPortGraphicsObject* i1_p_divide {nullptr}; + VisualShaderInputPortGraphicsObject* i2_p_divide {nullptr}; + VisualShaderOutputPortGraphicsObject* o_p_divide {nullptr}; + + { + VisualShaderNodeGraphicsObject* divide_node {scene->get_node_graphics_object(5)}; // 5 is the node id + QVERIFY(divide_node != nullptr); + i1_p_divide = divide_node->get_input_port_graphics_object(0); // The first input port + QVERIFY(i1_p_divide != nullptr); + i2_p_divide = divide_node->get_input_port_graphics_object(1); // The second input port + QVERIFY(i2_p_divide != nullptr); + o_p_divide = divide_node->get_output_port_graphics_object(0); // The only output port + QVERIFY(o_p_divide != nullptr); + QWidget* embed_widget {divide_node->get_embed_widget()}; + QVERIFY(embed_widget != nullptr); + VisualShaderNodeFloatOpEmbedWidget* divide_embed_widget {dynamic_cast(embed_widget)}; + QVERIFY(divide_embed_widget != nullptr); + divide_embed_widget->set_current_index(3); // 3 is divide + } + + { + // Connect UV to noise + } + + // Wait + QTest::qWait(10000); } diff --git a/Tests/Editors/VisualShaderEditorTests.h b/Tests/Editors/VisualShaderEditorTests.h index e127d3572..e26296cbc 100644 --- a/Tests/Editors/VisualShaderEditorTests.h +++ b/Tests/Editors/VisualShaderEditorTests.h @@ -40,7 +40,7 @@ class TestVisualShaderEditor : public QObject { void cleanupTestCase(); // Will be called after the last test function was executed. void cleanup(); // Will be called after every test function. - void testCreateFullGraph(); + void test_create_full_graph(); private: VisualShaderEditor* editor = nullptr; From 1f05b4b324636251a78e4e6bc963b35a8dee6f73 Mon Sep 17 00:00:00 2001 From: Saif Kandil <74428638+k0T0z@users.noreply.github.com> Date: Fri, 27 Sep 2024 15:06:28 +0300 Subject: [PATCH 35/56] Added the UI for both the matching image and the rendering shader for preview --- Editors/VisualShaderEditor.cpp | 133 ++++++++++++++++++++++++++++----- Editors/VisualShaderEditor.h | 107 +++++++++++++++++++++++++- 2 files changed, 221 insertions(+), 19 deletions(-) diff --git a/Editors/VisualShaderEditor.cpp b/Editors/VisualShaderEditor.cpp index 2298412a4..a6113d5b8 100644 --- a/Editors/VisualShaderEditor.cpp +++ b/Editors/VisualShaderEditor.cpp @@ -260,6 +260,7 @@ void VisualShaderEditor::init() { load_image_button->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom load_image_button->setToolTip("Load an image to match"); menu_bar->addWidget(load_image_button); + QObject::connect(load_image_button, &QPushButton::pressed, this, &VisualShaderEditor::on_load_image_button_pressed); match_image_button = new QPushButton("Match Image", scene_layer); match_image_button->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); @@ -493,6 +494,13 @@ void VisualShaderEditor::on_menu_button_pressed() { menu_button->setText(!is_visible ? "Hide Menu" : "Show Menu"); } +void VisualShaderEditor::on_load_image_button_pressed() { + // TODO: Decide on how to load an image + // For example, use QFileDialog to open an image file or + // load an existing sprite or background from the project. + // Then, send the image to OriginalMatchingImageWidget widget to display it. +} + std::vector VisualShaderEditor::parse_node_category_path(const std::string& node_category_path) { std::vector tokens; std::stringstream ss(node_category_path); @@ -666,11 +674,9 @@ VisualShaderGraphicsScene::~VisualShaderGraphicsScene() {} bool VisualShaderGraphicsScene::add_node(const std::string& type, const QPointF& coordinate) { // Instantiate the node based on the type std::shared_ptr n; - QWidget* embed_widget {nullptr}; if (type == "VisualShaderNodeInput") { n = std::make_shared(); - embed_widget = new VisualShaderNodeInputEmbedWidget(std::dynamic_pointer_cast(n)); } else if (type == "VisualShaderNodeColorConstant") { n = std::make_shared(); } else if (type == "VisualShaderNodeBooleanConstant") { @@ -689,24 +695,18 @@ bool VisualShaderGraphicsScene::add_node(const std::string& type, const QPointF& n = std::make_shared(); } else if (type == "VisualShaderNodeFloatFunc") { n = std::make_shared(); - embed_widget = new VisualShaderNodeFloatFuncEmbedWidget(std::dynamic_pointer_cast(n)); } else if (type == "VisualShaderNodeIntFunc") { n = std::make_shared(); - embed_widget = new VisualShaderNodeIntFuncEmbedWidget(std::dynamic_pointer_cast(n)); } else if (type == "VisualShaderNodeUIntFunc") { n = std::make_shared(); - embed_widget = new VisualShaderNodeUIntFuncEmbedWidget(std::dynamic_pointer_cast(n)); } else if (type == "VisualShaderNodeDerivativeFunc") { n = std::make_shared(); } else if (type == "VisualShaderNodeFloatOp") { n = std::make_shared(); - embed_widget = new VisualShaderNodeFloatOpEmbedWidget(std::dynamic_pointer_cast(n)); } else if (type == "VisualShaderNodeIntOp") { n = std::make_shared(); - embed_widget = new VisualShaderNodeIntOpEmbedWidget(std::dynamic_pointer_cast(n)); } else if (type == "VisualShaderNodeUIntOp") { n = std::make_shared(); - embed_widget = new VisualShaderNodeUIntOpEmbedWidget(std::dynamic_pointer_cast(n)); } else if (type == "VisualShaderNodeValueNoise") { n = std::make_shared(); } else if (type == "VisualShaderNodeCompare") { @@ -732,10 +732,10 @@ bool VisualShaderGraphicsScene::add_node(const std::string& type, const QPointF& return false; } - return VisualShaderGraphicsScene::add_node(n_id, n, coordinate, embed_widget); + return VisualShaderGraphicsScene::add_node(n_id, n, coordinate); } -bool VisualShaderGraphicsScene::add_node(const int& n_id, const std::shared_ptr& n, const QPointF& coordinate, QWidget* embed_widget) { +bool VisualShaderGraphicsScene::add_node(const int& n_id, const std::shared_ptr& n, const QPointF& coordinate) { // Make sure the node doesn't already exist, we don't want to overwrite a node. if (node_graphics_objects.find(n_id) != node_graphics_objects.end()) { return false; @@ -767,10 +767,14 @@ bool VisualShaderGraphicsScene::add_node(const int& n_id, const std::shared_ptr< VisualShaderNodeGraphicsObject* n_o{new VisualShaderNodeGraphicsObject(n_id, coordinate, n)}; - if (embed_widget) { + if (n_id != (int)VisualShader::NODE_ID_OUTPUT) { + VisualShaderNodeEmbedWidget* embed_widget {new VisualShaderNodeEmbedWidget(n)}; QGraphicsProxyWidget* embed_widget_proxy{new QGraphicsProxyWidget(n_o)}; embed_widget_proxy->setWidget(embed_widget); n_o->set_embed_widget(embed_widget); + + // Send the shader previewer widget + embed_widget->set_shader_previewer_widget(n_o->get_shader_previewer_widget()); } QObject::connect(n_o, &VisualShaderNodeGraphicsObject::node_deleted, this, &VisualShaderGraphicsScene::on_node_deleted); @@ -1502,7 +1506,9 @@ VisualShaderNodeGraphicsObject::VisualShaderNodeGraphicsObject(const int& n_id, rect_height(0.0f), rect_margin(0.0f), rect_padding(0.0f), - embed_widget(nullptr) { + embed_widget(nullptr), + matching_image_widget(nullptr), + shader_previewer_widget(nullptr) { setFlag(QGraphicsItem::ItemDoesntPropagateOpacityToChildren, true); setFlag(QGraphicsItem::ItemIsFocusable, true); setFlag(QGraphicsItem::ItemIsMovable, true); @@ -1518,6 +1524,19 @@ VisualShaderNodeGraphicsObject::VisualShaderNodeGraphicsObject(const int& n_id, setPos(coordinate.x(), coordinate.y()); + // Output node should have a matching image widget + if (n_id == (int)VisualShader::NODE_ID_OUTPUT) { + QGraphicsProxyWidget* matching_image_widget_proxy{new QGraphicsProxyWidget(this)}; + matching_image_widget = new OriginalMatchingImageWidget(); + matching_image_widget_proxy->setWidget(matching_image_widget); + } else { + // Create the shader previewer widget + QGraphicsProxyWidget* shader_previewer_widget_proxy{new QGraphicsProxyWidget(this)}; + shader_previewer_widget = new ShaderPreviewerWidget(); + shader_previewer_widget->setVisible(false); + shader_previewer_widget_proxy->setWidget(shader_previewer_widget); + } + // Set the context menu context_menu = new QMenu(); delete_node_action = new QAction(QStringLiteral("Delete Node"), context_menu); @@ -1639,7 +1658,7 @@ QRectF VisualShaderNodeGraphicsObject::boundingRect() const { QRectF r({0.0f, 0.0f}, QSizeF(rect_width, rect_height)); // Calculate the margin - this->rect_margin = port_diameter * 0.5f + 5.0f; // 5.0f is the margin between the ports and the port names + this->rect_margin = port_diameter * 0.5f; // Calculate the rect padding // We add a safe area around the rect to prevent the ports from being cut off @@ -1656,6 +1675,15 @@ void VisualShaderNodeGraphicsObject::paint(QPainter* painter, const QStyleOption // Get the rect without the padding QRectF r{this->boundingRect()}; + + // { + // // Draw Node Rect + // painter->setPen(Qt::red); + // painter->setBrush(Qt::NoBrush); + // painter->drawRect(r); + // } + + // Add the padding to the rect r.adjust(rect_padding, rect_padding, -rect_padding, -rect_padding); { @@ -1675,17 +1703,35 @@ void VisualShaderNodeGraphicsObject::paint(QPainter* painter, const QStyleOption painter->drawRoundedRect(r, this->corner_radius, this->corner_radius); } + // Draw Matching Image Widget + if (n_id == (int)VisualShader::NODE_ID_OUTPUT) { + float matching_image_widget_x{(float)r.x() + (float)r.width() + spacing_between_output_node_and_matching_image}; + float matching_image_widget_y{(float)r.y()}; + + matching_image_widget->setGeometry(matching_image_widget_x, matching_image_widget_y, matching_image_widget->width(), matching_image_widget->height()); + } else { + // Draw Shader Previewer Widget + float shader_previewer_widget_x{(float)r.x()}; + float shader_previewer_widget_y{(float)r.y() + (float)r.height() + spacing_between_current_node_and_shader_previewer}; + shader_previewer_widget->setGeometry(shader_previewer_widget_x, shader_previewer_widget_y, shader_previewer_widget->width(), shader_previewer_widget->height()); + } + // Add the margin to the rect r.adjust(rect_margin, rect_margin, -rect_margin, -rect_margin); + // { + // // Draw Node Rect + // painter->setPen(Qt::red); + // painter->setBrush(Qt::NoBrush); + // painter->drawRect(r); + // } + float rect_x{(float)r.topLeft().x()}; float rect_y{(float)r.topLeft().y()}; float rect_w{(float)r.width()}; float rect_h{(float)r.height()}; - float min_side{qMin(rect_w, rect_h)}; - QRectF caption_rect(rect_x, rect_y, rect_w, caption_rect_height); { @@ -1742,7 +1788,7 @@ void VisualShaderNodeGraphicsObject::paint(QPainter* painter, const QStyleOption QFontMetrics fm(f); painter->setFont(f); - float x{rect_x}; + float x{rect_x + port_caption_spacing}; float y{(float)(port_rect.center().y()) + (float)((fm.ascent() + fm.descent()) * 0.5f - fm.descent())}; @@ -1798,7 +1844,7 @@ void VisualShaderNodeGraphicsObject::paint(QPainter* painter, const QStyleOption QFontMetrics fm(f); painter->setFont(f); - float x{rect_x + rect_w - (float)fm.horizontalAdvance(p_n)}; + float x{rect_x + rect_w - (float)fm.horizontalAdvance(p_n) - port_caption_spacing}; float y{(float)(port_rect.center().y()) + (float)((fm.ascent() + fm.descent()) * 0.5f - fm.descent())}; @@ -2234,6 +2280,59 @@ std::pair VisualShaderConnectionGraphicsObject::calculate_cont /**********************************************************************/ /**********************************************************************/ +VisualShaderNodeEmbedWidget::VisualShaderNodeEmbedWidget(const std::shared_ptr& node, + QWidget* parent) : QWidget(parent), + layout(nullptr), + preview_shader_button(nullptr), + shader_previewer_widget(nullptr) { + layout = new QVBoxLayout(this); + layout->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom + layout->setSizeConstraint(QLayout::SetNoConstraint); + layout->setSpacing(2); + layout->setAlignment(Qt::AlignVCenter | Qt::AlignHCenter); + + if (auto p {std::dynamic_pointer_cast(node)}) { + VisualShaderNodeInputEmbedWidget* embed_widget = new VisualShaderNodeInputEmbedWidget(p); + layout->addWidget(embed_widget); + } else if (auto p {std::dynamic_pointer_cast(node)}) { + VisualShaderNodeFloatFuncEmbedWidget* embed_widget = new VisualShaderNodeFloatFuncEmbedWidget(p); + layout->addWidget(embed_widget); + } else if (auto p {std::dynamic_pointer_cast(node)}) { + VisualShaderNodeIntFuncEmbedWidget* embed_widget = new VisualShaderNodeIntFuncEmbedWidget(p); + layout->addWidget(embed_widget); + } else if (auto p {std::dynamic_pointer_cast(node)}) { + VisualShaderNodeUIntFuncEmbedWidget* embed_widget = new VisualShaderNodeUIntFuncEmbedWidget(p); + layout->addWidget(embed_widget); + } else if (auto p {std::dynamic_pointer_cast(node)}) { + VisualShaderNodeFloatOpEmbedWidget* embed_widget = new VisualShaderNodeFloatOpEmbedWidget(p); + layout->addWidget(embed_widget); + } else if (auto p {std::dynamic_pointer_cast(node)}) { + VisualShaderNodeIntOpEmbedWidget* embed_widget = new VisualShaderNodeIntOpEmbedWidget(p); + layout->addWidget(embed_widget); + } else if (auto p {std::dynamic_pointer_cast(node)}) { + VisualShaderNodeUIntOpEmbedWidget* embed_widget = new VisualShaderNodeUIntOpEmbedWidget(p); + layout->addWidget(embed_widget); + } + + // Create the button that will show/hide the shader previewer + preview_shader_button = new QPushButton("Show Preview", this); + preview_shader_button->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); + preview_shader_button->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + preview_shader_button->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom + preview_shader_button->setToolTip("Create a new node"); + layout->addWidget(preview_shader_button); + QObject::connect(preview_shader_button, &QPushButton::pressed, this, &VisualShaderNodeEmbedWidget::on_preview_shader_button_pressed); + + this->setContentsMargins(10, 10, 10, 10); // Left, top, right, bottom + setLayout(layout); +} + +VisualShaderNodeEmbedWidget::~VisualShaderNodeEmbedWidget() {} + +/*************************************/ +/* Input Node */ +/*************************************/ + VisualShaderNodeInputEmbedWidget::VisualShaderNodeInputEmbedWidget(const std::shared_ptr& node) : QComboBox(), node(node) { setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); diff --git a/Editors/VisualShaderEditor.h b/Editors/VisualShaderEditor.h index 428c2c5d0..50e000661 100644 --- a/Editors/VisualShaderEditor.h +++ b/Editors/VisualShaderEditor.h @@ -117,6 +117,7 @@ class VisualShaderEditor : public BaseEditor { void on_preview_shader_button_pressed(); void on_menu_button_pressed(); + void on_load_image_button_pressed(); private: VisualShader* visual_shader; @@ -245,6 +246,70 @@ class CreateNodeDialog : public QDialog { QTreeWidgetItem* selected_item; }; +/**********************************************************************/ +/**********************************************************************/ +/**********************************************************************/ +/***** *****/ +/***** OriginalMatchingImageWidget *****/ +/***** *****/ +/**********************************************************************/ +/**********************************************************************/ +/**********************************************************************/ + +class OriginalMatchingImageWidget : public QWidget { +public: + OriginalMatchingImageWidget(QWidget* parent = nullptr) : QWidget(parent) { + // Set the fixed size to 100x100 pixels + setFixedSize(100, 100); + + // Create a red pixmap of 100x100 pixels + pixmap = QPixmap(100, 100); + pixmap.fill(Qt::red); // Fill it with the red color + } + +protected: + // Override the paintEvent to display the pixmap + void paintEvent(QPaintEvent* event) override { + QPainter painter(this); + painter.drawPixmap(0, 0, pixmap); // Draw the pixmap starting at (0, 0) + } + +private: + QPixmap pixmap; +}; + +/**********************************************************************/ +/**********************************************************************/ +/**********************************************************************/ +/***** *****/ +/***** ShaderPreviewerWidget *****/ +/***** *****/ +/**********************************************************************/ +/**********************************************************************/ +/**********************************************************************/ + +class ShaderPreviewerWidget : public QWidget { + public: + ShaderPreviewerWidget(QWidget* parent = nullptr) : QWidget(parent) { + // Set the fixed size to 100x100 pixels + setFixedSize(100, 100); + + // Create a red pixmap of 100x100 pixels + pixmap = QPixmap(100, 100); + pixmap.fill(Qt::blue); // Fill it with the red color + } + +protected: + // Override the paintEvent to display the pixmap + void paintEvent(QPaintEvent* event) override { + QPainter painter(this); + painter.drawPixmap(0, 0, pixmap); // Draw the pixmap starting at (0, 0) + } + +private: + QPixmap pixmap; +}; + /**********************************************************************/ /**********************************************************************/ /**********************************************************************/ @@ -264,7 +329,7 @@ class VisualShaderGraphicsScene : public QGraphicsScene { ~VisualShaderGraphicsScene(); bool add_node(const std::string& type, const QPointF& coordinate); - bool add_node(const int& n_id, const std::shared_ptr& n, const QPointF& coordinate, QWidget* embed_widget = nullptr); + bool add_node(const int& n_id, const std::shared_ptr& n, const QPointF& coordinate); bool delete_node(const int& n_id); VisualShaderEditor* get_editor() const { return editor; } @@ -471,6 +536,8 @@ class VisualShaderNodeGraphicsObject : public QGraphicsObject { QWidget* get_embed_widget() const { return embed_widget; } void set_embed_widget(QWidget* embed_widget) { this->embed_widget = embed_widget; } + ShaderPreviewerWidget* get_shader_previewer_widget() const { return shader_previewer_widget; } + Q_SIGNALS: /** * @brief Send a request to delete a node. @@ -533,6 +600,8 @@ class VisualShaderNodeGraphicsObject : public QGraphicsObject { mutable float rect_padding; // Calculated in boundingRect() mutable float rect_margin; // Calculated in boundingRect() + float port_caption_spacing = 4.0f; // Distance between the port and its caption + // Ports Style float connected_port_diameter = 8.0f; float unconnected_port_diameter = 6.0f; @@ -542,9 +611,15 @@ class VisualShaderNodeGraphicsObject : public QGraphicsObject { float port_caption_font_size = 8.0f; QWidget* embed_widget; - float embed_widget_h_padding = 10.0f; + float embed_widget_h_padding = 15.0f; float embed_widget_v_padding = 5.0f; + OriginalMatchingImageWidget* matching_image_widget; + float spacing_between_output_node_and_matching_image = 10.0f; + + ShaderPreviewerWidget* shader_previewer_widget; + float spacing_between_current_node_and_shader_previewer = 10.0f; + QRectF boundingRect() const override; void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget = nullptr) override; QVariant itemChange(GraphicsItemChange change, const QVariant& value) override; @@ -745,6 +820,34 @@ class VisualShaderConnectionGraphicsObject : public QGraphicsObject { /**********************************************************************/ /**********************************************************************/ +class VisualShaderNodeEmbedWidget : public QWidget { + Q_OBJECT + + public: + VisualShaderNodeEmbedWidget(const std::shared_ptr& node, QWidget* parent = nullptr); + ~VisualShaderNodeEmbedWidget(); + + void set_shader_previewer_widget(QWidget* shader_previewer_widget) { this->shader_previewer_widget = shader_previewer_widget; } + + private Q_SLOTS: + void on_preview_shader_button_pressed() { + bool is_visible{shader_previewer_widget->isVisible()}; + shader_previewer_widget->setVisible(!is_visible); + preview_shader_button->setText(!is_visible ? "Hide Preview" : "Show Preview"); + } + + private: + QVBoxLayout* layout; + + QPushButton* preview_shader_button; + + QWidget* shader_previewer_widget; +}; + +/*************************************/ +/* Input Node */ +/*************************************/ + class VisualShaderNodeInputEmbedWidget : public QComboBox { Q_OBJECT From 948dda806f48b803e199fa8f79ef99ce73ae66e0 Mon Sep 17 00:00:00 2001 From: Saif Kandil <74428638+k0T0z@users.noreply.github.com> Date: Fri, 27 Sep 2024 15:52:47 +0300 Subject: [PATCH 36/56] Fixed a crash due to accessing invalid pointer --- Editors/VisualShaderEditor.cpp | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/Editors/VisualShaderEditor.cpp b/Editors/VisualShaderEditor.cpp index a6113d5b8..14b461888 100644 --- a/Editors/VisualShaderEditor.cpp +++ b/Editors/VisualShaderEditor.cpp @@ -892,6 +892,7 @@ bool VisualShaderGraphicsScene::add_connection(const int& from_node_id, const in new VisualShaderConnectionGraphicsObject(from_node_id, from_port_index, from_o_port->get_global_coordinate()); from_o_port->connect(this->temporary_connection_graphics_object); addItem(this->temporary_connection_graphics_object); + return true; } if (to_node_id != (int)VisualShader::NODE_ID_INVALID && to_port_index != (int)VisualShader::PORT_INDEX_INVALID) { @@ -931,9 +932,11 @@ bool VisualShaderGraphicsScene::add_connection(const int& from_node_id, const in this->temporary_connection_graphics_object->set_to_node_id(to_node_id); this->temporary_connection_graphics_object->set_to_port_index(to_port_index); this->temporary_connection_graphics_object = nullptr; // Make sure to reset the temporary connection object + + return true; } - return true; + return false; } bool VisualShaderGraphicsScene::delete_connection(const int& from_node_id, const int& from_port_index, @@ -949,9 +952,17 @@ bool VisualShaderGraphicsScene::delete_connection(const int& from_node_id, const if (!from_o_port) { return false; } + + if (this->temporary_connection_graphics_object) { + from_o_port->detach_connection(); + removeItem(this->temporary_connection_graphics_object); + delete this->temporary_connection_graphics_object; + this->temporary_connection_graphics_object = nullptr; + return true; + } // If we have a complete connection, then we can disconnect the nodes - if (to_node_id != (int)VisualShader::NODE_ID_INVALID && to_port_index != (int)VisualShader::PORT_INDEX_INVALID && !this->temporary_connection_graphics_object) { + if (to_node_id != (int)VisualShader::NODE_ID_INVALID && to_port_index != (int)VisualShader::PORT_INDEX_INVALID) { VisualShaderConnectionGraphicsObject* c_o{from_o_port->get_connection_graphics_object()}; if (!c_o) { @@ -981,12 +992,6 @@ bool VisualShaderGraphicsScene::delete_connection(const int& from_node_id, const removeItem(c_o); delete c_o; return true; - } else if (to_node_id != (int)VisualShader::NODE_ID_INVALID && to_port_index != (int)VisualShader::PORT_INDEX_INVALID) { - from_o_port->detach_connection(); - removeItem(this->temporary_connection_graphics_object); - delete this->temporary_connection_graphics_object; - this->temporary_connection_graphics_object = nullptr; - return true; } return false; @@ -1131,6 +1136,10 @@ void VisualShaderGraphicsScene::on_port_dragged(QGraphicsObject* port, const QPo } void VisualShaderGraphicsScene::on_port_dropped(QGraphicsObject* port, const QPointF& coordinate) { + if (!temporary_connection_graphics_object) { + return; + } + // Find all items under the coordinate QList items_at_coordinate{this->items(coordinate)}; From 56f03894b831d2d909b15381b837939755ae875a Mon Sep 17 00:00:00 2001 From: Saif Kandil <74428638+k0T0z@users.noreply.github.com> Date: Fri, 27 Sep 2024 17:01:51 +0300 Subject: [PATCH 37/56] Added debugging rects to make sure of accurate calculations --- Editors/VisualShaderEditor.cpp | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/Editors/VisualShaderEditor.cpp b/Editors/VisualShaderEditor.cpp index 14b461888..93634482f 100644 --- a/Editors/VisualShaderEditor.cpp +++ b/Editors/VisualShaderEditor.cpp @@ -104,7 +104,8 @@ VisualShaderEditor::VisualShaderEditor(MessageModel* model, QWidget* parent) _nodeMapper->addMapping(name_edit, TreeNode::kNameFieldNumber); QObject::connect(save_button, &QAbstractButton::pressed, this, &BaseEditor::OnSave); - // visual_shader_model = _model->GetSubModel(TreeNode::kVisualShaderFieldNumber); + RebindSubModels(); + visual_shader_model = _model->GetSubModel(TreeNode::kVisualShaderFieldNumber); } VisualShaderEditor::~VisualShaderEditor() { @@ -1760,6 +1761,12 @@ void VisualShaderNodeGraphicsObject::paint(QPainter* painter, const QStyleOption // Instead of subtracting, add the ascent to properly align text within the rect float y{(float)(caption_rect.center().y() + (float)((fm.ascent() + fm.descent()) * 0.5f - fm.descent()))}; + // { + // painter->setPen(Qt::red); + // painter->setBrush(Qt::NoBrush); + // painter->drawRect(QRectF(x,y, (float)fm.horizontalAdvance(caption), (float)(fm.ascent() + fm.descent()))); + // } + QPointF coordinate{x, y}; painter->setPen(this->font_color); @@ -1801,6 +1808,12 @@ void VisualShaderNodeGraphicsObject::paint(QPainter* painter, const QStyleOption float y{(float)(port_rect.center().y()) + (float)((fm.ascent() + fm.descent()) * 0.5f - fm.descent())}; + // { + // painter->setPen(Qt::red); + // painter->setBrush(Qt::NoBrush); + // painter->drawRect(QRectF(x,y, (float)fm.horizontalAdvance(p_n), (float)(fm.ascent() + fm.descent()))); + // } + QPointF coordinate{x, y}; painter->setPen(this->font_color); @@ -1857,6 +1870,12 @@ void VisualShaderNodeGraphicsObject::paint(QPainter* painter, const QStyleOption float y{(float)(port_rect.center().y()) + (float)((fm.ascent() + fm.descent()) * 0.5f - fm.descent())}; + // { + // painter->setPen(Qt::red); + // painter->setBrush(Qt::NoBrush); + // painter->drawRect(QRectF(x,y, (float)fm.horizontalAdvance(p_n), (float)(fm.ascent() + fm.descent()))); + // } + QPointF coordinate{x, y}; painter->setPen(this->font_color); From 690e09d554dbb86b62db7ea2f2d7c78ff09a4bd7 Mon Sep 17 00:00:00 2001 From: Saif Kandil <74428638+k0T0z@users.noreply.github.com> Date: Fri, 27 Sep 2024 22:41:56 +0300 Subject: [PATCH 38/56] Improved a comment --- Editors/VisualShaderEditor.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Editors/VisualShaderEditor.cpp b/Editors/VisualShaderEditor.cpp index 93634482f..4ae737ff2 100644 --- a/Editors/VisualShaderEditor.cpp +++ b/Editors/VisualShaderEditor.cpp @@ -500,6 +500,10 @@ void VisualShaderEditor::on_load_image_button_pressed() { // For example, use QFileDialog to open an image file or // load an existing sprite or background from the project. // Then, send the image to OriginalMatchingImageWidget widget to display it. + // R0bert — 27/09/2024 at 22:10 + // i would use resource picker and let user pick a sprite or background that exists in the project + // Josh — 27/09/2024 at 22:13 + // sprites have multiple frames, which is a headache for this project because it's a lot more behavior we need to define } std::vector VisualShaderEditor::parse_node_category_path(const std::string& node_category_path) { From 3a65416ea4cf031ba284abb63ad132695a59821e Mon Sep 17 00:00:00 2001 From: Saif Kandil <74428638+k0T0z@users.noreply.github.com> Date: Sun, 29 Sep 2024 00:28:27 +0300 Subject: [PATCH 39/56] Finished the rendering of shaders using Qt GL and few other improvements and bug fixes --- Editors/VisualShaderEditor.cpp | 287 ++++++++++++++++++++++++++++++--- Editors/VisualShaderEditor.h | 115 +++++++++---- 2 files changed, 353 insertions(+), 49 deletions(-) diff --git a/Editors/VisualShaderEditor.cpp b/Editors/VisualShaderEditor.cpp index 4ae737ff2..68eb036bc 100644 --- a/Editors/VisualShaderEditor.cpp +++ b/Editors/VisualShaderEditor.cpp @@ -653,6 +653,183 @@ void CreateNodeDialog::update_selected_item() { } } +/**********************************************************************/ +/**********************************************************************/ +/**********************************************************************/ +/***** *****/ +/***** ShaderPreviewerWidget *****/ +/***** *****/ +/**********************************************************************/ +/**********************************************************************/ +/**********************************************************************/ + +ShaderPreviewerWidget::ShaderPreviewerWidget(QWidget* parent) + : QOpenGLWidget(parent), shader_program(nullptr), VAO(0), VBO(0), EBO(0) { + setFixedSize(100, 100); +} + +ShaderPreviewerWidget::~ShaderPreviewerWidget() { + makeCurrent(); + cleanup_buffers(); + doneCurrent(); +} + +void ShaderPreviewerWidget::set_code(const std::string& new_code) { + if (new_code == code) return; + + code = new_code; + shader_needs_update = true; + if (isVisible()) { + update_shader_program(); + timer.restart(); + update(); + } +} + +void ShaderPreviewerWidget::initializeGL() { + initializeOpenGLFunctions(); + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); // Black background + init_buffers(); + init_shaders(); + timer.start(); // Start the timer once OpenGL is initialized +} + +void ShaderPreviewerWidget::resizeGL(int w, int h) { + glViewport(0, 0, w, h); +} + +void ShaderPreviewerWidget::paintGL() { + if (!isVisible()) return; + + if (shader_needs_update) { + update_shader_program(); + } + + if (!shader_program) return; + + float time_value {timer.elapsed() * 0.001f}; + + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + + shader_program->bind(); + shader_program->setUniformValue("uTime", time_value); + + glBindVertexArray(VAO); + glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr); + glBindVertexArray(0); + + shader_program->release(); + + update(); // Request a repaint + Q_EMIT scene_update_requested(); // Update the scene +} + +void ShaderPreviewerWidget::init_buffers() { + float vertices[] = { + // positions // texCoords + -1.0f, 1.0f, 0.0f, 1.0f, + -1.0f, -1.0f, 0.0f, 0.0f, + 1.0f, -1.0f, 1.0f, 0.0f, + 1.0f, 1.0f, 1.0f, 1.0f + }; + + unsigned int indices[] = { 0, 1, 2, 0, 2, 3 }; + + glGenVertexArrays(1, &VAO); + glGenBuffers(1, &VBO); + glGenBuffers(1, &EBO); + + glBindVertexArray(VAO); + + glBindBuffer(GL_ARRAY_BUFFER, VBO); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); + + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)0); + glEnableVertexAttribArray(0); + + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)(2 * sizeof(float))); + glEnableVertexAttribArray(1); + + glBindVertexArray(0); +} + +void ShaderPreviewerWidget::cleanup_buffers() { + makeCurrent(); + glDeleteVertexArrays(1, &VAO); + glDeleteBuffers(1, &VBO); + glDeleteBuffers(1, &EBO); + doneCurrent(); +} + +void ShaderPreviewerWidget::update_shader_program() { + shader_program.reset(new QOpenGLShaderProgram(this)); + + const char* vertex_shader_source = R"( + #version 330 core + layout(location = 0) in vec2 aPos; + layout(location = 1) in vec2 aTexCoord; + + out vec2 TexCoord; + + void main() { + gl_Position = vec4(aPos, 0.0, 1.0); + TexCoord = aTexCoord; + } + )"; + + std::string fragment_shader_source = code.empty() ? R"( + #version 330 core + out vec4 FragColor; + in vec2 TexCoord; + + uniform float uTime; + + void main() { + FragColor = vec4(0.0, 0.0, 0.0, 1.0); + } + )" : "#version 330 core\n\n" + code; + + if (!shader_program->addShaderFromSourceCode(QOpenGLShader::Vertex, vertex_shader_source)) { + qWarning() << "Vertex shader compilation failed:" << shader_program->log(); + } + + if (!shader_program->addShaderFromSourceCode(QOpenGLShader::Fragment, fragment_shader_source.c_str())) { + qWarning() << "Fragment shader compilation failed:" << shader_program->log(); + } + + if (!shader_program->link()) { + qWarning() << "Shader program linking failed:" << shader_program->log(); + } + + shader_needs_update = false; +} + +void ShaderPreviewerWidget::init_shaders() { + update_shader_program(); +} + +void ShaderPreviewerWidget::showEvent(QShowEvent* event) { + QOpenGLWidget::showEvent(event); + if (!timer.isValid()) { + timer.start(); // Start the timer on first show + } else { + timer.restart(); + } + update(); // Trigger repaint when the widget becomes visible +} + +void ShaderPreviewerWidget::hideEvent(QHideEvent* event) { + QOpenGLWidget::hideEvent(event); + makeCurrent(); + cleanup_buffers(); + doneCurrent(); +} + + /**********************************************************************/ /**********************************************************************/ /**********************************************************************/ @@ -670,8 +847,6 @@ void CreateNodeDialog::update_selected_item() { VisualShaderGraphicsScene::VisualShaderGraphicsScene(VisualShader* vs, QObject* parent) : QGraphicsScene(parent), vs(vs), temporary_connection_graphics_object(nullptr) { setItemIndexMethod(QGraphicsScene::NoIndex); // https://doc.qt.io/qt-6/qgraphicsscene.html#ItemIndexMethod-enum - - QObject::connect(this, &VisualShaderGraphicsScene::node_moved, this, &VisualShaderGraphicsScene::on_node_moved); } VisualShaderGraphicsScene::~VisualShaderGraphicsScene() {} @@ -753,7 +928,7 @@ bool VisualShaderGraphicsScene::add_node(const int& n_id, const std::shared_ptr< } // The output node cannot be removed or added by the user - if (n_id >= VisualShader::NODE_ID_OUTPUT + 1) { + if (n_id >= (int)VisualShader::NODE_ID_OUTPUT + 1) { bool result{vs->add_node(n, {(float)coordinate.x(), (float)coordinate.y()}, n_id)}; if (!result) { @@ -772,16 +947,32 @@ bool VisualShaderGraphicsScene::add_node(const int& n_id, const std::shared_ptr< VisualShaderNodeGraphicsObject* n_o{new VisualShaderNodeGraphicsObject(n_id, coordinate, n)}; + QObject::connect(n_o, &VisualShaderNodeGraphicsObject::node_moved, this, &VisualShaderGraphicsScene::on_node_moved); + QObject::connect(n_o, &VisualShaderNodeGraphicsObject::in_port_pressed, this, &VisualShaderGraphicsScene::on_port_pressed); + QObject::connect(n_o, &VisualShaderNodeGraphicsObject::in_port_dragged, this, &VisualShaderGraphicsScene::on_port_dragged); + QObject::connect(n_o, &VisualShaderNodeGraphicsObject::in_port_dropped, this, &VisualShaderGraphicsScene::on_port_dropped); + QObject::connect(n_o, &VisualShaderNodeGraphicsObject::out_port_pressed, this, &VisualShaderGraphicsScene::on_port_pressed); + QObject::connect(n_o, &VisualShaderNodeGraphicsObject::out_port_dragged, this, &VisualShaderGraphicsScene::on_port_dragged); + QObject::connect(n_o, &VisualShaderNodeGraphicsObject::out_port_dropped, this, &VisualShaderGraphicsScene::on_port_dropped); + if (n_id != (int)VisualShader::NODE_ID_OUTPUT) { VisualShaderNodeEmbedWidget* embed_widget {new VisualShaderNodeEmbedWidget(n)}; QGraphicsProxyWidget* embed_widget_proxy{new QGraphicsProxyWidget(n_o)}; embed_widget_proxy->setWidget(embed_widget); n_o->set_embed_widget(embed_widget); + QObject::connect(embed_widget, + &VisualShaderNodeEmbedWidget::shader_preview_update_requested, + this, + &VisualShaderGraphicsScene::on_update_shader_previewer_widgets_requested); // Send the shader previewer widget embed_widget->set_shader_previewer_widget(n_o->get_shader_previewer_widget()); } + if (ShaderPreviewerWidget* spw{n_o->get_shader_previewer_widget()}) { + QObject::connect(spw, &ShaderPreviewerWidget::scene_update_requested, this, &VisualShaderGraphicsScene::on_scene_update_requested); + } + QObject::connect(n_o, &VisualShaderNodeGraphicsObject::node_deleted, this, &VisualShaderGraphicsScene::on_node_deleted); node_graphics_objects[n_id] = n_o; @@ -864,6 +1055,25 @@ bool VisualShaderGraphicsScene::delete_node(const int& n_id) { return true; } +void VisualShaderGraphicsScene::on_update_shader_previewer_widgets_requested() { + for (auto& [n_id, n_o] : node_graphics_objects) { + if (n_id == (int)VisualShader::NODE_ID_OUTPUT) { + continue; + } + + ShaderPreviewerWidget* spw{n_o->get_shader_previewer_widget()}; + if (!spw) { + continue; + } + + spw->set_code(vs->generate_preview_shader(n_id, 0)); // 0 is the output port index + } +} + +void VisualShaderGraphicsScene::on_scene_update_requested() { + update(); +} + bool VisualShaderGraphicsScene::add_connection(const int& from_node_id, const int& from_port_index, const int& to_node_id, const int& to_port_index) { QList views{this->views()}; @@ -938,6 +1148,8 @@ bool VisualShaderGraphicsScene::add_connection(const int& from_node_id, const in this->temporary_connection_graphics_object->set_to_port_index(to_port_index); this->temporary_connection_graphics_object = nullptr; // Make sure to reset the temporary connection object + on_update_shader_previewer_widgets_requested(); + return true; } @@ -996,6 +1208,9 @@ bool VisualShaderGraphicsScene::delete_connection(const int& from_node_id, const from_o_port->detach_connection(); removeItem(c_o); delete c_o; + + on_update_shader_previewer_widgets_requested(); + return true; } @@ -1093,6 +1308,8 @@ void VisualShaderGraphicsScene::on_port_dragged(QGraphicsObject* port, const QPo } i_port->detach_connection(); c_o->detach_end(); + + on_update_shader_previewer_widgets_requested(); } else if (!i_port->is_connected() && temporary_connection_graphics_object) { c_o = temporary_connection_graphics_object; } else { @@ -1133,6 +1350,12 @@ void VisualShaderGraphicsScene::on_port_dragged(QGraphicsObject* port, const QPo } i_port->detach_connection(); c_o->detach_end(); + + on_update_shader_previewer_widgets_requested(); + + // Here we must request a repaint as this connection will be drawn some where else. + // I know this doesn't make any sense, just comment it and try to dray an output port that is already connected. + update(); // Request a repaint } else { return; } @@ -1833,11 +2056,9 @@ void VisualShaderNodeGraphicsObject::paint(QPainter* painter, const QStyleOption in_port_graphics_objects[i] = p_o; // Connect the signals - QObject::connect(p_o, &VisualShaderInputPortGraphicsObject::port_pressed, dynamic_cast(scene()), &VisualShaderGraphicsScene::on_port_pressed); - QObject::connect(p_o, &VisualShaderInputPortGraphicsObject::port_dragged, - dynamic_cast(scene()), &VisualShaderGraphicsScene::on_port_dragged); - QObject::connect(p_o, &VisualShaderInputPortGraphicsObject::port_dropped, - dynamic_cast(scene()), &VisualShaderGraphicsScene::on_port_dropped); + QObject::connect(p_o, &VisualShaderInputPortGraphicsObject::port_pressed, this, &VisualShaderNodeGraphicsObject::on_in_port_pressed); + QObject::connect(p_o, &VisualShaderInputPortGraphicsObject::port_dragged, this, &VisualShaderNodeGraphicsObject::on_in_port_dragged); + QObject::connect(p_o, &VisualShaderInputPortGraphicsObject::port_dropped, this, &VisualShaderNodeGraphicsObject::on_in_port_dropped); } } @@ -1895,11 +2116,9 @@ void VisualShaderNodeGraphicsObject::paint(QPainter* painter, const QStyleOption out_port_graphics_objects[i] = p_o; // Connect the signals - QObject::connect(p_o, &VisualShaderOutputPortGraphicsObject::port_pressed, dynamic_cast(scene()), &VisualShaderGraphicsScene::on_port_pressed); - QObject::connect(p_o, &VisualShaderOutputPortGraphicsObject::port_dragged, - dynamic_cast(scene()), &VisualShaderGraphicsScene::on_port_dragged); - QObject::connect(p_o, &VisualShaderOutputPortGraphicsObject::port_dropped, - dynamic_cast(scene()), &VisualShaderGraphicsScene::on_port_dropped); + QObject::connect(p_o, &VisualShaderOutputPortGraphicsObject::port_pressed, this, &VisualShaderNodeGraphicsObject::on_out_port_pressed); + QObject::connect(p_o, &VisualShaderOutputPortGraphicsObject::port_dragged, this, &VisualShaderNodeGraphicsObject::on_out_port_dragged); + QObject::connect(p_o, &VisualShaderOutputPortGraphicsObject::port_dropped, this, &VisualShaderNodeGraphicsObject::on_out_port_dropped); } } @@ -1916,7 +2135,7 @@ void VisualShaderNodeGraphicsObject::paint(QPainter* painter, const QStyleOption QVariant VisualShaderNodeGraphicsObject::itemChange(GraphicsItemChange change, const QVariant& value) { if (scene() && change == ItemScenePositionHasChanged) { - Q_EMIT dynamic_cast(scene())->node_moved(n_id, pos()); + Q_EMIT node_moved(n_id, pos()); } return QGraphicsObject::itemChange(change, value); @@ -1963,17 +2182,17 @@ void VisualShaderInputPortGraphicsObject::paint(QPainter* painter, const QStyleO } void VisualShaderInputPortGraphicsObject::mousePressEvent(QGraphicsSceneMouseEvent* event) { - emit port_pressed(this, event->scenePos()); + Q_EMIT port_pressed(this, event->scenePos()); QGraphicsObject::mousePressEvent(event); } void VisualShaderInputPortGraphicsObject::mouseMoveEvent(QGraphicsSceneMouseEvent* event) { - emit port_dragged(this, event->scenePos()); + Q_EMIT port_dragged(this, event->scenePos()); QGraphicsObject::mouseMoveEvent(event); } void VisualShaderInputPortGraphicsObject::mouseReleaseEvent(QGraphicsSceneMouseEvent* event) { - emit port_dropped(this, event->scenePos()); + Q_EMIT port_dropped(this, event->scenePos()); QGraphicsObject::mouseReleaseEvent(event); } @@ -2012,17 +2231,17 @@ void VisualShaderOutputPortGraphicsObject::paint(QPainter* painter, const QStyle } void VisualShaderOutputPortGraphicsObject::mousePressEvent(QGraphicsSceneMouseEvent* event) { - emit port_pressed(this, event->scenePos()); + Q_EMIT port_pressed(this, event->scenePos()); QGraphicsObject::mousePressEvent(event); } void VisualShaderOutputPortGraphicsObject::mouseMoveEvent(QGraphicsSceneMouseEvent* event) { - emit port_dragged(this, event->scenePos()); + Q_EMIT port_dragged(this, event->scenePos()); QGraphicsObject::mouseMoveEvent(event); } void VisualShaderOutputPortGraphicsObject::mouseReleaseEvent(QGraphicsSceneMouseEvent* event) { - emit port_dropped(this, event->scenePos()); + Q_EMIT port_dropped(this, event->scenePos()); QGraphicsObject::mouseReleaseEvent(event); } @@ -2326,24 +2545,52 @@ VisualShaderNodeEmbedWidget::VisualShaderNodeEmbedWidget(const std::shared_ptr(node)}) { VisualShaderNodeInputEmbedWidget* embed_widget = new VisualShaderNodeInputEmbedWidget(p); layout->addWidget(embed_widget); + QObject::connect(embed_widget, + QOverload::of(&QComboBox::currentIndexChanged), + this, + &VisualShaderNodeEmbedWidget::on_shader_preview_update_requested); } else if (auto p {std::dynamic_pointer_cast(node)}) { VisualShaderNodeFloatFuncEmbedWidget* embed_widget = new VisualShaderNodeFloatFuncEmbedWidget(p); layout->addWidget(embed_widget); + QObject::connect(embed_widget, + QOverload::of(&QComboBox::currentIndexChanged), + this, + &VisualShaderNodeEmbedWidget::on_shader_preview_update_requested); } else if (auto p {std::dynamic_pointer_cast(node)}) { VisualShaderNodeIntFuncEmbedWidget* embed_widget = new VisualShaderNodeIntFuncEmbedWidget(p); layout->addWidget(embed_widget); + QObject::connect(embed_widget, + QOverload::of(&QComboBox::currentIndexChanged), + this, + &VisualShaderNodeEmbedWidget::on_shader_preview_update_requested); } else if (auto p {std::dynamic_pointer_cast(node)}) { VisualShaderNodeUIntFuncEmbedWidget* embed_widget = new VisualShaderNodeUIntFuncEmbedWidget(p); layout->addWidget(embed_widget); + QObject::connect(embed_widget, + QOverload::of(&QComboBox::currentIndexChanged), + this, + &VisualShaderNodeEmbedWidget::on_shader_preview_update_requested); } else if (auto p {std::dynamic_pointer_cast(node)}) { VisualShaderNodeFloatOpEmbedWidget* embed_widget = new VisualShaderNodeFloatOpEmbedWidget(p); layout->addWidget(embed_widget); + QObject::connect(embed_widget, + QOverload::of(&QComboBox::currentIndexChanged), + this, + &VisualShaderNodeEmbedWidget::on_shader_preview_update_requested); } else if (auto p {std::dynamic_pointer_cast(node)}) { VisualShaderNodeIntOpEmbedWidget* embed_widget = new VisualShaderNodeIntOpEmbedWidget(p); layout->addWidget(embed_widget); + QObject::connect(embed_widget, + QOverload::of(&QComboBox::currentIndexChanged), + this, + &VisualShaderNodeEmbedWidget::on_shader_preview_update_requested); } else if (auto p {std::dynamic_pointer_cast(node)}) { VisualShaderNodeUIntOpEmbedWidget* embed_widget = new VisualShaderNodeUIntOpEmbedWidget(p); layout->addWidget(embed_widget); + QObject::connect(embed_widget, + QOverload::of(&QComboBox::currentIndexChanged), + this, + &VisualShaderNodeEmbedWidget::on_shader_preview_update_requested); } // Create the button that will show/hide the shader previewer diff --git a/Editors/VisualShaderEditor.h b/Editors/VisualShaderEditor.h index 50e000661..b39494863 100644 --- a/Editors/VisualShaderEditor.h +++ b/Editors/VisualShaderEditor.h @@ -47,6 +47,11 @@ #include #include #include +#include +// #include +#include // https://stackoverflow.com/a/64288966/14629018 explains why we need this. +#include +#include #include #include @@ -288,26 +293,38 @@ class OriginalMatchingImageWidget : public QWidget { /**********************************************************************/ /**********************************************************************/ -class ShaderPreviewerWidget : public QWidget { - public: - ShaderPreviewerWidget(QWidget* parent = nullptr) : QWidget(parent) { - // Set the fixed size to 100x100 pixels - setFixedSize(100, 100); +class ShaderPreviewerWidget : public QOpenGLWidget, protected QOpenGLFunctions_3_3_Core { + Q_OBJECT - // Create a red pixmap of 100x100 pixels - pixmap = QPixmap(100, 100); - pixmap.fill(Qt::blue); // Fill it with the red color - } +public: + ShaderPreviewerWidget(QWidget* parent = nullptr); + ~ShaderPreviewerWidget() override; + + void set_code(const std::string& code); + +Q_SIGNALS: + void scene_update_requested(); protected: - // Override the paintEvent to display the pixmap - void paintEvent(QPaintEvent* event) override { - QPainter painter(this); - painter.drawPixmap(0, 0, pixmap); // Draw the pixmap starting at (0, 0) - } + void initializeGL() override; + void resizeGL(int w, int h) override; + void paintGL() override; + + void showEvent(QShowEvent* event) override; + void hideEvent(QHideEvent* event) override; private: - QPixmap pixmap; + std::unique_ptr shader_program; + GLuint VAO, VBO, EBO; + QElapsedTimer timer; + + std::string code; + bool shader_needs_update {false}; // Track if the shader needs recompiling + + void init_shaders(); + void init_buffers(); + void update_shader_program(); + void cleanup_buffers(); }; /**********************************************************************/ @@ -375,20 +392,6 @@ class VisualShaderGraphicsScene : public QGraphicsScene { void on_port_dragged(QGraphicsObject* port, const QPointF& coordinate); void on_port_dropped(QGraphicsObject* port, const QPointF& coordinate); - Q_SIGNALS: - /** - * @brief Notify the scene that a node has been moved. - * - * @note EMITTED from @c VisualShaderNodeGraphicsObject::itemChange function. - * - * @note Connected to @c VisualShaderGraphicsScene::on_node_moved slot in - * @c VisualShaderGraphicsScene::VisualShaderGraphicsScene constructor. - * - * @param n_id - * @param new_coordinate - */ - void node_moved(const int& n_id, const QPointF& new_coordinate); - private Q_SLOTS: /** * @brief Called when a node is moved. @@ -411,6 +414,14 @@ class VisualShaderGraphicsScene : public QGraphicsScene { */ void on_node_deleted(const int& n_id); + /** + * @brief Updates the code inside all the nodes except the output node. + * + */ + void on_update_shader_previewer_widgets_requested(); + + void on_scene_update_requested(); + private: VisualShader* vs; mutable VisualShaderEditor* editor; @@ -551,6 +562,27 @@ class VisualShaderNodeGraphicsObject : public QGraphicsObject { */ void node_deleted(const int& n_id); + /** + * @brief Notify the scene that a node has been moved. + * + * @note EMITTED from @c VisualShaderNodeGraphicsObject::itemChange function. + * + * @note Connected to @c VisualShaderGraphicsScene::on_node_moved slot in + * @c VisualShaderGraphicsScene::VisualShaderGraphicsScene constructor. + * + * @param n_id + * @param new_coordinate + */ + void node_moved(const int& n_id, const QPointF& new_coordinate); + + void in_port_pressed(VisualShaderInputPortGraphicsObject* port, const QPointF& coordinate); + void in_port_dragged(VisualShaderInputPortGraphicsObject* port, const QPointF& coordinate); + void in_port_dropped(VisualShaderInputPortGraphicsObject* port, const QPointF& coordinate); + + void out_port_pressed(VisualShaderOutputPortGraphicsObject* port, const QPointF& coordinate); + void out_port_dragged(VisualShaderOutputPortGraphicsObject* port, const QPointF& coordinate); + void out_port_dropped(VisualShaderOutputPortGraphicsObject* port, const QPointF& coordinate); + private Q_SLOTS: /** * @brief Called when @c VisualShaderNodeGraphicsObject::delete_node_action is triggered. @@ -563,6 +595,26 @@ class VisualShaderNodeGraphicsObject : public QGraphicsObject { */ void on_delete_node_action_triggered(); + void on_in_port_pressed(VisualShaderInputPortGraphicsObject* port, const QPointF& coordinate) { + Q_EMIT in_port_pressed(port, coordinate); + } + void on_in_port_dragged(VisualShaderInputPortGraphicsObject* port, const QPointF& coordinate) { + Q_EMIT in_port_dragged(port, coordinate); + } + void on_in_port_dropped(VisualShaderInputPortGraphicsObject* port, const QPointF& coordinate) { + Q_EMIT in_port_dropped(port, coordinate); + } + + void on_out_port_pressed(VisualShaderOutputPortGraphicsObject* port, const QPointF& coordinate) { + Q_EMIT out_port_pressed(port, coordinate); + } + void on_out_port_dragged(VisualShaderOutputPortGraphicsObject* port, const QPointF& coordinate) { + Q_EMIT out_port_dragged(port, coordinate); + } + void on_out_port_dropped(VisualShaderOutputPortGraphicsObject* port, const QPointF& coordinate) { + Q_EMIT out_port_dropped(port, coordinate); + } + private: int n_id; QPointF coordinate; @@ -829,6 +881,9 @@ class VisualShaderNodeEmbedWidget : public QWidget { void set_shader_previewer_widget(QWidget* shader_previewer_widget) { this->shader_previewer_widget = shader_previewer_widget; } + Q_SIGNALS: + void shader_preview_update_requested(); + private Q_SLOTS: void on_preview_shader_button_pressed() { bool is_visible{shader_previewer_widget->isVisible()}; @@ -836,6 +891,8 @@ class VisualShaderNodeEmbedWidget : public QWidget { preview_shader_button->setText(!is_visible ? "Hide Preview" : "Show Preview"); } + void on_shader_preview_update_requested() { Q_EMIT shader_preview_update_requested(); } + private: QVBoxLayout* layout; From cef01a16b4f921d46797fba84d8d31890ea8a7df Mon Sep 17 00:00:00 2001 From: Saif Kandil <74428638+k0T0z@users.noreply.github.com> Date: Sun, 29 Sep 2024 18:51:15 +0300 Subject: [PATCH 40/56] Bug fixes and few improvements --- Editors/VisualShaderEditor.cpp | 143 ++++++++++++++++++++------------- Editors/VisualShaderEditor.h | 29 +++++-- 2 files changed, 106 insertions(+), 66 deletions(-) diff --git a/Editors/VisualShaderEditor.cpp b/Editors/VisualShaderEditor.cpp index 68eb036bc..bf6daf713 100644 --- a/Editors/VisualShaderEditor.cpp +++ b/Editors/VisualShaderEditor.cpp @@ -663,16 +663,10 @@ void CreateNodeDialog::update_selected_item() { /**********************************************************************/ /**********************************************************************/ -ShaderPreviewerWidget::ShaderPreviewerWidget(QWidget* parent) - : QOpenGLWidget(parent), shader_program(nullptr), VAO(0), VBO(0), EBO(0) { - setFixedSize(100, 100); -} +ShaderPreviewerWidget::ShaderPreviewerWidget(QWidget* parent) + : QOpenGLWidget(parent), shader_program(nullptr), VAO(0), VBO(0) {} -ShaderPreviewerWidget::~ShaderPreviewerWidget() { - makeCurrent(); - cleanup_buffers(); - doneCurrent(); -} +ShaderPreviewerWidget::~ShaderPreviewerWidget() {} void ShaderPreviewerWidget::set_code(const std::string& new_code) { if (new_code == code) return; @@ -682,24 +676,54 @@ void ShaderPreviewerWidget::set_code(const std::string& new_code) { if (isVisible()) { update_shader_program(); timer.restart(); - update(); } } void ShaderPreviewerWidget::initializeGL() { - initializeOpenGLFunctions(); - glClearColor(0.0f, 0.0f, 0.0f, 1.0f); // Black background + QOpenGLFunctions_4_3_Core* f {QOpenGLContext::currentContext()->versionFunctions()}; + + if (!f) { + qWarning() << "Failed to get OpenGL 4.3 functions"; + return; + } + + if (!f->initializeOpenGLFunctions()) { + qWarning() << "Failed to initialize OpenGL functions"; + return; + } + + f->glClearColor(0.0f, 0.0f, 0.0f, 1.0f); // Black background init_buffers(); init_shaders(); - timer.start(); // Start the timer once OpenGL is initialized + + timer.start(); + + connect(context(), &QOpenGLContext::aboutToBeDestroyed, this, &ShaderPreviewerWidget::cleanup); } void ShaderPreviewerWidget::resizeGL(int w, int h) { - glViewport(0, 0, w, h); + QOpenGLFunctions_4_3_Core* f {QOpenGLContext::currentContext()->versionFunctions()}; + + if (!f) { + qWarning() << "Failed to get OpenGL 4.3 functions"; + return; + } + + f->glViewport(0, 0, w, h); } void ShaderPreviewerWidget::paintGL() { - if (!isVisible()) return; + // Check https://doc.qt.io/qt-5/qopenglwidget.html#isValid + // At start, the widget is hidden so this call returns false which results + // in returning (no painting happens). + if (!isValid()) return; + + QOpenGLFunctions_4_3_Core* f {QOpenGLContext::currentContext()->versionFunctions()}; + + if (!f) { + qWarning() << "Failed to get OpenGL 4.3 functions"; + return; + } if (shader_needs_update) { update_shader_program(); @@ -709,64 +733,71 @@ void ShaderPreviewerWidget::paintGL() { float time_value {timer.elapsed() * 0.001f}; - glClearColor(0.0f, 0.0f, 0.0f, 1.0f); - glClear(GL_COLOR_BUFFER_BIT); + f->glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + f->glClear(GL_COLOR_BUFFER_BIT); shader_program->bind(); shader_program->setUniformValue("uTime", time_value); - glBindVertexArray(VAO); - glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr); - glBindVertexArray(0); + f->glBindVertexArray(VAO); + f->glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + f->glBindVertexArray(0); shader_program->release(); update(); // Request a repaint - Q_EMIT scene_update_requested(); // Update the scene + Q_EMIT scene_update_requested(); +} + +void ShaderPreviewerWidget::cleanup() { + makeCurrent(); + + QOpenGLFunctions_4_3_Core* f {QOpenGLContext::currentContext()->versionFunctions()}; + + if (!f) { + qWarning() << "Failed to get OpenGL 4.3 functions"; + return; + } + + f->glDeleteVertexArrays(1, &VAO); + f->glDeleteBuffers(1, &VBO); } void ShaderPreviewerWidget::init_buffers() { + QOpenGLFunctions_4_3_Core* f {QOpenGLContext::currentContext()->versionFunctions()}; + + if (!f) { + qWarning() << "Failed to get OpenGL 4.3 functions"; + return; + } + float vertices[] = { // positions // texCoords -1.0f, 1.0f, 0.0f, 1.0f, -1.0f, -1.0f, 0.0f, 0.0f, - 1.0f, -1.0f, 1.0f, 0.0f, - 1.0f, 1.0f, 1.0f, 1.0f + 1.0f, 1.0f, 1.0f, 1.0f, + 1.0f, -1.0f, 1.0f, 0.0f }; - unsigned int indices[] = { 0, 1, 2, 0, 2, 3 }; - - glGenVertexArrays(1, &VAO); - glGenBuffers(1, &VBO); - glGenBuffers(1, &EBO); - - glBindVertexArray(VAO); - - glBindBuffer(GL_ARRAY_BUFFER, VBO); - glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); + f->glGenVertexArrays(1, &VAO); + f->glGenBuffers(1, &VBO); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); + f->glBindVertexArray(VAO); - glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)0); - glEnableVertexAttribArray(0); + f->glBindBuffer(GL_ARRAY_BUFFER, VBO); + f->glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); - glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)(2 * sizeof(float))); - glEnableVertexAttribArray(1); + f->glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)0); + f->glEnableVertexAttribArray(0); - glBindVertexArray(0); -} + f->glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)(2 * sizeof(float))); + f->glEnableVertexAttribArray(1); -void ShaderPreviewerWidget::cleanup_buffers() { - makeCurrent(); - glDeleteVertexArrays(1, &VAO); - glDeleteBuffers(1, &VBO); - glDeleteBuffers(1, &EBO); - doneCurrent(); + f->glBindVertexArray(0); } void ShaderPreviewerWidget::update_shader_program() { - shader_program.reset(new QOpenGLShaderProgram(this)); + shader_program.reset(new QOpenGLShaderProgram()); const char* vertex_shader_source = R"( #version 330 core @@ -815,21 +846,17 @@ void ShaderPreviewerWidget::init_shaders() { void ShaderPreviewerWidget::showEvent(QShowEvent* event) { QOpenGLWidget::showEvent(event); if (!timer.isValid()) { - timer.start(); // Start the timer on first show - } else { - timer.restart(); + // See https://doc.qt.io/qt-5/qelapsedtimer.html#start. + timer.start(); // Start the timer on first show } - update(); // Trigger repaint when the widget becomes visible } void ShaderPreviewerWidget::hideEvent(QHideEvent* event) { QOpenGLWidget::hideEvent(event); - makeCurrent(); - cleanup_buffers(); - doneCurrent(); + // See https://doc.qt.io/qt-5/qelapsedtimer.html#invalidate. + timer.invalidate(); } - /**********************************************************************/ /**********************************************************************/ /**********************************************************************/ @@ -1945,12 +1972,12 @@ void VisualShaderNodeGraphicsObject::paint(QPainter* painter, const QStyleOption float matching_image_widget_x{(float)r.x() + (float)r.width() + spacing_between_output_node_and_matching_image}; float matching_image_widget_y{(float)r.y()}; - matching_image_widget->setGeometry(matching_image_widget_x, matching_image_widget_y, matching_image_widget->width(), matching_image_widget->height()); + matching_image_widget->setGeometry(matching_image_widget_x, matching_image_widget_y, matching_image_widget_width, r.height()); } else { // Draw Shader Previewer Widget float shader_previewer_widget_x{(float)r.x()}; float shader_previewer_widget_y{(float)r.y() + (float)r.height() + spacing_between_current_node_and_shader_previewer}; - shader_previewer_widget->setGeometry(shader_previewer_widget_x, shader_previewer_widget_y, shader_previewer_widget->width(), shader_previewer_widget->height()); + shader_previewer_widget->setGeometry(shader_previewer_widget_x, shader_previewer_widget_y, r.width(), shader_previewer_widget_height); } // Add the margin to the rect diff --git a/Editors/VisualShaderEditor.h b/Editors/VisualShaderEditor.h index b39494863..44b63f2aa 100644 --- a/Editors/VisualShaderEditor.h +++ b/Editors/VisualShaderEditor.h @@ -49,7 +49,7 @@ #include #include // #include -#include // https://stackoverflow.com/a/64288966/14629018 explains why we need this. +#include // https://stackoverflow.com/a/64288966/14629018 explains why we need this. #include #include @@ -264,9 +264,6 @@ class CreateNodeDialog : public QDialog { class OriginalMatchingImageWidget : public QWidget { public: OriginalMatchingImageWidget(QWidget* parent = nullptr) : QWidget(parent) { - // Set the fixed size to 100x100 pixels - setFixedSize(100, 100); - // Create a red pixmap of 100x100 pixels pixmap = QPixmap(100, 100); pixmap.fill(Qt::red); // Fill it with the red color @@ -293,7 +290,7 @@ class OriginalMatchingImageWidget : public QWidget { /**********************************************************************/ /**********************************************************************/ -class ShaderPreviewerWidget : public QOpenGLWidget, protected QOpenGLFunctions_3_3_Core { +class ShaderPreviewerWidget : public QOpenGLWidget { Q_OBJECT public: @@ -315,16 +312,29 @@ class ShaderPreviewerWidget : public QOpenGLWidget, protected QOpenGLFunctions_3 private: std::unique_ptr shader_program; - GLuint VAO, VBO, EBO; + GLuint VAO, VBO; QElapsedTimer timer; std::string code; - bool shader_needs_update {false}; // Track if the shader needs recompiling + bool shader_needs_update {false}; void init_shaders(); void init_buffers(); void update_shader_program(); - void cleanup_buffers(); + + /** + * @brief Cleans up the OpenGL resources. + * + * @note This function is called automatically when the widget is destroyed. + * It is connected @c QOpenGLContext::aboutToBeDestroyed signal. + * + * @note DON'T call this function in the destructor as it is + * called automatically. If you call it from the destructor, + * it will crash as @c makeCurrent() won't be able to make the + * context current. + * + */ + void cleanup(); }; /**********************************************************************/ @@ -672,6 +682,9 @@ class VisualShaderNodeGraphicsObject : public QGraphicsObject { ShaderPreviewerWidget* shader_previewer_widget; float spacing_between_current_node_and_shader_previewer = 10.0f; + const float matching_image_widget_width = 100.0f; + const float shader_previewer_widget_height = 100.0f; + QRectF boundingRect() const override; void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget = nullptr) override; QVariant itemChange(GraphicsItemChange change, const QVariant& value) override; From 2fea6645a6d021b5c4142e89242601465383c305 Mon Sep 17 00:00:00 2001 From: Saif Kandil <74428638+k0T0z@users.noreply.github.com> Date: Sun, 29 Sep 2024 19:22:51 +0300 Subject: [PATCH 41/56] few improvements --- Editors/VisualShaderEditor.cpp | 4 ++-- Editors/VisualShaderEditor.h | 6 +----- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/Editors/VisualShaderEditor.cpp b/Editors/VisualShaderEditor.cpp index bf6daf713..e13806caa 100644 --- a/Editors/VisualShaderEditor.cpp +++ b/Editors/VisualShaderEditor.cpp @@ -1972,12 +1972,12 @@ void VisualShaderNodeGraphicsObject::paint(QPainter* painter, const QStyleOption float matching_image_widget_x{(float)r.x() + (float)r.width() + spacing_between_output_node_and_matching_image}; float matching_image_widget_y{(float)r.y()}; - matching_image_widget->setGeometry(matching_image_widget_x, matching_image_widget_y, matching_image_widget_width, r.height()); + matching_image_widget->setGeometry(matching_image_widget_x, matching_image_widget_y, r.height(), r.height()); } else { // Draw Shader Previewer Widget float shader_previewer_widget_x{(float)r.x()}; float shader_previewer_widget_y{(float)r.y() + (float)r.height() + spacing_between_current_node_and_shader_previewer}; - shader_previewer_widget->setGeometry(shader_previewer_widget_x, shader_previewer_widget_y, r.width(), shader_previewer_widget_height); + shader_previewer_widget->setGeometry(shader_previewer_widget_x, shader_previewer_widget_y, r.width(), r.width()); } // Add the margin to the rect diff --git a/Editors/VisualShaderEditor.h b/Editors/VisualShaderEditor.h index 44b63f2aa..13cfc7152 100644 --- a/Editors/VisualShaderEditor.h +++ b/Editors/VisualShaderEditor.h @@ -264,8 +264,7 @@ class CreateNodeDialog : public QDialog { class OriginalMatchingImageWidget : public QWidget { public: OriginalMatchingImageWidget(QWidget* parent = nullptr) : QWidget(parent) { - // Create a red pixmap of 100x100 pixels - pixmap = QPixmap(100, 100); + pixmap = QPixmap(size()); pixmap.fill(Qt::red); // Fill it with the red color } @@ -682,9 +681,6 @@ class VisualShaderNodeGraphicsObject : public QGraphicsObject { ShaderPreviewerWidget* shader_previewer_widget; float spacing_between_current_node_and_shader_previewer = 10.0f; - const float matching_image_widget_width = 100.0f; - const float shader_previewer_widget_height = 100.0f; - QRectF boundingRect() const override; void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget = nullptr) override; QVariant itemChange(GraphicsItemChange change, const QVariant& value) override; From 9585ef42625b3d5be2bd55bff55e83475a8af7a1 Mon Sep 17 00:00:00 2001 From: Saif Kandil <74428638+k0T0z@users.noreply.github.com> Date: Sun, 29 Sep 2024 23:00:38 +0300 Subject: [PATCH 42/56] Added support for controlling noises parameters --- Editors/VisualShaderEditor.cpp | 149 +++++++++++++++++++++++++++++++++ Editors/VisualShaderEditor.h | 68 +++++++++++++++ 2 files changed, 217 insertions(+) diff --git a/Editors/VisualShaderEditor.cpp b/Editors/VisualShaderEditor.cpp index e13806caa..795ea0a39 100644 --- a/Editors/VisualShaderEditor.cpp +++ b/Editors/VisualShaderEditor.cpp @@ -426,6 +426,8 @@ const VisualShaderEditor::CreateNodeDialogNodesTreeItem VisualShaderEditor::crea {"ValueNoise", "Procedural/Noise", "VisualShaderNodeValueNoise", "Generates a simple, or Value, noise based on input 'UV'. The scale of the generated noise is controlled by input " "'Scale'."}, + {"PerlinNoise", "Procedural/Noise", "VisualShaderNodePerlinNoise", "Generates a gradient, or Perlin, noise based on input 'UV'. The scale of the generated noise is controlled by input 'Scale'."}, + {"VoronoiNoise", "Procedural/Noise", "VisualShaderNodeVoronoiNoise", "Generates a Voronoi, or Worley, noise based on input 'UV'. Voronoi noise is generated by calculating distances between a pixel and a lattice of points. By offsetting these points by a pseudo-random number, controlled by input 'Angle Offset', a cluster of cells can be generated. The scale of these cells, and the resulting noise, is controlled by input 'Cell Density'. The output 'Cells' contains the raw cell data."}, // Utility @@ -916,6 +918,18 @@ bool VisualShaderGraphicsScene::add_node(const std::string& type, const QPointF& n = std::make_shared(); } else if (type == "VisualShaderNodeValueNoise") { n = std::make_shared(); + } else if (type == "VisualShaderNodePerlinNoise") { + n = std::make_shared(); + } else if (type == "VisualShaderNodeVoronoiNoise") { + n = std::make_shared(); + } else if (type == "VisualShaderNodeVectorFunc") { + n = std::make_shared(); + } else if (type == "VisualShaderNodeVectorOp") { + n = std::make_shared(); + } else if (type == "VisualShaderNodeVectorCompose") { + n = std::make_shared(); + } else if (type == "VisualShaderNodeVectorDecompose") { + n = std::make_shared(); } else if (type == "VisualShaderNodeCompare") { n = std::make_shared(); } else if (type == "VisualShaderNodeIf") { @@ -2618,6 +2632,33 @@ VisualShaderNodeEmbedWidget::VisualShaderNodeEmbedWidget(const std::shared_ptr::of(&QComboBox::currentIndexChanged), this, &VisualShaderNodeEmbedWidget::on_shader_preview_update_requested); + } else if (auto p {std::dynamic_pointer_cast(node)}) { + VisualShaderNodeValueNoiseEmbedWidget* embed_widget = new VisualShaderNodeValueNoiseEmbedWidget(p); + layout->addWidget(embed_widget); + QObject::connect(embed_widget, + &QLineEdit::textChanged, + this, + &VisualShaderNodeEmbedWidget::on_shader_preview_update_requested); + } else if (auto p {std::dynamic_pointer_cast(node)}) { + VisualShaderNodePerlinNoiseEmbedWidget* embed_widget = new VisualShaderNodePerlinNoiseEmbedWidget(p); + layout->addWidget(embed_widget); + QObject::connect(embed_widget, + &QLineEdit::textChanged, + this, + &VisualShaderNodeEmbedWidget::on_shader_preview_update_requested); + } else if (auto p {std::dynamic_pointer_cast(node)}) { + VisualShaderNodeVoronoiNoiseAngleOffsetEmbedWidget* embed_widget = new VisualShaderNodeVoronoiNoiseAngleOffsetEmbedWidget(p); + VisualShaderNodeVoronoiNoiseCellDensityEmbedWidget* embed_widget2 = new VisualShaderNodeVoronoiNoiseCellDensityEmbedWidget(p); + layout->addWidget(embed_widget); + layout->addWidget(embed_widget2); + QObject::connect(embed_widget, + &QLineEdit::textChanged, + this, + &VisualShaderNodeEmbedWidget::on_shader_preview_update_requested); + QObject::connect(embed_widget2, + &QLineEdit::textChanged, + this, + &VisualShaderNodeEmbedWidget::on_shader_preview_update_requested); } // Create the button that will show/hide the shader previewer @@ -2960,3 +3001,111 @@ VisualShaderNodeVectorFuncEmbedWidget::~VisualShaderNodeVectorFuncEmbedWidget() void VisualShaderNodeVectorFuncEmbedWidget::on_current_index_changed(const int& index) { node->set_function((VisualShaderNodeVectorFunc::Function)itemData(index).toInt()); } + +/*************************************/ +/* Value Noise Node */ +/*************************************/ + +VisualShaderNodeValueNoiseEmbedWidget::VisualShaderNodeValueNoiseEmbedWidget(const std::shared_ptr& node) : QLineEdit(), + node(node) { + setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom + + setPlaceholderText("Scale"); + + QObject::connect(this, &QLineEdit::textChanged, this, &VisualShaderNodeValueNoiseEmbedWidget::on_text_changed); +} + +VisualShaderNodeValueNoiseEmbedWidget::~VisualShaderNodeValueNoiseEmbedWidget() {} + +void VisualShaderNodeValueNoiseEmbedWidget::on_text_changed(const QString& text) { + if (text.isEmpty()) { + node->set_scale(100.0f); + } else { + bool ok; + text.toFloat(&ok); + if (ok) { + node->set_scale(text.toFloat()); + } + } +} + +/*************************************/ +/* Perlin Noise Node */ +/*************************************/ + +VisualShaderNodePerlinNoiseEmbedWidget::VisualShaderNodePerlinNoiseEmbedWidget(const std::shared_ptr& node) : QLineEdit(), + node(node) { + setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom + + setPlaceholderText("Scale"); + + QObject::connect(this, &QLineEdit::textChanged, this, &VisualShaderNodePerlinNoiseEmbedWidget::on_text_changed); +} + +VisualShaderNodePerlinNoiseEmbedWidget::~VisualShaderNodePerlinNoiseEmbedWidget() {} + +void VisualShaderNodePerlinNoiseEmbedWidget::on_text_changed(const QString& text) { + if (text.isEmpty()) { + node->set_scale(10.0f); + } else { + bool ok; + text.toFloat(&ok); + if (ok) { + node->set_scale(text.toFloat()); + } + } +} + +/*************************************/ +/* Voronoi Noise Node */ +/*************************************/ + +VisualShaderNodeVoronoiNoiseAngleOffsetEmbedWidget::VisualShaderNodeVoronoiNoiseAngleOffsetEmbedWidget(const std::shared_ptr& node) : QLineEdit(), + node(node) { + setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom + + setPlaceholderText("Angle Offset"); + + QObject::connect(this, &QLineEdit::textChanged, this, &VisualShaderNodeVoronoiNoiseAngleOffsetEmbedWidget::on_text_changed); +} + +VisualShaderNodeVoronoiNoiseAngleOffsetEmbedWidget::~VisualShaderNodeVoronoiNoiseAngleOffsetEmbedWidget() {} + +void VisualShaderNodeVoronoiNoiseAngleOffsetEmbedWidget::on_text_changed(const QString& text) { + if (text.isEmpty()) { + node->set_angle_offset(10.0f); + } else { + bool ok; + text.toFloat(&ok); + if (ok) { + node->set_angle_offset(text.toFloat()); + } + } +} + +VisualShaderNodeVoronoiNoiseCellDensityEmbedWidget::VisualShaderNodeVoronoiNoiseCellDensityEmbedWidget(const std::shared_ptr& node) : QLineEdit(), + node(node) { + setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom + + setPlaceholderText("Cell Density"); + + QObject::connect(this, &QLineEdit::textChanged, this, &VisualShaderNodeVoronoiNoiseCellDensityEmbedWidget::on_text_changed); +} + +VisualShaderNodeVoronoiNoiseCellDensityEmbedWidget::~VisualShaderNodeVoronoiNoiseCellDensityEmbedWidget() {} + +void VisualShaderNodeVoronoiNoiseCellDensityEmbedWidget::on_text_changed(const QString& text) { + if (text.isEmpty()) { + node->set_cell_density(10.0f); + } else { + bool ok; + text.toFloat(&ok); + if (ok) { + node->set_cell_density(text.toFloat()); + } + } +} diff --git a/Editors/VisualShaderEditor.h b/Editors/VisualShaderEditor.h index 13cfc7152..c7131b5ae 100644 --- a/Editors/VisualShaderEditor.h +++ b/Editors/VisualShaderEditor.h @@ -1090,4 +1090,72 @@ class VisualShaderNodeVectorFuncEmbedWidget : public QComboBox { std::shared_ptr node; }; +/*************************************/ +/* Value Noise Node */ +/*************************************/ + +class VisualShaderNodeValueNoiseEmbedWidget : public QLineEdit { + Q_OBJECT + + public: + VisualShaderNodeValueNoiseEmbedWidget(const std::shared_ptr& node); + ~VisualShaderNodeValueNoiseEmbedWidget(); + + private Q_SLOTS: + void on_text_changed(const QString& text); + + private: + std::shared_ptr node; +}; + +/*************************************/ +/* Perlin Noise Node */ +/*************************************/ + +class VisualShaderNodePerlinNoiseEmbedWidget : public QLineEdit { + Q_OBJECT + + public: + VisualShaderNodePerlinNoiseEmbedWidget(const std::shared_ptr& node); + ~VisualShaderNodePerlinNoiseEmbedWidget(); + + private Q_SLOTS: + void on_text_changed(const QString& text); + + private: + std::shared_ptr node; +}; + +/*************************************/ +/* Voronoi Noise Node */ +/*************************************/ + +class VisualShaderNodeVoronoiNoiseAngleOffsetEmbedWidget : public QLineEdit { + Q_OBJECT + + public: + VisualShaderNodeVoronoiNoiseAngleOffsetEmbedWidget(const std::shared_ptr& node); + ~VisualShaderNodeVoronoiNoiseAngleOffsetEmbedWidget(); + + private Q_SLOTS: + void on_text_changed(const QString& text); + + private: + std::shared_ptr node; +}; + +class VisualShaderNodeVoronoiNoiseCellDensityEmbedWidget : public QLineEdit { + Q_OBJECT + + public: + VisualShaderNodeVoronoiNoiseCellDensityEmbedWidget(const std::shared_ptr& node); + ~VisualShaderNodeVoronoiNoiseCellDensityEmbedWidget(); + + private Q_SLOTS: + void on_text_changed(const QString& text); + + private: + std::shared_ptr node; +}; + #endif // ENIGMA_VISUAL_SHADER_EDITOR_H From 7831a1a49fc521fff0a51a1c37a611b6be35b674 Mon Sep 17 00:00:00 2001 From: Saif Kandil <74428638+k0T0z@users.noreply.github.com> Date: Mon, 30 Sep 2024 08:29:45 +0300 Subject: [PATCH 43/56] Added support for more control on parameters --- Editors/VisualShaderEditor.cpp | 506 +++++++++++++++++++++++++++++++++ Editors/VisualShaderEditor.h | 182 ++++++++++++ 2 files changed, 688 insertions(+) diff --git a/Editors/VisualShaderEditor.cpp b/Editors/VisualShaderEditor.cpp index 795ea0a39..1245b37bb 100644 --- a/Editors/VisualShaderEditor.cpp +++ b/Editors/VisualShaderEditor.cpp @@ -2659,6 +2659,86 @@ VisualShaderNodeEmbedWidget::VisualShaderNodeEmbedWidget(const std::shared_ptr(node)}) { + VisualShaderNodeColorConstantEmbedWidget* embed_widget = new VisualShaderNodeColorConstantEmbedWidget(p); + layout->addWidget(embed_widget); + QObject::connect(embed_widget, + &VisualShaderNodeColorConstantEmbedWidget::color_changed, + this, + &VisualShaderNodeEmbedWidget::on_shader_preview_update_requested); + } else if (auto p {std::dynamic_pointer_cast(node)}) { + VisualShaderNodeBooleanConstantEmbedWidget* embed_widget = new VisualShaderNodeBooleanConstantEmbedWidget(p); + layout->addWidget(embed_widget); + QObject::connect(embed_widget, + &QCheckBox::stateChanged, + this, + &VisualShaderNodeEmbedWidget::on_shader_preview_update_requested); + } else if (auto p {std::dynamic_pointer_cast(node)}) { + VisualShaderNodeFloatConstantEmbedWidget* embed_widget = new VisualShaderNodeFloatConstantEmbedWidget(p); + layout->addWidget(embed_widget); + QObject::connect(embed_widget, + &QLineEdit::textChanged, + this, + &VisualShaderNodeEmbedWidget::on_shader_preview_update_requested); + } else if (auto p {std::dynamic_pointer_cast(node)}) { + VisualShaderNodeIntConstantEmbedWidget* embed_widget = new VisualShaderNodeIntConstantEmbedWidget(p); + layout->addWidget(embed_widget); + QObject::connect(embed_widget, + &QLineEdit::textChanged, + this, + &VisualShaderNodeEmbedWidget::on_shader_preview_update_requested); + } else if (auto p {std::dynamic_pointer_cast(node)}) { + VisualShaderNodeUIntConstantEmbedWidget* embed_widget = new VisualShaderNodeUIntConstantEmbedWidget(p); + layout->addWidget(embed_widget); + QObject::connect(embed_widget, + &QLineEdit::textChanged, + this, + &VisualShaderNodeEmbedWidget::on_shader_preview_update_requested); + } else if (auto p {std::dynamic_pointer_cast(node)}) { + VisualShaderNodeVec2ConstantEmbedWidget* embed_widget = new VisualShaderNodeVec2ConstantEmbedWidget(p); + layout->addLayout(embed_widget); + QObject::connect(embed_widget->get_x_edit_widget(), + &QLineEdit::textChanged, + this, + &VisualShaderNodeEmbedWidget::on_shader_preview_update_requested); + QObject::connect(embed_widget->get_y_edit_widget(), + &QLineEdit::textChanged, + this, + &VisualShaderNodeEmbedWidget::on_shader_preview_update_requested); + } else if (auto p {std::dynamic_pointer_cast(node)}) { + VisualShaderNodeVec3ConstantEmbedWidget* embed_widget = new VisualShaderNodeVec3ConstantEmbedWidget(p); + layout->addLayout(embed_widget); + QObject::connect(embed_widget->get_x_edit_widget(), + &QLineEdit::textChanged, + this, + &VisualShaderNodeEmbedWidget::on_shader_preview_update_requested); + QObject::connect(embed_widget->get_y_edit_widget(), + &QLineEdit::textChanged, + this, + &VisualShaderNodeEmbedWidget::on_shader_preview_update_requested); + QObject::connect(embed_widget->get_z_edit_widget(), + &QLineEdit::textChanged, + this, + &VisualShaderNodeEmbedWidget::on_shader_preview_update_requested); + } else if (auto p {std::dynamic_pointer_cast(node)}) { + VisualShaderNodeVec4ConstantEmbedWidget* embed_widget = new VisualShaderNodeVec4ConstantEmbedWidget(p); + layout->addLayout(embed_widget); + QObject::connect(embed_widget->get_x_edit_widget(), + &QLineEdit::textChanged, + this, + &VisualShaderNodeEmbedWidget::on_shader_preview_update_requested); + QObject::connect(embed_widget->get_y_edit_widget(), + &QLineEdit::textChanged, + this, + &VisualShaderNodeEmbedWidget::on_shader_preview_update_requested); + QObject::connect(embed_widget->get_z_edit_widget(), + &QLineEdit::textChanged, + this, + &VisualShaderNodeEmbedWidget::on_shader_preview_update_requested); + QObject::connect(embed_widget->get_w_edit_widget(), + &QLineEdit::textChanged, + this, + &VisualShaderNodeEmbedWidget::on_shader_preview_update_requested); } // Create the button that will show/hide the shader previewer @@ -3002,6 +3082,420 @@ void VisualShaderNodeVectorFuncEmbedWidget::on_current_index_changed(const int& node->set_function((VisualShaderNodeVectorFunc::Function)itemData(index).toInt()); } +/*************************************/ +/* Color Constant Node */ +/*************************************/ + +VisualShaderNodeColorConstantEmbedWidget::VisualShaderNodeColorConstantEmbedWidget(const std::shared_ptr& node) : QPushButton(), + node(node) { + setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom + + QObject::connect(this, + &QPushButton::pressed, + this, + &VisualShaderNodeColorConstantEmbedWidget::on_pressed); +} + +VisualShaderNodeColorConstantEmbedWidget::~VisualShaderNodeColorConstantEmbedWidget() {} + +void VisualShaderNodeColorConstantEmbedWidget::on_pressed() { + TColor c{node->get_constant()}; + QColor color {QColorDialog::getColor(QColor(c.r, c.g, c.b, c.a), this, "Select Color")}; + + // If a valid color is picked, update the button and store the color + if (color.isValid()) { + node->set_constant({(float)color.red(), (float)color.green(), (float)color.blue(), (float)color.alpha()}); + QPalette palette {this->palette()}; + palette.setColor(QPalette::Button, color); + this->setPalette(palette); + this->update(); + Q_EMIT color_changed(); + } else { + // If the user cancels the color dialog, reset the button to the previous color + QColor previous_color {QColor(c.r, c.g, c.b, c.a)}; + QPalette palette {this->palette()}; + palette.setColor(QPalette::Button, previous_color); + this->setPalette(palette); + this->update(); + } +} + +/*************************************/ +/* Boolean Constant Node */ +/*************************************/ + +VisualShaderNodeBooleanConstantEmbedWidget::VisualShaderNodeBooleanConstantEmbedWidget(const std::shared_ptr& node) : QCheckBox(), + node(node) { + setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom + + QObject::connect(this, + &QCheckBox::stateChanged, + this, + &VisualShaderNodeBooleanConstantEmbedWidget::on_state_changed); +} + +VisualShaderNodeBooleanConstantEmbedWidget::~VisualShaderNodeBooleanConstantEmbedWidget() {} + +void VisualShaderNodeBooleanConstantEmbedWidget::on_state_changed(const int& state) { + node->set_constant(state == Qt::Checked); +} + +/*************************************/ +/* Float Constant */ +/*************************************/ + +VisualShaderNodeFloatConstantEmbedWidget::VisualShaderNodeFloatConstantEmbedWidget(const std::shared_ptr& node) : QLineEdit(), + node(node) { + setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom + + setPlaceholderText("Value"); + + QObject::connect(this, + &QLineEdit::textChanged, + this, + &VisualShaderNodeFloatConstantEmbedWidget::on_text_changed); +} + +VisualShaderNodeFloatConstantEmbedWidget::~VisualShaderNodeFloatConstantEmbedWidget() {} + +void VisualShaderNodeFloatConstantEmbedWidget::on_text_changed(const QString& text) { + if (text.isEmpty()) { + node->set_constant(0.0f); + } else { + bool ok; + text.toFloat(&ok); + if (ok) { + node->set_constant(text.toFloat()); + } else { + node->set_constant(0.0f); + setText(""); + } + } +} + +/*************************************/ +/* Int Constant */ +/*************************************/ + +VisualShaderNodeIntConstantEmbedWidget::VisualShaderNodeIntConstantEmbedWidget(const std::shared_ptr& node) : QLineEdit(), + node(node) { + setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom + + setPlaceholderText("Value"); + + QObject::connect(this, + &QLineEdit::textChanged, + this, + &VisualShaderNodeIntConstantEmbedWidget::on_text_changed); +} + +VisualShaderNodeIntConstantEmbedWidget::~VisualShaderNodeIntConstantEmbedWidget() {} + +void VisualShaderNodeIntConstantEmbedWidget::on_text_changed(const QString& text) { + if (text.isEmpty()) { + node->set_constant(0); + } else { + bool ok; + text.toInt(&ok); + if (ok) { + node->set_constant(text.toInt()); + } else { + node->set_constant(0); + setText(""); + } + } +} + +/*************************************/ +/* UInt Constant */ +/*************************************/ + +VisualShaderNodeUIntConstantEmbedWidget::VisualShaderNodeUIntConstantEmbedWidget(const std::shared_ptr& node) : QLineEdit(), + node(node) { + setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom + + setPlaceholderText("Value"); + + QObject::connect(this, + &QLineEdit::textChanged, + this, + &VisualShaderNodeUIntConstantEmbedWidget::on_text_changed); +} + +VisualShaderNodeUIntConstantEmbedWidget::~VisualShaderNodeUIntConstantEmbedWidget() {} + +void VisualShaderNodeUIntConstantEmbedWidget::on_text_changed(const QString& text) { + if (text.isEmpty()) { + node->set_constant(0); + } else { + bool ok; + text.toUInt(&ok); + if (ok) { + node->set_constant(text.toUInt()); + } else { + node->set_constant(0); + setText(""); + } + } +} + +/*************************************/ +/* Vec2 Constant Node */ +/*************************************/ + +VisualShaderNodeVec2ConstantEmbedWidget::VisualShaderNodeVec2ConstantEmbedWidget(const std::shared_ptr& node) : QVBoxLayout(), + node(node) { + setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom + setSizeConstraint(QLayout::SetNoConstraint); + setSpacing(2); + setAlignment(Qt::AlignVCenter | Qt::AlignHCenter); + + x_edit_widget = new QLineEdit(); + y_edit_widget = new QLineEdit(); + + x_edit_widget->setPlaceholderText("X"); + y_edit_widget->setPlaceholderText("Y"); + + QObject::connect(x_edit_widget, + &QLineEdit::textChanged, + this, + &VisualShaderNodeVec2ConstantEmbedWidget::on_x_text_changed); + QObject::connect(y_edit_widget, + &QLineEdit::textChanged, + this, + &VisualShaderNodeVec2ConstantEmbedWidget::on_y_text_changed); + + addWidget(x_edit_widget); + addWidget(y_edit_widget); +} + +VisualShaderNodeVec2ConstantEmbedWidget::~VisualShaderNodeVec2ConstantEmbedWidget() {} + +void VisualShaderNodeVec2ConstantEmbedWidget::on_x_text_changed(const QString& text) { + if (text.isEmpty()) { + node->set_constant({0.0f, node->get_constant().y}); + } else { + bool ok; + text.toFloat(&ok); + if (ok) { + node->set_constant({text.toFloat(), node->get_constant().y}); + } else { + node->set_constant({0.0f, node->get_constant().y}); + x_edit_widget->setText(""); + } + } +} + +void VisualShaderNodeVec2ConstantEmbedWidget::on_y_text_changed(const QString& text) { + if (text.isEmpty()) { + node->set_constant({node->get_constant().x, 0.0f}); + } else { + bool ok; + text.toFloat(&ok); + if (ok) { + node->set_constant({node->get_constant().x, text.toFloat()}); + } else { + node->set_constant({node->get_constant().x, 0.0f}); + y_edit_widget->setText(""); + } + } +} + +/*************************************/ +/* Vec3 Constant Node */ +/*************************************/ + +VisualShaderNodeVec3ConstantEmbedWidget::VisualShaderNodeVec3ConstantEmbedWidget(const std::shared_ptr& node) : QVBoxLayout(), + node(node) { + setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom + setSizeConstraint(QLayout::SetNoConstraint); + setSpacing(2); + setAlignment(Qt::AlignVCenter | Qt::AlignHCenter); + + x_edit_widget = new QLineEdit(); + y_edit_widget = new QLineEdit(); + z_edit_widget = new QLineEdit(); + + x_edit_widget->setPlaceholderText("X"); + y_edit_widget->setPlaceholderText("Y"); + z_edit_widget->setPlaceholderText("Z"); + + QObject::connect(x_edit_widget, + &QLineEdit::textChanged, + this, + &VisualShaderNodeVec3ConstantEmbedWidget::on_x_text_changed); + QObject::connect(y_edit_widget, + &QLineEdit::textChanged, + this, + &VisualShaderNodeVec3ConstantEmbedWidget::on_y_text_changed); + QObject::connect(z_edit_widget, + &QLineEdit::textChanged, + this, + &VisualShaderNodeVec3ConstantEmbedWidget::on_z_text_changed); + + addWidget(x_edit_widget); + addWidget(y_edit_widget); + addWidget(z_edit_widget); +} + +VisualShaderNodeVec3ConstantEmbedWidget::~VisualShaderNodeVec3ConstantEmbedWidget() {} + +void VisualShaderNodeVec3ConstantEmbedWidget::on_x_text_changed(const QString& text) { + if (text.isEmpty()) { + node->set_constant({0.0f, node->get_constant().y, node->get_constant().z}); + } else { + bool ok; + text.toFloat(&ok); + if (ok) { + node->set_constant({text.toFloat(), node->get_constant().y, node->get_constant().z}); + } else { + node->set_constant({0.0f, node->get_constant().y, node->get_constant().z}); + x_edit_widget->setText(""); + } + } +} + +void VisualShaderNodeVec3ConstantEmbedWidget::on_y_text_changed(const QString& text) { + if (text.isEmpty()) { + node->set_constant({node->get_constant().x, 0.0f, node->get_constant().z}); + } else { + bool ok; + text.toFloat(&ok); + if (ok) { + node->set_constant({node->get_constant().x, text.toFloat(), node->get_constant().z}); + } else { + node->set_constant({node->get_constant().x, 0.0f, node->get_constant().z}); + y_edit_widget->setText(""); + } + } +} + +void VisualShaderNodeVec3ConstantEmbedWidget::on_z_text_changed(const QString& text) { + if (text.isEmpty()) { + node->set_constant({node->get_constant().x, node->get_constant().y, 0.0f}); + } else { + bool ok; + text.toFloat(&ok); + if (ok) { + node->set_constant({node->get_constant().x, node->get_constant().y, text.toFloat()}); + } else { + node->set_constant({node->get_constant().x, node->get_constant().y, 0.0f}); + z_edit_widget->setText(""); + } + } +} + +/*************************************/ +/* Vec4 Constant Node */ +/*************************************/ + +VisualShaderNodeVec4ConstantEmbedWidget::VisualShaderNodeVec4ConstantEmbedWidget(const std::shared_ptr& node) : QVBoxLayout(), + node(node) { + setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom + setSizeConstraint(QLayout::SetNoConstraint); + setSpacing(2); + setAlignment(Qt::AlignVCenter | Qt::AlignHCenter); + + x_edit_widget = new QLineEdit(); + y_edit_widget = new QLineEdit(); + z_edit_widget = new QLineEdit(); + w_edit_widget = new QLineEdit(); + + x_edit_widget->setPlaceholderText("X"); + y_edit_widget->setPlaceholderText("Y"); + z_edit_widget->setPlaceholderText("Z"); + w_edit_widget->setPlaceholderText("W"); + + QObject::connect(x_edit_widget, + &QLineEdit::textChanged, + this, + &VisualShaderNodeVec4ConstantEmbedWidget::on_x_text_changed); + QObject::connect(y_edit_widget, + &QLineEdit::textChanged, + this, + &VisualShaderNodeVec4ConstantEmbedWidget::on_y_text_changed); + QObject::connect(z_edit_widget, + &QLineEdit::textChanged, + this, + &VisualShaderNodeVec4ConstantEmbedWidget::on_z_text_changed); + QObject::connect(w_edit_widget, + &QLineEdit::textChanged, + this, + &VisualShaderNodeVec4ConstantEmbedWidget::on_w_text_changed); + + addWidget(x_edit_widget); + addWidget(y_edit_widget); + addWidget(z_edit_widget); + addWidget(w_edit_widget); +} + +VisualShaderNodeVec4ConstantEmbedWidget::~VisualShaderNodeVec4ConstantEmbedWidget() {} + +void VisualShaderNodeVec4ConstantEmbedWidget::on_x_text_changed(const QString& text) { + if (text.isEmpty()) { + node->set_constant({0.0f, node->get_constant().y, node->get_constant().z, node->get_constant().w}); + } else { + bool ok; + text.toFloat(&ok); + if (ok) { + node->set_constant({text.toFloat(), node->get_constant().y, node->get_constant().z, node->get_constant().w}); + } else { + node->set_constant({0.0f, node->get_constant().y, node->get_constant().z, node->get_constant().w}); + x_edit_widget->setText(""); + } + } +} + +void VisualShaderNodeVec4ConstantEmbedWidget::on_y_text_changed(const QString& text) { + if (text.isEmpty()) { + node->set_constant({node->get_constant().x, 0.0f, node->get_constant().z, node->get_constant().w}); + } else { + bool ok; + text.toFloat(&ok); + if (ok) { + node->set_constant({node->get_constant().x, text.toFloat(), node->get_constant().z, node->get_constant().w}); + } else { + node->set_constant({node->get_constant().x, 0.0f, node->get_constant().z, node->get_constant().w}); + y_edit_widget->setText(""); + } + } +} + +void VisualShaderNodeVec4ConstantEmbedWidget::on_z_text_changed(const QString& text) { + if (text.isEmpty()) { + node->set_constant({node->get_constant().x, node->get_constant().y, 0.0f, node->get_constant().w}); + } else { + bool ok; + text.toFloat(&ok); + if (ok) { + node->set_constant({node->get_constant().x, node->get_constant().y, text.toFloat(), node->get_constant().w}); + } else { + node->set_constant({node->get_constant().x, node->get_constant().y, 0.0f, node->get_constant().w}); + z_edit_widget->setText(""); + } + } +} + +void VisualShaderNodeVec4ConstantEmbedWidget::on_w_text_changed(const QString& text) { + if (text.isEmpty()) { + node->set_constant({node->get_constant().x, node->get_constant().y, node->get_constant().z, 0.0f}); + } else { + bool ok; + text.toFloat(&ok); + if (ok) { + node->set_constant({node->get_constant().x, node->get_constant().y, node->get_constant().z, text.toFloat()}); + } else { + node->set_constant({node->get_constant().x, node->get_constant().y, node->get_constant().z, 0.0f}); + w_edit_widget->setText(""); + } + } +} + /*************************************/ /* Value Noise Node */ /*************************************/ @@ -3026,6 +3520,9 @@ void VisualShaderNodeValueNoiseEmbedWidget::on_text_changed(const QString& text) text.toFloat(&ok); if (ok) { node->set_scale(text.toFloat()); + } else { + node->set_scale(0.0f); + setText(""); } } } @@ -3054,6 +3551,9 @@ void VisualShaderNodePerlinNoiseEmbedWidget::on_text_changed(const QString& text text.toFloat(&ok); if (ok) { node->set_scale(text.toFloat()); + } else { + node->set_scale(0.0f); + setText(""); } } } @@ -3082,6 +3582,9 @@ void VisualShaderNodeVoronoiNoiseAngleOffsetEmbedWidget::on_text_changed(const Q text.toFloat(&ok); if (ok) { node->set_angle_offset(text.toFloat()); + } else { + node->set_angle_offset(0.0f); + setText(""); } } } @@ -3106,6 +3609,9 @@ void VisualShaderNodeVoronoiNoiseCellDensityEmbedWidget::on_text_changed(const Q text.toFloat(&ok); if (ok) { node->set_cell_density(text.toFloat()); + } else { + node->set_cell_density(0.0f); + setText(""); } } } diff --git a/Editors/VisualShaderEditor.h b/Editors/VisualShaderEditor.h index c7131b5ae..a76c2025e 100644 --- a/Editors/VisualShaderEditor.h +++ b/Editors/VisualShaderEditor.h @@ -52,6 +52,8 @@ #include // https://stackoverflow.com/a/64288966/14629018 explains why we need this. #include #include +#include +#include #include #include @@ -1090,6 +1092,186 @@ class VisualShaderNodeVectorFuncEmbedWidget : public QComboBox { std::shared_ptr node; }; +/*************************************/ +/* Color Constant Node */ +/*************************************/ + +class VisualShaderNodeColorConstantEmbedWidget : public QPushButton { + Q_OBJECT + + public: + VisualShaderNodeColorConstantEmbedWidget(const std::shared_ptr& node); + ~VisualShaderNodeColorConstantEmbedWidget(); + + Q_SIGNALS: + void color_changed(); + + private Q_SLOTS: + void on_pressed(); + + private: + std::shared_ptr node; +}; + +/*************************************/ +/* Boolean Constant Node */ +/*************************************/ + +class VisualShaderNodeBooleanConstantEmbedWidget : public QCheckBox { + Q_OBJECT + + public: + VisualShaderNodeBooleanConstantEmbedWidget(const std::shared_ptr& node); + ~VisualShaderNodeBooleanConstantEmbedWidget(); + + private Q_SLOTS: + void on_state_changed(const int& state); + + private: + std::shared_ptr node; +}; + +/*************************************/ +/* Float Constant */ +/*************************************/ + +class VisualShaderNodeFloatConstantEmbedWidget : public QLineEdit { + Q_OBJECT + + public: + VisualShaderNodeFloatConstantEmbedWidget(const std::shared_ptr& node); + ~VisualShaderNodeFloatConstantEmbedWidget(); + + private Q_SLOTS: + void on_text_changed(const QString& text); + + private: + std::shared_ptr node; +}; + +/*************************************/ +/* Int Constant */ +/*************************************/ + +class VisualShaderNodeIntConstantEmbedWidget : public QLineEdit { + Q_OBJECT + + public: + VisualShaderNodeIntConstantEmbedWidget(const std::shared_ptr& node); + ~VisualShaderNodeIntConstantEmbedWidget(); + + private Q_SLOTS: + void on_text_changed(const QString& text); + + private: + std::shared_ptr node; +}; + +/*************************************/ +/* UInt Constant */ +/*************************************/ + +class VisualShaderNodeUIntConstantEmbedWidget : public QLineEdit { + Q_OBJECT + + public: + VisualShaderNodeUIntConstantEmbedWidget(const std::shared_ptr& node); + ~VisualShaderNodeUIntConstantEmbedWidget(); + + private Q_SLOTS: + void on_text_changed(const QString& text); + + private: + std::shared_ptr node; +}; + +/*************************************/ +/* Vec2 Constant Node */ +/*************************************/ + +class VisualShaderNodeVec2ConstantEmbedWidget : public QVBoxLayout { + Q_OBJECT + + public: + VisualShaderNodeVec2ConstantEmbedWidget(const std::shared_ptr& node); + ~VisualShaderNodeVec2ConstantEmbedWidget(); + + QLineEdit* get_x_edit_widget() const { return x_edit_widget; } + QLineEdit* get_y_edit_widget() const { return y_edit_widget; } + + private Q_SLOTS: + + void on_x_text_changed(const QString& text); + void on_y_text_changed(const QString& text); + + private: + std::shared_ptr node; + + QLineEdit* x_edit_widget; + QLineEdit* y_edit_widget; +}; + +/*************************************/ +/* Vec3 Constant Node */ +/*************************************/ + +class VisualShaderNodeVec3ConstantEmbedWidget : public QVBoxLayout { + Q_OBJECT + + public: + VisualShaderNodeVec3ConstantEmbedWidget(const std::shared_ptr& node); + ~VisualShaderNodeVec3ConstantEmbedWidget(); + + QLineEdit* get_x_edit_widget() const { return x_edit_widget; } + QLineEdit* get_y_edit_widget() const { return y_edit_widget; } + QLineEdit* get_z_edit_widget() const { return z_edit_widget; } + + private Q_SLOTS: + + void on_x_text_changed(const QString& text); + void on_y_text_changed(const QString& text); + void on_z_text_changed(const QString& text); + + private: + std::shared_ptr node; + + QLineEdit* x_edit_widget; + QLineEdit* y_edit_widget; + QLineEdit* z_edit_widget; +}; + +/*************************************/ +/* Vec4 Constant Node */ +/*************************************/ + +class VisualShaderNodeVec4ConstantEmbedWidget : public QVBoxLayout { + Q_OBJECT + + public: + VisualShaderNodeVec4ConstantEmbedWidget(const std::shared_ptr& node); + ~VisualShaderNodeVec4ConstantEmbedWidget(); + + QLineEdit* get_x_edit_widget() const { return x_edit_widget; } + QLineEdit* get_y_edit_widget() const { return y_edit_widget; } + QLineEdit* get_z_edit_widget() const { return z_edit_widget; } + QLineEdit* get_w_edit_widget() const { return w_edit_widget; } + + private Q_SLOTS: + + void on_x_text_changed(const QString& text); + void on_y_text_changed(const QString& text); + void on_z_text_changed(const QString& text); + void on_w_text_changed(const QString& text); + + private: + std::shared_ptr node; + + QLineEdit* x_edit_widget; + QLineEdit* y_edit_widget; + QLineEdit* z_edit_widget; + QLineEdit* w_edit_widget; +}; + /*************************************/ /* Value Noise Node */ /*************************************/ From 6e135b2b84d509b20cd3b7420f0512e363276a57 Mon Sep 17 00:00:00 2001 From: Saif Kandil <74428638+k0T0z@users.noreply.github.com> Date: Mon, 30 Sep 2024 09:24:47 +0300 Subject: [PATCH 44/56] Added support for multiple connection at output ports --- Editors/VisualShaderEditor.cpp | 97 +++++++++++++++------------------- Editors/VisualShaderEditor.h | 31 ++++++----- 2 files changed, 61 insertions(+), 67 deletions(-) diff --git a/Editors/VisualShaderEditor.cpp b/Editors/VisualShaderEditor.cpp index 1245b37bb..17e9588b6 100644 --- a/Editors/VisualShaderEditor.cpp +++ b/Editors/VisualShaderEditor.cpp @@ -731,7 +731,10 @@ void ShaderPreviewerWidget::paintGL() { update_shader_program(); } - if (!shader_program) return; + if (!shader_program || !shader_program->isLinked()) { + qWarning() << "Shader program is not linked."; + return; + } float time_value {timer.elapsed() * 0.001f}; @@ -774,11 +777,11 @@ void ShaderPreviewerWidget::init_buffers() { } float vertices[] = { - // positions // texCoords - -1.0f, 1.0f, 0.0f, 1.0f, - -1.0f, -1.0f, 0.0f, 0.0f, - 1.0f, 1.0f, 1.0f, 1.0f, - 1.0f, -1.0f, 1.0f, 0.0f + // coordinates // texCoords + -1.0f, 1.0f, 0.0f, 1.0f, + -1.0f, -1.0f, 0.0f, 0.0f, + 1.0f, 1.0f, 1.0f, 1.0f, + 1.0f, -1.0f, 1.0f, 0.0f }; f->glGenVertexArrays(1, &VAO); @@ -1066,18 +1069,19 @@ bool VisualShaderGraphicsScene::delete_node(const int& n_id) { continue; } - // Get the input port of the connection - VisualShaderConnectionGraphicsObject* c_o{o_port->get_connection_graphics_object()}; + std::vector c_os{o_port->get_connection_graphics_objects()}; - if (!c_o) { - continue; - } + for (VisualShaderConnectionGraphicsObject* c_o : c_os) { + if (!c_o) { + continue; + } - bool result{this->delete_connection(n_id, i, c_o->get_to_node_id(), c_o->get_to_port_index())}; + bool result{this->delete_connection(n_id, i, c_o->get_to_node_id(), c_o->get_to_port_index())}; - if (!result) { - std::cout << "Failed to delete connection" << std::endl; - continue; + if (!result) { + std::cout << "Failed to delete connection" << std::endl; + continue; + } } } @@ -1212,7 +1216,7 @@ bool VisualShaderGraphicsScene::delete_connection(const int& from_node_id, const } if (this->temporary_connection_graphics_object) { - from_o_port->detach_connection(); + from_o_port->detach_connection(this->temporary_connection_graphics_object); removeItem(this->temporary_connection_graphics_object); delete this->temporary_connection_graphics_object; this->temporary_connection_graphics_object = nullptr; @@ -1221,7 +1225,7 @@ bool VisualShaderGraphicsScene::delete_connection(const int& from_node_id, const // If we have a complete connection, then we can disconnect the nodes if (to_node_id != (int)VisualShader::NODE_ID_INVALID && to_port_index != (int)VisualShader::PORT_INDEX_INVALID) { - VisualShaderConnectionGraphicsObject* c_o{from_o_port->get_connection_graphics_object()}; + VisualShaderConnectionGraphicsObject* c_o{from_o_port->get_connection_graphics_object(to_node_id, to_port_index)}; if (!c_o) { return false; @@ -1246,7 +1250,7 @@ bool VisualShaderGraphicsScene::delete_connection(const int& from_node_id, const } to_i_port->detach_connection(); - from_o_port->detach_connection(); + from_o_port->detach_connection(c_o); removeItem(c_o); delete c_o; @@ -1305,13 +1309,15 @@ void VisualShaderGraphicsScene::on_node_moved(const int& n_id, const QPointF& ne continue; } - VisualShaderConnectionGraphicsObject* c_o{o_port->get_connection_graphics_object()}; + std::vector c_os{o_port->get_connection_graphics_objects()}; - if (!c_o) { - continue; - } + for (VisualShaderConnectionGraphicsObject* c_o : c_os) { + if (!c_o) { + continue; + } - c_o->set_start_coordinate(o_port->get_global_coordinate()); + c_o->set_start_coordinate(o_port->get_global_coordinate()); + } } } @@ -1362,41 +1368,15 @@ void VisualShaderGraphicsScene::on_port_dragged(QGraphicsObject* port, const QPo return; } - if (!o_port->is_connected() && !temporary_connection_graphics_object) { + if (o_port->is_connected() && temporary_connection_graphics_object) { + c_o = temporary_connection_graphics_object; + } else if (!temporary_connection_graphics_object) { bool result{this->add_connection(o_port->get_node_id(), o_port->get_port_index())}; if (!result) { std::cout << "Failed to add connection" << std::endl; return; } c_o = temporary_connection_graphics_object; - } else if (o_port->is_connected() && temporary_connection_graphics_object) { - c_o = temporary_connection_graphics_object; - } else if (o_port->is_connected() && !temporary_connection_graphics_object) { - c_o = o_port->get_connection_graphics_object(); - temporary_connection_graphics_object = c_o; // Store the connection object for access in the next drag call - - // Detach the connection from the input port - VisualShaderNodeGraphicsObject* n_o{this->get_node_graphics_object(c_o->get_to_node_id())}; - if (!n_o) { - return; - } - VisualShaderInputPortGraphicsObject* i_port{n_o->get_input_port_graphics_object(c_o->get_to_port_index())}; - if (!i_port) { - return; - } - bool result{vs->disconnect_nodes(c_o->get_from_node_id(), c_o->get_from_port_index(), c_o->get_to_node_id(), - c_o->get_to_port_index())}; - if (!result) { - std::cout << "Failed to disconnect nodes" << std::endl; - } - i_port->detach_connection(); - c_o->detach_end(); - - on_update_shader_previewer_widgets_requested(); - - // Here we must request a repaint as this connection will be drawn some where else. - // I know this doesn't make any sense, just comment it and try to dray an output port that is already connected. - update(); // Request a repaint } else { return; } @@ -2164,7 +2144,7 @@ void VisualShaderNodeGraphicsObject::paint(QPainter* painter, const QStyleOption } { - // Correct the position of the embed widget + // Correct the coordinate of the embed widget if (embed_widget) { float embed_widget_x{rect_x + rect_w * 0.5f - embed_widget->width() * 0.5f}; float embed_widget_y{rect_y + caption_rect_height + body_rect_header_height + embed_widget_v_padding}; @@ -2239,7 +2219,7 @@ void VisualShaderInputPortGraphicsObject::mouseReleaseEvent(QGraphicsSceneMouseE VisualShaderOutputPortGraphicsObject::VisualShaderOutputPortGraphicsObject(const QRectF& rect, const int& n_id, const int& p_index, QGraphicsItem* parent) - : QGraphicsObject(parent), rect(rect), n_id(n_id), p_index(p_index), connection_graphics_object(nullptr) { + : QGraphicsObject(parent), rect(rect), n_id(n_id), p_index(p_index) { setFlag(QGraphicsItem::ItemDoesntPropagateOpacityToChildren, true); setFlag(QGraphicsItem::ItemIsFocusable, true); setFlag(QGraphicsItem::ItemIsSelectable, true); @@ -2256,6 +2236,15 @@ VisualShaderOutputPortGraphicsObject::VisualShaderOutputPortGraphicsObject(const VisualShaderOutputPortGraphicsObject::~VisualShaderOutputPortGraphicsObject() {} +VisualShaderConnectionGraphicsObject* VisualShaderOutputPortGraphicsObject::get_connection_graphics_object(const int& to_node_id, const int& to_port_index) const { + for (auto c_g_o : connection_graphics_objects) { + if (c_g_o->get_to_node_id() == to_node_id && c_g_o->get_to_port_index() == to_port_index) { + return c_g_o; + } + } + return nullptr; +} + QRectF VisualShaderOutputPortGraphicsObject::boundingRect() const { QRectF t_rect{rect}; t_rect.adjust(-padding, -padding, padding, padding); diff --git a/Editors/VisualShaderEditor.h b/Editors/VisualShaderEditor.h index a76c2025e..168cd1652 100644 --- a/Editors/VisualShaderEditor.h +++ b/Editors/VisualShaderEditor.h @@ -715,11 +715,11 @@ class VisualShaderInputPortGraphicsObject : public QGraphicsObject { * @c VisualShaderGraphicsScene::on_port_* slots. * * @param port - * @param pos + * @param coordinate */ - void port_pressed(VisualShaderInputPortGraphicsObject* port, const QPointF& pos); - void port_dragged(VisualShaderInputPortGraphicsObject* port, const QPointF& pos); - void port_dropped(VisualShaderInputPortGraphicsObject* port, const QPointF& pos); + void port_pressed(VisualShaderInputPortGraphicsObject* port, const QPointF& coordinate); + void port_dragged(VisualShaderInputPortGraphicsObject* port, const QPointF& coordinate); + void port_dropped(VisualShaderInputPortGraphicsObject* port, const QPointF& coordinate); private: int n_id; @@ -756,10 +756,14 @@ class VisualShaderOutputPortGraphicsObject : public QGraphicsObject { int get_node_id() const { return n_id; } int get_port_index() const { return p_index; } - VisualShaderConnectionGraphicsObject* get_connection_graphics_object() const { return connection_graphics_object; } - void connect(VisualShaderConnectionGraphicsObject* c_o) const { this->connection_graphics_object = c_o; } - void detach_connection() const { this->connection_graphics_object = nullptr; } - bool is_connected() const { return connection_graphics_object != nullptr; } + std::vector get_connection_graphics_objects() const { return connection_graphics_objects; } + VisualShaderConnectionGraphicsObject* get_connection_graphics_object(const int& to_node_id, const int& to_port_index) const; + void connect(VisualShaderConnectionGraphicsObject* c_o) { this->connection_graphics_objects.emplace_back(c_o); } + void detach_connection(VisualShaderConnectionGraphicsObject* c_o) { + connection_graphics_objects.erase(std::remove(connection_graphics_objects.begin(), connection_graphics_objects.end(), c_o), + connection_graphics_objects.end()); + } + bool is_connected() const { return connection_graphics_objects.size() > 0; } Q_SIGNALS: /** @@ -769,18 +773,19 @@ class VisualShaderOutputPortGraphicsObject : public QGraphicsObject { * @c VisualShaderGraphicsScene::on_port_* slots. * * @param port - * @param pos + * @param coordinate */ - void port_pressed(VisualShaderOutputPortGraphicsObject* port, const QPointF& pos); - void port_dragged(VisualShaderOutputPortGraphicsObject* port, const QPointF& pos); - void port_dropped(VisualShaderOutputPortGraphicsObject* port, const QPointF& pos); + void port_pressed(VisualShaderOutputPortGraphicsObject* port, const QPointF& coordinate); + void port_dragged(VisualShaderOutputPortGraphicsObject* port, const QPointF& coordinate); + void port_dropped(VisualShaderOutputPortGraphicsObject* port, const QPointF& coordinate); private: int n_id; int p_index; QRectF rect; - mutable VisualShaderConnectionGraphicsObject* connection_graphics_object; + // An output port can have multiple connections. + std::vector connection_graphics_objects; float padding = 0.5f; From c316264d25fd44049d14995a557ee87bf2a38197 Mon Sep 17 00:00:00 2001 From: Saif Kandil <74428638+k0T0z@users.noreply.github.com> Date: Mon, 30 Sep 2024 09:33:29 +0300 Subject: [PATCH 45/56] consistency ... --- Editors/VisualShaderEditor.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Editors/VisualShaderEditor.cpp b/Editors/VisualShaderEditor.cpp index 17e9588b6..3586c3cc6 100644 --- a/Editors/VisualShaderEditor.cpp +++ b/Editors/VisualShaderEditor.cpp @@ -817,7 +817,7 @@ void ShaderPreviewerWidget::update_shader_program() { } )"; - std::string fragment_shader_source = code.empty() ? R"( + std::string fragment_shader_source {code.empty() ? R"( #version 330 core out vec4 FragColor; in vec2 TexCoord; @@ -827,7 +827,7 @@ void ShaderPreviewerWidget::update_shader_program() { void main() { FragColor = vec4(0.0, 0.0, 0.0, 1.0); } - )" : "#version 330 core\n\n" + code; + )" : "#version 330 core\n\n" + code}; if (!shader_program->addShaderFromSourceCode(QOpenGLShader::Vertex, vertex_shader_source)) { qWarning() << "Vertex shader compilation failed:" << shader_program->log(); From 3696a7e0ae2b36eaf8ab6eb0ccba18ee337c71df Mon Sep 17 00:00:00 2001 From: Saif Kandil <74428638+k0T0z@users.noreply.github.com> Date: Mon, 30 Sep 2024 10:03:41 +0300 Subject: [PATCH 46/56] Added support for vector operations and functions --- Editors/VisualShaderEditor.cpp | 111 +++++++++++++++++++++++---------- Editors/VisualShaderEditor.h | 60 ++++++++++++------ 2 files changed, 117 insertions(+), 54 deletions(-) diff --git a/Editors/VisualShaderEditor.cpp b/Editors/VisualShaderEditor.cpp index 3586c3cc6..69c8f759b 100644 --- a/Editors/VisualShaderEditor.cpp +++ b/Editors/VisualShaderEditor.cpp @@ -2728,6 +2728,24 @@ VisualShaderNodeEmbedWidget::VisualShaderNodeEmbedWidget(const std::shared_ptr(node)}) { + VisualShaderNodeVectorBaseEmbedWidget* embed_widget = new VisualShaderNodeVectorBaseEmbedWidget(p); + layout->addWidget(embed_widget); + VisualShaderNodeVectorOpEmbedWidget* embed_widget2 = new VisualShaderNodeVectorOpEmbedWidget(p); + layout->addWidget(embed_widget2); + QObject::connect(embed_widget2, + QOverload::of(&QComboBox::currentIndexChanged), + this, + &VisualShaderNodeEmbedWidget::on_shader_preview_update_requested); + } else if (auto p {std::dynamic_pointer_cast(node)}) { + VisualShaderNodeVectorBaseEmbedWidget* embed_widget = new VisualShaderNodeVectorBaseEmbedWidget(p); + layout->addWidget(embed_widget); + VisualShaderNodeVectorFuncEmbedWidget* embed_widget2 = new VisualShaderNodeVectorFuncEmbedWidget(p); + layout->addWidget(embed_widget2); + QObject::connect(embed_widget2, + QOverload::of(&QComboBox::currentIndexChanged), + this, + &VisualShaderNodeEmbedWidget::on_shader_preview_update_requested); } // Create the button that will show/hide the shader previewer @@ -2878,40 +2896,6 @@ void VisualShaderNodeUIntOpEmbedWidget::on_current_index_changed(const int& inde node->set_operator((VisualShaderNodeUIntOp::Operator)itemData(index).toInt()); } -/*************************************/ -/* Vector Op Node */ -/*************************************/ - -VisualShaderNodeVectorOpEmbedWidget::VisualShaderNodeVectorOpEmbedWidget(const std::shared_ptr& node) : QComboBox(), - node(node) { - setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); - setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom - - addItem("Add", (int)VisualShaderNodeVectorOp::OP_ADD); - addItem("Subtract", (int)VisualShaderNodeVectorOp::OP_SUB); - addItem("Multiply", (int)VisualShaderNodeVectorOp::OP_MUL); - addItem("Divide", (int)VisualShaderNodeVectorOp::OP_DIV); - addItem("Modulus", (int)VisualShaderNodeVectorOp::OP_MOD); - addItem("Power", (int)VisualShaderNodeVectorOp::OP_POW); - addItem("Maximum", (int)VisualShaderNodeVectorOp::OP_MAX); - addItem("Minimum", (int)VisualShaderNodeVectorOp::OP_MIN); - addItem("Cross Product", (int)VisualShaderNodeVectorOp::OP_CROSS); - addItem("Arc Tangent 2", (int)VisualShaderNodeVectorOp::OP_ATAN2); - addItem("Reflect", (int)VisualShaderNodeVectorOp::OP_REFLECT); - addItem("Step", (int)VisualShaderNodeVectorOp::OP_STEP); - - QObject::connect(this, - QOverload::of(&QComboBox::currentIndexChanged), - this, - &VisualShaderNodeVectorOpEmbedWidget::on_current_index_changed); -} - -VisualShaderNodeVectorOpEmbedWidget::~VisualShaderNodeVectorOpEmbedWidget() {} - -void VisualShaderNodeVectorOpEmbedWidget::on_current_index_changed(const int& index) { - node->set_operator((VisualShaderNodeVectorOp::Operator)itemData(index).toInt()); -} - /*************************************/ /* Float Funcs Node */ /*************************************/ @@ -3016,6 +3000,65 @@ void VisualShaderNodeUIntFuncEmbedWidget::on_current_index_changed(const int& in node->set_function((VisualShaderNodeUIntFunc::Function)itemData(index).toInt()); } +/*************************************/ +/* Vector Base */ +/*************************************/ + +VisualShaderNodeVectorBaseEmbedWidget::VisualShaderNodeVectorBaseEmbedWidget(const std::shared_ptr& node) : QComboBox(), + node(node) { + setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom + + addItem("Vector 2D", (int)VisualShaderNodeVectorBase::OP_TYPE_VECTOR_2D); + addItem("Vector 3D", (int)VisualShaderNodeVectorBase::OP_TYPE_VECTOR_3D); + addItem("Vector 4D", (int)VisualShaderNodeVectorBase::OP_TYPE_VECTOR_4D); + + QObject::connect(this, + QOverload::of(&QComboBox::currentIndexChanged), + this, + &VisualShaderNodeVectorBaseEmbedWidget::on_current_index_changed); +} + +VisualShaderNodeVectorBaseEmbedWidget::~VisualShaderNodeVectorBaseEmbedWidget() {} + +void VisualShaderNodeVectorBaseEmbedWidget::on_current_index_changed(const int& index) { + node->set_op_type((VisualShaderNodeVectorBase::OpType)itemData(index).toInt()); +} + +/*************************************/ +/* Vector Op Node */ +/*************************************/ + +VisualShaderNodeVectorOpEmbedWidget::VisualShaderNodeVectorOpEmbedWidget(const std::shared_ptr& node) : QComboBox(), + node(node) { + setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom + + addItem("Add", (int)VisualShaderNodeVectorOp::OP_ADD); + addItem("Subtract", (int)VisualShaderNodeVectorOp::OP_SUB); + addItem("Multiply", (int)VisualShaderNodeVectorOp::OP_MUL); + addItem("Divide", (int)VisualShaderNodeVectorOp::OP_DIV); + addItem("Modulus", (int)VisualShaderNodeVectorOp::OP_MOD); + addItem("Power", (int)VisualShaderNodeVectorOp::OP_POW); + addItem("Maximum", (int)VisualShaderNodeVectorOp::OP_MAX); + addItem("Minimum", (int)VisualShaderNodeVectorOp::OP_MIN); + addItem("Cross Product", (int)VisualShaderNodeVectorOp::OP_CROSS); + addItem("Arc Tangent 2", (int)VisualShaderNodeVectorOp::OP_ATAN2); + addItem("Reflect", (int)VisualShaderNodeVectorOp::OP_REFLECT); + addItem("Step", (int)VisualShaderNodeVectorOp::OP_STEP); + + QObject::connect(this, + QOverload::of(&QComboBox::currentIndexChanged), + this, + &VisualShaderNodeVectorOpEmbedWidget::on_current_index_changed); +} + +VisualShaderNodeVectorOpEmbedWidget::~VisualShaderNodeVectorOpEmbedWidget() {} + +void VisualShaderNodeVectorOpEmbedWidget::on_current_index_changed(const int& index) { + node->set_operator((VisualShaderNodeVectorOp::Operator)itemData(index).toInt()); +} + /*************************************/ /* Vector Funcs Node */ /*************************************/ diff --git a/Editors/VisualShaderEditor.h b/Editors/VisualShaderEditor.h index 168cd1652..3e0a685b0 100644 --- a/Editors/VisualShaderEditor.h +++ b/Editors/VisualShaderEditor.h @@ -997,26 +997,6 @@ class VisualShaderNodeUIntOpEmbedWidget : public QComboBox { std::shared_ptr node; }; -/*************************************/ -/* Vector Op Node */ -/*************************************/ - -class VisualShaderNodeVectorOpEmbedWidget : public QComboBox { - Q_OBJECT - - public: - VisualShaderNodeVectorOpEmbedWidget(const std::shared_ptr& node); - ~VisualShaderNodeVectorOpEmbedWidget(); - - void set_current_index(const int& index) { this->setCurrentIndex(index); } - - private Q_SLOTS: - void on_current_index_changed(const int& index); - - private: - std::shared_ptr node; -}; - /*************************************/ /* Float Funcs Node */ /*************************************/ @@ -1077,6 +1057,46 @@ class VisualShaderNodeUIntFuncEmbedWidget : public QComboBox { std::shared_ptr node; }; +/*************************************/ +/* Vector Base */ +/*************************************/ + +class VisualShaderNodeVectorBaseEmbedWidget : public QComboBox { + Q_OBJECT + + public: + VisualShaderNodeVectorBaseEmbedWidget(const std::shared_ptr& node); + ~VisualShaderNodeVectorBaseEmbedWidget(); + + void set_current_index(const int& index) { this->setCurrentIndex(index); } + + private Q_SLOTS: + void on_current_index_changed(const int& index); + + private: + std::shared_ptr node; +}; + +/*************************************/ +/* Vector Op Node */ +/*************************************/ + +class VisualShaderNodeVectorOpEmbedWidget : public QComboBox { + Q_OBJECT + + public: + VisualShaderNodeVectorOpEmbedWidget(const std::shared_ptr& node); + ~VisualShaderNodeVectorOpEmbedWidget(); + + void set_current_index(const int& index) { this->setCurrentIndex(index); } + + private Q_SLOTS: + void on_current_index_changed(const int& index); + + private: + std::shared_ptr node; +}; + /*************************************/ /* Vector Funcs Node */ /*************************************/ From 022703895d2fcda79773b3f092e9ede7e81fa8db Mon Sep 17 00:00:00 2001 From: Saif Kandil <74428638+k0T0z@users.noreply.github.com> Date: Tue, 1 Oct 2024 06:29:02 +0300 Subject: [PATCH 47/56] Added a TODO for replacing ShaderPreviewerWidget class with ENIGMA Graphics System in the future --- Editors/VisualShaderEditor.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Editors/VisualShaderEditor.h b/Editors/VisualShaderEditor.h index 3e0a685b0..9f7c7c452 100644 --- a/Editors/VisualShaderEditor.h +++ b/Editors/VisualShaderEditor.h @@ -291,6 +291,13 @@ class OriginalMatchingImageWidget : public QWidget { /**********************************************************************/ /**********************************************************************/ +/** + * @brief This class is meant to be a temporary solution to preview the shader + * code. We should preview the shader code using ENIGMA's Graphics System. + * + * @todo Replace this class with ENIGMA's Graphics System. + * + */ class ShaderPreviewerWidget : public QOpenGLWidget { Q_OBJECT From 308886aa7c1a14c33bda1f91feadc0033ee82aab Mon Sep 17 00:00:00 2001 From: Saif Kandil <74428638+k0T0z@users.noreply.github.com> Date: Thu, 3 Oct 2024 22:25:06 +0300 Subject: [PATCH 48/56] bug fixes and few improvements --- Editors/VisualShaderEditor.cpp | 84 ++++++++++++++++++++++++---------- Editors/VisualShaderEditor.h | 18 ++++++-- 2 files changed, 73 insertions(+), 29 deletions(-) diff --git a/Editors/VisualShaderEditor.cpp b/Editors/VisualShaderEditor.cpp index 69c8f759b..3073f2faa 100644 --- a/Editors/VisualShaderEditor.cpp +++ b/Editors/VisualShaderEditor.cpp @@ -224,7 +224,7 @@ void VisualShaderEditor::init() { menu_bar->addWidget(create_node_button); QObject::connect(create_node_button, &QPushButton::pressed, this, &VisualShaderEditor::on_create_node_button_pressed); - this->connect(this, &VisualShaderEditor::on_create_node_dialog_requested, this, + this->connect(this, &VisualShaderEditor::create_node_dialog_requested, this, &VisualShaderEditor::show_create_node_dialog); // Create the preview shader button. @@ -411,6 +411,8 @@ const VisualShaderEditor::CreateNodeDialogNodesTreeItem VisualShaderEditor::crea {"UIntFunc", "Functions/Scalar", "VisualShaderNodeUIntFunc", "Unsigned integer function."}, {"VectorFunc", "Functions/Vector", "VisualShaderNodeVectorFunc", "Vector function."}, {"DerivativeFunc", "Functions/Others", "VisualShaderNodeDerivativeFunc", "Derivative function."}, + {"Step", "Functions/Others", "VisualShaderNodeStep", "Step function( scalar(edge), scalar(x) ).\n\nReturns 0.0 if 'x' is smaller than 'edge' and otherwise 1.0."}, + {"SmoothStep", "Functions/Others", "VisualShaderNodeSmoothStep", "SmoothStep function( scalar(edge0), scalar(edge1), scalar(x) ).\n\nReturns 0.0 if 'x' is smaller than 'edge0' and 1.0 if x is larger than 'edge1'. Otherwise the return value is interpolated between 0.0 and 1.0 using Hermite polynomials."}, // Operators @@ -479,7 +481,12 @@ void VisualShaderEditor::show_create_node_dialog(const QPointF& coordinate) { } } -void VisualShaderEditor::on_create_node_button_pressed() { Q_EMIT on_create_node_dialog_requested(); } +void VisualShaderEditor::on_create_node_button_pressed() { + // Send the center of the current view port + QPointF coordinate{view->mapToScene(view->viewport()->rect().center())}; + + Q_EMIT create_node_dialog_requested(coordinate); +} void VisualShaderEditor::on_preview_shader_button_pressed() { bool result{visual_shader->generate_shader()}; @@ -941,6 +948,10 @@ bool VisualShaderGraphicsScene::add_node(const std::string& type, const QPointF& n = std::make_shared(); } else if (type == "VisualShaderNodeSwitch") { n = std::make_shared(); + } else if (type == "VisualShaderNodeStep") { + n = std::make_shared(); + } else if (type == "VisualShaderNodeSmoothStep") { + n = std::make_shared(); } else { std::cout << "Unknown node type: " << type << std::endl; } @@ -1544,7 +1555,7 @@ VisualShaderGraphicsView::~VisualShaderGraphicsView() {} void VisualShaderGraphicsView::on_create_node_action_triggered() { VisualShaderEditor* editor {scene->get_editor()}; - Q_EMIT editor->on_create_node_dialog_requested(this->last_context_menu_coordinate); + Q_EMIT editor->create_node_dialog_requested(this->last_context_menu_coordinate); } void VisualShaderGraphicsView::zoom_in() { @@ -1623,16 +1634,20 @@ void VisualShaderGraphicsView::drawBackground(QPainter* painter, const QRectF& r } void VisualShaderGraphicsView::contextMenuEvent(QContextMenuEvent* event) { - QGraphicsView::contextMenuEvent(event); + QGraphicsItem* item {itemAt(event->pos())}; - // TODO: Make sure to not show the context menu if an item is under the mouse - if (itemAt(event->pos())) { + // If there is an item and this item is a node object, pass the event to the + // children + if (item && dynamic_cast(item)) { + QGraphicsView::contextMenuEvent(event); + return; + } else if (item) { return; } - this->last_context_menu_coordinate = (QPointF)event->globalPos(); + this->last_context_menu_coordinate = mapToScene(event->pos()); - context_menu->exec({(int)last_context_menu_coordinate.x(), (int)last_context_menu_coordinate.y()}); + context_menu->exec(event->globalPos()); } void VisualShaderGraphicsView::wheelEvent(QWheelEvent* event) { @@ -1865,15 +1880,22 @@ QRectF VisualShaderNodeGraphicsObject::boundingRect() const { for (unsigned i{0}; i < in_p_count; ++i) { QString p_n{QString::fromStdString(node->get_input_port_name(i))}; - if (!p_n.isEmpty()) { - QFont f("Arial", port_caption_font_size); - QFontMetrics fm(f); + QFont f("Arial", port_caption_font_size); + QFontMetrics fm(f); + + float w{0.0f}; - float w{(float)fm.horizontalAdvance(p_n)}; + if (!p_n.isEmpty()) { + w = (float)fm.horizontalAdvance(p_n); + } else { + // If the port name is empty, use a default name + // This is because the horizontal advance of an empty string is 0 and + // this will wrong the calculation of the rect width + w = (float)fm.horizontalAdvance("Input"); + } - if (w > max_in_p_width) { - max_in_p_width = w; - } + if ((w + port_caption_spacing) > max_in_p_width) { + max_in_p_width = w + port_caption_spacing; } } @@ -1883,15 +1905,22 @@ QRectF VisualShaderNodeGraphicsObject::boundingRect() const { for (unsigned i{0}; i < out_p_count; ++i) { QString p_n{QString::fromStdString(node->get_output_port_name(i))}; - if (!p_n.isEmpty()) { - QFont f("Arial", port_caption_font_size); - QFontMetrics fm(f); + QFont f("Arial", port_caption_font_size); + QFontMetrics fm(f); - float w{(float)fm.horizontalAdvance(p_n)}; + float w{0.0f}; - if (w > max_out_p_width) { - max_out_p_width = w; - } + if (!p_n.isEmpty()) { + w = (float)fm.horizontalAdvance(p_n); + } else { + // If the port name is empty, use a default name + // This is because the horizontal advance of an empty string is 0 and + // this will wrong the calculation of the rect width + w = (float)fm.horizontalAdvance("Output"); + } + + if ((w + port_caption_spacing) > max_out_p_width) { + max_out_p_width = w + port_caption_spacing; } } @@ -2150,21 +2179,26 @@ void VisualShaderNodeGraphicsObject::paint(QPainter* painter, const QStyleOption float embed_widget_y{rect_y + caption_rect_height + body_rect_header_height + embed_widget_v_padding}; embed_widget->setGeometry(embed_widget_x, embed_widget_y, embed_widget->width(), embed_widget->height()); + + // { + // // Draw Embed Widget + // painter->setPen(Qt::red); + // painter->setBrush(Qt::NoBrush); + // painter->drawRect(embed_widget_x, embed_widget_y, embed_widget->width(), embed_widget->height()); + // } } } } QVariant VisualShaderNodeGraphicsObject::itemChange(GraphicsItemChange change, const QVariant& value) { if (scene() && change == ItemScenePositionHasChanged) { - Q_EMIT node_moved(n_id, pos()); + Q_EMIT node_moved(n_id, scenePos()); } return QGraphicsObject::itemChange(change, value); } void VisualShaderNodeGraphicsObject::contextMenuEvent(QGraphicsSceneContextMenuEvent* event) { - QGraphicsObject::contextMenuEvent(event); - context_menu->exec(event->screenPos()); } diff --git a/Editors/VisualShaderEditor.h b/Editors/VisualShaderEditor.h index 9f7c7c452..fb4f5db67 100644 --- a/Editors/VisualShaderEditor.h +++ b/Editors/VisualShaderEditor.h @@ -107,8 +107,7 @@ class VisualShaderEditor : public BaseEditor { * * @param coordinate */ - void on_create_node_dialog_requested(const QPointF& coordinate = {0, - 0}); // {0, 0} is the top-left corner of the scene. + void create_node_dialog_requested(const QPointF& coordinate); private Q_SLOTS: /** @@ -117,7 +116,7 @@ class VisualShaderEditor : public BaseEditor { * @note Connected in @c VisualShaderEditor::init function * to @c QPushButton::pressed signal. * - * @note EMITS @c VisualShaderEditor::on_create_node_dialog_requested signal. + * @note EMITS @c VisualShaderEditor::create_node_dialog_requested signal. * */ void on_create_node_button_pressed(); @@ -487,7 +486,7 @@ class VisualShaderGraphicsView : public QGraphicsView { * @note Connected in @c VisualShaderGraphicsView::VisualShaderGraphicsView constructor * to @c QAction::triggered signal. * - * @note EMITS @c VisualShaderEditor::on_create_node_dialog_requested signal. + * @note EMITS @c VisualShaderEditor::create_node_dialog_requested signal. * */ void on_create_node_action_triggered(); @@ -691,6 +690,17 @@ class VisualShaderNodeGraphicsObject : public QGraphicsObject { float spacing_between_current_node_and_shader_previewer = 10.0f; QRectF boundingRect() const override; + + /** + * @brief + * + * @note This function contains some commented code that is meant to be used + * for debugging purposes. + * + * @param painter + * @param option + * @param widget + */ void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget = nullptr) override; QVariant itemChange(GraphicsItemChange change, const QVariant& value) override; void contextMenuEvent(QGraphicsSceneContextMenuEvent *event) override; From 0a0bc408f7a32a1ac426a11281464fd4781231df Mon Sep 17 00:00:00 2001 From: Saif Kandil <74428638+k0T0z@users.noreply.github.com> Date: Thu, 3 Oct 2024 22:31:03 +0300 Subject: [PATCH 49/56] Add Dot node --- Editors/VisualShaderEditor.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Editors/VisualShaderEditor.cpp b/Editors/VisualShaderEditor.cpp index 3073f2faa..8363fa0ab 100644 --- a/Editors/VisualShaderEditor.cpp +++ b/Editors/VisualShaderEditor.cpp @@ -413,6 +413,7 @@ const VisualShaderEditor::CreateNodeDialogNodesTreeItem VisualShaderEditor::crea {"DerivativeFunc", "Functions/Others", "VisualShaderNodeDerivativeFunc", "Derivative function."}, {"Step", "Functions/Others", "VisualShaderNodeStep", "Step function( scalar(edge), scalar(x) ).\n\nReturns 0.0 if 'x' is smaller than 'edge' and otherwise 1.0."}, {"SmoothStep", "Functions/Others", "VisualShaderNodeSmoothStep", "SmoothStep function( scalar(edge0), scalar(edge1), scalar(x) ).\n\nReturns 0.0 if 'x' is smaller than 'edge0' and 1.0 if x is larger than 'edge1'. Otherwise the return value is interpolated between 0.0 and 1.0 using Hermite polynomials."}, + {"Dot", "Functions/Others", "VisualShaderNodeDotProduct", "Calculates the dot product of two vectors."}, // Operators @@ -952,6 +953,8 @@ bool VisualShaderGraphicsScene::add_node(const std::string& type, const QPointF& n = std::make_shared(); } else if (type == "VisualShaderNodeSmoothStep") { n = std::make_shared(); + } else if (type == "VisualShaderNodeDotProduct") { + n = std::make_shared(); } else { std::cout << "Unknown node type: " << type << std::endl; } From d3f963c1d12e173666d9f0b1a1f686b0e3f28cee Mon Sep 17 00:00:00 2001 From: Saif Kandil <74428638+k0T0z@users.noreply.github.com> Date: Fri, 4 Oct 2024 16:55:30 +0300 Subject: [PATCH 50/56] Bug fixes and few improvements --- Editors/VisualShaderEditor.cpp | 68 ++++++++++++++++++++++++++++++---- Editors/VisualShaderEditor.h | 27 +++++++++++++- 2 files changed, 85 insertions(+), 10 deletions(-) diff --git a/Editors/VisualShaderEditor.cpp b/Editors/VisualShaderEditor.cpp index 8363fa0ab..caf5b7d5d 100644 --- a/Editors/VisualShaderEditor.cpp +++ b/Editors/VisualShaderEditor.cpp @@ -268,6 +268,7 @@ void VisualShaderEditor::init() { match_image_button->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom match_image_button->setToolTip("Match the shader to the loaded image"); menu_bar->addWidget(match_image_button); + QObject::connect(match_image_button, &QPushButton::pressed, this, &VisualShaderEditor::on_match_image_button_pressed); // Set the top layer layout. top_layer->setLayout(menu_bar); @@ -516,6 +517,8 @@ void VisualShaderEditor::on_load_image_button_pressed() { // sprites have multiple frames, which is a headache for this project because it's a lot more behavior we need to define } +void VisualShaderEditor::on_match_image_button_pressed() {} + std::vector VisualShaderEditor::parse_node_category_path(const std::string& node_category_path) { std::vector tokens; std::stringstream ss(node_category_path); @@ -1013,6 +1016,9 @@ bool VisualShaderGraphicsScene::add_node(const int& n_id, const std::shared_ptr< QObject::connect(n_o, &VisualShaderNodeGraphicsObject::out_port_dragged, this, &VisualShaderGraphicsScene::on_port_dragged); QObject::connect(n_o, &VisualShaderNodeGraphicsObject::out_port_dropped, this, &VisualShaderGraphicsScene::on_port_dropped); + QObject::connect(n_o, &VisualShaderNodeGraphicsObject::scene_update_requested, this, &VisualShaderGraphicsScene::on_scene_update_requested); + QObject::connect(n_o, &VisualShaderNodeGraphicsObject::scene_item_remove_requested, this, &VisualShaderGraphicsScene::on_scene_item_remove_requested); + if (n_id != (int)VisualShader::NODE_ID_OUTPUT) { VisualShaderNodeEmbedWidget* embed_widget {new VisualShaderNodeEmbedWidget(n)}; QGraphicsProxyWidget* embed_widget_proxy{new QGraphicsProxyWidget(n_o)}; @@ -1023,6 +1029,11 @@ bool VisualShaderGraphicsScene::add_node(const int& n_id, const std::shared_ptr< this, &VisualShaderGraphicsScene::on_update_shader_previewer_widgets_requested); + QObject::connect(embed_widget, + &VisualShaderNodeEmbedWidget::node_update_requested, + n_o, + &VisualShaderNodeGraphicsObject::on_node_update_requested); + // Send the shader previewer widget embed_widget->set_shader_previewer_widget(n_o->get_shader_previewer_widget()); } @@ -1106,10 +1117,8 @@ bool VisualShaderGraphicsScene::delete_node(const int& n_id) { } // Remove the node from the scene - removeItem(n_o); this->node_graphics_objects.erase(n_id); - - delete n_o; // Make sure to delete the node object to delete all child ports + on_scene_item_remove_requested(n_o); return true; } @@ -1127,12 +1136,19 @@ void VisualShaderGraphicsScene::on_update_shader_previewer_widgets_requested() { spw->set_code(vs->generate_preview_shader(n_id, 0)); // 0 is the output port index } + + on_scene_update_requested(); } void VisualShaderGraphicsScene::on_scene_update_requested() { update(); } +void VisualShaderGraphicsScene::on_scene_item_remove_requested(QGraphicsItem* item) { + removeItem(item); + delete item; +} + bool VisualShaderGraphicsScene::add_connection(const int& from_node_id, const int& from_port_index, const int& to_node_id, const int& to_port_index) { QList views{this->views()}; @@ -1231,8 +1247,7 @@ bool VisualShaderGraphicsScene::delete_connection(const int& from_node_id, const if (this->temporary_connection_graphics_object) { from_o_port->detach_connection(this->temporary_connection_graphics_object); - removeItem(this->temporary_connection_graphics_object); - delete this->temporary_connection_graphics_object; + on_scene_item_remove_requested(this->temporary_connection_graphics_object); this->temporary_connection_graphics_object = nullptr; return true; } @@ -1265,8 +1280,7 @@ bool VisualShaderGraphicsScene::delete_connection(const int& from_node_id, const to_i_port->detach_connection(); from_o_port->detach_connection(c_o); - removeItem(c_o); - delete c_o; + on_scene_item_remove_requested(c_o); on_update_shader_previewer_widgets_requested(); @@ -1336,6 +1350,10 @@ void VisualShaderGraphicsScene::on_node_moved(const int& n_id, const QPointF& ne } void VisualShaderGraphicsScene::on_node_deleted(const int& n_id) { + if (n_id == (int)VisualShader::NODE_ID_OUTPUT) { + return; + } + bool result{this->delete_node(n_id)}; if (!result) { @@ -1373,6 +1391,7 @@ void VisualShaderGraphicsScene::on_port_dragged(QGraphicsObject* port, const QPo on_update_shader_previewer_widgets_requested(); } else if (!i_port->is_connected() && temporary_connection_graphics_object) { c_o = temporary_connection_graphics_object; + on_scene_update_requested(); } else { return; } @@ -1849,6 +1868,22 @@ VisualShaderOutputPortGraphicsObject* VisualShaderNodeGraphicsObject::get_output return nullptr; } +void VisualShaderNodeGraphicsObject::on_node_update_requested() { + for (auto& [p_index, i_port] : in_port_graphics_objects) { + Q_EMIT scene_item_remove_requested(i_port); + } + + for (auto& [p_index, o_port] : out_port_graphics_objects) { + Q_EMIT scene_item_remove_requested(o_port); + } + + in_port_graphics_objects.clear(); + out_port_graphics_objects.clear(); + + update(); + Q_EMIT scene_update_requested(); +} + QRectF VisualShaderNodeGraphicsObject::boundingRect() const { QFont f("Arial", caption_font_size); f.setBold(true); @@ -2194,7 +2229,7 @@ void VisualShaderNodeGraphicsObject::paint(QPainter* painter, const QStyleOption } QVariant VisualShaderNodeGraphicsObject::itemChange(GraphicsItemChange change, const QVariant& value) { - if (scene() && change == ItemScenePositionHasChanged) { + if (change == ItemScenePositionHasChanged) { Q_EMIT node_moved(n_id, scenePos()); } @@ -2783,6 +2818,20 @@ VisualShaderNodeEmbedWidget::VisualShaderNodeEmbedWidget(const std::shared_ptr::of(&QComboBox::currentIndexChanged), this, &VisualShaderNodeEmbedWidget::on_shader_preview_update_requested); + } else if (auto p {std::dynamic_pointer_cast(node)}) { + VisualShaderNodeVectorBaseEmbedWidget* embed_widget = new VisualShaderNodeVectorBaseEmbedWidget(p); + layout->addWidget(embed_widget); + QObject::connect(embed_widget, + QOverload::of(&QComboBox::currentIndexChanged), + this, + &VisualShaderNodeEmbedWidget::on_node_update_requested); + } else if (auto p {std::dynamic_pointer_cast(node)}) { + VisualShaderNodeVectorBaseEmbedWidget* embed_widget = new VisualShaderNodeVectorBaseEmbedWidget(p); + layout->addWidget(embed_widget); + QObject::connect(embed_widget, + QOverload::of(&QComboBox::currentIndexChanged), + this, + &VisualShaderNodeEmbedWidget::on_node_update_requested); } // Create the button that will show/hide the shader previewer @@ -3050,6 +3099,9 @@ VisualShaderNodeVectorBaseEmbedWidget::VisualShaderNodeVectorBaseEmbedWidget(con addItem("Vector 3D", (int)VisualShaderNodeVectorBase::OP_TYPE_VECTOR_3D); addItem("Vector 4D", (int)VisualShaderNodeVectorBase::OP_TYPE_VECTOR_4D); + // set the current index + setCurrentIndex((int)node->get_op_type()); + QObject::connect(this, QOverload::of(&QComboBox::currentIndexChanged), this, diff --git a/Editors/VisualShaderEditor.h b/Editors/VisualShaderEditor.h index fb4f5db67..12cef8212 100644 --- a/Editors/VisualShaderEditor.h +++ b/Editors/VisualShaderEditor.h @@ -87,6 +87,12 @@ class VisualShaderEditor : public BaseEditor { Q_OBJECT public: + /** + * @brief This constructor is meant to be used for testing purposes. As + * it doesn't require a MessageModel object. + * + * @param parent + */ VisualShaderEditor(QWidget* parent = nullptr); VisualShaderEditor(MessageModel* model, QWidget* parent = nullptr); ~VisualShaderEditor() override; @@ -124,6 +130,7 @@ class VisualShaderEditor : public BaseEditor { void on_menu_button_pressed(); void on_load_image_button_pressed(); + void on_match_image_button_pressed(); private: VisualShader* visual_shader; @@ -195,6 +202,11 @@ class VisualShaderEditor : public BaseEditor { */ void init(); + /** + * @brief The VisualShader class may have some nodes at the beginning. This function + * is meant to add those nodes to the scene. + * + */ void init_graph(); void create_node(const QPointF& coordinate); @@ -395,7 +407,7 @@ class VisualShaderGraphicsScene : public QGraphicsScene { VisualShaderNodeGraphicsObject* get_node_graphics_object(const int& n_id) const; - public Q_SLOTS: + private Q_SLOTS: /** * @brief Called when an interaction with a port is made. * @@ -409,7 +421,6 @@ class VisualShaderGraphicsScene : public QGraphicsScene { void on_port_dragged(QGraphicsObject* port, const QPointF& coordinate); void on_port_dropped(QGraphicsObject* port, const QPointF& coordinate); - private Q_SLOTS: /** * @brief Called when a node is moved. * @@ -439,6 +450,8 @@ class VisualShaderGraphicsScene : public QGraphicsScene { void on_scene_update_requested(); + void on_scene_item_remove_requested(QGraphicsItem* item); + private: VisualShader* vs; mutable VisualShaderEditor* editor; @@ -566,6 +579,9 @@ class VisualShaderNodeGraphicsObject : public QGraphicsObject { ShaderPreviewerWidget* get_shader_previewer_widget() const { return shader_previewer_widget; } + public Q_SLOTS: + void on_node_update_requested(); + Q_SIGNALS: /** * @brief Send a request to delete a node. @@ -600,6 +616,10 @@ class VisualShaderNodeGraphicsObject : public QGraphicsObject { void out_port_dragged(VisualShaderOutputPortGraphicsObject* port, const QPointF& coordinate); void out_port_dropped(VisualShaderOutputPortGraphicsObject* port, const QPointF& coordinate); + void scene_update_requested(); + + void scene_item_remove_requested(QGraphicsItem* item); + private Q_SLOTS: /** * @brief Called when @c VisualShaderNodeGraphicsObject::delete_node_action is triggered. @@ -916,6 +936,7 @@ class VisualShaderNodeEmbedWidget : public QWidget { Q_SIGNALS: void shader_preview_update_requested(); + void node_update_requested(); private Q_SLOTS: void on_preview_shader_button_pressed() { @@ -926,6 +947,8 @@ class VisualShaderNodeEmbedWidget : public QWidget { void on_shader_preview_update_requested() { Q_EMIT shader_preview_update_requested(); } + void on_node_update_requested() { Q_EMIT node_update_requested(); } + private: QVBoxLayout* layout; From cb810b6acf8ecfdcc68a4363d2925f4a4df265b4 Mon Sep 17 00:00:00 2001 From: Saif Kandil <74428638+k0T0z@users.noreply.github.com> Date: Fri, 4 Oct 2024 19:32:46 +0300 Subject: [PATCH 51/56] Added last nodes --- Editors/VisualShaderEditor.cpp | 279 +++++++++++++++++++++++++++++++++ Editors/VisualShaderEditor.h | 100 ++++++++++++ 2 files changed, 379 insertions(+) diff --git a/Editors/VisualShaderEditor.cpp b/Editors/VisualShaderEditor.cpp index caf5b7d5d..bf1d22433 100644 --- a/Editors/VisualShaderEditor.cpp +++ b/Editors/VisualShaderEditor.cpp @@ -2832,6 +2832,51 @@ VisualShaderNodeEmbedWidget::VisualShaderNodeEmbedWidget(const std::shared_ptr::of(&QComboBox::currentIndexChanged), this, &VisualShaderNodeEmbedWidget::on_node_update_requested); + } else if (auto p {std::dynamic_pointer_cast(node)}) { + VisualShaderNodeDerivativeFuncEmbedWidget* embed_widget = new VisualShaderNodeDerivativeFuncEmbedWidget(p); + layout->addLayout(embed_widget); + QObject::connect(embed_widget->get_op_type_combo_box(), + QOverload::of(&QComboBox::currentIndexChanged), + this, + &VisualShaderNodeEmbedWidget::on_shader_preview_update_requested); + + QObject::connect(embed_widget->get_function_combo_box(), + QOverload::of(&QComboBox::currentIndexChanged), + this, + &VisualShaderNodeEmbedWidget::on_shader_preview_update_requested); + QObject::connect(embed_widget->get_precision_combo_box(), + QOverload::of(&QComboBox::currentIndexChanged), + this, + &VisualShaderNodeEmbedWidget::on_shader_preview_update_requested); + } else if (auto p {std::dynamic_pointer_cast(node)}) { + VisualShaderNodeCompareEmbedWidget* embed_widget = new VisualShaderNodeCompareEmbedWidget(p); + layout->addLayout(embed_widget); + QObject::connect(embed_widget->get_comparison_type_combo_box(), + QOverload::of(&QComboBox::currentIndexChanged), + this, + &VisualShaderNodeEmbedWidget::on_shader_preview_update_requested); + QObject::connect(embed_widget->get_func_combo_box(), + QOverload::of(&QComboBox::currentIndexChanged), + this, + &VisualShaderNodeEmbedWidget::on_shader_preview_update_requested); + QObject::connect(embed_widget->get_condition_combo_box(), + QOverload::of(&QComboBox::currentIndexChanged), + this, + &VisualShaderNodeEmbedWidget::on_shader_preview_update_requested); + } else if (auto p {std::dynamic_pointer_cast(node)}) { + VisualShaderNodeSwitchEmbedWidget* embed_widget = new VisualShaderNodeSwitchEmbedWidget(p); + layout->addWidget(embed_widget); + QObject::connect(embed_widget, + QOverload::of(&QComboBox::currentIndexChanged), + this, + &VisualShaderNodeEmbedWidget::on_shader_preview_update_requested); + } else if (auto p {std::dynamic_pointer_cast(node)}) { + VisualShaderNodeIsEmbedWidget* embed_widget = new VisualShaderNodeIsEmbedWidget(p); + layout->addWidget(embed_widget); + QObject::connect(embed_widget, + QOverload::of(&QComboBox::currentIndexChanged), + this, + &VisualShaderNodeEmbedWidget::on_shader_preview_update_requested); } // Create the button that will show/hide the shader previewer @@ -2902,6 +2947,8 @@ VisualShaderNodeFloatOpEmbedWidget::VisualShaderNodeFloatOpEmbedWidget(const std addItem("Arc Tangent 2", (int)VisualShaderNodeFloatOp::OP_ATAN2); addItem("Step", (int)VisualShaderNodeFloatOp::OP_STEP); + setCurrentIndex((int)node->get_operator()); + QObject::connect(this, QOverload::of(&QComboBox::currentIndexChanged), this, @@ -2936,6 +2983,8 @@ VisualShaderNodeIntOpEmbedWidget::VisualShaderNodeIntOpEmbedWidget(const std::sh addItem("Bitwise Left Shift", (int)VisualShaderNodeIntOp::OP_BITWISE_LEFT_SHIFT); addItem("Bitwise Right Shift", (int)VisualShaderNodeIntOp::OP_BITWISE_RIGHT_SHIFT); + setCurrentIndex((int)node->get_operator()); + QObject::connect(this, QOverload::of(&QComboBox::currentIndexChanged), this, @@ -2970,6 +3019,8 @@ VisualShaderNodeUIntOpEmbedWidget::VisualShaderNodeUIntOpEmbedWidget(const std:: addItem("Bitwise Left Shift", (int)VisualShaderNodeUIntOp::OP_BITWISE_LEFT_SHIFT); addItem("Bitwise Right Shift", (int)VisualShaderNodeUIntOp::OP_BITWISE_RIGHT_SHIFT); + setCurrentIndex((int)node->get_operator()); + QObject::connect(this, QOverload::of(&QComboBox::currentIndexChanged), this, @@ -3024,6 +3075,8 @@ VisualShaderNodeFloatFuncEmbedWidget::VisualShaderNodeFloatFuncEmbedWidget(const addItem("Truncate", (int)VisualShaderNodeFloatFunc::FUNC_TRUNC); addItem("One Minus", (int)VisualShaderNodeFloatFunc::FUNC_ONEMINUS); + setCurrentIndex((int)node->get_function()); + QObject::connect(this, QOverload::of(&QComboBox::currentIndexChanged), this, @@ -3050,6 +3103,8 @@ VisualShaderNodeIntFuncEmbedWidget::VisualShaderNodeIntFuncEmbedWidget(const std addItem("Sign", (int)VisualShaderNodeIntFunc::FUNC_SIGN); addItem("Bitwise NOT", (int)VisualShaderNodeIntFunc::FUNC_BITWISE_NOT); + setCurrentIndex((int)node->get_function()); + QObject::connect(this, QOverload::of(&QComboBox::currentIndexChanged), this, @@ -3074,6 +3129,8 @@ VisualShaderNodeUIntFuncEmbedWidget::VisualShaderNodeUIntFuncEmbedWidget(const s addItem("Negate", (int)VisualShaderNodeUIntFunc::FUNC_NEGATE); addItem("Bitwise NOT", (int)VisualShaderNodeUIntFunc::FUNC_BITWISE_NOT); + setCurrentIndex((int)node->get_function()); + QObject::connect(this, QOverload::of(&QComboBox::currentIndexChanged), this, @@ -3136,6 +3193,8 @@ VisualShaderNodeVectorOpEmbedWidget::VisualShaderNodeVectorOpEmbedWidget(const s addItem("Reflect", (int)VisualShaderNodeVectorOp::OP_REFLECT); addItem("Step", (int)VisualShaderNodeVectorOp::OP_STEP); + setCurrentIndex((int)node->get_operator()); + QObject::connect(this, QOverload::of(&QComboBox::currentIndexChanged), this, @@ -3191,6 +3250,8 @@ VisualShaderNodeVectorFuncEmbedWidget::VisualShaderNodeVectorFuncEmbedWidget(con addItem("Truncate", (int)VisualShaderNodeVectorFunc::FUNC_TRUNC); addItem("One Minus", (int)VisualShaderNodeVectorFunc::FUNC_ONEMINUS); + setCurrentIndex((int)node->get_function()); + QObject::connect(this, QOverload::of(&QComboBox::currentIndexChanged), this, @@ -3212,6 +3273,13 @@ VisualShaderNodeColorConstantEmbedWidget::VisualShaderNodeColorConstantEmbedWidg setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom + { + QPalette palette {this->palette()}; + TColor c{node->get_constant()}; + palette.setColor(QPalette::Button, QColor(c.r, c.g, c.b, c.a)); + this->setPalette(palette); + } + QObject::connect(this, &QPushButton::pressed, this, @@ -3251,6 +3319,8 @@ VisualShaderNodeBooleanConstantEmbedWidget::VisualShaderNodeBooleanConstantEmbed setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom + setCheckState(node->get_constant() ? Qt::Checked : Qt::Unchecked); + QObject::connect(this, &QCheckBox::stateChanged, this, @@ -3274,6 +3344,8 @@ VisualShaderNodeFloatConstantEmbedWidget::VisualShaderNodeFloatConstantEmbedWidg setPlaceholderText("Value"); + setText(QString::number(node->get_constant())); + QObject::connect(this, &QLineEdit::textChanged, this, @@ -3308,6 +3380,8 @@ VisualShaderNodeIntConstantEmbedWidget::VisualShaderNodeIntConstantEmbedWidget(c setPlaceholderText("Value"); + setText(QString::number(node->get_constant())); + QObject::connect(this, &QLineEdit::textChanged, this, @@ -3342,6 +3416,8 @@ VisualShaderNodeUIntConstantEmbedWidget::VisualShaderNodeUIntConstantEmbedWidget setPlaceholderText("Value"); + setText(QString::number(node->get_constant())); + QObject::connect(this, &QLineEdit::textChanged, this, @@ -3382,6 +3458,9 @@ VisualShaderNodeVec2ConstantEmbedWidget::VisualShaderNodeVec2ConstantEmbedWidget x_edit_widget->setPlaceholderText("X"); y_edit_widget->setPlaceholderText("Y"); + x_edit_widget->setText(QString::number(node->get_constant().x)); + y_edit_widget->setText(QString::number(node->get_constant().y)); + QObject::connect(x_edit_widget, &QLineEdit::textChanged, this, @@ -3446,6 +3525,10 @@ VisualShaderNodeVec3ConstantEmbedWidget::VisualShaderNodeVec3ConstantEmbedWidget y_edit_widget->setPlaceholderText("Y"); z_edit_widget->setPlaceholderText("Z"); + x_edit_widget->setText(QString::number(node->get_constant().x)); + y_edit_widget->setText(QString::number(node->get_constant().y)); + z_edit_widget->setText(QString::number(node->get_constant().z)); + QObject::connect(x_edit_widget, &QLineEdit::textChanged, this, @@ -3532,6 +3615,11 @@ VisualShaderNodeVec4ConstantEmbedWidget::VisualShaderNodeVec4ConstantEmbedWidget z_edit_widget->setPlaceholderText("Z"); w_edit_widget->setPlaceholderText("W"); + x_edit_widget->setText(QString::number(node->get_constant().x)); + y_edit_widget->setText(QString::number(node->get_constant().y)); + z_edit_widget->setText(QString::number(node->get_constant().z)); + w_edit_widget->setText(QString::number(node->get_constant().w)); + QObject::connect(x_edit_widget, &QLineEdit::textChanged, this, @@ -3617,6 +3705,72 @@ void VisualShaderNodeVec4ConstantEmbedWidget::on_w_text_changed(const QString& t } } +/*************************************/ +/* Derivative Func Node */ +/*************************************/ + +VisualShaderNodeDerivativeFuncEmbedWidget::VisualShaderNodeDerivativeFuncEmbedWidget(const std::shared_ptr& node) : QVBoxLayout(), + node(node) { + setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom + setSizeConstraint(QLayout::SetNoConstraint); + setSpacing(2); + setAlignment(Qt::AlignVCenter | Qt::AlignHCenter); + + op_type_combo_box = new QComboBox(); + function_combo_box = new QComboBox(); + precision_combo_box = new QComboBox(); + + op_type_combo_box->addItem("Scalar", (int)VisualShaderNodeDerivativeFunc::OP_TYPE_SCALAR); + op_type_combo_box->addItem("Vector 2D", (int)VisualShaderNodeDerivativeFunc::OP_TYPE_VECTOR_2D); + op_type_combo_box->addItem("Vector 3D", (int)VisualShaderNodeDerivativeFunc::OP_TYPE_VECTOR_3D); + op_type_combo_box->addItem("Vector 4D", (int)VisualShaderNodeDerivativeFunc::OP_TYPE_VECTOR_4D); + + op_type_combo_box->setCurrentIndex((int)node->get_op_type()); + + function_combo_box->addItem("Sum", (int)VisualShaderNodeDerivativeFunc::FUNC_SUM); + function_combo_box->addItem("X", (int)VisualShaderNodeDerivativeFunc::FUNC_X); + function_combo_box->addItem("Y", (int)VisualShaderNodeDerivativeFunc::FUNC_Y); + + function_combo_box->setCurrentIndex((int)node->get_function()); + + precision_combo_box->addItem("None", (int)VisualShaderNodeDerivativeFunc::PRECISION_NONE); + precision_combo_box->addItem("Coarse", (int)VisualShaderNodeDerivativeFunc::PRECISION_COARSE); + precision_combo_box->addItem("Fine", (int)VisualShaderNodeDerivativeFunc::PRECISION_FINE); + + precision_combo_box->setCurrentIndex((int)node->get_precision()); + + QObject::connect(op_type_combo_box, + QOverload::of(&QComboBox::currentIndexChanged), + this, + &VisualShaderNodeDerivativeFuncEmbedWidget::on_op_type_current_index_changed); + QObject::connect(function_combo_box, + QOverload::of(&QComboBox::currentIndexChanged), + this, + &VisualShaderNodeDerivativeFuncEmbedWidget::on_function_current_index_changed); + QObject::connect(precision_combo_box, + QOverload::of(&QComboBox::currentIndexChanged), + this, + &VisualShaderNodeDerivativeFuncEmbedWidget::on_precision_current_index_changed); + + addWidget(op_type_combo_box); + addWidget(function_combo_box); + addWidget(precision_combo_box); +} + +VisualShaderNodeDerivativeFuncEmbedWidget::~VisualShaderNodeDerivativeFuncEmbedWidget() {} + +void VisualShaderNodeDerivativeFuncEmbedWidget::on_op_type_current_index_changed(const int& index) { + node->set_op_type((VisualShaderNodeDerivativeFunc::OpType)op_type_combo_box->itemData(index).toInt()); +} + +void VisualShaderNodeDerivativeFuncEmbedWidget::on_function_current_index_changed(const int& index) { + node->set_function((VisualShaderNodeDerivativeFunc::Function)function_combo_box->itemData(index).toInt()); +} + +void VisualShaderNodeDerivativeFuncEmbedWidget::on_precision_current_index_changed(const int& index) { + node->set_precision((VisualShaderNodeDerivativeFunc::Precision)precision_combo_box->itemData(index).toInt()); +} + /*************************************/ /* Value Noise Node */ /*************************************/ @@ -3628,6 +3782,8 @@ VisualShaderNodeValueNoiseEmbedWidget::VisualShaderNodeValueNoiseEmbedWidget(con setPlaceholderText("Scale"); + setText(QString::number(node->get_scale())); + QObject::connect(this, &QLineEdit::textChanged, this, &VisualShaderNodeValueNoiseEmbedWidget::on_text_changed); } @@ -3659,6 +3815,8 @@ VisualShaderNodePerlinNoiseEmbedWidget::VisualShaderNodePerlinNoiseEmbedWidget(c setPlaceholderText("Scale"); + setText(QString::number(node->get_scale())); + QObject::connect(this, &QLineEdit::textChanged, this, &VisualShaderNodePerlinNoiseEmbedWidget::on_text_changed); } @@ -3690,6 +3848,8 @@ VisualShaderNodeVoronoiNoiseAngleOffsetEmbedWidget::VisualShaderNodeVoronoiNoise setPlaceholderText("Angle Offset"); + setText(QString::number(node->get_angle_offset())); + QObject::connect(this, &QLineEdit::textChanged, this, &VisualShaderNodeVoronoiNoiseAngleOffsetEmbedWidget::on_text_changed); } @@ -3717,6 +3877,8 @@ VisualShaderNodeVoronoiNoiseCellDensityEmbedWidget::VisualShaderNodeVoronoiNoise setPlaceholderText("Cell Density"); + setText(QString::number(node->get_cell_density())); + QObject::connect(this, &QLineEdit::textChanged, this, &VisualShaderNodeVoronoiNoiseCellDensityEmbedWidget::on_text_changed); } @@ -3736,3 +3898,120 @@ void VisualShaderNodeVoronoiNoiseCellDensityEmbedWidget::on_text_changed(const Q } } } + +/*************************************/ +/* Logic */ +/*************************************/ + +/*************************************/ +/* Compare Node */ +/*************************************/ + +VisualShaderNodeCompareEmbedWidget::VisualShaderNodeCompareEmbedWidget(const std::shared_ptr& node) : QVBoxLayout(), + node(node) { + setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom + setSizeConstraint(QLayout::SetNoConstraint); + setSpacing(2); + setAlignment(Qt::AlignVCenter | Qt::AlignHCenter); + + comparison_type_combo_box = new QComboBox(); + func_combo_box = new QComboBox(); + condition_combo_box = new QComboBox(); + + comparison_type_combo_box->addItem("Scalar", (int)VisualShaderNodeCompare::CMP_TYPE_SCALAR); + comparison_type_combo_box->addItem("Scalar Int", (int)VisualShaderNodeCompare::CMP_TYPE_SCALAR_INT); + comparison_type_combo_box->addItem("Scalar UInt", (int)VisualShaderNodeCompare::CMP_TYPE_SCALAR_UINT); + comparison_type_combo_box->addItem("Vector 2D", (int)VisualShaderNodeCompare::CMP_TYPE_VECTOR_2D); + comparison_type_combo_box->addItem("Vector 3D", (int)VisualShaderNodeCompare::CMP_TYPE_VECTOR_3D); + comparison_type_combo_box->addItem("Vector 4D", (int)VisualShaderNodeCompare::CMP_TYPE_VECTOR_4D); + comparison_type_combo_box->addItem("Boolean", (int)VisualShaderNodeCompare::CMP_TYPE_BOOLEAN); + + comparison_type_combo_box->setCurrentIndex((int)node->get_comparison_type()); + + func_combo_box->addItem("==", (int)VisualShaderNodeCompare::FUNC_EQUAL); + func_combo_box->addItem("!=", (int)VisualShaderNodeCompare::FUNC_NOT_EQUAL); + func_combo_box->addItem(">", (int)VisualShaderNodeCompare::FUNC_GREATER_THAN); + func_combo_box->addItem(">=", (int)VisualShaderNodeCompare::FUNC_GREATER_THAN_EQUAL); + func_combo_box->addItem("<", (int)VisualShaderNodeCompare::FUNC_LESS_THAN); + func_combo_box->addItem("<=", (int)VisualShaderNodeCompare::FUNC_LESS_THAN_EQUAL); + + func_combo_box->setCurrentIndex((int)node->get_function()); + + condition_combo_box->addItem("All", (int)VisualShaderNodeCompare::COND_ALL); + condition_combo_box->addItem("Any", (int)VisualShaderNodeCompare::COND_ANY); + + condition_combo_box->setCurrentIndex((int)node->get_condition()); + + addWidget(comparison_type_combo_box); + addWidget(func_combo_box); + addWidget(condition_combo_box); +} + +VisualShaderNodeCompareEmbedWidget::~VisualShaderNodeCompareEmbedWidget() {} + +void VisualShaderNodeCompareEmbedWidget::on_comparison_type_current_index_changed(const int& index) { + node->set_comparison_type((VisualShaderNodeCompare::ComparisonType)comparison_type_combo_box->itemData(index).toInt()); +} +void VisualShaderNodeCompareEmbedWidget::on_func_current_index_changed(const int& index) { + node->set_function((VisualShaderNodeCompare::Function)func_combo_box->itemData(index).toInt()); +} +void VisualShaderNodeCompareEmbedWidget::on_condition_current_index_changed(const int& index) { + node->set_condition((VisualShaderNodeCompare::Condition)condition_combo_box->itemData(index).toInt()); +} + +/*************************************/ +/* Switch Node */ +/*************************************/ + +VisualShaderNodeSwitchEmbedWidget::VisualShaderNodeSwitchEmbedWidget(const std::shared_ptr& node) : QComboBox(), + node(node) { + setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom + + addItem("Float", (int)VisualShaderNodeSwitch::OP_TYPE_FLOAT); + addItem("Int", (int)VisualShaderNodeSwitch::OP_TYPE_INT); + addItem("UInt", (int)VisualShaderNodeSwitch::OP_TYPE_UINT); + addItem("Vector 2D", (int)VisualShaderNodeSwitch::OP_TYPE_VECTOR_2D); + addItem("Vector 3D", (int)VisualShaderNodeSwitch::OP_TYPE_VECTOR_3D); + addItem("Vector 4D", (int)VisualShaderNodeSwitch::OP_TYPE_VECTOR_4D); + addItem("Boolean", (int)VisualShaderNodeSwitch::OP_TYPE_BOOLEAN); + + setCurrentIndex((int)node->get_op_type()); + + QObject::connect(this, + QOverload::of(&QComboBox::currentIndexChanged), + this, + &VisualShaderNodeSwitchEmbedWidget::on_current_index_changed); +} + +VisualShaderNodeSwitchEmbedWidget::~VisualShaderNodeSwitchEmbedWidget() {} + +void VisualShaderNodeSwitchEmbedWidget::on_current_index_changed(const int& index) { + node->set_op_type((VisualShaderNodeSwitch::OpType)itemData(index).toInt()); +} + +/*************************************/ +/* Is Node */ +/*************************************/ + +VisualShaderNodeIsEmbedWidget::VisualShaderNodeIsEmbedWidget(const std::shared_ptr& node) : QComboBox(), + node(node) { + setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom + + addItem("Is Inf", (int)VisualShaderNodeIs::FUNC_IS_INF); + addItem("Is NaN", (int)VisualShaderNodeIs::FUNC_IS_NAN); + + setCurrentIndex((int)node->get_function()); + + QObject::connect(this, + QOverload::of(&QComboBox::currentIndexChanged), + this, + &VisualShaderNodeIsEmbedWidget::on_current_index_changed); +} + +VisualShaderNodeIsEmbedWidget::~VisualShaderNodeIsEmbedWidget() {} + +void VisualShaderNodeIsEmbedWidget::on_current_index_changed(const int& index) { + node->set_function((VisualShaderNodeIs::Function)itemData(index).toInt()); +} diff --git a/Editors/VisualShaderEditor.h b/Editors/VisualShaderEditor.h index 12cef8212..5adc5a1f6 100644 --- a/Editors/VisualShaderEditor.h +++ b/Editors/VisualShaderEditor.h @@ -1337,6 +1337,34 @@ class VisualShaderNodeVec4ConstantEmbedWidget : public QVBoxLayout { QLineEdit* w_edit_widget; }; +/*************************************/ +/* Derivative Func Node */ +/*************************************/ + +class VisualShaderNodeDerivativeFuncEmbedWidget : public QVBoxLayout { + Q_OBJECT + + public: + VisualShaderNodeDerivativeFuncEmbedWidget(const std::shared_ptr& node); + ~VisualShaderNodeDerivativeFuncEmbedWidget(); + + QComboBox* get_op_type_combo_box() const { return op_type_combo_box; } + QComboBox* get_function_combo_box() const { return function_combo_box; } + QComboBox* get_precision_combo_box() const { return precision_combo_box; } + + private Q_SLOTS: + void on_op_type_current_index_changed(const int& index); + void on_function_current_index_changed(const int& index); + void on_precision_current_index_changed(const int& index); + + private: + std::shared_ptr node; + + QComboBox* op_type_combo_box; + QComboBox* function_combo_box; + QComboBox* precision_combo_box; +}; + /*************************************/ /* Value Noise Node */ /*************************************/ @@ -1405,4 +1433,76 @@ class VisualShaderNodeVoronoiNoiseCellDensityEmbedWidget : public QLineEdit { std::shared_ptr node; }; +/*************************************/ +/* Logic */ +/*************************************/ + +/*************************************/ +/* Compare Node */ +/*************************************/ + +class VisualShaderNodeCompareEmbedWidget : public QVBoxLayout { + Q_OBJECT + + public: + VisualShaderNodeCompareEmbedWidget(const std::shared_ptr& node); + ~VisualShaderNodeCompareEmbedWidget(); + + QComboBox* get_comparison_type_combo_box() const { return comparison_type_combo_box; } + QComboBox* get_func_combo_box() const { return func_combo_box; } + QComboBox* get_condition_combo_box() const { return condition_combo_box; } + + private Q_SLOTS: + void on_comparison_type_current_index_changed(const int& index); + void on_func_current_index_changed(const int& index); + void on_condition_current_index_changed(const int& index); + + private: + std::shared_ptr node; + + QComboBox* comparison_type_combo_box; + QComboBox* func_combo_box; + QComboBox* condition_combo_box; +}; + +/*************************************/ +/* Switch Node */ +/*************************************/ + +class VisualShaderNodeSwitchEmbedWidget : public QComboBox { + Q_OBJECT + + public: + VisualShaderNodeSwitchEmbedWidget(const std::shared_ptr& node); + ~VisualShaderNodeSwitchEmbedWidget(); + + void set_current_index(const int& index) { this->setCurrentIndex(index); } + + private Q_SLOTS: + void on_current_index_changed(const int& index); + + private: + std::shared_ptr node; +}; + +/*************************************/ +/* Is Node */ +/*************************************/ + +class VisualShaderNodeIsEmbedWidget : public QComboBox { + Q_OBJECT + + public: + VisualShaderNodeIsEmbedWidget(const std::shared_ptr& node); + ~VisualShaderNodeIsEmbedWidget(); + + void set_current_index(const int& index) { this->setCurrentIndex(index); } + + private Q_SLOTS: + void on_current_index_changed(const int& index); + + private: + std::shared_ptr node; +}; + #endif // ENIGMA_VISUAL_SHADER_EDITOR_H From d9faf00c82bee88d1a32691945dcf9b09266240c Mon Sep 17 00:00:00 2001 From: Saif Kandil <74428638+k0T0z@users.noreply.github.com> Date: Fri, 4 Oct 2024 20:41:40 +0300 Subject: [PATCH 52/56] few improvements --- Editors/VisualShaderEditor.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Editors/VisualShaderEditor.cpp b/Editors/VisualShaderEditor.cpp index bf1d22433..a1f13fbcd 100644 --- a/Editors/VisualShaderEditor.cpp +++ b/Editors/VisualShaderEditor.cpp @@ -788,7 +788,7 @@ void ShaderPreviewerWidget::init_buffers() { } float vertices[] = { - // coordinates // texCoords + // coordinates // frag coords -1.0f, 1.0f, 0.0f, 1.0f, -1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, @@ -818,20 +818,20 @@ void ShaderPreviewerWidget::update_shader_program() { const char* vertex_shader_source = R"( #version 330 core layout(location = 0) in vec2 aPos; - layout(location = 1) in vec2 aTexCoord; + layout(location = 1) in vec2 aFragCoord; - out vec2 TexCoord; + out vec2 FragCoord; void main() { gl_Position = vec4(aPos, 0.0, 1.0); - TexCoord = aTexCoord; + FragCoord = aFragCoord; } )"; std::string fragment_shader_source {code.empty() ? R"( #version 330 core out vec4 FragColor; - in vec2 TexCoord; + in vec2 FragCoord; uniform float uTime; From f9acab26ae715a660046ac9a41a5a9bebdbe2ce3 Mon Sep 17 00:00:00 2001 From: Saif Kandil <74428638+k0T0z@users.noreply.github.com> Date: Fri, 4 Oct 2024 23:47:02 +0300 Subject: [PATCH 53/56] fixed a corner case where the ports are lost when changing the operation type that requires changing the number of ports --- Editors/VisualShaderEditor.cpp | 35 +++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/Editors/VisualShaderEditor.cpp b/Editors/VisualShaderEditor.cpp index a1f13fbcd..0036a23ec 100644 --- a/Editors/VisualShaderEditor.cpp +++ b/Editors/VisualShaderEditor.cpp @@ -1869,17 +1869,38 @@ VisualShaderOutputPortGraphicsObject* VisualShaderNodeGraphicsObject::get_output } void VisualShaderNodeGraphicsObject::on_node_update_requested() { - for (auto& [p_index, i_port] : in_port_graphics_objects) { - Q_EMIT scene_item_remove_requested(i_port); + // Here if the number of ports changed, for example, changing the operation type in a decompose node, + // we need to remove any extra ports that are not needed anymore. Don't forget to remove the connections + // as well. + if (in_port_graphics_objects.size() > node->get_input_port_count()) { + int start_index{node->get_input_port_count()}; + int size{(int)in_port_graphics_objects.size() - start_index}; + for (int i{0}; i < size; ++i) { + VisualShaderInputPortGraphicsObject* i_port{in_port_graphics_objects.at(start_index)}; + in_port_graphics_objects.erase(start_index); + if (i_port->is_connected()) { + Q_EMIT scene_item_remove_requested(i_port->get_connection_graphics_object()); + } + Q_EMIT scene_item_remove_requested(i_port); + } } - for (auto& [p_index, o_port] : out_port_graphics_objects) { - Q_EMIT scene_item_remove_requested(o_port); + if (out_port_graphics_objects.size() > node->get_output_port_count()) { + int start_index{node->get_output_port_count()}; + int size{(int)out_port_graphics_objects.size() - start_index}; + for (int i{0}; i < size; ++i) { + VisualShaderOutputPortGraphicsObject* o_port{out_port_graphics_objects.at(start_index)}; + out_port_graphics_objects.erase(start_index); + if (o_port->is_connected()) { + std::vector c_os{o_port->get_connection_graphics_objects()}; + for (VisualShaderConnectionGraphicsObject* c_o : c_os) { + Q_EMIT scene_item_remove_requested(c_o); + } + } + Q_EMIT scene_item_remove_requested(o_port); + } } - in_port_graphics_objects.clear(); - out_port_graphics_objects.clear(); - update(); Q_EMIT scene_update_requested(); } From c68194768d3c8a1fbb7fae4c9681ff7779c4da45 Mon Sep 17 00:00:00 2001 From: Saif Kandil <74428638+k0T0z@users.noreply.github.com> Date: Sat, 5 Oct 2024 00:04:02 +0300 Subject: [PATCH 54/56] Fixed another ... if i continue talking, i am gonna lose it --- Editors/VisualShaderEditor.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Editors/VisualShaderEditor.cpp b/Editors/VisualShaderEditor.cpp index 0036a23ec..06f2bb7ae 100644 --- a/Editors/VisualShaderEditor.cpp +++ b/Editors/VisualShaderEditor.cpp @@ -1882,6 +1882,7 @@ void VisualShaderNodeGraphicsObject::on_node_update_requested() { Q_EMIT scene_item_remove_requested(i_port->get_connection_graphics_object()); } Q_EMIT scene_item_remove_requested(i_port); + start_index++; } } @@ -1898,6 +1899,7 @@ void VisualShaderNodeGraphicsObject::on_node_update_requested() { } } Q_EMIT scene_item_remove_requested(o_port); + start_index++; } } From f4420108eb46124c3782229832e50e664ce5c7be Mon Sep 17 00:00:00 2001 From: Saif Kandil <74428638+k0T0z@users.noreply.github.com> Date: Sat, 5 Oct 2024 14:48:27 +0300 Subject: [PATCH 55/56] few improvements --- Editors/VisualShaderEditor.cpp | 73 +++++++++++++++++++++------------- Editors/VisualShaderEditor.h | 8 +++- 2 files changed, 51 insertions(+), 30 deletions(-) diff --git a/Editors/VisualShaderEditor.cpp b/Editors/VisualShaderEditor.cpp index 06f2bb7ae..fed7f17a9 100644 --- a/Editors/VisualShaderEditor.cpp +++ b/Editors/VisualShaderEditor.cpp @@ -1017,7 +1017,8 @@ bool VisualShaderGraphicsScene::add_node(const int& n_id, const std::shared_ptr< QObject::connect(n_o, &VisualShaderNodeGraphicsObject::out_port_dropped, this, &VisualShaderGraphicsScene::on_port_dropped); QObject::connect(n_o, &VisualShaderNodeGraphicsObject::scene_update_requested, this, &VisualShaderGraphicsScene::on_scene_update_requested); - QObject::connect(n_o, &VisualShaderNodeGraphicsObject::scene_item_remove_requested, this, &VisualShaderGraphicsScene::on_scene_item_remove_requested); + QObject::connect(n_o, &VisualShaderNodeGraphicsObject::in_port_remove_requested, this, &VisualShaderGraphicsScene::on_in_port_remove_requested); + QObject::connect(n_o, &VisualShaderNodeGraphicsObject::out_port_remove_requested, this, &VisualShaderGraphicsScene::on_out_port_remove_requested); if (n_id != (int)VisualShader::NODE_ID_OUTPUT) { VisualShaderNodeEmbedWidget* embed_widget {new VisualShaderNodeEmbedWidget(n)}; @@ -1117,8 +1118,9 @@ bool VisualShaderGraphicsScene::delete_node(const int& n_id) { } // Remove the node from the scene + // TODO: Why if we exchange the order of these two lines, the program crashes? this->node_graphics_objects.erase(n_id); - on_scene_item_remove_requested(n_o); + remove_item(n_o); return true; } @@ -1144,9 +1146,30 @@ void VisualShaderGraphicsScene::on_scene_update_requested() { update(); } -void VisualShaderGraphicsScene::on_scene_item_remove_requested(QGraphicsItem* item) { - removeItem(item); - delete item; +void VisualShaderGraphicsScene::on_in_port_remove_requested(VisualShaderInputPortGraphicsObject* in_port) { + if (in_port->is_connected()) { + VisualShaderConnectionGraphicsObject* c_o{in_port->get_connection_graphics_object()}; + delete_connection(c_o->get_from_node_id(), + c_o->get_from_port_index(), + c_o->get_to_node_id(), + c_o->get_to_port_index()); + } + + remove_item(in_port); +} + +void VisualShaderGraphicsScene::on_out_port_remove_requested(VisualShaderOutputPortGraphicsObject* out_port) { + if (out_port->is_connected()) { + std::vector c_os{out_port->get_connection_graphics_objects()}; + for (VisualShaderConnectionGraphicsObject* c_o : c_os) { + delete_connection(c_o->get_from_node_id(), + c_o->get_from_port_index(), + c_o->get_to_node_id(), + c_o->get_to_port_index()); + } + } + + remove_item(out_port); } bool VisualShaderGraphicsScene::add_connection(const int& from_node_id, const int& from_port_index, @@ -1247,7 +1270,7 @@ bool VisualShaderGraphicsScene::delete_connection(const int& from_node_id, const if (this->temporary_connection_graphics_object) { from_o_port->detach_connection(this->temporary_connection_graphics_object); - on_scene_item_remove_requested(this->temporary_connection_graphics_object); + remove_item(this->temporary_connection_graphics_object); this->temporary_connection_graphics_object = nullptr; return true; } @@ -1280,7 +1303,7 @@ bool VisualShaderGraphicsScene::delete_connection(const int& from_node_id, const to_i_port->detach_connection(); from_o_port->detach_connection(c_o); - on_scene_item_remove_requested(c_o); + remove_item(c_o); on_update_shader_previewer_widgets_requested(); @@ -1500,6 +1523,11 @@ void VisualShaderGraphicsScene::on_port_dropped(QGraphicsObject* port, const QPo } } +void VisualShaderGraphicsScene::remove_item(QGraphicsItem* item) { + removeItem(item); + delete item; +} + /**********************************************************************/ /**********************************************************************/ /**********************************************************************/ @@ -1873,33 +1901,22 @@ void VisualShaderNodeGraphicsObject::on_node_update_requested() { // we need to remove any extra ports that are not needed anymore. Don't forget to remove the connections // as well. if (in_port_graphics_objects.size() > node->get_input_port_count()) { - int start_index{node->get_input_port_count()}; - int size{(int)in_port_graphics_objects.size() - start_index}; + int p_index{node->get_input_port_count()}; + int size{(int)in_port_graphics_objects.size() - p_index}; for (int i{0}; i < size; ++i) { - VisualShaderInputPortGraphicsObject* i_port{in_port_graphics_objects.at(start_index)}; - in_port_graphics_objects.erase(start_index); - if (i_port->is_connected()) { - Q_EMIT scene_item_remove_requested(i_port->get_connection_graphics_object()); - } - Q_EMIT scene_item_remove_requested(i_port); - start_index++; + Q_EMIT in_port_remove_requested(in_port_graphics_objects.at(p_index)); + in_port_graphics_objects.erase(p_index); + p_index++; } } if (out_port_graphics_objects.size() > node->get_output_port_count()) { - int start_index{node->get_output_port_count()}; - int size{(int)out_port_graphics_objects.size() - start_index}; + int p_index{node->get_output_port_count()}; + int size{(int)out_port_graphics_objects.size() - p_index}; for (int i{0}; i < size; ++i) { - VisualShaderOutputPortGraphicsObject* o_port{out_port_graphics_objects.at(start_index)}; - out_port_graphics_objects.erase(start_index); - if (o_port->is_connected()) { - std::vector c_os{o_port->get_connection_graphics_objects()}; - for (VisualShaderConnectionGraphicsObject* c_o : c_os) { - Q_EMIT scene_item_remove_requested(c_o); - } - } - Q_EMIT scene_item_remove_requested(o_port); - start_index++; + Q_EMIT out_port_remove_requested(out_port_graphics_objects.at(p_index)); + out_port_graphics_objects.erase(p_index); + p_index++; } } diff --git a/Editors/VisualShaderEditor.h b/Editors/VisualShaderEditor.h index 5adc5a1f6..9b03c17b6 100644 --- a/Editors/VisualShaderEditor.h +++ b/Editors/VisualShaderEditor.h @@ -450,7 +450,8 @@ class VisualShaderGraphicsScene : public QGraphicsScene { void on_scene_update_requested(); - void on_scene_item_remove_requested(QGraphicsItem* item); + void on_in_port_remove_requested(VisualShaderInputPortGraphicsObject* in_port); + void on_out_port_remove_requested(VisualShaderOutputPortGraphicsObject* out_port); private: VisualShader* vs; @@ -459,6 +460,8 @@ class VisualShaderGraphicsScene : public QGraphicsScene { std::unordered_map node_graphics_objects; VisualShaderConnectionGraphicsObject* temporary_connection_graphics_object; + + void remove_item(QGraphicsItem* item); }; /**********************************************************************/ @@ -618,7 +621,8 @@ class VisualShaderNodeGraphicsObject : public QGraphicsObject { void scene_update_requested(); - void scene_item_remove_requested(QGraphicsItem* item); + void in_port_remove_requested(VisualShaderInputPortGraphicsObject* in_port); + void out_port_remove_requested(VisualShaderOutputPortGraphicsObject* out_port); private Q_SLOTS: /** From 598f996da7af62fec4c60b0ea4a0ed8e4ea67165 Mon Sep 17 00:00:00 2001 From: Saif Kandil <74428638+k0T0z@users.noreply.github.com> Date: Sat, 5 Oct 2024 15:10:35 +0300 Subject: [PATCH 56/56] format code --- Editors/VisualShaderEditor.cpp | 782 ++++++++++------------ Editors/VisualShaderEditor.h | 152 +++-- Tests/Editors/VisualShaderEditorTests.cpp | 94 +-- Tests/Editors/VisualShaderEditorTests.h | 4 +- 4 files changed, 469 insertions(+), 563 deletions(-) diff --git a/Editors/VisualShaderEditor.cpp b/Editors/VisualShaderEditor.cpp index fed7f17a9..39120004d 100644 --- a/Editors/VisualShaderEditor.cpp +++ b/Editors/VisualShaderEditor.cpp @@ -42,7 +42,7 @@ /**********************************************************************/ VisualShaderEditor::VisualShaderEditor(QWidget* parent) - : BaseEditor(parent), + : BaseEditor(parent), visual_shader(nullptr), layout(nullptr), side_widget(nullptr), @@ -189,7 +189,7 @@ void VisualShaderEditor::init() { view->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom view->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); - VisualShaderEditor::init_graph(); // Must be called after a scene and a view are created. + VisualShaderEditor::init_graph(); // Must be called after a scene and a view are created. scene_layer_layout->addWidget(view); @@ -233,7 +233,8 @@ void VisualShaderEditor::init() { preview_shader_button->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom preview_shader_button->setToolTip("Preview the expected generated shader code"); menu_bar->addWidget(preview_shader_button); - QObject::connect(preview_shader_button, &QPushButton::pressed, this, &VisualShaderEditor::on_preview_shader_button_pressed); + QObject::connect(preview_shader_button, &QPushButton::pressed, this, + &VisualShaderEditor::on_preview_shader_button_pressed); zoom_in_button = new QPushButton("Zoom In", scene_layer); zoom_in_button->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); @@ -289,7 +290,7 @@ void VisualShaderEditor::init() { code_previewer_layout = new QVBoxLayout(code_previewer_dialog); code_previewer_layout->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom - code_previewer_layout->setSpacing(0); // Adjust spacing as needed + code_previewer_layout->setSpacing(0); // Adjust spacing as needed code_previewer_layout->setAlignment(Qt::AlignTop | Qt::AlignLeft); code_previewer_layout->setSizeConstraint(QLayout::SetMinimumSize); @@ -412,8 +413,12 @@ const VisualShaderEditor::CreateNodeDialogNodesTreeItem VisualShaderEditor::crea {"UIntFunc", "Functions/Scalar", "VisualShaderNodeUIntFunc", "Unsigned integer function."}, {"VectorFunc", "Functions/Vector", "VisualShaderNodeVectorFunc", "Vector function."}, {"DerivativeFunc", "Functions/Others", "VisualShaderNodeDerivativeFunc", "Derivative function."}, - {"Step", "Functions/Others", "VisualShaderNodeStep", "Step function( scalar(edge), scalar(x) ).\n\nReturns 0.0 if 'x' is smaller than 'edge' and otherwise 1.0."}, - {"SmoothStep", "Functions/Others", "VisualShaderNodeSmoothStep", "SmoothStep function( scalar(edge0), scalar(edge1), scalar(x) ).\n\nReturns 0.0 if 'x' is smaller than 'edge0' and 1.0 if x is larger than 'edge1'. Otherwise the return value is interpolated between 0.0 and 1.0 using Hermite polynomials."}, + {"Step", "Functions/Others", "VisualShaderNodeStep", + "Step function( scalar(edge), scalar(x) ).\n\nReturns 0.0 if 'x' is smaller than 'edge' and otherwise 1.0."}, + {"SmoothStep", "Functions/Others", "VisualShaderNodeSmoothStep", + "SmoothStep function( scalar(edge0), scalar(edge1), scalar(x) ).\n\nReturns 0.0 if 'x' is smaller than 'edge0' " + "and 1.0 if x is larger than 'edge1'. Otherwise the return value is interpolated between 0.0 and 1.0 using " + "Hermite polynomials."}, {"Dot", "Functions/Others", "VisualShaderNodeDotProduct", "Calculates the dot product of two vectors."}, // Operators @@ -430,8 +435,14 @@ const VisualShaderEditor::CreateNodeDialogNodesTreeItem VisualShaderEditor::crea {"ValueNoise", "Procedural/Noise", "VisualShaderNodeValueNoise", "Generates a simple, or Value, noise based on input 'UV'. The scale of the generated noise is controlled by input " "'Scale'."}, - {"PerlinNoise", "Procedural/Noise", "VisualShaderNodePerlinNoise", "Generates a gradient, or Perlin, noise based on input 'UV'. The scale of the generated noise is controlled by input 'Scale'."}, - {"VoronoiNoise", "Procedural/Noise", "VisualShaderNodeVoronoiNoise", "Generates a Voronoi, or Worley, noise based on input 'UV'. Voronoi noise is generated by calculating distances between a pixel and a lattice of points. By offsetting these points by a pseudo-random number, controlled by input 'Angle Offset', a cluster of cells can be generated. The scale of these cells, and the resulting noise, is controlled by input 'Cell Density'. The output 'Cells' contains the raw cell data."}, + {"PerlinNoise", "Procedural/Noise", "VisualShaderNodePerlinNoise", + "Generates a gradient, or Perlin, noise based on input 'UV'. The scale of the generated noise is controlled by " + "input 'Scale'."}, + {"VoronoiNoise", "Procedural/Noise", "VisualShaderNodeVoronoiNoise", + "Generates a Voronoi, or Worley, noise based on input 'UV'. Voronoi noise is generated by calculating distances " + "between a pixel and a lattice of points. By offsetting these points by a pseudo-random number, controlled by " + "input 'Angle Offset', a cluster of cells can be generated. The scale of these cells, and the resulting noise, is " + "controlled by input 'Cell Density'. The output 'Cells' contains the raw cell data."}, // Utility @@ -693,7 +704,7 @@ void ShaderPreviewerWidget::set_code(const std::string& new_code) { } void ShaderPreviewerWidget::initializeGL() { - QOpenGLFunctions_4_3_Core* f {QOpenGLContext::currentContext()->versionFunctions()}; + QOpenGLFunctions_4_3_Core* f{QOpenGLContext::currentContext()->versionFunctions()}; if (!f) { qWarning() << "Failed to get OpenGL 4.3 functions"; @@ -705,7 +716,7 @@ void ShaderPreviewerWidget::initializeGL() { return; } - f->glClearColor(0.0f, 0.0f, 0.0f, 1.0f); // Black background + f->glClearColor(0.0f, 0.0f, 0.0f, 1.0f); // Black background init_buffers(); init_shaders(); @@ -715,7 +726,7 @@ void ShaderPreviewerWidget::initializeGL() { } void ShaderPreviewerWidget::resizeGL(int w, int h) { - QOpenGLFunctions_4_3_Core* f {QOpenGLContext::currentContext()->versionFunctions()}; + QOpenGLFunctions_4_3_Core* f{QOpenGLContext::currentContext()->versionFunctions()}; if (!f) { qWarning() << "Failed to get OpenGL 4.3 functions"; @@ -731,7 +742,7 @@ void ShaderPreviewerWidget::paintGL() { // in returning (no painting happens). if (!isValid()) return; - QOpenGLFunctions_4_3_Core* f {QOpenGLContext::currentContext()->versionFunctions()}; + QOpenGLFunctions_4_3_Core* f{QOpenGLContext::currentContext()->versionFunctions()}; if (!f) { qWarning() << "Failed to get OpenGL 4.3 functions"; @@ -743,11 +754,11 @@ void ShaderPreviewerWidget::paintGL() { } if (!shader_program || !shader_program->isLinked()) { - qWarning() << "Shader program is not linked."; - return; + qWarning() << "Shader program is not linked."; + return; } - float time_value {timer.elapsed() * 0.001f}; + float time_value{timer.elapsed() * 0.001f}; f->glClearColor(0.0f, 0.0f, 0.0f, 1.0f); f->glClear(GL_COLOR_BUFFER_BIT); @@ -768,19 +779,19 @@ void ShaderPreviewerWidget::paintGL() { void ShaderPreviewerWidget::cleanup() { makeCurrent(); - QOpenGLFunctions_4_3_Core* f {QOpenGLContext::currentContext()->versionFunctions()}; + QOpenGLFunctions_4_3_Core* f{QOpenGLContext::currentContext()->versionFunctions()}; if (!f) { qWarning() << "Failed to get OpenGL 4.3 functions"; return; } - + f->glDeleteVertexArrays(1, &VAO); f->glDeleteBuffers(1, &VBO); } void ShaderPreviewerWidget::init_buffers() { - QOpenGLFunctions_4_3_Core* f {QOpenGLContext::currentContext()->versionFunctions()}; + QOpenGLFunctions_4_3_Core* f{QOpenGLContext::currentContext()->versionFunctions()}; if (!f) { qWarning() << "Failed to get OpenGL 4.3 functions"; @@ -788,12 +799,8 @@ void ShaderPreviewerWidget::init_buffers() { } float vertices[] = { - // coordinates // frag coords - -1.0f, 1.0f, 0.0f, 1.0f, - -1.0f, -1.0f, 0.0f, 0.0f, - 1.0f, 1.0f, 1.0f, 1.0f, - 1.0f, -1.0f, 1.0f, 0.0f - }; + // coordinates // frag coords + -1.0f, 1.0f, 0.0f, 1.0f, -1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 0.0f}; f->glGenVertexArrays(1, &VAO); f->glGenBuffers(1, &VBO); @@ -828,7 +835,7 @@ void ShaderPreviewerWidget::update_shader_program() { } )"; - std::string fragment_shader_source {code.empty() ? R"( + std::string fragment_shader_source{code.empty() ? R"( #version 330 core out vec4 FragColor; in vec2 FragCoord; @@ -838,7 +845,8 @@ void ShaderPreviewerWidget::update_shader_program() { void main() { FragColor = vec4(0.0, 0.0, 0.0, 1.0); } - )" : "#version 330 core\n\n" + code}; + )" + : "#version 330 core\n\n" + code}; if (!shader_program->addShaderFromSourceCode(QOpenGLShader::Vertex, vertex_shader_source)) { qWarning() << "Vertex shader compilation failed:" << shader_program->log(); @@ -855,9 +863,7 @@ void ShaderPreviewerWidget::update_shader_program() { shader_needs_update = false; } -void ShaderPreviewerWidget::init_shaders() { - update_shader_program(); -} +void ShaderPreviewerWidget::init_shaders() { update_shader_program(); } void ShaderPreviewerWidget::showEvent(QShowEvent* event) { QOpenGLWidget::showEvent(event); @@ -976,7 +982,8 @@ bool VisualShaderGraphicsScene::add_node(const std::string& type, const QPointF& return VisualShaderGraphicsScene::add_node(n_id, n, coordinate); } -bool VisualShaderGraphicsScene::add_node(const int& n_id, const std::shared_ptr& n, const QPointF& coordinate) { +bool VisualShaderGraphicsScene::add_node(const int& n_id, const std::shared_ptr& n, + const QPointF& coordinate) { // Make sure the node doesn't already exist, we don't want to overwrite a node. if (node_graphics_objects.find(n_id) != node_graphics_objects.end()) { return false; @@ -1004,49 +1011,56 @@ bool VisualShaderGraphicsScene::add_node(const int& n_id, const std::shared_ptr< vs->get_node_coordinate(n_id).y < view->get_y() || vs->get_node_coordinate(n_id).y > view->get_y() + view->get_height()) { std::cout << "Node is out of view bounds" << std::endl; - } + } VisualShaderNodeGraphicsObject* n_o{new VisualShaderNodeGraphicsObject(n_id, coordinate, n)}; QObject::connect(n_o, &VisualShaderNodeGraphicsObject::node_moved, this, &VisualShaderGraphicsScene::on_node_moved); - QObject::connect(n_o, &VisualShaderNodeGraphicsObject::in_port_pressed, this, &VisualShaderGraphicsScene::on_port_pressed); - QObject::connect(n_o, &VisualShaderNodeGraphicsObject::in_port_dragged, this, &VisualShaderGraphicsScene::on_port_dragged); - QObject::connect(n_o, &VisualShaderNodeGraphicsObject::in_port_dropped, this, &VisualShaderGraphicsScene::on_port_dropped); - QObject::connect(n_o, &VisualShaderNodeGraphicsObject::out_port_pressed, this, &VisualShaderGraphicsScene::on_port_pressed); - QObject::connect(n_o, &VisualShaderNodeGraphicsObject::out_port_dragged, this, &VisualShaderGraphicsScene::on_port_dragged); - QObject::connect(n_o, &VisualShaderNodeGraphicsObject::out_port_dropped, this, &VisualShaderGraphicsScene::on_port_dropped); - - QObject::connect(n_o, &VisualShaderNodeGraphicsObject::scene_update_requested, this, &VisualShaderGraphicsScene::on_scene_update_requested); - QObject::connect(n_o, &VisualShaderNodeGraphicsObject::in_port_remove_requested, this, &VisualShaderGraphicsScene::on_in_port_remove_requested); - QObject::connect(n_o, &VisualShaderNodeGraphicsObject::out_port_remove_requested, this, &VisualShaderGraphicsScene::on_out_port_remove_requested); + QObject::connect(n_o, &VisualShaderNodeGraphicsObject::in_port_pressed, this, + &VisualShaderGraphicsScene::on_port_pressed); + QObject::connect(n_o, &VisualShaderNodeGraphicsObject::in_port_dragged, this, + &VisualShaderGraphicsScene::on_port_dragged); + QObject::connect(n_o, &VisualShaderNodeGraphicsObject::in_port_dropped, this, + &VisualShaderGraphicsScene::on_port_dropped); + QObject::connect(n_o, &VisualShaderNodeGraphicsObject::out_port_pressed, this, + &VisualShaderGraphicsScene::on_port_pressed); + QObject::connect(n_o, &VisualShaderNodeGraphicsObject::out_port_dragged, this, + &VisualShaderGraphicsScene::on_port_dragged); + QObject::connect(n_o, &VisualShaderNodeGraphicsObject::out_port_dropped, this, + &VisualShaderGraphicsScene::on_port_dropped); + + QObject::connect(n_o, &VisualShaderNodeGraphicsObject::scene_update_requested, this, + &VisualShaderGraphicsScene::on_scene_update_requested); + QObject::connect(n_o, &VisualShaderNodeGraphicsObject::in_port_remove_requested, this, + &VisualShaderGraphicsScene::on_in_port_remove_requested); + QObject::connect(n_o, &VisualShaderNodeGraphicsObject::out_port_remove_requested, this, + &VisualShaderGraphicsScene::on_out_port_remove_requested); if (n_id != (int)VisualShader::NODE_ID_OUTPUT) { - VisualShaderNodeEmbedWidget* embed_widget {new VisualShaderNodeEmbedWidget(n)}; + VisualShaderNodeEmbedWidget* embed_widget{new VisualShaderNodeEmbedWidget(n)}; QGraphicsProxyWidget* embed_widget_proxy{new QGraphicsProxyWidget(n_o)}; embed_widget_proxy->setWidget(embed_widget); n_o->set_embed_widget(embed_widget); - QObject::connect(embed_widget, - &VisualShaderNodeEmbedWidget::shader_preview_update_requested, - this, + QObject::connect(embed_widget, &VisualShaderNodeEmbedWidget::shader_preview_update_requested, this, &VisualShaderGraphicsScene::on_update_shader_previewer_widgets_requested); - QObject::connect(embed_widget, - &VisualShaderNodeEmbedWidget::node_update_requested, - n_o, + QObject::connect(embed_widget, &VisualShaderNodeEmbedWidget::node_update_requested, n_o, &VisualShaderNodeGraphicsObject::on_node_update_requested); // Send the shader previewer widget embed_widget->set_shader_previewer_widget(n_o->get_shader_previewer_widget()); } - if (ShaderPreviewerWidget* spw{n_o->get_shader_previewer_widget()}) { - QObject::connect(spw, &ShaderPreviewerWidget::scene_update_requested, this, &VisualShaderGraphicsScene::on_scene_update_requested); + if (ShaderPreviewerWidget * spw{n_o->get_shader_previewer_widget()}) { + QObject::connect(spw, &ShaderPreviewerWidget::scene_update_requested, this, + &VisualShaderGraphicsScene::on_scene_update_requested); } - QObject::connect(n_o, &VisualShaderNodeGraphicsObject::node_deleted, this, &VisualShaderGraphicsScene::on_node_deleted); + QObject::connect(n_o, &VisualShaderNodeGraphicsObject::node_deleted, this, + &VisualShaderGraphicsScene::on_node_deleted); node_graphics_objects[n_id] = n_o; - + addItem(n_o); return true; @@ -1136,22 +1150,18 @@ void VisualShaderGraphicsScene::on_update_shader_previewer_widgets_requested() { continue; } - spw->set_code(vs->generate_preview_shader(n_id, 0)); // 0 is the output port index + spw->set_code(vs->generate_preview_shader(n_id, 0)); // 0 is the output port index } on_scene_update_requested(); } -void VisualShaderGraphicsScene::on_scene_update_requested() { - update(); -} +void VisualShaderGraphicsScene::on_scene_update_requested() { update(); } void VisualShaderGraphicsScene::on_in_port_remove_requested(VisualShaderInputPortGraphicsObject* in_port) { if (in_port->is_connected()) { VisualShaderConnectionGraphicsObject* c_o{in_port->get_connection_graphics_object()}; - delete_connection(c_o->get_from_node_id(), - c_o->get_from_port_index(), - c_o->get_to_node_id(), + delete_connection(c_o->get_from_node_id(), c_o->get_from_port_index(), c_o->get_to_node_id(), c_o->get_to_port_index()); } @@ -1162,10 +1172,8 @@ void VisualShaderGraphicsScene::on_out_port_remove_requested(VisualShaderOutputP if (out_port->is_connected()) { std::vector c_os{out_port->get_connection_graphics_objects()}; for (VisualShaderConnectionGraphicsObject* c_o : c_os) { - delete_connection(c_o->get_from_node_id(), - c_o->get_from_port_index(), - c_o->get_to_node_id(), - c_o->get_to_port_index()); + delete_connection(c_o->get_from_node_id(), c_o->get_from_port_index(), c_o->get_to_node_id(), + c_o->get_to_port_index()); } } @@ -1274,7 +1282,7 @@ bool VisualShaderGraphicsScene::delete_connection(const int& from_node_id, const this->temporary_connection_graphics_object = nullptr; return true; } - + // If we have a complete connection, then we can disconnect the nodes if (to_node_id != (int)VisualShader::NODE_ID_INVALID && to_port_index != (int)VisualShader::PORT_INDEX_INVALID) { VisualShaderConnectionGraphicsObject* c_o{from_o_port->get_connection_graphics_object(to_node_id, to_port_index)}; @@ -1282,7 +1290,7 @@ bool VisualShaderGraphicsScene::delete_connection(const int& from_node_id, const if (!c_o) { return false; } - + VisualShaderNodeGraphicsObject* to_n_o{this->get_node_graphics_object(to_node_id)}; if (!to_n_o) { @@ -1469,7 +1477,7 @@ void VisualShaderGraphicsScene::on_port_dropped(QGraphicsObject* port, const QPo } if (!in_p_o) { - bool result{this->delete_connection(temporary_connection_graphics_object->get_from_node_id(), + bool result{this->delete_connection(temporary_connection_graphics_object->get_from_node_id(), temporary_connection_graphics_object->get_from_port_index())}; if (!result) { @@ -1479,13 +1487,12 @@ void VisualShaderGraphicsScene::on_port_dropped(QGraphicsObject* port, const QPo return; // Return because we dragging an input port and dropped on nothing } - bool result {add_connection(temporary_connection_graphics_object->get_from_node_id(), - temporary_connection_graphics_object->get_from_port_index(), - in_p_o->get_node_id(), - in_p_o->get_port_index())}; + bool result{add_connection(temporary_connection_graphics_object->get_from_node_id(), + temporary_connection_graphics_object->get_from_port_index(), in_p_o->get_node_id(), + in_p_o->get_port_index())}; if (!result) { - bool result{this->delete_connection(temporary_connection_graphics_object->get_from_node_id(), + bool result{this->delete_connection(temporary_connection_graphics_object->get_from_node_id(), temporary_connection_graphics_object->get_from_port_index())}; if (!result) { @@ -1499,7 +1506,7 @@ void VisualShaderGraphicsScene::on_port_dropped(QGraphicsObject* port, const QPo } if (!in_p_o) { - bool result{this->delete_connection(temporary_connection_graphics_object->get_from_node_id(), + bool result{this->delete_connection(temporary_connection_graphics_object->get_from_node_id(), temporary_connection_graphics_object->get_from_port_index())}; if (!result) { @@ -1508,11 +1515,12 @@ void VisualShaderGraphicsScene::on_port_dropped(QGraphicsObject* port, const QPo return; // Return because we dragging an output port and dropped on nothing } - - bool result{add_connection(o_port->get_node_id(), o_port->get_port_index(), in_p_o->get_node_id(), in_p_o->get_port_index())}; + + bool result{ + add_connection(o_port->get_node_id(), o_port->get_port_index(), in_p_o->get_node_id(), in_p_o->get_port_index())}; if (!result) { - bool result{this->delete_connection(temporary_connection_graphics_object->get_from_node_id(), + bool result{this->delete_connection(temporary_connection_graphics_object->get_from_node_id(), temporary_connection_graphics_object->get_from_port_index())}; if (!result) { @@ -1603,7 +1611,7 @@ VisualShaderGraphicsView::~VisualShaderGraphicsView() {} ////////////////////////////// void VisualShaderGraphicsView::on_create_node_action_triggered() { - VisualShaderEditor* editor {scene->get_editor()}; + VisualShaderEditor* editor{scene->get_editor()}; Q_EMIT editor->create_node_dialog_requested(this->last_context_menu_coordinate); } @@ -1684,7 +1692,7 @@ void VisualShaderGraphicsView::drawBackground(QPainter* painter, const QRectF& r } void VisualShaderGraphicsView::contextMenuEvent(QContextMenuEvent* event) { - QGraphicsItem* item {itemAt(event->pos())}; + QGraphicsItem* item{itemAt(event->pos())}; // If there is an item and this item is a node object, pass the event to the // children @@ -1815,23 +1823,23 @@ void VisualShaderGraphicsView::move_view_to_fit_items() { /**********************************************************************/ /**********************************************************************/ -VisualShaderNodeGraphicsObject::VisualShaderNodeGraphicsObject(const int& n_id, - const QPointF& coordinate, +VisualShaderNodeGraphicsObject::VisualShaderNodeGraphicsObject(const int& n_id, const QPointF& coordinate, const std::shared_ptr& node, - QGraphicsItem* parent) : QGraphicsObject(parent), - n_id(n_id), - coordinate(coordinate), - node(node), - context_menu(nullptr), - delete_node_action(nullptr), - rect_width(0.0f), - caption_rect_height(0.0f), - rect_height(0.0f), - rect_margin(0.0f), - rect_padding(0.0f), - embed_widget(nullptr), - matching_image_widget(nullptr), - shader_previewer_widget(nullptr) { + QGraphicsItem* parent) + : QGraphicsObject(parent), + n_id(n_id), + coordinate(coordinate), + node(node), + context_menu(nullptr), + delete_node_action(nullptr), + rect_width(0.0f), + caption_rect_height(0.0f), + rect_height(0.0f), + rect_margin(0.0f), + rect_padding(0.0f), + embed_widget(nullptr), + matching_image_widget(nullptr), + shader_previewer_widget(nullptr) { setFlag(QGraphicsItem::ItemDoesntPropagateOpacityToChildren, true); setFlag(QGraphicsItem::ItemIsFocusable, true); setFlag(QGraphicsItem::ItemIsMovable, true); @@ -1866,7 +1874,7 @@ VisualShaderNodeGraphicsObject::VisualShaderNodeGraphicsObject(const int& n_id, delete_node_action->setShortcutContext(Qt::ShortcutContext::WidgetShortcut); delete_node_action->setShortcut(QKeySequence(QKeySequence::Delete)); QObject::connect(delete_node_action, &QAction::triggered, this, - &VisualShaderNodeGraphicsObject::on_delete_node_action_triggered); + &VisualShaderNodeGraphicsObject::on_delete_node_action_triggered); context_menu->addAction(delete_node_action); } @@ -1874,9 +1882,7 @@ VisualShaderNodeGraphicsObject::~VisualShaderNodeGraphicsObject() { if (context_menu) delete context_menu; } -void VisualShaderNodeGraphicsObject::on_delete_node_action_triggered() { - Q_EMIT node_deleted(n_id); -} +void VisualShaderNodeGraphicsObject::on_delete_node_action_triggered() { Q_EMIT node_deleted(n_id); } VisualShaderInputPortGraphicsObject* VisualShaderNodeGraphicsObject::get_input_port_graphics_object( const int& p_index) const { @@ -1898,7 +1904,7 @@ VisualShaderOutputPortGraphicsObject* VisualShaderNodeGraphicsObject::get_output void VisualShaderNodeGraphicsObject::on_node_update_requested() { // Here if the number of ports changed, for example, changing the operation type in a decompose node, - // we need to remove any extra ports that are not needed anymore. Don't forget to remove the connections + // we need to remove any extra ports that are not needed anymore. Don't forget to remove the connections // as well. if (in_port_graphics_objects.size() > node->get_input_port_count()) { int p_index{node->get_input_port_count()}; @@ -1967,7 +1973,7 @@ QRectF VisualShaderNodeGraphicsObject::boundingRect() const { w = (float)fm.horizontalAdvance(p_n); } else { // If the port name is empty, use a default name - // This is because the horizontal advance of an empty string is 0 and + // This is because the horizontal advance of an empty string is 0 and // this will wrong the calculation of the rect width w = (float)fm.horizontalAdvance("Input"); } @@ -1992,7 +1998,7 @@ QRectF VisualShaderNodeGraphicsObject::boundingRect() const { w = (float)fm.horizontalAdvance(p_n); } else { // If the port name is empty, use a default name - // This is because the horizontal advance of an empty string is 0 and + // This is because the horizontal advance of an empty string is 0 and // this will wrong the calculation of the rect width w = (float)fm.horizontalAdvance("Output"); } @@ -2002,18 +2008,15 @@ QRectF VisualShaderNodeGraphicsObject::boundingRect() const { } } - float calculated_rect {max_in_p_width + embed_widget_width + max_out_p_width + embed_widget_h_padding * 2.0f}; + float calculated_rect{max_in_p_width + embed_widget_width + max_out_p_width + embed_widget_h_padding * 2.0f}; if (calculated_rect > rect_width) { rect_width = calculated_rect; } // Check the height - float calculated_height{caption_rect_height + - body_rect_header_height + - embed_widget->height() + - body_rect_footer_height + - embed_widget_v_padding * 2.0f}; + float calculated_height{caption_rect_height + body_rect_header_height + embed_widget->height() + + body_rect_footer_height + embed_widget_v_padding * 2.0f}; if (calculated_height > rect_height) { rect_height = calculated_height; @@ -2077,7 +2080,8 @@ void VisualShaderNodeGraphicsObject::paint(QPainter* painter, const QStyleOption } else { // Draw Shader Previewer Widget float shader_previewer_widget_x{(float)r.x()}; - float shader_previewer_widget_y{(float)r.y() + (float)r.height() + spacing_between_current_node_and_shader_previewer}; + float shader_previewer_widget_y{(float)r.y() + (float)r.height() + + spacing_between_current_node_and_shader_previewer}; shader_previewer_widget->setGeometry(shader_previewer_widget_x, shader_previewer_widget_y, r.width(), r.width()); } @@ -2184,9 +2188,12 @@ void VisualShaderNodeGraphicsObject::paint(QPainter* painter, const QStyleOption in_port_graphics_objects[i] = p_o; // Connect the signals - QObject::connect(p_o, &VisualShaderInputPortGraphicsObject::port_pressed, this, &VisualShaderNodeGraphicsObject::on_in_port_pressed); - QObject::connect(p_o, &VisualShaderInputPortGraphicsObject::port_dragged, this, &VisualShaderNodeGraphicsObject::on_in_port_dragged); - QObject::connect(p_o, &VisualShaderInputPortGraphicsObject::port_dropped, this, &VisualShaderNodeGraphicsObject::on_in_port_dropped); + QObject::connect(p_o, &VisualShaderInputPortGraphicsObject::port_pressed, this, + &VisualShaderNodeGraphicsObject::on_in_port_pressed); + QObject::connect(p_o, &VisualShaderInputPortGraphicsObject::port_dragged, this, + &VisualShaderNodeGraphicsObject::on_in_port_dragged); + QObject::connect(p_o, &VisualShaderInputPortGraphicsObject::port_dropped, this, + &VisualShaderNodeGraphicsObject::on_in_port_dropped); } } @@ -2244,9 +2251,12 @@ void VisualShaderNodeGraphicsObject::paint(QPainter* painter, const QStyleOption out_port_graphics_objects[i] = p_o; // Connect the signals - QObject::connect(p_o, &VisualShaderOutputPortGraphicsObject::port_pressed, this, &VisualShaderNodeGraphicsObject::on_out_port_pressed); - QObject::connect(p_o, &VisualShaderOutputPortGraphicsObject::port_dragged, this, &VisualShaderNodeGraphicsObject::on_out_port_dragged); - QObject::connect(p_o, &VisualShaderOutputPortGraphicsObject::port_dropped, this, &VisualShaderNodeGraphicsObject::on_out_port_dropped); + QObject::connect(p_o, &VisualShaderOutputPortGraphicsObject::port_pressed, this, + &VisualShaderNodeGraphicsObject::on_out_port_pressed); + QObject::connect(p_o, &VisualShaderOutputPortGraphicsObject::port_dragged, this, + &VisualShaderNodeGraphicsObject::on_out_port_dragged); + QObject::connect(p_o, &VisualShaderOutputPortGraphicsObject::port_dropped, this, + &VisualShaderNodeGraphicsObject::on_out_port_dropped); } } @@ -2348,7 +2358,8 @@ VisualShaderOutputPortGraphicsObject::VisualShaderOutputPortGraphicsObject(const VisualShaderOutputPortGraphicsObject::~VisualShaderOutputPortGraphicsObject() {} -VisualShaderConnectionGraphicsObject* VisualShaderOutputPortGraphicsObject::get_connection_graphics_object(const int& to_node_id, const int& to_port_index) const { +VisualShaderConnectionGraphicsObject* VisualShaderOutputPortGraphicsObject::get_connection_graphics_object( + const int& to_node_id, const int& to_port_index) const { for (auto c_g_o : connection_graphics_objects) { if (c_g_o->get_to_node_id() == to_node_id && c_g_o->get_to_port_index() == to_port_index) { return c_g_o; @@ -2673,249 +2684,174 @@ std::pair VisualShaderConnectionGraphicsObject::calculate_cont /**********************************************************************/ /**********************************************************************/ -VisualShaderNodeEmbedWidget::VisualShaderNodeEmbedWidget(const std::shared_ptr& node, - QWidget* parent) : QWidget(parent), - layout(nullptr), - preview_shader_button(nullptr), - shader_previewer_widget(nullptr) { +VisualShaderNodeEmbedWidget::VisualShaderNodeEmbedWidget(const std::shared_ptr& node, QWidget* parent) + : QWidget(parent), layout(nullptr), preview_shader_button(nullptr), shader_previewer_widget(nullptr) { layout = new QVBoxLayout(this); layout->setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom layout->setSizeConstraint(QLayout::SetNoConstraint); layout->setSpacing(2); layout->setAlignment(Qt::AlignVCenter | Qt::AlignHCenter); - if (auto p {std::dynamic_pointer_cast(node)}) { + if (auto p{std::dynamic_pointer_cast(node)}) { VisualShaderNodeInputEmbedWidget* embed_widget = new VisualShaderNodeInputEmbedWidget(p); layout->addWidget(embed_widget); - QObject::connect(embed_widget, - QOverload::of(&QComboBox::currentIndexChanged), - this, + QObject::connect(embed_widget, QOverload::of(&QComboBox::currentIndexChanged), this, &VisualShaderNodeEmbedWidget::on_shader_preview_update_requested); - } else if (auto p {std::dynamic_pointer_cast(node)}) { + } else if (auto p{std::dynamic_pointer_cast(node)}) { VisualShaderNodeFloatFuncEmbedWidget* embed_widget = new VisualShaderNodeFloatFuncEmbedWidget(p); layout->addWidget(embed_widget); - QObject::connect(embed_widget, - QOverload::of(&QComboBox::currentIndexChanged), - this, + QObject::connect(embed_widget, QOverload::of(&QComboBox::currentIndexChanged), this, &VisualShaderNodeEmbedWidget::on_shader_preview_update_requested); - } else if (auto p {std::dynamic_pointer_cast(node)}) { + } else if (auto p{std::dynamic_pointer_cast(node)}) { VisualShaderNodeIntFuncEmbedWidget* embed_widget = new VisualShaderNodeIntFuncEmbedWidget(p); layout->addWidget(embed_widget); - QObject::connect(embed_widget, - QOverload::of(&QComboBox::currentIndexChanged), - this, + QObject::connect(embed_widget, QOverload::of(&QComboBox::currentIndexChanged), this, &VisualShaderNodeEmbedWidget::on_shader_preview_update_requested); - } else if (auto p {std::dynamic_pointer_cast(node)}) { + } else if (auto p{std::dynamic_pointer_cast(node)}) { VisualShaderNodeUIntFuncEmbedWidget* embed_widget = new VisualShaderNodeUIntFuncEmbedWidget(p); layout->addWidget(embed_widget); - QObject::connect(embed_widget, - QOverload::of(&QComboBox::currentIndexChanged), - this, + QObject::connect(embed_widget, QOverload::of(&QComboBox::currentIndexChanged), this, &VisualShaderNodeEmbedWidget::on_shader_preview_update_requested); - } else if (auto p {std::dynamic_pointer_cast(node)}) { + } else if (auto p{std::dynamic_pointer_cast(node)}) { VisualShaderNodeFloatOpEmbedWidget* embed_widget = new VisualShaderNodeFloatOpEmbedWidget(p); layout->addWidget(embed_widget); - QObject::connect(embed_widget, - QOverload::of(&QComboBox::currentIndexChanged), - this, + QObject::connect(embed_widget, QOverload::of(&QComboBox::currentIndexChanged), this, &VisualShaderNodeEmbedWidget::on_shader_preview_update_requested); - } else if (auto p {std::dynamic_pointer_cast(node)}) { + } else if (auto p{std::dynamic_pointer_cast(node)}) { VisualShaderNodeIntOpEmbedWidget* embed_widget = new VisualShaderNodeIntOpEmbedWidget(p); layout->addWidget(embed_widget); - QObject::connect(embed_widget, - QOverload::of(&QComboBox::currentIndexChanged), - this, + QObject::connect(embed_widget, QOverload::of(&QComboBox::currentIndexChanged), this, &VisualShaderNodeEmbedWidget::on_shader_preview_update_requested); - } else if (auto p {std::dynamic_pointer_cast(node)}) { + } else if (auto p{std::dynamic_pointer_cast(node)}) { VisualShaderNodeUIntOpEmbedWidget* embed_widget = new VisualShaderNodeUIntOpEmbedWidget(p); layout->addWidget(embed_widget); - QObject::connect(embed_widget, - QOverload::of(&QComboBox::currentIndexChanged), - this, + QObject::connect(embed_widget, QOverload::of(&QComboBox::currentIndexChanged), this, &VisualShaderNodeEmbedWidget::on_shader_preview_update_requested); - } else if (auto p {std::dynamic_pointer_cast(node)}) { + } else if (auto p{std::dynamic_pointer_cast(node)}) { VisualShaderNodeValueNoiseEmbedWidget* embed_widget = new VisualShaderNodeValueNoiseEmbedWidget(p); layout->addWidget(embed_widget); - QObject::connect(embed_widget, - &QLineEdit::textChanged, - this, + QObject::connect(embed_widget, &QLineEdit::textChanged, this, &VisualShaderNodeEmbedWidget::on_shader_preview_update_requested); - } else if (auto p {std::dynamic_pointer_cast(node)}) { + } else if (auto p{std::dynamic_pointer_cast(node)}) { VisualShaderNodePerlinNoiseEmbedWidget* embed_widget = new VisualShaderNodePerlinNoiseEmbedWidget(p); layout->addWidget(embed_widget); - QObject::connect(embed_widget, - &QLineEdit::textChanged, - this, + QObject::connect(embed_widget, &QLineEdit::textChanged, this, &VisualShaderNodeEmbedWidget::on_shader_preview_update_requested); - } else if (auto p {std::dynamic_pointer_cast(node)}) { - VisualShaderNodeVoronoiNoiseAngleOffsetEmbedWidget* embed_widget = new VisualShaderNodeVoronoiNoiseAngleOffsetEmbedWidget(p); - VisualShaderNodeVoronoiNoiseCellDensityEmbedWidget* embed_widget2 = new VisualShaderNodeVoronoiNoiseCellDensityEmbedWidget(p); + } else if (auto p{std::dynamic_pointer_cast(node)}) { + VisualShaderNodeVoronoiNoiseAngleOffsetEmbedWidget* embed_widget = + new VisualShaderNodeVoronoiNoiseAngleOffsetEmbedWidget(p); + VisualShaderNodeVoronoiNoiseCellDensityEmbedWidget* embed_widget2 = + new VisualShaderNodeVoronoiNoiseCellDensityEmbedWidget(p); layout->addWidget(embed_widget); layout->addWidget(embed_widget2); - QObject::connect(embed_widget, - &QLineEdit::textChanged, - this, + QObject::connect(embed_widget, &QLineEdit::textChanged, this, + &VisualShaderNodeEmbedWidget::on_shader_preview_update_requested); + QObject::connect(embed_widget2, &QLineEdit::textChanged, this, &VisualShaderNodeEmbedWidget::on_shader_preview_update_requested); - QObject::connect(embed_widget2, - &QLineEdit::textChanged, - this, - &VisualShaderNodeEmbedWidget::on_shader_preview_update_requested); - } else if (auto p {std::dynamic_pointer_cast(node)}) { + } else if (auto p{std::dynamic_pointer_cast(node)}) { VisualShaderNodeColorConstantEmbedWidget* embed_widget = new VisualShaderNodeColorConstantEmbedWidget(p); layout->addWidget(embed_widget); - QObject::connect(embed_widget, - &VisualShaderNodeColorConstantEmbedWidget::color_changed, - this, + QObject::connect(embed_widget, &VisualShaderNodeColorConstantEmbedWidget::color_changed, this, &VisualShaderNodeEmbedWidget::on_shader_preview_update_requested); - } else if (auto p {std::dynamic_pointer_cast(node)}) { + } else if (auto p{std::dynamic_pointer_cast(node)}) { VisualShaderNodeBooleanConstantEmbedWidget* embed_widget = new VisualShaderNodeBooleanConstantEmbedWidget(p); layout->addWidget(embed_widget); - QObject::connect(embed_widget, - &QCheckBox::stateChanged, - this, + QObject::connect(embed_widget, &QCheckBox::stateChanged, this, &VisualShaderNodeEmbedWidget::on_shader_preview_update_requested); - } else if (auto p {std::dynamic_pointer_cast(node)}) { + } else if (auto p{std::dynamic_pointer_cast(node)}) { VisualShaderNodeFloatConstantEmbedWidget* embed_widget = new VisualShaderNodeFloatConstantEmbedWidget(p); layout->addWidget(embed_widget); - QObject::connect(embed_widget, - &QLineEdit::textChanged, - this, + QObject::connect(embed_widget, &QLineEdit::textChanged, this, &VisualShaderNodeEmbedWidget::on_shader_preview_update_requested); - } else if (auto p {std::dynamic_pointer_cast(node)}) { + } else if (auto p{std::dynamic_pointer_cast(node)}) { VisualShaderNodeIntConstantEmbedWidget* embed_widget = new VisualShaderNodeIntConstantEmbedWidget(p); layout->addWidget(embed_widget); - QObject::connect(embed_widget, - &QLineEdit::textChanged, - this, + QObject::connect(embed_widget, &QLineEdit::textChanged, this, &VisualShaderNodeEmbedWidget::on_shader_preview_update_requested); - } else if (auto p {std::dynamic_pointer_cast(node)}) { + } else if (auto p{std::dynamic_pointer_cast(node)}) { VisualShaderNodeUIntConstantEmbedWidget* embed_widget = new VisualShaderNodeUIntConstantEmbedWidget(p); layout->addWidget(embed_widget); - QObject::connect(embed_widget, - &QLineEdit::textChanged, - this, + QObject::connect(embed_widget, &QLineEdit::textChanged, this, &VisualShaderNodeEmbedWidget::on_shader_preview_update_requested); - } else if (auto p {std::dynamic_pointer_cast(node)}) { + } else if (auto p{std::dynamic_pointer_cast(node)}) { VisualShaderNodeVec2ConstantEmbedWidget* embed_widget = new VisualShaderNodeVec2ConstantEmbedWidget(p); layout->addLayout(embed_widget); - QObject::connect(embed_widget->get_x_edit_widget(), - &QLineEdit::textChanged, - this, + QObject::connect(embed_widget->get_x_edit_widget(), &QLineEdit::textChanged, this, &VisualShaderNodeEmbedWidget::on_shader_preview_update_requested); - QObject::connect(embed_widget->get_y_edit_widget(), - &QLineEdit::textChanged, - this, - &VisualShaderNodeEmbedWidget::on_shader_preview_update_requested); - } else if (auto p {std::dynamic_pointer_cast(node)}) { + QObject::connect(embed_widget->get_y_edit_widget(), &QLineEdit::textChanged, this, + &VisualShaderNodeEmbedWidget::on_shader_preview_update_requested); + } else if (auto p{std::dynamic_pointer_cast(node)}) { VisualShaderNodeVec3ConstantEmbedWidget* embed_widget = new VisualShaderNodeVec3ConstantEmbedWidget(p); layout->addLayout(embed_widget); - QObject::connect(embed_widget->get_x_edit_widget(), - &QLineEdit::textChanged, - this, + QObject::connect(embed_widget->get_x_edit_widget(), &QLineEdit::textChanged, this, + &VisualShaderNodeEmbedWidget::on_shader_preview_update_requested); + QObject::connect(embed_widget->get_y_edit_widget(), &QLineEdit::textChanged, this, &VisualShaderNodeEmbedWidget::on_shader_preview_update_requested); - QObject::connect(embed_widget->get_y_edit_widget(), - &QLineEdit::textChanged, - this, - &VisualShaderNodeEmbedWidget::on_shader_preview_update_requested); - QObject::connect(embed_widget->get_z_edit_widget(), - &QLineEdit::textChanged, - this, - &VisualShaderNodeEmbedWidget::on_shader_preview_update_requested); - } else if (auto p {std::dynamic_pointer_cast(node)}) { + QObject::connect(embed_widget->get_z_edit_widget(), &QLineEdit::textChanged, this, + &VisualShaderNodeEmbedWidget::on_shader_preview_update_requested); + } else if (auto p{std::dynamic_pointer_cast(node)}) { VisualShaderNodeVec4ConstantEmbedWidget* embed_widget = new VisualShaderNodeVec4ConstantEmbedWidget(p); layout->addLayout(embed_widget); - QObject::connect(embed_widget->get_x_edit_widget(), - &QLineEdit::textChanged, - this, + QObject::connect(embed_widget->get_x_edit_widget(), &QLineEdit::textChanged, this, + &VisualShaderNodeEmbedWidget::on_shader_preview_update_requested); + QObject::connect(embed_widget->get_y_edit_widget(), &QLineEdit::textChanged, this, &VisualShaderNodeEmbedWidget::on_shader_preview_update_requested); - QObject::connect(embed_widget->get_y_edit_widget(), - &QLineEdit::textChanged, - this, - &VisualShaderNodeEmbedWidget::on_shader_preview_update_requested); - QObject::connect(embed_widget->get_z_edit_widget(), - &QLineEdit::textChanged, - this, - &VisualShaderNodeEmbedWidget::on_shader_preview_update_requested); - QObject::connect(embed_widget->get_w_edit_widget(), - &QLineEdit::textChanged, - this, - &VisualShaderNodeEmbedWidget::on_shader_preview_update_requested); - } else if (auto p {std::dynamic_pointer_cast(node)}) { + QObject::connect(embed_widget->get_z_edit_widget(), &QLineEdit::textChanged, this, + &VisualShaderNodeEmbedWidget::on_shader_preview_update_requested); + QObject::connect(embed_widget->get_w_edit_widget(), &QLineEdit::textChanged, this, + &VisualShaderNodeEmbedWidget::on_shader_preview_update_requested); + } else if (auto p{std::dynamic_pointer_cast(node)}) { VisualShaderNodeVectorBaseEmbedWidget* embed_widget = new VisualShaderNodeVectorBaseEmbedWidget(p); layout->addWidget(embed_widget); VisualShaderNodeVectorOpEmbedWidget* embed_widget2 = new VisualShaderNodeVectorOpEmbedWidget(p); layout->addWidget(embed_widget2); - QObject::connect(embed_widget2, - QOverload::of(&QComboBox::currentIndexChanged), - this, + QObject::connect(embed_widget2, QOverload::of(&QComboBox::currentIndexChanged), this, &VisualShaderNodeEmbedWidget::on_shader_preview_update_requested); - } else if (auto p {std::dynamic_pointer_cast(node)}) { + } else if (auto p{std::dynamic_pointer_cast(node)}) { VisualShaderNodeVectorBaseEmbedWidget* embed_widget = new VisualShaderNodeVectorBaseEmbedWidget(p); layout->addWidget(embed_widget); VisualShaderNodeVectorFuncEmbedWidget* embed_widget2 = new VisualShaderNodeVectorFuncEmbedWidget(p); layout->addWidget(embed_widget2); - QObject::connect(embed_widget2, - QOverload::of(&QComboBox::currentIndexChanged), - this, + QObject::connect(embed_widget2, QOverload::of(&QComboBox::currentIndexChanged), this, &VisualShaderNodeEmbedWidget::on_shader_preview_update_requested); - } else if (auto p {std::dynamic_pointer_cast(node)}) { + } else if (auto p{std::dynamic_pointer_cast(node)}) { VisualShaderNodeVectorBaseEmbedWidget* embed_widget = new VisualShaderNodeVectorBaseEmbedWidget(p); layout->addWidget(embed_widget); - QObject::connect(embed_widget, - QOverload::of(&QComboBox::currentIndexChanged), - this, + QObject::connect(embed_widget, QOverload::of(&QComboBox::currentIndexChanged), this, &VisualShaderNodeEmbedWidget::on_node_update_requested); - } else if (auto p {std::dynamic_pointer_cast(node)}) { + } else if (auto p{std::dynamic_pointer_cast(node)}) { VisualShaderNodeVectorBaseEmbedWidget* embed_widget = new VisualShaderNodeVectorBaseEmbedWidget(p); layout->addWidget(embed_widget); - QObject::connect(embed_widget, - QOverload::of(&QComboBox::currentIndexChanged), - this, + QObject::connect(embed_widget, QOverload::of(&QComboBox::currentIndexChanged), this, &VisualShaderNodeEmbedWidget::on_node_update_requested); - } else if (auto p {std::dynamic_pointer_cast(node)}) { + } else if (auto p{std::dynamic_pointer_cast(node)}) { VisualShaderNodeDerivativeFuncEmbedWidget* embed_widget = new VisualShaderNodeDerivativeFuncEmbedWidget(p); layout->addLayout(embed_widget); - QObject::connect(embed_widget->get_op_type_combo_box(), - QOverload::of(&QComboBox::currentIndexChanged), - this, + QObject::connect(embed_widget->get_op_type_combo_box(), QOverload::of(&QComboBox::currentIndexChanged), this, &VisualShaderNodeEmbedWidget::on_shader_preview_update_requested); - QObject::connect(embed_widget->get_function_combo_box(), - QOverload::of(&QComboBox::currentIndexChanged), - this, - &VisualShaderNodeEmbedWidget::on_shader_preview_update_requested); - QObject::connect(embed_widget->get_precision_combo_box(), - QOverload::of(&QComboBox::currentIndexChanged), - this, - &VisualShaderNodeEmbedWidget::on_shader_preview_update_requested); - } else if (auto p {std::dynamic_pointer_cast(node)}) { + QObject::connect(embed_widget->get_function_combo_box(), QOverload::of(&QComboBox::currentIndexChanged), this, + &VisualShaderNodeEmbedWidget::on_shader_preview_update_requested); + QObject::connect(embed_widget->get_precision_combo_box(), QOverload::of(&QComboBox::currentIndexChanged), this, + &VisualShaderNodeEmbedWidget::on_shader_preview_update_requested); + } else if (auto p{std::dynamic_pointer_cast(node)}) { VisualShaderNodeCompareEmbedWidget* embed_widget = new VisualShaderNodeCompareEmbedWidget(p); layout->addLayout(embed_widget); - QObject::connect(embed_widget->get_comparison_type_combo_box(), - QOverload::of(&QComboBox::currentIndexChanged), - this, + QObject::connect(embed_widget->get_comparison_type_combo_box(), QOverload::of(&QComboBox::currentIndexChanged), + this, &VisualShaderNodeEmbedWidget::on_shader_preview_update_requested); + QObject::connect(embed_widget->get_func_combo_box(), QOverload::of(&QComboBox::currentIndexChanged), this, &VisualShaderNodeEmbedWidget::on_shader_preview_update_requested); - QObject::connect(embed_widget->get_func_combo_box(), - QOverload::of(&QComboBox::currentIndexChanged), - this, + QObject::connect(embed_widget->get_condition_combo_box(), QOverload::of(&QComboBox::currentIndexChanged), this, &VisualShaderNodeEmbedWidget::on_shader_preview_update_requested); - QObject::connect(embed_widget->get_condition_combo_box(), - QOverload::of(&QComboBox::currentIndexChanged), - this, - &VisualShaderNodeEmbedWidget::on_shader_preview_update_requested); - } else if (auto p {std::dynamic_pointer_cast(node)}) { + } else if (auto p{std::dynamic_pointer_cast(node)}) { VisualShaderNodeSwitchEmbedWidget* embed_widget = new VisualShaderNodeSwitchEmbedWidget(p); layout->addWidget(embed_widget); - QObject::connect(embed_widget, - QOverload::of(&QComboBox::currentIndexChanged), - this, + QObject::connect(embed_widget, QOverload::of(&QComboBox::currentIndexChanged), this, &VisualShaderNodeEmbedWidget::on_shader_preview_update_requested); - } else if (auto p {std::dynamic_pointer_cast(node)}) { + } else if (auto p{std::dynamic_pointer_cast(node)}) { VisualShaderNodeIsEmbedWidget* embed_widget = new VisualShaderNodeIsEmbedWidget(p); layout->addWidget(embed_widget); - QObject::connect(embed_widget, - QOverload::of(&QComboBox::currentIndexChanged), - this, + QObject::connect(embed_widget, QOverload::of(&QComboBox::currentIndexChanged), this, &VisualShaderNodeEmbedWidget::on_shader_preview_update_requested); } @@ -2926,7 +2862,8 @@ VisualShaderNodeEmbedWidget::VisualShaderNodeEmbedWidget(const std::shared_ptrsetContentsMargins(0, 0, 0, 0); // Left, top, right, bottom preview_shader_button->setToolTip("Create a new node"); layout->addWidget(preview_shader_button); - QObject::connect(preview_shader_button, &QPushButton::pressed, this, &VisualShaderNodeEmbedWidget::on_preview_shader_button_pressed); + QObject::connect(preview_shader_button, &QPushButton::pressed, this, + &VisualShaderNodeEmbedWidget::on_preview_shader_button_pressed); this->setContentsMargins(10, 10, 10, 10); // Left, top, right, bottom setLayout(layout); @@ -2938,15 +2875,15 @@ VisualShaderNodeEmbedWidget::~VisualShaderNodeEmbedWidget() {} /* Input Node */ /*************************************/ -VisualShaderNodeInputEmbedWidget::VisualShaderNodeInputEmbedWidget(const std::shared_ptr& node) : QComboBox(), - node(node) { +VisualShaderNodeInputEmbedWidget::VisualShaderNodeInputEmbedWidget(const std::shared_ptr& node) + : QComboBox(), node(node) { setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom // Add the default item addItem(QString::fromStdString(node->get_input_name()), ""); - const VisualShaderNodeInput::Port* ps {VisualShaderNodeInput::get_ports()}; + const VisualShaderNodeInput::Port* ps{VisualShaderNodeInput::get_ports()}; int i{0}; @@ -2955,9 +2892,7 @@ VisualShaderNodeInputEmbedWidget::VisualShaderNodeInputEmbedWidget(const std::sh i++; } - QObject::connect(this, - QOverload::of(&QComboBox::currentIndexChanged), - this, + QObject::connect(this, QOverload::of(&QComboBox::currentIndexChanged), this, &VisualShaderNodeInputEmbedWidget::on_current_index_changed); } @@ -2971,8 +2906,9 @@ void VisualShaderNodeInputEmbedWidget::on_current_index_changed(const int& index /* Float Op Node */ /*************************************/ -VisualShaderNodeFloatOpEmbedWidget::VisualShaderNodeFloatOpEmbedWidget(const std::shared_ptr& node) : QComboBox(), - node(node) { +VisualShaderNodeFloatOpEmbedWidget::VisualShaderNodeFloatOpEmbedWidget( + const std::shared_ptr& node) + : QComboBox(), node(node) { setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom @@ -2989,9 +2925,7 @@ VisualShaderNodeFloatOpEmbedWidget::VisualShaderNodeFloatOpEmbedWidget(const std setCurrentIndex((int)node->get_operator()); - QObject::connect(this, - QOverload::of(&QComboBox::currentIndexChanged), - this, + QObject::connect(this, QOverload::of(&QComboBox::currentIndexChanged), this, &VisualShaderNodeFloatOpEmbedWidget::on_current_index_changed); } @@ -3005,8 +2939,8 @@ void VisualShaderNodeFloatOpEmbedWidget::on_current_index_changed(const int& ind /* Int Op Node */ /*************************************/ -VisualShaderNodeIntOpEmbedWidget::VisualShaderNodeIntOpEmbedWidget(const std::shared_ptr& node) : QComboBox(), - node(node) { +VisualShaderNodeIntOpEmbedWidget::VisualShaderNodeIntOpEmbedWidget(const std::shared_ptr& node) + : QComboBox(), node(node) { setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom @@ -3025,9 +2959,7 @@ VisualShaderNodeIntOpEmbedWidget::VisualShaderNodeIntOpEmbedWidget(const std::sh setCurrentIndex((int)node->get_operator()); - QObject::connect(this, - QOverload::of(&QComboBox::currentIndexChanged), - this, + QObject::connect(this, QOverload::of(&QComboBox::currentIndexChanged), this, &VisualShaderNodeIntOpEmbedWidget::on_current_index_changed); } @@ -3041,8 +2973,9 @@ void VisualShaderNodeIntOpEmbedWidget::on_current_index_changed(const int& index /* UInt Op Node */ /*************************************/ -VisualShaderNodeUIntOpEmbedWidget::VisualShaderNodeUIntOpEmbedWidget(const std::shared_ptr& node) : QComboBox(), - node(node) { +VisualShaderNodeUIntOpEmbedWidget::VisualShaderNodeUIntOpEmbedWidget( + const std::shared_ptr& node) + : QComboBox(), node(node) { setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom @@ -3061,9 +2994,7 @@ VisualShaderNodeUIntOpEmbedWidget::VisualShaderNodeUIntOpEmbedWidget(const std:: setCurrentIndex((int)node->get_operator()); - QObject::connect(this, - QOverload::of(&QComboBox::currentIndexChanged), - this, + QObject::connect(this, QOverload::of(&QComboBox::currentIndexChanged), this, &VisualShaderNodeUIntOpEmbedWidget::on_current_index_changed); } @@ -3077,8 +3008,9 @@ void VisualShaderNodeUIntOpEmbedWidget::on_current_index_changed(const int& inde /* Float Funcs Node */ /*************************************/ -VisualShaderNodeFloatFuncEmbedWidget::VisualShaderNodeFloatFuncEmbedWidget(const std::shared_ptr& node) : QComboBox(), - node(node) { +VisualShaderNodeFloatFuncEmbedWidget::VisualShaderNodeFloatFuncEmbedWidget( + const std::shared_ptr& node) + : QComboBox(), node(node) { setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom @@ -3117,9 +3049,7 @@ VisualShaderNodeFloatFuncEmbedWidget::VisualShaderNodeFloatFuncEmbedWidget(const setCurrentIndex((int)node->get_function()); - QObject::connect(this, - QOverload::of(&QComboBox::currentIndexChanged), - this, + QObject::connect(this, QOverload::of(&QComboBox::currentIndexChanged), this, &VisualShaderNodeFloatFuncEmbedWidget::on_current_index_changed); } @@ -3133,8 +3063,9 @@ void VisualShaderNodeFloatFuncEmbedWidget::on_current_index_changed(const int& i /* Int Funcs Node */ /*************************************/ -VisualShaderNodeIntFuncEmbedWidget::VisualShaderNodeIntFuncEmbedWidget(const std::shared_ptr& node) : QComboBox(), - node(node) { +VisualShaderNodeIntFuncEmbedWidget::VisualShaderNodeIntFuncEmbedWidget( + const std::shared_ptr& node) + : QComboBox(), node(node) { setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom @@ -3145,9 +3076,7 @@ VisualShaderNodeIntFuncEmbedWidget::VisualShaderNodeIntFuncEmbedWidget(const std setCurrentIndex((int)node->get_function()); - QObject::connect(this, - QOverload::of(&QComboBox::currentIndexChanged), - this, + QObject::connect(this, QOverload::of(&QComboBox::currentIndexChanged), this, &VisualShaderNodeIntFuncEmbedWidget::on_current_index_changed); } @@ -3161,8 +3090,9 @@ void VisualShaderNodeIntFuncEmbedWidget::on_current_index_changed(const int& ind /* UInt Funcs Node */ /*************************************/ -VisualShaderNodeUIntFuncEmbedWidget::VisualShaderNodeUIntFuncEmbedWidget(const std::shared_ptr& node) : QComboBox(), - node(node) { +VisualShaderNodeUIntFuncEmbedWidget::VisualShaderNodeUIntFuncEmbedWidget( + const std::shared_ptr& node) + : QComboBox(), node(node) { setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom @@ -3171,9 +3101,7 @@ VisualShaderNodeUIntFuncEmbedWidget::VisualShaderNodeUIntFuncEmbedWidget(const s setCurrentIndex((int)node->get_function()); - QObject::connect(this, - QOverload::of(&QComboBox::currentIndexChanged), - this, + QObject::connect(this, QOverload::of(&QComboBox::currentIndexChanged), this, &VisualShaderNodeUIntFuncEmbedWidget::on_current_index_changed); } @@ -3187,8 +3115,9 @@ void VisualShaderNodeUIntFuncEmbedWidget::on_current_index_changed(const int& in /* Vector Base */ /*************************************/ -VisualShaderNodeVectorBaseEmbedWidget::VisualShaderNodeVectorBaseEmbedWidget(const std::shared_ptr& node) : QComboBox(), - node(node) { +VisualShaderNodeVectorBaseEmbedWidget::VisualShaderNodeVectorBaseEmbedWidget( + const std::shared_ptr& node) + : QComboBox(), node(node) { setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom @@ -3199,9 +3128,7 @@ VisualShaderNodeVectorBaseEmbedWidget::VisualShaderNodeVectorBaseEmbedWidget(con // set the current index setCurrentIndex((int)node->get_op_type()); - QObject::connect(this, - QOverload::of(&QComboBox::currentIndexChanged), - this, + QObject::connect(this, QOverload::of(&QComboBox::currentIndexChanged), this, &VisualShaderNodeVectorBaseEmbedWidget::on_current_index_changed); } @@ -3215,8 +3142,9 @@ void VisualShaderNodeVectorBaseEmbedWidget::on_current_index_changed(const int& /* Vector Op Node */ /*************************************/ -VisualShaderNodeVectorOpEmbedWidget::VisualShaderNodeVectorOpEmbedWidget(const std::shared_ptr& node) : QComboBox(), - node(node) { +VisualShaderNodeVectorOpEmbedWidget::VisualShaderNodeVectorOpEmbedWidget( + const std::shared_ptr& node) + : QComboBox(), node(node) { setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom @@ -3235,9 +3163,7 @@ VisualShaderNodeVectorOpEmbedWidget::VisualShaderNodeVectorOpEmbedWidget(const s setCurrentIndex((int)node->get_operator()); - QObject::connect(this, - QOverload::of(&QComboBox::currentIndexChanged), - this, + QObject::connect(this, QOverload::of(&QComboBox::currentIndexChanged), this, &VisualShaderNodeVectorOpEmbedWidget::on_current_index_changed); } @@ -3251,8 +3177,9 @@ void VisualShaderNodeVectorOpEmbedWidget::on_current_index_changed(const int& in /* Vector Funcs Node */ /*************************************/ -VisualShaderNodeVectorFuncEmbedWidget::VisualShaderNodeVectorFuncEmbedWidget(const std::shared_ptr& node) : QComboBox(), - node(node) { +VisualShaderNodeVectorFuncEmbedWidget::VisualShaderNodeVectorFuncEmbedWidget( + const std::shared_ptr& node) + : QComboBox(), node(node) { setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom @@ -3292,9 +3219,7 @@ VisualShaderNodeVectorFuncEmbedWidget::VisualShaderNodeVectorFuncEmbedWidget(con setCurrentIndex((int)node->get_function()); - QObject::connect(this, - QOverload::of(&QComboBox::currentIndexChanged), - this, + QObject::connect(this, QOverload::of(&QComboBox::currentIndexChanged), this, &VisualShaderNodeVectorFuncEmbedWidget::on_current_index_changed); } @@ -3308,45 +3233,43 @@ void VisualShaderNodeVectorFuncEmbedWidget::on_current_index_changed(const int& /* Color Constant Node */ /*************************************/ -VisualShaderNodeColorConstantEmbedWidget::VisualShaderNodeColorConstantEmbedWidget(const std::shared_ptr& node) : QPushButton(), - node(node) { +VisualShaderNodeColorConstantEmbedWidget::VisualShaderNodeColorConstantEmbedWidget( + const std::shared_ptr& node) + : QPushButton(), node(node) { setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom { - QPalette palette {this->palette()}; + QPalette palette{this->palette()}; TColor c{node->get_constant()}; palette.setColor(QPalette::Button, QColor(c.r, c.g, c.b, c.a)); this->setPalette(palette); } - QObject::connect(this, - &QPushButton::pressed, - this, - &VisualShaderNodeColorConstantEmbedWidget::on_pressed); + QObject::connect(this, &QPushButton::pressed, this, &VisualShaderNodeColorConstantEmbedWidget::on_pressed); } VisualShaderNodeColorConstantEmbedWidget::~VisualShaderNodeColorConstantEmbedWidget() {} void VisualShaderNodeColorConstantEmbedWidget::on_pressed() { TColor c{node->get_constant()}; - QColor color {QColorDialog::getColor(QColor(c.r, c.g, c.b, c.a), this, "Select Color")}; - + QColor color{QColorDialog::getColor(QColor(c.r, c.g, c.b, c.a), this, "Select Color")}; + // If a valid color is picked, update the button and store the color if (color.isValid()) { - node->set_constant({(float)color.red(), (float)color.green(), (float)color.blue(), (float)color.alpha()}); - QPalette palette {this->palette()}; - palette.setColor(QPalette::Button, color); - this->setPalette(palette); - this->update(); - Q_EMIT color_changed(); + node->set_constant({(float)color.red(), (float)color.green(), (float)color.blue(), (float)color.alpha()}); + QPalette palette{this->palette()}; + palette.setColor(QPalette::Button, color); + this->setPalette(palette); + this->update(); + Q_EMIT color_changed(); } else { - // If the user cancels the color dialog, reset the button to the previous color - QColor previous_color {QColor(c.r, c.g, c.b, c.a)}; - QPalette palette {this->palette()}; - palette.setColor(QPalette::Button, previous_color); - this->setPalette(palette); - this->update(); + // If the user cancels the color dialog, reset the button to the previous color + QColor previous_color{QColor(c.r, c.g, c.b, c.a)}; + QPalette palette{this->palette()}; + palette.setColor(QPalette::Button, previous_color); + this->setPalette(palette); + this->update(); } } @@ -3354,17 +3277,15 @@ void VisualShaderNodeColorConstantEmbedWidget::on_pressed() { /* Boolean Constant Node */ /*************************************/ -VisualShaderNodeBooleanConstantEmbedWidget::VisualShaderNodeBooleanConstantEmbedWidget(const std::shared_ptr& node) : QCheckBox(), - node(node) { +VisualShaderNodeBooleanConstantEmbedWidget::VisualShaderNodeBooleanConstantEmbedWidget( + const std::shared_ptr& node) + : QCheckBox(), node(node) { setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom setCheckState(node->get_constant() ? Qt::Checked : Qt::Unchecked); - QObject::connect(this, - &QCheckBox::stateChanged, - this, - &VisualShaderNodeBooleanConstantEmbedWidget::on_state_changed); + QObject::connect(this, &QCheckBox::stateChanged, this, &VisualShaderNodeBooleanConstantEmbedWidget::on_state_changed); } VisualShaderNodeBooleanConstantEmbedWidget::~VisualShaderNodeBooleanConstantEmbedWidget() {} @@ -3377,8 +3298,9 @@ void VisualShaderNodeBooleanConstantEmbedWidget::on_state_changed(const int& sta /* Float Constant */ /*************************************/ -VisualShaderNodeFloatConstantEmbedWidget::VisualShaderNodeFloatConstantEmbedWidget(const std::shared_ptr& node) : QLineEdit(), - node(node) { +VisualShaderNodeFloatConstantEmbedWidget::VisualShaderNodeFloatConstantEmbedWidget( + const std::shared_ptr& node) + : QLineEdit(), node(node) { setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom @@ -3386,10 +3308,7 @@ VisualShaderNodeFloatConstantEmbedWidget::VisualShaderNodeFloatConstantEmbedWidg setText(QString::number(node->get_constant())); - QObject::connect(this, - &QLineEdit::textChanged, - this, - &VisualShaderNodeFloatConstantEmbedWidget::on_text_changed); + QObject::connect(this, &QLineEdit::textChanged, this, &VisualShaderNodeFloatConstantEmbedWidget::on_text_changed); } VisualShaderNodeFloatConstantEmbedWidget::~VisualShaderNodeFloatConstantEmbedWidget() {} @@ -3413,8 +3332,9 @@ void VisualShaderNodeFloatConstantEmbedWidget::on_text_changed(const QString& te /* Int Constant */ /*************************************/ -VisualShaderNodeIntConstantEmbedWidget::VisualShaderNodeIntConstantEmbedWidget(const std::shared_ptr& node) : QLineEdit(), - node(node) { +VisualShaderNodeIntConstantEmbedWidget::VisualShaderNodeIntConstantEmbedWidget( + const std::shared_ptr& node) + : QLineEdit(), node(node) { setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom @@ -3422,10 +3342,7 @@ VisualShaderNodeIntConstantEmbedWidget::VisualShaderNodeIntConstantEmbedWidget(c setText(QString::number(node->get_constant())); - QObject::connect(this, - &QLineEdit::textChanged, - this, - &VisualShaderNodeIntConstantEmbedWidget::on_text_changed); + QObject::connect(this, &QLineEdit::textChanged, this, &VisualShaderNodeIntConstantEmbedWidget::on_text_changed); } VisualShaderNodeIntConstantEmbedWidget::~VisualShaderNodeIntConstantEmbedWidget() {} @@ -3449,8 +3366,9 @@ void VisualShaderNodeIntConstantEmbedWidget::on_text_changed(const QString& text /* UInt Constant */ /*************************************/ -VisualShaderNodeUIntConstantEmbedWidget::VisualShaderNodeUIntConstantEmbedWidget(const std::shared_ptr& node) : QLineEdit(), - node(node) { +VisualShaderNodeUIntConstantEmbedWidget::VisualShaderNodeUIntConstantEmbedWidget( + const std::shared_ptr& node) + : QLineEdit(), node(node) { setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom @@ -3458,10 +3376,7 @@ VisualShaderNodeUIntConstantEmbedWidget::VisualShaderNodeUIntConstantEmbedWidget setText(QString::number(node->get_constant())); - QObject::connect(this, - &QLineEdit::textChanged, - this, - &VisualShaderNodeUIntConstantEmbedWidget::on_text_changed); + QObject::connect(this, &QLineEdit::textChanged, this, &VisualShaderNodeUIntConstantEmbedWidget::on_text_changed); } VisualShaderNodeUIntConstantEmbedWidget::~VisualShaderNodeUIntConstantEmbedWidget() {} @@ -3485,8 +3400,9 @@ void VisualShaderNodeUIntConstantEmbedWidget::on_text_changed(const QString& tex /* Vec2 Constant Node */ /*************************************/ -VisualShaderNodeVec2ConstantEmbedWidget::VisualShaderNodeVec2ConstantEmbedWidget(const std::shared_ptr& node) : QVBoxLayout(), - node(node) { +VisualShaderNodeVec2ConstantEmbedWidget::VisualShaderNodeVec2ConstantEmbedWidget( + const std::shared_ptr& node) + : QVBoxLayout(), node(node) { setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom setSizeConstraint(QLayout::SetNoConstraint); setSpacing(2); @@ -3501,20 +3417,16 @@ VisualShaderNodeVec2ConstantEmbedWidget::VisualShaderNodeVec2ConstantEmbedWidget x_edit_widget->setText(QString::number(node->get_constant().x)); y_edit_widget->setText(QString::number(node->get_constant().y)); - QObject::connect(x_edit_widget, - &QLineEdit::textChanged, - this, + QObject::connect(x_edit_widget, &QLineEdit::textChanged, this, &VisualShaderNodeVec2ConstantEmbedWidget::on_x_text_changed); - QObject::connect(y_edit_widget, - &QLineEdit::textChanged, - this, + QObject::connect(y_edit_widget, &QLineEdit::textChanged, this, &VisualShaderNodeVec2ConstantEmbedWidget::on_y_text_changed); addWidget(x_edit_widget); addWidget(y_edit_widget); } -VisualShaderNodeVec2ConstantEmbedWidget::~VisualShaderNodeVec2ConstantEmbedWidget() {} +VisualShaderNodeVec2ConstantEmbedWidget::~VisualShaderNodeVec2ConstantEmbedWidget() {} void VisualShaderNodeVec2ConstantEmbedWidget::on_x_text_changed(const QString& text) { if (text.isEmpty()) { @@ -3550,8 +3462,9 @@ void VisualShaderNodeVec2ConstantEmbedWidget::on_y_text_changed(const QString& t /* Vec3 Constant Node */ /*************************************/ -VisualShaderNodeVec3ConstantEmbedWidget::VisualShaderNodeVec3ConstantEmbedWidget(const std::shared_ptr& node) : QVBoxLayout(), - node(node) { +VisualShaderNodeVec3ConstantEmbedWidget::VisualShaderNodeVec3ConstantEmbedWidget( + const std::shared_ptr& node) + : QVBoxLayout(), node(node) { setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom setSizeConstraint(QLayout::SetNoConstraint); setSpacing(2); @@ -3569,17 +3482,11 @@ VisualShaderNodeVec3ConstantEmbedWidget::VisualShaderNodeVec3ConstantEmbedWidget y_edit_widget->setText(QString::number(node->get_constant().y)); z_edit_widget->setText(QString::number(node->get_constant().z)); - QObject::connect(x_edit_widget, - &QLineEdit::textChanged, - this, + QObject::connect(x_edit_widget, &QLineEdit::textChanged, this, &VisualShaderNodeVec3ConstantEmbedWidget::on_x_text_changed); - QObject::connect(y_edit_widget, - &QLineEdit::textChanged, - this, + QObject::connect(y_edit_widget, &QLineEdit::textChanged, this, &VisualShaderNodeVec3ConstantEmbedWidget::on_y_text_changed); - QObject::connect(z_edit_widget, - &QLineEdit::textChanged, - this, + QObject::connect(z_edit_widget, &QLineEdit::textChanged, this, &VisualShaderNodeVec3ConstantEmbedWidget::on_z_text_changed); addWidget(x_edit_widget); @@ -3638,8 +3545,9 @@ void VisualShaderNodeVec3ConstantEmbedWidget::on_z_text_changed(const QString& t /* Vec4 Constant Node */ /*************************************/ -VisualShaderNodeVec4ConstantEmbedWidget::VisualShaderNodeVec4ConstantEmbedWidget(const std::shared_ptr& node) : QVBoxLayout(), - node(node) { +VisualShaderNodeVec4ConstantEmbedWidget::VisualShaderNodeVec4ConstantEmbedWidget( + const std::shared_ptr& node) + : QVBoxLayout(), node(node) { setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom setSizeConstraint(QLayout::SetNoConstraint); setSpacing(2); @@ -3660,21 +3568,13 @@ VisualShaderNodeVec4ConstantEmbedWidget::VisualShaderNodeVec4ConstantEmbedWidget z_edit_widget->setText(QString::number(node->get_constant().z)); w_edit_widget->setText(QString::number(node->get_constant().w)); - QObject::connect(x_edit_widget, - &QLineEdit::textChanged, - this, + QObject::connect(x_edit_widget, &QLineEdit::textChanged, this, &VisualShaderNodeVec4ConstantEmbedWidget::on_x_text_changed); - QObject::connect(y_edit_widget, - &QLineEdit::textChanged, - this, + QObject::connect(y_edit_widget, &QLineEdit::textChanged, this, &VisualShaderNodeVec4ConstantEmbedWidget::on_y_text_changed); - QObject::connect(z_edit_widget, - &QLineEdit::textChanged, - this, + QObject::connect(z_edit_widget, &QLineEdit::textChanged, this, &VisualShaderNodeVec4ConstantEmbedWidget::on_z_text_changed); - QObject::connect(w_edit_widget, - &QLineEdit::textChanged, - this, + QObject::connect(w_edit_widget, &QLineEdit::textChanged, this, &VisualShaderNodeVec4ConstantEmbedWidget::on_w_text_changed); addWidget(x_edit_widget); @@ -3749,8 +3649,9 @@ void VisualShaderNodeVec4ConstantEmbedWidget::on_w_text_changed(const QString& t /* Derivative Func Node */ /*************************************/ -VisualShaderNodeDerivativeFuncEmbedWidget::VisualShaderNodeDerivativeFuncEmbedWidget(const std::shared_ptr& node) : QVBoxLayout(), - node(node) { +VisualShaderNodeDerivativeFuncEmbedWidget::VisualShaderNodeDerivativeFuncEmbedWidget( + const std::shared_ptr& node) + : QVBoxLayout(), node(node) { setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom setSizeConstraint(QLayout::SetNoConstraint); setSpacing(2); @@ -3759,7 +3660,7 @@ VisualShaderNodeDerivativeFuncEmbedWidget::VisualShaderNodeDerivativeFuncEmbedWi op_type_combo_box = new QComboBox(); function_combo_box = new QComboBox(); precision_combo_box = new QComboBox(); - + op_type_combo_box->addItem("Scalar", (int)VisualShaderNodeDerivativeFunc::OP_TYPE_SCALAR); op_type_combo_box->addItem("Vector 2D", (int)VisualShaderNodeDerivativeFunc::OP_TYPE_VECTOR_2D); op_type_combo_box->addItem("Vector 3D", (int)VisualShaderNodeDerivativeFunc::OP_TYPE_VECTOR_3D); @@ -3779,17 +3680,11 @@ VisualShaderNodeDerivativeFuncEmbedWidget::VisualShaderNodeDerivativeFuncEmbedWi precision_combo_box->setCurrentIndex((int)node->get_precision()); - QObject::connect(op_type_combo_box, - QOverload::of(&QComboBox::currentIndexChanged), - this, + QObject::connect(op_type_combo_box, QOverload::of(&QComboBox::currentIndexChanged), this, &VisualShaderNodeDerivativeFuncEmbedWidget::on_op_type_current_index_changed); - QObject::connect(function_combo_box, - QOverload::of(&QComboBox::currentIndexChanged), - this, + QObject::connect(function_combo_box, QOverload::of(&QComboBox::currentIndexChanged), this, &VisualShaderNodeDerivativeFuncEmbedWidget::on_function_current_index_changed); - QObject::connect(precision_combo_box, - QOverload::of(&QComboBox::currentIndexChanged), - this, + QObject::connect(precision_combo_box, QOverload::of(&QComboBox::currentIndexChanged), this, &VisualShaderNodeDerivativeFuncEmbedWidget::on_precision_current_index_changed); addWidget(op_type_combo_box); @@ -3815,8 +3710,9 @@ void VisualShaderNodeDerivativeFuncEmbedWidget::on_precision_current_index_chang /* Value Noise Node */ /*************************************/ -VisualShaderNodeValueNoiseEmbedWidget::VisualShaderNodeValueNoiseEmbedWidget(const std::shared_ptr& node) : QLineEdit(), - node(node) { +VisualShaderNodeValueNoiseEmbedWidget::VisualShaderNodeValueNoiseEmbedWidget( + const std::shared_ptr& node) + : QLineEdit(), node(node) { setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom @@ -3831,9 +3727,9 @@ VisualShaderNodeValueNoiseEmbedWidget::~VisualShaderNodeValueNoiseEmbedWidget() void VisualShaderNodeValueNoiseEmbedWidget::on_text_changed(const QString& text) { if (text.isEmpty()) { - node->set_scale(100.0f); + node->set_scale(100.0f); } else { - bool ok; + bool ok; text.toFloat(&ok); if (ok) { node->set_scale(text.toFloat()); @@ -3848,8 +3744,9 @@ void VisualShaderNodeValueNoiseEmbedWidget::on_text_changed(const QString& text) /* Perlin Noise Node */ /*************************************/ -VisualShaderNodePerlinNoiseEmbedWidget::VisualShaderNodePerlinNoiseEmbedWidget(const std::shared_ptr& node) : QLineEdit(), - node(node) { +VisualShaderNodePerlinNoiseEmbedWidget::VisualShaderNodePerlinNoiseEmbedWidget( + const std::shared_ptr& node) + : QLineEdit(), node(node) { setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom @@ -3864,9 +3761,9 @@ VisualShaderNodePerlinNoiseEmbedWidget::~VisualShaderNodePerlinNoiseEmbedWidget( void VisualShaderNodePerlinNoiseEmbedWidget::on_text_changed(const QString& text) { if (text.isEmpty()) { - node->set_scale(10.0f); + node->set_scale(10.0f); } else { - bool ok; + bool ok; text.toFloat(&ok); if (ok) { node->set_scale(text.toFloat()); @@ -3881,8 +3778,9 @@ void VisualShaderNodePerlinNoiseEmbedWidget::on_text_changed(const QString& text /* Voronoi Noise Node */ /*************************************/ -VisualShaderNodeVoronoiNoiseAngleOffsetEmbedWidget::VisualShaderNodeVoronoiNoiseAngleOffsetEmbedWidget(const std::shared_ptr& node) : QLineEdit(), - node(node) { +VisualShaderNodeVoronoiNoiseAngleOffsetEmbedWidget::VisualShaderNodeVoronoiNoiseAngleOffsetEmbedWidget( + const std::shared_ptr& node) + : QLineEdit(), node(node) { setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom @@ -3890,14 +3788,15 @@ VisualShaderNodeVoronoiNoiseAngleOffsetEmbedWidget::VisualShaderNodeVoronoiNoise setText(QString::number(node->get_angle_offset())); - QObject::connect(this, &QLineEdit::textChanged, this, &VisualShaderNodeVoronoiNoiseAngleOffsetEmbedWidget::on_text_changed); + QObject::connect(this, &QLineEdit::textChanged, this, + &VisualShaderNodeVoronoiNoiseAngleOffsetEmbedWidget::on_text_changed); } VisualShaderNodeVoronoiNoiseAngleOffsetEmbedWidget::~VisualShaderNodeVoronoiNoiseAngleOffsetEmbedWidget() {} void VisualShaderNodeVoronoiNoiseAngleOffsetEmbedWidget::on_text_changed(const QString& text) { if (text.isEmpty()) { - node->set_angle_offset(10.0f); + node->set_angle_offset(10.0f); } else { bool ok; text.toFloat(&ok); @@ -3910,8 +3809,9 @@ void VisualShaderNodeVoronoiNoiseAngleOffsetEmbedWidget::on_text_changed(const Q } } -VisualShaderNodeVoronoiNoiseCellDensityEmbedWidget::VisualShaderNodeVoronoiNoiseCellDensityEmbedWidget(const std::shared_ptr& node) : QLineEdit(), - node(node) { +VisualShaderNodeVoronoiNoiseCellDensityEmbedWidget::VisualShaderNodeVoronoiNoiseCellDensityEmbedWidget( + const std::shared_ptr& node) + : QLineEdit(), node(node) { setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom @@ -3919,14 +3819,15 @@ VisualShaderNodeVoronoiNoiseCellDensityEmbedWidget::VisualShaderNodeVoronoiNoise setText(QString::number(node->get_cell_density())); - QObject::connect(this, &QLineEdit::textChanged, this, &VisualShaderNodeVoronoiNoiseCellDensityEmbedWidget::on_text_changed); + QObject::connect(this, &QLineEdit::textChanged, this, + &VisualShaderNodeVoronoiNoiseCellDensityEmbedWidget::on_text_changed); } VisualShaderNodeVoronoiNoiseCellDensityEmbedWidget::~VisualShaderNodeVoronoiNoiseCellDensityEmbedWidget() {} void VisualShaderNodeVoronoiNoiseCellDensityEmbedWidget::on_text_changed(const QString& text) { if (text.isEmpty()) { - node->set_cell_density(10.0f); + node->set_cell_density(10.0f); } else { bool ok; text.toFloat(&ok); @@ -3947,8 +3848,9 @@ void VisualShaderNodeVoronoiNoiseCellDensityEmbedWidget::on_text_changed(const Q /* Compare Node */ /*************************************/ -VisualShaderNodeCompareEmbedWidget::VisualShaderNodeCompareEmbedWidget(const std::shared_ptr& node) : QVBoxLayout(), - node(node) { +VisualShaderNodeCompareEmbedWidget::VisualShaderNodeCompareEmbedWidget( + const std::shared_ptr& node) + : QVBoxLayout(), node(node) { setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom setSizeConstraint(QLayout::SetNoConstraint); setSpacing(2); @@ -3990,7 +3892,8 @@ VisualShaderNodeCompareEmbedWidget::VisualShaderNodeCompareEmbedWidget(const std VisualShaderNodeCompareEmbedWidget::~VisualShaderNodeCompareEmbedWidget() {} void VisualShaderNodeCompareEmbedWidget::on_comparison_type_current_index_changed(const int& index) { - node->set_comparison_type((VisualShaderNodeCompare::ComparisonType)comparison_type_combo_box->itemData(index).toInt()); + node->set_comparison_type( + (VisualShaderNodeCompare::ComparisonType)comparison_type_combo_box->itemData(index).toInt()); } void VisualShaderNodeCompareEmbedWidget::on_func_current_index_changed(const int& index) { node->set_function((VisualShaderNodeCompare::Function)func_combo_box->itemData(index).toInt()); @@ -4003,8 +3906,9 @@ void VisualShaderNodeCompareEmbedWidget::on_condition_current_index_changed(cons /* Switch Node */ /*************************************/ -VisualShaderNodeSwitchEmbedWidget::VisualShaderNodeSwitchEmbedWidget(const std::shared_ptr& node) : QComboBox(), - node(node) { +VisualShaderNodeSwitchEmbedWidget::VisualShaderNodeSwitchEmbedWidget( + const std::shared_ptr& node) + : QComboBox(), node(node) { setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom @@ -4018,9 +3922,7 @@ VisualShaderNodeSwitchEmbedWidget::VisualShaderNodeSwitchEmbedWidget(const std:: setCurrentIndex((int)node->get_op_type()); - QObject::connect(this, - QOverload::of(&QComboBox::currentIndexChanged), - this, + QObject::connect(this, QOverload::of(&QComboBox::currentIndexChanged), this, &VisualShaderNodeSwitchEmbedWidget::on_current_index_changed); } @@ -4034,19 +3936,17 @@ void VisualShaderNodeSwitchEmbedWidget::on_current_index_changed(const int& inde /* Is Node */ /*************************************/ -VisualShaderNodeIsEmbedWidget::VisualShaderNodeIsEmbedWidget(const std::shared_ptr& node) : QComboBox(), - node(node) { +VisualShaderNodeIsEmbedWidget::VisualShaderNodeIsEmbedWidget(const std::shared_ptr& node) + : QComboBox(), node(node) { setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); setContentsMargins(0, 0, 0, 0); // Left, top, right, bottom - + addItem("Is Inf", (int)VisualShaderNodeIs::FUNC_IS_INF); addItem("Is NaN", (int)VisualShaderNodeIs::FUNC_IS_NAN); setCurrentIndex((int)node->get_function()); - QObject::connect(this, - QOverload::of(&QComboBox::currentIndexChanged), - this, + QObject::connect(this, QOverload::of(&QComboBox::currentIndexChanged), this, &VisualShaderNodeIsEmbedWidget::on_current_index_changed); } diff --git a/Editors/VisualShaderEditor.h b/Editors/VisualShaderEditor.h index 9b03c17b6..71b5d2480 100644 --- a/Editors/VisualShaderEditor.h +++ b/Editors/VisualShaderEditor.h @@ -30,38 +30,38 @@ #include #include +#include #include #include #include #include #include +#include #include #include #include +#include +#include #include +#include #include #include #include #include -#include -#include -#include -#include -#include // #include -#include // https://stackoverflow.com/a/64288966/14629018 explains why we need this. -#include -#include -#include #include +#include +#include +#include // https://stackoverflow.com/a/64288966/14629018 explains why we need this. +#include #include #include +#include "Editors/BaseEditor.h" #include "ResourceTransformations/VisualShader/visual_shader.h" #include "ResourceTransformations/VisualShader/visual_shader_nodes.h" #include "ResourceTransformations/VisualShader/vs_noise_nodes.h" -#include "Editors/BaseEditor.h" class VisualShaderGraphicsScene; class VisualShaderGraphicsView; @@ -275,21 +275,21 @@ class CreateNodeDialog : public QDialog { /**********************************************************************/ class OriginalMatchingImageWidget : public QWidget { -public: - OriginalMatchingImageWidget(QWidget* parent = nullptr) : QWidget(parent) { - pixmap = QPixmap(size()); - pixmap.fill(Qt::red); // Fill it with the red color - } - -protected: - // Override the paintEvent to display the pixmap - void paintEvent(QPaintEvent* event) override { - QPainter painter(this); - painter.drawPixmap(0, 0, pixmap); // Draw the pixmap starting at (0, 0) - } - -private: - QPixmap pixmap; + public: + OriginalMatchingImageWidget(QWidget* parent = nullptr) : QWidget(parent) { + pixmap = QPixmap(size()); + pixmap.fill(Qt::red); // Fill it with the red color + } + + protected: + // Override the paintEvent to display the pixmap + void paintEvent(QPaintEvent* event) override { + QPainter painter(this); + painter.drawPixmap(0, 0, pixmap); // Draw the pixmap starting at (0, 0) + } + + private: + QPixmap pixmap; }; /**********************************************************************/ @@ -310,38 +310,38 @@ class OriginalMatchingImageWidget : public QWidget { * */ class ShaderPreviewerWidget : public QOpenGLWidget { - Q_OBJECT + Q_OBJECT -public: - ShaderPreviewerWidget(QWidget* parent = nullptr); - ~ShaderPreviewerWidget() override; + public: + ShaderPreviewerWidget(QWidget* parent = nullptr); + ~ShaderPreviewerWidget() override; - void set_code(const std::string& code); + void set_code(const std::string& code); -Q_SIGNALS: - void scene_update_requested(); + Q_SIGNALS: + void scene_update_requested(); -protected: - void initializeGL() override; - void resizeGL(int w, int h) override; - void paintGL() override; + protected: + void initializeGL() override; + void resizeGL(int w, int h) override; + void paintGL() override; - void showEvent(QShowEvent* event) override; - void hideEvent(QHideEvent* event) override; + void showEvent(QShowEvent* event) override; + void hideEvent(QHideEvent* event) override; -private: - std::unique_ptr shader_program; - GLuint VAO, VBO; - QElapsedTimer timer; + private: + std::unique_ptr shader_program; + GLuint VAO, VBO; + QElapsedTimer timer; - std::string code; - bool shader_needs_update {false}; + std::string code; + bool shader_needs_update{false}; - void init_shaders(); - void init_buffers(); - void update_shader_program(); + void init_shaders(); + void init_buffers(); + void update_shader_program(); - /** + /** * @brief Cleans up the OpenGL resources. * * @note This function is called automatically when the widget is destroyed. @@ -353,7 +353,7 @@ class ShaderPreviewerWidget : public QOpenGLWidget { * context current. * */ - void cleanup(); + void cleanup(); }; /**********************************************************************/ @@ -514,9 +514,9 @@ class VisualShaderGraphicsView : public QGraphicsView { VisualShaderGraphicsScene* scene; // Style - QColor background_color = QColor(40, 40, 40); // Dark Charcoal - QColor fine_grid_color = QColor(50, 50, 50); // Soft Dark Gray - QColor coarse_grid_color = QColor(30, 30, 30); // Muted Deep Gray + QColor background_color = QColor(40, 40, 40); // Dark Charcoal + QColor fine_grid_color = QColor(50, 50, 50); // Soft Dark Gray + QColor coarse_grid_color = QColor(30, 30, 30); // Muted Deep Gray // Scene Rect float t_size = std::numeric_limits::max(); // 32767 @@ -568,10 +568,8 @@ class VisualShaderNodeGraphicsObject : public QGraphicsObject { Q_OBJECT public: - VisualShaderNodeGraphicsObject(const int& n_id, - const QPointF& coordinate, - const std::shared_ptr& node, - QGraphicsItem* parent = nullptr); + VisualShaderNodeGraphicsObject(const int& n_id, const QPointF& coordinate, + const std::shared_ptr& node, QGraphicsItem* parent = nullptr); ~VisualShaderNodeGraphicsObject(); VisualShaderInputPortGraphicsObject* get_input_port_graphics_object(const int& p_index) const; @@ -668,10 +666,10 @@ class VisualShaderNodeGraphicsObject : public QGraphicsObject { std::unordered_map out_port_graphics_objects; // Style - QColor normal_boundary_color = QColor(220, 20, 60); // Crimson Red - QColor selected_boundary_color = QColor(255, 69, 0); // Red-Orange - QColor font_color = QColor(255, 255, 255); // Pure White - QColor fill_color = QColor(40, 40, 40, 200); // Semi-transparent Dark Gray + QColor normal_boundary_color = QColor(220, 20, 60); // Crimson Red + QColor selected_boundary_color = QColor(255, 69, 0); // Red-Orange + QColor font_color = QColor(255, 255, 255); // Pure White + QColor fill_color = QColor(40, 40, 40, 200); // Semi-transparent Dark Gray float pen_width = 1.0f; @@ -693,7 +691,7 @@ class VisualShaderNodeGraphicsObject : public QGraphicsObject { mutable float rect_padding; // Calculated in boundingRect() mutable float rect_margin; // Calculated in boundingRect() - float port_caption_spacing = 4.0f; // Distance between the port and its caption + float port_caption_spacing = 4.0f; // Distance between the port and its caption // Ports Style float connected_port_diameter = 8.0f; @@ -727,7 +725,7 @@ class VisualShaderNodeGraphicsObject : public QGraphicsObject { */ void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget = nullptr) override; QVariant itemChange(GraphicsItemChange change, const QVariant& value) override; - void contextMenuEvent(QGraphicsSceneContextMenuEvent *event) override; + void contextMenuEvent(QGraphicsSceneContextMenuEvent* event) override; }; class VisualShaderInputPortGraphicsObject : public QGraphicsObject { @@ -773,7 +771,7 @@ class VisualShaderInputPortGraphicsObject : public QGraphicsObject { // Style QColor font_color = QColor(255, 255, 255); - QColor connection_point_color = QColor(220, 20, 60); // Crimson + QColor connection_point_color = QColor(220, 20, 60); // Crimson float opacity = 1.0f; @@ -797,12 +795,16 @@ class VisualShaderOutputPortGraphicsObject : public QGraphicsObject { int get_node_id() const { return n_id; } int get_port_index() const { return p_index; } - std::vector get_connection_graphics_objects() const { return connection_graphics_objects; } - VisualShaderConnectionGraphicsObject* get_connection_graphics_object(const int& to_node_id, const int& to_port_index) const; + std::vector get_connection_graphics_objects() const { + return connection_graphics_objects; + } + VisualShaderConnectionGraphicsObject* get_connection_graphics_object(const int& to_node_id, + const int& to_port_index) const; void connect(VisualShaderConnectionGraphicsObject* c_o) { this->connection_graphics_objects.emplace_back(c_o); } void detach_connection(VisualShaderConnectionGraphicsObject* c_o) { - connection_graphics_objects.erase(std::remove(connection_graphics_objects.begin(), connection_graphics_objects.end(), c_o), - connection_graphics_objects.end()); + connection_graphics_objects.erase( + std::remove(connection_graphics_objects.begin(), connection_graphics_objects.end(), c_o), + connection_graphics_objects.end()); } bool is_connected() const { return connection_graphics_objects.size() > 0; } @@ -832,7 +834,7 @@ class VisualShaderOutputPortGraphicsObject : public QGraphicsObject { // Style QColor font_color = QColor(255, 255, 255); - QColor connection_point_color = QColor(220, 20, 60); // Crimson + QColor connection_point_color = QColor(220, 20, 60); // Crimson float opacity = 1.0f; @@ -894,9 +896,9 @@ class VisualShaderConnectionGraphicsObject : public QGraphicsObject { QPointF end_coordinate; // Style - QColor construction_color = QColor(139, 0, 0); // Dark Red - QColor normal_color = QColor(178, 34, 34); // Firebrick Red - QColor selected_color = QColor(55, 55, 55); // Dark Gray + QColor construction_color = QColor(139, 0, 0); // Dark Red + QColor normal_color = QColor(178, 34, 34); // Firebrick Red + QColor selected_color = QColor(55, 55, 55); // Dark Gray QColor connection_point_color = QColor(211, 211, 211); float line_width = 3.0f; @@ -936,13 +938,15 @@ class VisualShaderNodeEmbedWidget : public QWidget { VisualShaderNodeEmbedWidget(const std::shared_ptr& node, QWidget* parent = nullptr); ~VisualShaderNodeEmbedWidget(); - void set_shader_previewer_widget(QWidget* shader_previewer_widget) { this->shader_previewer_widget = shader_previewer_widget; } + void set_shader_previewer_widget(QWidget* shader_previewer_widget) { + this->shader_previewer_widget = shader_previewer_widget; + } Q_SIGNALS: void shader_preview_update_requested(); void node_update_requested(); - private Q_SLOTS: + private Q_SLOTS: void on_preview_shader_button_pressed() { bool is_visible{shader_previewer_widget->isVisible()}; shader_previewer_widget->setVisible(!is_visible); @@ -953,7 +957,7 @@ class VisualShaderNodeEmbedWidget : public QWidget { void on_node_update_requested() { Q_EMIT node_update_requested(); } - private: + private: QVBoxLayout* layout; QPushButton* preview_shader_button; diff --git a/Tests/Editors/VisualShaderEditorTests.cpp b/Tests/Editors/VisualShaderEditorTests.cpp index ef6283b2b..2b6135760 100644 --- a/Tests/Editors/VisualShaderEditorTests.cpp +++ b/Tests/Editors/VisualShaderEditorTests.cpp @@ -27,50 +27,50 @@ #include "Editors/VisualShaderEditorTests.h" -#include #include +#include void TestVisualShaderEditor::initTestCase() { editor = new VisualShaderEditor(); // Get the scene - VisualShaderGraphicsScene* scene {editor->get_scene()}; + VisualShaderGraphicsScene* scene{editor->get_scene()}; QVERIFY(scene != nullptr); { // Add an input UV node - bool result {scene->add_node("VisualShaderNodeInput", {-200, 0})}; + bool result{scene->add_node("VisualShaderNodeInput", {-200, 0})}; QVERIFY(result); } { // Add an input TIME node - bool result {scene->add_node("VisualShaderNodeInput", {-200, 350})}; + bool result{scene->add_node("VisualShaderNodeInput", {-200, 350})}; QVERIFY(result); } { // Add a value noise node - bool result {scene->add_node("VisualShaderNodeValueNoise", {-50, 0})}; + bool result{scene->add_node("VisualShaderNodeValueNoise", {-50, 0})}; QVERIFY(result); } { // Add a float function node: sin - bool result {scene->add_node("VisualShaderNodeFloatFunc", {-50, 350})}; + bool result{scene->add_node("VisualShaderNodeFloatFunc", {-50, 350})}; QVERIFY(result); } { // Add a divide operator node - bool result {scene->add_node("VisualShaderNodeFloatOp", {150, 150})}; + bool result{scene->add_node("VisualShaderNodeFloatOp", {150, 150})}; QVERIFY(result); } } -void TestVisualShaderEditor::init() { } +void TestVisualShaderEditor::init() {} void TestVisualShaderEditor::cleanupTestCase() { delete editor; } -void TestVisualShaderEditor::cleanup() { } +void TestVisualShaderEditor::cleanup() {} void TestVisualShaderEditor::test_create_full_graph() { editor->show(); @@ -79,86 +79,88 @@ void TestVisualShaderEditor::test_create_full_graph() { QVERIFY(QTest::qWaitForWindowExposed(editor)); // Get the scene - VisualShaderGraphicsScene* scene {editor->get_scene()}; + VisualShaderGraphicsScene* scene{editor->get_scene()}; QVERIFY(scene != nullptr); - VisualShaderGraphicsView* view {editor->get_view()}; + VisualShaderGraphicsView* view{editor->get_view()}; QVERIFY(view != nullptr); - VisualShaderOutputPortGraphicsObject* o_p_uv {nullptr}; + VisualShaderOutputPortGraphicsObject* o_p_uv{nullptr}; { - VisualShaderNodeGraphicsObject* uv_node {scene->get_node_graphics_object(1)}; // 1 is the node id + VisualShaderNodeGraphicsObject* uv_node{scene->get_node_graphics_object(1)}; // 1 is the node id QVERIFY(uv_node != nullptr); - o_p_uv = uv_node->get_output_port_graphics_object(0); // The only output port + o_p_uv = uv_node->get_output_port_graphics_object(0); // The only output port QVERIFY(o_p_uv != nullptr); - QWidget* embed_widget {uv_node->get_embed_widget()}; + QWidget* embed_widget{uv_node->get_embed_widget()}; QVERIFY(embed_widget != nullptr); - VisualShaderNodeInputEmbedWidget* uv_embed_widget {dynamic_cast(embed_widget)}; + VisualShaderNodeInputEmbedWidget* uv_embed_widget{dynamic_cast(embed_widget)}; QVERIFY(uv_embed_widget != nullptr); - uv_embed_widget->set_current_index(1); // 1 is UV + uv_embed_widget->set_current_index(1); // 1 is UV } - VisualShaderOutputPortGraphicsObject* o_p_time {nullptr}; + VisualShaderOutputPortGraphicsObject* o_p_time{nullptr}; { - VisualShaderNodeGraphicsObject* uv_node {scene->get_node_graphics_object(2)}; // 2 is the node id + VisualShaderNodeGraphicsObject* uv_node{scene->get_node_graphics_object(2)}; // 2 is the node id QVERIFY(uv_node != nullptr); - o_p_time = uv_node->get_output_port_graphics_object(0); // The only output port + o_p_time = uv_node->get_output_port_graphics_object(0); // The only output port QVERIFY(o_p_time != nullptr); - QWidget* embed_widget {uv_node->get_embed_widget()}; + QWidget* embed_widget{uv_node->get_embed_widget()}; QVERIFY(embed_widget != nullptr); - VisualShaderNodeInputEmbedWidget* uv_embed_widget {dynamic_cast(embed_widget)}; + VisualShaderNodeInputEmbedWidget* uv_embed_widget{dynamic_cast(embed_widget)}; QVERIFY(uv_embed_widget != nullptr); - uv_embed_widget->set_current_index(2); // 2 is TIME + uv_embed_widget->set_current_index(2); // 2 is TIME } - VisualShaderInputPortGraphicsObject* i_p_noise {nullptr}; - VisualShaderOutputPortGraphicsObject* o_p_noise {nullptr}; + VisualShaderInputPortGraphicsObject* i_p_noise{nullptr}; + VisualShaderOutputPortGraphicsObject* o_p_noise{nullptr}; { - VisualShaderNodeGraphicsObject* noise_node {scene->get_node_graphics_object(3)}; // 3 is the node id + VisualShaderNodeGraphicsObject* noise_node{scene->get_node_graphics_object(3)}; // 3 is the node id QVERIFY(noise_node != nullptr); - i_p_noise = noise_node->get_input_port_graphics_object(0); // The only input port + i_p_noise = noise_node->get_input_port_graphics_object(0); // The only input port QVERIFY(i_p_noise != nullptr); - o_p_noise = noise_node->get_output_port_graphics_object(0); // The only output port + o_p_noise = noise_node->get_output_port_graphics_object(0); // The only output port QVERIFY(o_p_noise != nullptr); } - VisualShaderInputPortGraphicsObject* i_p_sin {nullptr}; - VisualShaderOutputPortGraphicsObject* o_p_sin {nullptr}; + VisualShaderInputPortGraphicsObject* i_p_sin{nullptr}; + VisualShaderOutputPortGraphicsObject* o_p_sin{nullptr}; { - VisualShaderNodeGraphicsObject* sin_node {scene->get_node_graphics_object(4)}; // 4 is the node id + VisualShaderNodeGraphicsObject* sin_node{scene->get_node_graphics_object(4)}; // 4 is the node id QVERIFY(sin_node != nullptr); - i_p_sin = sin_node->get_input_port_graphics_object(0); // The only input port + i_p_sin = sin_node->get_input_port_graphics_object(0); // The only input port QVERIFY(i_p_sin != nullptr); - o_p_sin = sin_node->get_output_port_graphics_object(0); // The only output port + o_p_sin = sin_node->get_output_port_graphics_object(0); // The only output port QVERIFY(o_p_sin != nullptr); - QWidget* embed_widget {sin_node->get_embed_widget()}; + QWidget* embed_widget{sin_node->get_embed_widget()}; QVERIFY(embed_widget != nullptr); - VisualShaderNodeFloatFuncEmbedWidget* sin_embed_widget {dynamic_cast(embed_widget)}; + VisualShaderNodeFloatFuncEmbedWidget* sin_embed_widget{ + dynamic_cast(embed_widget)}; QVERIFY(sin_embed_widget != nullptr); - sin_embed_widget->set_current_index(0); // 0 is sin + sin_embed_widget->set_current_index(0); // 0 is sin } - VisualShaderInputPortGraphicsObject* i1_p_divide {nullptr}; - VisualShaderInputPortGraphicsObject* i2_p_divide {nullptr}; - VisualShaderOutputPortGraphicsObject* o_p_divide {nullptr}; + VisualShaderInputPortGraphicsObject* i1_p_divide{nullptr}; + VisualShaderInputPortGraphicsObject* i2_p_divide{nullptr}; + VisualShaderOutputPortGraphicsObject* o_p_divide{nullptr}; { - VisualShaderNodeGraphicsObject* divide_node {scene->get_node_graphics_object(5)}; // 5 is the node id + VisualShaderNodeGraphicsObject* divide_node{scene->get_node_graphics_object(5)}; // 5 is the node id QVERIFY(divide_node != nullptr); - i1_p_divide = divide_node->get_input_port_graphics_object(0); // The first input port + i1_p_divide = divide_node->get_input_port_graphics_object(0); // The first input port QVERIFY(i1_p_divide != nullptr); - i2_p_divide = divide_node->get_input_port_graphics_object(1); // The second input port + i2_p_divide = divide_node->get_input_port_graphics_object(1); // The second input port QVERIFY(i2_p_divide != nullptr); - o_p_divide = divide_node->get_output_port_graphics_object(0); // The only output port + o_p_divide = divide_node->get_output_port_graphics_object(0); // The only output port QVERIFY(o_p_divide != nullptr); - QWidget* embed_widget {divide_node->get_embed_widget()}; + QWidget* embed_widget{divide_node->get_embed_widget()}; QVERIFY(embed_widget != nullptr); - VisualShaderNodeFloatOpEmbedWidget* divide_embed_widget {dynamic_cast(embed_widget)}; + VisualShaderNodeFloatOpEmbedWidget* divide_embed_widget{ + dynamic_cast(embed_widget)}; QVERIFY(divide_embed_widget != nullptr); - divide_embed_widget->set_current_index(3); // 3 is divide + divide_embed_widget->set_current_index(3); // 3 is divide } { diff --git a/Tests/Editors/VisualShaderEditorTests.h b/Tests/Editors/VisualShaderEditorTests.h index e26296cbc..243afb13b 100644 --- a/Tests/Editors/VisualShaderEditorTests.h +++ b/Tests/Editors/VisualShaderEditorTests.h @@ -35,10 +35,10 @@ class TestVisualShaderEditor : public QObject { private slots: void initTestCase(); // Will be called before the first test function is executed. - void init(); // Will be called before each test function is executed. + void init(); // Will be called before each test function is executed. void cleanupTestCase(); // Will be called after the last test function was executed. - void cleanup(); // Will be called after every test function. + void cleanup(); // Will be called after every test function. void test_create_full_graph();