Gizmo3D.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568
  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/Atomic3D/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. {
  38. ResourceCache* cache = GetSubsystem<ResourceCache>();
  39. gizmoNode_ = new Node(context_);
  40. gizmo_ = gizmoNode_->CreateComponent<StaticModel>();
  41. gizmo_->SetModel(cache->GetResource<Model>("AtomicEditor/Models/Axes.mdl"));
  42. gizmo_->SetEnabled(false);
  43. gizmo_->SetViewMask(0x80000000); // Editor raycasts use viewmask 0x7fffffff
  44. gizmo_->SetOccludee(false);
  45. axisMode_ = AXIS_LOCAL;
  46. gizmoAxisX_.lastSelected_ = false;
  47. gizmoAxisY_.lastSelected_ = false;
  48. gizmoAxisZ_.lastSelected_ = false;
  49. editMode_ = EDIT_MOVE;
  50. lastEditMode_ = EDIT_SELECT;
  51. gizmo_->SetMaterial(0, cache->GetResource<Material>("AtomicEditor/Materials/RedUnlit.xml"));
  52. gizmo_->SetMaterial(1, cache->GetResource<Material>("AtomicEditor/Materials/GreenUnlit.xml"));
  53. gizmo_->SetMaterial(2, cache->GetResource<Material>("AtomicEditor/Materials/BlueUnlit.xml"));
  54. }
  55. Gizmo3D::~Gizmo3D()
  56. {
  57. }
  58. void Gizmo3D::SetView(SceneView3D* view3D)
  59. {
  60. view3D_ = view3D;
  61. scene_ = view3D->GetScene();
  62. camera_ = view3D->GetCameraNode()->GetComponent<Camera>();
  63. selection_ = view3D_->GetSceneEditor3D()->GetSelection();
  64. assert(camera_.NotNull());
  65. }
  66. void Gizmo3D::Position()
  67. {
  68. Vector3 center(0, 0, 0);
  69. bool containsScene = false;
  70. Vector<SharedPtr<Node>>& editNodes = selection_->GetNodes();
  71. for (unsigned i = 0; i < editNodes.Size(); ++i)
  72. {
  73. // Scene's transform should not be edited, so hide gizmo if it is included
  74. if (editNodes[i] == scene_)
  75. {
  76. containsScene = true;
  77. break;
  78. }
  79. center += editNodes[i]->GetWorldPosition();
  80. }
  81. if (editNodes.Empty() || containsScene)
  82. {
  83. Hide();
  84. return;
  85. }
  86. center /= editNodes.Size();
  87. gizmoNode_->SetPosition(center);
  88. if (axisMode_ == AXIS_WORLD || editNodes.Size() > 1)
  89. gizmoNode_->SetRotation(Quaternion());
  90. else
  91. gizmoNode_->SetRotation(editNodes[0]->GetWorldRotation());
  92. ResourceCache* cache = GetSubsystem<ResourceCache>();
  93. if (editMode_ != lastEditMode_)
  94. {
  95. switch (editMode_)
  96. {
  97. case EDIT_MOVE:
  98. gizmo_->SetModel(cache->GetResource<Model>("AtomicEditor/Models/Axes.mdl"));
  99. break;
  100. case EDIT_ROTATE:
  101. gizmo_->SetModel(cache->GetResource<Model>("AtomicEditor/Models/RotateAxes.mdl"));
  102. break;
  103. case EDIT_SCALE:
  104. gizmo_->SetModel(cache->GetResource<Model>("AtomicEditor/Models/ScaleAxes.mdl"));
  105. break;
  106. default:
  107. break;
  108. }
  109. lastEditMode_ = editMode_;
  110. }
  111. bool orbiting = false;
  112. if ((editMode_ != EDIT_SELECT && !orbiting) && !gizmo_->IsEnabled())
  113. Show();
  114. else if ((editMode_ == EDIT_SELECT || orbiting) && gizmo_->IsEnabled())
  115. Hide();
  116. if (gizmo_->IsEnabled())
  117. {
  118. float scale = 0.1f / camera_->GetZoom();
  119. if (camera_->IsOrthographic())
  120. scale *= camera_->GetOrthoSize();
  121. else
  122. scale *= (camera_->GetView() * gizmoNode_->GetPosition()).z_;
  123. gizmoNode_->SetScale(Vector3(scale, scale, scale));
  124. }
  125. }
  126. void Gizmo3D::Update()
  127. {
  128. Use();
  129. Position();
  130. }
  131. void Gizmo3D::CalculateGizmoAxes()
  132. {
  133. gizmoAxisX_.axisRay_ = Ray(gizmoNode_->GetPosition(), gizmoNode_->GetRotation() * Vector3(1, 0, 0));
  134. gizmoAxisY_.axisRay_ = Ray(gizmoNode_->GetPosition(), gizmoNode_->GetRotation() * Vector3(0, 1, 0));
  135. gizmoAxisZ_.axisRay_ = Ray(gizmoNode_->GetPosition(), gizmoNode_->GetRotation() * Vector3(0, 0, 1));
  136. }
  137. void Gizmo3D::Use()
  138. {
  139. if (gizmo_.Null() || !gizmo_->IsEnabled() || editMode_ == EDIT_SELECT)
  140. {
  141. return;
  142. }
  143. ResourceCache* cache = GetSubsystem<ResourceCache>();
  144. Input* input = GetSubsystem<Input>();
  145. Vector<SharedPtr<Node>>& editNodes = selection_->GetNodes();
  146. Ray cameraRay = view3D_->GetCameraRay();
  147. float scale = gizmoNode_->GetScale().x_;
  148. // Clones an object when it's dragged while holding down Shift
  149. bool dragShift = input->GetMouseButtonDown(MOUSEB_LEFT) && input->GetKeyDown(KEY_SHIFT);
  150. if (dragShift && !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. cloning_ = false;
  166. CalculateGizmoAxes();
  167. }
  168. gizmoAxisX_.Update(cameraRay, scale, drag, camera_->GetNode());
  169. gizmoAxisY_.Update(cameraRay, scale, drag, camera_->GetNode());
  170. gizmoAxisZ_.Update(cameraRay, scale, drag, camera_->GetNode());
  171. if (!editNodes.Size() || editNodes[0] == scene_)
  172. {
  173. gizmoAxisX_.selected_ = gizmoAxisY_.selected_ = gizmoAxisZ_.selected_ = false;
  174. // this just forces an update
  175. gizmoAxisX_.lastSelected_ = gizmoAxisY_.lastSelected_ = gizmoAxisZ_.lastSelected_ = true;
  176. }
  177. if (gizmoAxisX_.selected_ != gizmoAxisX_.lastSelected_)
  178. {
  179. gizmo_->SetMaterial(0, cache->GetResource<Material>(
  180. gizmoAxisX_.selected_ ?
  181. "AtomicEditor/Materials/BrightRedUnlit.xml" : "AtomicEditor/Materials/RedUnlit.xml"));
  182. gizmoAxisX_.lastSelected_ = gizmoAxisX_.selected_;
  183. }
  184. if (gizmoAxisY_.selected_ != gizmoAxisY_.lastSelected_)
  185. {
  186. gizmo_->SetMaterial(1, cache->GetResource<Material>(
  187. gizmoAxisY_.selected_ ?
  188. "AtomicEditor/Materials/BrightGreenUnlit.xml" : "AtomicEditor/Materials/GreenUnlit.xml"));
  189. gizmoAxisY_.lastSelected_ = gizmoAxisY_.selected_;
  190. }
  191. if (gizmoAxisZ_.selected_ != gizmoAxisZ_.lastSelected_)
  192. {
  193. gizmo_->SetMaterial(2, cache->GetResource<Material>(
  194. gizmoAxisZ_.selected_ ?
  195. "AtomicEditor/Materials/BrightBlueUnlit.xml" : "AtomicEditor/Materials/BlueUnlit.xml"));
  196. gizmoAxisZ_.lastSelected_ = gizmoAxisZ_.selected_;
  197. }
  198. if (drag && Selected())
  199. Drag();
  200. }
  201. bool Gizmo3D::MoveEditNodes(Vector3 adjust)
  202. {
  203. bool moved = false;
  204. Input* input = GetSubsystem<Input>();
  205. #ifdef ATOMIC_PLATFORM_OSX
  206. bool moveSnap = input->GetKeyDown(KEY_LGUI) || input->GetKeyDown(KEY_RGUI);
  207. #else
  208. bool moveSnap = input->GetKeyDown(KEY_LCTRL) || input->GetKeyDown(KEY_RCTRL);
  209. #endif
  210. Vector<SharedPtr<Node>>& editNodes = selection_->GetNodes();
  211. if (adjust.Length() > M_EPSILON)
  212. {
  213. for (unsigned i = 0; i < editNodes.Size(); ++i)
  214. {
  215. if (moveSnap)
  216. {
  217. float moveStepScaled = snapTranslationX_;
  218. adjust.x_ = floorf(adjust.x_ / moveStepScaled + 0.5) * moveStepScaled;
  219. moveStepScaled = snapTranslationY_;
  220. adjust.y_ = floorf(adjust.y_ / moveStepScaled + 0.5) * moveStepScaled;
  221. moveStepScaled = snapTranslationZ_;
  222. adjust.z_ = floorf(adjust.z_ / moveStepScaled + 0.5) * moveStepScaled;
  223. }
  224. Node* node = editNodes[i];
  225. Vector3 nodeAdjust = adjust;
  226. if (axisMode_ == AXIS_LOCAL && editNodes.Size() == 1)
  227. nodeAdjust = node->GetWorldRotation() * nodeAdjust;
  228. Vector3 worldPos = node->GetWorldPosition();
  229. Vector3 oldPos = node->GetPosition();
  230. worldPos += nodeAdjust;
  231. if (!node->GetParent())
  232. node->SetPosition(worldPos);
  233. else
  234. node->SetPosition(node->GetParent()->WorldToLocal(worldPos));
  235. if (node->GetPosition() != oldPos)
  236. moved = true;
  237. }
  238. }
  239. return moved;
  240. }
  241. bool Gizmo3D::RotateEditNodes(Vector3 adjust)
  242. {
  243. bool moved = false;
  244. Input* input = GetSubsystem<Input>();
  245. #ifdef ATOMIC_PLATFORM_OSX
  246. bool rotateSnap = input->GetKeyDown(KEY_LGUI) || input->GetKeyDown(KEY_RGUI);
  247. #else
  248. bool rotateSnap = input->GetKeyDown(KEY_LCTRL) || input->GetKeyDown(KEY_RCTRL);
  249. #endif
  250. Vector<SharedPtr<Node>>& editNodes = selection_->GetNodes();
  251. if (rotateSnap)
  252. {
  253. float rotateStepScaled = snapRotation_;
  254. adjust.x_ = floorf(adjust.x_ / rotateStepScaled + 0.5) * rotateStepScaled;
  255. adjust.y_ = floorf(adjust.y_ / rotateStepScaled + 0.5) * rotateStepScaled;
  256. adjust.z_ = floorf(adjust.z_ / rotateStepScaled + 0.5) * rotateStepScaled;
  257. }
  258. if (adjust.Length() > M_EPSILON)
  259. {
  260. moved = true;
  261. for (unsigned i = 0; i < editNodes.Size(); ++i)
  262. {
  263. Node* node = editNodes[i];
  264. Quaternion rotQuat(adjust.x_, adjust.y_, adjust.z_);
  265. if (axisMode_ == AXIS_LOCAL && editNodes.Size() == 1)
  266. node->SetRotation(node->GetRotation() * rotQuat);
  267. else
  268. {
  269. Vector3 offset = node->GetWorldPosition() - gizmoAxisX_.axisRay_.origin_;
  270. if (node->GetParent() && node->GetParent()->GetWorldRotation() != Quaternion(1, 0, 0, 0))
  271. rotQuat = node->GetParent()->GetWorldRotation().Inverse() * rotQuat * node->GetParent()->GetWorldRotation();
  272. node->SetRotation(rotQuat * node->GetRotation());
  273. Vector3 newPosition = gizmoAxisX_.axisRay_.origin_ + rotQuat * offset;
  274. if (node->GetParent())
  275. newPosition = node->GetParent()->WorldToLocal(newPosition);
  276. node->SetPosition(newPosition);
  277. }
  278. }
  279. }
  280. return moved;
  281. }
  282. bool Gizmo3D::ScaleEditNodes(Vector3 adjust)
  283. {
  284. bool moved = false;
  285. Input* input = GetSubsystem<Input>();
  286. #ifdef ATOMIC_PLATFORM_OSX
  287. bool scaleSnap = input->GetKeyDown(KEY_LGUI) || input->GetKeyDown(KEY_RGUI);
  288. #else
  289. bool scaleSnap = input->GetKeyDown(KEY_LCTRL) || input->GetKeyDown(KEY_RCTRL);
  290. #endif
  291. Vector<SharedPtr<Node>>& editNodes = selection_->GetNodes();
  292. if (adjust.Length() > M_EPSILON)
  293. {
  294. for (unsigned i = 0; i < editNodes.Size(); ++i)
  295. {
  296. Node* node = editNodes[i];
  297. Vector3 scale = node->GetScale();
  298. Vector3 oldScale = scale;
  299. if (!scaleSnap)
  300. scale += adjust;
  301. else
  302. {
  303. float scaleStepScaled = snapScale_;
  304. if (adjust.x_ != 0)
  305. {
  306. scale.x_ += adjust.x_ * scaleStepScaled;
  307. scale.x_ = floorf(scale.x_ / scaleStepScaled + 0.5) * scaleStepScaled;
  308. }
  309. if (adjust.y_ != 0)
  310. {
  311. scale.y_ += adjust.y_ * scaleStepScaled;
  312. scale.y_ = floorf(scale.y_ / scaleStepScaled + 0.5) * scaleStepScaled;
  313. }
  314. if (adjust.z_ != 0)
  315. {
  316. scale.z_ += adjust.z_ * scaleStepScaled;
  317. scale.z_ = floorf(scale.z_ / scaleStepScaled + 0.5) * scaleStepScaled;
  318. }
  319. }
  320. if (scale != oldScale)
  321. moved = true;
  322. node->SetScale(scale);
  323. }
  324. }
  325. return moved;
  326. }
  327. void Gizmo3D::Moved()
  328. {
  329. gizmoAxisX_.Moved();
  330. gizmoAxisY_.Moved();
  331. gizmoAxisZ_.Moved();
  332. SendEvent(E_GIZMOMOVED);
  333. }
  334. void Gizmo3D::Drag()
  335. {
  336. bool moved = false;
  337. dragging_ = true;
  338. float scale = gizmoNode_->GetScale().x_;
  339. if (editMode_ == EDIT_MOVE)
  340. {
  341. Vector3 adjust(0, 0, 0);
  342. if (gizmoAxisX_.selected_)
  343. adjust += Vector3(1, 0, 0) * (gizmoAxisX_.t_ - gizmoAxisX_.lastT_);
  344. if (gizmoAxisY_.selected_)
  345. adjust += Vector3(0, 1, 0) * (gizmoAxisY_.t_ - gizmoAxisY_.lastT_);
  346. if (gizmoAxisZ_.selected_)
  347. adjust += Vector3(0, 0, 1) * (gizmoAxisZ_.t_ - gizmoAxisZ_.lastT_);
  348. moved = MoveEditNodes(adjust);
  349. }
  350. else if (editMode_ == EDIT_ROTATE)
  351. {
  352. const float rotSensitivity = 50.0;
  353. Vector3 adjust(0, 0, 0);
  354. if (gizmoAxisX_.selected_)
  355. adjust.x_ = (gizmoAxisX_.d_ - gizmoAxisX_.lastD_) * rotSensitivity / scale;
  356. if (gizmoAxisY_.selected_)
  357. adjust.y_ = -(gizmoAxisY_.d_ - gizmoAxisY_.lastD_) * rotSensitivity / scale;
  358. if (gizmoAxisZ_.selected_)
  359. adjust.z_ = (gizmoAxisZ_.d_ - gizmoAxisZ_.lastD_) * rotSensitivity / scale;
  360. moved = RotateEditNodes(adjust);
  361. }
  362. else if (editMode_ == EDIT_SCALE)
  363. {
  364. Vector3 adjust(0, 0, 0);
  365. if (gizmoAxisX_.selected_)
  366. adjust += Vector3(1, 0, 0) * (gizmoAxisX_.t_ - gizmoAxisX_.lastT_);
  367. if (gizmoAxisY_.selected_)
  368. adjust += Vector3(0, 1, 0) * (gizmoAxisY_.t_ - gizmoAxisY_.lastT_);
  369. if (gizmoAxisZ_.selected_)
  370. adjust += Vector3(0, 0, 1) * (gizmoAxisZ_.t_ - gizmoAxisZ_.lastT_);
  371. // Special handling for uniform scale: use the unmodified X-axis movement only
  372. if (editMode_ == EDIT_SCALE && gizmoAxisX_.selected_ && gizmoAxisY_.selected_ && gizmoAxisZ_.selected_)
  373. {
  374. float x = gizmoAxisX_.t_ - gizmoAxisX_.lastT_;
  375. adjust = Vector3(x, x, x);
  376. }
  377. moved = ScaleEditNodes(adjust);
  378. }
  379. if (moved)
  380. {
  381. Moved();
  382. //UpdateNodeAttributes();
  383. //needGizmoUndo = true;
  384. }
  385. }
  386. void Gizmo3D::SetAxisMode(AxisMode mode)
  387. {
  388. axisMode_ = mode;
  389. }
  390. void Gizmo3D::SetEditMode(EditMode mode)
  391. {
  392. editMode_ = mode;
  393. }
  394. void Gizmo3D::Hide()
  395. {
  396. gizmoAxisX_.selected_ = gizmoAxisY_.selected_ = gizmoAxisZ_.selected_ = false;
  397. gizmo_->SetEnabled(false);
  398. }
  399. void Gizmo3D::Show()
  400. {
  401. if (scene_.Null())
  402. return;
  403. gizmo_->SetEnabled(true);
  404. Octree* octree = scene_->GetComponent<Octree>();
  405. if (!octree)
  406. return;
  407. octree->AddManualDrawable(gizmo_);
  408. }
  409. float Gizmo3D::GetSnapTranslationX() const
  410. {
  411. return snapTranslationX_;
  412. }
  413. float Gizmo3D::GetSnapTranslationY() const
  414. {
  415. return snapTranslationY_;
  416. }
  417. float Gizmo3D::GetSnapTranslationZ() const
  418. {
  419. return snapTranslationZ_;
  420. }
  421. float Gizmo3D::GetSnapRotation() const
  422. {
  423. return snapRotation_;
  424. }
  425. float Gizmo3D::GetSnapScale() const
  426. {
  427. return snapScale_;
  428. }
  429. void Gizmo3D::SetSnapTranslationX(float value)
  430. {
  431. snapTranslationX_ = value;
  432. }
  433. void Gizmo3D::SetSnapTranslationY(float value)
  434. {
  435. snapTranslationY_ = value;
  436. }
  437. void Gizmo3D::SetSnapTranslationZ(float value)
  438. {
  439. snapTranslationZ_ = value;
  440. }
  441. void Gizmo3D::SetSnapRotation(float value)
  442. {
  443. snapRotation_ = value;
  444. }
  445. void Gizmo3D::SetSnapScale(float value)
  446. {
  447. snapScale_ = value;
  448. }
  449. }