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
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();
|
||
|
}
|
||
|
}
|
||
|
}
|