File size: 6,930 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 |
DefineClass.XPlaceMultipleObjectsToolBase = {
__parents = { "XEditorBrushTool" },
properties = {
{ id = "Distance", editor = "number", default = 5 * guim, scale = "m", min = guim , max = 100 * guim, step = guim / 10,
slider = true, persisted_setting = true, auto_select_all = true, sort_order = -1, },
{ id = "MinDistance", name = "Min distance", editor = "number", default = 0, scale = "m", min = 0, max = function(self) return self:GetDistance() end, step = guim / 10,
slider = true, persisted_setting = true, auto_select_all = true, sort_order = -1, },
},
deleted_objects = false,
new_objects = false,
new_positions = false,
box_changed = false,
init_terrain_type = false,
terrain_normal = false,
distance_visualization = false,
}
function XPlaceMultipleObjectsToolBase:Init()
self.distance_visualization = Mesh:new()
self.distance_visualization:SetMeshFlags(const.mfWorldSpace)
self.distance_visualization:SetShader(ProceduralMeshShaders.mesh_linelist)
self.distance_visualization:SetPos(point(0, 0))
end
function XPlaceMultipleObjectsToolBase:Done()
self.distance_visualization:delete()
end
function XPlaceMultipleObjectsToolBase:OnEditorSetProperty(prop_id, old_value, ged)
if prop_id == "Distance" then
self:SetMinDistance(Min(self:GetMinDistance(), self:GetDistance()))
end
end
function XPlaceMultipleObjectsToolBase:UpdateCursor()
local v_pstr = self:CreateCircleCursor()
local strength = self:GetCursorHeight()
if strength then
v_pstr:AppendVertex(point(0, 0, 0))
v_pstr:AppendVertex(point(0, 0, strength))
end
self.cursor_mesh:SetMeshFlags(self.cursor_default_flags + self:GetCursorExtraFlags())
local pt = self:GetWorldMousePos()
local radius = self:GetCursorRadius()
self.box_changed = box(pt:x() - radius, pt:y() - radius, pt:x() + radius, pt:y() + radius)
self.box_changed = terrain.ClampBox(self.box_changed)
local distance = self:GetDistance()
local bxDistanceGrid = self.box_changed / distance
local vpstr = pstr("")
for i = bxDistanceGrid:miny(), bxDistanceGrid:maxy() do
for j = bxDistanceGrid:minx(), bxDistanceGrid:maxx() do
local ptDistance = point(j, i)
local ptReal = ptDistance * distance
local color = self:GetCursorColor()
if ptReal:Dist(pt) <= self:GetCursorRadius() then
ptReal = ptReal:SetZ(terrain.GetHeight(ptReal))
vpstr:AppendVertex(ptReal + point(-200, -200, 0), color)
vpstr:AppendVertex(ptReal + point(200, 200, 0))
vpstr:AppendVertex(ptReal + point(-200, 200, 0), color)
vpstr:AppendVertex(ptReal + point(200, -200, 0))
end
end
end
self.distance_visualization:SetMesh(vpstr)
self.cursor_mesh:SetMesh(v_pstr)
self.cursor_mesh:SetPos(GetTerrainCursor())
end
function XPlaceMultipleObjectsToolBase:GetCursorRadius()
local radius = self:GetSize() / 2
return radius, radius
end
function XPlaceMultipleObjectsToolBase:MarkObjectsForDelete()
local radius = self:GetCursorRadius()
MapForEach(GetTerrainCursor(), radius, function(o)
local classes = self:GetClassesForDelete() or {}
if not self.deleted_objects[o] and not self.new_objects[o] and XEditorFilters:IsVisible(o) and IsKindOfClasses(o, classes)
and (not self.init_terrain_type or self.init_terrain_type[terrain.GetTerrainType(o:GetPos())])then
self.deleted_objects[o] = true
o:ClearEnumFlags(const.efVisible)
end
end)
end
function XPlaceMultipleObjectsToolBase:PlaceObjects(pt)
local newobjs = {}
local distance = self:GetDistance()
local min_distance = self:GetMinDistance()
local bxDistanceGrid = self.box_changed / distance
for i = bxDistanceGrid:miny(), bxDistanceGrid:maxy() do
for j = bxDistanceGrid:minx(), bxDistanceGrid:maxx() do
local ptDistance = point(j, i)
local ptReal = ptDistance * distance
local offset = (distance - min_distance) / 2
local randX = -offset + AsyncRand(2 * offset)
local randY = -offset + AsyncRand(2 * offset)
ptReal = ptReal + point(randX, randY)
local classes = self:GetClassesForPlace(ptReal)
local class = classes and classes[AsyncRand(#classes) + 1]
local terrainNormal, scale, scaleDeviation, angle, colorMin, colorMax = self:GetParams(ptReal)
if ptReal:InBox2D(GetMapBox()) and ptReal:Dist(pt) <= self:GetCursorRadius() and (not self.new_positions[j] or not self.new_positions[j][i]) and class and scale
and (not self.init_terrain_type or self.init_terrain_type[terrain.GetTerrainType(ptReal)]) then
local axis = terrainNormal and terrain.GetTerrainNormal(ptReal) or axis_z
local obj = XEditorPlaceObject(class)
scaleDeviation = scaleDeviation == 0 and 0 or -scaleDeviation + AsyncRand(2 * scaleDeviation)
scale = Clamp(MulDivRound(scale, 100 + scaleDeviation, 100), 10, 250)
angle = angle * 60
angle = AsyncRand(-angle, angle)
local minR, minG, minB = GetRGB(colorMin)
local maxR, maxG, maxB = GetRGB(colorMax)
minR, maxR = MinMax(minR, maxR)
minG, maxG = MinMax(minG, maxG)
minB, maxB = MinMax(minB, maxB)
local color = RGB(AsyncRand(minR, maxR), AsyncRand(minG, maxG), AsyncRand(minB, maxB))
obj:SetPos(ptReal)
obj:SetInvalidZ()
obj:SetScale(scale)
obj:SetOrientation(axis, angle)
obj:SetColorModifier(color)
obj:RestoreHierarchyEnumFlags() -- will rebuild surfaces if required
obj:SetHierarchyEnumFlags(const.efVisible)
obj:SetGameFlags(const.gofPermanent)
obj:SetCollection(Collections[editor.GetLockedCollectionIdx()])
self.new_positions[j] = self.new_positions[j] or {}
self.new_positions[j][i] = true
self.new_objects[obj] = true
newobjs[#newobjs + 1] = obj
end
end
end
Msg("EditorCallback", "EditorCallbackPlace", newobjs)
end
function XPlaceMultipleObjectsToolBase:StartDraw(pt)
SuspendPassEdits("XEditorPlaceMultipleObjects")
self.deleted_objects = {}
self.new_objects = {}
self.new_positions = {}
if terminal.IsKeyPressed(const.vkControl) then self.init_terrain_type = {[terrain.GetTerrainType(pt)] = true} end
if terminal.IsKeyPressed(const.vkAlt) then self.terrain_normal = true end
end
function XPlaceMultipleObjectsToolBase:Draw(pt1, pt2)
self:MarkObjectsForDelete()
self:PlaceObjects(pt2)
end
function XPlaceMultipleObjectsToolBase:EndDraw(pt)
local objs = table.validate(table.keys(self.deleted_objects))
for _, obj in ipairs(objs) do obj:SetEnumFlags(const.efVisible) end
XEditorUndo:BeginOp({ objects = objs, name = "Placed multiple objects" })
Msg("EditorCallback", "EditorCallbackDelete", objs)
for _, obj in ipairs(objs) do obj:delete() end
XEditorUndo:EndOp(table.keys(self.new_objects))
ResumePassEdits("XEditorPlaceMultipleObjects", true)
self.deleted_objects = false
self.new_objects = false
self.new_positions = false
self.init_terrain_type = false
self.terrain_normal = false
end
function XPlaceMultipleObjectsToolBase:GetParams()
end
function XPlaceMultipleObjectsToolBase:GetClassesForDelete()
end
function XPlaceMultipleObjectsToolBase:GetClassesForPlace(pt)
end |