guiShaderEditor.cpp 15 KB

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