guiShaderEditor.cpp 14 KB

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