Skip to content

Commit 4c89dab

Browse files
committed
fix: generate missing catalog thumbnails, reduce memory leaked
Some objects do legitimately have blank thumbnails, but others have the game generate them (such as the toilet).
1 parent 67086f0 commit 4c89dab

10 files changed

Lines changed: 138 additions & 985 deletions

File tree

TSOClient/FSO.IDE/ClassDiagram1.cd

Lines changed: 0 additions & 977 deletions
This file was deleted.

TSOClient/FSO.IDE/Common/UIInteractiveDGRP.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using FSO.Client.UI.Framework;
33
using FSO.Common.Rendering.Framework.Model;
44
using FSO.Files.Formats.IFF.Chunks;
5+
using FSO.LotView;
56
using FSO.LotView.Components;
67
using FSO.LotView.Model;
78
using FSO.SimAntics;

TSOClient/FSO.IDE/Common/UIThumbnailRenderer.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
using FSO.SimAntics;
33
using FSO.SimAntics.Entities;
44
using Microsoft.Xna.Framework.Graphics;
5-
using System;
65
using FSO.Common.Rendering.Framework.Model;
76
using FSO.Client;
87
using FSO.SimAntics.NetPlay.Drivers;
@@ -11,6 +10,7 @@
1110
using FSO.LotView.Components;
1211
using Microsoft.Xna.Framework;
1312
using FSO.SimAntics.Engine.TSOTransaction;
13+
using FSO.LotView;
1414

1515
using Color = Microsoft.Xna.Framework.Color;
1616

TSOClient/FSO.UI/Utils/CatThumbGenerator.cs

Lines changed: 68 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,56 @@
11
using FSO.Client;
22
using FSO.Common.Utils;
3+
using FSO.LotView;
34
using FSO.LotView.Components;
5+
using FSO.LotView.Model;
46
using FSO.SimAntics;
7+
using FSO.SimAntics.Engine.TSOTransaction;
58
using FSO.SimAntics.Entities;
9+
using FSO.SimAntics.Model;
10+
using FSO.SimAntics.NetPlay.Drivers;
611
using Microsoft.Xna.Framework;
712
using Microsoft.Xna.Framework.Graphics;
8-
using System;
913

1014
namespace FSO.UI.Utils
1115
{
1216
public static class CatThumbGenerator
1317
{
18+
private static VM ThumbVM;
19+
20+
private static VM GetThumbVM()
21+
{
22+
if (ThumbVM == null)
23+
{
24+
var world = new ExternalWorld(GameFacade.GraphicsDevice);
25+
world.Initialize(GameFacade.Scenes);
26+
var context = new VMContext(world);
27+
28+
ThumbVM = new VM(context, new VMServerDriver(new VMTSOGlobalLinkStub()), new VMNullHeadlineProvider());
29+
ThumbVM.Init();
30+
31+
var blueprint = new Blueprint(1, 1)
32+
{
33+
Light =
34+
[
35+
new RoomLighting() { OutsideLight = 100 },
36+
new RoomLighting() { OutsideLight = 100 },
37+
new RoomLighting() { OutsideLight = 100 },
38+
],
39+
OutsideColor = Color.White
40+
};
41+
blueprint.GenerateRoomLights();
42+
blueprint.RoomColors[2].A /= 2;
43+
world.State.AmbientLight.SetData(blueprint.RoomColors);
44+
world.State.OutsidePx.SetData([Color.White]);
45+
46+
world.InitBlueprint(blueprint);
47+
context.Blueprint = blueprint;
48+
context.Architecture = new VMArchitecture(1, 1, blueprint, ThumbVM.Context);
49+
}
50+
51+
return ThumbVM;
52+
}
53+
1454
public static Texture2D GenerateThumb(VMMultitileGroup obj, VM vm)
1555
{
1656
var gd = GameFacade.GraphicsDevice;
@@ -34,7 +74,15 @@ public static Texture2D GenerateThumb(VMMultitileGroup obj, VM vm)
3474
var oldRts = gd.GetRenderTargets();
3575
gd.SetRenderTarget(result);
3676
gd.Clear(Color.Black);
37-
sb.Begin(blendState: BlendState.AlphaBlend);
77+
var sampler = new SamplerState()
78+
{
79+
AddressU = TextureAddressMode.Clamp,
80+
AddressV = TextureAddressMode.Clamp,
81+
AddressW = TextureAddressMode.Clamp,
82+
Filter = TextureFilter.Linear,
83+
MipMapLevelOfDetailBias = -0.5f,
84+
};
85+
sb.Begin(blendState: BlendState.NonPremultiplied, samplerState: sampler);
3886
var minScale = Math.Min(37f/newAgain.Width, 37f/newAgain.Height);
3987
if (minScale > 1) minScale = 1;
4088
var rect = new Rectangle(
@@ -56,5 +104,23 @@ public static Texture2D GenerateThumb(VMMultitileGroup obj, VM vm)
56104
newAgain.Dispose();
57105
return result;
58106
}
107+
108+
public static Texture2D GenerateThumb(uint guid)
109+
{
110+
var vm = GetThumbVM();
111+
112+
var obj = vm.Context.CreateObjectInstance(guid, LotTilePos.OUT_OF_WORLD, Direction.NORTH, true);
113+
114+
if (obj == null)
115+
{
116+
return null;
117+
}
118+
119+
var icon = GenerateThumb(obj, vm);
120+
121+
obj.Delete(vm.Context);
122+
123+
return icon;
124+
}
59125
}
60126
}

TSOClient/tso.client/UI/Controls/Catalog/UICatalog.cs

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
using FSO.Content.Interfaces;
1212
using FSO.Client.UI.Panels;
1313
using System.Text.RegularExpressions;
14+
using FSO.UI.Utils;
1415

1516
namespace FSO.Client.UI.Controls.Catalog
1617
{
@@ -428,19 +429,44 @@ void InnerSelect(UIElement button)
428429
public Texture2D GetObjIcon(uint GUID)
429430
{
430431
if (!IconCache.ContainsKey(GUID)) {
431-
var obj = Content.Content.Get().WorldObjects.Get(GUID);
432+
var objs = Content.Content.Get().WorldObjects;
433+
var obj = objs.Get(GUID);
432434
if (obj == null)
433435
{
434436
IconCache[GUID] = null;
435437
return null;
436438
}
437439
var bmp = obj.Resource.Get<BMP>(obj.OBJ.CatalogStringsID);
438-
if (bmp != null) IconCache[GUID] = bmp.GetTexture(GameFacade.GraphicsDevice);
439-
else IconCache[GUID] = null;
440+
441+
if (bmp != null)
442+
{
443+
var result = bmp.GetTexture(GameFacade.GraphicsDevice);
444+
result.Tag = this; // We can dispose this texture later.
445+
IconCache[GUID] = result;
446+
}
447+
else
448+
{
449+
IconCache[GUID] = objs.GetOrAddGeneratedIcon(GUID, () => CatThumbGenerator.GenerateThumb(GUID));
450+
}
440451
}
441452
return IconCache[GUID];
442453
}
443454

455+
public override void Removed()
456+
{
457+
foreach (var entry in IconCache.Values)
458+
{
459+
if (entry.Tag == this)
460+
{
461+
entry.Dispose();
462+
}
463+
}
464+
465+
IconCache.Clear();
466+
467+
base.Removed();
468+
}
469+
444470
private class CatalogSorter : IComparer<UICatalogElement>
445471
{
446472
#region IComparer<UICatalogElement> Members

TSOClient/tso.client/UI/Controls/UIInteraction.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,5 +142,16 @@ public override Rectangle GetBounds()
142142
{
143143
return new Rectangle(0, 0, ClickHandler.Region.Width, ClickHandler.Region.Height);
144144
}
145+
146+
public override void Removed()
147+
{
148+
base.Removed();
149+
150+
if (Icon?.Tag == this)
151+
{
152+
Icon.Dispose();
153+
Icon = null;
154+
}
155+
}
145156
}
146157
}

TSOClient/tso.client/UI/Panels/UIInteractionQueue.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
using FSO.SimAntics.NetPlay.Model.Commands;
1111
using FSO.Client.UI.Controls;
1212
using FSO.Common;
13+
using FSO.UI.Utils;
1314

1415
namespace FSO.Client.UI.Panels
1516
{
@@ -244,6 +245,17 @@ public void Update()
244245
public void UpdateInteractionIcon()
245246
{
246247
UI.Icon = IconOwner?.GetIcon(GameFacade.GraphicsDevice, 0);
248+
249+
if (UI.Icon == null)
250+
{
251+
uint guid = IconOwner.GroupDefinition.GUID;
252+
UI.Icon = Content.Content.Get().WorldObjects.GetOrAddGeneratedIcon(guid, () => CatThumbGenerator.GenerateThumb(guid));
253+
}
254+
else
255+
{
256+
// This lets the UI know it can delete the texture when it's removed.
257+
UI.Icon.Tag = UI;
258+
}
247259
}
248260

249261
public void UpdateInteractionResult()

TSOClient/tso.content/Interfaces/AbstractObjectProvider.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using FSO.Common.Utils;
33
using FSO.Files.Formats.IFF;
44
using FSO.Files.Formats.IFF.Chunks;
5+
using Microsoft.Xna.Framework.Graphics;
56
using System;
67
using System.Collections.Generic;
78
using System.IO;
@@ -18,6 +19,8 @@ public abstract class AbstractObjectProvider : IContentProvider<GameObject>
1819
public Dictionary<ulong, GameObjectCatalogEnrich> CatalogEnrich = new Dictionary<ulong, GameObjectCatalogEnrich>();
1920
public List<GameObjectReference> ControllerObjects = new List<GameObjectReference>();
2021

22+
public Dictionary<ulong, Texture2D> IconCache = [];
23+
2124
public AbstractObjectProvider(Content contentManager)
2225
{
2326
this.ContentManager = contentManager;
@@ -204,5 +207,16 @@ public GameObject Get(string name)
204207
if (guid == 0) return null;
205208
return Get(guid);
206209
}
210+
211+
public Texture2D GetOrAddGeneratedIcon(uint guid, Func<Texture2D> generator)
212+
{
213+
if (!IconCache.TryGetValue(guid, out Texture2D result))
214+
{
215+
result = generator();
216+
IconCache[guid] = result;
217+
}
218+
219+
return result;
220+
}
207221
}
208222
}

TSOClient/tso.simantics/Entities/VMEntity.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ public virtual void SetAttribute(int index, short value)
120120
public VMEntityTuning TuningReplacement;
121121
public bool Portal => EntryPoints[15].ActionFunction != 0;
122122
public bool Window => ((VMEntityFlags2)GetValue(VMStackObjectVariable.FlagField2)).HasFlag(VMEntityFlags2.ArchitectualWindow);
123+
public OBJD GroupDefinition => MasterDefinition ?? Object.OBJ;
123124
public virtual bool MovesOften
124125
{
125126
get
Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
using FSO.Common;
22
using FSO.Common.Rendering.Framework;
33
using FSO.Common.Utils;
4-
using FSO.LotView;
54
using FSO.LotView.Utils;
65
using Microsoft.Xna.Framework.Graphics;
76

8-
namespace FSO.IDE.Common
7+
namespace FSO.LotView
98
{
109
public class ExternalWorld : World
1110
{

0 commit comments

Comments
 (0)