namespace Mapbox.Unity.MeshGeneration.Modifiers { using UnityEngine; using System.Collections.Generic; using Mapbox.Unity.MeshGeneration.Data; using Mapbox.Unity.MeshGeneration.Components; using System; public enum PositionTargetType { TileCenter, FirstVertex, CenterOfVertices } /// /// Modifier Stacks /// Modifier Stack can be thought as styles as as they contain all the data/settings for how the feature will be visualized. /// They also create the game objects in default implementations in the sdk. /// Currently there's two implementations of this; Modifier Stack and Merged Modifier Stack.They work almost exactly same /// (logically) with one difference; modifier stacks creates a game object for each feature while merged modifier stack, /// merges them up as the name suggest and create one game object for multiple(as many as possible) features.Both have /// their advantages but the main factor here is the performance.Regular modifier stack creates individual game object so /// it's easier to interact, move, animate etc features.But if you want to visualize whole San Francisco, that would mean /// just 200k-300k buildings which would hit performance really hard. In such a case, especially if you don't need /// individual interaction or something, you can use merged modifier stack, which will probably be able to create whole /// SF around a few hundred game objects. /// They contain two lists; mesh modifier list and game object modifier list.These modifiers are used to create and /// decorate game objects. /// Mesh modifiers generate data required for the game objects mesh. I.e.polygon mesh modifier triangulates the polygn, /// height modifier extrudes the polygon and adds volume etc, uv modifier changes UV mapping etc. /// Game object modifiers decorate created game objects, like settings material, interaction scripts, animations etc. /// i.e.Material modifier sets materials to mesh and submeshes, highlight modifier adds mouse highlight to features, /// feature behaviour adds a script to keep feature data on game objects etc. /// So the idea here is; run all mesh modifiers first, generate all the data required for mesh.Create game object /// using that mesh data.Run all game object modifiers to decorate that game object. /// [CreateAssetMenu(menuName = "Mapbox/Modifiers/Modifier Stack")] public class ModifierStack : ModifierStackBase { [SerializeField] public PositionTargetType moveFeaturePositionTo; [NonSerialized] private int vertexIndex = 1; [NonSerialized] private Dictionary> _activeObjects; [NonSerialized] private ObjectPool _pool; [NonSerialized] private Vector3 _tempPoint; [NonSerialized] private VectorEntity _tempVectorEntity; [NonSerialized] private ObjectPool> _listPool; [NonSerialized] private int _counter; [NonSerialized] private int _secondCounter; protected virtual void OnEnable() { _pool = new ObjectPool(() => { var go = new GameObject(); var mf = go.AddComponent(); mf.sharedMesh = new Mesh(); mf.sharedMesh.name = "feature"; var mr = go.AddComponent(); _tempVectorEntity = new VectorEntity() { GameObject = go, Transform = go.transform, MeshFilter = mf, MeshRenderer = mr, Mesh = mf.sharedMesh }; return _tempVectorEntity; }); _listPool = new ObjectPool>(() => { return new List(); }); _activeObjects = new Dictionary>(); } public override void OnUnregisterTile(UnityTile tile) { if (_activeObjects.ContainsKey(tile)) { _counter = _activeObjects[tile].Count; for (int i = 0; i < _counter; i++) { foreach (var item in GoModifiers) { item.OnPoolItem(_activeObjects[tile][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); } } public override void Initialize() { base.Initialize(); _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 = "") { _counter = feature.Points.Count; _secondCounter = 0; if (moveFeaturePositionTo != PositionTargetType.TileCenter) { _tempPoint = Constants.Math.Vector3Zero; if (moveFeaturePositionTo == PositionTargetType.FirstVertex) { _tempPoint = feature.Points[0][0]; } else if (moveFeaturePositionTo == PositionTargetType.CenterOfVertices) { //this is not precisely the center because of the duplicates (first/last vertex) but close to center _tempPoint = feature.Points[0][0]; vertexIndex = 1; for (int i = 0; i < _counter; i++) { _secondCounter = feature.Points[i].Count; for (int j = 0; j < _secondCounter; j++) { _tempPoint += feature.Points[i][j]; vertexIndex++; } } _tempPoint /= vertexIndex; } for (int i = 0; i < _counter; i++) { _secondCounter = feature.Points[i].Count; for (int j = 0; j < _secondCounter; j++) { feature.Points[i][j] = new Vector3(feature.Points[i][j].x - _tempPoint.x, 0, feature.Points[i][j].z - _tempPoint.z); } } meshData.PositionInTile = _tempPoint; } meshData.PositionInTile = _tempPoint; _counter = MeshModifiers.Count; for (int i = 0; i < _counter; i++) { if (MeshModifiers[i] != null && MeshModifiers[i].Active) { MeshModifiers[i].Run(feature, meshData, tile); } } _tempVectorEntity = _pool.GetObject(); // It is possible that we changed scenes in the middle of map generation. // This object can be null as a result of Unity cleaning up game objects in the scene. // Let's bail if we don't have our object. if (_tempVectorEntity.GameObject == null) { return null; } _tempVectorEntity.GameObject.SetActive(true); _tempVectorEntity.Mesh.Clear(); _tempVectorEntity.Feature = feature; #if UNITY_EDITOR if (feature.Data != null) { _tempVectorEntity.GameObject.name = type + " - " + feature.Data.Id; } else { _tempVectorEntity.GameObject.name = type; } #endif _tempVectorEntity.Mesh.subMeshCount = meshData.Triangles.Count; _tempVectorEntity.Mesh.SetVertices(meshData.Vertices); _tempVectorEntity.Mesh.SetNormals(meshData.Normals); if (meshData.Tangents.Count > 0) { _tempVectorEntity.Mesh.SetTangents(meshData.Tangents); } _counter = meshData.Triangles.Count; for (int i = 0; i < _counter; i++) { _tempVectorEntity.Mesh.SetTriangles(meshData.Triangles[i], i); } _counter = meshData.UV.Count; for (int i = 0; i < _counter; i++) { _tempVectorEntity.Mesh.SetUVs(i, meshData.UV[i]); } _tempVectorEntity.Transform.SetParent(parent.transform, false); if (!_activeObjects.ContainsKey(tile)) { _activeObjects.Add(tile, _listPool.GetObject()); } _activeObjects[tile].Add(_tempVectorEntity); _tempVectorEntity.Transform.localPosition = meshData.PositionInTile; _counter = GoModifiers.Count; for (int i = 0; i < _counter; i++) { if (GoModifiers[i].Active) { GoModifiers[i].Run(_tempVectorEntity, tile); } } return _tempVectorEntity.GameObject; } 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(); } } }