using System.Collections.Generic; using UnityEngine; using Mapbox.Unity.MeshGeneration.Enums; using Mapbox.Unity.MeshGeneration.Data; using Mapbox.Unity.MeshGeneration.Interfaces; using Mapbox.Map; using Mapbox.Unity.Map; using System; namespace Mapbox.Unity.MeshGeneration.Factories { /// /// Vector Tile Factory /// Vector data is much more detailed compared to terrain and image data so we have a different structure to process /// vector data(compared to other factories). First of all, how does the vector data itself structured? Vector tile /// data contains 'vector layers' as immediate children.And then each of these vector layers contains a number of /// 'features' inside.I.e.vector data for a tile has 'building', 'road', 'landuse' etc layers. Then building layer /// has a number of polygon features, road layer has line features etc. /// Similar to this, vector tile factory contains bunch of 'layer visualizers' and each one of them corresponds to /// one (or more) vector layers in data.So when data is received, factory goes through all layers inside and passes /// them to designated layer visualizers.We're using layer name as key here, to find the designated layer visualizer, /// like 'building', 'road'. (vector tile factory visual would help here). If it can't find a layer visualizer for /// that layer, it'll be skipped and not processed at all.If all you need is 1-2 layers, it's indeed a big waste to /// pull whole vector data and you can use 'Style Optimized Vector Tile Factory' to pull only the layer you want to use. /// //[CreateAssetMenu(menuName = "Mapbox/Factories/Vector Tile Factory")] public class VectorTileFactory : AbstractTileFactory { #region Private/Protected Fields private Dictionary> _layerBuilder; private VectorLayerProperties _properties; private Dictionary> _layerProgress; protected VectorDataFetcher DataFetcher; #endregion #region Properties public string TilesetId { get { return _properties.sourceOptions.Id; } set { _properties.sourceOptions.Id = value; } } public VectorLayerProperties Properties { get { return _properties; } } #endregion #region Public Methods public void RedrawSubLayer(UnityTile tile, LayerVisualizerBase visualizer) { CreateFeatureWithBuilder(tile, visualizer.SubLayerProperties.coreOptions.layerName, visualizer); } public void UnregisterLayer(UnityTile tile, LayerVisualizerBase visualizer) { if (_layerProgress.ContainsKey(tile)) { _layerProgress.Remove(tile); } if (_tilesWaitingProcessing.Contains(tile)) { _tilesWaitingProcessing.Remove(tile); } if (visualizer != null) { visualizer.UnregisterTile(tile); } } #endregion #region Public Layer Operation Api Methods for public virtual LayerVisualizerBase AddVectorLayerVisualizer(VectorSubLayerProperties subLayer) { //if its of type prefabitemoptions then separate the visualizer type LayerVisualizerBase visualizer = CreateInstance(); //TODO : FIX THIS !! visualizer.LayerVisualizerHasChanged += UpdateTileFactory; // Set honorBuildingSettings - need to set here in addition to the UI. // Not setting it here can lead to wrong filtering. bool isPrimitiveTypeValidForBuidingIds = (subLayer.coreOptions.geometryType == VectorPrimitiveType.Polygon) || (subLayer.coreOptions.geometryType == VectorPrimitiveType.Custom); bool isSourceValidForBuildingIds = _properties.sourceType != VectorSourceType.MapboxStreets; subLayer.honorBuildingIdSetting = isPrimitiveTypeValidForBuidingIds && isSourceValidForBuildingIds; // Setup visualizer. ((VectorLayerVisualizer)visualizer).SetProperties(subLayer); visualizer.Initialize(); if (visualizer == null) { return visualizer; } if (_layerBuilder.ContainsKey(visualizer.Key)) { _layerBuilder[visualizer.Key].Add(visualizer); } else { _layerBuilder.Add(visualizer.Key, new List { visualizer }); } return visualizer; } public virtual LayerVisualizerBase AddPOIVectorLayerVisualizer(PrefabItemOptions poiSubLayer) { LayerVisualizerBase visualizer = CreateInstance(); poiSubLayer.performanceOptions = _properties.performanceOptions; ((LocationPrefabsLayerVisualizer)visualizer).SetProperties((PrefabItemOptions)poiSubLayer); visualizer.LayerVisualizerHasChanged += UpdateTileFactory; visualizer.Initialize(); if (visualizer == null) { return null; } if (_layerBuilder.ContainsKey(visualizer.Key)) { _layerBuilder[visualizer.Key].Add(visualizer); } else { _layerBuilder.Add(visualizer.Key, new List() { visualizer }); } return visualizer; } public virtual LayerVisualizerBase FindVectorLayerVisualizer(VectorSubLayerProperties subLayer) { if (_layerBuilder.ContainsKey(subLayer.Key)) { var visualizer = _layerBuilder[subLayer.Key].Find((obj) => obj.SubLayerProperties == subLayer); return visualizer; } return null; } public virtual void RemoveVectorLayerVisualizer(LayerVisualizerBase subLayer) { subLayer.Clear(); if (_layerBuilder.ContainsKey(subLayer.Key)) { if (Properties.vectorSubLayers.Contains(subLayer.SubLayerProperties)) { Properties.vectorSubLayers.Remove(subLayer.SubLayerProperties); } else if (subLayer.SubLayerProperties is PrefabItemOptions && Properties.locationPrefabList.Contains(subLayer.SubLayerProperties as PrefabItemOptions)) { Properties.locationPrefabList.Remove(subLayer.SubLayerProperties as PrefabItemOptions); } subLayer.LayerVisualizerHasChanged -= UpdateTileFactory; subLayer.UnbindSubLayerEvents(); _layerBuilder[subLayer.Key].Remove(subLayer); } } #endregion #region AbstractFactoryOverrides /// /// Set up sublayers using VectorLayerVisualizers. /// protected override void OnInitialized() { _layerProgress = new Dictionary>(); _layerBuilder = new Dictionary>(); DataFetcher = ScriptableObject.CreateInstance(); DataFetcher.DataRecieved += OnVectorDataRecieved; DataFetcher.FetchingError += OnDataError; CreatePOILayerVisualizers(); CreateLayerVisualizers(); } protected override void OnRegistered(UnityTile tile) { if (string.IsNullOrEmpty(TilesetId) || _properties.sourceOptions.isActive == false || (_properties.vectorSubLayers.Count + _properties.locationPrefabList.Count) == 0) { tile.VectorDataState = TilePropertyState.None; return; } tile.VectorDataState = TilePropertyState.Loading; _tilesWaitingResponse.Add(tile); VectorDataFetcherParameters parameters = new VectorDataFetcherParameters() { canonicalTileId = tile.CanonicalTileId, tilesetId = TilesetId, tile = tile, useOptimizedStyle = _properties.useOptimizedStyle, style = _properties.optimizedStyle }; DataFetcher.FetchData(parameters); } protected override void OnUnregistered(UnityTile tile) { if (_layerProgress != null && _layerProgress.ContainsKey(tile)) { _layerProgress.Remove(tile); } if (_tilesWaitingResponse != null && _tilesWaitingProcessing.Contains(tile)) { _tilesWaitingProcessing.Remove(tile); } if (_layerBuilder != null) { foreach (var layer in _layerBuilder.Values) { foreach (var visualizer in layer) { visualizer.UnregisterTile(tile); } } } } public override void Clear() { DestroyImmediate(DataFetcher); if (_layerBuilder != null) { foreach (var layerList in _layerBuilder.Values) { foreach (var layerVisualizerBase in layerList) { layerVisualizerBase.Clear(); DestroyImmediate(layerVisualizerBase); } } _layerProgress.Clear(); _tilesWaitingResponse.Clear(); _tilesWaitingProcessing.Clear(); } } public override void SetOptions(LayerProperties options) { _properties = (VectorLayerProperties)options; if (_layerBuilder != null) { RemoveAllLayerVisualiers(); CreatePOILayerVisualizers(); CreateLayerVisualizers(); } } public override void UpdateTileProperty(UnityTile tile, LayerUpdateArgs updateArgs) { updateArgs.property.UpdateProperty(tile); if (updateArgs.property.NeedsForceUpdate()) { Unregister(tile); } Register(tile); } protected override void UpdateTileFactory(object sender, EventArgs args) { VectorLayerUpdateArgs layerUpdateArgs = args as VectorLayerUpdateArgs; layerUpdateArgs.factory = this; base.UpdateTileFactory(sender, layerUpdateArgs); } /// /// Method to be called when a tile error has occurred. /// /// instance/ protected override void OnErrorOccurred(UnityTile tile, TileErrorEventArgs e) { base.OnErrorOccurred(tile, e); } protected override void OnPostProcess(UnityTile tile) { } public override void UnbindEvents() { base.UnbindEvents(); } protected override void OnUnbindEvents() { if (_layerBuilder != null) { foreach (var layer in _layerBuilder.Values) { foreach (var visualizer in layer) { visualizer.LayerVisualizerHasChanged -= UpdateTileFactory; visualizer.UnbindSubLayerEvents(); } } } } #endregion #region DataFetcherEvents private void OnVectorDataRecieved(UnityTile tile, Mapbox.Map.VectorTile vectorTile) { if (tile != null) { _tilesWaitingResponse.Remove(tile); if (tile.VectorDataState != TilePropertyState.Unregistered) { tile.SetVectorData(vectorTile); // FIXME: we can make the request BEFORE getting a response from these! if (tile.HeightDataState == TilePropertyState.Loading || tile.RasterDataState == TilePropertyState.Loading) { tile.OnHeightDataChanged += DataChangedHandler; tile.OnRasterDataChanged += DataChangedHandler; } else { CreateMeshes(tile); } } } } private void DataChangedHandler(UnityTile tile) { if (tile.VectorDataState != TilePropertyState.Unregistered && tile.RasterDataState != TilePropertyState.Loading && tile.HeightDataState != TilePropertyState.Loading) { tile.OnHeightDataChanged -= DataChangedHandler; tile.OnRasterDataChanged -= DataChangedHandler; CreateMeshes(tile); } } private void OnDataError(UnityTile tile, Mapbox.Map.VectorTile vectorTile, TileErrorEventArgs e) { if (tile != null) { _tilesWaitingResponse.Remove(tile); if (tile.VectorDataState != TilePropertyState.Unregistered) { tile.SetVectorData(null); tile.VectorDataState = TilePropertyState.Error; OnErrorOccurred(e); } } } #endregion #region Private Methods private void CreateMeshes(UnityTile tile) { var nameList = new List(); var builderList = new List(); foreach (var layerName in tile.VectorData.Data.LayerNames()) { if (_layerBuilder.ContainsKey(layerName)) { //two loops; first one to add it to waiting/tracking list, second to start it foreach (var builder in _layerBuilder[layerName]) { nameList.Add(layerName); builderList.Add(builder); TrackFeatureWithBuilder(tile, layerName, builder); } } } for (int i = 0; i < nameList.Count; i++) { CreateFeatureWithBuilder(tile, nameList[i], builderList[i]); } builderList.Clear(); //emptylayer for visualizers that don't depend on outside data sources string emptyLayer = ""; if (_layerBuilder.ContainsKey(emptyLayer)) { //two loops; first one to add it to waiting/tracking list, second to start it foreach (var builder in _layerBuilder[emptyLayer]) { builderList.Add(builder); TrackFeatureWithBuilder(tile, emptyLayer, builder); } } for (int i = 0; i < builderList.Count; i++) { CreateFeatureWithBuilder(tile, emptyLayer, builderList[i]); } if (!_layerProgress.ContainsKey(tile)) { tile.VectorDataState = TilePropertyState.Loaded; } } private void TrackFeatureWithBuilder(UnityTile tile, string layerName, LayerVisualizerBase builder) { if (builder.Active) { if (_layerProgress.ContainsKey(tile)) { _layerProgress[tile].Add(builder); } else { _layerProgress.Add(tile, new HashSet {builder}); if (!_tilesWaitingProcessing.Contains(tile)) { _tilesWaitingProcessing.Add(tile); } } } } private void CreateFeatureWithBuilder(UnityTile tile, string layerName, LayerVisualizerBase builder) { if (builder.Active) { if (_layerProgress.ContainsKey(tile)) { _layerProgress[tile].Add(builder); } else { _layerProgress.Add(tile, new HashSet { builder }); if (!_tilesWaitingProcessing.Contains(tile)) { _tilesWaitingProcessing.Add(tile); } } if (layerName != "") { builder.Create(tile.VectorData.Data.GetLayer(layerName), tile, DecreaseProgressCounter); } else { //just pass the first available layer - we should create a static null layer for this builder.Create(tile.VectorData.Data.GetLayer(tile.VectorData.Data.LayerNames()[0]), tile, DecreaseProgressCounter); } } } private void DecreaseProgressCounter(UnityTile tile, LayerVisualizerBase builder) { if (_layerProgress.ContainsKey(tile)) { if (_layerProgress[tile].Contains(builder)) { _layerProgress[tile].Remove(builder); } if (_layerProgress[tile].Count == 0) { _layerProgress.Remove(tile); _tilesWaitingProcessing.Remove(tile); tile.VectorDataState = TilePropertyState.Loaded; } } } private void CreatePOILayerVisualizers() { foreach (var item in _properties.locationPrefabList) { AddPOIVectorLayerVisualizer(item); } } private void CreateLayerVisualizers() { foreach (var sublayer in _properties.vectorSubLayers) { AddVectorLayerVisualizer(sublayer); } } private void RemoveAllLayerVisualiers() { //Clearing gameobjects pooled and managed by modifiers to prevent zombie gameobjects. foreach (var pairs in _layerBuilder) { foreach (var layerVisualizerBase in pairs.Value) { layerVisualizerBase.Clear(); } } _layerBuilder.Clear(); } #endregion } }