File size: 6,525 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 210 211 212 213 |
if not const.SlabSizeX then return end
local offset = point(const.SlabSizeX, const.SlabSizeY, const.SlabSizeZ) / 2
local retrace = point(const.SlabSizeX, const.SlabSizeY, 0) / 2
local function snap_to_voxel_grid(pt)
return SnapToVoxel(pt + offset) - retrace
end
DefineClass.XCreateGuidesTool = {
__parents = { "XEditorTool" },
properties = {
persisted_setting = true,
{ id = "Snapping", editor = "bool", default = true, },
{ id = "Vertical", editor = "bool", default = true, },
{ id = "Prg", name = "Apply Prg", editor = "choice", default = "",
items = function(self)
return PresetsCombo("ExtrasGen", nil, "", function(prg)
return prg.RequiresClass == "EditorLineGuide" and prg.RequiresGuideType == (self:GetVertical() and "Vertical" or "Horizontal")
end)
end,
},
},
ToolTitle = "Create Guides",
Description = {
"(drag to place guide or guides)\n" ..
"(<style GedHighlight>hold Ctrl</style> to disable snapping)",
},
UsesCodeRenderables = true,
start_pos = false,
guides = false,
prg_applied = false,
old_guides_hash = false,
}
function XCreateGuidesTool:OnEditorSetProperty(prop_id, old_value, ged)
if prop_id == "Vertical" then
self:SetPrg("")
end
end
function XCreateGuidesTool:Done()
if self.start_pos then -- tool destroyed while dragging
ResumePassEdits("XCreateGuidesTool")
self:CreateGuides(0)
end
end
function XCreateGuidesTool:CreateGuides(count)
self.guides = self.guides or {}
local guides = self.guides
if count == #guides then return end
for i = 1, Max(count, #guides) do
if i <= count and not guides[i] then
guides[i] = EditorLineGuide:new()
guides[i]:SetOpacity(self:GetPrg() == "" and 100 or 0)
elseif i > count and guides[i] then
DoneObject(guides[i])
guides[i] = nil
end
end
end
function XCreateGuidesTool:UpdateGuides(pt_min, pt_max)
local x1, y1 = pt_min:xy()
local x2, y2 = pt_max:xy()
local z = Max(
terrain.GetHeight(point(x1, y1)),
terrain.GetHeight(point(x1, y2)),
terrain.GetHeight(point(x2, y1)),
terrain.GetHeight(point(x2, y2))
)
if x1 == x2 then
local count = y1 == y2 and 0 or 1
self:CreateGuides(count)
if count == 0 then return end
local pos, lookat = GetCamera()
local dot = Dot(SetLen((lookat - pos):SetZ(0), 4096), axis_x)
self.guides[1]:Set(point(x1, y1, z), point(x1, y2, z), dot > 0 and -axis_x or axis_x)
elseif y1 == y2 then
self:CreateGuides(1)
local pos, lookat = GetCamera()
local dot = Dot(SetLen((lookat - pos):SetZ(0), 4096), axis_y)
self.guides[1]:Set(point(x1, y1, z), point(x2, y1, z), dot > 0 and -axis_y or axis_y)
else
self:CreateGuides(4)
self.guides[1]:Set(point(x1, y1, z), point(x1, y2, z), -axis_x)
self.guides[2]:Set(point(x2, y1, z), point(x2, y2, z), axis_x)
self.guides[3]:Set(point(x1, y1, z), point(x2, y1, z), -axis_y)
self.guides[4]:Set(point(x1, y2, z), point(x2, y2, z), axis_y)
end
end
function XCreateGuidesTool:GetGuidesHash()
local hash = 42
for _, guide in ipairs(self.guides or empty_table) do
hash = xxhash(hash, guide:GetPos1(), guide:GetPos2(), guide:GetNormal())
end
return hash
end
function XCreateGuidesTool:ApplyPrg()
local hash = self:GetGuidesHash()
if self:GetPrg() ~= "" and hash ~= self.old_guides_hash and self.guides and #self.guides ~= 0 then
if self.prg_applied then
XEditorUndo:UndoRedo("undo")
end
-- create copies of the guides and apple the Prg to them (some Prgs change the guides)
local guides = {}
for _, guide in ipairs(self.guides) do
local g = EditorLineGuide:new()
g:Set(guide:GetPos1(), guide:GetPos2(), guide:GetNormal())
guides[#guides + 1] = g
end
GenExtras(self:GetPrg(), guides)
for _, guide in ipairs(guides) do
DoneObject(guide)
end
self.old_guides_hash = hash
self.prg_applied = true
end
end
function XCreateGuidesTool:OnMouseButtonDown(pt, button)
if button == "L" then
self.start_pos = GetTerrainCursor()
self.snapping = self:GetSnapping() and not terminal.IsKeyPressed(const.vkControl)
if self.snapping then
self.start_pos = snap_to_voxel_grid(self.start_pos)
end
self.desktop:SetMouseCapture(self)
SuspendPassEdits("XCreateGuidesTool")
return "break"
end
return XEditorTool.OnMouseButtonDown(self, pt, button)
end
local function MinMaxPtXY(f, p1, p2)
return point(f(p1:x(), p2:x()), f(p1:y(), p2:y()))
end
function XCreateGuidesTool:OnMousePos(pt, button)
local start_pos = self.start_pos
if start_pos then
if self:GetVertical() then
local eye, lookat = GetCamera()
local cursor = ScreenToGame(pt)
-- vertical plane parallel to screen
local pt1, pt2, pt3 = start_pos, start_pos + axis_z, start_pos + SetLen(Cross(lookat - eye, axis_z), 4096)
local intersection = IntersectRayPlane(eye, cursor, pt1, pt2, pt3)
intersection = ProjectPointOnLine(pt1, pt2, intersection)
intersection = self.snapping and snap_to_voxel_grid(intersection) or intersection
if start_pos ~= intersection then
local angle = CalcSignedAngleBetween2D(axis_x, eye - lookat)
local axis = Rotate(axis_x, CardinalDirection(angle))
if start_pos:Dist(intersection) > guim / 2 then
self:CreateGuides(1)
self.guides[1]:Set(start_pos, intersection, axis)
end
end
self:ApplyPrg()
return "break"
end
local pt_new = GetTerrainCursor()
if self.snapping then
pt_new = snap_to_voxel_grid(pt_new)
else
if abs(pt_new:x() - start_pos:x()) < guim / 2 then pt_new = pt_new:SetX(start_pos:x()) end
if abs(pt_new:y() - start_pos:y()) < guim / 2 then pt_new = pt_new:SetY(start_pos:y()) end
end
local pt_min = MinMaxPtXY(Min, pt_new, start_pos)
local pt_max = MinMaxPtXY(Max, pt_new, start_pos)
self:UpdateGuides(pt_min, pt_max)
self:ApplyPrg()
return "break"
end
return XEditorTool.OnMousePos(self, pt, button)
end
function XCreateGuidesTool:OnMouseButtonUp(pt, button)
local start_pos = self.start_pos
if start_pos then
if self.prg_applied then
self:CreateGuides(0)
elseif self.guides and #self.guides > 1 then
XEditorUndo:BeginOp{ name = "Created guides" }
local collection = Collection.Create()
for _, obj in ipairs(self.guides) do
obj:SetCollection(collection)
end
editor.ChangeSelWithUndoRedo(self.guides)
XEditorUndo:EndOp(table.iappend(self.guides, { collection }))
end
self.desktop:SetMouseCapture()
self.start_pos = nil
self.prg_applied = nil
self.guides = nil
ResumePassEdits("XCreateGuidesTool")
return "break"
end
return XEditorTool.OnMouseButtonUp(self, pt, button)
end
|