guiShaderEditor.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658
  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. IMPLEMENT_CONOBJECT(GuiShaderEditor);
  32. ConsoleDocClass(GuiShaderEditor,
  33. "@brief Implementation of a shader node editor.\n\n"
  34. "Editor use only.\n\n"
  35. "@internal"
  36. );
  37. GuiShaderEditor::GuiShaderEditor()
  38. : mDragBeginPoint(-1, -1),
  39. mViewOffset(0,0),
  40. mZoomScale(1.0f),
  41. mFullBoxSelection(false),
  42. mDragAddSelection(false),
  43. mDragMoveUndo(false)
  44. {
  45. VECTOR_SET_ASSOCIATION(mCurrNodes);
  46. VECTOR_SET_ASSOCIATION(mSelectedNodes);
  47. VECTOR_SET_ASSOCIATION(mDragBeginPoints);
  48. mActive = true;
  49. mMouseDownMode = GuiShaderEditor::Selecting;
  50. mTrash = NULL;
  51. // test
  52. mCurrNodes.push_back(new ShaderNode());
  53. mCurrNodes.push_back(new ShaderNode());
  54. }
  55. bool GuiShaderEditor::onWake()
  56. {
  57. if (!Parent::onWake())
  58. return false;
  59. return true;
  60. }
  61. void GuiShaderEditor::onSleep()
  62. {
  63. Parent::onSleep();
  64. }
  65. void GuiShaderEditor::initPersistFields()
  66. {
  67. docsURL;
  68. addGroup("Selection");
  69. addField("fullBoxSelection", TypeBool, Offset(mFullBoxSelection, GuiShaderEditor),
  70. "If true, rectangle selection will only select controls fully inside the drag rectangle.");
  71. endGroup("Selection");
  72. Parent::initPersistFields();
  73. }
  74. bool GuiShaderEditor::onAdd()
  75. {
  76. if (!Parent::onAdd())
  77. return false;
  78. mTrash = new SimGroup();
  79. if (!mTrash->registerObject())
  80. return false;
  81. return true;
  82. }
  83. void GuiShaderEditor::onRemove()
  84. {
  85. Parent::onRemove();
  86. mTrash->deleteObject();
  87. mTrash = NULL;
  88. for (ShaderNode* node : mCurrNodes)
  89. {
  90. SAFE_DELETE(node);
  91. }
  92. for (ShaderNode* node : mSelectedNodes)
  93. {
  94. SAFE_DELETE(node);
  95. }
  96. }
  97. void GuiShaderEditor::onPreRender()
  98. {
  99. setUpdate();
  100. }
  101. void GuiShaderEditor::renderNodes(Point2I offset, const RectI& updateRect)
  102. {
  103. // Save the current clip rect
  104. // so we can restore it at the end of this method.
  105. RectI savedClipRect = GFX->getClipRect();
  106. // offset is the upper-left corner of this control in screen coordinates
  107. // updateRect is the intersection rectangle in screen coords of the control
  108. // hierarchy. This can be set as the clip rectangle in most cases.
  109. RectI clipRect = updateRect;
  110. clipRect.inset(2, 2);
  111. for (ShaderNode* node : mCurrNodes)
  112. {
  113. // this is useful for sub node graphs.
  114. if (node->isVisible())
  115. {
  116. Point2I childPos = offset + node->getPosition();
  117. RectI childClip(childPos, node->getExtent() );
  118. if (selectionContains(node))
  119. {
  120. node->mSelected = true;
  121. }
  122. else
  123. {
  124. node->mSelected = false;
  125. }
  126. if (childClip.intersect(clipRect))
  127. {
  128. GFX->setClipRect(childClip);
  129. GFX->setStateBlock(mDefaultGuiSB);
  130. node->onRender(childPos, childClip);
  131. }
  132. }
  133. }
  134. // Restore the clip rect to what it was at the start
  135. // of this method.
  136. GFX->setClipRect(savedClipRect);
  137. }
  138. void GuiShaderEditor::onRender(Point2I offset, const RectI& updateRect)
  139. {
  140. offset += mViewOffset;
  141. GFXDrawUtil* drawer = GFX->getDrawUtil();
  142. // render our nodes.
  143. renderNodes(offset, updateRect);
  144. // Draw selection rectangle last so it is rendered on top.
  145. if (mActive && mMouseDownMode == DragSelecting)
  146. {
  147. RectI b;
  148. getDragRect(b);
  149. b.point += offset;
  150. // Draw outline.
  151. drawer->drawRect(b, ColorI(100, 100, 100, 128));
  152. // Draw fill.
  153. b.inset(1, 1);
  154. drawer->drawRectFill(b, ColorI(150, 150, 150, 128));
  155. }
  156. }
  157. bool GuiShaderEditor::onKeyDown(const GuiEvent& event)
  158. {
  159. if (!mActive)
  160. return Parent::onKeyDown(event);
  161. if (!(event.modifier & SI_PRIMARY_CTRL))
  162. {
  163. switch (event.keyCode)
  164. {
  165. case KEY_BACKSPACE:
  166. case KEY_DELETE:
  167. deleteSelection();
  168. return true;
  169. default:
  170. break;
  171. }
  172. }
  173. return false;
  174. }
  175. void GuiShaderEditor::onMouseDown(const GuiEvent& event)
  176. {
  177. if (!mActive)
  178. {
  179. Parent::onMouseDown(event);
  180. return;
  181. }
  182. setFirstResponder();
  183. // lock mouse
  184. mouseLock();
  185. // get mouse pos with our view offset and scale.
  186. mLastMousePos = globalToLocalCoord(event.mousePoint) - mViewOffset;
  187. ShaderNode* hitNode = findHitNode(mLastMousePos);
  188. if (event.modifier & SI_SHIFT)
  189. {
  190. startDragRectangle(mLastMousePos);
  191. mDragAddSelection = true;
  192. }
  193. else if (selectionContains(hitNode))
  194. {
  195. if (event.modifier & SI_MULTISELECT)
  196. {
  197. removeSelection(hitNode);
  198. setMouseMode(Selecting);
  199. }
  200. else if (event.modifier & SI_PRIMARY_ALT)
  201. {
  202. startDragClone(mLastMousePos);
  203. }
  204. else
  205. {
  206. startDragMove(mLastMousePos);
  207. }
  208. }
  209. else
  210. {
  211. if (hitNode == NULL)
  212. {
  213. startDragRectangle(mLastMousePos);
  214. mDragAddSelection = false;
  215. }
  216. else if (event.modifier & SI_PRIMARY_ALT && hitNode != NULL)
  217. {
  218. // Alt is down. Start a drag clone.
  219. clearSelection();
  220. addSelection(hitNode);
  221. startDragClone(mLastMousePos);
  222. }
  223. else if (event.modifier & SI_MULTISELECT)
  224. {
  225. addSelection(hitNode);
  226. }
  227. else
  228. {
  229. // Clicked on node. Start move.
  230. clearSelection();
  231. addSelection(hitNode);
  232. startDragMove(mLastMousePos);
  233. }
  234. }
  235. }
  236. void GuiShaderEditor::onMouseUp(const GuiEvent& event)
  237. {
  238. if (!mActive)
  239. {
  240. Parent::onMouseUp(event);
  241. return;
  242. }
  243. //unlock the mouse
  244. mouseUnlock();
  245. // Reset Drag Axis Alignment Information
  246. mDragBeginPoint.set(-1, -1);
  247. mDragBeginPoints.clear();
  248. // get mouse pos with our view offset and scale.
  249. mLastMousePos = globalToLocalCoord(event.mousePoint) - mViewOffset;
  250. if (mMouseDownMode == DragSelecting)
  251. {
  252. if (!(event.modifier & SI_MULTISELECT) && !mDragAddSelection)
  253. clearSelection();
  254. RectI rect;
  255. getDragRect(rect);
  256. if (rect.extent.x <= 2 && rect.extent.y <= 2)
  257. {
  258. addSelectionAtPoint(rect.point);
  259. }
  260. else
  261. {
  262. Vector< ShaderNode* > hits;
  263. findNodesInRect(rect, hits);
  264. for (ShaderNode* node : hits)
  265. {
  266. addSelection(node);
  267. }
  268. }
  269. }
  270. //reset the mouse mode
  271. setFirstResponder();
  272. setMouseMode(Selecting);
  273. }
  274. void GuiShaderEditor::onMouseDragged(const GuiEvent& event)
  275. {
  276. if (!mActive)
  277. {
  278. Parent::onMouseDragged(event);
  279. return;
  280. }
  281. // get mouse pos with our view offset and scale.
  282. Point2I mousePoint = globalToLocalCoord(event.mousePoint) - mViewOffset;
  283. if (mMouseDownMode == DragClone)
  284. {
  285. // If we haven't yet crossed the mouse delta to actually start the
  286. // clone, check if we have now.
  287. S32 delta = mAbs((mousePoint - mDragBeginPoint).len());
  288. if (delta >= 4)
  289. {
  290. cloneSelection();
  291. mLastMousePos = mDragBeginPoint;
  292. mDragMoveUndo = false;
  293. setMouseMode(MovingSelection);
  294. }
  295. }
  296. if (mMouseDownMode == MovingSelection && mSelectedNodes.size())
  297. {
  298. Point2I delta = mousePoint - mLastMousePos;
  299. RectI selBounds = getSelectionBounds();
  300. if (delta.x || delta.y)
  301. moveSelection(delta, mDragMoveUndo);
  302. mLastMousePos += delta;
  303. }
  304. else
  305. mLastMousePos = mousePoint;
  306. }
  307. void GuiShaderEditor::onMiddleMouseDown(const GuiEvent& event)
  308. {
  309. if (!mActive)
  310. {
  311. Parent::onMiddleMouseDown(event);
  312. return;
  313. }
  314. setFirstResponder();
  315. // lock mouse
  316. mouseLock();
  317. // get mouse pos with our view offset and scale.
  318. mLastMousePos = globalToLocalCoord(event.mousePoint);
  319. setMouseMode(DragPanning);
  320. getRoot()->showCursor(false);
  321. }
  322. void GuiShaderEditor::onMiddleMouseUp(const GuiEvent& event)
  323. {
  324. if (!mActive)
  325. {
  326. Parent::onMiddleMouseUp(event);
  327. return;
  328. }
  329. getRoot()->showCursor(true);
  330. //unlock the mouse
  331. mouseUnlock();
  332. // Reset Drag Axis Alignment Information
  333. mDragBeginPoint.set(-1, -1);
  334. mDragBeginPoints.clear();
  335. // get mouse pos with our view offset and scale.
  336. mLastMousePos = globalToLocalCoord(event.mousePoint);
  337. setFirstResponder();
  338. setMouseMode(Selecting);
  339. }
  340. void GuiShaderEditor::onMiddleMouseDragged(const GuiEvent& event)
  341. {
  342. if (!mActive)
  343. {
  344. Parent::onMiddleMouseDragged(event);
  345. return;
  346. }
  347. // get mouse pos with our view offset and scale.
  348. Point2I mousePoint = globalToLocalCoord(event.mousePoint);
  349. if (mMouseDownMode == DragPanning)
  350. {
  351. Point2I delta = mousePoint - mLastMousePos;
  352. // invert it
  353. if (delta.x || delta.y)
  354. mViewOffset += -delta;
  355. mLastMousePos += delta;
  356. }
  357. else
  358. mLastMousePos = mousePoint;
  359. }
  360. bool GuiShaderEditor::onMouseWheelUp(const GuiEvent& event)
  361. {
  362. if (!mActive || !mAwake || !mVisible)
  363. return Parent::onMouseWheelUp(event);
  364. mZoomScale *= 1.1;
  365. return true;
  366. }
  367. bool GuiShaderEditor::onMouseWheelDown(const GuiEvent& event)
  368. {
  369. if (!mActive || !mAwake || !mVisible)
  370. return Parent::onMouseWheelDown(event);
  371. mZoomScale *= 0.9;
  372. return true;
  373. }
  374. RectI GuiShaderEditor::getSelectionBounds()
  375. {
  376. Vector<ShaderNode*>::const_iterator i = mSelectedNodes.begin();
  377. Point2I minPos = (*i)->localToGlobalCoord(Point2I(0, 0));
  378. Point2I maxPos = minPos;
  379. for (; i != mSelectedNodes.end(); i++)
  380. {
  381. Point2I iPos = (**i).localToGlobalCoord(Point2I(0, 0));
  382. minPos.x = getMin(iPos.x, minPos.x);
  383. minPos.y = getMin(iPos.y, minPos.y);
  384. Point2I iExt = (**i).getExtent();
  385. iPos.x += iExt.x;
  386. iPos.y += iExt.y;
  387. maxPos.x = getMax(iPos.x, maxPos.x);
  388. maxPos.y = getMax(iPos.y, maxPos.y);
  389. }
  390. minPos = globalToLocalCoord(minPos);
  391. maxPos = globalToLocalCoord(maxPos);
  392. return RectI(minPos.x, minPos.y, (maxPos.x - minPos.x), (maxPos.y - minPos.y));
  393. }
  394. void GuiShaderEditor::deleteSelection()
  395. {
  396. for (ShaderNode* node : mSelectedNodes)
  397. {
  398. mTrash->addObject(node);
  399. Vector< ShaderNode* >::iterator i = T3D::find(mCurrNodes.begin(), mCurrNodes.end(), node);
  400. if (i != mCurrNodes.end())
  401. mCurrNodes.erase(i);
  402. }
  403. clearSelection();
  404. }
  405. void GuiShaderEditor::moveSelection(const Point2I& delta, bool callback)
  406. {
  407. for (ShaderNode* node : mSelectedNodes)
  408. {
  409. node->setPosition(node->getPosition() + delta);
  410. }
  411. }
  412. void GuiShaderEditor::clearSelection()
  413. {
  414. mSelectedNodes.clear();
  415. }
  416. void GuiShaderEditor::cloneSelection()
  417. {
  418. Vector<ShaderNode*> newSelection;
  419. for (ShaderNode* node : mSelectedNodes)
  420. {
  421. ShaderNode* clone = dynamic_cast<ShaderNode*>(node->deepClone());
  422. if (clone)
  423. newSelection.push_back(clone);
  424. }
  425. clearSelection();
  426. for (ShaderNode* cloneNode : newSelection)
  427. {
  428. addSelection(cloneNode);
  429. }
  430. }
  431. void GuiShaderEditor::addSelectionAtPoint(const Point2I& pos)
  432. {
  433. // turn hit off on already selected nodes.
  434. canHitSelectedNodes(false);
  435. ShaderNode* node = findHitNode(pos);
  436. // reset hit status.
  437. canHitSelectedNodes();
  438. if (node)
  439. addSelection(node);
  440. }
  441. void GuiShaderEditor::addSelection(ShaderNode* inNode)
  442. {
  443. if (inNode != NULL && !selectionContains(inNode))
  444. {
  445. mSelectedNodes.push_back(inNode);
  446. }
  447. }
  448. bool GuiShaderEditor::selectionContains(ShaderNode* inNode)
  449. {
  450. for (ShaderNode* node : mSelectedNodes)
  451. {
  452. if (node == inNode)
  453. return true;
  454. }
  455. return false;
  456. }
  457. void GuiShaderEditor::removeSelection(ShaderNode* inNode)
  458. {
  459. if (selectionContains(inNode))
  460. {
  461. Vector< ShaderNode* >::iterator i = T3D::find(mSelectedNodes.begin(), mSelectedNodes.end(), inNode);
  462. if (i != mSelectedNodes.end())
  463. mSelectedNodes.erase(i);
  464. }
  465. }
  466. void GuiShaderEditor::canHitSelectedNodes(bool state)
  467. {
  468. for (ShaderNode* node : mSelectedNodes)
  469. node->setCanHit(state);
  470. }
  471. //-----------------------------------------------------------------------------
  472. // Input handling
  473. //-----------------------------------------------------------------------------
  474. ShaderNode* GuiShaderEditor::findHitNode(const Point2I& pt)
  475. {
  476. for (ShaderNode* node : mCurrNodes)
  477. {
  478. if (node->pointInControl(pt))
  479. {
  480. return node;
  481. }
  482. }
  483. return nullptr;
  484. }
  485. void GuiShaderEditor::findNodesInRect(const RectI& rect, Vector<ShaderNode*>& outResult)
  486. {
  487. canHitSelectedNodes(false);
  488. for (ShaderNode* node : mCurrNodes)
  489. {
  490. if (node->getBounds().overlaps(rect))
  491. {
  492. outResult.push_back(node);
  493. }
  494. }
  495. canHitSelectedNodes();
  496. }
  497. void GuiShaderEditor::getDragRect(RectI& box)
  498. {
  499. box.point.x = getMin(mLastMousePos.x, mSelectionAnchor.x);
  500. box.extent.x = getMax(mLastMousePos.x, mSelectionAnchor.x) - box.point.x + 1;
  501. box.point.y = getMin(mLastMousePos.y, mSelectionAnchor.y);
  502. box.extent.y = getMax(mLastMousePos.y, mSelectionAnchor.y) - box.point.y + 1;
  503. }
  504. void GuiShaderEditor::startDragMove(const Point2I& startPoint)
  505. {
  506. mDragMoveUndo = true;
  507. mDragBeginPoint = startPoint;
  508. mDragBeginPoints.reserve(mSelectedNodes.size());
  509. for (ShaderNode* node : mSelectedNodes)
  510. {
  511. mDragBeginPoints.push_back(node->getPosition());
  512. }
  513. setMouseMode(MovingSelection);
  514. }
  515. void GuiShaderEditor::startDragRectangle(const Point2I& startPoint)
  516. {
  517. mSelectionAnchor = startPoint;
  518. setMouseMode(DragSelecting);
  519. }
  520. void GuiShaderEditor::startDragClone(const Point2I& startPoint)
  521. {
  522. mDragBeginPoint = startPoint;
  523. setMouseMode(DragClone);
  524. }
  525. void GuiShaderEditor::setMouseMode(mouseModes mode)
  526. {
  527. if (mMouseDownMode != mode)
  528. {
  529. mMouseDownMode = mode;
  530. }
  531. }