myspace / CommonLua /Libs /Debug /FlightDebug.lua
sirnii's picture
Upload 1816 files
b6a38d7 verified
raw
history blame
20.6 kB
if not const.FlightTile then
return
end
local default_inspect = {
--show_energy_map = true,
--show_flight_map = true,
--inspect_map = true,
--show_points = true,
--inspect_spline = true,
--linelist = true,
show_splines = true,
spline_color = cyan,
spline_color_alt = blue,
}
--]]
function FlightDbgToggleMap(obj, obstacles)
local mesh = obj.dbg_flight_map_mesh
if mesh then
DoneObject(obj.dbg_flight_map_mesh)
obj.dbg_flight_map_mesh = nil
return
end
mesh = Mesh:new()
obj.dbg_flight_map_mesh = mesh
local flight_map, energy_map = FlightCreateGrids(mapdata.PassBorder)
FlightMarkObstacle(flight_map, obj)
for _, obstacle in ipairs(obstacles) do
FlightMarkObstacle(flight_map, obstacle)
end
local collected_tiles = {}
local GetHeight = terrain.GetHeight
GridForeach(flight_map, function(v, x, y)
local x, y, z = FlightToGame(x, y, v)
if z and z > GetHeight(x, y) then
local mark_color = 0x3300ffff
table.insert(collected_tiles, { x, y, z, mark_color })
end
end, 1)
if #collected_tiles ~= 0 then
table.sort(collected_tiles, function(lhs, rhs) return lhs[3] < rhs[3] end)
local v_pstr = pstr("", 1024*1024)
local mark_tile = const.FlightTile - 200
for _,tile in ipairs(collected_tiles) do
local x, y, z, c = tile[1], tile[2], tile[3], tile[4]
AppendTileVertices(v_pstr, x, y, z, mark_tile, c)
end
mesh:SetMesh(v_pstr)
mesh:SetVisible(true)
mesh:SetDepthTest(true)
mesh:SetShader(ProceduralMeshShaders.default_mesh)
mesh:SetMeshFlags(const.mfWorldSpace)
obj:Attach(mesh)
end
flight_map:free()
energy_map:free()
end
function FlightObstacle:AsyncCheatFlight()
FlightDbgToggleMap(self)
end
DbgFlightToggleFlightMap = empty_func
if not Platform.developer and not Platform.debug then
return
end
local getters = {
FlightSimMaxSpeedXY = function(self)
local a = self:GetProperty("FlightSimAttract")
local cf = self:GetProperty("FlightSimFrictionXY")
return cf > 0 and 100 * a / cf or max_int
end,
FlightSimMaxSpeedUp = function(self)
local a = self:GetProperty("FlightSimMaxLift")
local cf = self:GetProperty("FlightSimFrictionZ")
return cf > 0 and 100 * a / cf or max_int
end,
FlightSimMaxSpeedDown = function(self)
local a = self:GetProperty("FlightSimMaxWeight")
local cf = self:GetProperty("FlightSimFrictionZ")
return cf > 0 and 100 * a / cf or max_int
end,
FlightPathIntervalMinStep = function(self)
return 1000 * self:GetProperty("FlightPathStepMin") / self:GetProperty("FlightSpeedMin")
end,
FlightPathIntervalMaxStep = function(self)
return 1000 * self:GetProperty("FlightPathStepMax") / self:GetProperty("FlightSpeedMax")
end,
FlightAchievableSpeed = function(self)
local a = self:GetProperty("FlightAccelMax")
local cf = self:GetProperty("FlightFriction")
return cf > 0 and 100 * a / cf or max_int
end,
}
function getter(self, prop)
local func = getters[prop]
if func then
return func(self)
end
end
local props = {
{ category = "Flight Path", id = "FlightSimMaxSpeedXY", name = "Max Speed XY", editor = "number", default = 0, scale = guim, getter = getter, read_only = true, dont_save = true, template = true, developer = true },
{ category = "Flight Path", id = "FlightSimMaxSpeedUp", name = "Max Speed Up", editor = "number", default = 0, scale = guim, getter = getter, read_only = true, dont_save = true, template = true, developer = true },
{ category = "Flight Path", id = "FlightSimMaxSpeedDown", name = "Max Speed Down", editor = "number", default = 0, scale = guim, getter = getter, read_only = true, dont_save = true, template = true, developer = true },
{ category = "Flight", id = "FlightAchievableSpeed", name = "Achievable Speed (m/s)", editor = "number", default = 0, scale = guim, getter = getter, read_only = true, dont_save = true, template = true, developer = true },
{ category = "Flight", id = "FlightPathIntervalMinStep", name = "Path Interval Min Step (ms)", editor = "number", default = 0, getter = getter, read_only = true, dont_save = true, template = true, developer = true },
{ category = "Flight", id = "FlightPathIntervalMaxStep", name = "Path Interval Max Step (ms)", editor = "number", default = 0, getter = getter, read_only = true, dont_save = true, template = true, developer = true },
}
for _, prop in ipairs(props) do
FlyingObj["Get" .. prop.id] = getters[prop.id]
end
table.iappend(FlyingObj.properties, props)
local setter = function(self, value, prop_id, prop_meta)
self[prop_id] = value
if SelectedObj and IsKindOf(SelectedObj, "FlyingObj") then
SelectedObj[prop_id] = value
SelectedObj:RecalcFlightPath()
end
end
for _, prop_meta in ipairs(FlyingObj.properties) do
if prop_meta.sim and not prop_meta.read_only and not prop_meta.setter then
prop_meta.setter = setter
end
end
function FlyingObj:OnEditorSetProperty(prop_id, old_value, ged)
local prop_meta = self:GetPropertyMetadata(prop_id) or empty_table
if prop_meta.sim then
self:RecalcFlightPath()
end
end
----
if const.FlightDebugPath then
table.insert(FlyingObj.properties, {
category = "Flight Path", id = "FlightDebugIter", name = "Debug Iteration",
editor = "number", default = 0, min = -1, max = PropGetter("flight_path_iters"), step = 1, slider = true,
dont_save = true, buttons = {{"Recalc", "FlightDbgRecalcAction"}, {"Prev", "FlightDbgPrevAction"}, {"Next", "FlightDbgNextAction"}} })
FlyingObj.flight_debug_iter = 0
local function RecalcFlightPath(obj, step)
step = step or 0
DbgClear()
obj:SetFlightDebugIter(obj:GetFlightDebugIter() + step)
end
function FlightDbgRecalcAction(_, obj)
RecalcFlightPath(obj)
FlightDbgShow{ show_energy_map = true }
ObjModifiedDelayed(obj)
end
function FlightDbgNextAction(_, obj)
RecalcFlightPath(obj, 1)
end
function FlightDbgPrevAction(_, obj)
RecalcFlightPath(obj, -1)
end
function FlyingObj:SetFlightDebugIter(iter)
iter = iter and Clamp(iter, -1, self.flight_path_iters) or 0
self.flight_debug_iter = iter ~= 0 and iter or nil
table.change(config, "RecalcFlightPath", {
DebugFlightDisabled = true
})
self:FindFlightPath(self.flight_target, self.flight_target_range, self.flight_path_flags, self.flight_debug_iter)
table.restore(config, "RecalcFlightPath")
end
function FlyingObj:GetFlightDebugIter()
return self.flight_debug_iter
end
end -- const.FlightDebugPath
----
MapVar("DbgFlightObjs", false)
function FlightDbgAdd(obj)
DbgFlightObjs = table.create_add(DbgFlightObjs, obj)
end
function FlightDbgClear(delayed)
local objs = DbgFlightObjs
if not objs then return end
DbgFlightObjs = false
local function DeleteAll(objs)
for _, obj in ipairs(objs) do
if IsValid(obj) then
DoneObject(obj)
elseif IsValidThread(obj) then
DeleteThread(obj)
end
end
end
if delayed then
CreateRealTimeThread(DeleteAll, objs)
else
DeleteAll(objs)
end
end
function OnMsg.DbgClear() FlightDbgClear() end
function OnMsg.LoadGame() FlightDbgClear() end
local function AppendTileVerticesLinelist(v_pstr, x, y, z, mark_tile, mark_color, offset_z, get_height)
offset_z = offset_z or 0
z = z or InvalidZ
local d = mark_tile / 2
local tile_box = box(x - d, y - d, x + d, y + d)
local pts = { tile_box:ToPoints2D() }
pts[#pts + 1] = pts[1]
get_height = get_height or terrain.GetHeight
local AppendVertex = v_pstr.AppendVertex
for i=1,4 do
local x1, y1 = pts[i]:xy()
local x2, y2 = pts[i+1]:xy()
local z1 = (z == InvalidZ and get_height(x1, y1) or z) + offset_z
local z2 = (z == InvalidZ and get_height(x2, y2) or z) + offset_z
AppendVertex(v_pstr, x1, y1, z1, mark_color)
AppendVertex(v_pstr, x2, y2, z2, mark_color)
end
end
function FlightAvgSplineLen(splines)
local n = #(splines or "")
if n == 0 then return 0 end
local pt0 = splines[1][1]
local pt1 = splines[n][4]
local dist = pt0:Dist(pt1)
return dist / n
end
MapVar("FlightDbgObj", false)
MapVar("FlightDbgSaveTime", 0)
function FlightDbgSelectObj(obj)
obj = obj or FlightDbgObj
FlightDbgObj = obj
SelectObj(obj) ViewObjectRTS(obj)
end
function FlightDbgBreak(obj, err)
if err and config.DebugFlight then
FlightDbgSelectObj(obj)
obj:SetFlightFlag(const.ffpDebug, true)
if CanYield() and FlightDbgSaveTime ~= GameTime() then
FlightDbgSaveTime = GameTime()
CreateRealTimeThread(QuickSaveGame, "FlightFindPath") Sleep(1)
SetGameSpeed("pause") InterruptAdvance()
end
end
end
function FlightDbgResults(obj)
if config.DebugFlightDisabled then return end
obj = obj or FlightFrom
if not IsValid(obj) then
return
end
local splines = obj.flight_path
local status = obj.flight_path_status
local target = obj.flight_target or FlightTo
local path_errors = FlightGetErrors(status)
local dest_error = obj:GetAdjustFlightTarget() and not obj:CanFlyTo(target)
local err = path_errors or dest_error
local should_debug = err or config.DebugFlight and (table.find(Selection, obj) or IsValid(obj) and obj:GetFlightFlag(const.ffpDebug))
if not should_debug then
return
end
FlightDbgShow{ splines = splines }
ObjModifiedDelayed(obj)
if err then
if dest_error then
StoreErrorSource(obj, "Flying to a forbidden flight destination:", ValueToStr(target))
end
local path_error = path_errors and table.concat(path_errors, "', '")
if path_error then
StoreErrorSource(obj, string.format("Errors '%s' when trying to reach %s", path_error, ValueToStr(target)))
end
DbgAddSegment(obj, target, red) DbgAddCircle(target, guim/2, red) DbgAddVector(target, guim, red)
for i, spline in ipairs(splines) do
DbgAddSpline(spline, (i % 2 == 0) and cyan or blue)
end
end
FlightDbgBreak(obj, err)
end
function FlightDbgMark(ptFrom, ptTo)
local idx = FlightMarkIdx + 1
FlightMarkIdx = idx
local pt0, pt1 = ResolveVisualPos(ptFrom), ResolveVisualPos(ptTo)
local x0, y0 = GameToFlight(pt0)
local x1, y1 = GameToFlight(pt1)
local inside_from, inside_to = FlightArea:Point2DInside(x0, y0), FlightArea:Point2DInside(x1, y1)
local valid_from, valid_to = FlightMap:get(x0, y0) ~= const.FlightInvalid, FlightMap:get(x1, y1) ~= const.FlightInvalid
local inside = inside_from and inside_to
local valid = valid_from and valid_to
local color = not inside and blue or not valid and red or green
DbgClear(true)
DbgAddText(idx, pt0:AddZ(5*guim))
DbgAddVector(pt0, pt1 - pt0, yellow)
DbgAddSegment(pt0:SetInvalidZ(), pt1:SetInvalidZ(), 0xff888888)
DbgAddSegment(pt1, pt1:SetInvalidZ())
DbgAddSegment(pt0, pt0:SetInvalidZ())
DbgAddBox(boxdiag(pt0, pt1):grow(FlightMarkBorder):SetInvalidZ(), color)
if not inside or not valid then
SetGameSpeed("pause")
ViewPos(pt0)
print("Invalid flight mark", FlightMarkIdx, marked, ValueToStr(ptFrom))
assert(false, "Invalid flight mark!")
end
end
function FlightDbgShowPaths()
MapForEach("map", "FlyingObj", function(obj)
if #(obj.flight_path or "") > 0 then
_FlightDbgShow{
splines = obj.flight_path,
spline_color = RandColor(obj.handle),
spline_color_alt = false,
show_flight_map = false,
}
end
end)
end
function FlightDbgShow(params)
return DelayedCall(0, _FlightDbgShow, params)
end
function _FlightDbgShow(params)
FlightDbgClear()
params = params or {}
table.append(params, default_inspect)
if not next(params) then return end
local energy_map = FlightEnergyMin and FlightEnergy
local flight_map = FlightMap
local flight_area = FlightArea
local path_from = FlightFrom
local path_to = FlightTo
local energy_min = FlightEnergyMin
local splines, points, raw_points = params.splines, params.points, params.raw_points
local z_offset = const.FlightScale / 2
local v_pstr
local max_energy = const.FlightMaxEnergy
local FlightToGame = FlightToGame
local GetHeight = terrain.GetSurfaceHeight
local function FlightDbgAddSegment(ptA, ptB, color)
FlightDbgAdd(PlacePolyLine({ptA, ptB}, color, false))
end
local function FlightDbgAddCircle(pt, radius, color)
FlightDbgAdd(PlaceCircle(pt, radius, color, false))
end
energy_min = energy_min and ResolvePoint(FlightToGame(energy_min))
if energy_min and params.show_energy_map then
FlightDbgAddSegment(energy_min, energy_min:AddZ(10*guim), 0xffff0000)
end
local path_obj = IsValid(path_from) and path_from
path_from = path_from and ResolvePoint(path_from)
path_to = path_to and ResolvePoint(path_to)
local collected_tiles = {}
if flight_area and energy_map and params.show_energy_map then
local pts = {}
local mine, maxe = max_energy, 0
GridForeach(energy_map, flight_area, function(e, x, y)
mine, maxe = Min(mine, e), Max(maxe, e)
pts[#pts + 1] = point(x, y, e)
end, 0, max_energy - 1)
for _, pt in ipairs(pts) do
local x, y, e = pt:xyz()
local z
if flight_map then
z = flight_map:get(x, y)
x, y, z = FlightToGame(x, y, z)
else
x, y = FlightToGame(x, y)
end
z = z or GetHeight(x, y)
local mark_color = e == 0 and cyan or InterpolateRGB(red, green, e - mine, maxe - mine)
table.insert(collected_tiles, { x, y, z, mark_color })
end
elseif flight_area and flight_map and params.show_flight_map then
GridForeach(flight_map, flight_area, function(v, x, y)
local x, y, z = FlightToGame(x, y, v)
local mark_color = z and 0x3300ffff or 0x33ffff00
z = z or GetHeight(x, y)
table.insert(collected_tiles, { x, y, z, mark_color })
end, 0, const.FlightInvalid - 1)
end
if #collected_tiles ~= 0 then
table.sort(collected_tiles, function(lhs, rhs) return lhs[3] < rhs[3] end)
local v_pstr = pstr("", 1024*1024)
local __AppendTileVertices
local mark_tile
if params.linelist then
__AppendTileVertices = AppendTileVerticesLinelist
mark_tile = const.FlightTile
else
__AppendTileVertices = AppendTileVertices
mark_tile = const.FlightTile - 200
end
for _,tile in ipairs(collected_tiles) do
local x, y, z, c = tile[1], tile[2], tile[3], tile[4]
if z == GetHeight(x, y) then
z = nil
end
__AppendTileVertices(v_pstr, x, y, z, mark_tile, c, z_offset, GetHeight)
end
local mesh = Mesh:new()
mesh:SetMesh(v_pstr)
mesh:SetVisible(true)
mesh:SetDepthTest(true)
mesh:SetShader(params.linelist and ProceduralMeshShaders.mesh_linelist or ProceduralMeshShaders.default_mesh)
mesh:SetMeshFlags(const.mfWorldSpace)
mesh:SetPos(GetTerrainCursor())
FlightDbgAdd(mesh)
end
if params.inspect_map then
FlightDbgAdd(CreateRealTimeThread(function()
local emax = const.FlightMaxEnergy
local escale = const.FlightEnergyScale
while true do
DbgClear()
local pt0 = GetTerrainCursor()
local x, y = pt0:xy()
local fx, fy = GameToFlight(x, y)
local inside = not flight_area or flight_area:Point2DInside(fx, fy)
local z, e
if inside then
local pt1 = pt0
if flight_map then
z = FlightGetHeight(flight_map, flight_area, x, y)
pt1 = point(x, y, z + z_offset)
end
if pt0 ~= pt1 then
DbgAddSegment(pt0, pt1, blue)
end
if energy_map then
e = energy_map:get(fx, fy)
if e == emax then
e = nil
elseif e > 0 then
--[[
local f = point20
for dy=-1,1 do
for dx = -1,1 do
if dx ~= 0 or dy ~= 0 then
local ei = energy_map:get(fx + dx, fy + dy)
if ei ~= 0 and ei ~= emax then
local de = (e - ei) * escale
local v
if de > 0 then
v = SetLen(point(dx, dy), de)
else
v = SetLen(point(-dx, -dy), -de)
end
f = f + v
DbgAddVector(pt1, v)
end
end
end
end
DbgAddVector(pt1, f, yellow)
--]]
end
end
end
DbgAddText(string.format("z %s, e %s", tostring(z), tostring(e)), pt0)
Sleep(50)
end
end))
elseif splines and params.show_splines and params.inspect_spline then
FlightDbgAdd(CreateRealTimeThread(function()
while true do
local pt = GetTerrainCursor()
local spline
local min_dist2 = max_int
for _, spline_i in ipairs(splines) do
local pt0, pt1 = spline_i[1], spline_i[4]
local dist2 = DistSegmentToPt2D2(pt0, pt1, pt)
if dist2 < min_dist2 then
min_dist2 = dist2
spline = spline_i
end
end
DbgClear() DbgSetVectorZTest(false)
if spline then
local pt0, pt1 = spline[1], spline[4]
local dist2, x, y, z = DistSegmentToPt2D2(pt0, pt1, pt)
local k = pt0:Dist(x, y, z)
local max_k = pt0:Dist(pt1)
local x, y, z, dx, dy, dz, ddx, ddy, ddz = BS3_GetSplinePosDirCurve(spline, k, max_k)
local pt_p = point(x, y, z)
local pt_v = point(dx, dy, dz)
local pt_a = point(ddx, ddy, ddz)
local v = pt_v:Len()
local a = pt_a:Len()
local v2D = pt_v:Len2D()
local c = v2D > 0 and Cross2D(pt_v, pt_a) / v2D or 0
local pt_c = c > 0 and SetLen(point(-dy, dx, 0), c) or c < 0 and SetLen(point(dy, -dx, 0), -c) or point30
local t = atan(c, v2D) / 60
DbgAddSegment(pt_p, pt)
DbgAddSegment(pt_p, pt_p:SetTerrainZ(), 0xff666666)
DbgAddSegment(pt, pt_p:SetTerrainZ(), 0xff666666)
DbgAddVector(pt_p, pt_v, green)
DbgAddVector(pt_p, pt_a, red)
DbgAddVector(pt_p, pt_c, yellow)
DbgAddText(string.format("v %d, a %d, c %d, t %d", v, a, c, t), pt_p )
end
Sleep(50)
end
end))
end
if raw_points and #raw_points > 1 then
FlightDbgAdd(PlacePolyLine(raw_points, white, false))
end
if points and #points > 1 and params.show_points then
--local dist = points[1]:Dist2D(points[#points])
--printf("Points per meter %.3f", 1.0 * guim * #points / dist)
if path_from then
FlightDbgAddSegment(path_from, path_from:AddZ(10*guim), 0xff00ff00)
end
if path_to then
FlightDbgAddSegment(path_to, path_to:AddZ(10*guim), 0xffffff00)
end
FlightDbgAdd(PlacePolyLine(points, green, false))
local ptA, ptB
for i=2,#points-1 do
local pt = points[i]
if not flight_map then
local z = GetHeight(pt)
FlightDbgAddSegment(pt, pt:SetZ(z), 0xff666666)
else
local z = FlightGetHeight(flight_map, flight_area, pt)
FlightDbgAddSegment(pt, pt:SetZ(z), 0xff666666)
if path_obj then
local z_min = z + path_obj.FlightSimHeightMin
local z_max = z + path_obj.FlightSimHeightMax
local ptA1, ptB1 = pt:SetZ(z_max), pt:SetZ(z_min)
if ptA then
FlightDbgAddSegment(ptA, ptA1, 0xff666600)
FlightDbgAddSegment(ptB, ptB1, 0xff666600)
end
ptA, ptB = ptA1, ptB1
end
end
end
if path_obj then
FlightDbgAddCircle(points[#points], path_obj.FlightSimDecelDist, 0xff666666)
end
end
if splines and params.show_splines and #splines > 0 then
local spline_color = params.spline_color or cyan
local spline_color_alt = params.spline_color_alt or spline_color
for i, spline in ipairs(splines) do
FlightDbgAdd(PlaceSpline(spline, i % 2 == 0 and spline_color or spline_color_alt, false, guim/2))
--FlightDbgAdd(PlacePolyLine(spline, red, false))
end
end
end
function OnMsg.SelectedObjChange(obj, prev)
FlightDbgClear()
if obj and #(obj.flight_path or "") > 0 and config.DebugFlight and obj:IsKindOf("FlyingObj") then
FlightDbgShow{ splines = obj.flight_path, show_flight_map = false }
end
end
function FlightDbgTestPerformance(unit, pos, count)
unit = unit or SelectedObj
pos = pos or unit.flight_target or terrain.FindPassable(GetCursorPos())
count = count or 1000
local st = GetPreciseTicks(1000000)
table.change(config, "FlightDbgTestPerformance", {
DebugFlightDisabled = true
})
FlightDbgClear()
for i=1,count do
FlightPassVersion = false
unit:FindFlightPath(pos)
end
table.restore(config, "FlightDbgTestPerformance")
print("Avg Time:", (GetPreciseTicks(1000000) - st) / (1000.0 * count), "Dist:", unit:GetVisualDist2D(pos))
end
if FirstLoad then
DbgFlightMapThread = false
end
function DbgFlightMarkAround(pos, radius)
pos = pos or GetTerrainCursor()
radius = radius or 64*guim
FlightTimestamp = false
FlightMarkBetween(pos, pos, 0, 0, radius)
FlightDbgClear(true)
_FlightDbgShow{ show_flight_map = true }
end
function DbgFlightToggleFlightMap(radius)
local running = IsValidThread(DbgFlightMapThread)
DeleteThread(DbgFlightMapThread)
FlightDbgClear(true)
if running and not radius then return end
DbgFlightMapThread = CreateMapRealTimeThread(function()
radius = radius or 64*guim
local last_pos
while true do
local dt = 10
local pos = GetTerrainCursor():SetInvalidZ()
if last_pos ~= pos then
last_pos = pos
local st = GetPreciseTicks()
DbgFlightMarkAround(pos, radius)
dt = dt + (GetPreciseTicks() - st)
end
if WaitMsg("DbgClear", dt) then
FlightDbgClear()
break
end
end
end)
end