| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471 |
- // Urho3D editor node transform gizmo handling
- Node@ gizmoNode;
- StaticModel@ gizmo;
- const float axisMaxD = 0.1;
- const float axisMaxT = 1.0;
- const float rotSensitivity = 50.0;
- EditMode lastGizmoMode;
- // For undo
- bool previousGizmoDrag;
- bool needGizmoUndo;
- Array<Transform> oldGizmoTransforms;
- class GizmoAxis
- {
- Ray axisRay;
- bool selected;
- bool lastSelected;
- float t;
- float d;
- float lastT;
- float lastD;
- GizmoAxis()
- {
- selected = false;
- lastSelected = false;
- t = 0.0;
- d = 0.0;
- lastT = 0.0;
- lastD = 0.0;
- }
- void Update(Ray cameraRay, float scale, bool drag)
- {
- // Do not select when UI has modal element
- if (ui.HasModalElement())
- {
- selected = false;
- return;
- }
-
- Vector3 closest = cameraRay.ClosestPoint(axisRay);
- Vector3 projected = axisRay.Project(closest);
- d = axisRay.Distance(closest);
- t = (projected - axisRay.origin).DotProduct(axisRay.direction);
- // Determine the sign of d from a plane that goes through the camera position to the axis
- Plane axisPlane(cameraNode.position, axisRay.origin, axisRay.origin + axisRay.direction);
- if (axisPlane.Distance(closest) < 0.0)
- d = -d;
- // Update selected status only when not dragging
- if (!drag)
- {
- selected = Abs(d) < axisMaxD * scale && t >= -axisMaxD * scale && t <= axisMaxT * scale;
- lastT = t;
- lastD = d;
- }
- }
- void Moved()
- {
- lastT = t;
- lastD = d;
- }
- }
- GizmoAxis gizmoAxisX;
- GizmoAxis gizmoAxisY;
- GizmoAxis gizmoAxisZ;
- void CreateGizmo()
- {
- gizmoNode = Node();
- gizmo = gizmoNode.CreateComponent("StaticModel");
- gizmo.model = cache.GetResource("Model", "Models/Editor/Axes.mdl");
- gizmo.materials[0] = cache.GetResource("Material", "Materials/Editor/RedUnlit.xml");
- gizmo.materials[1] = cache.GetResource("Material", "Materials/Editor/GreenUnlit.xml");
- gizmo.materials[2] = cache.GetResource("Material", "Materials/Editor/BlueUnlit.xml");
- gizmo.enabled = false;
- gizmo.viewMask = 0x80000000; // Editor raycasts use viewmask 0x7fffffff
- gizmo.occludee = false;
- gizmoAxisX.lastSelected = false;
- gizmoAxisY.lastSelected = false;
- gizmoAxisZ.lastSelected = false;
- lastGizmoMode = EDIT_MOVE;
- }
- void HideGizmo()
- {
- if (gizmo !is null)
- gizmo.enabled = false;
- }
- void ShowGizmo()
- {
- if (gizmo !is null)
- {
- gizmo.enabled = true;
- // Because setting enabled = false detaches the gizmo from octree,
- // and it is a manually added drawable, must readd to octree when showing
- if (editorScene.octree !is null)
- editorScene.octree.AddManualDrawable(gizmo);
- }
- }
- void UpdateGizmo()
- {
- UseGizmo();
- PositionGizmo();
- ResizeGizmo();
- }
- void PositionGizmo()
- {
- if (gizmo is null)
- return;
- Vector3 center(0, 0, 0);
- bool containsScene = false;
- for (uint i = 0; i < editNodes.length; ++i)
- {
- // Scene's transform should not be edited, so hide gizmo if it is included
- if (editNodes[i] is editorScene)
- {
- containsScene = true;
- break;
- }
- center += editNodes[i].worldPosition;
- }
- if (editNodes.empty || containsScene)
- {
- HideGizmo();
- return;
- }
- center /= editNodes.length;
- gizmoNode.position = center;
- if (axisMode == AXIS_WORLD || editNodes.length > 1)
- gizmoNode.rotation = Quaternion();
- else
- gizmoNode.rotation = editNodes[0].worldRotation;
- if (editMode != lastGizmoMode)
- {
- switch (editMode)
- {
- case EDIT_MOVE:
- gizmo.model = cache.GetResource("Model", "Models/Editor/Axes.mdl");
- break;
- case EDIT_ROTATE:
- gizmo.model = cache.GetResource("Model", "Models/Editor/RotateAxes.mdl");
- break;
- case EDIT_SCALE:
- gizmo.model = cache.GetResource("Model", "Models/Editor/ScaleAxes.mdl");
- break;
- }
- lastGizmoMode = editMode;
- }
- if ((editMode != EDIT_SELECT && !orbiting) && !gizmo.enabled)
- ShowGizmo();
- else if ((editMode == EDIT_SELECT || orbiting) && gizmo.enabled)
- HideGizmo();
- }
- void ResizeGizmo()
- {
- if (gizmo is null || !gizmo.enabled)
- return;
- float c = 0.1;
- if (camera.orthographic)
- gizmoNode.scale = Vector3(c, c, c);
- else
- {
- /// \todo if matrix classes were exposed to script could simply use the camera's inverse world transform
- float z = (cameraNode.worldRotation.Inverse() * (gizmoNode.worldPosition - cameraNode.worldPosition)).z;
- gizmoNode.scale = Vector3(c * z, c * z, c * z);
- }
- }
- void CalculateGizmoAxes()
- {
- gizmoAxisX.axisRay = Ray(gizmoNode.position, gizmoNode.rotation * Vector3(1, 0, 0));
- gizmoAxisY.axisRay = Ray(gizmoNode.position, gizmoNode.rotation * Vector3(0, 1, 0));
- gizmoAxisZ.axisRay = Ray(gizmoNode.position, gizmoNode.rotation * Vector3(0, 0, 1));
- }
- void GizmoMoved()
- {
- gizmoAxisX.Moved();
- gizmoAxisY.Moved();
- gizmoAxisZ.Moved();
- }
- void UseGizmo()
- {
- if (gizmo is null || !gizmo.enabled || editMode == EDIT_SELECT)
- {
- StoreGizmoEditActions();
- previousGizmoDrag = false;
- return;
- }
- IntVector2 pos = ui.cursorPosition;
- if (ui.GetElementAt(pos) !is null)
- return;
- Ray cameraRay = GetActiveViewportCameraRay();
- float scale = gizmoNode.scale.x;
- // Recalculate axes only when not left-dragging
- bool drag = input.mouseButtonDown[MOUSEB_LEFT];
- if (!drag)
- CalculateGizmoAxes();
- gizmoAxisX.Update(cameraRay, scale, drag);
- gizmoAxisY.Update(cameraRay, scale, drag);
- gizmoAxisZ.Update(cameraRay, scale, drag);
- if (gizmoAxisX.selected != gizmoAxisX.lastSelected)
- {
- gizmo.materials[0] = cache.GetResource("Material", gizmoAxisX.selected ? "Materials/Editor/BrightRedUnlit.xml" :
- "Materials/Editor/RedUnlit.xml");
- gizmoAxisX.lastSelected = gizmoAxisX.selected;
- }
- if (gizmoAxisY.selected != gizmoAxisY.lastSelected)
- {
- gizmo.materials[1] = cache.GetResource("Material", gizmoAxisY.selected ? "Materials/Editor/BrightGreenUnlit.xml" :
- "Materials/Editor/GreenUnlit.xml");
- gizmoAxisY.lastSelected = gizmoAxisY.selected;
- }
- if (gizmoAxisZ.selected != gizmoAxisZ.lastSelected)
- {
- gizmo.materials[2] = cache.GetResource("Material", gizmoAxisZ.selected ? "Materials/Editor/BrightBlueUnlit.xml" :
- "Materials/Editor/BlueUnlit.xml");
- gizmoAxisZ.lastSelected = gizmoAxisZ.selected;
- };
- if (drag)
- {
- // Store initial transforms for undo when gizmo drag started
- if (!previousGizmoDrag)
- {
- oldGizmoTransforms.Resize(editNodes.length);
- for (uint i = 0; i < editNodes.length; ++i)
- oldGizmoTransforms[i].Define(editNodes[i]);
- }
- bool moved = false;
- if (editMode == EDIT_MOVE)
- {
- Vector3 adjust(0, 0, 0);
- if (gizmoAxisX.selected)
- adjust += Vector3(1, 0, 0) * (gizmoAxisX.t - gizmoAxisX.lastT);
- if (gizmoAxisY.selected)
- adjust += Vector3(0, 1, 0) * (gizmoAxisY.t - gizmoAxisY.lastT);
- if (gizmoAxisZ.selected)
- adjust += Vector3(0, 0, 1) * (gizmoAxisZ.t - gizmoAxisZ.lastT);
- moved = MoveNodes(adjust);
- }
- else if (editMode == EDIT_ROTATE)
- {
- Vector3 adjust(0, 0, 0);
- if (gizmoAxisX.selected)
- adjust.x = (gizmoAxisX.d - gizmoAxisX.lastD) * rotSensitivity / scale;
- if (gizmoAxisY.selected)
- adjust.y = -(gizmoAxisY.d - gizmoAxisY.lastD) * rotSensitivity / scale;
- if (gizmoAxisZ.selected)
- adjust.z = (gizmoAxisZ.d - gizmoAxisZ.lastD) * rotSensitivity / scale;
- moved = RotateNodes(adjust);
- }
- else if (editMode == EDIT_SCALE)
- {
- Vector3 adjust(0, 0, 0);
- if (gizmoAxisX.selected)
- adjust += Vector3(1, 0, 0) * (gizmoAxisX.t - gizmoAxisX.lastT);
- if (gizmoAxisY.selected)
- adjust += Vector3(0, 1, 0) * (gizmoAxisY.t - gizmoAxisY.lastT);
- if (gizmoAxisZ.selected)
- adjust += Vector3(0, 0, 1) * (gizmoAxisZ.t - gizmoAxisZ.lastT);
- // Special handling for uniform scale: use the unmodified X-axis movement only
- if (editMode == EDIT_SCALE && gizmoAxisX.selected && gizmoAxisY.selected && gizmoAxisZ.selected)
- {
- float x = gizmoAxisX.t - gizmoAxisX.lastT;
- adjust = Vector3(x, x, x);
- }
- moved = ScaleNodes(adjust);
- }
- if (moved)
- {
- GizmoMoved();
- UpdateNodeAttributes();
- needGizmoUndo = true;
- }
- }
- else
- {
- if (previousGizmoDrag)
- StoreGizmoEditActions();
- }
- previousGizmoDrag = drag;
- }
- bool IsGizmoSelected()
- {
- return gizmo !is null && gizmo.enabled && (gizmoAxisX.selected || gizmoAxisY.selected || gizmoAxisZ.selected);
- }
- bool MoveNodes(Vector3 adjust)
- {
- bool moved = false;
- if (adjust.length > M_EPSILON)
- {
- for (uint i = 0; i < editNodes.length; ++i)
- {
- if (moveSnap)
- {
- float moveStepScaled = moveStep * snapScale;
- adjust.x = Floor(adjust.x / moveStepScaled + 0.5) * moveStepScaled;
- adjust.y = Floor(adjust.y / moveStepScaled + 0.5) * moveStepScaled;
- adjust.z = Floor(adjust.z / moveStepScaled + 0.5) * moveStepScaled;
- }
- Node@ node = editNodes[i];
- Vector3 nodeAdjust = adjust;
- if (axisMode == AXIS_LOCAL && editNodes.length == 1)
- nodeAdjust = node.worldRotation * nodeAdjust;
- Vector3 worldPos = node.worldPosition;
- Vector3 oldPos = node.position;
- worldPos += nodeAdjust;
- if (node.parent is null)
- node.position = worldPos;
- else
- node.position = node.parent.WorldToLocal(worldPos);
- if (node.position != oldPos)
- moved = true;
- }
- }
- return moved;
- }
- bool RotateNodes(Vector3 adjust)
- {
- bool moved = false;
- if (rotateSnap)
- {
- float rotateStepScaled = rotateStep * snapScale;
- adjust.x = Floor(adjust.x / rotateStepScaled + 0.5) * rotateStepScaled;
- adjust.y = Floor(adjust.y / rotateStepScaled + 0.5) * rotateStepScaled;
- adjust.z = Floor(adjust.z / rotateStepScaled + 0.5) * rotateStepScaled;
- }
- if (adjust.length > M_EPSILON)
- {
- moved = true;
- for (uint i = 0; i < editNodes.length; ++i)
- {
- Node@ node = editNodes[i];
- Quaternion rotQuat(adjust);
- if (axisMode == AXIS_LOCAL && editNodes.length == 1)
- node.rotation = node.rotation * rotQuat;
- else
- {
- Vector3 offset = node.worldPosition - gizmoAxisX.axisRay.origin;
- if (node.parent !is null && node.parent.worldRotation != Quaternion(1, 0, 0, 0))
- rotQuat = node.parent.worldRotation.Inverse() * rotQuat * node.parent.worldRotation;
- node.rotation = rotQuat * node.rotation;
- Vector3 newPosition = gizmoAxisX.axisRay.origin + rotQuat * offset;
- if (node.parent !is null)
- newPosition = node.parent.WorldToLocal(newPosition);
- node.position = newPosition;
- }
- }
- }
- return moved;
- }
- bool ScaleNodes(Vector3 adjust)
- {
- bool moved = false;
- if (adjust.length > M_EPSILON)
- {
- for (uint i = 0; i < editNodes.length; ++i)
- {
- Node@ node = editNodes[i];
- Vector3 scale = node.scale;
- Vector3 oldScale = scale;
- if (!scaleSnap)
- scale += adjust;
- else
- {
- float scaleStepScaled = scaleStep * snapScale;
- if (adjust.x != 0)
- {
- scale.x += adjust.x * scaleStepScaled;
- scale.x = Floor(scale.x / scaleStepScaled + 0.5) * scaleStepScaled;
- }
- if (adjust.y != 0)
- {
- scale.y += adjust.y * scaleStepScaled;
- scale.y = Floor(scale.y / scaleStepScaled + 0.5) * scaleStepScaled;
- }
- if (adjust.z != 0)
- {
- scale.z += adjust.z * scaleStepScaled;
- scale.z = Floor(scale.z / scaleStepScaled + 0.5) * scaleStepScaled;
- }
- }
- if (scale != oldScale)
- moved = true;
- node.scale = scale;
- }
- }
- return moved;
- }
- void StoreGizmoEditActions()
- {
- if (needGizmoUndo && editNodes.length > 0 && oldGizmoTransforms.length == editNodes.length)
- {
- EditActionGroup group;
-
- for (uint i = 0; i < editNodes.length; ++i)
- {
- EditNodeTransformAction action;
- action.Define(editNodes[i], oldGizmoTransforms[i]);
- group.actions.Push(action);
- }
-
- SaveEditActionGroup(group);
- SetSceneModified();
- }
- needGizmoUndo = false;
- }
|