Download CommonLua/Editor/XEditor/MoveGizmo.lua from sirnii/myspace: direct link, hf CLI and curl.
- Browser
- Download file 13.5 kB
-
https://huggingface.co/spaces/sirnii/myspace/resolve/main/CommonLua/Editor/XEditor/MoveGizmo.lua
- Command line
-
hf download hf://spaces/sirnii/myspace/CommonLua/Editor/XEditor/MoveGizmo.lua
-
curl -L -o MoveGizmo.lua https://huggingface.co/spaces/sirnii/myspace/resolve/main/CommonLua/Editor/XEditor/MoveGizmo.lua
13.5 kB
| DefineClass.MoveGizmoTool = { | |
| __parents = { "Mesh" }, | |
| mesh_x = pstr(""), | |
| mesh_y = pstr(""), | |
| mesh_z = pstr(""), | |
| mesh_xy = pstr(""), | |
| mesh_xz = pstr(""), | |
| mesh_yz = pstr(""), | |
| b_over_axis_x = false, | |
| b_over_axis_y = false, | |
| b_over_axis_z = false, | |
| b_over_plane_xy = false, | |
| b_over_plane_xz = false, | |
| b_over_plane_yz = false, | |
| v_axis_x = axis_x, | |
| v_axis_y = axis_y, | |
| v_axis_z = axis_z, | |
| opacity = 110, | |
| r_color = RGB(192, 0, 0), | |
| g_color = RGB(0, 192, 0), | |
| b_color = RGB(0, 0, 192), | |
| -- rotation arrows, clockwise (cw) and counter-clockwise (ccw) | |
| arrow_xcw = pstr(""), | |
| arrow_xccw = pstr(""), | |
| arrow_ycw = pstr(""), | |
| arrow_yccw = pstr(""), | |
| arrow_zcw = pstr(""), | |
| arrow_zccw = pstr(""), | |
| -- settings | |
| rotation_arrows_on = false, | |
| rotation_arrows_z_only = false, | |
| } | |
| function MoveGizmoTool:ApplyOpacity(color, opacity) | |
| local r, g, b = GetRGB(color) | |
| return RGBA(r, g, b, self.opacity) | |
| end | |
| function MoveGizmoTool:RenderGizmo() | |
| local vpstr = pstr("") | |
| self.mesh_x = self:RenderAxis(nil, axis_y, self.b_over_axis_x, false) | |
| self.mesh_y = self:RenderAxis(nil, -axis_x, self.b_over_axis_y, false) | |
| self.mesh_z = self:RenderAxis(nil, axis_z, self.b_over_axis_z, false) | |
| self.mesh_xy = self:RenderPlane(nil, axis_z) | |
| self.mesh_xz = self:RenderPlane(nil, axis_x) | |
| self.mesh_yz = self:RenderPlane(nil, -axis_y) | |
| local r = self:ApplyOpacity(self.r_color) | |
| local g = self:ApplyOpacity(self.g_color) | |
| local b = self:ApplyOpacity(self.b_color) | |
| self.arrow_xcw = self:RenderRotationArrow(nil, axis_x, g, false) | |
| self.arrow_xccw = self:RenderRotationArrow(nil, axis_x, g, true) | |
| self.arrow_ycw = self:RenderRotationArrow(nil, -axis_y, r, false) | |
| self.arrow_yccw = self:RenderRotationArrow(nil, -axis_y, r, true) | |
| self.arrow_zcw = self:RenderRotationArrow(nil, axis_z, b, false) | |
| self.arrow_zccw = self:RenderRotationArrow(nil, axis_z, b, true) | |
| vpstr = self:RenderAxis(vpstr, axis_y, self.b_over_axis_x, true, r) | |
| vpstr = self:RenderAxis(vpstr, -axis_x, self.b_over_axis_y, true, g) | |
| vpstr = self:RenderAxis(vpstr, axis_z, self.b_over_axis_z, true, b) | |
| vpstr = self:RenderPlaneOutlines(vpstr) | |
| if self.b_over_plane_xy then vpstr = self:RenderPlane(vpstr, axis_z) end | |
| if self.b_over_plane_xz then vpstr = self:RenderPlane(vpstr, axis_x) end | |
| if self.b_over_plane_yz then vpstr = self:RenderPlane(vpstr, -axis_y) end | |
| if self.rotation_arrows_on then | |
| if not self.rotation_arrows_z_only then | |
| self:RenderRotationArrow(vpstr, axis_x, g, false) | |
| self:RenderRotationArrow(vpstr, axis_x, g, true) | |
| self:RenderRotationArrow(vpstr, -axis_y, r, false) | |
| self:RenderRotationArrow(vpstr, -axis_y, r, true) | |
| end | |
| self:RenderRotationArrow(vpstr, axis_z, b, false) | |
| self:RenderRotationArrow(vpstr, axis_z, b, true) | |
| end | |
| return vpstr | |
| end | |
| function MoveGizmoTool:ChangeScale() | |
| local eye = camera.GetEye() | |
| local dir = self:GetVisualPos() | |
| local ray = dir - eye | |
| local cameraDistanceSquared = ray:x() * ray:x() + ray:y() * ray:y() + ray:z() * ray:z() | |
| local cameraDistance = 0 | |
| if cameraDistanceSquared >= 0 then cameraDistance = sqrt(cameraDistanceSquared) end | |
| self.scale = cameraDistance / 20 * self.scale / 100 | |
| end | |
| function MoveGizmoTool:CursorIntersection(mouse_pos) | |
| local pt1 = camera.GetEye() | |
| local precision = 128 -- should not lead to overflow for maps up to 20x20 km (with guim == 1000) | |
| local dir = ScreenToGame(mouse_pos, precision) - pt1 * precision | |
| local pt2 = pt1 + dir | |
| local pos = self:GetVisualPos() | |
| if self.b_over_axis_x or self.b_over_axis_y or self.b_over_axis_z then | |
| local axis | |
| if self.b_over_axis_x then | |
| axis = self.v_axis_x | |
| elseif self.b_over_axis_y then | |
| axis = self.v_axis_y | |
| elseif self.b_over_axis_z then | |
| axis = self.v_axis_z | |
| end | |
| local camDir = Normalize(camera.GetEye() - pos) | |
| local camX = Normalize(Cross((camDir), axis_z)) | |
| local planeB = pos + camX | |
| local planeC = pos + Normalize(Cross((camDir), camX)) | |
| local ptA = pos | |
| local ptB = ptA + axis | |
| local intersection = IntersectRayPlane(pt1, pt2, pos, planeB, planeC) | |
| intersection = ProjectPointOnLine(ptA, ptB, intersection) | |
| return intersection | |
| elseif self.b_over_plane_xy or self.b_over_plane_xz or self.b_over_plane_yz then | |
| local normal, planeB, planeC | |
| if self.b_over_plane_xy then | |
| normal = self.v_axis_z | |
| planeB = pos + self.v_axis_x | |
| planeC = pos + self.v_axis_y | |
| elseif self.b_over_plane_xz then | |
| normal = self.v_axis_y | |
| planeB = pos + self.v_axis_x | |
| planeC = pos + self.v_axis_z | |
| elseif self.b_over_plane_yz then | |
| normal = self.v_axis_x | |
| planeB = pos + self.v_axis_y | |
| planeC = pos + self.v_axis_z | |
| end | |
| local intersection = IntersectRayPlane(pt1, pt2, pos, planeB, planeC) | |
| intersection = ProjectPointOnPlane(pos, normal, intersection) | |
| return intersection | |
| end | |
| end | |
| function MoveGizmoTool:IntersectRay(pt1, pt2) | |
| self.b_over_plane_xy = false | |
| self.b_over_plane_xz = false | |
| self.b_over_plane_yz = false | |
| self.b_over_axis_x = IntersectRayMesh(self, pt1, pt2, self.mesh_x) | |
| self.b_over_axis_y = IntersectRayMesh(self, pt1, pt2, self.mesh_y) | |
| self.b_over_axis_z = IntersectRayMesh(self, pt1, pt2, self.mesh_z) | |
| local hit, axis, ccw | |
| if IntersectRayMesh(self, pt1, pt2, self.arrow_xcw ) then hit, axis, ccw = true, axis_x, false | |
| elseif IntersectRayMesh(self, pt1, pt2, self.arrow_xccw) then hit, axis, ccw = true, axis_x, true | |
| elseif IntersectRayMesh(self, pt1, pt2, self.arrow_ycw ) then hit, axis, ccw = true, -axis_y, false | |
| elseif IntersectRayMesh(self, pt1, pt2, self.arrow_yccw) then hit, axis, ccw = true, -axis_y, true | |
| elseif IntersectRayMesh(self, pt1, pt2, self.arrow_zcw ) then hit, axis, ccw = true, axis_z, false | |
| elseif IntersectRayMesh(self, pt1, pt2, self.arrow_zccw) then hit, axis, ccw = true, axis_z, true end | |
| self.b_over_rotation_arrow = hit | |
| self.arrow_axis = axis | |
| self.arrow_ccw = ccw | |
| if self.b_over_axis_x then | |
| self.b_over_axis_y = false | |
| self.b_over_axis_z = false | |
| return true | |
| elseif self.b_over_axis_y then | |
| self.b_over_axis_x = false | |
| self.b_over_axis_z = false | |
| return true | |
| elseif self.b_over_axis_z then | |
| self.b_over_axis_x = false | |
| self.b_over_axis_y = false | |
| return true | |
| else | |
| local overPlaneXY, lenXY = IntersectRayMesh(self, pt1, pt2, self.mesh_xy) | |
| local overPlaneXZ, lenXZ = IntersectRayMesh(self, pt1, pt2, self.mesh_xz) | |
| local overPlaneYZ, lenYZ = IntersectRayMesh(self, pt1, pt2, self.mesh_yz) | |
| if not (overPlaneXY or overPlaneXZ or overPlaneYZ) then return end | |
| if overPlaneXY then | |
| self.b_over_plane_xy = true | |
| return true | |
| elseif lenXZ and lenYZ then | |
| if lenXZ < lenYZ then | |
| self.b_over_plane_xz = overPlaneXZ | |
| return overPlaneXZ | |
| else | |
| self.b_over_plane_yz = overPlaneYZ | |
| return overPlaneYZ | |
| end | |
| elseif overPlaneXZ then | |
| self.b_over_plane_xz = true | |
| return true | |
| elseif overPlaneYZ then | |
| self.b_over_plane_yz = true | |
| return true | |
| end | |
| end | |
| end | |
| function MoveGizmoTool:RenderAxis(vpstr, axis, selected, visual, color) | |
| vpstr = vpstr or pstr("") | |
| local cylinderRadius = visual and 0.1 * self.scale * self.thickness / 100 or 0.1 * self.scale | |
| local cylinderHeight = 4.0 * self.scale | |
| local coneRadius = visual and 0.45 * self.scale * self.thickness / 100 or 0.45 * self.scale | |
| local coneHeight = 1.0 * self.scale | |
| color = selected and RGBA(255, 255, 0, self.opacity) or color | |
| vpstr = AppendConeVertices(vpstr, nil, point(0, 0, cylinderHeight), cylinderRadius, cylinderRadius, axis, 90, color) | |
| vpstr = AppendConeVertices(vpstr, point(0, 0, cylinderHeight), point(0, 0, coneHeight), coneRadius, 0, axis, 90, color) | |
| return vpstr | |
| end | |
| function MoveGizmoTool:RenderPlaneOutlines(vpstr) | |
| local height = 2.5 * self.scale | |
| local radius = 0.05 * self.scale * self.thickness / 100 | |
| local r = self:ApplyOpacity(self.r_color) | |
| local g = self:ApplyOpacity(self.g_color) | |
| local b = self:ApplyOpacity(self.b_color) | |
| vpstr = AppendConeVertices(vpstr, nil, point(0, 0, height), radius, radius, axis_z, 90, r, point(height, 0, 0)) | |
| vpstr = AppendConeVertices(vpstr, nil, point(0, 0, height), radius, radius, -axis_x, 90, r, point(height, 0, 0)) | |
| vpstr = AppendConeVertices(vpstr, nil, point(0, 0, height), radius, radius, axis_z, 90, g, point(0, height, 0)) | |
| vpstr = AppendConeVertices(vpstr, nil, point(0, 0, height), radius, radius, axis_y, 90, g, point(0, height, 0)) | |
| vpstr = AppendConeVertices(vpstr, nil, point(0, 0, height), radius, radius, axis_y, 90, b, point(0, 0, height)) | |
| vpstr = AppendConeVertices(vpstr, nil, point(0, 0, height), radius, radius, -axis_x, 90, b, point(0, 0, height)) | |
| return vpstr | |
| end | |
| function MoveGizmoTool:TransformPointsToAxisPlane(axis, ...) | |
| local angle = (axis == -axis_y or axis == axis_x) and 90 * 60 or 0 | |
| local pts = table.pack(...) | |
| for i, pt in ipairs(pts) do | |
| pts[i] = RotateAxis(pt, axis, angle) | |
| end | |
| return pts | |
| end | |
| function MoveGizmoTool:RenderPlane(vpstr, axis) | |
| vpstr = vpstr or pstr("") | |
| local color = RGBA(255, 255, 0, self.opacity * 200 / 255) | |
| local dist = 2.5 * self.scale | |
| local pts = self:TransformPointsToAxisPlane(axis, point30, point(dist, 0, 0), point(dist, dist, 0), point(0, dist, 0)) | |
| vpstr:AppendVertex(pts[1], color) | |
| vpstr:AppendVertex(pts[2]) | |
| vpstr:AppendVertex(pts[4]) | |
| vpstr:AppendVertex(pts[3]) | |
| vpstr:AppendVertex(pts[2]) | |
| vpstr:AppendVertex(pts[4]) | |
| return vpstr | |
| end | |
| function MoveGizmoTool:RenderRotationArrow(vpstr, axis, color, ccw) | |
| local size = 0.7 * self.scale | |
| local dist = 2.1 * self.scale | |
| local offs = 0.1 * self.scale | |
| local corner = point(dist, dist, 0) | |
| local offset = point(offs, offs, 0) | |
| local a1, a2, a3 = point(size, 0, 0), point(0, size, 0), point(size, size, 0) | |
| a1 = Rotate(a1 + offset, (ccw and 45 or -45) * 60) + corner | |
| a2 = Rotate(a2 + offset, (ccw and 45 or -45) * 60) + corner | |
| a3 = Rotate(a3 + offset, (ccw and 45 or -45) * 60) + corner | |
| if self.b_over_rotation_arrow and axis == self.arrow_axis and self.arrow_ccw == ccw then | |
| color = RGBA(255, 255, 0, self.opacity * 200 / 255) | |
| end | |
| vpstr = vpstr or pstr("") | |
| local pts = self:TransformPointsToAxisPlane(axis, a1, a2, a3) | |
| vpstr:AppendVertex(pts[1], color) | |
| vpstr:AppendVertex(pts[2]) | |
| vpstr:AppendVertex(pts[3]) | |
| return vpstr | |
| end | |
| DefineClass.MoveGizmo = { | |
| __parents = { "XEditorGizmo", "MoveGizmoTool" }, | |
| HasLocalCSSetting = true, | |
| HasSnapSetting = true, | |
| Title = "Move gizmo (W)", | |
| Description = false, | |
| ActionSortKey = "1", | |
| ActionIcon = "CommonAssets/UI/Editor/Tools/MoveGizmo.tga", | |
| ActionShortcut = "W", | |
| UndoOpName = "Moved %d object(s)", | |
| rotation_arrows_on = true, | |
| operation_started = false, | |
| initial_positions = false, | |
| initial_pos = false, | |
| initial_gizmo_pos = false, | |
| move_by_slabs = false, | |
| } | |
| function MoveGizmo:CheckStartOperation(pt, btn_pressed) | |
| local objs = editor.GetSel() | |
| if #objs == 0 then return end | |
| -- check move operations first | |
| local ret = self:IntersectRay(camera.GetEye(), ScreenToGame(pt)) | |
| if ret then return ret end | |
| if self.b_over_rotation_arrow and btn_pressed then | |
| XEditorUndo:BeginOp{ objects = objs, name = string.format("Rotated %d objects", #objs) } | |
| local center = self:GetVisualPos() | |
| local axis = self.arrow_axis | |
| if axis ~= axis_z then | |
| axis = SetLen(Cross(self.arrow_axis, axis_z), 4096) | |
| end | |
| local angle = self.arrow_ccw and 90*60 or -90*60 | |
| if self.local_cs then | |
| axis = self:GetRelativePoint(axis) - self:GetVisualPos() | |
| end | |
| local rotate_logic = XEditorRotateLogic:new() | |
| SuspendPassEditsForEditOp() | |
| rotate_logic:InitRotation(objs, center, 0) | |
| rotate_logic:Rotate(objs, "group_rotation", center, axis, angle) | |
| ResumePassEditsForEditOp() | |
| XEditorUndo:EndOp(objs) | |
| return "break" | |
| end | |
| end | |
| function MoveGizmo:StartOperation(pt) | |
| self.initial_positions = {} | |
| local has_aligned, move_by_z = false, self.b_over_axis_z or self.b_over_plane_xz or self.b_over_plane_yz | |
| for _, obj in ipairs(editor.GetSel()) do | |
| local pos = obj:GetVisualPos() | |
| if obj:IsKindOf("AlignedObj") then | |
| pos = move_by_z and pos:AddZ((const.SlabSizeZ or 0) / 2) or obj:GetPos() | |
| has_aligned = true | |
| end | |
| self.initial_positions[obj] = pos | |
| end | |
| self.move_by_slabs = has_aligned | |
| self.initial_pos = self:CursorIntersection(pt) | |
| self.initial_gizmo_pos = self:GetVisualPos() | |
| self.operation_started = true | |
| end | |
| function MoveGizmo:PerformOperation(pt) | |
| local intersection = self:CursorIntersection(pt) | |
| if intersection then | |
| local objs = {} | |
| local vMove = intersection - self.initial_pos | |
| for obj, pos in pairs(self.initial_positions) do | |
| XEditorSnapPos(obj, pos, vMove, self.move_by_slabs) | |
| objs[#objs + 1] = obj | |
| end | |
| self:SetPos(self.initial_gizmo_pos + vMove) | |
| Msg("EditorCallback", "EditorCallbackMove", objs) | |
| end | |
| end | |
| function MoveGizmo:EndOperation() | |
| self.initial_positions = false | |
| self.initial_pos = false | |
| self.operation_started = false | |
| self.initial_gizmo_pos = false | |
| end | |
| local saneBox = box(-const.SanePosMaxXY, -const.SanePosMaxXY, const.SanePosMaxXY - 1, const.SanePosMaxXY - 1) | |
| local saneZ = const.SanePosMaxZ | |
| function MoveGizmo:Render() | |
| local obj = not XEditorIsContextMenuOpen() and selo() | |
| if obj then | |
| if self.local_cs then | |
| self.v_axis_x, self.v_axis_y, self.v_axis_z = GetAxisVectors(obj) | |
| self:SetAxisAngle(obj:GetAxis(), obj:GetAngle()) | |
| else | |
| self.v_axis_x = axis_x | |
| self.v_axis_y = axis_y | |
| self.v_axis_z = axis_z | |
| self:SetOrientation(axis_z, 0) | |
| end | |
| if not self.operation_started then | |
| local pos = CenterOfMasses(editor.GetSel()) | |
| local clamped_pos = ClampPoint(pos, saneBox):SetZ(Clamp(pos:z(), -saneZ, saneZ - 1)) | |
| self:SetPos(clamped_pos, 0) | |
| end | |
| self:ChangeScale() | |
| self:SetMesh(self:RenderGizmo()) | |
| else | |
| self:SetMesh(pstr("")) | |
| end | |
| end | |