Files
LifeProject/LifeMaterialEditor/GUINodeContainer.cpp
T

531 lines
13 KiB
C++
Raw Normal View History

2025-05-13 02:46:21 +03:00
#include "GUINodeContainer.h"
#include <gui/GUIManager.h>
#include <math/Spline.h>
#include <Property.h>
#include <gui/PropertyItemFile.h>
#include "GUIShaderNodeTexture.h"
#include "EditorShared.h"
GUINodeContainer::GUINodeContainer(IShaderGraph* graph)
:cache_length(0)
{
graphics_graph = graph;
//skip_mouse = true;
menu = new GUIMenu(true);
}
GUIShaderNode* GUINodeContainer::FindNodeByGraphicsID(int id)
{
int node_count = GetNodeCount();
for(int i = 0; i < node_count; i++){
GUIShaderNode* node = nodes[i];
if(node->GetGraphicsNode()->id == id)
return node;
}
return nullptr;
}
bool GUINodeContainer::Serialize(Core::IMemoryStream* stm)
{
int node_count = GetNodeCount();
stm->Write(node_count);
for(int i = 0; i < node_count; i++){
GUIShaderNode* node = nodes[i];
stm->Write(node->GetGraphicsNode()->id);
stm->Write(node->GetPosition().X);
stm->Write(node->GetPosition().Y);
stm->Write(node->GetWidth());
stm->Write(node->GetHeight());
StringSerialize(node->GetCaption(), stm);
}
return true;
}
bool GUINodeContainer::Deserialize(Core::IMemoryStream* stm)
{
int node_count;
stm->ReadInt32(node_count);
for(int i = 0; i < node_count; i++){
int graphics_id;
int pos_x, pos_y;
int node_width, node_height;
String caption;
GUIShaderNode* node;
stm->ReadInt32(graphics_id);
stm->ReadInt32(pos_x);
stm->ReadInt32(pos_y);
stm->ReadInt32(node_width);
stm->ReadInt32(node_height);
caption = StringDeserialize(stm);
node = FindNodeByGraphicsID(graphics_id);
if(node != nullptr){
node->SetPosition(Vec2i(pos_x, pos_y));
node->SetWidth(node_width);
node->SetHeight(node_height);
node->SetCaption(caption);
}
}
return true;
}
void GUINodeContainer::MakeLink(GUIShaderNodeIO* left, GUIShaderNodeIO* right)
{
LinkInfo info;
info.left = left;
info.right = right;
links.push_back(info);
Invalidate();
}
void GUINodeContainer::BreakLink(GUIShaderNodeIO* left, GUIShaderNodeIO* right)
{
for(LinksCollectionItr it = links.begin(); it != links.end(); ++it)
{
LinkInfo info = *it;
if((info.left == left && info.right == right) ||
(info.left == right && info.right == left))
{
links.erase(it);
return;
}
}
}
void GUINodeContainer::OnNodeSelect(GUIShaderNode* node)
{
EditorShared::me()->propGrid->SetTarget(node);
}
NodeCollection* GUINodeContainer::GetTextureUnits()
{
return &texNodes;
}
void GUINodeContainer::SetSelection(GUIShaderNode* node)
{
selection = node;
}
void GUINodeContainer::OnNodeHeaderMouseClick(GUIWindow* wnd, int param)
{
if(param == KEY_MOUSE_RIGHT)
{
this->SetSelection((GUIShaderNode*)wnd);
menu->RemoveItems();
menu->AddItem(STR("Delete node"), false)->Event_Select += NewDelegate(this, &GUINodeContainer::OnDeleteNode);
menu->SetPosition(FindOverlay()->cursorPos);
FindOverlay()->AddChild(menu);
FindOverlay()->SetFocusWindow(menu);
}
}
void GUINodeContainer::OnDeleteNode(GUIMenuItem* itm)
{
DeleteNode(selection);
}
void GUINodeContainer::DeleteNode(GUIShaderNode* node)
{
Event_DeleteNode.Invoke(this, node);
this->RemoveChild(node->hwnd);
graphics_graph->DeleteNode(node->GetGraphicsNode()->id);
restart_delete_links:
for(LinksCollectionItr it = links.begin(); it != links.end(); ++it)
{
if((*it).left->Owner == node || (*it).right->Owner == node)
{
links.erase(it);
goto restart_delete_links;
}
}
for(NodeCollectionItr it = nodes.begin(); it != nodes.end(); it++)
{
if((*it)->ID == node->ID)
{
nodes.erase(it);
goto done;
}
}
done:
delete node;
Invalidate();
}
void GUINodeContainer::RegisterNewNode(GUIShaderNode* node)
{
nodes.push_back(node);
node->Event_HeaderClick += NewDelegate(this, &GUINodeContainer::OnNodeHeaderMouseClick);
}
void GUINodeContainer::RebuildGraph()
{
//remove all nodes firstly
for(NodeCollectionItr it = nodes.begin(); it != nodes.end(); it++)
{
//Event_DeleteNode.Invoke(this, node);
this->RemoveChild((*it)->hwnd);
delete (*it);
}
nodes.clear();
links.clear();
int cnt = graphics_graph->GetNodeCount();
//build nodes
for(int k = 0; k < cnt; k++)
{
IShaderGraphNode* gn = graphics_graph->GetNodeByArrayID(k);
GUIShaderNode* node;
if(gn->Name == STR("SrcTex2D")){
node = new GUIShaderNodeTexture(gn);
texNodes.push_back(node);
}
else
node = new GUIShaderNode(gn);
//copy all properties =)
PropertyCollection* props = gn->GetProperties();
for(int i = 0; i < props->size(); i++)
{
IProperty* prop = (*props)[i];
prop->Event_ValueChanged.Invoke();
node->RegisterProperty(prop);
}
node->Event_Select += NewDelegate(this, &GUINodeContainer::OnNodeSelect);
node->SetCaption(gn->Name);
int in_count = gn->Inputs.size();
int out_count = gn->Outputs.size();
//create input ports
for(int i = 0; i < in_count; i++)
{
IShaderGraphNodePort* port = gn->Inputs[i];
GUIShaderNodeIO* io = new GUIShaderNodeIO(node);
io->SetCaption(port->Name);
node->AddInput(io);
io->GraphicsPort = port;
io->Event_PortClick += NewDelegate(this, &GUINodeContainer::OnNodePortMouseClick);
}
//create output ports
for(int i = 0; i < out_count; i++)
{
IShaderGraphNodePort* port = gn->Outputs[i];
GUIShaderNodeIO* io = new GUIShaderNodeIO(node);
io->SetCaption(port->Name);
io->GraphicsPort = port;
io->Event_PortClick += NewDelegate(this, &GUINodeContainer::OnNodePortMouseClick);
node->AddOutput(io);
}
if(name == STR("Material"))
{
materialNode = node;
}
AddChild(node);
RegisterNewNode(node);
}
//build links
cnt = nodes.size();
for(int k = 0; k < cnt; k++)
{
GUIShaderNode* node = nodes[k];
IShaderGraphNode* n = node->GetGraphicsNode();
int inc = n->Inputs.size();
for(int c = 0; c < inc; c++)
{
//make link
if(n->Inputs[c]->GetLinkCount())
{
IShaderGraphNodePort* right_port = n->Inputs[c]->GetLink(0);
//get right node
GUIShaderNode* rn = this->FindNode(right_port->Parent->id);
int roc = rn->GetOutputCount();
int rightId = -1;
for(int m = 0; m < roc; m++)
{
if(rn->GetGraphicsNode()->GetOutputPortByID(m) == right_port)
{
rightId = m;
}
}
//found right side link - make it!
if(rightId >=0)
{
MakeLink(node->GetInput(c), rn->GetOutput(rightId));
}
}
}
}
}
String GUINodeContainer::GetTexNameByID(int id)
{
int cnt = nodes.size();
for(int i = 0; i < cnt; i++)
{
if(nodes[i]->GetGraphicsNode()->id == id)
{
GUIShaderNodeTexture* tn = (GUIShaderNodeTexture*)nodes[i];
return tn->GetFilename();
}
}
return "TexNotFound";
}
GUIShaderNode* GUINodeContainer::FindNode(int id)
{
int cnt = nodes.size();
for(int k = 0; k < cnt; k++)
{
GUIShaderNode* node = nodes[k];
if(node->GetGraphicsNode()->id == id)
return node;
}
return nullptr;
}
GUIShaderNode* GUINodeContainer::AddNode(const String& name)
{
IShaderGraphNode* gn = graphics_graph->AddNode(name);
GUIShaderNode* node;
if(name == STR("SrcTex2D"))
{
node = new GUIShaderNodeTexture(gn);
texNodes.push_back(node);
}
else
{
node = new GUIShaderNode(gn);
}
//copy all properties =)
PropertyCollection* props = gn->GetProperties();
for(int i = 0; i < props->size(); i++)
{
IProperty* prop = (*props)[i];
node->RegisterProperty(prop);
}
node->Event_Select += NewDelegate(this, &GUINodeContainer::OnNodeSelect);
int in_count = gn->Inputs.size();
int out_count = gn->Outputs.size();
//create input ports
for(int i = 0; i < in_count; i++)
{
IShaderGraphNodePort* port = gn->Inputs[i];
GUIShaderNodeIO* io = new GUIShaderNodeIO(node);
io->SetCaption(port->Name);
node->AddInput(io);
io->GraphicsPort = port;
io->Event_PortClick += NewDelegate(this, &GUINodeContainer::OnNodePortMouseClick);
}
//create output ports
for(int i = 0; i < out_count; i++)
{
IShaderGraphNodePort* port = gn->Outputs[i];
GUIShaderNodeIO* io = new GUIShaderNodeIO(node);
io->SetCaption(port->Name);
node->AddOutput(io);
io->GraphicsPort = port;
io->Event_PortClick += NewDelegate(this, &GUINodeContainer::OnNodePortMouseClick);
}
if(name == STR("Material"))
{
materialNode = node;
}
AddChild(node);
RegisterNewNode(node);
return node;
}
void GUINodeContainer::OnDeleteLink(GUIMenuItem* itm)
{
selected_port->GraphicsPort->BreakLink();
restart_delete_links:
for(LinksCollectionItr it = links.begin(); it != links.end(); ++it)
{
if((*it).left == selected_port || (*it).right == selected_port)
{
links.erase(it);
goto restart_delete_links;
}
}
Invalidate();
}
void GUINodeContainer::OnNodePortMouseClick(GUIWindow* wnd, int param)
{
//right click on node IO port
if(param == KEY_MOUSE_RIGHT)
{
menu->RemoveItems();
selected_port = (GUIShaderNodeIO*)wnd;
menu->AddItem("Delete links", false)->Event_Select += NewDelegate(this, &GUINodeContainer::OnDeleteLink);
menu->SetPosition(FindOverlay()->cursorPos);
FindOverlay()->AddChild(menu);
FindOverlay()->SetFocusWindow(menu);
}
}
void GUINodeContainer::OnNodeLayoutUpdate()
{
Invalidate();
}
GUINodeContainer::~GUINodeContainer()
{
}
void GUINodeContainer::SetBackColor(const Vec4f& color)
{
}
void GUINodeContainer::GenGeom()
{
cache_length = 0;
int lc = links.size();
for(int i = 0; i < lc; i++)
{
LinkInfo info = links[i];
Vec2i p1 = info.left->GetAttachPoint();
Vec2i p2;
Vec2i p3;
Vec2i p4 = info.right->GetAttachPoint();
//if input - shift to right
if(info.left->IsInput())
{
p2 = p1 + Vec2i(50, 0);
p3 = p4 + Vec2i(-50, 0);
}
else
{
p2 = p1 + Vec2i(-50, 0);
p3 = p4 + Vec2i(50, 0);
}
GUIVertex points[4];
points[0].Color = Vec4f(1,1,1,1);
points[1].Color = Vec4f(1,1,1,1);
points[2].Color = Vec4f(1,1,1,1);
points[3].Color = Vec4f(1,1,1,1);
points[0].Position = Vec3f((float)p1.X, (float)p1.Y, 0);
points[1].Position = Vec3f((float)p2.X, (float)p2.Y, 0);
points[2].Position = Vec3f((float)p3.X, (float)p3.Y, 0);
points[3].Position = Vec3f((float)p4.X, (float)p4.Y, 0);
//we have 4 points! now we have to convert them to cubic spline
int offset = 0;
GUIVertex prev;
prev = points[0];
for(int i = 1; i <= SPLINE_FREQ; i++)
{
GUIVertex newP = Core::CubicBezier<GUIVertex>((1.0f / (float)SPLINE_FREQ) * (float)i, points[0], points[1], points[2], points[3]);
cache[offset++] = prev;
cache[offset++] = newP;
prev = newP;
}
offset = 0;
Rect2i sc = GetScissor();
GUIVertex tmp;
//all spline points generated! Now it's time to cut them by owner's client rectangle
for(int i = 0; i < SPLINE_FREQ; i++)
{
GUIVertex v1 = cache[i * 2];
GUIVertex v2 = cache[i * 2 + 1];
//test is vertices are visible
if(v1.Position.X < sc.x && v2.Position.X < sc.x) continue;
if(v1.Position.X > sc.GetMaxX() && v2.Position.X > sc.GetMaxX()) continue;
if(v1.Position.Y < sc.y && v2.Position.Y < sc.y) continue;
if(v1.Position.Y > sc.GetMaxY() && v2.Position.Y > sc.GetMaxY()) continue;
//sort vertices
if(v1.Position.X > v2.Position.X)
{
tmp = v1;
v1 = v2;
v2 = tmp;
}
//crop right side
if(v2.Position.X > sc.GetMaxX())
{
float slope = (v2.Position.Y - v1.Position.Y) / (v2.Position.X - v1.Position.X);
float xd = v2.Position.X - float(sc.GetMaxX());
v2.Position.X -= xd;
v2.Position.Y -= xd * slope;
}
//crop left side
if(v1.Position.X < sc.x)
{
float slope = (v2.Position.Y - v1.Position.Y) / (v2.Position.X - v1.Position.X);
float xd = sc.x - v1.Position.X;
v1.Position.X += xd;
v1.Position.Y += xd * slope;
}
//sort vertices
if(v1.Position.Y > v2.Position.Y)
{
tmp = v1;
v1 = v2;
v2 = tmp;
}
//crop bottom side
if(v2.Position.Y > sc.GetMaxY())
{
float slope = (v2.Position.X - v1.Position.X) / (v2.Position.Y - v1.Position.Y);
float yd = v2.Position.Y - float(sc.GetMaxY());
v2.Position.X -= yd * slope;
v2.Position.Y -= yd;
}
//crop top side
if(v1.Position.Y < sc.y)
{
float slope = (v2.Position.X - v1.Position.X) / (v2.Position.Y - v1.Position.Y);
float yd = sc.y - v1.Position.Y;
v1.Position.X += yd * slope;
v1.Position.Y += yd;
}
cache2[cache_length++] = v1;
cache2[cache_length++] = v2;
}
}
invalid = false;
}
void GUINodeContainer::RenderSelf()
{
if(invalid)
GenGeom();
if(cache_length)
{
Video::IGUIRenderer* renderer = GUIManager::me()->GetRenderer();
renderer->SetTexture(GUIManager::me()->DIFF, 0, true);
renderer->Write(&cache2[0], cache_length);
}
}