diff --git a/Engine/source/gui/shaderEditor/guiShaderEditor.cpp b/Engine/source/gui/shaderEditor/guiShaderEditor.cpp index fd0312769..b79de5643 100644 --- a/Engine/source/gui/shaderEditor/guiShaderEditor.cpp +++ b/Engine/source/gui/shaderEditor/guiShaderEditor.cpp @@ -30,6 +30,7 @@ #include "gui/core/guiCanvas.h" #include "console/engineAPI.h" #include "console/script.h" +#include "console/typeValidators.h" IMPLEMENT_CONOBJECT(GuiShaderEditor); @@ -55,10 +56,10 @@ GuiShaderEditor::GuiShaderEditor() mMouseDownMode = GuiShaderEditor::Selecting; mTrash = NULL; - + mNodeSize = 10; // test - mCurrNodes.push_back(new ShaderNode()); - mCurrNodes.push_back(new ShaderNode()); + mCurrNodes.push_back(new GuiShaderNode()); + mCurrNodes.push_back(new GuiShaderNode()); } bool GuiShaderEditor::onWake() @@ -74,12 +75,20 @@ void GuiShaderEditor::onSleep() Parent::onSleep(); } +// anything smaller than 4 is way too small.... +IRangeValidator nodeSizeRange(4, 30); + void GuiShaderEditor::initPersistFields() { docsURL; + addGroup("Node Settings"); + addFieldV("nodeSize", TypeS32, Offset(mNodeSize, GuiShaderEditor),&nodeSizeRange, + "Size of nodes."); + endGroup("Node Settings"); + addGroup("Selection"); - addField("fullBoxSelection", TypeBool, Offset(mFullBoxSelection, GuiShaderEditor), - "If true, rectangle selection will only select controls fully inside the drag rectangle."); + addField("fullBoxSelection", TypeBool, Offset(mFullBoxSelection, GuiShaderEditor), + "If true, rectangle selection will only select controls fully inside the drag rectangle."); endGroup("Selection"); Parent::initPersistFields(); } @@ -106,12 +115,12 @@ void GuiShaderEditor::onRemove() mTrash = NULL; - for (ShaderNode* node : mCurrNodes) + for (GuiShaderNode* node : mCurrNodes) { SAFE_DELETE(node); } - for (ShaderNode* node : mSelectedNodes) + for (GuiShaderNode* node : mSelectedNodes) { SAFE_DELETE(node); } @@ -134,8 +143,12 @@ void GuiShaderEditor::renderNodes(Point2I offset, const RectI& updateRect) RectI clipRect = updateRect; clipRect.inset(2, 2); - for (ShaderNode* node : mCurrNodes) + GFXDrawUtil* drawer = GFX->getDrawUtil(); + + // render nodes in reverse order. + for (ShaderNodeVector::iterator i = mCurrNodes.end(); i-- != mCurrNodes.begin(); ) { + GuiShaderNode* node = *i; // this is useful for sub node graphs. if (node->isVisible()) { @@ -155,7 +168,39 @@ void GuiShaderEditor::renderNodes(Point2I offset, const RectI& updateRect) { GFX->setClipRect(childClip); GFX->setStateBlock(mDefaultGuiSB); - node->onRender(childPos, childClip); + node->onRender(childPos, childClip, mNodeSize); + } + + GFX->setClipRect(clipRect); + GFX->setStateBlock(mDefaultGuiSB); + for (NodeInput* input : node->mInputNodes) + { + Point2I pos = node->localToGlobalCoord(input->pos) + offset; + + ColorI border = mProfile->mBorderColor; + + if (node->mSelected) + border = mProfile->mBorderColorSEL; + + RectI socketRect(pos, Point2I(mNodeSize, mNodeSize)); + drawer->drawRect(socketRect, border); + socketRect.inset(1, 1); + drawer->drawRectFill(socketRect, mProfile->mFillColor); + } + + for (NodeOutput* output : node->mOutputNodes) + { + Point2I pos = node->localToGlobalCoord(output->pos) + offset; + + ColorI border = mProfile->mBorderColor; + + if (node->mSelected) + border = mProfile->mBorderColorSEL; + + RectI socketRect(pos, Point2I(mNodeSize, mNodeSize)); + drawer->drawRect(socketRect, border); + socketRect.inset(1, 1); + drawer->drawRectFill(socketRect, mProfile->mFillColor); } } } @@ -227,7 +272,7 @@ void GuiShaderEditor::onMouseDown(const GuiEvent& event) // get mouse pos with our view offset and scale. mLastMousePos = globalToLocalCoord(event.mousePoint) - mViewOffset; - ShaderNode* hitNode = findHitNode(mLastMousePos); + GuiShaderNode* hitNode = findHitNode(mLastMousePos); if (event.modifier & SI_SHIFT) { @@ -311,10 +356,10 @@ void GuiShaderEditor::onMouseUp(const GuiEvent& event) } else { - Vector< ShaderNode* > hits; + Vector< GuiShaderNode* > hits; findNodesInRect(rect, hits); - for (ShaderNode* node : hits) + for (GuiShaderNode* node : hits) { addSelection(node); } @@ -458,7 +503,7 @@ bool GuiShaderEditor::onMouseWheelDown(const GuiEvent& event) RectI GuiShaderEditor::getSelectionBounds() { - Vector::const_iterator i = mSelectedNodes.begin(); + Vector::const_iterator i = mSelectedNodes.begin(); Point2I minPos = (*i)->localToGlobalCoord(Point2I(0, 0)); Point2I maxPos = minPos; @@ -487,11 +532,11 @@ RectI GuiShaderEditor::getSelectionBounds() void GuiShaderEditor::deleteSelection() { - for (ShaderNode* node : mSelectedNodes) + for (GuiShaderNode* node : mSelectedNodes) { mTrash->addObject(node); - Vector< ShaderNode* >::iterator i = T3D::find(mCurrNodes.begin(), mCurrNodes.end(), node); + Vector< GuiShaderNode* >::iterator i = T3D::find(mCurrNodes.begin(), mCurrNodes.end(), node); if (i != mCurrNodes.end()) mCurrNodes.erase(i); } @@ -501,7 +546,7 @@ void GuiShaderEditor::deleteSelection() void GuiShaderEditor::moveSelection(const Point2I& delta, bool callback) { - for (ShaderNode* node : mSelectedNodes) + for (GuiShaderNode* node : mSelectedNodes) { node->setPosition(node->getPosition() + delta); } @@ -514,19 +559,20 @@ void GuiShaderEditor::clearSelection() void GuiShaderEditor::cloneSelection() { - Vector newSelection; + Vector newSelection; - for (ShaderNode* node : mSelectedNodes) + for (GuiShaderNode* node : mSelectedNodes) { - ShaderNode* clone = dynamic_cast(node->deepClone()); + GuiShaderNode* clone = dynamic_cast(node->deepClone()); if (clone) newSelection.push_back(clone); } clearSelection(); - for (ShaderNode* cloneNode : newSelection) + for (GuiShaderNode* cloneNode : newSelection) { + mCurrNodes.push_back(cloneNode); addSelection(cloneNode); } } @@ -536,7 +582,7 @@ void GuiShaderEditor::addSelectionAtPoint(const Point2I& pos) // turn hit off on already selected nodes. canHitSelectedNodes(false); - ShaderNode* node = findHitNode(pos); + GuiShaderNode* node = findHitNode(pos); // reset hit status. canHitSelectedNodes(); @@ -545,7 +591,7 @@ void GuiShaderEditor::addSelectionAtPoint(const Point2I& pos) addSelection(node); } -void GuiShaderEditor::addSelection(ShaderNode* inNode) +void GuiShaderEditor::addSelection(GuiShaderNode* inNode) { if (inNode != NULL && !selectionContains(inNode)) { @@ -553,9 +599,9 @@ void GuiShaderEditor::addSelection(ShaderNode* inNode) } } -bool GuiShaderEditor::selectionContains(ShaderNode* inNode) +bool GuiShaderEditor::selectionContains(GuiShaderNode* inNode) { - for (ShaderNode* node : mSelectedNodes) + for (GuiShaderNode* node : mSelectedNodes) { if (node == inNode) return true; @@ -564,11 +610,11 @@ bool GuiShaderEditor::selectionContains(ShaderNode* inNode) return false; } -void GuiShaderEditor::removeSelection(ShaderNode* inNode) +void GuiShaderEditor::removeSelection(GuiShaderNode* inNode) { if (selectionContains(inNode)) { - Vector< ShaderNode* >::iterator i = T3D::find(mSelectedNodes.begin(), mSelectedNodes.end(), inNode); + Vector< GuiShaderNode* >::iterator i = T3D::find(mSelectedNodes.begin(), mSelectedNodes.end(), inNode); if (i != mSelectedNodes.end()) mSelectedNodes.erase(i); } @@ -576,7 +622,7 @@ void GuiShaderEditor::removeSelection(ShaderNode* inNode) void GuiShaderEditor::canHitSelectedNodes(bool state) { - for (ShaderNode* node : mSelectedNodes) + for (GuiShaderNode* node : mSelectedNodes) node->setCanHit(state); } @@ -584,12 +630,20 @@ void GuiShaderEditor::canHitSelectedNodes(bool state) // Input handling //----------------------------------------------------------------------------- -ShaderNode* GuiShaderEditor::findHitNode(const Point2I& pt) +GuiShaderNode* GuiShaderEditor::findHitNode(const Point2I& pt) { - for (ShaderNode* node : mCurrNodes) + for (GuiShaderNode* node : mCurrNodes) { if (node->pointInControl(pt)) { + // selecting one node, push it to the front of the mcurrnodes stack so its rendered on top. + Vector< GuiShaderNode* >::iterator i = T3D::find(mCurrNodes.begin(), mCurrNodes.end(), node); + if (i != mCurrNodes.end()) + { + mCurrNodes.erase(i); + mCurrNodes.insert(mCurrNodes.begin(), node); + } + return node; } } @@ -597,10 +651,10 @@ ShaderNode* GuiShaderEditor::findHitNode(const Point2I& pt) return nullptr; } -void GuiShaderEditor::findNodesInRect(const RectI& rect, Vector& outResult) +void GuiShaderEditor::findNodesInRect(const RectI& rect, Vector& outResult) { canHitSelectedNodes(false); - for (ShaderNode* node : mCurrNodes) + for (GuiShaderNode* node : mCurrNodes) { if (node->getBounds().overlaps(rect)) { @@ -627,7 +681,7 @@ void GuiShaderEditor::startDragMove(const Point2I& startPoint) mDragBeginPoints.reserve(mSelectedNodes.size()); - for (ShaderNode* node : mSelectedNodes) + for (GuiShaderNode* node : mSelectedNodes) { mDragBeginPoints.push_back(node->getPosition()); } diff --git a/Engine/source/gui/shaderEditor/guiShaderEditor.h b/Engine/source/gui/shaderEditor/guiShaderEditor.h index 5e9d3f866..011e3cb58 100644 --- a/Engine/source/gui/shaderEditor/guiShaderEditor.h +++ b/Engine/source/gui/shaderEditor/guiShaderEditor.h @@ -48,7 +48,7 @@ public: protected: // list - typedef Vector ShaderNodeVector; + typedef Vector ShaderNodeVector; // all nodes in this graph. ShaderNodeVector mCurrNodes; @@ -68,13 +68,14 @@ protected: bool mDragAddSelection; bool mDragMoveUndo; bool mFullBoxSelection; + S32 mNodeSize; ShaderNodeVector mSelectedNodes; void renderNodes(Point2I offset, const RectI& updateRect); // functions for handling mouse events. - ShaderNode* findHitNode(const Point2I& pt); - void findNodesInRect(const RectI& rect, Vector& outResult); + GuiShaderNode* findHitNode(const Point2I& pt); + void findNodesInRect(const RectI& rect, Vector& outResult); void getDragRect(RectI& box); void startDragMove(const Point2I& startPoint); @@ -115,9 +116,9 @@ public: void clearSelection(); void cloneSelection(); void addSelectionAtPoint(const Point2I& pos); - void addSelection(ShaderNode* inNode); - bool selectionContains(ShaderNode* inNode); - void removeSelection(ShaderNode* inNode); + void addSelection(GuiShaderNode* inNode); + bool selectionContains(GuiShaderNode* inNode); + void removeSelection(GuiShaderNode* inNode); void canHitSelectedNodes(bool state = true); }; diff --git a/Engine/source/gui/shaderEditor/nodes/shaderNode.cpp b/Engine/source/gui/shaderEditor/nodes/shaderNode.cpp index 16aa08c8b..ee5766a40 100644 --- a/Engine/source/gui/shaderEditor/nodes/shaderNode.cpp +++ b/Engine/source/gui/shaderEditor/nodes/shaderNode.cpp @@ -25,29 +25,46 @@ #include "gui/core/guiCanvas.h" -IMPLEMENT_CONOBJECT(ShaderNode); +IMPLEMENT_CONOBJECT(GuiShaderNode); -ConsoleDocClass(ShaderNode, +ConsoleDocClass(GuiShaderNode, "@brief Base class for all nodes to derive from.\n\n" "Editor use only.\n\n" "@internal" ); -ShaderNode::ShaderNode() +GuiShaderNode::GuiShaderNode() { + VECTOR_SET_ASSOCIATION(mInputNodes); + VECTOR_SET_ASSOCIATION(mOutputNodes); + mTitle = "Default Node"; mSelected = false; - mNodeType = NodeTypes::Uniform; - // fixed extent for all nodes, only height should be changed - setExtent(210, 100); + mNodeType = NodeTypes::Default; + GuiControlProfile* profile = NULL; - if (Sim::findObject("ToolsGuiDefaultProfile", profile)) + if (Sim::findObject("GuiShaderEditorProfile", profile)) setControlProfile(profile); + + mInputNodes.push_back(new NodeInput("RGBA", DataDimensions::Dynamic)); + mInputNodes.push_back(new NodeInput("RGBA", DataDimensions::Dynamic)); + mInputNodes.push_back(new NodeInput("RGBA", DataDimensions::Dynamic)); + mInputNodes.push_back(new NodeInput("RGBA", DataDimensions::Dynamic)); + + mOutputNodes.push_back(new NodeOutput("RGBA", DataDimensions::Dynamic)); + mOutputNodes.push_back(new NodeOutput("RGBA", DataDimensions::Dynamic)); + mOutputNodes.push_back(new NodeOutput("RGBA", DataDimensions::Dynamic)); + mOutputNodes.push_back(new NodeOutput("RGBA", DataDimensions::Dynamic)); + + // fixed extent for all nodes, only height should be changed + setExtent(210, 35); + + mPrevNodeSize = -1; } -bool ShaderNode::onWake() +bool GuiShaderNode::onWake() { if (!Parent::onWake()) return false; @@ -55,18 +72,18 @@ bool ShaderNode::onWake() return true; } -void ShaderNode::onSleep() +void GuiShaderNode::onSleep() { Parent::onSleep(); } -void ShaderNode::initPersistFields() +void GuiShaderNode::initPersistFields() { docsURL; Parent::initPersistFields(); } -bool ShaderNode::onAdd() +bool GuiShaderNode::onAdd() { if (!Parent::onAdd()) return false; @@ -74,12 +91,12 @@ bool ShaderNode::onAdd() return true; } -void ShaderNode::onRemove() +void GuiShaderNode::onRemove() { Parent::onRemove(); } -void ShaderNode::onRender(Point2I offset, const RectI& updateRect) +void GuiShaderNode::onRender(Point2I offset, const RectI& updateRect, const S32 nodeSize) { if (!mProfile) return Parent::onRender(offset, updateRect); @@ -94,10 +111,6 @@ void ShaderNode::onRender(Point2I offset, const RectI& updateRect) drawer->drawRectFill(winRect, mProfile->mFillColor); // draw header - RectI headRect; - headRect.point = offset; - headRect.extent = Point2I(getExtent().x, 30); - ColorI header(50, 50, 50, 128); switch (mNodeType) @@ -131,28 +144,72 @@ void ShaderNode::onRender(Point2I offset, const RectI& updateRect) break; } + RectI headRect; + U32 headerSize = 30; + headRect.point = offset; + headRect.extent = Point2I(getExtent().x, headerSize); drawer->drawRectFill(headRect, header); // draw header text. U32 strWidth = mProfile->mFont->getStrWidth(mTitle.c_str()); - Point2I headerPos = Point2I((getExtent().x / 2) - (strWidth / 2), (30 / 2) - (mProfile->mFont->getFontSize() / 2)); + Point2I headerPos = Point2I((getExtent().x / 2) - (strWidth / 2), (headerSize / 2) - (mProfile->mFont->getFontSize() / 2)); drawer->setBitmapModulation(mProfile->mFontColor); drawer->drawText(mProfile->mFont, headerPos + offset, mTitle); drawer->clearBitmapModulation(); - ColorI border(128, 128, 128, 128); + ColorI border = mProfile->mBorderColor; if (mSelected) - border = ColorI(128, 0, 128, 255); + border = mProfile->mBorderColorSEL; drawer->drawRect(winRect, border); + + if (mInputNodes.size() > 0 || mOutputNodes.size() > 0) + { + U32 textPadX = nodeSize, textPadY = mProfile->mFont->getFontSize() + (nodeSize / 2); + Point2I slotPos(textPadX, headerSize + (nodeSize / 2)); + drawer->setBitmapModulation(mProfile->mFontColor); + for (NodeInput* input : mInputNodes) + { + drawer->drawText(mProfile->mFont, slotPos + offset, input->name); + + if (input->pos == Point2I::Zero || mPrevNodeSize != nodeSize) + input->pos = Point2I(-(nodeSize / 2), slotPos.y + ((mProfile->mFont->getFontSize() / 2) - (nodeSize / 2))); + + slotPos.y += textPadY; + } + + U32 inputY = slotPos.y; + + slotPos = Point2I(getExtent().x, headerSize + (nodeSize / 2)); + for (NodeOutput* output : mOutputNodes) + { + strWidth = mProfile->mFont->getStrWidth(output->name.c_str()); + slotPos.x = getExtent().x - strWidth - textPadX; + + drawer->drawText(mProfile->mFont, slotPos + offset, output->name); + + if (output->pos == Point2I::Zero || mPrevNodeSize != nodeSize) + output->pos = Point2I(getExtent().x - (nodeSize / 2), slotPos.y + ((mProfile->mFont->getFontSize() / 2) - (nodeSize / 2))); + + slotPos.y += textPadY; + } + drawer->clearBitmapModulation(); + + U32 outputY = slotPos.y; + + if (getExtent().y < slotPos.y || mPrevNodeSize != nodeSize) + setExtent(Point2I(getExtent().x, mMax(inputY, outputY))); + + mPrevNodeSize = nodeSize; + } } -void ShaderNode::write(Stream& stream, U32 tabStop, U32 flags) +void GuiShaderNode::write(Stream& stream, U32 tabStop, U32 flags) { } -void ShaderNode::read(Stream& stream) +void GuiShaderNode::read(Stream& stream) { } diff --git a/Engine/source/gui/shaderEditor/nodes/shaderNode.h b/Engine/source/gui/shaderEditor/nodes/shaderNode.h index 09db42bc2..645125e05 100644 --- a/Engine/source/gui/shaderEditor/nodes/shaderNode.h +++ b/Engine/source/gui/shaderEditor/nodes/shaderNode.h @@ -62,15 +62,31 @@ struct NodeInput { String name; DataDimensions dimensions; + Point2I pos = Point2I::Zero; + + NodeInput() + :name(String::EmptyString), dimensions(DataDimensions::Dynamic) + {} + NodeInput(String inName , DataDimensions inDim) + :name(inName), dimensions(inDim) + {} }; struct NodeOutput { String name; DataDimensions dimensions; + Point2I pos = Point2I::Zero; + + NodeOutput() + :name(String::EmptyString), dimensions(DataDimensions::Dynamic) + {} + NodeOutput(String inName, DataDimensions inDim) + :name(inName), dimensions(inDim) + {} }; -class ShaderNode : public GuiControl +class GuiShaderNode : public GuiControl { private: typedef GuiControl Parent; @@ -78,9 +94,12 @@ private: protected: String mTitle; NodeTypes mNodeType; - + S32 mPrevNodeSize; public: - ShaderNode(); + Vector mInputNodes; + Vector mOutputNodes; + + GuiShaderNode(); bool onWake(); void onSleep(); @@ -88,14 +107,14 @@ public: virtual bool onAdd() override; virtual void onRemove() override; - virtual void onRender(Point2I offset, const RectI& updateRect) override; + void onRender(Point2I offset, const RectI& updateRect, const S32 nodeSize); // Serialization functions void write(Stream& stream, U32 tabStop = 0, U32 flags = 0); void read(Stream& stream); // is the parent that all other nodes are derived from. - DECLARE_CONOBJECT(ShaderNode); + DECLARE_CONOBJECT(GuiShaderNode); DECLARE_CATEGORY("Shader Core"); DECLARE_DESCRIPTION("Base class for all shader nodes.");