using UnityEngine; using System.Collections; using System.Collections.Generic; using System; using Mapbox.Map; using Mapbox.Unity.MeshGeneration.Modifiers; using Mapbox.Unity.MeshGeneration.Data; using Mapbox.Unity.MeshGeneration.Components; namespace Mapbox.Unity.MeshGeneration.Modifiers { /// /// Merged Modifier Stack, just like regular Modifier stack, creates a game object from features. But the difference is, regular modifier stack creates a game object for each given faeture meanwhile Merged Modifier Stack merges meshes and creates one game object for all features (until the 65k vertex limit). /// It has extremely higher performance compared to regular modifier stack but since it merged all entities together, it also loses all individual entity data & makes it harder to interact with them. /// It pools and merges objects based on the tile contains them. /// [CreateAssetMenu(menuName = "Mapbox/Modifiers/Merged Modifier Stack")] public class MergedModifierStack : ModifierStackBase { private Dictionary _cacheVertexCount = new Dictionary(); private Dictionary> _cached = new Dictionary>(); private Dictionary _buildingCount = new Dictionary(); private Dictionary> _activeObjects = new Dictionary>(); private MeshData _tempMeshData; private MeshFilter _tempMeshFilter; private GameObject _tempGameObject; private VectorEntity _tempVectorEntity; private MeshData _temp2MeshData; private ObjectPool _pool; private ObjectPool> _listPool; private ObjectPool> _meshDataPool; private int _counter, _counter2; protected virtual void OnEnable() { //we'll use this to concat building data until it reaches 65000 verts _pool = new ObjectPool(() => { _tempGameObject = new GameObject(); _tempMeshFilter = _tempGameObject.AddComponent(); _tempMeshFilter.sharedMesh = new Mesh(); _tempVectorEntity = new VectorEntity() { GameObject = _tempGameObject, Transform = _tempGameObject.transform, MeshFilter = _tempMeshFilter, MeshRenderer = _tempGameObject.AddComponent(), Mesh = _tempMeshFilter.sharedMesh }; return _tempVectorEntity; }); _listPool = new ObjectPool>(() => { return new List(); }); _meshDataPool = new ObjectPool>(() => { return new List(); }); _tempMeshData = new MeshData(); } public override void OnUnregisterTile(UnityTile tile) { //removing all caches if (_activeObjects.ContainsKey(tile)) { _counter = _activeObjects[tile].Count; for (int i = 0; i < _counter; i++) { if (null != _activeObjects[tile][i].GameObject) { _activeObjects[tile][i].GameObject.SetActive(false); } _pool.Put(_activeObjects[tile][i]); } _activeObjects[tile].Clear(); //pooling these lists as they'll reused anyway, saving hundreds of list instantiations _listPool.Put(_activeObjects[tile]); _activeObjects.Remove(tile); } //reset all counters if (_cacheVertexCount.ContainsKey(tile)) { _cacheVertexCount.Remove(tile); } if (_cached.ContainsKey(tile)) { _cached[tile].Clear(); _meshDataPool.Put(_cached[tile]); _cached.Remove(tile); } if (_buildingCount.ContainsKey(tile)) { _buildingCount.Remove(tile); } } public override void Initialize() { base.Initialize(); //init is also used for reloading map/ location change, so reseting everything here _cacheVertexCount.Clear(); _cached.Clear(); _buildingCount.Clear(); _pool.Clear(); _counter = MeshModifiers.Count; for (int i = 0; i < _counter; i++) { MeshModifiers[i].Initialize(); } _counter = GoModifiers.Count; for (int i = 0; i < _counter; i++) { GoModifiers[i].Initialize(); } } public override GameObject Execute(UnityTile tile, VectorFeatureUnity feature, MeshData meshData, GameObject parent = null, string type = "") { base.Execute(tile, feature, meshData, parent, type); if (!_cacheVertexCount.ContainsKey(tile)) { _cacheVertexCount.Add(tile, 0); _cached.Add(tile, _meshDataPool.GetObject()); _buildingCount.Add(tile, 0); } _buildingCount[tile]++; _counter = MeshModifiers.Count; for (int i = 0; i < _counter; i++) { if (MeshModifiers[i] != null && MeshModifiers[i].Active) { MeshModifiers[i].Run(feature, meshData, tile); } } GameObject go = null; //65000 is the vertex limit for meshes, keep stashing it until that _counter = meshData.Vertices.Count; if (_cacheVertexCount[tile] + _counter < 65000) { _cacheVertexCount[tile] += _counter; _cached[tile].Add(meshData); } else { go = End(tile, parent, type); } return go; } public GameObject End(UnityTile tile, GameObject parent, string name = "") { var c2 = 0; if (_cached.ContainsKey(tile)) { _tempMeshData.Clear(); //concat mesh data into _tempMeshData _counter = _cached[tile].Count; for (int i = 0; i < _counter; i++) { _temp2MeshData = _cached[tile][i]; if (_temp2MeshData.Vertices.Count <= 3) continue; var st = _tempMeshData.Vertices.Count; _tempMeshData.Vertices.AddRange(_temp2MeshData.Vertices); _tempMeshData.Normals.AddRange(_temp2MeshData.Normals); c2 = _temp2MeshData.UV.Count; for (int j = 0; j < c2; j++) { if (_tempMeshData.UV.Count <= j) { _tempMeshData.UV.Add(new List(_temp2MeshData.UV[j].Count)); } } c2 = _temp2MeshData.UV.Count; for (int j = 0; j < c2; j++) { _tempMeshData.UV[j].AddRange(_temp2MeshData.UV[j]); } c2 = _temp2MeshData.Triangles.Count; for (int j = 0; j < c2; j++) { if (_tempMeshData.Triangles.Count <= j) { _tempMeshData.Triangles.Add(new List(_temp2MeshData.Triangles[j].Count)); } } for (int j = 0; j < c2; j++) { for (int k = 0; k < _temp2MeshData.Triangles[j].Count; k++) { _tempMeshData.Triangles[j].Add(_temp2MeshData.Triangles[j][k] + st); } } } //update pooled vector entity with new data if (_tempMeshData.Vertices.Count > 3) { _cached[tile].Clear(); _cacheVertexCount[tile] = 0; _tempVectorEntity = null; _tempVectorEntity = _pool.GetObject(); _tempVectorEntity.GameObject.SetActive(true); _tempVectorEntity.Mesh.Clear(); _tempVectorEntity.GameObject.name = name; _tempVectorEntity.Mesh.subMeshCount = _tempMeshData.Triangles.Count; _tempVectorEntity.Mesh.SetVertices(_tempMeshData.Vertices); _tempVectorEntity.Mesh.SetNormals(_tempMeshData.Normals); _counter = _tempMeshData.Triangles.Count; for (int i = 0; i < _counter; i++) { _tempVectorEntity.Mesh.SetTriangles(_tempMeshData.Triangles[i], i); } _counter = _tempMeshData.UV.Count; for (int i = 0; i < _counter; i++) { _tempVectorEntity.Mesh.SetUVs(i, _tempMeshData.UV[i]); } _tempVectorEntity.GameObject.transform.SetParent(tile.transform, false); if (!_activeObjects.ContainsKey(tile)) { _activeObjects.Add(tile, _listPool.GetObject()); } _activeObjects[tile].Add(_tempVectorEntity); _counter = GoModifiers.Count; for (int i = 0; i < _counter; i++) { if (GoModifiers[i].Active) { GoModifiers[i].Run(_tempVectorEntity, tile); } } return _tempVectorEntity.GameObject; } } return null; } public override void Clear() { foreach (var vectorEntity in _pool.GetQueue()) { if (vectorEntity.Mesh != null) { vectorEntity.Mesh.Destroy(true); } vectorEntity.GameObject.Destroy(); } foreach (var tileTuple in _activeObjects) { foreach (var vectorEntity in tileTuple.Value) { if (vectorEntity.Mesh != null) { vectorEntity.Mesh.Destroy(true); } vectorEntity.GameObject.Destroy(); } } _pool.Clear(); _activeObjects.Clear(); _pool.Clear(); } } }