2
0

guiShaderEditor.cpp 26 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2012 GarageGames, LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to
  6. // deal in the Software without restriction, including without limitation the
  7. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. // sell copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. // IN THE SOFTWARE.
  21. //-----------------------------------------------------------------------------
  22. #include "platform/platform.h"
  23. #include "gui/shaderEditor/guiShaderEditor.h"
  24. #include "gui/shaderEditor/nodes/materialOutputNode.h"
  25. #include "gui/shaderEditor/nodes/mathNode.h"
  26. #include "core/frameAllocator.h"
  27. #include "core/stream/fileStream.h"
  28. #include "core/stream/memStream.h"
  29. #include "console/consoleTypes.h"
  30. #include "gui/core/guiCanvas.h"
  31. #include "console/engineAPI.h"
  32. #include "console/script.h"
  33. #include "console/typeValidators.h"
  34. #include "gfx/primBuilder.h"
  35. IMPLEMENT_CONOBJECT(GuiShaderEditor);
  36. ConsoleDocClass(GuiShaderEditor,
  37. "@brief Implementation of a shader node editor.\n\n"
  38. "Editor use only.\n\n"
  39. "@internal"
  40. );
  41. GuiShaderEditor::GuiShaderEditor()
  42. : mDragBeginPoint(-1, -1),
  43. mViewOffset(0,0),
  44. mZoomScale(1.0f),
  45. mFullBoxSelection(false),
  46. mDragAddSelection(false),
  47. mDragMoveUndo(false)
  48. {
  49. VECTOR_SET_ASSOCIATION(mCurrNodes);
  50. VECTOR_SET_ASSOCIATION(mSelectedNodes);
  51. VECTOR_SET_ASSOCIATION(mDragBeginPoints);
  52. VECTOR_SET_ASSOCIATION(mCurrConnections);
  53. mActive = true;
  54. mMouseDownMode = GuiShaderEditor::Selecting;
  55. mTrash = NULL;
  56. mTempConnection = NULL;
  57. mNodeSize = 10;
  58. // test
  59. addNode(new BRDFOutputNode());
  60. addNode(new MathAddNode());
  61. addNode(new GuiShaderNode());
  62. }
  63. bool GuiShaderEditor::onWake()
  64. {
  65. if (!Parent::onWake())
  66. return false;
  67. return true;
  68. }
  69. void GuiShaderEditor::onSleep()
  70. {
  71. Parent::onSleep();
  72. }
  73. // anything smaller than 4 is way too small....
  74. IRangeValidator nodeSizeRange(4, 30);
  75. void GuiShaderEditor::initPersistFields()
  76. {
  77. docsURL;
  78. addGroup("Node Settings");
  79. addFieldV("nodeSize", TypeRangedS32, Offset(mNodeSize, GuiShaderEditor),&nodeSizeRange,
  80. "Size of nodes.");
  81. endGroup("Node Settings");
  82. addGroup("Selection");
  83. addField("fullBoxSelection", TypeBool, Offset(mFullBoxSelection, GuiShaderEditor),
  84. "If true, rectangle selection will only select controls fully inside the drag rectangle.");
  85. endGroup("Selection");
  86. Parent::initPersistFields();
  87. }
  88. bool GuiShaderEditor::onAdd()
  89. {
  90. if (!Parent::onAdd())
  91. return false;
  92. mTrash = new SimGroup();
  93. if (!mTrash->registerObject())
  94. return false;
  95. return true;
  96. }
  97. void GuiShaderEditor::onRemove()
  98. {
  99. Parent::onRemove();
  100. mTrash->deleteObject();
  101. mTrash = NULL;
  102. /* for (GuiShaderNode* node : mCurrNodes)
  103. {
  104. SAFE_DELETE(node);
  105. }*/
  106. for (GuiShaderNode* node : mSelectedNodes)
  107. {
  108. SAFE_DELETE(node);
  109. }
  110. }
  111. void GuiShaderEditor::onPreRender()
  112. {
  113. setUpdate();
  114. }
  115. void GuiShaderEditor::renderNodes(Point2I offset, const RectI& updateRect)
  116. {
  117. // Save the current clip rect
  118. // so we can restore it at the end of this method.
  119. RectI savedClipRect = GFX->getClipRect();
  120. // offset is the upper-left corner of this control in screen coordinates
  121. // updateRect is the intersection rectangle in screen coords of the control
  122. // hierarchy. This can be set as the clip rectangle in most cases.
  123. RectI clipRect = updateRect;
  124. clipRect.inset(2, 2);
  125. GFXDrawUtil* drawer = GFX->getDrawUtil();
  126. // render nodes in reverse order.
  127. for (ShaderNodeVector::iterator i = mCurrNodes.end(); i-- != mCurrNodes.begin(); )
  128. {
  129. GuiShaderNode* node = *i;
  130. // this is useful for sub node graphs.
  131. if (node->isVisible())
  132. {
  133. Point2I childPos = offset + node->getPosition();
  134. RectI childClip(childPos, node->getExtent() );
  135. if (selectionContains(node))
  136. {
  137. node->mSelected = true;
  138. }
  139. else
  140. {
  141. node->mSelected = false;
  142. }
  143. if (childClip.intersect(clipRect))
  144. {
  145. GFX->setClipRect(childClip);
  146. GFX->setStateBlock(mDefaultGuiSB);
  147. node->renderNode(childPos, childClip, mNodeSize);
  148. }
  149. GFX->setClipRect(clipRect);
  150. GFX->setStateBlock(mDefaultGuiSB);
  151. for (NodeInput* input : node->mInputNodes)
  152. {
  153. Point2I pos = node->localToGlobalCoord(input->pos) + offset;
  154. ColorI border = input->col;
  155. ColorI fill = mProfile->mFillColor;
  156. if (hasConnection(input))
  157. {
  158. fill = input->col;
  159. }
  160. RectI socketRect(pos, Point2I(mNodeSize, mNodeSize));
  161. drawer->drawCircleFill(socketRect, fill, mNodeSize / 2, 1.0f, border);
  162. }
  163. for (NodeOutput* output : node->mOutputNodes)
  164. {
  165. Point2I pos = node->localToGlobalCoord(output->pos) + offset;
  166. ColorI border = output->col;
  167. ColorI fill = mProfile->mFillColor;
  168. if (hasConnection(output))
  169. {
  170. fill = output->col;
  171. }
  172. RectI socketRect(pos, Point2I(mNodeSize, mNodeSize));
  173. drawer->drawCircleFill(socketRect, fill, mNodeSize / 2, 1.0f, border);
  174. }
  175. }
  176. }
  177. // Restore the clip rect to what it was at the start
  178. // of this method.
  179. GFX->setClipRect(savedClipRect);
  180. }
  181. void GuiShaderEditor::renderConnections(Point2I offset, const RectI& updateRect)
  182. {
  183. // Save the current clip rect
  184. // so we can restore it at the end of this method.
  185. RectI savedClipRect = GFX->getClipRect();
  186. // offset is the upper-left corner of this control in screen coordinates
  187. // updateRect is the intersection rectangle in screen coords of the control
  188. // hierarchy. This can be set as the clip rectangle in most cases.
  189. RectI clipRect = updateRect;
  190. clipRect.inset(2, 2);
  191. GFXDrawUtil* drawer = GFX->getDrawUtil();
  192. for (NodeConnection* conn : mCurrConnections)
  193. {
  194. // for temp connetion, nodeA is always the first node selected.
  195. Point2I start(Point2I::Zero);
  196. Point2I end(Point2I::Zero);
  197. start = conn->nodeA->localToGlobalCoord(conn->inSocket->pos) + offset;
  198. end = conn->nodeB->localToGlobalCoord(conn->outSocket->pos) + offset;
  199. start += Point2I(mNodeSize / 2, mNodeSize / 2);
  200. end += Point2I(mNodeSize / 2, mNodeSize / 2);
  201. drawer->drawThickLine(start, end,ColorI(255,255,255,255), 2.0f);
  202. }
  203. // Restore the clip rect to what it was at the start
  204. // of this method.
  205. GFX->setClipRect(savedClipRect);
  206. }
  207. void GuiShaderEditor::onRender(Point2I offset, const RectI& updateRect)
  208. {
  209. offset += mViewOffset;
  210. GFXDrawUtil* drawer = GFX->getDrawUtil();
  211. // render our nodes.
  212. renderConnections(offset, updateRect);
  213. renderNodes(offset, updateRect);
  214. if (mActive)
  215. {
  216. if (mMouseDownMode == DragConnection)
  217. {
  218. // something went wrong.... fix it fix it fix it.
  219. if (mTempConnection == NULL)
  220. return;
  221. else
  222. {
  223. // for temp connetion, nodeA is always the first node selected.
  224. Point2I start(Point2I::Zero);
  225. ColorI color(ColorI::WHITE);
  226. if (mTempConnection->inSocket != NULL)
  227. {
  228. start = mTempConnection->nodeA->localToGlobalCoord(mTempConnection->inSocket->pos) + offset;
  229. color = mTempConnection->inSocket->col;
  230. }
  231. else if (mTempConnection->outSocket != NULL)
  232. {
  233. start = mTempConnection->nodeA->localToGlobalCoord(mTempConnection->outSocket->pos) + offset;
  234. color = mTempConnection->outSocket->col;
  235. }
  236. RectI sockActive(start, Point2I(mNodeSize, mNodeSize));
  237. start += Point2I(mNodeSize / 2, mNodeSize / 2);
  238. drawer->drawThickLine(start, mLastMousePos + offset, color, 2.0f);
  239. // draw socket overlay over the top of the line.
  240. drawer->drawCircleFill(sockActive, color, mNodeSize / 2);
  241. }
  242. }
  243. // Draw selection rectangle last so it is rendered on top.
  244. if (mMouseDownMode == DragSelecting)
  245. {
  246. RectI b;
  247. getDragRect(b);
  248. b.point += offset;
  249. // Draw outline.
  250. drawer->drawRect(b, ColorI(100, 100, 100, 128));
  251. // Draw fill.
  252. b.inset(1, 1);
  253. drawer->drawRectFill(b, ColorI(150, 150, 150, 128));
  254. }
  255. }
  256. }
  257. bool GuiShaderEditor::onKeyDown(const GuiEvent& event)
  258. {
  259. if (!mActive)
  260. return Parent::onKeyDown(event);
  261. if (!(event.modifier & SI_PRIMARY_CTRL))
  262. {
  263. switch (event.keyCode)
  264. {
  265. case KEY_BACKSPACE:
  266. case KEY_DELETE:
  267. deleteSelection();
  268. return true;
  269. default:
  270. break;
  271. }
  272. }
  273. return false;
  274. }
  275. void GuiShaderEditor::onMouseDown(const GuiEvent& event)
  276. {
  277. if (!mActive)
  278. {
  279. Parent::onMouseDown(event);
  280. return;
  281. }
  282. setFirstResponder();
  283. // lock mouse
  284. mouseLock();
  285. // get mouse pos with our view offset and scale.
  286. mLastMousePos = globalToLocalCoord(event.mousePoint) - mViewOffset;
  287. GuiShaderNode* hitNode = findHitNode(mLastMousePos);
  288. if(findHitSocket(mLastMousePos))
  289. {
  290. // handled in hit socket.
  291. return;
  292. }
  293. else
  294. {
  295. if (event.modifier & SI_SHIFT)
  296. {
  297. startDragRectangle(mLastMousePos);
  298. mDragAddSelection = true;
  299. }
  300. else if (selectionContains(hitNode))
  301. {
  302. if (event.modifier & SI_MULTISELECT)
  303. {
  304. removeSelection(hitNode);
  305. setMouseMode(Selecting);
  306. }
  307. else if (event.modifier & SI_PRIMARY_ALT)
  308. {
  309. startDragClone(mLastMousePos);
  310. }
  311. else
  312. {
  313. startDragMove(mLastMousePos);
  314. }
  315. }
  316. else
  317. {
  318. if (hitNode == NULL)
  319. {
  320. startDragRectangle(mLastMousePos);
  321. mDragAddSelection = false;
  322. }
  323. else if (event.modifier & SI_PRIMARY_ALT && hitNode != NULL)
  324. {
  325. // Alt is down. Start a drag clone.
  326. clearSelection();
  327. addSelection(hitNode);
  328. startDragClone(mLastMousePos);
  329. }
  330. else if (event.modifier & SI_MULTISELECT)
  331. {
  332. addSelection(hitNode);
  333. }
  334. else
  335. {
  336. // Clicked on node. Start move.
  337. clearSelection();
  338. addSelection(hitNode);
  339. startDragMove(mLastMousePos);
  340. }
  341. }
  342. }
  343. }
  344. void GuiShaderEditor::onMouseUp(const GuiEvent& event)
  345. {
  346. if (!mActive)
  347. {
  348. Parent::onMouseUp(event);
  349. return;
  350. }
  351. //unlock the mouse
  352. mouseUnlock();
  353. // Reset Drag Axis Alignment Information
  354. mDragBeginPoint.set(-1, -1);
  355. mDragBeginPoints.clear();
  356. // get mouse pos with our view offset and scale.
  357. mLastMousePos = globalToLocalCoord(event.mousePoint) - mViewOffset;
  358. if (mMouseDownMode == DragConnection)
  359. {
  360. U32 ret = finishConnection(mLastMousePos);
  361. if (ret == 1) // we set the input on mouse up, nodeB is the inputSocket, swap order.
  362. {
  363. NodeConnection* conn = new NodeConnection();
  364. conn->nodeA = mTempConnection->nodeB;
  365. conn->nodeB = mTempConnection->nodeA;
  366. conn->inSocket = mTempConnection->inSocket;
  367. conn->outSocket = mTempConnection->outSocket;
  368. mCurrConnections.push_back(conn);
  369. }
  370. if (ret == 2) // we set the output on mouse up, nodeB is the outputSocket
  371. {
  372. NodeConnection* conn = new NodeConnection();
  373. conn->nodeA = mTempConnection->nodeA;
  374. conn->nodeB = mTempConnection->nodeB;
  375. conn->inSocket = mTempConnection->inSocket;
  376. conn->outSocket = mTempConnection->outSocket;
  377. mCurrConnections.push_back(conn);
  378. }
  379. mTempConnection = NULL;
  380. }
  381. if (mMouseDownMode == DragSelecting)
  382. {
  383. if (!(event.modifier & SI_MULTISELECT) && !mDragAddSelection)
  384. clearSelection();
  385. RectI rect;
  386. getDragRect(rect);
  387. if (rect.extent.x <= 2 && rect.extent.y <= 2)
  388. {
  389. addSelectionAtPoint(rect.point);
  390. }
  391. else
  392. {
  393. Vector< GuiShaderNode* > hits;
  394. findNodesInRect(rect, hits);
  395. for (GuiShaderNode* node : hits)
  396. {
  397. addSelection(node);
  398. }
  399. }
  400. }
  401. //reset the mouse mode
  402. setFirstResponder();
  403. setMouseMode(Selecting);
  404. }
  405. void GuiShaderEditor::onMouseDragged(const GuiEvent& event)
  406. {
  407. if (!mActive)
  408. {
  409. Parent::onMouseDragged(event);
  410. return;
  411. }
  412. // get mouse pos with our view offset and scale.
  413. Point2I mousePoint = globalToLocalCoord(event.mousePoint) - mViewOffset;
  414. if (mMouseDownMode == DragClone)
  415. {
  416. // If we haven't yet crossed the mouse delta to actually start the
  417. // clone, check if we have now.
  418. S32 delta = mAbs((mousePoint - mDragBeginPoint).len());
  419. if (delta >= 4)
  420. {
  421. cloneSelection();
  422. mLastMousePos = mDragBeginPoint;
  423. mDragMoveUndo = false;
  424. setMouseMode(MovingSelection);
  425. }
  426. }
  427. if (mMouseDownMode == MovingSelection && mSelectedNodes.size())
  428. {
  429. Point2I delta = mousePoint - mLastMousePos;
  430. RectI selBounds = getSelectionBounds();
  431. if (delta.x || delta.y)
  432. moveSelection(delta, mDragMoveUndo);
  433. mLastMousePos += delta;
  434. }
  435. else
  436. mLastMousePos = mousePoint;
  437. }
  438. void GuiShaderEditor::onMiddleMouseDown(const GuiEvent& event)
  439. {
  440. if (!mActive)
  441. {
  442. Parent::onMiddleMouseDown(event);
  443. return;
  444. }
  445. setFirstResponder();
  446. // lock mouse
  447. mouseLock();
  448. // get mouse pos with our view offset and scale.
  449. mLastMousePos = globalToLocalCoord(event.mousePoint);
  450. setMouseMode(DragPanning);
  451. getRoot()->showCursor(false);
  452. }
  453. void GuiShaderEditor::onMiddleMouseUp(const GuiEvent& event)
  454. {
  455. if (!mActive)
  456. {
  457. Parent::onMiddleMouseUp(event);
  458. return;
  459. }
  460. getRoot()->showCursor(true);
  461. //unlock the mouse
  462. mouseUnlock();
  463. // Reset Drag Axis Alignment Information
  464. mDragBeginPoint.set(-1, -1);
  465. mDragBeginPoints.clear();
  466. // get mouse pos with our view offset and scale.
  467. mLastMousePos = globalToLocalCoord(event.mousePoint);
  468. setFirstResponder();
  469. setMouseMode(Selecting);
  470. }
  471. void GuiShaderEditor::onMiddleMouseDragged(const GuiEvent& event)
  472. {
  473. if (!mActive)
  474. {
  475. Parent::onMiddleMouseDragged(event);
  476. return;
  477. }
  478. // get mouse pos with our view offset and scale.
  479. Point2I mousePoint = globalToLocalCoord(event.mousePoint);
  480. if (mMouseDownMode == DragPanning)
  481. {
  482. Point2I delta = mousePoint - mLastMousePos;
  483. // invert it
  484. if (delta.x || delta.y)
  485. mViewOffset += -delta;
  486. mLastMousePos += delta;
  487. }
  488. else
  489. mLastMousePos = mousePoint;
  490. }
  491. bool GuiShaderEditor::onMouseWheelUp(const GuiEvent& event)
  492. {
  493. if (!mActive || !mAwake || !mVisible)
  494. return Parent::onMouseWheelUp(event);
  495. mZoomScale *= 1.1f;
  496. return true;
  497. }
  498. bool GuiShaderEditor::onMouseWheelDown(const GuiEvent& event)
  499. {
  500. if (!mActive || !mAwake || !mVisible)
  501. return Parent::onMouseWheelDown(event);
  502. mZoomScale *= 0.9f;
  503. return true;
  504. }
  505. RectI GuiShaderEditor::getSelectionBounds()
  506. {
  507. Vector<GuiShaderNode*>::const_iterator i = mSelectedNodes.begin();
  508. Point2I minPos = (*i)->localToGlobalCoord(Point2I(0, 0));
  509. Point2I maxPos = minPos;
  510. for (; i != mSelectedNodes.end(); i++)
  511. {
  512. Point2I iPos = (**i).localToGlobalCoord(Point2I(0, 0));
  513. minPos.x = getMin(iPos.x, minPos.x);
  514. minPos.y = getMin(iPos.y, minPos.y);
  515. Point2I iExt = (**i).getExtent();
  516. iPos.x += iExt.x;
  517. iPos.y += iExt.y;
  518. maxPos.x = getMax(iPos.x, maxPos.x);
  519. maxPos.y = getMax(iPos.y, maxPos.y);
  520. }
  521. minPos = globalToLocalCoord(minPos);
  522. maxPos = globalToLocalCoord(maxPos);
  523. return RectI(minPos.x, minPos.y, (maxPos.x - minPos.x), (maxPos.y - minPos.y));
  524. }
  525. void GuiShaderEditor::deleteSelection()
  526. {
  527. for (GuiShaderNode* node : mSelectedNodes)
  528. {
  529. mTrash->addObject(node);
  530. Vector<NodeConnection*> connVec;
  531. for (NodeInput* input : node->mInputNodes)
  532. {
  533. hasConnection(input, connVec);
  534. }
  535. for (NodeOutput* output : node->mOutputNodes)
  536. {
  537. hasConnection(output, connVec);
  538. }
  539. for (NodeConnection* conn : connVec)
  540. {
  541. Vector< NodeConnection* >::iterator i = T3D::find(mCurrConnections.begin(), mCurrConnections.end(), conn);
  542. if (i != mCurrConnections.end())
  543. {
  544. mCurrConnections.erase(i);
  545. }
  546. }
  547. Vector< GuiShaderNode* >::iterator i = T3D::find(mCurrNodes.begin(), mCurrNodes.end(), node);
  548. if (i != mCurrNodes.end())
  549. mCurrNodes.erase(i);
  550. }
  551. clearSelection();
  552. }
  553. void GuiShaderEditor::moveSelection(const Point2I& delta, bool callback)
  554. {
  555. for (GuiShaderNode* node : mSelectedNodes)
  556. {
  557. node->setPosition(node->getPosition() + delta);
  558. }
  559. }
  560. void GuiShaderEditor::clearSelection()
  561. {
  562. mSelectedNodes.clear();
  563. }
  564. void GuiShaderEditor::cloneSelection()
  565. {
  566. Vector<GuiShaderNode*> newSelection;
  567. for (GuiShaderNode* node : mSelectedNodes)
  568. {
  569. GuiShaderNode* clone = dynamic_cast<GuiShaderNode*>(node->deepClone());
  570. if (clone)
  571. newSelection.push_back(clone);
  572. }
  573. clearSelection();
  574. for (GuiShaderNode* cloneNode : newSelection)
  575. {
  576. mCurrNodes.push_back(cloneNode);
  577. addSelection(cloneNode);
  578. }
  579. }
  580. void GuiShaderEditor::addSelectionAtPoint(const Point2I& pos)
  581. {
  582. // turn hit off on already selected nodes.
  583. canHitSelectedNodes(false);
  584. GuiShaderNode* node = findHitNode(pos);
  585. // reset hit status.
  586. canHitSelectedNodes();
  587. if (node)
  588. addSelection(node);
  589. }
  590. void GuiShaderEditor::addSelection(GuiShaderNode* inNode)
  591. {
  592. if (inNode != NULL && !selectionContains(inNode))
  593. {
  594. mSelectedNodes.push_back(inNode);
  595. }
  596. }
  597. bool GuiShaderEditor::selectionContains(GuiShaderNode* inNode)
  598. {
  599. for (GuiShaderNode* node : mSelectedNodes)
  600. {
  601. if (node == inNode)
  602. return true;
  603. }
  604. return false;
  605. }
  606. void GuiShaderEditor::removeSelection(GuiShaderNode* inNode)
  607. {
  608. if (selectionContains(inNode))
  609. {
  610. Vector< GuiShaderNode* >::iterator i = T3D::find(mSelectedNodes.begin(), mSelectedNodes.end(), inNode);
  611. if (i != mSelectedNodes.end())
  612. mSelectedNodes.erase(i);
  613. }
  614. }
  615. void GuiShaderEditor::canHitSelectedNodes(bool state)
  616. {
  617. for (GuiShaderNode* node : mSelectedNodes)
  618. node->setCanHit(state);
  619. }
  620. //-----------------------------------------------------------------------------
  621. // Input handling
  622. //-----------------------------------------------------------------------------
  623. GuiShaderNode* GuiShaderEditor::findHitNode(const Point2I& pt)
  624. {
  625. for (GuiShaderNode* node : mCurrNodes)
  626. {
  627. if (node->pointInControl(pt))
  628. {
  629. // selecting one node, push it to the front of the mcurrnodes stack so its rendered on top.
  630. Vector< GuiShaderNode* >::iterator i = T3D::find(mCurrNodes.begin(), mCurrNodes.end(), node);
  631. if (i != mCurrNodes.end())
  632. {
  633. mCurrNodes.erase(i);
  634. mCurrNodes.insert(mCurrNodes.begin(), node);
  635. }
  636. return node;
  637. }
  638. }
  639. return nullptr;
  640. }
  641. bool GuiShaderEditor::findHitSocket(const Point2I& pt)
  642. {
  643. Point2I parentOffset = localToGlobalCoord(getPosition()) + mViewOffset;
  644. Point2I offsetPoint = pt + localToGlobalCoord(getPosition());
  645. for (GuiShaderNode* node : mCurrNodes)
  646. {
  647. for (NodeInput* inNode : node->mInputNodes)
  648. {
  649. Point2I offSet = node->localToGlobalCoord(inNode->pos) + parentOffset;
  650. RectI box(offSet.x, offSet.y, mNodeSize, mNodeSize);
  651. S32 xt = offsetPoint.x - box.point.x;
  652. S32 yt = offsetPoint.y - box.point.y;
  653. if (xt >= 0 && yt >= 0 && xt < box.extent.x && yt < box.extent.y)
  654. {
  655. mTempConnection = new NodeConnection();
  656. mTempConnection->nodeA = node;
  657. mTempConnection->inSocket = inNode;
  658. setMouseMode(DragConnection);
  659. return true;
  660. }
  661. }
  662. for (NodeOutput* outNode : node->mOutputNodes)
  663. {
  664. Point2I offSet = node->localToGlobalCoord(outNode->pos) + parentOffset;
  665. RectI box(offSet.x, offSet.y, mNodeSize, mNodeSize);
  666. S32 xt = offsetPoint.x - box.point.x;
  667. S32 yt = offsetPoint.y - box.point.y;
  668. if (xt >= 0 && yt >= 0 && xt < box.extent.x && yt < box.extent.y)
  669. {
  670. mTempConnection = new NodeConnection();
  671. mTempConnection->nodeA = node;
  672. mTempConnection->outSocket = outNode;
  673. setMouseMode(DragConnection);
  674. return true;
  675. }
  676. }
  677. }
  678. return false;
  679. }
  680. U32 GuiShaderEditor::finishConnection(const Point2I& pt)
  681. {
  682. Point2I parentOffset = localToGlobalCoord(getPosition()) + mViewOffset;
  683. Point2I offsetPoint = pt + localToGlobalCoord(getPosition());
  684. for (GuiShaderNode* node : mCurrNodes)
  685. {
  686. for (NodeInput* inNode : node->mInputNodes)
  687. {
  688. Point2I offSet = node->localToGlobalCoord(inNode->pos) + parentOffset;
  689. RectI box(offSet.x, offSet.y, mNodeSize, mNodeSize);
  690. S32 xt = offsetPoint.x - box.point.x;
  691. S32 yt = offsetPoint.y - box.point.y;
  692. if (xt >= 0 && yt >= 0 && xt < box.extent.x && yt < box.extent.y)
  693. {
  694. if (mTempConnection->nodeA == node || mTempConnection->inSocket != NULL)
  695. return false;
  696. NodeConnection* conn;
  697. if(hasConnection(inNode, conn))
  698. {
  699. Vector< NodeConnection* >::iterator i = T3D::find(mCurrConnections.begin(), mCurrConnections.end(), conn);
  700. if (i != mCurrConnections.end())
  701. {
  702. mCurrConnections.erase(i);
  703. }
  704. }
  705. mTempConnection->nodeB = node;
  706. mTempConnection->inSocket = inNode;
  707. return 1;
  708. }
  709. }
  710. for (NodeOutput* outNode : node->mOutputNodes)
  711. {
  712. Point2I offSet = node->localToGlobalCoord(outNode->pos) + parentOffset;
  713. RectI box(offSet.x, offSet.y, mNodeSize, mNodeSize);
  714. S32 xt = offsetPoint.x - box.point.x;
  715. S32 yt = offsetPoint.y - box.point.y;
  716. if (xt >= 0 && yt >= 0 && xt < box.extent.x && yt < box.extent.y)
  717. {
  718. if (mTempConnection->nodeA == node || mTempConnection->outSocket != NULL)
  719. return false;
  720. NodeConnection* conn;
  721. if (hasConnection(mTempConnection->inSocket, conn))
  722. {
  723. Vector< NodeConnection* >::iterator i = T3D::find(mCurrConnections.begin(), mCurrConnections.end(), conn);
  724. if (i != mCurrConnections.end())
  725. {
  726. mCurrConnections.erase(i);
  727. }
  728. }
  729. mTempConnection->nodeB = node;
  730. mTempConnection->outSocket = outNode;
  731. return 2;
  732. }
  733. }
  734. }
  735. return 0;
  736. }
  737. bool GuiShaderEditor::hasConnection(NodeSocket* inSocket)
  738. {
  739. for (NodeConnection* con : mCurrConnections)
  740. {
  741. if (con->inSocket == dynamic_cast<NodeInput*>(inSocket) || con->outSocket == dynamic_cast<NodeOutput*>(inSocket))
  742. {
  743. return true;
  744. }
  745. }
  746. return false;
  747. }
  748. bool GuiShaderEditor::hasConnection(NodeSocket* inSocket, Vector<NodeConnection*>& conn)
  749. {
  750. bool ret = false;
  751. for (NodeConnection* con : mCurrConnections)
  752. {
  753. if (con->inSocket == dynamic_cast<NodeInput*>(inSocket) || con->outSocket == dynamic_cast<NodeOutput*>(inSocket))
  754. {
  755. conn.push_back(con);
  756. ret = true;
  757. }
  758. }
  759. return ret;
  760. }
  761. bool GuiShaderEditor::hasConnection(NodeSocket* inSocket, NodeConnection*& conn)
  762. {
  763. for (NodeConnection* con : mCurrConnections)
  764. {
  765. if (con->inSocket == dynamic_cast<NodeInput*>(inSocket) || con->outSocket == dynamic_cast<NodeOutput*>(inSocket))
  766. {
  767. if (conn != nullptr)
  768. conn = con;
  769. return true;
  770. }
  771. }
  772. return false;
  773. }
  774. void GuiShaderEditor::findNodesInRect(const RectI& rect, Vector<GuiShaderNode*>& outResult)
  775. {
  776. canHitSelectedNodes(false);
  777. for (GuiShaderNode* node : mCurrNodes)
  778. {
  779. if (node->getBounds().overlaps(rect))
  780. {
  781. outResult.push_back(node);
  782. }
  783. }
  784. canHitSelectedNodes();
  785. }
  786. void GuiShaderEditor::getDragRect(RectI& box)
  787. {
  788. box.point.x = getMin(mLastMousePos.x, mSelectionAnchor.x);
  789. box.extent.x = getMax(mLastMousePos.x, mSelectionAnchor.x) - box.point.x + 1;
  790. box.point.y = getMin(mLastMousePos.y, mSelectionAnchor.y);
  791. box.extent.y = getMax(mLastMousePos.y, mSelectionAnchor.y) - box.point.y + 1;
  792. }
  793. void GuiShaderEditor::startDragMove(const Point2I& startPoint)
  794. {
  795. mDragMoveUndo = true;
  796. mDragBeginPoint = startPoint;
  797. mDragBeginPoints.reserve(mSelectedNodes.size());
  798. for (GuiShaderNode* node : mSelectedNodes)
  799. {
  800. mDragBeginPoints.push_back(node->getPosition());
  801. }
  802. setMouseMode(MovingSelection);
  803. }
  804. void GuiShaderEditor::startDragRectangle(const Point2I& startPoint)
  805. {
  806. mSelectionAnchor = startPoint;
  807. setMouseMode(DragSelecting);
  808. }
  809. void GuiShaderEditor::startDragClone(const Point2I& startPoint)
  810. {
  811. mDragBeginPoint = startPoint;
  812. setMouseMode(DragClone);
  813. }
  814. void GuiShaderEditor::setMouseMode(mouseModes mode)
  815. {
  816. if (mMouseDownMode != mode)
  817. {
  818. mMouseDownMode = mode;
  819. }
  820. }
  821. void GuiShaderEditor::addNode(GuiShaderNode* newNode)
  822. {
  823. mCurrNodes.push_back(newNode);
  824. }