|
DefineClass.StatsMarker = { |
|
__parents = { "GridMarker" }, |
|
properties = |
|
{ |
|
{ category = "Grid Marker", id = "Type", name = "Type", editor = "dropdownlist", items = PresetGroupCombo("GridMarkerType", "Default"), default = "Stats", no_edit = true }, |
|
{ category = "Marker", id = "AreaHeight", name = "Area Height", editor = "number", default = 10, help = "Defining a voxel-aligned rectangle with North-South and East-West axes" }, |
|
{ category = "Marker", id = "AreaWidth", name = "Area Width", editor = "number", default = 10, help = "Defining a voxel-aligned rectangle with North-South and East-West axes" }, |
|
{ category = "Marker", id = "Color", name = "Color", editor = "color", default = const.clrYellow}, |
|
{ category = "Marker", id = "Reachable", name = "Reachable only", editor = "bool", default = false, no_edit = true}, |
|
}, |
|
stats_text = false, |
|
vertex_weight = false, |
|
moved = false, |
|
} |
|
|
|
local stats_marker_z_offset = 5000 |
|
function StatsMarker:UpdateTextStats(obj_cnt, obj_vcnt, obj_tcnt, shadow_cnt, shadow_vcnt, shadow_tcnt, pt_cnt, pt_vcnt, pt_tcnt, sp_cnt, sp_vcnt, sp_tcnt) |
|
if not IsValid(self.stats_text) then |
|
self:DestroyAttaches("Text") |
|
local text = PlaceObject("Text") |
|
text:SetTextStyle("InfoText") |
|
text:SetShadowOffset(2) |
|
self.stats_text = text |
|
self:Attach(text) |
|
text:SetAttachOffset(0, 0, stats_marker_z_offset) |
|
end |
|
local text = "" |
|
if obj_cnt > 0 or shadow_cnt > 0 then |
|
text = string.format("Main + Shadow: %d/v%dK/t%dK\n", obj_cnt + shadow_cnt, (obj_vcnt + shadow_vcnt)/1000, (obj_tcnt + shadow_tcnt)/1000) |
|
end |
|
if pt_cnt > 0 or pt_vcnt > 0 or pt_tcnt > 0 then |
|
text = string.format("%sPoint light shadow: %d/v%dK/t%dK\n", text, pt_cnt, pt_vcnt/1000, pt_tcnt/1000) |
|
end |
|
if sp_cnt > 0 then |
|
text = string.format("%sSpot light shadow: %d/v%dK/t%dK\n", text, sp_cnt, sp_vcnt/1000, sp_tcnt/1000) |
|
end |
|
self.stats_text:SetText(text) |
|
self.stats_text:SetColor(self.Color) |
|
end |
|
|
|
function StatsMarker:GetBox() |
|
local pos = self:GetPos() |
|
local width = self.AreaWidth*const.SlabSizeX |
|
local height = self.AreaHeight*const.SlabSizeY |
|
local area_left = pos:x() - width/2 - const.SlabSizeX / 2 |
|
local area_top = pos:y() - height/2 - const.SlabSizeY / 2 |
|
return box(area_left, area_top, area_left + width, area_top + height) |
|
end |
|
|
|
local excl_classes = {"EditorMarker", "Unit", "AppearanceObject", "Light", "InvisibleObjectHelper"} |
|
function StatsMarker:VisualizeStats() |
|
if not IsEditorActive() then return end |
|
local obj_cnt, obj_vcnt, obj_tcnt, shadow_cnt, shadow_vcnt, |
|
shadow_tcnt, pt_cnt, pt_vcnt, pt_tcnt, sp_cnt, sp_vcnt, sp_tcnt = GetBoxRenderingStats(self:GetBox(), excl_classes) |
|
local vertices = (obj_vcnt + shadow_vcnt + pt_vcnt + sp_vcnt)/1000 |
|
local color |
|
if vertices < 600 then |
|
color = RGB(0, 255, 0) |
|
elseif vertices < 1000 then |
|
color = RGB(224, 224, 0) |
|
else |
|
color = RGB(255, 0, 0) |
|
end |
|
self:SetColor(color) |
|
self:UpdateTextStats(obj_cnt, obj_vcnt, obj_tcnt, shadow_cnt, shadow_vcnt, shadow_tcnt, pt_cnt, pt_vcnt, pt_tcnt, sp_cnt, sp_vcnt, sp_tcnt) |
|
if self.moved or not self.area_ground_mesh then |
|
self.moved = false |
|
self:ShowArea() |
|
end |
|
end |
|
|
|
function StatsMarker:RecalcAreaPositions() |
|
end |
|
|
|
function StatsMarker:ShowArea() |
|
local _ = self.area_ground_mesh and self.area_ground_mesh:delete() |
|
self.area_ground_mesh = PlaceTerrainBox(self:GetBox():grow(-500, -500), self.Color) |
|
end |
|
|
|
function StatsMarker:EditorCallbackMove() |
|
VoxelSnappingObj.EditorCallbackMove(self) |
|
self.moved = true |
|
self:VisualizeStats() |
|
end |
|
|
|
function StatsMarker:EditorCallbackPlace() |
|
GridMarker.EditorCallbackPlace(self) |
|
g_StatsMarkers = g_StatsMarkers or {} |
|
table.insert(g_StatsMarkers, self) |
|
self.moved = true |
|
CreateRealTimeThread(function(self) |
|
self:VisualizeStats() |
|
end, self) |
|
end |
|
|
|
function StatsMarker:EditorCallbackDelete() |
|
GridMarker.EditorCallbackDelete(self) |
|
if not g_StatsMarkers then return end |
|
table.remove_entry(g_StatsMarkers, self) |
|
end |
|
|
|
MapVar("g_StatsMarkers", false) |
|
if FirstLoad then |
|
g_StatsMarkersThread = false |
|
end |
|
|
|
function OnMsg.GameEnterEditor() |
|
if not g_StatsMarkers then |
|
g_StatsMarkers = MapGetMarkers("Stats") or false |
|
end |
|
if not g_StatsMarkersThread then |
|
g_StatsMarkersThread = CreateRealTimeThread(function() |
|
while true do |
|
Sleep(5000) |
|
if IsEditorActive() then |
|
for _, m in ipairs(g_StatsMarkers or empty_table) do |
|
m:VisualizeStats() |
|
end |
|
end |
|
end |
|
end) |
|
end |
|
end |
|
|
|
if FirstLoad then |
|
g_DbgStatsMarkersVertexSorted = false |
|
StatsMarkerDbgActionIdx = false |
|
end |
|
|
|
local half_box_size_outdoor = 8 |
|
local half_box_size_underground = 5 |
|
function PopulateMapWithStatsMarkers() |
|
g_StatsMarkers = g_StatsMarkers or {} |
|
local half_area_size = IsCurrentMapUnderground() and half_box_size_underground or half_box_size_outdoor |
|
local first_x = half_area_size*const.SlabSizeX + const.SlabSizeX/2 |
|
local max_x = terrain.GetMapWidth() |
|
local step_x = 2*half_area_size*const.SlabSizeX |
|
local first_y = half_area_size*const.SlabSizeY + const.SlabSizeY/2 |
|
local max_y = terrain.GetMapHeight() |
|
local step_y = 2*half_area_size*const.SlabSizeY |
|
g_DbgStatsMarkersVertexSorted = {} |
|
|
|
for x = first_x, max_x, step_x do |
|
for y = first_y, max_y, step_y do |
|
local sm = PlaceObject("StatsMarker") |
|
if not IsCurrentMapUnderground() then |
|
sm.AreaWidth = 2*half_box_size_outdoor |
|
sm.AreaHeight = 2*half_box_size_outdoor |
|
end |
|
sm:SetPos(x, y, const.InvalidZ) |
|
if IsEditorActive() then |
|
sm:SetHierarchyEnumFlags(const.efVisible) |
|
end |
|
table.insert(g_StatsMarkers, sm) |
|
|
|
local _, obj_vcnt, _, _, shadow_vcnt, _, _, pt_vcnt, _, _, sp_vcnt = GetBoxRenderingStats(sm:GetBox()) |
|
sm.vertex_weight = obj_vcnt + shadow_vcnt + pt_vcnt + sp_vcnt |
|
table.insert(g_DbgStatsMarkersVertexSorted, sm) |
|
if IsEditorActive() then |
|
sm:VisualizeStats() |
|
end |
|
end |
|
end |
|
table.sortby_field(g_DbgStatsMarkersVertexSorted, "vertex_weight") |
|
PrintLightsTotalStats() |
|
end |
|
|
|
function PrintLightsTotalStats() |
|
local _, _, _, _, _, _, pt_lights, _, _, sp_lights = GetBoxRenderingStats(GetMapBox()) |
|
print(string.format("Point Lights: %d, Spot Lights: %d, Total Lights: %d", pt_lights, sp_lights, pt_lights + sp_lights)) |
|
end |
|
|
|
function DeleteAllStatsMarkers() |
|
for _, m in ipairs(g_StatsMarkers or empty_table) do |
|
DoneObject(m) |
|
end |
|
g_StatsMarkers = false |
|
g_DbgStatsMarkersVertexSorted = false |
|
end |
|
|
|
local view_stats_marker_dist = 30000 |
|
function ViewHeaviestStatsMarker(rank) |
|
if not g_DbgStatsMarkersVertexSorted then return end |
|
ViewObject(g_DbgStatsMarkersVertexSorted[#g_DbgStatsMarkersVertexSorted - rank + 1], view_stats_marker_dist) |
|
end |
|
|
|
function StatsMarkerDebugNext() |
|
if not StatsMarkerDbgActionIdx then |
|
PopulateMapWithStatsMarkers() |
|
StatsMarkerDbgActionIdx = 1 |
|
elseif StatsMarkerDbgActionIdx == 5 then |
|
DeleteAllStatsMarkers() |
|
StatsMarkerDbgActionIdx = false |
|
else |
|
ViewHeaviestStatsMarker(StatsMarkerDbgActionIdx) |
|
StatsMarkerDbgActionIdx = StatsMarkerDbgActionIdx + 1 |
|
end |
|
end |
|
|
|
function OnMsg.NewMapLoaded() |
|
StatsMarkerDbgActionIdx = false |
|
end |
|
|
|
local underground_maps = { |
|
"H-3 - Bunker FB45-68", |
|
"L-6U - Underground Prison", |
|
} |
|
function IsCurrentMapUnderground() |
|
return table.find(underground_maps, mapdata.id) |
|
end |