reuse existing static geometry

This commit is contained in:
in0finite 2022-01-24 14:21:17 +01:00
parent 25a0093c8f
commit 8e08c8b1c8
5 changed files with 187 additions and 19 deletions

View file

@ -20,9 +20,6 @@ namespace SanAndreasUnity.Behaviours.World
private MapObject[] m_cars;
private List<EntranceExitMapObject> m_enexes;
private bool m_createdStaticGeometry = false;
private bool m_initializedStaticGeometry = false;
public IReadOnlyList<int> CellIds { get; } = Enumerable.Range(0, 19).ToList();
public bool ignoreLodObjectsWhenInitializing = false;
@ -140,23 +137,136 @@ namespace SanAndreasUnity.Behaviours.World
}
public void CreateStaticGeometry ()
{
if (m_createdStaticGeometry)
return;
private static string GetKey(StaticGeometry sg)
{
return $"{sg.SerializedObjectDefinitionId}_{sg.SerializedInstancePosition}_{sg.SerializedInstanceRotation}";
}
m_createdStaticGeometry = true;
private static string GetKey(Instance inst)
{
return $"{inst.ObjectId}_{inst.Position}_{inst.Rotation}";
}
public void CreateStaticGeometry ()
{
/*var gr = Item.GetPlacements<Instance>(CellIds.ToArray())
.GroupBy(_ => $"{_.ObjectId}_{_.Position}_{_.Rotation}")
.Where(g => g.ElementAtOrDefault(1) != default)
.ToList();*/
var placements = Item.GetPlacements<Instance>(CellIds.ToArray());
m_insts = new Dictionary<Instance,StaticGeometry> (48 * 1024);
foreach (var plcm in placements) {
// find existing objects
var existingObjects = new Dictionary<string, object>(this.transform.childCount);
foreach(var sg in this.gameObject.GetFirstLevelChildrenSingleComponent<StaticGeometry>())
{
string key = GetKey(sg);
if (existingObjects.TryGetValue(key, out object obj))
{
if (obj is List<StaticGeometry> list)
{
list.Add(sg);
}
else
{
list = new List<StaticGeometry>();
list.Add(sg);
existingObjects[key] = list;
}
}
else
{
existingObjects.Add(key, sg);
}
}
Debug.Log($"Found {existingObjects.Count} existing objects");
// create new, or update existing objects
m_insts = new Dictionary<Instance, StaticGeometry> (48 * 1024);
int numObjectsReused = 0;
foreach (var plcm in placements)
{
if (this.ignoreLodObjectsWhenInitializing && plcm.IsLod)
continue;
m_insts.Add (plcm, StaticGeometry.Create ());
}
//m_insts = placements.ToDictionary(x => x, x => StaticGeometry.Create());
string key = GetKey(plcm);
if (existingObjects.TryGetValue(key, out object obj))
{
StaticGeometry sg;
if (obj is List<StaticGeometry> list)
{
sg = list.RemoveFirst();
if (list.Count == 0)
existingObjects.Remove(key);
}
else
{
sg = (StaticGeometry) obj;
existingObjects.Remove(key);
}
m_insts.Add(plcm, sg);
numObjectsReused++;
}
else
{
m_insts.Add(plcm, StaticGeometry.Create());
}
}
Debug.Log($"Reused {numObjectsReused} existing objects");
// delete unused existing objects
foreach (var pair in existingObjects)
{
if (pair.Value is List<StaticGeometry> list)
list.ForEach(sg => F.DestroyEvenInEditMode(sg.gameObject));
else
F.DestroyEvenInEditMode(((StaticGeometry)pair.Value).gameObject);
}
Debug.Log($"Deleted {existingObjects.Count} existing objects");
/*
// gather existing objects, and destroy invalid ones
var toDestroy = new List<StaticGeometry>();
foreach (var sg in this.gameObject.GetFirstLevelChildrenSingleComponent<StaticGeometry>())
{
if (sg.SerializedObjectDefinitionId <= 0
|| !placements.TryGetValue(sg.SerializedObjectDefinitionId, out var plcm)
|| (this.ignoreLodObjectsWhenInitializing && plcm.IsLod))
toDestroy.Add(sg);
else
m_insts.Add(plcm, sg);
}
toDestroy.ForEach(sg => F.DestroyEvenInEditMode(sg.gameObject));
// add new objects
foreach (var pair in placements)
{
var plcm = pair.Value;
if (this.ignoreLodObjectsWhenInitializing && plcm.IsLod)
continue;
if (!m_insts.ContainsKey(plcm))
m_insts.Add(plcm, StaticGeometry.Create());
}*/
UnityEngine.Debug.Log("Num static geometries " + m_insts.Count);
@ -171,11 +281,6 @@ namespace SanAndreasUnity.Behaviours.World
public void InitStaticGeometry ()
{
if (m_initializedStaticGeometry)
return;
m_initializedStaticGeometry = true;
foreach (var inst in m_insts)
{
var staticGeometry = inst.Value;

View file

@ -34,7 +34,22 @@ namespace SanAndreasUnity.Behaviours.World
public ISimpleObjectDefinition ObjectDefinition { get; private set; }
private bool _canLoad;
[SerializeField]
[HideInInspector]
private int m_serializedObjectDefinitionId = -1;
public int SerializedObjectDefinitionId => m_serializedObjectDefinitionId;
[SerializeField]
[HideInInspector]
private Vector3 m_serializedInstancePosition;
public Vector3 SerializedInstancePosition => m_serializedInstancePosition;
[SerializeField]
[HideInInspector]
private Quaternion m_serializedInstanceRotation;
public Quaternion SerializedInstanceRotation => m_serializedInstanceRotation;
private bool _canLoad;
private bool _isGeometryLoaded = false;
private bool _isFading;
@ -89,6 +104,10 @@ namespace SanAndreasUnity.Behaviours.World
public void Initialize(Instance inst, Dictionary<Instance, StaticGeometry> dict)
{
Instance = inst;
m_serializedObjectDefinitionId = inst.ObjectId;
m_serializedInstancePosition = inst.Position;
m_serializedInstanceRotation = inst.Rotation;
ObjectDefinition = Item.GetDefinition<Importing.Items.Definitions.ISimpleObjectDefinition>(inst.ObjectId);
if (ObjectDefinition is TimeObjectDef)
@ -104,6 +123,10 @@ namespace SanAndreasUnity.Behaviours.World
name = _canLoad ? ObjectDefinition.ModelName : string.Format("Unknown ({0})", Instance.ObjectId);
if (LodChild != null)
LodChild.LodParent = null;
LodChild = null;
if (_canLoad && Instance.LodInstance != null)
{
if (dict.TryGetValue(Instance.LodInstance, out StaticGeometry dictValue))
@ -124,6 +147,9 @@ namespace SanAndreasUnity.Behaviours.World
if (!_canLoad) return;
if (null != this.GetComponent<MeshFilter>()) // already loaded - this also works in edit mode
return;
Profiler.BeginSample ("StaticGeometry.OnLoad", this);

View file

@ -0,0 +1,21 @@
using System.Collections.Generic;
namespace SanAndreasUnity.Utilities
{
public static class CollectionExtensions
{
public static T RemoveLast<T>(this IList<T> list)
{
T lastElement = list[list.Count - 1];
list.RemoveAt(list.Count - 1);
return lastElement;
}
public static T RemoveFirst<T>(this IList<T> list)
{
T firstElement = list[0];
list.RemoveAt(0);
return firstElement;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d1a08f49d78741c47838f4a11e35ecc5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -292,6 +292,11 @@ namespace SanAndreasUnity.Utilities
return go.transform.GetFirstLevelChildren().SelectMany(c => c.GetComponents<T>());
}
public static IEnumerable<T> GetFirstLevelChildrenSingleComponent<T>(this GameObject go) where T : Component
{
return go.transform.GetFirstLevelChildren().Select(c => c.GetComponent<T>()).Where(_ => _ != null);
}
public static void SetY(this Transform t, float yPos) {
Vector3 pos = t.position;
pos.y = yPos;