Gizmo3D.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577
  1. // Portions Copyright (c) 2008-2015 the Urho3D project.
  2. //
  3. // Copyright (c) 2014-2016 THUNDERBEAST GAMES LLC
  4. //
  5. // Permission is hereby granted, free of charge, to any person obtaining a copy
  6. // of this software and associated documentation files (the "Software"), to deal
  7. // in the Software without restriction, including without limitation the rights
  8. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9. // copies of the Software, and to permit persons to whom the Software is
  10. // furnished to do so, subject to the following conditions:
  11. //
  12. // The above copyright notice and this permission notice shall be included in
  13. // all copies or substantial portions of the Software.
  14. //
  15. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  21. // THE SOFTWARE.
  22. //
  23. #include <Atomic/Graphics/Model.h>
  24. #include <Atomic/Graphics/Material.h>
  25. #include <Atomic/Graphics/Octree.h>
  26. #include <Atomic/Resource/ResourceCache.h>
  27. #include <Atomic/Input/Input.h>
  28. #include "SceneEditor3DEvents.h"
  29. #include "SceneSelection.h"
  30. #include "SceneEditor3D.h"
  31. #include "Gizmo3D.h"
  32. namespace AtomicEditor
  33. {
  34. Gizmo3D::Gizmo3D(Context* context) : Object(context),
  35. dragging_(false),
  36. cloning_(false),
  37. startClone_(false)
  38. {
  39. ResourceCache* cache = GetSubsystem<ResourceCache>();
  40. gizmoNode_ = new Node(context_);
  41. gizmo_ = gizmoNode_->CreateComponent<StaticModel>();
  42. gizmo_->SetModel(cache->GetResource<Model>("AtomicEditor/Models/Axes.mdl"));
  43. gizmo_->SetEnabled(false);
  44. gizmo_->SetViewMask(0x80000000); // Editor raycasts use viewmask 0x7fffffff
  45. gizmo_->SetOccludee(false);
  46. axisMode_ = AXIS_LOCAL;
  47. gizmoAxisX_.lastSelected_ = false;
  48. gizmoAxisY_.lastSelected_ = false;
  49. gizmoAxisZ_.lastSelected_ = false;
  50. editMode_ = EDIT_MOVE;
  51. lastEditMode_ = EDIT_SELECT;
  52. gizmo_->SetMaterial(0, cache->GetResource<Material>("AtomicEditor/Materials/RedUnlit.xml"));
  53. gizmo_->SetMaterial(1, cache->GetResource<Material>("AtomicEditor/Materials/GreenUnlit.xml"));
  54. gizmo_->SetMaterial(2, cache->GetResource<Material>("AtomicEditor/Materials/BlueUnlit.xml"));
  55. }
  56. Gizmo3D::~Gizmo3D()
  57. {
  58. }
  59. void Gizmo3D::SetView(SceneView3D* view3D)
  60. {
  61. view3D_ = view3D;
  62. scene_ = view3D->GetScene();
  63. camera_ = view3D->GetCameraNode()->GetComponent<Camera>();
  64. selection_ = view3D_->GetSceneEditor3D()->GetSelection();
  65. assert(camera_.NotNull());
  66. }
  67. void Gizmo3D::Position()
  68. {
  69. Vector3 center(0, 0, 0);
  70. bool containsScene = false;
  71. Vector<SharedPtr<Node>>& editNodes = selection_->GetNodes();
  72. for (unsigned i = 0; i < editNodes.Size(); ++i)
  73. {
  74. // Scene's transform should not be edited, so hide gizmo if it is included
  75. if (editNodes[i] == scene_)
  76. {
  77. containsScene = true;
  78. break;
  79. }
  80. center += editNodes[i]->GetWorldPosition();
  81. }
  82. if (editNodes.Empty() || containsScene)
  83. {
  84. Hide();
  85. return;
  86. }
  87. center /= editNodes.Size();
  88. gizmoNode_->SetPosition(center);
  89. if (axisMode_ == AXIS_WORLD || editNodes.Size() > 1)
  90. gizmoNode_->SetRotation(Quaternion());
  91. else
  92. gizmoNode_->SetRotation(editNodes[0]->GetWorldRotation());
  93. ResourceCache* cache = GetSubsystem<ResourceCache>();
  94. if (editMode_ != lastEditMode_)
  95. {
  96. switch (editMode_)
  97. {
  98. case EDIT_MOVE:
  99. gizmo_->SetModel(cache->GetResource<Model>("AtomicEditor/Models/Axes.mdl"));
  100. break;
  101. case EDIT_ROTATE:
  102. gizmo_->SetModel(cache->GetResource<Model>("AtomicEditor/Models/RotateAxes.mdl"));
  103. break;
  104. case EDIT_SCALE:
  105. gizmo_->SetModel(cache->GetResource<Model>("AtomicEditor/Models/ScaleAxes.mdl"));
  106. break;
  107. default:
  108. break;
  109. }
  110. lastEditMode_ = editMode_;
  111. }
  112. bool orbiting = false;
  113. if ((editMode_ != EDIT_SELECT && !orbiting) && !gizmo_->IsEnabled())
  114. Show();
  115. else if ((editMode_ == EDIT_SELECT || orbiting) && gizmo_->IsEnabled())
  116. Hide();
  117. if (gizmo_->IsEnabled())
  118. {
  119. float scale = 0.1f / camera_->GetZoom();
  120. if (camera_->IsOrthographic())
  121. scale *= camera_->GetOrthoSize();
  122. else
  123. scale *= (camera_->GetView() * gizmoNode_->GetPosition()).z_;
  124. gizmoNode_->SetScale(Vector3(scale, scale, scale));
  125. }
  126. }
  127. void Gizmo3D::Update()
  128. {
  129. Use();
  130. Position();
  131. }
  132. void Gizmo3D::CalculateGizmoAxes()
  133. {
  134. gizmoAxisX_.axisRay_ = Ray(gizmoNode_->GetPosition(), gizmoNode_->GetRotation() * Vector3(1, 0, 0));
  135. gizmoAxisY_.axisRay_ = Ray(gizmoNode_->GetPosition(), gizmoNode_->GetRotation() * Vector3(0, 1, 0));
  136. gizmoAxisZ_.axisRay_ = Ray(gizmoNode_->GetPosition(), gizmoNode_->GetRotation() * Vector3(0, 0, 1));
  137. }
  138. void Gizmo3D::Use()
  139. {
  140. if (gizmo_.Null() || !gizmo_->IsEnabled() || editMode_ == EDIT_SELECT)
  141. {
  142. return;
  143. }
  144. ResourceCache* cache = GetSubsystem<ResourceCache>();
  145. Input* input = GetSubsystem<Input>();
  146. Vector<SharedPtr<Node>>& editNodes = selection_->GetNodes();
  147. Ray cameraRay = view3D_->GetCameraRay();
  148. float scale = gizmoNode_->GetScale().x_;
  149. // Clones an object when it's dragged/rotated/scaled while holding down Shift
  150. if (startClone_ && !cloning_)
  151. {
  152. cloning_ = true;
  153. selection_->Copy();
  154. selection_->Paste();
  155. }
  156. // Recalculate axes only when not left-dragging
  157. bool drag = input->GetMouseButtonDown(MOUSEB_LEFT);// && (Abs(input->GetMouseMoveX()) > 3 || Abs(input->GetMouseMoveY()) > 3);
  158. if (!drag)
  159. {
  160. if (dragging_)
  161. {
  162. scene_->SendEvent(E_SCENEEDITEND);
  163. dragging_ = false;
  164. }
  165. startClone_ = false;
  166. cloning_ = false;
  167. CalculateGizmoAxes();
  168. }
  169. gizmoAxisX_.Update(cameraRay, scale, drag, camera_->GetNode());
  170. gizmoAxisY_.Update(cameraRay, scale, drag, camera_->GetNode());
  171. gizmoAxisZ_.Update(cameraRay, scale, drag, camera_->GetNode());
  172. if (!editNodes.Size() || editNodes[0] == scene_)
  173. {
  174. gizmoAxisX_.selected_ = gizmoAxisY_.selected_ = gizmoAxisZ_.selected_ = false;
  175. // this just forces an update
  176. gizmoAxisX_.lastSelected_ = gizmoAxisY_.lastSelected_ = gizmoAxisZ_.lastSelected_ = true;
  177. }
  178. if (gizmoAxisX_.selected_ != gizmoAxisX_.lastSelected_)
  179. {
  180. gizmo_->SetMaterial(0, cache->GetResource<Material>(
  181. gizmoAxisX_.selected_ ?
  182. "AtomicEditor/Materials/BrightRedUnlit.xml" : "AtomicEditor/Materials/RedUnlit.xml"));
  183. gizmoAxisX_.lastSelected_ = gizmoAxisX_.selected_;
  184. }
  185. if (gizmoAxisY_.selected_ != gizmoAxisY_.lastSelected_)
  186. {
  187. gizmo_->SetMaterial(1, cache->GetResource<Material>(
  188. gizmoAxisY_.selected_ ?
  189. "AtomicEditor/Materials/BrightGreenUnlit.xml" : "AtomicEditor/Materials/GreenUnlit.xml"));
  190. gizmoAxisY_.lastSelected_ = gizmoAxisY_.selected_;
  191. }
  192. if (gizmoAxisZ_.selected_ != gizmoAxisZ_.lastSelected_)
  193. {
  194. gizmo_->SetMaterial(2, cache->GetResource<Material>(
  195. gizmoAxisZ_.selected_ ?
  196. "AtomicEditor/Materials/BrightBlueUnlit.xml" : "AtomicEditor/Materials/BlueUnlit.xml"));
  197. gizmoAxisZ_.lastSelected_ = gizmoAxisZ_.selected_;
  198. }
  199. if (drag && Selected())
  200. Drag();
  201. }
  202. bool Gizmo3D::MoveEditNodes(Vector3 adjust)
  203. {
  204. bool moved = false;
  205. Input* input = GetSubsystem<Input>();
  206. #ifdef ATOMIC_PLATFORM_OSX
  207. bool moveSnap = input->GetKeyDown(KEY_LGUI) || input->GetKeyDown(KEY_RGUI);
  208. #else
  209. bool moveSnap = input->GetKeyDown(KEY_LCTRL) || input->GetKeyDown(KEY_RCTRL);
  210. #endif
  211. Vector<SharedPtr<Node>>& editNodes = selection_->GetNodes();
  212. if (adjust.Length() > M_EPSILON)
  213. {
  214. for (unsigned i = 0; i < editNodes.Size(); ++i)
  215. {
  216. if (moveSnap)
  217. {
  218. float moveStepScaled = snapTranslationX_;
  219. adjust.x_ = floorf(adjust.x_ / moveStepScaled + 0.5) * moveStepScaled;
  220. moveStepScaled = snapTranslationY_;
  221. adjust.y_ = floorf(adjust.y_ / moveStepScaled + 0.5) * moveStepScaled;
  222. moveStepScaled = snapTranslationZ_;
  223. adjust.z_ = floorf(adjust.z_ / moveStepScaled + 0.5) * moveStepScaled;
  224. }
  225. Node* node = editNodes[i];
  226. Vector3 nodeAdjust = adjust;
  227. if (axisMode_ == AXIS_LOCAL && editNodes.Size() == 1)
  228. nodeAdjust = node->GetWorldRotation() * nodeAdjust;
  229. Vector3 worldPos = node->GetWorldPosition();
  230. Vector3 oldPos = node->GetPosition();
  231. worldPos += nodeAdjust;
  232. if (!node->GetParent())
  233. node->SetPosition(worldPos);
  234. else
  235. node->SetPosition(node->GetParent()->WorldToLocal(worldPos));
  236. if (node->GetPosition() != oldPos)
  237. moved = true;
  238. }
  239. }
  240. return moved;
  241. }
  242. bool Gizmo3D::RotateEditNodes(Vector3 adjust)
  243. {
  244. bool moved = false;
  245. Input* input = GetSubsystem<Input>();
  246. #ifdef ATOMIC_PLATFORM_OSX
  247. bool rotateSnap = input->GetKeyDown(KEY_LGUI) || input->GetKeyDown(KEY_RGUI);
  248. #else
  249. bool rotateSnap = input->GetKeyDown(KEY_LCTRL) || input->GetKeyDown(KEY_RCTRL);
  250. #endif
  251. Vector<SharedPtr<Node>>& editNodes = selection_->GetNodes();
  252. if (rotateSnap)
  253. {
  254. float rotateStepScaled = snapRotation_;
  255. adjust.x_ = floorf(adjust.x_ / rotateStepScaled + 0.5) * rotateStepScaled;
  256. adjust.y_ = floorf(adjust.y_ / rotateStepScaled + 0.5) * rotateStepScaled;
  257. adjust.z_ = floorf(adjust.z_ / rotateStepScaled + 0.5) * rotateStepScaled;
  258. }
  259. if (adjust.Length() > M_EPSILON)
  260. {
  261. moved = true;
  262. for (unsigned i = 0; i < editNodes.Size(); ++i)
  263. {
  264. Node* node = editNodes[i];
  265. Quaternion rotQuat(adjust.x_, adjust.y_, adjust.z_);
  266. if (axisMode_ == AXIS_LOCAL && editNodes.Size() == 1)
  267. node->SetRotation(node->GetRotation() * rotQuat);
  268. else
  269. {
  270. Vector3 offset = node->GetWorldPosition() - gizmoAxisX_.axisRay_.origin_;
  271. if (node->GetParent() && node->GetParent()->GetWorldRotation() != Quaternion(1, 0, 0, 0))
  272. rotQuat = node->GetParent()->GetWorldRotation().Inverse() * rotQuat * node->GetParent()->GetWorldRotation();
  273. node->SetRotation(rotQuat * node->GetRotation());
  274. Vector3 newPosition = gizmoAxisX_.axisRay_.origin_ + rotQuat * offset;
  275. if (node->GetParent())
  276. newPosition = node->GetParent()->WorldToLocal(newPosition);
  277. node->SetPosition(newPosition);
  278. }
  279. }
  280. }
  281. return moved;
  282. }
  283. bool Gizmo3D::ScaleEditNodes(Vector3 adjust)
  284. {
  285. bool moved = false;
  286. Input* input = GetSubsystem<Input>();
  287. #ifdef ATOMIC_PLATFORM_OSX
  288. bool scaleSnap = input->GetKeyDown(KEY_LGUI) || input->GetKeyDown(KEY_RGUI);
  289. #else
  290. bool scaleSnap = input->GetKeyDown(KEY_LCTRL) || input->GetKeyDown(KEY_RCTRL);
  291. #endif
  292. Vector<SharedPtr<Node>>& editNodes = selection_->GetNodes();
  293. if (adjust.Length() > M_EPSILON)
  294. {
  295. for (unsigned i = 0; i < editNodes.Size(); ++i)
  296. {
  297. Node* node = editNodes[i];
  298. Vector3 scale = node->GetScale();
  299. Vector3 oldScale = scale;
  300. if (!scaleSnap)
  301. scale += adjust;
  302. else
  303. {
  304. float scaleStepScaled = snapScale_;
  305. if (adjust.x_ != 0)
  306. {
  307. scale.x_ += adjust.x_ * scaleStepScaled;
  308. scale.x_ = floorf(scale.x_ / scaleStepScaled + 0.5) * scaleStepScaled;
  309. }
  310. if (adjust.y_ != 0)
  311. {
  312. scale.y_ += adjust.y_ * scaleStepScaled;
  313. scale.y_ = floorf(scale.y_ / scaleStepScaled + 0.5) * scaleStepScaled;
  314. }
  315. if (adjust.z_ != 0)
  316. {
  317. scale.z_ += adjust.z_ * scaleStepScaled;
  318. scale.z_ = floorf(scale.z_ / scaleStepScaled + 0.5) * scaleStepScaled;
  319. }
  320. }
  321. if (scale != oldScale)
  322. moved = true;
  323. node->SetScale(scale);
  324. }
  325. }
  326. return moved;
  327. }
  328. void Gizmo3D::Moved()
  329. {
  330. gizmoAxisX_.Moved();
  331. gizmoAxisY_.Moved();
  332. gizmoAxisZ_.Moved();
  333. SendEvent(E_GIZMOMOVED);
  334. }
  335. void Gizmo3D::Drag()
  336. {
  337. bool moved = false;
  338. dragging_ = true;
  339. float scale = gizmoNode_->GetScale().x_;
  340. Input* input = GetSubsystem<Input>();
  341. //Activates cloning when any of these modes is used while holding shift
  342. if (editMode_ == EDIT_MOVE ||
  343. editMode_ == EDIT_ROTATE ||
  344. editMode_ == EDIT_SCALE)
  345. {
  346. startClone_ = input->GetKeyDown(KEY_SHIFT);
  347. }
  348. if (editMode_ == EDIT_MOVE)
  349. {
  350. Vector3 adjust(0, 0, 0);
  351. if (gizmoAxisX_.selected_)
  352. adjust += Vector3(1, 0, 0) * (gizmoAxisX_.t_ - gizmoAxisX_.lastT_);
  353. if (gizmoAxisY_.selected_)
  354. adjust += Vector3(0, 1, 0) * (gizmoAxisY_.t_ - gizmoAxisY_.lastT_);
  355. if (gizmoAxisZ_.selected_)
  356. adjust += Vector3(0, 0, 1) * (gizmoAxisZ_.t_ - gizmoAxisZ_.lastT_);
  357. moved = MoveEditNodes(adjust);
  358. }
  359. else if (editMode_ == EDIT_ROTATE)
  360. {
  361. const float rotSensitivity = 50.0;
  362. Vector3 adjust(0, 0, 0);
  363. if (gizmoAxisX_.selected_)
  364. adjust.x_ = (gizmoAxisX_.d_ - gizmoAxisX_.lastD_) * rotSensitivity / scale;
  365. if (gizmoAxisY_.selected_)
  366. adjust.y_ = -(gizmoAxisY_.d_ - gizmoAxisY_.lastD_) * rotSensitivity / scale;
  367. if (gizmoAxisZ_.selected_)
  368. adjust.z_ = (gizmoAxisZ_.d_ - gizmoAxisZ_.lastD_) * rotSensitivity / scale;
  369. moved = RotateEditNodes(adjust);
  370. }
  371. else if (editMode_ == EDIT_SCALE)
  372. {
  373. Vector3 adjust(0, 0, 0);
  374. if (gizmoAxisX_.selected_)
  375. adjust += Vector3(1, 0, 0) * (gizmoAxisX_.t_ - gizmoAxisX_.lastT_);
  376. if (gizmoAxisY_.selected_)
  377. adjust += Vector3(0, 1, 0) * (gizmoAxisY_.t_ - gizmoAxisY_.lastT_);
  378. if (gizmoAxisZ_.selected_)
  379. adjust += Vector3(0, 0, 1) * (gizmoAxisZ_.t_ - gizmoAxisZ_.lastT_);
  380. // Special handling for uniform scale: use the unmodified X-axis movement only
  381. if (editMode_ == EDIT_SCALE && gizmoAxisX_.selected_ && gizmoAxisY_.selected_ && gizmoAxisZ_.selected_)
  382. {
  383. float x = gizmoAxisX_.t_ - gizmoAxisX_.lastT_;
  384. adjust = Vector3(x, x, x);
  385. }
  386. moved = ScaleEditNodes(adjust);
  387. }
  388. if (moved)
  389. {
  390. Moved();
  391. //UpdateNodeAttributes();
  392. //needGizmoUndo = true;
  393. }
  394. }
  395. void Gizmo3D::SetAxisMode(AxisMode mode)
  396. {
  397. axisMode_ = mode;
  398. }
  399. void Gizmo3D::SetEditMode(EditMode mode)
  400. {
  401. editMode_ = mode;
  402. }
  403. void Gizmo3D::Hide()
  404. {
  405. gizmoAxisX_.selected_ = gizmoAxisY_.selected_ = gizmoAxisZ_.selected_ = false;
  406. gizmo_->SetEnabled(false);
  407. }
  408. void Gizmo3D::Show()
  409. {
  410. if (scene_.Null())
  411. return;
  412. gizmo_->SetEnabled(true);
  413. Octree* octree = scene_->GetComponent<Octree>();
  414. if (!octree)
  415. return;
  416. octree->AddManualDrawable(gizmo_);
  417. }
  418. float Gizmo3D::GetSnapTranslationX() const
  419. {
  420. return snapTranslationX_;
  421. }
  422. float Gizmo3D::GetSnapTranslationY() const
  423. {
  424. return snapTranslationY_;
  425. }
  426. float Gizmo3D::GetSnapTranslationZ() const
  427. {
  428. return snapTranslationZ_;
  429. }
  430. float Gizmo3D::GetSnapRotation() const
  431. {
  432. return snapRotation_;
  433. }
  434. float Gizmo3D::GetSnapScale() const
  435. {
  436. return snapScale_;
  437. }
  438. void Gizmo3D::SetSnapTranslationX(float value)
  439. {
  440. snapTranslationX_ = value;
  441. }
  442. void Gizmo3D::SetSnapTranslationY(float value)
  443. {
  444. snapTranslationY_ = value;
  445. }
  446. void Gizmo3D::SetSnapTranslationZ(float value)
  447. {
  448. snapTranslationZ_ = value;
  449. }
  450. void Gizmo3D::SetSnapRotation(float value)
  451. {
  452. snapRotation_ = value;
  453. }
  454. void Gizmo3D::SetSnapScale(float value)
  455. {
  456. snapScale_ = value;
  457. }
  458. }