guiShaderEditor.cpp 27 KB

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