mirror of
https://github.com/arabine/open-story-teller.git
synced 2025-12-06 17:09:06 +01:00
more separation between node UI logic and editor, working Studio format support
This commit is contained in:
parent
6c4ffbbd62
commit
a6dbc90b78
19 changed files with 536 additions and 340 deletions
|
|
@ -90,6 +90,7 @@ set(BUILD_SHARED_LIBS TRUE)
|
|||
set(SDL_STATIC TRUE)
|
||||
FetchContent_MakeAvailable(sdl3)
|
||||
include_directories(${sdl3_SOURCE_DIR}/include)
|
||||
# add_subdirectory(${sdl3_SOURCE_DIR})
|
||||
|
||||
# =========================================================================================================================
|
||||
# SDL3-Image
|
||||
|
|
@ -127,20 +128,25 @@ set(SRCS
|
|||
src/main_window.cpp
|
||||
src/main_window.h
|
||||
|
||||
src/node_editor_window.cpp
|
||||
src/node_editor_window.h
|
||||
|
||||
src/library_window.cpp
|
||||
src/library_window.h
|
||||
|
||||
src/media_node.h
|
||||
src/media_node.cpp
|
||||
|
||||
src/platform_folders.cpp
|
||||
src/platform_folders.h
|
||||
|
||||
src/base_node.h
|
||||
src/base_node.cpp
|
||||
src/node_engine/base_node.h
|
||||
src/node_engine/base_node.cpp
|
||||
src/node_engine/connection.cpp
|
||||
src/node_engine/connection.h
|
||||
|
||||
|
||||
src/node_editor/media_node.h
|
||||
src/node_editor/media_node.cpp
|
||||
src/node_editor/base_node_widget.h
|
||||
src/node_editor/base_node_widget.cpp
|
||||
src/node_editor/node_editor_window.h
|
||||
src/node_editor/node_editor_window.cpp
|
||||
|
||||
src/resources_window.cpp
|
||||
src/resources_window.h
|
||||
|
|
@ -154,9 +160,6 @@ set(SRCS
|
|||
src/code_editor.cpp
|
||||
src/code_editor.h
|
||||
|
||||
src/connection.cpp
|
||||
src/connection.h
|
||||
|
||||
src/media_converter.cpp
|
||||
src/media_converter.h
|
||||
|
||||
|
|
@ -234,6 +237,8 @@ target_include_directories(${STORY_EDITOR_PROJECT} PUBLIC
|
|||
|
||||
${CMAKE_SOURCE_DIR}/src
|
||||
${CMAKE_SOURCE_DIR}/src/importers
|
||||
${CMAKE_SOURCE_DIR}/src/node_editor
|
||||
${CMAKE_SOURCE_DIR}/src/node_engine
|
||||
|
||||
../firmware/library
|
||||
../firmware/chip32
|
||||
|
|
@ -271,15 +276,16 @@ target_compile_definitions(${STORY_EDITOR_PROJECT} PUBLIC "$<$<CONFIG:DEBUG>:DEB
|
|||
|
||||
target_link_directories(${STORY_EDITOR_PROJECT} PUBLIC ${sdl3_BINARY_DIR} ${curl_BINARY_DIR} ${CMAKE_BINARY_DIR}/libs/SDL_image)
|
||||
|
||||
|
||||
set(SDL2_BIN_DIR ${sdl3_BINARY_DIR})
|
||||
# On est obligé de passer par une variable pour injecter
|
||||
# certaines informations à CPACK
|
||||
set(SDL_BIN_DIR ${sdl3_BINARY_DIR})
|
||||
|
||||
if(UNIX)
|
||||
target_link_libraries(${STORY_EDITOR_PROJECT}
|
||||
pthread
|
||||
OpenGL::GL
|
||||
dl
|
||||
SDL3
|
||||
SDL3::SDL3
|
||||
SDL3_image
|
||||
libcurl_static
|
||||
OpenSSL::SSL OpenSSL::Crypto
|
||||
|
|
@ -313,21 +319,19 @@ install_files("." FILES "${CMAKE_SOURCE_DIR}/LICENSE")
|
|||
install_files("." FILES "${CMAKE_SOURCE_DIR}/tools/imgui.ini")
|
||||
|
||||
if(WIN32)
|
||||
install_files("." FILES "${SDL2_BIN_DIR}/SDL2.dll")
|
||||
install_files("." FILES "${SDL_BIN_DIR}/SDL3.dll")
|
||||
install_files("." FILES "/usr/lib/gcc/x86_64-w64-mingw32/10-posix/libstdc++-6.dll")
|
||||
install_files("." FILES "/usr/x86_64-w64-mingw32/lib/libwinpthread-1.dll")
|
||||
install_files("." FILES "/usr/lib/gcc/x86_64-w64-mingw32/10-posix/libgcc_s_seh-1.dll")
|
||||
endif()
|
||||
|
||||
# Personnaliser l'icône pour les installateurs Windows
|
||||
if(WIN32)
|
||||
set(CPACK_NSIS_MUI_ICON "${CMAKE_SOURCE_DIR}/story-editor-logo.ico")
|
||||
# Personnaliser l'icône pour les installateurs Windows
|
||||
set(CPACK_NSIS_MUI_ICON "${CMAKE_SOURCE_DIR}/story-editor-logo.ico")
|
||||
endif()
|
||||
|
||||
if (APPLE)
|
||||
set(CPACK_GENERATOR "DragNDrop")
|
||||
set(MACOSX_BUNDLE_INFO_PLIST ${PROJECT_SOURCE_DIR}/bundle.plist.in)
|
||||
install_files("." FILES "${SDL2_BIN_DIR}/libSDL2-2.0.0.dylib")
|
||||
install_files("." FILES "${SDL_BIN_DIR}/libSDL2-2.0.0.dylib")
|
||||
endif()
|
||||
|
||||
include(CPack)
|
||||
|
|
|
|||
|
|
@ -46,8 +46,8 @@ public:
|
|||
|
||||
// Node interaction
|
||||
virtual void Build() = 0;
|
||||
virtual std::list<std::shared_ptr<Connection>> GetNodeConnections(unsigned long nodeId) = 0;
|
||||
virtual std::string GetNodeEntryLabel(unsigned long nodeId) = 0;
|
||||
virtual std::list<std::shared_ptr<Connection>> GetNodeConnections(const std::string &nodeId) = 0;
|
||||
virtual std::string GetNodeEntryLabel(const std::string &nodeId) = 0;
|
||||
virtual void Play() = 0;
|
||||
virtual void Ok() = 0;
|
||||
virtual void Pause() = 0;
|
||||
|
|
|
|||
|
|
@ -1,8 +1,3 @@
|
|||
#include "pack_archive.h"
|
||||
#include "ni_parser.h"
|
||||
#include "json.hpp"
|
||||
#include "serializers.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
#include <fstream>
|
||||
|
|
@ -10,6 +5,15 @@
|
|||
#include <iomanip>
|
||||
#include <filesystem>
|
||||
|
||||
|
||||
#include "pack_archive.h"
|
||||
#include "ni_parser.h"
|
||||
#include "json.hpp"
|
||||
#include "serializers.h"
|
||||
#include "story_project.h"
|
||||
#include "resource_manager.h"
|
||||
#include "uuid.h"
|
||||
|
||||
PackArchive::PackArchive()
|
||||
{
|
||||
|
||||
|
|
@ -411,6 +415,123 @@ std::string PackArchive::OpenImage(const std::string &fileName)
|
|||
return f;
|
||||
}
|
||||
|
||||
bool PackArchive::ImportStudioFormat(const std::string &fileName, const std::string &outputDir)
|
||||
{
|
||||
auto uuid = UUID().String();
|
||||
std::string basePath = outputDir + "/" + uuid;
|
||||
Unzip(fileName, basePath);
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
// STUDIO format
|
||||
std::ifstream f(basePath + "/story.json");
|
||||
nlohmann::json j = nlohmann::json::parse(f);
|
||||
StoryProject proj;
|
||||
ResourceManager res;
|
||||
nlohmann::json model;
|
||||
|
||||
if (j.contains("title"))
|
||||
{
|
||||
proj.New(uuid, outputDir);
|
||||
proj.SetName(j["title"].get<std::string>());
|
||||
|
||||
// Create resources, scan asset files
|
||||
std::filesystem::path directoryPath(basePath + "/assets");
|
||||
if (std::filesystem::exists(directoryPath) && std::filesystem::is_directory(directoryPath))
|
||||
{
|
||||
for (const auto& entry : std::filesystem::directory_iterator(directoryPath))
|
||||
{
|
||||
if (std::filesystem::is_regular_file(entry.path()))
|
||||
{
|
||||
// Si c'est un sous-répertoire, récursivement scanner le contenu
|
||||
auto rData = std::make_shared<Resource>();
|
||||
|
||||
rData->file = entry.path().filename();
|
||||
rData->type = ResourceManager::ExtentionInfo(entry.path().extension(), 1);
|
||||
rData->format = ResourceManager::ExtentionInfo(entry.path().extension(), 0);
|
||||
res.Add(rData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Key: actionNode, value: Stage UUID
|
||||
std::map<std::string, std::string> stageActionLink;
|
||||
|
||||
nlohmann::json jnodes = nlohmann::json::array();
|
||||
for (const auto & n : j["stageNodes"])
|
||||
{
|
||||
nlohmann::json node;
|
||||
|
||||
auto node_uuid = n["uuid"].get<std::string>();
|
||||
node["uuid"] = node_uuid;
|
||||
node["type"] = "media-node";
|
||||
node["position"] = n["position"];
|
||||
|
||||
nlohmann::json internalData;
|
||||
auto img = n["image"];
|
||||
internalData["image"] = img.is_string() ? img.get<std::string>() : "";
|
||||
auto audio = n["audio"];
|
||||
internalData["sound"] = audio.is_string() ? audio.get<std::string>() : "";
|
||||
|
||||
node["internal-data"] = internalData;
|
||||
|
||||
stageActionLink[n["okTransition"]["actionNode"]] = node_uuid;
|
||||
/*
|
||||
"okTransition":{
|
||||
"actionNode":"19d7328f-d0d2-4443-a7a2-25270dafe52c",
|
||||
"optionIndex":0
|
||||
},
|
||||
*/
|
||||
|
||||
jnodes.push_back(node);
|
||||
}
|
||||
|
||||
model["nodes"] = jnodes;
|
||||
|
||||
nlohmann::json connections = nlohmann::json::array();
|
||||
|
||||
for (const auto & n : j["actionNodes"])
|
||||
{
|
||||
std::string action_node_uuid = n["id"].get<std::string>(); // le champs est "id" et non pas "uuid", pénible
|
||||
|
||||
if (stageActionLink.count(action_node_uuid) > 0)
|
||||
{
|
||||
|
||||
int i = 0;
|
||||
for (const auto & m : n["options"])
|
||||
{
|
||||
nlohmann::json c;
|
||||
|
||||
c["outNodeId"] = stageActionLink[action_node_uuid];
|
||||
c["outPortIndex"] = i;
|
||||
c["inNodeId"] = m; // On prend le stage node;
|
||||
c["inPortIndex"] = 0;
|
||||
|
||||
i++;
|
||||
connections.push_back(c);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "ActionNode UUID not found" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
model["connections"] = connections;
|
||||
|
||||
// Save on disk
|
||||
proj.Save(model, res);
|
||||
}
|
||||
}
|
||||
catch(std::exception &e)
|
||||
{
|
||||
std::cout << e.what() << std::endl;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string PackArchive::GetImage(const std::string &fileName)
|
||||
{
|
||||
//"C8B39950DE174EAA8E852A07FC468267/rf/000/05FB5530"
|
||||
|
|
|
|||
|
|
@ -14,6 +14,9 @@ public:
|
|||
|
||||
bool Load(const std::string &filePath);
|
||||
std::string OpenImage(const std::string &fileName);
|
||||
|
||||
bool ImportStudioFormat(const std::string &fileName, const std::string &outputDir);
|
||||
|
||||
std::string CurrentImage();
|
||||
std::string CurrentSound();
|
||||
std::string CurrentSoundName();
|
||||
|
|
|
|||
|
|
@ -235,8 +235,6 @@ void MainWindow::DrawStatusBar()
|
|||
ImGuiWindowFlags windowFlags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoDocking;
|
||||
ImGui::Begin("StatusBar", nullptr, windowFlags);
|
||||
|
||||
|
||||
|
||||
if (true)
|
||||
{
|
||||
float dy = ImGui::GetFontSize() * 0.15f;
|
||||
|
|
@ -657,117 +655,7 @@ void MainWindow::ImportProject(const std::string &fileName, int format)
|
|||
{
|
||||
PackArchive archive;
|
||||
|
||||
Log("Decompressing " + fileName);
|
||||
auto uuid = UUID().String();
|
||||
|
||||
std::string basePath = m_libraryManager.LibraryPath() + "/" + uuid;
|
||||
|
||||
archive.Unzip(fileName, basePath);
|
||||
|
||||
// Ok try to convert the archive into our format
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
// STUDIO format
|
||||
std::ifstream f(basePath + "/story.json");
|
||||
nlohmann::json j = nlohmann::json::parse(f);
|
||||
StoryProject proj;
|
||||
ResourceManager res;
|
||||
nlohmann::json model;
|
||||
|
||||
if (j.contains("title"))
|
||||
{
|
||||
proj.New(uuid, m_libraryManager.LibraryPath());
|
||||
proj.SetName(j["title"].get<std::string>());
|
||||
|
||||
// Create resources, scan asset files
|
||||
std::filesystem::path directoryPath(basePath + "/assets");
|
||||
if (std::filesystem::exists(directoryPath) && std::filesystem::is_directory(directoryPath))
|
||||
{
|
||||
for (const auto& entry : std::filesystem::directory_iterator(directoryPath))
|
||||
{
|
||||
if (std::filesystem::is_regular_file(entry.path()))
|
||||
{
|
||||
// Si c'est un sous-répertoire, récursivement scanner le contenu
|
||||
std::string asset = entry.path().filename();
|
||||
auto rData = std::make_shared<Resource>();
|
||||
|
||||
rData->type = ResourceManager::ExtentionInfo(entry.path().extension(), 1);
|
||||
rData->format = ResourceManager::ExtentionInfo(entry.path().extension(), 0);
|
||||
res.Add(rData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create model
|
||||
|
||||
int ids = 1;
|
||||
nlohmann::json nodes = nlohmann::json::array();
|
||||
for (const auto & n : j["stageNodes"])
|
||||
{
|
||||
nlohmann::json node;
|
||||
node["id"] = ids++;
|
||||
node["uuid"] = n["uuid"].get<std::string>();
|
||||
node["type"] = "media-node";
|
||||
|
||||
auto type = n["type"].get<std::string>();
|
||||
|
||||
int outPortCount = 1;
|
||||
int inPortCount = 1;
|
||||
|
||||
if (type == "stage") {
|
||||
outPortCount = 1;
|
||||
inPortCount = 1;
|
||||
}
|
||||
|
||||
node["outPortCount"] = outPortCount;
|
||||
node["inPortCount"] = inPortCount;
|
||||
node["position"] = n["position"];
|
||||
|
||||
nlohmann::json internalData;
|
||||
auto img = n["image"];
|
||||
internalData["image"] = img.is_string() ? img.get<std::string>() : "";
|
||||
auto audio = n["audio"];
|
||||
internalData["sound"] = audio.is_string() ? audio.get<std::string>() : "";
|
||||
|
||||
node["internal-data"] = internalData;
|
||||
nodes.push_back(node);
|
||||
}
|
||||
|
||||
model["nodes"] = nodes;
|
||||
|
||||
// Save links
|
||||
nlohmann::json connections = nlohmann::json::array();
|
||||
|
||||
/*
|
||||
for (const auto& linkInfo : m_links)
|
||||
{
|
||||
|
||||
nlohmann::json c;
|
||||
|
||||
Connection cnx = LinkToModel(linkInfo->ed_link->InputId, linkInfo->ed_link->OutputId);
|
||||
|
||||
c["outNodeId"] = cnx.outNodeId;
|
||||
c["outPortIndex"] = cnx.outPortIndex;
|
||||
c["inNodeId"] = cnx.inNodeId;
|
||||
c["inPortIndex"] = cnx.inPortIndex;
|
||||
|
||||
connections.push_back(c);
|
||||
}*/
|
||||
|
||||
model["connections"] = connections;
|
||||
|
||||
|
||||
}
|
||||
|
||||
// Save on disk
|
||||
proj.Save(model, res);
|
||||
}
|
||||
catch(std::exception &e)
|
||||
{
|
||||
std::cout << e.what() << std::endl;
|
||||
}
|
||||
archive.ImportStudioFormat(fileName, m_libraryManager.LibraryPath());
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -923,12 +811,12 @@ void MainWindow::Build()
|
|||
ConvertResources();
|
||||
}
|
||||
|
||||
std::string MainWindow::GetNodeEntryLabel(unsigned long nodeId)
|
||||
std::string MainWindow::GetNodeEntryLabel(const std::string &nodeId)
|
||||
{
|
||||
return m_nodeEditorWindow.GetNodeEntryLabel(nodeId);
|
||||
}
|
||||
|
||||
std::list<std::shared_ptr<Connection>> MainWindow::GetNodeConnections(unsigned long nodeId)
|
||||
std::list<std::shared_ptr<Connection>> MainWindow::GetNodeConnections(const std::string &nodeId)
|
||||
{
|
||||
return m_nodeEditorWindow.GetNodeConnections(nodeId);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -135,8 +135,8 @@ private:
|
|||
virtual std::pair<FilterIterator, FilterIterator> Resources() override;
|
||||
virtual void DeleteResource(FilterIterator &it) override;
|
||||
virtual void Build() override;
|
||||
virtual std::list<std::shared_ptr<Connection>> GetNodeConnections(unsigned long nodeId) override;
|
||||
virtual std::string GetNodeEntryLabel(unsigned long nodeId) override;
|
||||
virtual std::list<std::shared_ptr<Connection>> GetNodeConnections(const std::string &nodeId) override;
|
||||
virtual std::string GetNodeEntryLabel(const std::string &nodeId) override;
|
||||
virtual void Play() override;
|
||||
virtual void Ok() override;
|
||||
virtual void Pause() override;
|
||||
|
|
|
|||
|
|
@ -157,3 +157,101 @@ int MediaConverter::Mp3ToWav(const std::string &inputFileName, const std::string
|
|||
|
||||
return cSuccess;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
OGG TO WAV
|
||||
|
||||
#define STB_VORBIS_HEADER_ONLY
|
||||
#include "stb_vorbis.c"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
// Function to read the entire contents of a file into memory
|
||||
unsigned char* read_entire_file(const char* filename, int* length) {
|
||||
FILE* f = fopen(filename, "rb");
|
||||
if (!f) return NULL;
|
||||
fseek(f, 0, SEEK_END);
|
||||
long size = ftell(f);
|
||||
fseek(f, 0, SEEK_SET);
|
||||
unsigned char* buffer = (unsigned char*)malloc(size);
|
||||
if (!buffer) {
|
||||
fclose(f);
|
||||
return NULL;
|
||||
}
|
||||
fread(buffer, 1, size, f);
|
||||
fclose(f);
|
||||
if (length) *length = (int)size;
|
||||
return buffer;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
if (argc != 3) {
|
||||
printf("Usage: %s input.ogg output.wav\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
const char* input_filename = argv[1];
|
||||
const char* output_filename = argv[2];
|
||||
|
||||
int ogg_length;
|
||||
unsigned char* ogg_data = read_entire_file(input_filename, &ogg_length);
|
||||
if (!ogg_data) {
|
||||
printf("Failed to read input file %s\n", input_filename);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int channels, sample_rate;
|
||||
short* samples = stb_vorbis_decode_memory(ogg_data, ogg_length, &channels, &sample_rate);
|
||||
if (!samples) {
|
||||
printf("Failed to decode OGG file %s\n", input_filename);
|
||||
free(ogg_data);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Write WAV file header
|
||||
FILE* wav_file = fopen(output_filename, "wb");
|
||||
if (!wav_file) {
|
||||
printf("Failed to create output file %s\n", output_filename);
|
||||
free(ogg_data);
|
||||
free(samples);
|
||||
return 1;
|
||||
}
|
||||
fwrite("RIFF", 1, 4, wav_file);
|
||||
int total_size = 36 + channels * (sample_rate * sizeof(short));
|
||||
fwrite(&total_size, 4, 1, wav_file);
|
||||
fwrite("WAVEfmt ", 1, 8, wav_file);
|
||||
int format_size = 16;
|
||||
fwrite(&format_size, 4, 1, wav_file);
|
||||
short format_type = 1; // PCM
|
||||
fwrite(&format_type, 2, 1, wav_file);
|
||||
fwrite(&channels, 2, 1, wav_file);
|
||||
fwrite(&sample_rate, 4, 1, wav_file);
|
||||
int byte_rate = sample_rate * channels * sizeof(short);
|
||||
fwrite(&byte_rate, 4, 1, wav_file);
|
||||
short block_align = channels * sizeof(short);
|
||||
fwrite(&block_align, 2, 1, wav_file);
|
||||
short bits_per_sample = 16;
|
||||
fwrite(&bits_per_sample, 2, 1, wav_file);
|
||||
fwrite("data", 1, 4, wav_file);
|
||||
int data_size = channels * (sample_rate * sizeof(short));
|
||||
fwrite(&data_size, 4, 1, wav_file);
|
||||
|
||||
// Write sample data
|
||||
fwrite(samples, sizeof(short), sample_rate * channels, wav_file);
|
||||
|
||||
fclose(wav_file);
|
||||
|
||||
free(ogg_data);
|
||||
free(samples);
|
||||
|
||||
printf("Conversion complete. WAV file saved as %s\n", output_filename);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
*/
|
||||
|
||||
|
|
|
|||
|
|
@ -1,25 +1,24 @@
|
|||
#include "base_node.h"
|
||||
#include "base_node_widget.h"
|
||||
#include "uuid.h"
|
||||
|
||||
#include "IconsMaterialDesignIcons.h"
|
||||
|
||||
unsigned long BaseNode::s_nextId = 1;
|
||||
unsigned long BaseNodeWidget::s_nextId = 1;
|
||||
|
||||
BaseNode::BaseNode(const std::string &title, IStoryManager &proj)
|
||||
: m_story(proj)
|
||||
BaseNodeWidget::BaseNodeWidget(const std::string &type, IStoryManager &proj)
|
||||
: BaseNode(type)
|
||||
, m_story(proj)
|
||||
{
|
||||
// m_id = UUID().String();
|
||||
|
||||
m_id = -1; // Story Project Node ID
|
||||
m_node = std::make_unique<Node>(GetNextId(), title.c_str()); // ImGui internal ID
|
||||
m_node = std::make_unique<Node>(GetNextId(), ""); // ImGui internal ID
|
||||
}
|
||||
|
||||
void BaseNode::AddInput()
|
||||
void BaseNodeWidget::AddInput()
|
||||
{
|
||||
m_node->Inputs.emplace_back(GetNextId(), "", PinType::Flow);
|
||||
}
|
||||
|
||||
void BaseNode::AddOutputs(int num)
|
||||
void BaseNodeWidget::AddOutputs(int num)
|
||||
{
|
||||
for (int i = 0; i < num; i++)
|
||||
{
|
||||
|
|
@ -27,7 +26,7 @@ void BaseNode::AddOutputs(int num)
|
|||
}
|
||||
}
|
||||
|
||||
void BaseNode::SetOutputs(uint32_t num)
|
||||
void BaseNodeWidget::SetOutputs(uint32_t num)
|
||||
{
|
||||
if (num > Outputs())
|
||||
{
|
||||
|
|
@ -42,35 +41,38 @@ void BaseNode::SetOutputs(uint32_t num)
|
|||
}
|
||||
}
|
||||
|
||||
void BaseNode::DeleteOutput()
|
||||
void BaseNodeWidget::DeleteOutput()
|
||||
{
|
||||
m_node->Outputs.pop_back();
|
||||
}
|
||||
|
||||
void BaseNode::SetPosition(float x, float y)
|
||||
|
||||
void BaseNodeWidget::Initialize()
|
||||
{
|
||||
m_pos.x = x;
|
||||
m_pos.y = y;
|
||||
m_firstFrame = true;
|
||||
m_firstFrame = true;
|
||||
|
||||
}
|
||||
|
||||
void BaseNode::FrameStart()
|
||||
|
||||
void BaseNodeWidget::FrameStart()
|
||||
{
|
||||
ed::BeginNode(m_node->ID);
|
||||
|
||||
if (m_firstFrame)
|
||||
{
|
||||
ed::SetNodePosition(m_node->ID, ImVec2(m_pos.x, m_pos.y));
|
||||
// Use the parent node position, the one saved in the JSON project
|
||||
// FIXME: find a better way to do that?
|
||||
ed::SetNodePosition(m_node->ID, ImVec2(BaseNode::GetX(), BaseNode::GetY()));
|
||||
}
|
||||
m_firstFrame = false;
|
||||
}
|
||||
|
||||
void BaseNode::FrameEnd()
|
||||
void BaseNodeWidget::FrameEnd()
|
||||
{
|
||||
ed::EndNode();
|
||||
}
|
||||
|
||||
void BaseNode::DrawPins()
|
||||
void BaseNodeWidget::DrawPins()
|
||||
{
|
||||
static const char *str = "#1 >";
|
||||
static float textWidth = ImGui::CalcTextSize(str).x;
|
||||
|
|
@ -94,13 +96,13 @@ void BaseNode::DrawPins()
|
|||
}
|
||||
}
|
||||
|
||||
float BaseNode::GetX() const
|
||||
float BaseNodeWidget::GetX() const
|
||||
{
|
||||
auto pos = GetNodePosition(m_node->ID);
|
||||
return pos.x;
|
||||
}
|
||||
|
||||
float BaseNode::GetY() const
|
||||
float BaseNodeWidget::GetY() const
|
||||
{
|
||||
auto pos = GetNodePosition(m_node->ID);
|
||||
return pos.y;
|
||||
|
|
@ -2,12 +2,12 @@
|
|||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include "json.hpp"
|
||||
#include <random>
|
||||
#include <string>
|
||||
|
||||
#include "json.hpp"
|
||||
#include "i_story_manager.h"
|
||||
#include "base_node.h"
|
||||
|
||||
#include <imgui_node_editor.h>
|
||||
namespace ed = ax::NodeEditor;
|
||||
|
|
@ -91,8 +91,11 @@ struct Link
|
|||
}
|
||||
};
|
||||
|
||||
|
||||
class BaseNode
|
||||
/**
|
||||
* @brief Basically a wrapper class around ImGuiNodeEditor Node structure
|
||||
*
|
||||
*/
|
||||
class BaseNodeWidget : public BaseNode
|
||||
{
|
||||
public:
|
||||
struct NodePosition
|
||||
|
|
@ -101,8 +104,9 @@ public:
|
|||
float y;
|
||||
};
|
||||
|
||||
BaseNode(const std::string &title, IStoryManager &proj);
|
||||
BaseNodeWidget(const std::string &type, IStoryManager &proj);
|
||||
|
||||
virtual void Initialize();
|
||||
|
||||
virtual void Draw() = 0;
|
||||
virtual void DrawProperties() = 0;
|
||||
|
|
@ -110,45 +114,20 @@ public:
|
|||
virtual std::string Build() = 0;
|
||||
virtual std::string GetEntryLabel() = 0;
|
||||
|
||||
void SetPosition(float x, float y);
|
||||
|
||||
void FrameStart();
|
||||
void FrameEnd();
|
||||
|
||||
void DrawPins();
|
||||
|
||||
float GetX() const;
|
||||
float GetY() const;
|
||||
virtual float GetX() const;
|
||||
virtual float GetY() const;
|
||||
|
||||
|
||||
uint32_t Inputs() const { return m_node->Inputs.size(); }
|
||||
uint32_t Outputs() const { return m_node->Outputs.size(); }
|
||||
|
||||
void SetType(const std::string &type)
|
||||
{
|
||||
m_type = type;
|
||||
}
|
||||
|
||||
std::string GetType() const
|
||||
{
|
||||
return m_type;
|
||||
}
|
||||
|
||||
void SetId(unsigned long id) { m_id = id; }
|
||||
unsigned long GetId() const { return m_id; }
|
||||
unsigned long GetInternalId() const { return m_node->ID.Get(); }
|
||||
|
||||
void SeTitle(const std::string &title) { m_title = title; }
|
||||
std::string GetTitle() const { return m_title; }
|
||||
|
||||
virtual void FromJson(const nlohmann::json &) = 0;
|
||||
virtual void ToJson(nlohmann::json &) = 0;
|
||||
|
||||
virtual nlohmann::json ToJson() const {
|
||||
nlohmann::json j;
|
||||
|
||||
j["type"] = m_type;
|
||||
return j;
|
||||
}
|
||||
|
||||
static unsigned long GetNextId()
|
||||
{
|
||||
|
|
@ -227,16 +206,9 @@ private:
|
|||
|
||||
std::unique_ptr<Node> m_node;
|
||||
|
||||
std::string m_title{"Base node"};
|
||||
std::string m_type;
|
||||
unsigned long m_id;
|
||||
NodePosition m_pos;
|
||||
bool m_firstFrame{true};
|
||||
|
||||
static unsigned long s_nextId;
|
||||
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
|
@ -7,7 +7,7 @@ namespace ed = ax::NodeEditor;
|
|||
#include "story_project.h"
|
||||
|
||||
MediaNode::MediaNode(const std::string &title, IStoryManager &proj)
|
||||
: BaseNode(title, proj)
|
||||
: BaseNodeWidget(title, proj)
|
||||
, m_story(proj)
|
||||
{
|
||||
// Create defaut one input and one output
|
||||
|
|
@ -22,7 +22,7 @@ MediaNode::MediaNode(const std::string &title, IStoryManager &proj)
|
|||
|
||||
void MediaNode::Draw()
|
||||
{
|
||||
BaseNode::FrameStart();
|
||||
BaseNodeWidget::FrameStart();
|
||||
|
||||
|
||||
static ImGuiTableFlags flags = ImGuiTableFlags_Borders |
|
||||
|
|
@ -69,11 +69,11 @@ void MediaNode::Draw()
|
|||
uint32_t counter = Outputs();
|
||||
float spacing = ImGui::GetStyle().ItemInnerSpacing.x;
|
||||
ImGui::PushButtonRepeat(true);
|
||||
std::string leftSingle = "##left" + std::to_string(GetId());
|
||||
std::string leftSingle = "##left" + GetId();
|
||||
if (ImGui::ArrowButton(leftSingle.c_str(), ImGuiDir_Left)) { if (counter > 1) counter--; }
|
||||
ImGui::SameLine(0.0f, spacing);
|
||||
|
||||
std::string rightSingle = "##right" + std::to_string(GetId());
|
||||
std::string rightSingle = "##right" + GetId();
|
||||
if (ImGui::ArrowButton(rightSingle.c_str(), ImGuiDir_Right))
|
||||
{
|
||||
counter++;
|
||||
|
|
@ -86,7 +86,7 @@ void MediaNode::Draw()
|
|||
|
||||
DrawPins();
|
||||
|
||||
BaseNode::FrameEnd();
|
||||
BaseNodeWidget::FrameEnd();
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -96,18 +96,22 @@ void MediaNode::Draw()
|
|||
"image": "fairy.png",
|
||||
"sound": "la_fee_luminelle.mp3"
|
||||
},
|
||||
|
||||
*/
|
||||
void MediaNode::FromJson(const nlohmann::json &j)
|
||||
void MediaNode::Initialize()
|
||||
{
|
||||
BaseNodeWidget::Initialize();
|
||||
nlohmann::json j = GetInternalData();
|
||||
SetImage(j["image"].get<std::string>());
|
||||
SetSound(j["sound"].get<std::string>());
|
||||
}
|
||||
|
||||
void MediaNode::ToJson(nlohmann::json &j)
|
||||
void MediaNode::StoreInternalData()
|
||||
{
|
||||
nlohmann::json j;
|
||||
j["image"] = m_image.name;
|
||||
j["sound"] = m_soundName;
|
||||
|
||||
SetInternalData(j);
|
||||
}
|
||||
|
||||
void MediaNode::DrawProperties()
|
||||
|
|
@ -186,12 +190,14 @@ void MediaNode::SetImage(const std::string &f)
|
|||
{
|
||||
m_image.name = f;
|
||||
m_image.Load(m_story.BuildFullAssetsPath(f));
|
||||
StoreInternalData();
|
||||
}
|
||||
|
||||
void MediaNode::SetSound(const std::string &f)
|
||||
{
|
||||
m_soundName = f;
|
||||
m_soundPath = m_story.BuildFullAssetsPath(m_soundName);
|
||||
StoreInternalData();
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -5,35 +5,36 @@
|
|||
#include <mutex>
|
||||
#include <set>
|
||||
|
||||
#include "base_node.h"
|
||||
#include "base_node_widget.h"
|
||||
#include "i_story_manager.h"
|
||||
#include "gui.h"
|
||||
#include <imgui_node_editor.h>
|
||||
|
||||
|
||||
class MediaNode : public BaseNode
|
||||
class MediaNode : public BaseNodeWidget
|
||||
{
|
||||
public:
|
||||
MediaNode(const std::string &title, IStoryManager &proj);
|
||||
|
||||
void Draw() override;
|
||||
|
||||
virtual void FromJson(const nlohmann::json &j) override;
|
||||
virtual void ToJson(nlohmann::json &j) override;
|
||||
virtual void DrawProperties() override;
|
||||
virtual std::string Build() override;
|
||||
virtual std::string GetEntryLabel() override;
|
||||
virtual std::string GenerateConstants() override;
|
||||
|
||||
virtual void Initialize() override;
|
||||
|
||||
|
||||
private:
|
||||
IStoryManager &m_story;
|
||||
Gui::Image m_image;
|
||||
std::string m_soundName;
|
||||
std::string m_soundPath;
|
||||
|
||||
std::string m_id;
|
||||
|
||||
std::string m_buttonUniqueName;
|
||||
|
||||
void SetImage(const std::string &f);
|
||||
void SetSound(const std::string &f);
|
||||
std::string ChoiceLabel() const;
|
||||
void StoreInternalData();
|
||||
};
|
||||
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
#include "media_node.h"
|
||||
#include "gui.h"
|
||||
#include "uuid.h"
|
||||
|
||||
#include <stdexcept> // for std::runtime_error
|
||||
#define JSON_ASSERT(x) \
|
||||
|
|
@ -45,59 +46,15 @@ void NodeEditorWindow::Initialize()
|
|||
void NodeEditorWindow::Clear()
|
||||
{
|
||||
m_nodes.clear();
|
||||
m_ids.clear();
|
||||
}
|
||||
|
||||
|
||||
|
||||
void NodeEditorWindow::LoadNode(const nlohmann::json &nodeJson)
|
||||
std::string NodeEditorWindow::GenerateNodeId()
|
||||
{
|
||||
try
|
||||
{
|
||||
int restoredNodeId = nodeJson["id"].get<int>();
|
||||
nlohmann::json internalDataJson = nodeJson["internal-data"];
|
||||
std::string type = nodeJson["type"].get<std::string>();
|
||||
|
||||
auto n = createNode(type, "", m_story);
|
||||
if (n)
|
||||
{
|
||||
n->SetType(type); // FIXME: set type in createNode factory?
|
||||
n->SetId(restoredNodeId);
|
||||
nlohmann::json posJson = nodeJson["position"];
|
||||
n->SetOutputs(nodeJson["outPortCount"].get<int>());
|
||||
n->SetPosition(posJson["x"].get<double>(), posJson["y"].get<double>());
|
||||
n->FromJson(internalDataJson);
|
||||
|
||||
m_ids.insert(restoredNodeId);
|
||||
|
||||
m_nodes.push_back(n);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::logic_error(std::string("No registered model with name ") + type);
|
||||
}
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cout << "ERROR: " << e.what() << std::endl;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int NodeEditorWindow::GenerateNodeId()
|
||||
{
|
||||
int max = 1;
|
||||
if (m_ids.size() > 0)
|
||||
{
|
||||
auto max = *m_ids.rbegin();
|
||||
max++;
|
||||
m_ids.insert(max);
|
||||
}
|
||||
return max;
|
||||
return UUID().String();
|
||||
}
|
||||
|
||||
|
||||
ed::PinId NodeEditorWindow::GetInputPin(unsigned long modelNodeId, int pinIndex)
|
||||
ed::PinId NodeEditorWindow::GetInputPin(const std::string &modelNodeId, int pinIndex)
|
||||
{
|
||||
ed::PinId id = 0;
|
||||
|
||||
|
|
@ -106,18 +63,19 @@ ed::PinId NodeEditorWindow::GetInputPin(unsigned long modelNodeId, int pinIndex)
|
|||
if (n->GetId() == modelNodeId)
|
||||
{
|
||||
id = n->GetInputPinAt(pinIndex);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (id.Get() == 0)
|
||||
{
|
||||
std::cout << "Invalid Id, input pin not found" << std::endl;
|
||||
std::cout << "Invalid Id: " << modelNodeId << " input pin: " << pinIndex <<" not found" << std::endl;
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
ed::PinId NodeEditorWindow::GetOutputPin(unsigned long modelNodeId, int pinIndex)
|
||||
ed::PinId NodeEditorWindow::GetOutputPin(const std::string &modelNodeId, int pinIndex)
|
||||
{
|
||||
ed::PinId id = 0;
|
||||
|
||||
|
|
@ -126,12 +84,13 @@ ed::PinId NodeEditorWindow::GetOutputPin(unsigned long modelNodeId, int pinIndex
|
|||
if (n->GetId() == modelNodeId)
|
||||
{
|
||||
id = n->GetOutputPinAt(pinIndex);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (id.Get() == 0)
|
||||
{
|
||||
std::cout << "Invalid Id, output pin not found" << std::endl;
|
||||
std::cout << "Invalid Id: " << modelNodeId << " output pin: " << pinIndex <<" not found" << std::endl;
|
||||
}
|
||||
|
||||
return id;
|
||||
|
|
@ -141,15 +100,27 @@ void NodeEditorWindow::Load(const nlohmann::json &model)
|
|||
{
|
||||
try {
|
||||
|
||||
|
||||
nlohmann::json nodesJsonArray = model["nodes"];
|
||||
|
||||
BaseNode::InitId();
|
||||
BaseNodeWidget::InitId();
|
||||
m_nodes.clear();
|
||||
m_links.clear();
|
||||
|
||||
for (auto& element : nodesJsonArray) {
|
||||
LoadNode(element);
|
||||
|
||||
std::string type = element["type"].get<std::string>();
|
||||
|
||||
auto n = createNode(type, m_story);
|
||||
if (n)
|
||||
{
|
||||
n->FromJson(element);
|
||||
n->Initialize();
|
||||
m_nodes.push_back(n);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::logic_error(std::string("No registered model with name ") + type);
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << model.dump(4) << std::endl;
|
||||
|
|
@ -160,20 +131,47 @@ void NodeEditorWindow::Load(const nlohmann::json &model)
|
|||
{
|
||||
nlohmann::json connectionJsonArray = model["connections"];
|
||||
|
||||
// key: node UUID, value: output counts
|
||||
std::map<std::string, int> outputCounts;
|
||||
|
||||
for (auto& connection : connectionJsonArray)
|
||||
{
|
||||
Connection model = connection.get<Connection>();
|
||||
|
||||
|
||||
if (outputCounts.count(model.outNodeId) > 0)
|
||||
{
|
||||
outputCounts[model.outNodeId]++;
|
||||
}
|
||||
else
|
||||
{
|
||||
outputCounts[model.outNodeId] = 1;
|
||||
}
|
||||
|
||||
// Adjust the number of outputs for all nodes
|
||||
for (auto n : m_nodes)
|
||||
{
|
||||
if (outputCounts.count(n->GetId()) > 0)
|
||||
{
|
||||
n->SetOutputs(outputCounts[n->GetId()]);
|
||||
}
|
||||
}
|
||||
|
||||
CreateLink(model,
|
||||
GetInputPin(model.inNodeId, model.inPortIndex),
|
||||
GetOutputPin(model.outNodeId, model.outPortIndex));
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
m_loaded = true;
|
||||
}
|
||||
catch(std::exception &e)
|
||||
{
|
||||
std::cout << e.what() << std::endl;
|
||||
std::cout << "(NodeEditorWindow::Load) " << e.what() << std::endl;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -186,7 +184,7 @@ void NodeEditorWindow::CreateLink(const Connection &model, ed::PinId inId, ed::P
|
|||
*conn->model = model;
|
||||
|
||||
// ImGui stuff for links
|
||||
conn->ed_link->Id = BaseNode::GetNextId();
|
||||
conn->ed_link->Id = BaseNodeWidget::GetNextId();
|
||||
conn->ed_link->InputId = inId;
|
||||
conn->ed_link->OutputId = outId;
|
||||
|
||||
|
|
@ -202,23 +200,7 @@ void NodeEditorWindow::Save(nlohmann::json &model)
|
|||
nlohmann::json nodes = nlohmann::json::array();
|
||||
for (const auto & n : m_nodes)
|
||||
{
|
||||
nlohmann::json node;
|
||||
node["id"] = n->GetId();
|
||||
node["type"] = n->GetType();
|
||||
node["outPortCount"] = n->Outputs();
|
||||
node["inPortCount"] = n->Inputs();
|
||||
|
||||
nlohmann::json position;
|
||||
position["x"] = n->GetX();
|
||||
position["y"] = n->GetY();
|
||||
|
||||
nlohmann::json internalData;
|
||||
|
||||
n->ToJson(internalData);
|
||||
|
||||
node["position"] = position;
|
||||
node["internal-data"] = internalData;
|
||||
nodes.push_back(node);
|
||||
nodes.push_back(n->ToJson());
|
||||
}
|
||||
|
||||
model["nodes"] = nodes;
|
||||
|
|
@ -227,7 +209,6 @@ void NodeEditorWindow::Save(nlohmann::json &model)
|
|||
nlohmann::json connections = nlohmann::json::array();
|
||||
for (const auto& linkInfo : m_links)
|
||||
{
|
||||
|
||||
nlohmann::json c;
|
||||
|
||||
Connection cnx = LinkToModel(linkInfo->ed_link->InputId, linkInfo->ed_link->OutputId);
|
||||
|
|
@ -266,9 +247,9 @@ Connection NodeEditorWindow::LinkToModel(ed::PinId InputId, ed::PinId OutputId)
|
|||
return c;
|
||||
}
|
||||
|
||||
uint32_t NodeEditorWindow::FindFirstNode() const
|
||||
std::string NodeEditorWindow::FindFirstNode() const
|
||||
{
|
||||
uint32_t id = 0;
|
||||
std::string id;
|
||||
|
||||
// First node is the one without connection on its input port
|
||||
|
||||
|
|
@ -286,7 +267,7 @@ uint32_t NodeEditorWindow::FindFirstNode() const
|
|||
if (!foundConnection)
|
||||
{
|
||||
id = n->GetId();
|
||||
m_story.Log("First node is: " + std::to_string(id));
|
||||
m_story.Log("First node is: " + id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -302,7 +283,7 @@ std::string NodeEditorWindow::Build()
|
|||
|
||||
std::stringstream chip32;
|
||||
|
||||
uint32_t firstNode = FindFirstNode();
|
||||
std::string firstNode = FindFirstNode();
|
||||
|
||||
code << "\tjump " << GetNodeEntryLabel(firstNode) << "\r\n";
|
||||
|
||||
|
|
@ -321,13 +302,13 @@ std::string NodeEditorWindow::Build()
|
|||
return code.str();
|
||||
}
|
||||
|
||||
std::list<std::shared_ptr<Connection>> NodeEditorWindow::GetNodeConnections(unsigned long nodeId)
|
||||
std::list<std::shared_ptr<Connection>> NodeEditorWindow::GetNodeConnections(const std::string &nodeId)
|
||||
{
|
||||
std::list<std::shared_ptr<Connection>> c;
|
||||
ed::SetCurrentEditor(m_context);
|
||||
|
||||
for (const auto & l : m_links)
|
||||
{
|
||||
{
|
||||
if (l->model->outNodeId == nodeId)
|
||||
{
|
||||
c.push_back(l->model);
|
||||
|
|
@ -338,7 +319,7 @@ std::list<std::shared_ptr<Connection>> NodeEditorWindow::GetNodeConnections(unsi
|
|||
return c;
|
||||
}
|
||||
|
||||
std::string NodeEditorWindow::GetNodeEntryLabel(unsigned long nodeId)
|
||||
std::string NodeEditorWindow::GetNodeEntryLabel(const std::string &nodeId)
|
||||
{
|
||||
std::string label;
|
||||
ed::SetCurrentEditor(m_context);
|
||||
|
|
@ -357,9 +338,9 @@ std::string NodeEditorWindow::GetNodeEntryLabel(unsigned long nodeId)
|
|||
}
|
||||
|
||||
|
||||
std::shared_ptr<BaseNode> NodeEditorWindow::GetSelectedNode()
|
||||
std::shared_ptr<BaseNodeWidget> NodeEditorWindow::GetSelectedNode()
|
||||
{
|
||||
std::shared_ptr<BaseNode> selected;
|
||||
std::shared_ptr<BaseNodeWidget> selected;
|
||||
|
||||
ed::SetCurrentEditor(m_context);
|
||||
if (ed::GetSelectedObjectCount() > 0)
|
||||
|
|
@ -485,21 +466,16 @@ void NodeEditorWindow::Draw()
|
|||
Node* node = nullptr;
|
||||
if (ImGui::MenuItem("Media Node"))
|
||||
{
|
||||
auto n = createNode("media-node", "", m_story);
|
||||
auto n = createNode("media-node", m_story);
|
||||
if (n)
|
||||
{
|
||||
n->SetType("media-node"); // FIXME: set type in createNode factory?
|
||||
n->SetId(GenerateNodeId());
|
||||
n->SetPosition(newNodePostion.x, newNodePostion.y);
|
||||
n->Initialize();
|
||||
m_nodes.push_back(n);
|
||||
}
|
||||
}
|
||||
|
||||
// if (node)
|
||||
// {
|
||||
// ed::SetNodePosition(node->ID, newNodePostion);
|
||||
// }
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
#include <set>
|
||||
|
||||
#include <imgui_node_editor.h>
|
||||
#include "base_node.h"
|
||||
#include "base_node_widget.h"
|
||||
#include "window_base.h"
|
||||
#include "i_story_manager.h"
|
||||
#include "json.hpp"
|
||||
|
|
@ -55,10 +55,10 @@ public:
|
|||
void Load(const nlohmann::json &model);
|
||||
void Save(nlohmann::json &model);
|
||||
std::string Build();
|
||||
std::list<std::shared_ptr<Connection> > GetNodeConnections(unsigned long nodeId);
|
||||
std::string GetNodeEntryLabel(unsigned long nodeId);
|
||||
std::list<std::shared_ptr<Connection> > GetNodeConnections(const std::string &nodeId);
|
||||
std::string GetNodeEntryLabel(const std::string &nodeId);
|
||||
|
||||
std::shared_ptr<BaseNode> GetSelectedNode();
|
||||
std::shared_ptr<BaseNodeWidget> GetSelectedNode();
|
||||
|
||||
private:
|
||||
IStoryManager &m_story;
|
||||
|
|
@ -68,12 +68,10 @@ private:
|
|||
bool m_loaded{false};
|
||||
|
||||
// key: Id
|
||||
std::list<std::shared_ptr<BaseNode>> m_nodes;
|
||||
std::list<std::shared_ptr<BaseNodeWidget>> m_nodes;
|
||||
std::list<std::shared_ptr<LinkInfo>> m_links; // List of live links. It is dynamic unless you want to create read-only view over nodes.
|
||||
void ToolbarUI();
|
||||
|
||||
std::set<int> m_ids;
|
||||
|
||||
void BuildNode(Node* node)
|
||||
{
|
||||
for (auto& input : node->Inputs)
|
||||
|
|
@ -91,12 +89,12 @@ private:
|
|||
|
||||
template<class NodeType>
|
||||
struct Factory {
|
||||
static std::shared_ptr<BaseNode> create_func(const std::string &title, IStoryManager &proj) {
|
||||
return std::make_shared<NodeType>(title, proj);
|
||||
static std::shared_ptr<BaseNodeWidget> create_func(const std::string &type, IStoryManager &proj) {
|
||||
return std::make_shared<NodeType>(type, proj);
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<BaseNode> (*GenericCreator)(const std::string &title, IStoryManager &proj);
|
||||
typedef std::shared_ptr<BaseNodeWidget> (*GenericCreator)(const std::string &type, IStoryManager &proj);
|
||||
typedef std::map<std::string, GenericCreator> Registry;
|
||||
Registry m_registry;
|
||||
|
||||
|
|
@ -105,20 +103,23 @@ private:
|
|||
m_registry.insert(typename Registry::value_type(key, Factory<Derived>::create_func));
|
||||
}
|
||||
|
||||
std::shared_ptr<BaseNode> createNode(const std::string& key, const std::string &title, IStoryManager &proj) {
|
||||
std::shared_ptr<BaseNodeWidget> createNode(const std::string& key, IStoryManager &proj) {
|
||||
typename Registry::const_iterator i = m_registry.find(key);
|
||||
if (i == m_registry.end()) {
|
||||
throw std::invalid_argument(std::string(__PRETTY_FUNCTION__) +
|
||||
": key not registered");
|
||||
}
|
||||
else return i->second(title, proj);
|
||||
else
|
||||
{
|
||||
return i->second(key, proj);
|
||||
}
|
||||
}
|
||||
|
||||
void LoadNode(const nlohmann::json &nodeJson);
|
||||
ed::PinId GetInputPin(unsigned long modelNodeId, int pinIndex);
|
||||
ed::PinId GetOutputPin(unsigned long modelNodeId, int pinIndex);
|
||||
uint32_t FindFirstNode() const;
|
||||
int GenerateNodeId();
|
||||
|
||||
ed::PinId GetInputPin(const std::string &modelNodeId, int pinIndex);
|
||||
ed::PinId GetOutputPin(const std::string &modelNodeId, int pinIndex);
|
||||
std::string FindFirstNode() const;
|
||||
std::string GenerateNodeId();
|
||||
void CreateLink(const Connection &model, ed::PinId inId, ed::PinId outId);
|
||||
Connection LinkToModel(ed::PinId InputId, ed::PinId OutputId);
|
||||
};
|
||||
70
story-editor/src/node_engine/base_node.cpp
Normal file
70
story-editor/src/node_engine/base_node.cpp
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
#include "base_node.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
BaseNode::BaseNode(const std::string &type)
|
||||
{
|
||||
m_type = type;
|
||||
}
|
||||
|
||||
void BaseNode::FromJson(const nlohmann::json &j)
|
||||
{
|
||||
try
|
||||
{
|
||||
m_uuid = j["uuid"].get<std::string>();
|
||||
m_internal_data = j["internal-data"];
|
||||
m_type = j["type"].get<std::string>();
|
||||
m_title = j.value("title", "Default node");
|
||||
nlohmann::json posJson = j["position"];
|
||||
SetPosition(posJson["x"].get<double>(), posJson["y"].get<double>());
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cout << "ERROR: " << e.what() << std::endl;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
nlohmann::json BaseNode::ToJson() const
|
||||
{
|
||||
nlohmann::json node;
|
||||
node["uuid"] = GetId();
|
||||
node["type"] = GetType();
|
||||
|
||||
nlohmann::json position;
|
||||
position["x"] = GetX();
|
||||
position["y"] = GetY();
|
||||
|
||||
node["position"] = position;
|
||||
node["internal-data"] = m_internal_data;
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
|
||||
void BaseNode::SetInternalData(const nlohmann::json &j)
|
||||
{
|
||||
m_internal_data = j;
|
||||
}
|
||||
|
||||
nlohmann::json BaseNode::GetInternalData() const
|
||||
{
|
||||
return m_internal_data;
|
||||
}
|
||||
|
||||
void BaseNode::SetPosition(float x, float y)
|
||||
{
|
||||
m_pos.x = x;
|
||||
m_pos.y = y;
|
||||
}
|
||||
|
||||
float BaseNode::GetX() const
|
||||
{
|
||||
return m_pos.x;
|
||||
}
|
||||
|
||||
float BaseNode::GetY() const
|
||||
{
|
||||
return m_pos.y;
|
||||
}
|
||||
55
story-editor/src/node_engine/base_node.h
Normal file
55
story-editor/src/node_engine/base_node.h
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include "json.hpp"
|
||||
#include <random>
|
||||
#include <string>
|
||||
|
||||
#include "json.hpp"
|
||||
|
||||
class BaseNode
|
||||
{
|
||||
public:
|
||||
struct NodePosition
|
||||
{
|
||||
float x;
|
||||
float y;
|
||||
};
|
||||
|
||||
BaseNode(const std::string &type);
|
||||
|
||||
void SetPosition(float x, float y);
|
||||
|
||||
// make this virtual so that graphical node override the behavior
|
||||
virtual float GetX() const;
|
||||
virtual float GetY() const;
|
||||
|
||||
std::string GetType() const
|
||||
{
|
||||
return m_type;
|
||||
}
|
||||
|
||||
void SetId(const std::string &id) { m_uuid = id; }
|
||||
std::string GetId() const { return m_uuid; }
|
||||
|
||||
void SeTitle(const std::string &title) { m_title = title; }
|
||||
std::string GetTitle() const { return m_title; }
|
||||
|
||||
void FromJson(const nlohmann::json &);
|
||||
nlohmann::json ToJson() const;
|
||||
|
||||
|
||||
void SetInternalData(const nlohmann::json &j);
|
||||
nlohmann::json GetInternalData() const;
|
||||
|
||||
|
||||
private:
|
||||
std::string m_title{"Default title"};
|
||||
std::string m_type;
|
||||
std::string m_uuid;
|
||||
NodePosition m_pos;
|
||||
|
||||
nlohmann::json m_internal_data;
|
||||
};
|
||||
|
||||
|
|
@ -2,16 +2,17 @@
|
|||
|
||||
void to_json(nlohmann::json &j, const Connection &p) {
|
||||
j = nlohmann::json{
|
||||
{"outNodeId", static_cast<int64_t>(p.outNodeId)},
|
||||
{"outNodeId", p.outNodeId },
|
||||
{"outPortIndex", static_cast<int64_t>(p.outPortIndex)},
|
||||
{"inNodeId", static_cast<int64_t>(p.inNodeId)},
|
||||
{"inNodeId", p.inNodeId},
|
||||
{"inPortIndex", static_cast<int64_t>(p.inPortIndex)},
|
||||
};
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, Connection &p) {
|
||||
j.at("outNodeId").get_to(p.outNodeId);
|
||||
j.at("outPortIndex").get_to(p.outPortIndex);
|
||||
j.at("inNodeId").get_to(p.inNodeId);
|
||||
j.at("inPortIndex").get_to(p.inPortIndex);
|
||||
|
||||
p.outNodeId = j["outNodeId"].get<std::string>();
|
||||
p.inNodeId = j["inNodeId"].get<std::string>();
|
||||
p.outPortIndex = j["outPortIndex"].get<int>();
|
||||
p.inPortIndex = j["inPortIndex"].get<int>();
|
||||
}
|
||||
|
|
@ -6,9 +6,7 @@
|
|||
struct Connection
|
||||
{
|
||||
Connection()
|
||||
: outNodeId(0)
|
||||
, outPortIndex(0)
|
||||
, inNodeId(0)
|
||||
: outPortIndex(0)
|
||||
, inPortIndex(0)
|
||||
{
|
||||
|
||||
|
|
@ -18,9 +16,9 @@ struct Connection
|
|||
|
||||
}
|
||||
|
||||
unsigned int outNodeId{0};
|
||||
std::string outNodeId;
|
||||
unsigned int outPortIndex{0};
|
||||
unsigned int inNodeId{0};
|
||||
std::string inNodeId;
|
||||
unsigned int inPortIndex{0};
|
||||
|
||||
Connection(const Connection &other){
|
||||
|
|
@ -25,14 +25,14 @@ void PropertiesWindow::Draw()
|
|||
if (m_selectedNode)
|
||||
{
|
||||
static char buf1[32] = ""; ImGui::InputText("Title", buf1, 32);
|
||||
ImGui::Text("Node ID: %lu", m_selectedNode->GetId());
|
||||
ImGui::Text("Node ID: %s", m_selectedNode->GetId().data());
|
||||
m_selectedNode->DrawProperties();
|
||||
}
|
||||
|
||||
WindowBase::EndDraw();
|
||||
}
|
||||
|
||||
void PropertiesWindow::SetSelectedNode(std::shared_ptr<BaseNode> node)
|
||||
void PropertiesWindow::SetSelectedNode(std::shared_ptr<BaseNodeWidget> node)
|
||||
{
|
||||
m_selectedNode = node;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
#include "window_base.h"
|
||||
#include "gui.h"
|
||||
|
||||
#include "base_node.h"
|
||||
#include "base_node_widget.h"
|
||||
|
||||
class PropertiesWindow : public WindowBase
|
||||
{
|
||||
|
|
@ -13,10 +13,10 @@ public:
|
|||
void Initialize();
|
||||
virtual void Draw() override;
|
||||
|
||||
void SetSelectedNode(std::shared_ptr<BaseNode> node);
|
||||
void SetSelectedNode(std::shared_ptr<BaseNodeWidget> node);
|
||||
|
||||
private:
|
||||
std::shared_ptr<BaseNode> m_selectedNode;
|
||||
std::shared_ptr<BaseNodeWidget> m_selectedNode;
|
||||
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue