sirnii's picture
Upload 1816 files
b6a38d7 verified
raw
history blame
6.92 kB
DefineClass.ScaleGizmo = {
__parents = { "XEditorGizmo" },
HasLocalCSSetting = false,
HasSnapSetting = false,
Title = "Scale gizmo (R)",
Description = false,
ActionSortKey = "3",
ActionIcon = "CommonAssets/UI/Editor/Tools/ScaleGizmo.tga",
ActionShortcut = "R",
UndoOpName = "Scaled %d object(s)",
side_mesh_a = pstr(""),
side_mesh_b = pstr(""),
side_mesh_c = pstr(""),
b_over_a = false,
b_over_b = false,
b_over_c = false,
scale = 100,
thickness = 100,
opacity = 255,
sensitivity = 100,
operation_started = false,
initial_scales = false,
text = false,
scale_text = "",
init_pos = false,
init_mouse_pos = false,
group_scale = false,
}
function ScaleGizmo:Done()
self:DeleteText()
end
function ScaleGizmo:DeleteText()
if self.text then
self.text:delete()
self.text = nil
self.scale_text = ""
end
end
function ScaleGizmo:CheckStartOperation(pt)
return #editor.GetSel() > 0 and self:IntersectRay(camera.GetEye(), ScreenToGame(pt))
end
function ScaleGizmo:StartOperation(pt)
self.text = XTemplateSpawn("XFloatingText")
self.text:SetTextStyle("GizmoText")
self.text:AddDynamicPosModifier({id = "attached_ui", target = self:GetPos()})
self.text.TextColor = RGB(255, 255, 255)
self.text.ShadowType = "outline"
self.text.ShadowSize = 1
self.text.ShadowColor = RGB(64, 64, 64)
self.text.Translate = false
self.init_pos = self:GetPos()
self.init_mouse_pos = terminal.GetMousePos()
self.initial_scales = {}
for _, obj in ipairs(editor.GetSel()) do
self.initial_scales[obj] = { scale = obj:GetScale(), offset = obj:GetVisualPos() - self.init_pos }
end
self.group_scale = terminal.IsKeyPressed(const.vkAlt)
self.operation_started = true
end
function ScaleGizmo:PerformOperation(pt)
local screenHeight = UIL.GetScreenSize():y()
local mouseY = 4096.0 * (terminal.GetMousePos():y() - screenHeight / 2) / screenHeight
local initY = 4096.0 * (self.init_mouse_pos:y() - screenHeight / 2) / screenHeight
local scale
if mouseY < initY then
scale = 100 * (mouseY + 4096) / (initY + 4096) + 250 * (initY - mouseY) / (initY + 4096)
else
scale = 100 * (4096 - mouseY) / (4096 - initY) + 10 * (mouseY - initY) / (4096 - initY)
end
scale = 100 + MulDivRound(scale - 100, self.sensitivity, 100)
self:SetScaleClamped(scale)
for obj, data in pairs(self.initial_scales) do
obj:SetScaleClamped(MulDivRound(data.scale, scale, 100))
if self.group_scale then
XEditorSetPosAxisAngle(obj, self.init_pos + data.offset * scale / 100)
end
end
local objs = table.keys(self.initial_scales)
self.scale_text = #objs == 1 and
string.format("%.2f", objs[1]:GetScale() / 100.0) or
((scale >= 100 and "+" or "-") .. string.format("%d%%", abs(scale - 100)))
Msg("EditorCallback", "EditorCallbackScale", objs)
end
function ScaleGizmo:EndOperation()
self:DeleteText()
self:SetScale(100)
self.init_pos = false
self.init_mouse_pos = false
self.initial_scales = false
self.group_scale = false
self.operation_started = false
end
function ScaleGizmo:RenderGizmo()
local FloorPtA = MulDivRound(point(0, 4096, 0), self.scale * 25, 40960)
local FloorPtB = MulDivRound(point(-3547, -2048, 0), self.scale * 25, 40960)
local FloorPtC = MulDivRound(point(3547, -2048, 0), self.scale * 25, 40960)
local UpperPt = MulDivRound(point(0, 0, 5900), self.scale * 25, 40960)
local PyramidSize = FloorPtA:Dist(FloorPtB)
self.side_mesh_a = self:RenderPlane(nil, UpperPt, FloorPtB, FloorPtC)
self.side_mesh_b = self:RenderPlane(nil, FloorPtA, UpperPt, FloorPtC)
self.side_mesh_c = self:RenderPlane(nil, FloorPtA, UpperPt, FloorPtB)
if self.text then
self.text:SetText(self.scale_text)
end
local vpstr = pstr("")
vpstr = self:RenderCylinder(vpstr, PyramidSize, FloorPtA, 90, FloorPtB)
vpstr = self:RenderCylinder(vpstr, PyramidSize, FloorPtB, 90, FloorPtC)
vpstr = self:RenderCylinder(vpstr, PyramidSize, FloorPtC, 90, FloorPtA)
vpstr = self:RenderCylinder(vpstr, PyramidSize, Cross(FloorPtA, axis_z), 35, FloorPtA)
vpstr = self:RenderCylinder(vpstr, PyramidSize, Cross(FloorPtB, axis_z), 35, FloorPtB)
vpstr = self:RenderCylinder(vpstr, PyramidSize, Cross(FloorPtC, axis_z), 35, FloorPtC)
if self.b_over_a then vpstr = self:RenderPlane(vpstr, UpperPt, FloorPtB, FloorPtC)
elseif self.b_over_b then vpstr = self:RenderPlane(vpstr, FloorPtA, UpperPt, FloorPtC)
elseif self.b_over_c then vpstr = self:RenderPlane(vpstr, FloorPtA, UpperPt, FloorPtB) end
return vpstr
end
function ScaleGizmo: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 ScaleGizmo:Render()
local obj = not XEditorIsContextMenuOpen() and selo()
if obj then
self:SetPos(CenterOfMasses(editor.GetSel()))
self:ChangeScale()
self:SetMesh(self:RenderGizmo())
else self:SetMesh(pstr("")) end
end
function ScaleGizmo:CursorIntersection(mouse_pos)
if self.b_over_a or self.b_over_b or self.b_over_c then
local pos = self:GetVisualPos()
local planeB = pos + axis_z
local planeC = pos + axis_x
local pt1 = camera.GetEye()
local pt2 = ScreenToGame(mouse_pos)
local intersection = IntersectRayPlane(pt1, pt2, pos, planeB, planeC)
return ProjectPointOnLine(pos, pos + axis_z, intersection)
end
end
function ScaleGizmo:IntersectRay(pt1, pt2)
self.b_over_a = false
self.b_over_b = false
self.b_over_c = false
local overA, lenA = IntersectRayMesh(self, pt1, pt2, self.side_mesh_a)
local overB, lenB = IntersectRayMesh(self, pt1, pt2, self.side_mesh_b)
local overC, lenC = IntersectRayMesh(self, pt1, pt2, self.side_mesh_c)
if not (overA or overB or overC) then return end
if lenA and lenB then
if lenA < lenB then self.b_over_a = overA
else self.b_over_b = overB end
elseif lenA and lenC then
if lenA < lenC then self.b_over_a = overA
else self.b_over_c = overC end
elseif lenB and lenC then
if lenB < lenC then self.b_over_b = overB
else self.b_over_c = overC end
elseif lenA then self.b_over_a = overA
elseif lenB then self.b_over_b = overB
elseif lenC then self.b_over_c = overC end
return self.b_over_a or self.b_over_b or self.b_over_c
end
function ScaleGizmo:RenderPlane(vpstr, ptA, ptB, ptC)
vpstr = vpstr or pstr("")
vpstr:AppendVertex(ptA, RGBA(255, 255, 0, MulDivRound(200, self.opacity, 255)))
vpstr:AppendVertex(ptB)
vpstr:AppendVertex(ptC)
return vpstr
end
function ScaleGizmo:RenderCylinder(vpstr, height, axis, angle, offset)
vpstr = vpstr or pstr("")
local center = point(0, 0, 0)
local radius = 0.10 * self.scale * self.thickness / 100
local color = RGBA(0, 192, 192, self.opacity)
return AppendConeVertices(vpstr, center, point(0, 0, height), radius, radius, axis, angle, color, offset)
end