File size: 6,919 Bytes
b6a38d7 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 |
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 |