Skip to content

Commit

Permalink
Import Update 2
Browse files Browse the repository at this point in the history
implemented and documented the starting import functions in final state
added more tests
added wiki
added more to README.md
optimized save format even more
  • Loading branch information
gk646 committed May 9, 2024
1 parent b2bb0c1 commit 9b42587
Show file tree
Hide file tree
Showing 13 changed files with 345 additions and 216 deletions.
2 changes: 1 addition & 1 deletion .clang-format
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ AlignOperands: Align
AllowAllArgumentsOnNextLine: true
AllowAllConstructorInitializersOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortBlocksOnASingleLine: Empty
AllowShortBlocksOnASingleLine: Always
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: Inline
AllowShortIfStatementsOnASingleLine: AllIfsAndElse
Expand Down
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
cmake-*
.idea
saves
test/res/__TEST__.rn
test/res/__GEN1__.rn
test/res/__GEN2__.rn
72 changes: 31 additions & 41 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,54 +1,45 @@
# raynodes

`raynodes` is a standalone 2D node editor made using [raylib](https://github.com/raysan5/raylib) with a focus
extensibility. It aims to be an attractive tool for any node based task, but focuses on source code integration within
extensibility. It aims to be an attractive tool for any node based task, and supports being integrated into
bigger projects like games, editors...
In many cases it comes close to being a node-editor SDK of sorts.

A small showcase of its major features:

- **Clean**, **modularized** and **documented** source code
- Rich, documented **node and component** interface allowing for endless possibilities!
- **Fast** and **optimized** import and export functionalities without dependencies!
- Supports both **Windows and Linux** (and possibly MacOS)
- Documented **plugin interface** and capabilities
- **User created** node at runtime via a component library (script support with python and lua in the future)
- **Clean**, **modularized** and **documented** source code!
- Extensive and easy-to-use **node and component** interface allowing for endless customization!
- Custom filetype optimized for **low export size**
- **Fast and optimized** import header included for working with project exports!
- Supports both **Windows and Linux** (and possibly macOS)
- **Plugin interface** and capabilities
- **Unit testing** of critical parts (importing, persistence...)
- **Modern code base** using many C++20 and above
- **User created** nodes at runtime scripted in-editor in python (planned)

In a lot of places it uses my (header only) C++ helper library [cxstructs](https://github.com/gk646/cxstructs).
For more infos on the design choices go to [Software Design](#Software-Design)

For more information on how to use the editor look at the [raynodes-wiki](https://github.com/gk646/raynodes/wiki).

![Image](.github/fullEditor.png)

**1.** [Controls](#Controls)
**2.** [Editor Features](#Editor-Features)
**3.** [Components!](#Custom-Nodes)
**4.** [Plugins](#Plugins)
**5.** [Nodes!](#Custom-Nodes)
**6.** [Software Design](#Software-Design)

### Controls

- `CTRL+C` / **Copy selection**
- `CTRL+V` / **Paste selection**
- `CTRL+X` / **Cut (delete and copy) selection**
- `CTRL+Z` / **Undo Action**
- `CTRL+Y` / **Redo Action**
- `CTRL+LMB`(click on a node) / **Add to Selection**
- `CTRL+RMB`(drag) / **Delete nodes in selection**
- `RMB` / **Open context menu**
- `RMB` (drag mouse) / **Select in rectangle**
- `DEL` / **Delete selected nodes**

CTRL = Control
LMB = Left Mouse Button
RMB = Right Mouse Button
**1.** [Editor Features](#Editor-Features)
**2.** [Components!](#Custom-Nodes)
**3.** [Plugins](#Plugins)
**4.** [Nodes!](#Custom-Nodes)
**5.** [Software Design](#Software-Design)

## Editor Features:

### Action History
### Shortcuts

`raynodes` has supports all common shortcuts. For a comprehensive list check out
the [shortcuts](https://github.com/gk646/raynodes/wiki/Shortcuts) page in the wiki!.

### User Interface

Each action (move, edit, delete + many more) are tracked in a global list improving the workflow.
Actions have a generic interface and new ones can easily be added.
The user interface takes inspiration from other editors like paint.net. For a comprehensive list checkout
the [user-interface](https://github.com/gk646/raynodes/wiki/User-Interface) page of the wiki!

### User Defined Templates *(soon)*

Expand All @@ -60,7 +51,7 @@ Components are the second level of customization you can achieve. For that you c
inside your own plugin. For the most part a component is a simple struct that is drawn and updated inside the node.
Inside the update function you have full write and read access to the context and design and draw complex components.

This is a glimps at the interface but the Component.h header has a lot more and is **self documenting**! Check it out!
This is a glimpse at the interface but the Component.h header has a lot more and is **self documenting**! Check it out!

```cpp
//-----------CORE-----------//
Expand Down Expand Up @@ -194,12 +185,11 @@ Plugins will be loaded from the `./plugins` folder relative to the executable
## Custom Nodes!
The node interface is entirely optional but can be used to gain another level of control to implement functionality.
It's mostly there to orchestrate existing components by allowing to specify a update and render tasks that runs after
It's mostly there to orchestrate existing components by allowing to specify an update and render tasks that runs after
all components.
```cpp
//-----------CORE-----------//
[[nodiscard]] virtual Node* clone(NodeID nid);
// Guaranteed to be called each tick and AFTER all components have been updated
virtual void update(EditorContext& ec) {}
// Called only when the node bounds are (partially) within the screen
Expand Down Expand Up @@ -251,22 +241,22 @@ NodeEditor - Logic

### Node Editor Concepts

The main inspiration was to create a editor that just provides a basic interface that can be used to create anything
The main inspiration was to create an editor that just provides a basic interface that can be used to create anything
node based like:

- Blender Node Editor
- Quest Tree (for games, DialogueTree and choices)
- Basic Logic Circuit

`raynodes` is a component-centric editor meaning that most things happen in and around components. However with the
`raynodes` is a component-centric editor meaning that most things happen in and around components. However, with the
addition custom nodes which allows for even more control you could almost call it a hybrid.

This choice of having components was made due to a number of reasons:

- Having reusable building blocks (components) throughout nodes is good
- With only a node it's not clear who handles the input and outputs
- Now each node has its dedicated input and outputs -> might add node level connections aswell
- Components can be tied to some identifier (a string here) which allows nodes to built at runtime
- Now each node has its dedicated input and outputs -> might add node level connections as well
- Components can be tied to some identifier (a string here) which allows nodes to be built at runtime
- This allows for instructions in string form on how to build a specific node -> plugins

However, this also has some drawbacks:
Expand Down
2 changes: 1 addition & 1 deletion cmake/CompilerOptions.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ elseif (MSVC)

set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /O2 /GR-")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /O2 /Ob3 /GA /Gw /GL /EHsc /GR- /arch:AVX2 /GF")
set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /LTCG /OPT:REF /OPT:ICF")
set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /LTCG /OPT:REF /OPT:ICF")

if (ENABLE_SANITIZER)
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /fsanitize=address")
Expand Down
3 changes: 1 addition & 2 deletions src/raynodes/application/context/impl/ContextCore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -174,9 +174,8 @@ void Core::copy(EditorContext& /**/) {
void Core::addEditorAction(EditorContext& ec, Action* action) {
if (!action) return;

// Correctly handle unsaved changes
// We never unset it even if the user undoes the action - cause its straightforward
if (!hasUnsavedChanges) {
if (hasUnsavedChanges == false) {
hasUnsavedChanges = true;
ec.string.updateWindowTitle(ec);
}
Expand Down
18 changes: 7 additions & 11 deletions src/raynodes/application/context/impl/ContextPersist.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,24 +48,22 @@ struct ComponentIndices {
int get(const char* name) const {
if (name == nullptr) return -1;
for (int i = 0; i < count; ++i) {
if (strncmp(storage + i * Plugin::MAX_NAME_LEN, name, Plugin::MAX_NAME_LEN) == 0) {
return i;
}
if (strncmp(storage + i * Plugin::MAX_NAME_LEN, name, Plugin::MAX_NAME_LEN) == 0) { return i; }
}
return -1;
}

[[nodiscard]] const char* getName(int index) const {
if (index == -1 || index > count || index >= MAX_UNIQUE_LABELS) return nullptr;
return storage + index * Plugin::MAX_NAME_LEN;
}

void reset() {
std::memset(storage, 0, sizeof(storage));
count = 0;
}
};

// Static instance on the stack - this is only needed inside here
// Pure file size optimization the rest of the program doesnt know about
static ComponentIndices compIndices{};

using namespace cxstructs; // using the namespace here
Expand Down Expand Up @@ -120,10 +118,10 @@ void SaveTemplates(FILE* file, EditorContext& ec) {
for (const auto& nt : ec.templates.nodeTemplates | std::views::values) {
io_save(file, compIndices.add(nt.label)); // The arbitrary id
io_save(file, nt.label);
io_save(file, ColorToInt({nt.color.r, nt.color.g, nt.color.b, nt.color.a}));
for (const auto& [label, component] : nt.components) {
io_save(file, label == nullptr ? "" : label);
}
io_save(file, ColorToInt({nt.color.r, nt.color.g, nt.color.b, nt.color.a}));
io_save_newline(file);
}
}
Expand All @@ -139,7 +137,6 @@ int SaveNodes(FILE* file, EditorContext& ec) {
}
return count;
}

int SaveConnections(FILE* file, EditorContext& ec) {
io_save_section(file, "Connections");
int count = 0;
Expand Down Expand Up @@ -187,7 +184,6 @@ void LoadTemplates(FILE* file, EditorContext& ec) {
io_load_newline(file, true);
}
}

int LoadNodes(FILE* file, EditorContext& ec) {
int count = 0;
while (io_load_inside_section(file, "Nodes")) {
Expand Down Expand Up @@ -234,10 +230,10 @@ int LoadConnections(FILE* file, EditorContext& ec) {

bool Persist::saveToFile(EditorContext& ec, bool saveAsMode) {
// Strictly enforce this to limit saving -> Actions need to be accurate
if (ec.core.hasUnsavedChanges && !saveAsMode) return true;
if (!ec.core.hasUnsavedChanges && !saveAsMode) return true;

// If "SaveAs" we want to save with a new name - regardless of an existing one
if (openedFilePath.empty() || saveAsMode) {
if (saveAsMode || openedFilePath.empty()) {
auto* res = tinyfd_saveFileDialog("Save File", nullptr, 1, Info::fileFilter, Info::fileDescription);
if (res != nullptr) {
openedFilePath = res;
Expand Down Expand Up @@ -269,7 +265,7 @@ bool Persist::saveToFile(EditorContext& ec, bool saveAsMode) {
ec.string.updateWindowTitle(ec);

//printf("Saved %s nodes\n", ec.string.getPaddedNum(nodes));
// printf("Saved %s connections\n", ec.string.getPaddedNum(connections));
//printf("Saved %s connections\n", ec.string.getPaddedNum(connections));
return true;
}

Expand Down
Loading

0 comments on commit 9b42587

Please sign in to comment.