| 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 |