You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

265 lines
8.2 KiB

6 months ago
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
}
/// <summary>
/// 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.
/// </summary>
[CreateAssetMenu(menuName = "Mapbox/Modifiers/Modifier Stack")]
public class ModifierStack : ModifierStackBase
{
[SerializeField] public PositionTargetType moveFeaturePositionTo;
[NonSerialized] private int vertexIndex = 1;
[NonSerialized] private Dictionary<UnityTile, List<VectorEntity>> _activeObjects;
[NonSerialized] private ObjectPool<VectorEntity> _pool;
[NonSerialized] private Vector3 _tempPoint;
[NonSerialized] private VectorEntity _tempVectorEntity;
[NonSerialized] private ObjectPool<List<VectorEntity>> _listPool;
[NonSerialized] private int _counter;
[NonSerialized] private int _secondCounter;
protected virtual void OnEnable()
{
_pool = new ObjectPool<VectorEntity>(() =>
{
var go = new GameObject();
var mf = go.AddComponent<MeshFilter>();
mf.sharedMesh = new Mesh();
mf.sharedMesh.name = "feature";
var mr = go.AddComponent<MeshRenderer>();
_tempVectorEntity = new VectorEntity()
{
GameObject = go,
Transform = go.transform,
MeshFilter = mf,
MeshRenderer = mr,
Mesh = mf.sharedMesh
};
return _tempVectorEntity;
});
_listPool = new ObjectPool<List<VectorEntity>>(() => { return new List<VectorEntity>(); });
_activeObjects = new Dictionary<UnityTile, List<VectorEntity>>();
}
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();
}
}
}