TerraForge3D  2.3.1
3D Terrain And Landscape Generator
NodeEditor.cpp
1#include "Base/NodeEditor/NodeEditor.h"
2#include "Base/ImGuiShapes.h"
3#include "Base/UIFontManager.h"
4#include "GLFW/glfw3.h"
5#include "Application.h"
6
7static int uidSeed = 1;
8
9int GenerateUID()
10{
11 return uidSeed++;
12}
13
14void SeUIDSeed(int seed)
15{
16 uidSeed = seed;
17}
18
19
20nlohmann::json NodeEditorLink::Save()
21{
22 nlohmann::json data;
24 data["type"] = "Link";
25 data["from"] = from->id;
26 data["to"] = to->id;
27 data["id"] = id;
28 tmp["r"] = color.x;
29 tmp["g"] = color.y;
30 tmp["b"] = color.z;
31 tmp["a"] = color.w;
32 data["color"] = tmp;
33 tmp = nlohmann::json();
34 data["thickness"] = thickness;
35 return data;
36}
37
38NodeEditorLink::NodeEditorLink(int id)
39 :id(id)
40{
41 _id = id;
42}
43
44void NodeEditorLink::Load(nlohmann::json data)
45{
46 color.x = data["color"]["r"];
47 color.y = data["color"]["g"];
48 color.z = data["color"]["b"];
49 color.w = data["color"]["a"];
50 thickness = data["thickness"];
51 id = data["id"];
52 _id = id;
53}
54
55nlohmann::json NodeEditorPin::Save()
56{
57 nlohmann::json data;
58 data["id"] = id;
59 data["type"] = type;
60 data["userData"] = static_cast<uint32_t>(userData); // Temporary
61 data["color"] = color;
62 return data;
63}
64
65void NodeEditorPin::Load(nlohmann::json data)
66{
67 id = data["id"];
68 _id = id;
69 type = data["type"];
70 color = data["color"];
71 userData = data["userData"].get<uint32_t>(); // Temporary
72}
73
74void NodeEditorPin::Begin()
75{
76 ImGuiNodeEditor::PinKind pinKind = type == NodeEditorPinType::Input ? ImGuiNodeEditor::PinKind::Input : ImGuiNodeEditor::PinKind::Output;
77 ImGuiNodeEditor::BeginPin(id, pinKind);
78}
79
80void NodeEditorPin::End()
81{
82 ImGuiNodeEditor::EndPin();
83}
84
85bool NodeEditorPin::ValidateLink(NodeEditorLink *lnk)
86{
87 if (link)
88 {
89 return false;
90 }
91
92 if (lnk->from->parent->id == lnk->to->parent->id)
93 {
94 return false;
95 }
96
97 if (lnk->from->type == lnk->to->type)
98 {
99 return false;
100 }
101
102 return parent->OnLink(this, lnk);
103}
104
105void NodeEditorPin::Link(NodeEditorLink *lnk)
106{
107 link = lnk;
108
109 if (link->from->id == id)
110 {
111 other = link->to;
112 }
113
114 else
115 {
116 other = link->from;
117 }
118}
119
120bool NodeEditorPin::IsLinked()
121{
122 return link;
123}
124
125NodeEditorPin::NodeEditorPin(NodeEditorPinType type, int id)
126 :type(type), id(id), link(nullptr)
127{
128 _id = id;
129}
130
131void NodeEditorPin::Render()
132{
133 if (type == NodeEditorPinType::Output)
134 {
135 ImGui::SameLine();
136 }
137
138 Begin();
139
140 if(IsLinked())
141 {
142 ImGui::DrawFilledCircle(10, color);
143 }
144
145 else
146 {
147 ImGui::DrawCircle(10, color);
148 }
149
150 ImGui::Dummy(ImVec2(20, 20));
151 End();
152
153 if(type== NodeEditorPinType::Input)
154 {
155 ImGui::SameLine();
156 }
157}
158
159NodeEditorPin::~NodeEditorPin()
160{
161}
162
163void NodeEditorPin::Unlink()
164{
165 link = nullptr;
166}
167
168NodeOutput NodeEditorPin::Evaluate(NodeInputParam input)
169{
170// mutex.lock();
171 NodeOutput o = parent->Evaluate(input, this);
172// mutex.unlock();
173 return o;
174}
175
176std::vector<NodeEditorPin *> NodeEditorNode::GetPins()
177{
178 std::vector<NodeEditorPin *> pins;
179 pins.insert(
180 pins.end(),
181 std::make_move_iterator(inputPins.begin()),
182 std::make_move_iterator(inputPins.end())
183 );
184 pins.insert(
185 pins.end(),
186 std::make_move_iterator(outputPins.begin()),
187 std::make_move_iterator(outputPins.end())
188 );
189 return pins;
190}
191
192bool NodeEditorNode::OnLink(NodeEditorPin *pin, NodeEditorLink *link)
193{
194 return true;
195}
196
197void NodeEditorNode::OnDelete()
198{
199}
200
201void NodeEditorNode::Render()
202{
203 ImGui::PushID(id);
204 ImGuiNodeEditor::BeginNode(id);
205 OnRender();
206 ImGuiNodeEditor::EndNode();
207 ImGui::PopID();
208}
209
210NodeEditorNode::NodeEditorNode(int id)
211 :id(id)
212{
213 _id = id;
214 nodePosition = ImVec2(0, 0);
215}
216
217NodeEditorNode::~NodeEditorNode()
218{
219}
220
221void NodeEditorNode::DrawHeader(std::string text)
222{
223 ImVec2 pos = ImGui::GetCursorPos();
224 ImGui::SetCursorPos(ImVec2(pos.x - ImGuiNodeEditor::GetStyle().NodePadding.x, pos.y - ImGuiNodeEditor::GetStyle().NodePadding.y));
225 ImVec2 start = ImGuiNodeEditor::GetNodeSize(_id);
226 ImGui::DrawFilledRect(ImVec2(start.x, 60), headerColor, 13);
227 ImGui::SetCursorPos(ImVec2(pos.x + ImGuiNodeEditor::GetStyle().NodePadding.x, pos.y + ImGuiNodeEditor::GetStyle().NodePadding.x));
228 ImGui::PushFont(GetUIFont("OpenSans-Bold"));
229 ImGui::Text(text.c_str());
230 ImGui::PopFont();
231 ImGui::NewLine();
232}
233
234void NodeEditorNode::LoadInternal(nlohmann::json data)
235{
236 Load(data);
237 name = data["name"];
238 headerColor = data["headerColor"];
239 id = data["id"];
240 _id = id;
241
242 for (nlohmann::json pns : data["inputPins"])
243 {
244 inputPins[pns["index"]]->Load(pns);
245 }
246
247 for (nlohmann::json pns : data["outputPins"])
248 {
249 outputPins[pns["index"]]->Load(pns);
250 }
251
252 try // For Backward Ckompitabilaty
253 {
254 nodePosition = ImVec2(data["posX"], data["posY"]);
255 }
256
257 catch(...)
258 {
259 std::cout << "Failed to load node position!\n";
260 }
261
262 reqNodePosLoad = true;
263}
264
265
266nlohmann::json NodeEditorNode::SaveInternal()
267{
268 nlohmann::json data = Save();
269 data["name"] = name;
270 data["headerColor"] = headerColor;
271 data["id"] = id;
272 nlohmann::json tmp, tmp2;
273
274 for (int i = 0; i < inputPins.size(); i++)
275 {
276 tmp2 = inputPins[i]->Save();
277 tmp2["index"] = i;
278 tmp.push_back(tmp2);
279 }
280
281 data["inputPins"] = tmp;
282 tmp.clear();
283
284 for (int i = 0; i < outputPins.size(); i++)
285 {
286 tmp2 = outputPins[i]->Save();
287 tmp2["index"] = i;
288 tmp.push_back(tmp2);
289 }
290
291 data["posX"] = nodePosition.x;
292 data["posY"] = nodePosition.y;
293 data["outputPins"] = tmp;
294 return data;
295}
296
297void NodeEditorNode::Setup()
298{
299 for (NodeEditorPin *pin : inputPins)
300 {
301 pin->parent = this;
302 }
303
304 for (NodeEditorPin *pin : outputPins)
305 {
306 pin->parent = this;
307 }
308}
309
310nlohmann::json NodeEditor::Save()
311{
312 nlohmann::json data;
313 data["type"] = "NodeEditorData";
314 data["serializer"] = "Node Serializer v2.0";
315 nlohmann::json tmp;
316
317 for (auto &it : nodes)
318 {
319 if(it.second->name != "Output")
320 {
321 tmp.push_back(it.second->SaveInternal());
322 }
323 }
324
325 data["nodes"] = tmp;
326 tmp = nlohmann::json();
327
328 for (auto &it : links)
329 {
330 tmp.push_back(it.second->Save());
331 }
332
333 data["links"] = tmp;
334 data["uidSeed"] = GenerateUID();
335 return data;
336}
337
338void NodeEditor::Load(nlohmann::json data)
339{
340 if (!config.insNodeFunc)
341 {
342 throw std::runtime_error("Node Instantiate Function Not Found!");
343 }
344
345 Reset();
346 SeUIDSeed(data["uidSeed"]);
347
348 for (nlohmann::json ndata : data["nodes"])
349 {
350 NodeEditorNode *nd = (config.insNodeFunc(ndata));
351 nd->LoadInternal(ndata);
352 AddNode(nd);
353 }
354
355 for (nlohmann::json ldata : data["links"])
356 {
357 NodeEditorLink *lnk = new NodeEditorLink();
358 lnk->Load(ldata);
359 ImGuiNodeEditor::PinId fromId, toId;
360 fromId = (int)ldata["from"];
361 toId = (int)ldata["to"];
362 lnk->from = pins[fromId.Get()];
363 lnk->to = pins[toId.Get()];
364 lnk->from->Link(lnk);
365 lnk->to->Link(lnk);
366 links[lnk->_id.Get()] = lnk;
367 }
368}
369
370void NodeEditor::Render()
371{
372 bool windowFocused = ImGui::IsWindowFocused();
373 ImGuiNodeEditor::SetCurrentEditor(context);
374 ImGuiNodeEditor::Begin(name.c_str());
375 ImGuiNodeEditor::EnableShortcuts(windowFocused);
376
377 for (auto &it : nodes)
378 {
379 it.second->Render();
380 }
381
382 outputNode->Render();
383
384 for (auto &it : links)
385 {
386 ImGuiNodeEditor::Link(it.second->id, it.second->from->id, it.second->to->id, it.second->color, it.second->thickness);
387 }
388
389 if (ImGuiNodeEditor::BeginCreate())
390 {
391 ImGuiNodeEditor::PinId iPid, oPid;
392
393 if (ImGuiNodeEditor::QueryNewLink(&iPid, &oPid))
394 {
395 if (iPid && oPid)
396 {
397 if (ImGuiNodeEditor::AcceptNewItem())
398 {
399 NodeEditorLink *link = new NodeEditorLink();
400 link->from = pins[iPid.Get()];
401 link->to = pins[oPid.Get()];
402 bool lnkFrm = link->from->ValidateLink(link);
403 bool lnkTo = link->to->ValidateLink(link);
404
405 if (lnkFrm && lnkTo)
406 {
407 link->from->Link(link);
408 link->to->Link(link);
409 links[link->_id.Get()] = link;
410 ImGuiNodeEditor::Link(link->id, link->from->id, link->to->id);
411 }
412 }
413 }
414
415 else if(iPid)
416 {
417 NodeEditorPin *pin = pins[iPid.Get()];
418
419 if(pin->IsLinked())
420 {
421 DeleteLink(pin->link);
422 }
423 }
424
425 else if (oPid)
426 {
427 NodeEditorPin *pin = pins[oPid.Get()];
428
429 if (pin->IsLinked())
430 {
431 DeleteLink(pin->link);
432 }
433 }
434 }
435
436 ImGuiNodeEditor::PinId nPid;
437
438 if (config.makeNodeFunc && ImGuiNodeEditor::QueryNewNode(&nPid))
439 {
440 if (ImGuiNodeEditor::AcceptNewItem())
441 {
442 NodeEditorPin *pin = pins[nPid.Get()];
443
444 if (pin->IsLinked())
445 {
446 DeleteLink(pin->link);
447 }
448
449 else
450 {
451 config.makeNodeFunc();
452 }
453 }
454 }
455 }
456
457 ImGuiNodeEditor::EndCreate();
458
459 if (ImGuiNodeEditor::BeginDelete())
460 {
461 ImGuiNodeEditor::NodeId nId;
462
463 if (ImGuiNodeEditor::QueryDeletedNode(&nId))
464 {
465 if (nId && ImGuiNodeEditor::AcceptDeletedItem())
466 {
467 if (!(outputNode && nId == outputNode->_id))
468 {
469 NodeEditorNode *node = nodes[nId.Get()];
470 DeleteNode(node);
471 }
472 }
473 }
474
475 else
476 {
477 ImGuiNodeEditor::LinkId lId;
478
479 if (ImGuiNodeEditor::QueryDeletedLink(&lId))
480 {
481 if (lId && ImGuiNodeEditor::AcceptDeletedItem())
482 {
483 NodeEditorLink *link = links[lId.Get()];
484 DeleteLink(link);
485 }
486 }
487 }
488 }
489
490 ImGuiNodeEditor::EndDelete();
491 ImGuiNodeEditor::NodeId sNode;
492 ImGuiNodeEditor::GetSelectedNodes(&sNode, 1);
493
494 if (ImGui::IsWindowFocused())
495 {
496 GLFWwindow *wind = Application::Get()->GetWindow()->GetNativeWindow();
497
498 if (glfwGetKey(wind, GLFW_KEY_RIGHT_SHIFT) || glfwGetKey(wind, GLFW_KEY_LEFT_SHIFT))
499 {
500 if (glfwGetKey(wind, GLFW_KEY_D) && sNode)
501 {
502 if (!(sNode.Get() == outputNode->_id.Get()))
503 {
504 NodeEditorNode *node = (config.insNodeFunc(nodes[sNode.Get()]->Save()));
505 ImVec2 prePos = ImGuiNodeEditor::GetNodePosition(sNode);
506 ImGuiNodeEditor::SetNodePosition(node->_id, ImVec2(prePos.x + 10, prePos.y + 10));
507 ImGuiNodeEditor::DeselectNode(sNode);
508 AddNode(node);
509 }
510 }
511 }
512 }
513
514 for(auto &it : nodes)
515 {
516 if(it.second->reqNodePosLoad)
517 {
518 ImGuiNodeEditor::SetNodePosition(it.second->_id, it.second->nodePosition);
519 it.second->reqNodePosLoad = false;
520 }
521
522 else
523 {
524 it.second->nodePosition = ImGuiNodeEditor::GetNodePosition(it.second->_id);
525 }
526 }
527
528 if (config.updateFunc)
529 {
530 config.updateFunc();
531 }
532
533 ImGuiNodeEditor::End();
534 ImGuiNodeEditor::SetCurrentEditor(nullptr);
535}
536
537void NodeEditor::DeleteLink(NodeEditorLink *link)
538{
539 link->from->link = nullptr;
540 link->to->link = nullptr;
541 links.erase(links.find(link->_id.Get()));
542 delete link;
543}
544
545void NodeEditor::AddNode(NodeEditorNode *node)
546{
547 nodes[node->_id.Get()] = node;
548
549 for (auto &it : node->GetPins())
550 {
551 pins[it->_id.Get()] = it;
552 }
553
554 node->Setup();
555
556 if (nodes.size() > 2 && nodes.find(lastNodeId.Get()) != nodes.end() && !node->reqNodePosLoad)
557 {
558 NodeEditorNode *lastNode = nodes[lastNodeId.Get()];
559 node->nodePosition = lastNode->nodePosition;
560 node->reqNodePosLoad = true;
561 }
562
563 lastNodeId = node->_id;
564}
565
566NodeEditor::NodeEditor(NodeEditorConfig aconfig)
567{
568 config = aconfig;
569 ImGuiNodeEditor::Config iconfig;
570 iconfig.SettingsFile = config.saveFile.c_str();
571 context = ImGuiNodeEditor::CreateEditor(&iconfig);
572}
573
574void NodeEditor::DeleteNode(NodeEditorNode *node)
575{
576 mutex.lock();
577
578 if (nodes.find(node->_id.Get()) != nodes.end())
579 {
580#ifdef TERR3D_WIN32
581 using namespace std::chrono_literals; // This Line is temporary
582 std::this_thread::sleep_for(500ms); // This Line is temporary
583#endif
584 node->OnDelete();
585 std::vector<NodeEditorPin *> mPins = node->GetPins();
586
587 for (NodeEditorPin *pin : mPins)
588 {
589 if (pin->IsLinked())
590 {
591 links.erase(links.find(pin->link->_id.Get()));
592 pin->other->Unlink();
593 delete pin->link;
594 }
595
596 pins.erase(pins.find(pin->_id.Get()));
597 delete pin;
598 }
599
600 nodes.erase(nodes.find(node->_id.Get()));
601 delete node;
602 }
603
604 mutex.unlock();
605}
606
607NodeEditor::~NodeEditor()
608{
609 ImGuiNodeEditor::DestroyEditor(context);
610}
611
612void NodeEditor::Reset()
613{
614 // Clear Up Node Editor
615 int size = 0;
616 std::unordered_map<uintptr_t, NodeEditorNode *> nodesc = nodes;
617
618 for (auto &it : nodesc)
619 {
620 DeleteNode(it.second);
621 }
622
623 NodeEditorNode *node = outputNode;
624
625 for (auto &it : node->GetPins())
626 {
627 pins[it->_id.Get()] = it;
628 }
629
630 node->Setup();
631}
632
633void NodeEditor::SetOutputNode(NodeEditorNode *node)
634{
635 outputNode = node;
636
637 for (auto &it : node->GetPins())
638 {
639 pins[it->_id.Get()] = it;
640 }
641
642 node->Setup();
643}
644
645
646NodeEditorConfig::NodeEditorConfig(std::string saveFile)
647 :saveFile(saveFile)
648{
649}
650
651NodeInputParam::NodeInputParam()
652{
653}
654
655NodeInputParam::NodeInputParam(float *pos, float *texCoord, float *minPos, float *maxPos)
656{
657 x = pos[0];
658 y = pos[1];
659 z = pos[2];
660 texX = texCoord[0];
661 texY = texCoord[1];
662 minX = minPos[0];
663 minY = minPos[1];
664 minZ = minPos[2];
665 maxX = maxPos[0];
666 maxY = maxPos[1];
667 maxZ = maxPos[2];
668}
Definition: AddNode.h:7
void clear() noexcept
clears the contents
Definition: json.hpp:23069
void push_back(basic_json &&val)
add an object to an array
Definition: json.hpp:23148
auto get() const noexcept(noexcept(std::declval< const basic_json_t & >().template get_impl< ValueType >(detail::priority_tag< 4 > {}))) -> decltype(std::declval< const basic_json_t & >().template get_impl< ValueType >(detail::priority_tag< 4 > {}))
get a (pointer) value (explicit)
Definition: json.hpp:20907
a class to store JSON values
Definition: json.hpp:17860
basic_json<> json
default JSON class
Definition: json.hpp:3411