Gizmo3D.cpp 16 KB

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