[TASK] Initial commit with basic product setup
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b418c0cc9fb9f92448750e73736cb2e2
|
||||
folderAsset: yes
|
||||
timeCreated: 1485208035
|
||||
licenseType: Pro
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,49 @@
|
||||
namespace Mapbox.Unity.MeshGeneration.Components
|
||||
{
|
||||
using UnityEngine;
|
||||
using System.Linq;
|
||||
using Mapbox.Unity.MeshGeneration.Data;
|
||||
using System;
|
||||
|
||||
public class FeatureBehaviour : MonoBehaviour
|
||||
{
|
||||
public VectorEntity VectorEntity;
|
||||
public Transform Transform;
|
||||
public VectorFeatureUnity Data;
|
||||
|
||||
[Multiline(5)]
|
||||
public string DataString;
|
||||
|
||||
public void ShowDebugData()
|
||||
{
|
||||
DataString = string.Join("\r\n", Data.Properties.Select(x => x.Key + " - " + x.Value.ToString()).ToArray());
|
||||
}
|
||||
|
||||
public void ShowDataPoints()
|
||||
{
|
||||
foreach (var item in VectorEntity.Feature.Points)
|
||||
{
|
||||
for (int i = 0; i < item.Count; i++)
|
||||
{
|
||||
var go = GameObject.CreatePrimitive(PrimitiveType.Sphere);
|
||||
go.name = i.ToString();
|
||||
go.transform.SetParent(transform, false);
|
||||
go.transform.localPosition = item[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Initialize(VectorEntity ve)
|
||||
{
|
||||
VectorEntity = ve;
|
||||
Transform = transform;
|
||||
Data = ve.Feature;
|
||||
}
|
||||
|
||||
public void Initialize(VectorFeatureUnity feature)
|
||||
{
|
||||
Transform = transform;
|
||||
Data = feature;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1dd19505b960fb645953659207ca4bef
|
||||
timeCreated: 1485208038
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,89 @@
|
||||
namespace Mapbox.Unity.MeshGeneration.Components
|
||||
{
|
||||
using UnityEngine;
|
||||
using Random = UnityEngine.Random;
|
||||
using Mapbox.Unity.MeshGeneration.Data;
|
||||
|
||||
[RequireComponent(typeof(MeshRenderer))]
|
||||
public class TextureSelector : MonoBehaviour
|
||||
{
|
||||
private bool _useSatelliteRoof;
|
||||
private bool _textureTop;
|
||||
private bool _textureSides;
|
||||
|
||||
private UnityTile _tile;
|
||||
private MeshRenderer _meshRenderer;
|
||||
private Material[] _topTextures;
|
||||
private Material[] _sideTextures;
|
||||
|
||||
public void Initialize(VectorEntity ve, bool doTextureTop, bool useSatelliteRoof, Material[] topTextures, bool doTextureSides, Material[] sideTextures)
|
||||
{
|
||||
_useSatelliteRoof = useSatelliteRoof;
|
||||
_textureTop = doTextureTop;
|
||||
_textureSides = doTextureSides;
|
||||
|
||||
_tile = GetComponent<UnityTile>();
|
||||
var t = transform;
|
||||
while (_tile == null && t.parent != null)
|
||||
{
|
||||
t = t.parent;
|
||||
_tile = t.GetComponent<UnityTile>();
|
||||
}
|
||||
_topTextures = topTextures;
|
||||
_sideTextures = sideTextures;
|
||||
_meshRenderer = GetComponent<MeshRenderer>();
|
||||
|
||||
if (_textureSides && _sideTextures.Length > 0)
|
||||
{
|
||||
_meshRenderer.materials = new Material[2]
|
||||
{
|
||||
_topTextures[Random.Range(0, _topTextures.Length)],
|
||||
_sideTextures[Random.Range(0, _sideTextures.Length)]
|
||||
};
|
||||
}
|
||||
else if (_textureTop)
|
||||
{
|
||||
_meshRenderer.materials = new Material[1]
|
||||
{
|
||||
_topTextures[Random.Range(0, _topTextures.Length)]
|
||||
};
|
||||
}
|
||||
|
||||
if (_useSatelliteRoof)
|
||||
{
|
||||
_meshRenderer.materials[0].mainTexture = _tile.GetRasterData();
|
||||
_meshRenderer.materials[0].mainTextureScale = new Vector2(1f, 1f);
|
||||
}
|
||||
}
|
||||
|
||||
//private void TextureTop(Material topmat)
|
||||
//{
|
||||
// if (_useSatelliteRoof)
|
||||
// {
|
||||
// topmat.mainTexture = _tile.ImageData;
|
||||
// topmat.mainTextureScale = new Vector2(1f, 1f);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// topmat.mainTextureScale = new Vector2(0.07f, 0.07f);
|
||||
// if (_topTextures.Any())
|
||||
// _meshRenderer.materials[0].SetTexture("_MainTex", );
|
||||
// }
|
||||
|
||||
// var c = 1 - (Random.Range(0, 10) / 40f);
|
||||
// _meshRenderer.materials[0].color = new Color(c, c, c);
|
||||
//}
|
||||
|
||||
//private void TextureSides(Material sidemat)
|
||||
//{
|
||||
// var c = 1 - (Random.Range(0, 10) / 40f);
|
||||
// _meshRenderer.materials[1].color = new Color(c, c, c);
|
||||
|
||||
// if (_sideTextures.Any())
|
||||
// _meshRenderer.materials[1].SetTexture("_MainTex", _sideTextures[Random.Range(0, _sideTextures.Length)]);
|
||||
|
||||
//}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 97464c54f6d369444b297e02ee300633
|
||||
timeCreated: 1485208041
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,46 @@
|
||||
namespace Mapbox.Unity.MeshGeneration.Components
|
||||
{
|
||||
using UnityEngine;
|
||||
|
||||
public class VertexDebugger : MonoBehaviour
|
||||
{
|
||||
[Multiline(10)]
|
||||
public string Triangles;
|
||||
[Multiline(10)]
|
||||
public string Tangents;
|
||||
|
||||
protected virtual void Start()
|
||||
{
|
||||
var mf = GetComponent<MeshFilter>();
|
||||
if (mf)
|
||||
{
|
||||
var mesh = mf.mesh;
|
||||
var verts = mesh.vertices;
|
||||
var tang = mesh.tangents;
|
||||
for (int i = 0; i < verts.Length; i++)
|
||||
{
|
||||
var go = GameObject.CreatePrimitive(PrimitiveType.Sphere);
|
||||
go.name = i.ToString();
|
||||
go.transform.SetParent(transform, false);
|
||||
go.transform.localPosition = verts[i];
|
||||
|
||||
Debug.DrawLine(transform.position + verts[i], transform.position + verts[i] + new Vector3(tang[i].x, tang[i].y, tang[i].z) * .5f, Color.red, 10000);
|
||||
}
|
||||
var tris = mesh.triangles;
|
||||
Triangles = "";
|
||||
for (int i = 0; i < tris.Length; i += 3)
|
||||
{
|
||||
Triangles += tris[i] + "," + tris[i + 1] + "," + tris[i + 2] + "\r\n";
|
||||
}
|
||||
|
||||
//var tang = mesh.tangents;
|
||||
//Tangents = "";
|
||||
//for (int i = 0; i < tang.Length; i += 1)
|
||||
//{
|
||||
// Tangents += tang[i].x + "," + tang[i].y + "," + tang[i].z + "," + tang[i].w + "\r\n";
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 74c6dd1076bc8954dad41ed4206c598d
|
||||
timeCreated: 1499870729
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,48 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mapbox.Unity.MeshGeneration.Components
|
||||
{
|
||||
public class VertexDebuggerGizmo : MonoBehaviour
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
[SerializeField]
|
||||
float _radius = .2f;
|
||||
|
||||
[SerializeField]
|
||||
Color _color = new Color(0, 1, 0, .5f);
|
||||
|
||||
[Multiline(10)]
|
||||
public string Triangles;
|
||||
|
||||
Mesh _mesh;
|
||||
|
||||
protected virtual void Start()
|
||||
{
|
||||
var mf = GetComponent<MeshFilter>();
|
||||
if (mf)
|
||||
{
|
||||
_mesh = mf.mesh;
|
||||
var tris = _mesh.triangles;
|
||||
Triangles = "";
|
||||
for (int i = 0; i < tris.Length; i += 3)
|
||||
{
|
||||
Triangles += tris[i] + "," + tris[i + 1] + "," + tris[i + 2] + "\r\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void OnDrawGizmosSelected()
|
||||
{
|
||||
if (_mesh)
|
||||
{
|
||||
var verts = _mesh.vertices;
|
||||
for (int i = 0; i < verts.Length; i++)
|
||||
{
|
||||
Gizmos.color = _color;
|
||||
Gizmos.DrawWireCube(transform.position + verts[i] * transform.lossyScale.x, Vector3.one * _radius);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c4b8220219b204a288d4b67600489e3f
|
||||
timeCreated: 1499870729
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
9
Assets/Mapbox SDK/Mapbox/Unity/MeshGeneration/Data.meta
Normal file
9
Assets/Mapbox SDK/Mapbox/Unity/MeshGeneration/Data.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4b95895b4bbc4844cade47f970759aa5
|
||||
folderAsset: yes
|
||||
timeCreated: 1485207080
|
||||
licenseType: Pro
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,89 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
|
||||
namespace Mapbox.Unity.MeshGeneration.Data
|
||||
{
|
||||
[Serializable]
|
||||
public class AtlasEntity
|
||||
{
|
||||
public Rect TextureRect;
|
||||
public int MidFloorCount;
|
||||
public float ColumnCount;
|
||||
|
||||
public float TopSectionRatio;
|
||||
public float BottomSectionRatio;
|
||||
|
||||
public int PreferredEdgeSectionLength = 10;
|
||||
public float FloorHeight;
|
||||
public float FirstFloorHeight;
|
||||
public float TopFloorHeight;
|
||||
|
||||
[HideInInspector] public float bottomOfTopUv;
|
||||
[HideInInspector] public float topOfMidUv;
|
||||
[HideInInspector] public float topOfBottomUv;
|
||||
[HideInInspector] public float midUvHeight;
|
||||
[HideInInspector] public float WallToFloorRatio;
|
||||
|
||||
public void CalculateParameters()
|
||||
{
|
||||
bottomOfTopUv = TextureRect.yMax - (TextureRect.size.y * TopSectionRatio); //not doing that scaling thing for y axis and floors yet
|
||||
topOfMidUv = TextureRect.yMax - (TextureRect.height * TopSectionRatio);
|
||||
topOfBottomUv = TextureRect.yMin + (TextureRect.size.y * BottomSectionRatio); // * (Mathf.Max(1, (float)Math.Floor(tby * textureSection.TopSectionFloorCount)) / textureSection.TopSectionFloorCount);
|
||||
midUvHeight = TextureRect.height * (1 - TopSectionRatio - BottomSectionRatio);
|
||||
WallToFloorRatio = (1 - TopSectionRatio - BottomSectionRatio) * (TextureRect.height / TextureRect.width);
|
||||
}
|
||||
}
|
||||
|
||||
public enum AtlasEntityType
|
||||
{
|
||||
TextureBased,
|
||||
MeshGenBased
|
||||
}
|
||||
|
||||
[CreateAssetMenu(menuName = "Mapbox/AtlasInfo")]
|
||||
public class AtlasInfo : ScriptableObject
|
||||
{
|
||||
public List<AtlasEntity> Textures;
|
||||
public List<AtlasEntity> Roofs;
|
||||
|
||||
private UnityEvent m_OnValidate = new UnityEvent();
|
||||
|
||||
public AtlasEntityType AtlasEntityType;
|
||||
|
||||
public void AddOnValidateEvent(UnityAction action)
|
||||
{
|
||||
m_OnValidate.AddListener(action);
|
||||
}
|
||||
|
||||
protected virtual void OnValidate()
|
||||
{
|
||||
if(m_OnValidate != null)
|
||||
{
|
||||
m_OnValidate.Invoke();
|
||||
}
|
||||
|
||||
if(AtlasEntityType == AtlasEntityType.TextureBased)
|
||||
{
|
||||
foreach (var tex in Textures)
|
||||
{
|
||||
|
||||
tex.FirstFloorHeight = tex.PreferredEdgeSectionLength * ((tex.TextureRect.height * tex.BottomSectionRatio) / tex.TextureRect.width);
|
||||
tex.TopFloorHeight = tex.PreferredEdgeSectionLength * ((tex.TextureRect.height * tex.TopSectionRatio) / tex.TextureRect.width);
|
||||
tex.FloorHeight = tex.PreferredEdgeSectionLength * ((1 - tex.TopSectionRatio - tex.BottomSectionRatio) * (tex.TextureRect.height / tex.TextureRect.width));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var tex in Textures)
|
||||
{
|
||||
tex.BottomSectionRatio = (tex.FirstFloorHeight / tex.PreferredEdgeSectionLength) * tex.TextureRect.width / tex.TextureRect.height;
|
||||
tex.TopSectionRatio = (tex.TopFloorHeight / tex.PreferredEdgeSectionLength) * tex.TextureRect.width / tex.TextureRect.height;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 48879146f3ce9fb4abc113a9a2bb8e0a
|
||||
timeCreated: 1515082410
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,19 @@
|
||||
namespace Mapbox.Unity.MeshGeneration
|
||||
{
|
||||
using Mapbox.Unity.MeshGeneration.Data;
|
||||
using UnityEngine;
|
||||
|
||||
public class FeatureCollectionBase : ScriptableObject
|
||||
{
|
||||
public virtual void Initialize()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public virtual void AddFeature(double[] position, VectorEntity ve)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4d7c8347fa9db194e99888f8337ed5e2
|
||||
timeCreated: 1516628160
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4f15de9092560a644913bb912f5642ec
|
||||
folderAsset: yes
|
||||
timeCreated: 1519738933
|
||||
licenseType: Pro
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,77 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace KDTree
|
||||
{
|
||||
/// <summary>
|
||||
/// An interface which enables flexible distance functions.
|
||||
/// </summary>
|
||||
public interface DistanceFunctions
|
||||
{
|
||||
/// <summary>
|
||||
/// Compute a distance between two n-dimensional points.
|
||||
/// </summary>
|
||||
/// <param name="p1">The first point.</param>
|
||||
/// <param name="p2">The second point.</param>
|
||||
/// <returns>The n-dimensional distance.</returns>
|
||||
double Distance(double[] p1, double[] p2);
|
||||
|
||||
/// <summary>
|
||||
/// Find the shortest distance from a point to an axis aligned rectangle in n-dimensional space.
|
||||
/// </summary>
|
||||
/// <param name="point">The point of interest.</param>
|
||||
/// <param name="min">The minimum coordinate of the rectangle.</param>
|
||||
/// <param name="max">The maximum coorindate of the rectangle.</param>
|
||||
/// <returns>The shortest n-dimensional distance between the point and rectangle.</returns>
|
||||
double DistanceToRectangle(double[] point, double[] min, double[] max);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A distance function for our KD-Tree which returns squared euclidean distances.
|
||||
/// </summary>
|
||||
public class SquareEuclideanDistanceFunction : DistanceFunctions
|
||||
{
|
||||
/// <summary>
|
||||
/// Find the squared distance between two n-dimensional points.
|
||||
/// </summary>
|
||||
/// <param name="p1">The first point.</param>
|
||||
/// <param name="p2">The second point.</param>
|
||||
/// <returns>The n-dimensional squared distance.</returns>
|
||||
public double Distance(double[] p1, double[] p2)
|
||||
{
|
||||
double fSum = 0;
|
||||
for (int i = 0; i < p1.Length; i++)
|
||||
{
|
||||
double fDifference = (p1[i] - p2[i]);
|
||||
fSum += fDifference * fDifference;
|
||||
}
|
||||
return fSum;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Find the shortest distance from a point to an axis aligned rectangle in n-dimensional space.
|
||||
/// </summary>
|
||||
/// <param name="point">The point of interest.</param>
|
||||
/// <param name="min">The minimum coordinate of the rectangle.</param>
|
||||
/// <param name="max">The maximum coorindate of the rectangle.</param>
|
||||
/// <returns>The shortest squared n-dimensional squared distance between the point and rectangle.</returns>
|
||||
public double DistanceToRectangle(double[] point, double[] min, double[] max)
|
||||
{
|
||||
double fSum = 0;
|
||||
double fDifference = 0;
|
||||
for (int i = 0; i < point.Length; ++i)
|
||||
{
|
||||
fDifference = 0;
|
||||
if (point[i] > max[i])
|
||||
fDifference = (point[i] - max[i]);
|
||||
else if (point[i] < min[i])
|
||||
fDifference = (point[i] - min[i]);
|
||||
fSum += fDifference * fDifference;
|
||||
}
|
||||
return fSum;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1a40f078f2196e048b6b95fcc9738e90
|
||||
timeCreated: 1516636092
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,474 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace KDTree
|
||||
{
|
||||
/// <summary>
|
||||
/// A binary interval heap is double-ended priority queue is a priority queue that it allows
|
||||
/// for efficient removal of both the maximum and minimum element.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The data type contained at each key.</typeparam>
|
||||
/// <remarks>This is based on this: https://bitbucket.org/rednaxela/knn-benchmark/src/tip/ags/utils/dataStructures/trees/thirdGenKD/ </remarks>
|
||||
public class IntervalHeap<T>
|
||||
{
|
||||
/// <summary>
|
||||
/// The default size for a new interval heap.
|
||||
/// </summary>
|
||||
private const int DEFAULT_SIZE = 64;
|
||||
|
||||
/// <summary>
|
||||
/// The internal data array which contains the stored objects.
|
||||
/// </summary>
|
||||
private T[] tData;
|
||||
|
||||
/// <summary>
|
||||
/// The array of keys which
|
||||
/// </summary>
|
||||
private double[] tKeys;
|
||||
|
||||
/// <summary>
|
||||
/// Construct a new interval heap with the default capacity.
|
||||
/// </summary>
|
||||
public IntervalHeap() : this(DEFAULT_SIZE)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Construct a new interval heap with a custom capacity.
|
||||
/// </summary>
|
||||
/// <param name="capacity"></param>
|
||||
public IntervalHeap(int capacity)
|
||||
{
|
||||
this.tData = new T[capacity];
|
||||
this.tKeys = new double[capacity];
|
||||
this.Capacity = capacity;
|
||||
this.Size = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The number of items in this interval heap.
|
||||
/// </summary>
|
||||
public int Size { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The current capacity of this interval heap.
|
||||
/// </summary>
|
||||
public int Capacity { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Get the data with the smallest key.
|
||||
/// </summary>
|
||||
public T Min
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Size == 0)
|
||||
throw new Exception();
|
||||
return tData[0];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the data with the largest key.
|
||||
/// </summary>
|
||||
public T Max
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Size == 0)
|
||||
{
|
||||
throw new Exception();
|
||||
}
|
||||
else if (Size == 1)
|
||||
{
|
||||
return tData[0];
|
||||
}
|
||||
|
||||
return tData[1];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the smallest key.
|
||||
/// </summary>
|
||||
public double MinKey
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Size == 0)
|
||||
throw new Exception();
|
||||
return tKeys[0];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the largest key.
|
||||
/// </summary>
|
||||
public double MaxKey
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Size == 0)
|
||||
{
|
||||
throw new Exception();
|
||||
}
|
||||
else if (Size == 1)
|
||||
{
|
||||
return tKeys[0];
|
||||
}
|
||||
|
||||
return tKeys[1];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Insert a new data item at a given key.
|
||||
/// </summary>
|
||||
/// <param name="key">The value which represents our data (i.e. a distance).</param>
|
||||
/// <param name="value">The data we want to store.</param>
|
||||
public void Insert(double key, T value)
|
||||
{
|
||||
// If more room is needed, double the array size.
|
||||
if (Size >= Capacity)
|
||||
{
|
||||
// Double the capacity.
|
||||
Capacity *= 2;
|
||||
|
||||
// Expand the data array.
|
||||
var newData = new T[Capacity];
|
||||
Array.Copy(tData, newData, tData.Length);
|
||||
tData = newData;
|
||||
|
||||
// Expand the key array.
|
||||
var newKeys = new double[Capacity];
|
||||
Array.Copy(tKeys, newKeys, tKeys.Length);
|
||||
tKeys = newKeys;
|
||||
}
|
||||
|
||||
// Insert the new value at the end.
|
||||
Size++;
|
||||
tData[Size-1] = value;
|
||||
tKeys[Size-1] = key;
|
||||
|
||||
// Ensure it is in the right place.
|
||||
SiftInsertedValueUp();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove the item with the smallest key from the queue.
|
||||
/// </summary>
|
||||
public void RemoveMin()
|
||||
{
|
||||
// Check for errors.
|
||||
if (Size == 0)
|
||||
throw new Exception();
|
||||
|
||||
// Remove the item by
|
||||
Size--;
|
||||
tData[0] = tData[Size];
|
||||
tKeys[0] = tKeys[Size];
|
||||
tData[Size] = default(T);
|
||||
SiftDownMin(0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Replace the item with the smallest key in the queue.
|
||||
/// </summary>
|
||||
/// <param name="key">The new minimum key.</param>
|
||||
/// <param name="value">The new minumum data value.</param>
|
||||
public void ReplaceMin(double key, T value)
|
||||
{
|
||||
// Check for errors.
|
||||
if (Size == 0)
|
||||
throw new Exception();
|
||||
|
||||
// Add the data.
|
||||
tData[0] = value;
|
||||
tKeys[0] = key;
|
||||
|
||||
// If we have more than one item.
|
||||
if (Size > 1)
|
||||
{
|
||||
// Swap with pair if necessary.
|
||||
if (tKeys[1] < key)
|
||||
Swap(0, 1);
|
||||
SiftDownMin(0);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove the item with the largest key in the queue.
|
||||
/// </summary>
|
||||
public void RemoveMax()
|
||||
{
|
||||
// If we have no items in the queue.
|
||||
if (Size == 0)
|
||||
{
|
||||
throw new Exception();
|
||||
}
|
||||
|
||||
// If we have one item, remove the min.
|
||||
else if (Size == 1)
|
||||
{
|
||||
RemoveMin();
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove the max.
|
||||
Size--;
|
||||
tData[1] = tData[Size];
|
||||
tKeys[1] = tKeys[Size];
|
||||
tData[Size] = default(T);
|
||||
SiftDownMax(1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Swap out the item with the largest key in the queue.
|
||||
/// </summary>
|
||||
/// <param name="key">The new key for the largest item.</param>
|
||||
/// <param name="value">The new data for the largest item.</param>
|
||||
public void ReplaceMax(double key, T value)
|
||||
{
|
||||
if (Size == 0)
|
||||
{
|
||||
throw new Exception();
|
||||
}
|
||||
else if (Size == 1)
|
||||
{
|
||||
ReplaceMin(key, value);
|
||||
return;
|
||||
}
|
||||
|
||||
tData[1] = value;
|
||||
tKeys[1] = key;
|
||||
// Swap with pair if necessary
|
||||
if (key < tKeys[0]) {
|
||||
Swap(0, 1);
|
||||
}
|
||||
SiftDownMax(1);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Internal helper method which swaps two values in the arrays.
|
||||
/// This swaps both data and key entries.
|
||||
/// </summary>
|
||||
/// <param name="x">The first index.</param>
|
||||
/// <param name="y">The second index.</param>
|
||||
/// <returns>The second index.</returns>
|
||||
private int Swap(int x, int y)
|
||||
{
|
||||
// Store temp.
|
||||
T yData = tData[y];
|
||||
double yDist = tKeys[y];
|
||||
|
||||
// Swap
|
||||
tData[y] = tData[x];
|
||||
tKeys[y] = tKeys[x];
|
||||
tData[x] = yData;
|
||||
tKeys[x] = yDist;
|
||||
|
||||
// Return.
|
||||
return y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Min-side (u % 2 == 0):
|
||||
* - leftchild: 2u + 2
|
||||
* - rightchild: 2u + 4
|
||||
* - parent: (x/2-1)&~1
|
||||
*
|
||||
* Max-side (u % 2 == 1):
|
||||
* - leftchild: 2u + 1
|
||||
* - rightchild: 2u + 3
|
||||
* - parent: (x/2-1)|1
|
||||
*/
|
||||
|
||||
/// <summary>
|
||||
/// Place a newly inserted element a into the correct tree position.
|
||||
/// </summary>
|
||||
private void SiftInsertedValueUp()
|
||||
{
|
||||
// Work out where the element was inserted.
|
||||
int u = Size-1;
|
||||
|
||||
// If it is the only element, nothing to do.
|
||||
if (u == 0)
|
||||
{
|
||||
}
|
||||
|
||||
// If it is the second element, sort with it's pair.
|
||||
else if (u == 1)
|
||||
{
|
||||
// Swap if less than paired item.
|
||||
if (tKeys[u] < tKeys[u-1])
|
||||
Swap(u, u-1);
|
||||
}
|
||||
|
||||
// If it is on the max side,
|
||||
else if (u % 2 == 1)
|
||||
{
|
||||
// Already paired. Ensure pair is ordered right
|
||||
int p = (u/2-1)|1; // The larger value of the parent pair
|
||||
if (tKeys[u] < tKeys[u-1])
|
||||
{ // If less than it's pair
|
||||
u = Swap(u, u-1); // Swap with it's pair
|
||||
if (tKeys[u] < tKeys[p-1])
|
||||
{ // If smaller than smaller parent pair
|
||||
// Swap into min-heap side
|
||||
u = Swap(u, p-1);
|
||||
SiftUpMin(u);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (tKeys[u] > tKeys[p])
|
||||
{ // If larger that larger parent pair
|
||||
// Swap into max-heap side
|
||||
u = Swap(u, p);
|
||||
SiftUpMax(u);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Inserted in the lower-value slot without a partner
|
||||
int p = (u/2-1)|1; // The larger value of the parent pair
|
||||
if (tKeys[u] > tKeys[p])
|
||||
{ // If larger that larger parent pair
|
||||
// Swap into max-heap side
|
||||
u = Swap(u, p);
|
||||
SiftUpMax(u);
|
||||
}
|
||||
else if (tKeys[u] < tKeys[p-1])
|
||||
{ // If smaller than smaller parent pair
|
||||
// Swap into min-heap side
|
||||
u = Swap(u, p-1);
|
||||
SiftUpMin(u);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bubble elements up the min side of the tree.
|
||||
/// </summary>
|
||||
/// <param name="iChild">The child index.</param>
|
||||
private void SiftUpMin(int iChild)
|
||||
{
|
||||
// Min-side parent: (x/2-1)&~1
|
||||
for (int iParent = (iChild/2-1)&~1;
|
||||
iParent >= 0 && tKeys[iChild] < tKeys[iParent];
|
||||
iChild = iParent, iParent = (iChild/2-1)&~1)
|
||||
{
|
||||
Swap(iChild, iParent);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bubble elements up the max side of the tree.
|
||||
/// </summary>
|
||||
/// <param name="iChild">The child index.</param>
|
||||
private void SiftUpMax(int iChild)
|
||||
{
|
||||
// Max-side parent: (x/2-1)|1
|
||||
for (int iParent = (iChild/2-1)|1;
|
||||
iParent >= 0 && tKeys[iChild] > tKeys[iParent];
|
||||
iChild = iParent, iParent = (iChild/2-1)|1)
|
||||
{
|
||||
Swap(iChild, iParent);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bubble elements down the min side of the tree.
|
||||
/// </summary>
|
||||
/// <param name="iParent">The parent index.</param>
|
||||
private void SiftDownMin(int iParent)
|
||||
{
|
||||
// For each child of the parent.
|
||||
for (int iChild = iParent * 2 + 2; iChild < Size; iParent = iChild, iChild = iParent * 2 + 2)
|
||||
{
|
||||
// If the next child is less than the current child, select the next one.
|
||||
if (iChild + 2 < Size && tKeys[iChild + 2] < tKeys[iChild])
|
||||
{
|
||||
iChild += 2;
|
||||
}
|
||||
|
||||
// If it is less than our parent swap.
|
||||
if (tKeys[iChild] < tKeys[iParent])
|
||||
{
|
||||
Swap(iParent, iChild);
|
||||
|
||||
// Swap the pair if necessary.
|
||||
if (iChild+1 < Size && tKeys[iChild+1] < tKeys[iChild])
|
||||
{
|
||||
Swap(iChild, iChild+1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bubble elements down the max side of the tree.
|
||||
/// </summary>
|
||||
/// <param name="iParent"></param>
|
||||
private void SiftDownMax(int iParent)
|
||||
{
|
||||
// For each child on the max side of the tree.
|
||||
for (int iChild = iParent * 2 + 1; iChild <= Size; iParent = iChild, iChild = iParent * 2 + 1)
|
||||
{
|
||||
// If the child is the last one (and only has half a pair).
|
||||
if (iChild == Size)
|
||||
{
|
||||
// CHeck if we need to swap with th parent.
|
||||
if (tKeys[iChild - 1] > tKeys[iParent])
|
||||
Swap(iParent, iChild - 1);
|
||||
break;
|
||||
}
|
||||
|
||||
// If there is only room for a right child lower pair.
|
||||
else if (iChild + 2 == Size)
|
||||
{
|
||||
// Swap the children.
|
||||
if (tKeys[iChild + 1] > tKeys[iChild])
|
||||
{
|
||||
// Swap with the parent.
|
||||
if (tKeys[iChild + 1] > tKeys[iParent])
|
||||
Swap(iParent, iChild + 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
else if (iChild + 2 < Size)
|
||||
{
|
||||
// If there is room for a right child upper pair
|
||||
if (tKeys[iChild + 2] > tKeys[iChild])
|
||||
{
|
||||
iChild += 2;
|
||||
}
|
||||
}
|
||||
if (tKeys[iChild] > tKeys[iParent])
|
||||
{
|
||||
Swap(iParent, iChild);
|
||||
// Swap with pair if necessary
|
||||
if (tKeys[iChild-1] > tKeys[iChild])
|
||||
{
|
||||
Swap(iChild, iChild-1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1e090ab286001c3488cf724a6a3325cf
|
||||
timeCreated: 1519738987
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,301 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace KDTree
|
||||
{
|
||||
/// <summary>
|
||||
/// A KD-Tree node which supports a generic number of dimensions. All data items
|
||||
/// need the same number of dimensions.
|
||||
/// This node splits based on the largest range of any dimension.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The generic data type this structure contains.</typeparam>
|
||||
/// <remarks>This is based on this: https://bitbucket.org/rednaxela/knn-benchmark/src/tip/ags/utils/dataStructures/trees/thirdGenKD/ </remarks>
|
||||
public class KDNode<T>
|
||||
{
|
||||
#region Internal properties and constructor
|
||||
// All types
|
||||
/// <summary>
|
||||
/// The number of dimensions for this node.
|
||||
/// </summary>
|
||||
protected internal int iDimensions;
|
||||
|
||||
/// <summary>
|
||||
/// The maximum capacity of this node.
|
||||
/// </summary>
|
||||
protected internal int iBucketCapacity;
|
||||
|
||||
// Leaf only
|
||||
/// <summary>
|
||||
/// The array of locations. [index][dimension]
|
||||
/// </summary>
|
||||
protected internal double[][] tPoints;
|
||||
|
||||
/// <summary>
|
||||
/// The array of data values. [index]
|
||||
/// </summary>
|
||||
protected internal T[] tData;
|
||||
|
||||
// Stem only
|
||||
/// <summary>
|
||||
/// The left and right children.
|
||||
/// </summary>
|
||||
protected internal KDNode<T> pLeft, pRight;
|
||||
/// <summary>
|
||||
/// The split dimension.
|
||||
/// </summary>
|
||||
protected internal int iSplitDimension;
|
||||
/// <summary>
|
||||
/// The split value (larger go into the right, smaller go into left)
|
||||
/// </summary>
|
||||
protected internal double fSplitValue;
|
||||
|
||||
// Bounds
|
||||
/// <summary>
|
||||
/// The min and max bound for this node. All dimensions.
|
||||
/// </summary>
|
||||
protected internal double[] tMinBound, tMaxBound;
|
||||
|
||||
/// <summary>
|
||||
/// Does this node represent only one point.
|
||||
/// </summary>
|
||||
protected internal bool bSinglePoint;
|
||||
|
||||
/// <summary>
|
||||
/// Protected method which constructs a new KDNode.
|
||||
/// </summary>
|
||||
/// <param name="iDimensions">The number of dimensions for this node (all the same in the tree).</param>
|
||||
/// <param name="iBucketCapacity">The initial capacity of the bucket.</param>
|
||||
protected KDNode(int iDimensions, int iBucketCapacity)
|
||||
{
|
||||
// Variables.
|
||||
this.iDimensions = iDimensions;
|
||||
this.iBucketCapacity = iBucketCapacity;
|
||||
this.Size = 0;
|
||||
this.bSinglePoint = true;
|
||||
|
||||
// Setup leaf elements.
|
||||
this.tPoints = new double[iBucketCapacity+1][];
|
||||
this.tData = new T[iBucketCapacity+1];
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region External Operations
|
||||
/// <summary>
|
||||
/// The number of items in this leaf node and all children.
|
||||
/// </summary>
|
||||
public int Size { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Is this KDNode a leaf or not?
|
||||
/// </summary>
|
||||
public bool IsLeaf { get { return tPoints != null; } }
|
||||
|
||||
/// <summary>
|
||||
/// Insert a new point into this leaf node.
|
||||
/// </summary>
|
||||
/// <param name="tPoint">The position which represents the data.</param>
|
||||
/// <param name="kValue">The value of the data.</param>
|
||||
public void AddPoint(double[] tPoint, T kValue)
|
||||
{
|
||||
// Find the correct leaf node.
|
||||
KDNode<T> pCursor = this;
|
||||
while (!pCursor.IsLeaf)
|
||||
{
|
||||
// Extend the size of the leaf.
|
||||
pCursor.ExtendBounds(tPoint);
|
||||
pCursor.Size++;
|
||||
|
||||
// If it is larger select the right, or lower, select the left.
|
||||
if (tPoint[pCursor.iSplitDimension] > pCursor.fSplitValue)
|
||||
{
|
||||
pCursor = pCursor.pRight;
|
||||
}
|
||||
else
|
||||
{
|
||||
pCursor = pCursor.pLeft;
|
||||
}
|
||||
}
|
||||
|
||||
// Insert it into the leaf.
|
||||
pCursor.AddLeafPoint(tPoint, kValue);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Internal Operations
|
||||
/// <summary>
|
||||
/// Insert the point into the leaf.
|
||||
/// </summary>
|
||||
/// <param name="tPoint">The point to insert the data at.</param>
|
||||
/// <param name="kValue">The value at the point.</param>
|
||||
private void AddLeafPoint(double[] tPoint, T kValue)
|
||||
{
|
||||
// Add the data point to this node.
|
||||
tPoints[Size] = tPoint;
|
||||
tData[Size] = kValue;
|
||||
ExtendBounds(tPoint);
|
||||
Size++;
|
||||
|
||||
// Split if the node is getting too large in terms of data.
|
||||
if (Size == tPoints.Length - 1)
|
||||
{
|
||||
// If the node is getting too physically large.
|
||||
if (CalculateSplit())
|
||||
{
|
||||
// If the node successfully had it's split value calculated, split node.
|
||||
SplitLeafNode();
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the node could not be split, enlarge node data capacity.
|
||||
IncreaseLeafCapacity();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If the point lies outside the boundaries, return false else true.
|
||||
/// </summary>
|
||||
/// <param name="tPoint">The point.</param>
|
||||
/// <returns>True if the point is inside the boundaries, false outside.</returns>
|
||||
private bool CheckBounds(double[] tPoint)
|
||||
{
|
||||
for (int i = 0; i < iDimensions; ++i)
|
||||
{
|
||||
if (tPoint[i] > tMaxBound[i]) return false;
|
||||
if (tPoint[i] < tMinBound[i]) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extend this node to contain a new point.
|
||||
/// </summary>
|
||||
/// <param name="tPoint">The point to contain.</param>
|
||||
private void ExtendBounds(double[] tPoint)
|
||||
{
|
||||
// If we don't have bounds, create them using the new point then bail.
|
||||
if (tMinBound == null)
|
||||
{
|
||||
tMinBound = new double[iDimensions];
|
||||
tMaxBound = new double[iDimensions];
|
||||
Array.Copy(tPoint, tMinBound, iDimensions);
|
||||
Array.Copy(tPoint, tMaxBound, iDimensions);
|
||||
return;
|
||||
}
|
||||
|
||||
// For each dimension.
|
||||
for (int i = 0; i < iDimensions; ++i)
|
||||
{
|
||||
if (Double.IsNaN(tPoint[i]))
|
||||
{
|
||||
if (!Double.IsNaN(tMinBound[i]) || !Double.IsNaN(tMaxBound[i]))
|
||||
bSinglePoint = false;
|
||||
|
||||
tMinBound[i] = Double.NaN;
|
||||
tMaxBound[i] = Double.NaN;
|
||||
}
|
||||
else if (tMinBound[i] > tPoint[i])
|
||||
{
|
||||
tMinBound[i] = tPoint[i];
|
||||
bSinglePoint = false;
|
||||
}
|
||||
else if (tMaxBound[i] < tPoint[i])
|
||||
{
|
||||
tMaxBound[i] = tPoint[i];
|
||||
bSinglePoint = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Double the capacity of this leaf.
|
||||
/// </summary>
|
||||
private void IncreaseLeafCapacity()
|
||||
{
|
||||
Array.Resize<double[]>(ref tPoints, tPoints.Length * 2);
|
||||
Array.Resize<T>(ref tData, tData.Length * 2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Work out if this leaf node should split. If it should, a new split value and dimension is calculated
|
||||
/// based on the dimension with the largest range.
|
||||
/// </summary>
|
||||
/// <returns>True if the node split, false if not.</returns>
|
||||
private bool CalculateSplit()
|
||||
{
|
||||
// Don't split if we are just one point.
|
||||
if (bSinglePoint)
|
||||
return false;
|
||||
|
||||
// Find the dimension with the largest range. This will be our split dimension.
|
||||
double fWidth = 0;
|
||||
for (int i = 0; i < iDimensions; i++)
|
||||
{
|
||||
double fDelta = (tMaxBound[i] - tMinBound[i]);
|
||||
if (Double.IsNaN(fDelta))
|
||||
fDelta = 0;
|
||||
|
||||
if (fDelta > fWidth)
|
||||
{
|
||||
iSplitDimension = i;
|
||||
fWidth = fDelta;
|
||||
}
|
||||
}
|
||||
|
||||
// If we are not wide (i.e. all the points are in one place), don't split.
|
||||
if (fWidth == 0)
|
||||
return false;
|
||||
|
||||
// Split in the middle of the node along the widest dimension.
|
||||
fSplitValue = (tMinBound[iSplitDimension] + tMaxBound[iSplitDimension]) * 0.5;
|
||||
|
||||
// Never split on infinity or NaN.
|
||||
if (fSplitValue == Double.PositiveInfinity)
|
||||
fSplitValue = Double.MaxValue;
|
||||
else if (fSplitValue == Double.NegativeInfinity)
|
||||
fSplitValue = Double.MinValue;
|
||||
|
||||
// Don't let the split value be the same as the upper value as
|
||||
// can happen due to rounding errors!
|
||||
if (fSplitValue == tMaxBound[iSplitDimension])
|
||||
fSplitValue = tMinBound[iSplitDimension];
|
||||
|
||||
// Success
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Split this leaf node by creating left and right children, then moving all the children of
|
||||
/// this node into the respective buckets.
|
||||
/// </summary>
|
||||
private void SplitLeafNode()
|
||||
{
|
||||
// Create the new children.
|
||||
pRight = new KDNode<T>(iDimensions, iBucketCapacity);
|
||||
pLeft = new KDNode<T>(iDimensions, iBucketCapacity);
|
||||
|
||||
// Move each item in this leaf into the children.
|
||||
for (int i = 0; i < Size; ++i)
|
||||
{
|
||||
// Store.
|
||||
double[] tOldPoint = tPoints[i];
|
||||
T kOldData = tData[i];
|
||||
|
||||
// If larger, put it in the right.
|
||||
if (tOldPoint[iSplitDimension] > fSplitValue)
|
||||
pRight.AddLeafPoint(tOldPoint, kOldData);
|
||||
|
||||
// If smaller, put it in the left.
|
||||
else
|
||||
pLeft.AddLeafPoint(tOldPoint, kOldData);
|
||||
}
|
||||
|
||||
// Wipe the data from this KDNode.
|
||||
tPoints = null;
|
||||
tData = null;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ec5d72f02adf6254aad038350ce771a3
|
||||
timeCreated: 1516636093
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,62 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace KDTree
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// A KDTree class represents the root of a variable-dimension KD-Tree.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The generic data type we want this tree to contain.</typeparam>
|
||||
/// <remarks>This is based on this: https://bitbucket.org/rednaxela/knn-benchmark/src/tip/ags/utils/dataStructures/trees/thirdGenKD/ </remarks>
|
||||
public class KDTree<T> : KDNode<T>
|
||||
{
|
||||
/// <summary>
|
||||
/// Create a new KD-Tree given a number of dimensions.
|
||||
/// </summary>
|
||||
/// <param name="iDimensions">The number of data sorting dimensions. i.e. 3 for a 3D point.</param>
|
||||
public KDTree(int iDimensions)
|
||||
: base(iDimensions, 24)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new KD-Tree given a number of dimensions and initial bucket capacity.
|
||||
/// </summary>
|
||||
/// <param name="iDimensions">The number of data sorting dimensions. i.e. 3 for a 3D point.</param>
|
||||
/// <param name="iBucketCapacity">The default number of items that can be stored in each node.</param>
|
||||
public KDTree(int iDimensions, int iBucketCapacity)
|
||||
: base(iDimensions, iBucketCapacity)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the nearest neighbours to a point in the kd tree using a square euclidean distance function.
|
||||
/// </summary>
|
||||
/// <param name="tSearchPoint">The point of interest.</param>
|
||||
/// <param name="iMaxReturned">The maximum number of points which can be returned by the iterator.</param>
|
||||
/// <param name="fDistance">A threshold distance to apply. Optional. Negative values mean that it is not applied.</param>
|
||||
/// <returns>A new nearest neighbour iterator with the given parameters.</returns>
|
||||
public NearestNeighbour<T> NearestNeighbors(double[] tSearchPoint, int iMaxReturned, double fDistance = -1)
|
||||
{
|
||||
DistanceFunctions distanceFunction = new SquareEuclideanDistanceFunction();
|
||||
return NearestNeighbors(tSearchPoint, distanceFunction, iMaxReturned, fDistance);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the nearest neighbours to a point in the kd tree using a user defined distance function.
|
||||
/// </summary>
|
||||
/// <param name="tSearchPoint">The point of interest.</param>
|
||||
/// <param name="iMaxReturned">The maximum number of points which can be returned by the iterator.</param>
|
||||
/// <param name="kDistanceFunction">The distance function to use.</param>
|
||||
/// <param name="fDistance">A threshold distance to apply. Optional. Negative values mean that it is not applied.</param>
|
||||
/// <returns>A new nearest neighbour iterator with the given parameters.</returns>
|
||||
public NearestNeighbour<T> NearestNeighbors(double[] tSearchPoint, DistanceFunctions kDistanceFunction, int iMaxReturned, double fDistance)
|
||||
{
|
||||
return new NearestNeighbour<T>(this, tSearchPoint, kDistanceFunction, iMaxReturned, fDistance);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c297650f28b0efc4d97523269824786d
|
||||
timeCreated: 1516636093
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,189 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace KDTree
|
||||
{
|
||||
/// <summary>
|
||||
/// A MinHeap is a smallest-first queue based around a binary heap so it is quick to insert / remove items.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of data this MinHeap stores.</typeparam>
|
||||
/// <remarks>This is based on this: https://bitbucket.org/rednaxela/knn-benchmark/src/tip/ags/utils/dataStructures/trees/thirdGenKD/ </remarks>
|
||||
public class MinHeap<T>
|
||||
{
|
||||
/// <summary>
|
||||
/// The default size for a min heap.
|
||||
/// </summary>
|
||||
private static int DEFAULT_SIZE = 64;
|
||||
|
||||
/// <summary>
|
||||
/// The data array. This stores the data items in the heap.
|
||||
/// </summary>
|
||||
private T[] tData;
|
||||
|
||||
/// <summary>
|
||||
/// The key array. This determines how items are ordered. Smallest first.
|
||||
/// </summary>
|
||||
private double[] tKeys;
|
||||
|
||||
/// <summary>
|
||||
/// Create a new min heap with the default capacity.
|
||||
/// </summary>
|
||||
public MinHeap() : this(DEFAULT_SIZE)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new min heap with a given capacity.
|
||||
/// </summary>
|
||||
/// <param name="iCapacity"></param>
|
||||
public MinHeap(int iCapacity)
|
||||
{
|
||||
this.tData = new T[iCapacity];
|
||||
this.tKeys = new double[iCapacity];
|
||||
this.Capacity = iCapacity;
|
||||
this.Size = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The number of items in this queue.
|
||||
/// </summary>
|
||||
public int Size { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The amount of space in this queue.
|
||||
/// </summary>
|
||||
public int Capacity { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Insert a new element.
|
||||
/// </summary>
|
||||
/// <param name="key">The key which represents its position in the priority queue (ie. distance).</param>
|
||||
/// <param name="value">The value to be stored at the key.</param>
|
||||
public void Insert(double key, T value)
|
||||
{
|
||||
// If we need more room, double the space.
|
||||
if (Size >= Capacity)
|
||||
{
|
||||
// Calcualte the new capacity.
|
||||
Capacity *= 2;
|
||||
|
||||
// Copy the data array.
|
||||
var newData = new T[Capacity];
|
||||
Array.Copy(tData, newData, tData.Length);
|
||||
tData = newData;
|
||||
|
||||
// Copy the key array.
|
||||
var newKeys = new double[Capacity];
|
||||
Array.Copy(tKeys, newKeys, tKeys.Length);
|
||||
tKeys = newKeys;
|
||||
}
|
||||
|
||||
// Insert new value at the end
|
||||
tData[Size] = value;
|
||||
tKeys[Size] = key;
|
||||
SiftUp(Size);
|
||||
Size++;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove the smallest element.
|
||||
/// </summary>
|
||||
public void RemoveMin()
|
||||
{
|
||||
if (Size == 0)
|
||||
throw new Exception();
|
||||
|
||||
Size--;
|
||||
tData[0] = tData[Size];
|
||||
tKeys[0] = tKeys[Size];
|
||||
tData[Size] = default(T);
|
||||
SiftDown(0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the data stored at the minimum element.
|
||||
/// </summary>
|
||||
public T Min
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Size == 0)
|
||||
throw new Exception();
|
||||
|
||||
return tData[0];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the key which represents the minimum element.
|
||||
/// </summary>
|
||||
public double MinKey
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Size == 0)
|
||||
throw new Exception();
|
||||
|
||||
return tKeys[0];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bubble a child item up the tree.
|
||||
/// </summary>
|
||||
/// <param name="iChild"></param>
|
||||
private void SiftUp(int iChild)
|
||||
{
|
||||
// For each parent above the child, if the parent is smaller then bubble it up.
|
||||
for (int iParent = (iChild - 1) / 2;
|
||||
iChild != 0 && tKeys[iChild] < tKeys[iParent];
|
||||
iChild = iParent, iParent = (iChild - 1) / 2)
|
||||
{
|
||||
T kData = tData[iParent];
|
||||
double dDist = tKeys[iParent];
|
||||
|
||||
tData[iParent] = tData[iChild];
|
||||
tKeys[iParent] = tKeys[iChild];
|
||||
|
||||
tData[iChild] = kData;
|
||||
tKeys[iChild] = dDist;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bubble a parent down through the children so it goes in the right place.
|
||||
/// </summary>
|
||||
/// <param name="iParent">The index of the parent.</param>
|
||||
private void SiftDown(int iParent)
|
||||
{
|
||||
// For each child.
|
||||
for (int iChild = iParent * 2 + 1; iChild < Size; iParent = iChild, iChild = iParent * 2 + 1)
|
||||
{
|
||||
// If the child is larger, select the next child.
|
||||
if (iChild + 1 < Size && tKeys[iChild] > tKeys[iChild + 1])
|
||||
iChild++;
|
||||
|
||||
// If the parent is larger than the largest child, swap.
|
||||
if (tKeys[iParent] > tKeys[iChild])
|
||||
{
|
||||
// Swap the points
|
||||
T pData = tData[iParent];
|
||||
double pDist = tKeys[iParent];
|
||||
|
||||
tData[iParent] = tData[iChild];
|
||||
tKeys[iParent] = tKeys[iChild];
|
||||
|
||||
tData[iChild] = pData;
|
||||
tKeys[iChild] = pDist;
|
||||
}
|
||||
|
||||
// TODO: REMOVE THE BREAK
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b3dd3caca8a74224cab6c8dc36751097
|
||||
timeCreated: 1516636092
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,248 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace KDTree
|
||||
{
|
||||
/// <summary>
|
||||
/// A NearestNeighbour iterator for the KD-tree which intelligently iterates and captures relevant data in the search space.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of data the iterator should handle.</typeparam>
|
||||
public class NearestNeighbour<T> : IEnumerator<T>, IEnumerable<T>
|
||||
{
|
||||
/// <summary>The point from which are searching in n-dimensional space.</summary>
|
||||
private double[] tSearchPoint;
|
||||
/// <summary>A distance function which is used to compare nodes and value positions.</summary>
|
||||
private DistanceFunctions kDistanceFunction;
|
||||
/// <summary>The tree nodes which have yet to be evaluated.</summary>
|
||||
private MinHeap<KDNode<T>> pPending;
|
||||
/// <summary>The values which have been evaluated and selected.</summary>
|
||||
private IntervalHeap<T> pEvaluated;
|
||||
/// <summary>The root of the kd tree to begin searching from.</summary>
|
||||
private KDNode<T> pRoot = null;
|
||||
|
||||
/// <summary>The max number of points we can return through this iterator.</summary>
|
||||
private int iMaxPointsReturned = 0;
|
||||
/// <summary>The number of points we can still test before conclusion.</summary>
|
||||
private int iPointsRemaining;
|
||||
/// <summary>Threshold to apply to tree iteration. Negative numbers mean no threshold applied.</summary>
|
||||
private double fThreshold;
|
||||
|
||||
/// <summary>Current value distance.</summary>
|
||||
private double _CurrentDistance = -1;
|
||||
/// <summary>Current value reference.</summary>
|
||||
private T _Current = default(T);
|
||||
|
||||
/// <summary>
|
||||
/// Construct a new nearest neighbour iterator.
|
||||
/// </summary>
|
||||
/// <param name="pRoot">The root of the tree to begin searching from.</param>
|
||||
/// <param name="tSearchPoint">The point in n-dimensional space to search.</param>
|
||||
/// <param name="kDistance">The distance function used to evaluate the points.</param>
|
||||
/// <param name="iMaxPoints">The max number of points which can be returned by this iterator. Capped to max in tree.</param>
|
||||
/// <param name="fThreshold">Threshold to apply to the search space. Negative numbers indicate that no threshold is applied.</param>
|
||||
public NearestNeighbour(KDNode<T> pRoot, double[] tSearchPoint, DistanceFunctions kDistance, int iMaxPoints, double fThreshold)
|
||||
{
|
||||
// Check the dimensionality of the search point.
|
||||
if (tSearchPoint.Length != pRoot.iDimensions)
|
||||
throw new Exception("Dimensionality of search point and kd-tree are not the same.");
|
||||
|
||||
// Store the search point.
|
||||
this.tSearchPoint = new double[tSearchPoint.Length];
|
||||
Array.Copy(tSearchPoint, this.tSearchPoint, tSearchPoint.Length);
|
||||
|
||||
// Store the point count, distance function and tree root.
|
||||
this.iPointsRemaining = Math.Min(iMaxPoints, pRoot.Size);
|
||||
this.fThreshold = fThreshold;
|
||||
this.kDistanceFunction = kDistance;
|
||||
this.pRoot = pRoot;
|
||||
this.iMaxPointsReturned = iMaxPoints;
|
||||
_CurrentDistance = -1;
|
||||
|
||||
// Create an interval heap for the points we check.
|
||||
this.pEvaluated = new IntervalHeap<T>();
|
||||
|
||||
// Create a min heap for the things we need to check.
|
||||
this.pPending = new MinHeap<KDNode<T>>();
|
||||
this.pPending.Insert(0, pRoot);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check for the next iterator item.
|
||||
/// </summary>
|
||||
/// <returns>True if we have one, false if not.</returns>
|
||||
public bool MoveNext()
|
||||
{
|
||||
// Bail if we are finished.
|
||||
if (iPointsRemaining == 0)
|
||||
{
|
||||
_Current = default(T);
|
||||
return false;
|
||||
}
|
||||
|
||||
// While we still have paths to evaluate.
|
||||
while (pPending.Size > 0 && (pEvaluated.Size == 0 || (pPending.MinKey < pEvaluated.MinKey)))
|
||||
{
|
||||
// If there are pending paths possibly closer than the nearest evaluated point, check it out
|
||||
KDNode<T> pCursor = pPending.Min;
|
||||
pPending.RemoveMin();
|
||||
|
||||
// Descend the tree, recording paths not taken
|
||||
while (!pCursor.IsLeaf)
|
||||
{
|
||||
KDNode<T> pNotTaken;
|
||||
|
||||
// If the seach point is larger, select the right path.
|
||||
if (tSearchPoint[pCursor.iSplitDimension] > pCursor.fSplitValue)
|
||||
{
|
||||
pNotTaken = pCursor.pLeft;
|
||||
pCursor = pCursor.pRight;
|
||||
}
|
||||
else
|
||||
{
|
||||
pNotTaken = pCursor.pRight;
|
||||
pCursor = pCursor.pLeft;
|
||||
}
|
||||
|
||||
// Calculate the shortest distance between the search point and the min and max bounds of the kd-node.
|
||||
double fDistance = kDistanceFunction.DistanceToRectangle(tSearchPoint, pNotTaken.tMinBound, pNotTaken.tMaxBound);
|
||||
|
||||
// If it is greater than the threshold, skip.
|
||||
if (fThreshold >= 0 && fDistance > fThreshold)
|
||||
{
|
||||
//pPending.Insert(fDistance, pNotTaken);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Only add the path we need more points or the node is closer than furthest point on list so far.
|
||||
if (pEvaluated.Size < iPointsRemaining || fDistance <= pEvaluated.MaxKey)
|
||||
{
|
||||
pPending.Insert(fDistance, pNotTaken);
|
||||
}
|
||||
}
|
||||
|
||||
// If all the points in this KD node are in one place.
|
||||
if (pCursor.bSinglePoint)
|
||||
{
|
||||
// Work out the distance between this point and the search point.
|
||||
double fDistance = kDistanceFunction.Distance(pCursor.tPoints[0], tSearchPoint);
|
||||
|
||||
// Skip if the point exceeds the threshold.
|
||||
// Technically this should never happen, but be prescise.
|
||||
if (fThreshold >= 0 && fDistance >= fThreshold)
|
||||
continue;
|
||||
|
||||
// Add the point if either need more points or it's closer than furthest on list so far.
|
||||
if (pEvaluated.Size < iPointsRemaining || fDistance <= pEvaluated.MaxKey)
|
||||
{
|
||||
for (int i = 0; i < pCursor.Size; ++i)
|
||||
{
|
||||
// If we don't need any more, replace max
|
||||
if (pEvaluated.Size == iPointsRemaining)
|
||||
pEvaluated.ReplaceMax(fDistance, pCursor.tData[i]);
|
||||
|
||||
// Otherwise insert.
|
||||
else
|
||||
pEvaluated.Insert(fDistance, pCursor.tData[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If the points in the KD node are spread out.
|
||||
else
|
||||
{
|
||||
// Treat the distance of each point seperately.
|
||||
for (int i = 0; i < pCursor.Size; ++i)
|
||||
{
|
||||
// Compute the distance between the points.
|
||||
double fDistance = kDistanceFunction.Distance(pCursor.tPoints[i], tSearchPoint);
|
||||
|
||||
// Skip if it exceeds the threshold.
|
||||
if (fThreshold >= 0 && fDistance >= fThreshold)
|
||||
continue;
|
||||
|
||||
// Insert the point if we have more to take.
|
||||
if (pEvaluated.Size < iPointsRemaining)
|
||||
pEvaluated.Insert(fDistance, pCursor.tData[i]);
|
||||
|
||||
// Otherwise replace the max.
|
||||
else if (fDistance < pEvaluated.MaxKey)
|
||||
pEvaluated.ReplaceMax(fDistance, pCursor.tData[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Select the point with the smallest distance.
|
||||
if (pEvaluated.Size == 0)
|
||||
return false;
|
||||
|
||||
iPointsRemaining--;
|
||||
_CurrentDistance = pEvaluated.MinKey;
|
||||
_Current = pEvaluated.Min;
|
||||
pEvaluated.RemoveMin();
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reset the iterator.
|
||||
/// </summary>
|
||||
public void Reset()
|
||||
{
|
||||
// Store the point count and the distance function.
|
||||
this.iPointsRemaining = Math.Min(iMaxPointsReturned, pRoot.Size);
|
||||
_CurrentDistance = -1;
|
||||
|
||||
// Create an interval heap for the points we check.
|
||||
this.pEvaluated = new IntervalHeap<T>();
|
||||
|
||||
// Create a min heap for the things we need to check.
|
||||
this.pPending = new MinHeap<KDNode<T>>();
|
||||
this.pPending.Insert(0, pRoot);
|
||||
}
|
||||
|
||||
public T Current
|
||||
{
|
||||
get { return _Current; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return the distance of the current value to the search point.
|
||||
/// </summary>
|
||||
public double CurrentDistance
|
||||
{
|
||||
get { return _CurrentDistance; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return the current value referenced by the iterator as an object.
|
||||
/// </summary>
|
||||
object IEnumerator.Current
|
||||
{
|
||||
get { return _Current; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return the current value referenced by the iterator.
|
||||
/// </summary>
|
||||
T IEnumerator<T>.Current
|
||||
{
|
||||
get { return _Current; }
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
public IEnumerator<T> GetEnumerator()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 838799301f7b90c4c9ddad55847c8093
|
||||
timeCreated: 1516636092
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,38 @@
|
||||
namespace Mapbox.Unity.MeshGeneration
|
||||
{
|
||||
using UnityEngine;
|
||||
using KDTree;
|
||||
using Mapbox.Unity.MeshGeneration.Data;
|
||||
using System;
|
||||
|
||||
/// <summary>
|
||||
/// KdTree Collection is a feature collection using KdTree mainly for distance based searchs like "find all buildings 100m around
|
||||
/// player" or "find 10 closest buildings to this point".
|
||||
/// KdTree structures focus on search performance so querying for features will be very fast using this collection. On the other
|
||||
/// hand it's not good for dynamic/moving entities but we don't have such features on map so it's one of the best options for maps.
|
||||
/// </summary>
|
||||
|
||||
[CreateAssetMenu(menuName = "Mapbox/Feature Collections/Kd Tree Collection")]
|
||||
public class KdTreeCollection : FeatureCollectionBase
|
||||
{
|
||||
private KDTree<VectorEntity> _entities;
|
||||
public int Count;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
_entities = new KDTree.KDTree<VectorEntity>(2);
|
||||
}
|
||||
|
||||
public override void AddFeature(double[] position, VectorEntity ve)
|
||||
{
|
||||
_entities.AddPoint(position, ve);
|
||||
Count = _entities.Size;
|
||||
}
|
||||
|
||||
public NearestNeighbour<VectorEntity> NearestNeighbors(double[] v, int maxCount, float range)
|
||||
{
|
||||
return _entities.NearestNeighbors(v, maxCount, range);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 82ed8ed837e25084bbe8a37d53c5b77b
|
||||
timeCreated: 1519741039
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,49 @@
|
||||
namespace Mapbox.Unity.MeshGeneration.Data
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using Utils;
|
||||
|
||||
// TODO: Do we need this class? Why not just use `Mesh`?
|
||||
public class MeshData
|
||||
{
|
||||
public Vector3 PositionInTile;
|
||||
public List<int> Edges;
|
||||
public Vector2 MercatorCenter;
|
||||
public RectD TileRect;
|
||||
public List<Vector3> Vertices;
|
||||
public List<Vector3> Normals;
|
||||
public List<Vector4> Tangents;
|
||||
public List<List<int>> Triangles;
|
||||
public List<List<Vector2>> UV;
|
||||
|
||||
public MeshData()
|
||||
{
|
||||
Edges = new List<int>();
|
||||
Vertices = new List<Vector3>();
|
||||
Normals = new List<Vector3>();
|
||||
Tangents = new List<Vector4>();
|
||||
Triangles = new List<List<int>>();
|
||||
UV = new List<List<Vector2>>();
|
||||
UV.Add(new List<Vector2>());
|
||||
}
|
||||
|
||||
internal void Clear()
|
||||
{
|
||||
Edges.Clear();
|
||||
Vertices.Clear();
|
||||
Normals.Clear();
|
||||
Tangents.Clear();
|
||||
|
||||
foreach (var item in Triangles)
|
||||
{
|
||||
item.Clear();
|
||||
}
|
||||
foreach (var item in UV)
|
||||
{
|
||||
item.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2dc52b40d4a914f4d88ef8d5547272b5
|
||||
timeCreated: 1478552106
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
356
Assets/Mapbox SDK/Mapbox/Unity/MeshGeneration/Data/UnityTile.cs
Normal file
356
Assets/Mapbox SDK/Mapbox/Unity/MeshGeneration/Data/UnityTile.cs
Normal file
@@ -0,0 +1,356 @@
|
||||
using Mapbox.Unity.Map.Interfaces;
|
||||
|
||||
namespace Mapbox.Unity.MeshGeneration.Data
|
||||
{
|
||||
using UnityEngine;
|
||||
using Mapbox.Unity.MeshGeneration.Enums;
|
||||
using Mapbox.Unity.Utilities;
|
||||
using Utils;
|
||||
using Mapbox.Map;
|
||||
using System;
|
||||
using Mapbox.Unity.Map;
|
||||
using System.Collections.Generic;
|
||||
using Mapbox.Unity.MeshGeneration.Factories;
|
||||
|
||||
public class UnityTile : MonoBehaviour
|
||||
{
|
||||
public TileTerrainType ElevationType;
|
||||
|
||||
[SerializeField]
|
||||
private Texture2D _rasterData;
|
||||
public VectorTile VectorData { get; private set; }
|
||||
private Texture2D _heightTexture;
|
||||
private float[] _heightData;
|
||||
|
||||
private Texture2D _loadingTexture;
|
||||
//keeping track of tile objects to be able to cancel them safely if tile is destroyed before data fetching finishes
|
||||
private List<Tile> _tiles = new List<Tile>();
|
||||
|
||||
public bool IsRecycled = false;
|
||||
|
||||
#region CachedUnityComponents
|
||||
MeshRenderer _meshRenderer;
|
||||
public MeshRenderer MeshRenderer
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_meshRenderer == null)
|
||||
{
|
||||
_meshRenderer = GetComponent<MeshRenderer>();
|
||||
if (_meshRenderer == null)
|
||||
{
|
||||
_meshRenderer = gameObject.AddComponent<MeshRenderer>();
|
||||
}
|
||||
}
|
||||
return _meshRenderer;
|
||||
}
|
||||
}
|
||||
|
||||
private MeshFilter _meshFilter;
|
||||
public MeshFilter MeshFilter
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_meshFilter == null)
|
||||
{
|
||||
_meshFilter = GetComponent<MeshFilter>();
|
||||
if (_meshFilter == null)
|
||||
{
|
||||
_meshFilter = gameObject.AddComponent<MeshFilter>();
|
||||
ElevationType = TileTerrainType.None;
|
||||
}
|
||||
}
|
||||
return _meshFilter;
|
||||
}
|
||||
}
|
||||
|
||||
private Collider _collider;
|
||||
public Collider Collider
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_collider == null)
|
||||
{
|
||||
_collider = GetComponent<Collider>();
|
||||
}
|
||||
return _collider;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Tile Positon/Scale Properties
|
||||
public RectD Rect { get; private set; }
|
||||
public int InitialZoom { get; private set; }
|
||||
public int CurrentZoom { get; private set; }
|
||||
public float TileScale { get; private set; }
|
||||
public UnwrappedTileId UnwrappedTileId { get; private set; }
|
||||
public CanonicalTileId CanonicalTileId { get; private set; }
|
||||
|
||||
private float _relativeScale;
|
||||
#endregion
|
||||
|
||||
[SerializeField]
|
||||
private TilePropertyState _rasterDataState;
|
||||
public TilePropertyState RasterDataState
|
||||
{
|
||||
get
|
||||
{
|
||||
return _rasterDataState;
|
||||
}
|
||||
internal set
|
||||
{
|
||||
if (_rasterDataState != value)
|
||||
{
|
||||
_rasterDataState = value;
|
||||
OnRasterDataChanged(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
[SerializeField]
|
||||
private TilePropertyState _heightDataState;
|
||||
public TilePropertyState HeightDataState
|
||||
{
|
||||
get
|
||||
{
|
||||
return _heightDataState;
|
||||
}
|
||||
internal set
|
||||
{
|
||||
if (_heightDataState != value)
|
||||
{
|
||||
_heightDataState = value;
|
||||
OnHeightDataChanged(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
[SerializeField]
|
||||
private TilePropertyState _vectorDataState;
|
||||
public TilePropertyState VectorDataState
|
||||
{
|
||||
get
|
||||
{
|
||||
return _vectorDataState;
|
||||
}
|
||||
internal set
|
||||
{
|
||||
if (_vectorDataState != value)
|
||||
{
|
||||
_vectorDataState = value;
|
||||
OnVectorDataChanged(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
private TilePropertyState _tileState = TilePropertyState.None;
|
||||
public TilePropertyState TileState
|
||||
{
|
||||
get
|
||||
{
|
||||
return _tileState;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (_tileState != value)
|
||||
{
|
||||
_tileState = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public event Action<UnityTile> OnHeightDataChanged = delegate { };
|
||||
public event Action<UnityTile> OnRasterDataChanged = delegate { };
|
||||
public event Action<UnityTile> OnVectorDataChanged = delegate { };
|
||||
|
||||
private bool _isInitialized = false;
|
||||
|
||||
internal void Initialize(IMapReadable map, UnwrappedTileId tileId, float scale, int zoom, Texture2D loadingTexture = null)
|
||||
{
|
||||
ElevationType = TileTerrainType.None;
|
||||
TileScale = scale;
|
||||
_relativeScale = 1 / Mathf.Cos(Mathf.Deg2Rad * (float)map.CenterLatitudeLongitude.x);
|
||||
Rect = Conversions.TileBounds(tileId);
|
||||
UnwrappedTileId = tileId;
|
||||
CanonicalTileId = tileId.Canonical;
|
||||
_loadingTexture = loadingTexture;
|
||||
|
||||
float scaleFactor = 1.0f;
|
||||
if (_isInitialized == false)
|
||||
{
|
||||
_isInitialized = true;
|
||||
InitialZoom = zoom;
|
||||
}
|
||||
CurrentZoom = zoom;
|
||||
scaleFactor = Mathf.Pow(2, (map.InitialZoom - zoom));
|
||||
gameObject.transform.localScale = new Vector3(scaleFactor, scaleFactor, scaleFactor);
|
||||
gameObject.SetActive(true);
|
||||
|
||||
IsRecycled = false;
|
||||
//MeshRenderer.enabled = true;
|
||||
|
||||
|
||||
// Setup Loading as initial state - Unregistered
|
||||
// When tile registers with factories, it will set the appropriate state.
|
||||
// None, if Factory source is None, Loading otherwise.
|
||||
}
|
||||
|
||||
internal void Recycle()
|
||||
{
|
||||
if (_loadingTexture && MeshRenderer != null)
|
||||
{
|
||||
MeshRenderer.material.mainTexture = _loadingTexture;
|
||||
//MeshRenderer.enabled = false;
|
||||
}
|
||||
|
||||
gameObject.SetActive(false);
|
||||
IsRecycled = true;
|
||||
|
||||
// Reset internal state.
|
||||
RasterDataState = TilePropertyState.Unregistered;
|
||||
HeightDataState = TilePropertyState.Unregistered;
|
||||
VectorDataState = TilePropertyState.Unregistered;
|
||||
TileState = TilePropertyState.Unregistered;
|
||||
|
||||
OnHeightDataChanged = delegate { };
|
||||
OnRasterDataChanged = delegate { };
|
||||
OnVectorDataChanged = delegate { };
|
||||
|
||||
Cancel();
|
||||
_tiles.Clear();
|
||||
}
|
||||
|
||||
public void SetHeightData(byte[] data, float heightMultiplier = 1f, bool useRelative = false, bool addCollider = false)
|
||||
{
|
||||
if (HeightDataState != TilePropertyState.Unregistered)
|
||||
{
|
||||
//reset height data
|
||||
if(data == null)
|
||||
{
|
||||
_heightData = new float[256 * 256];
|
||||
HeightDataState = TilePropertyState.None;
|
||||
return;
|
||||
}
|
||||
|
||||
// HACK: compute height values for terrain. We could probably do this without a texture2d.
|
||||
if (_heightTexture == null)
|
||||
{
|
||||
_heightTexture = new Texture2D(0, 0);
|
||||
}
|
||||
|
||||
_heightTexture.LoadImage(data);
|
||||
byte[] rgbData = _heightTexture.GetRawTextureData();
|
||||
|
||||
// Get rid of this temporary texture. We don't need to bloat memory.
|
||||
_heightTexture.LoadImage(null);
|
||||
|
||||
if (_heightData == null)
|
||||
{
|
||||
_heightData = new float[256 * 256];
|
||||
}
|
||||
|
||||
var relativeScale = useRelative ? _relativeScale : 1f;
|
||||
for (int xx = 0; xx < 256; ++xx)
|
||||
{
|
||||
for (int yy = 0; yy < 256; ++yy)
|
||||
{
|
||||
float r = rgbData[(xx * 256 + yy) * 4 + 1];
|
||||
float g = rgbData[(xx * 256 + yy) * 4 + 2];
|
||||
float b = rgbData[(xx * 256 + yy) * 4 + 3];
|
||||
_heightData[xx * 256 + yy] = relativeScale * heightMultiplier * Conversions.GetAbsoluteHeightFromColor(r, g, b);
|
||||
}
|
||||
}
|
||||
|
||||
if (addCollider && gameObject.GetComponent<MeshCollider>() == null)
|
||||
{
|
||||
gameObject.AddComponent<MeshCollider>();
|
||||
}
|
||||
|
||||
HeightDataState = TilePropertyState.Loaded;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetRasterData(byte[] data, bool useMipMap = true, bool useCompression = false)
|
||||
{
|
||||
// Don't leak the texture, just reuse it.
|
||||
if (RasterDataState != TilePropertyState.Unregistered)
|
||||
{
|
||||
//reset image on null data
|
||||
if (data == null)
|
||||
{
|
||||
MeshRenderer.material.mainTexture = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (_rasterData == null)
|
||||
{
|
||||
_rasterData = new Texture2D(0, 0, TextureFormat.RGB24, useMipMap);
|
||||
_rasterData.wrapMode = TextureWrapMode.Clamp;
|
||||
}
|
||||
|
||||
_rasterData.LoadImage(data);
|
||||
if (useCompression)
|
||||
{
|
||||
// High quality = true seems to decrease image quality?
|
||||
_rasterData.Compress(false);
|
||||
}
|
||||
|
||||
MeshRenderer.material.mainTexture = _rasterData;
|
||||
RasterDataState = TilePropertyState.Loaded;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetVectorData(VectorTile vectorTile)
|
||||
{
|
||||
if (VectorDataState != TilePropertyState.Unregistered)
|
||||
{
|
||||
VectorData = vectorTile;
|
||||
}
|
||||
}
|
||||
|
||||
public float QueryHeightData(float x, float y)
|
||||
{
|
||||
if (_heightData != null)
|
||||
{
|
||||
var intX = (int)Mathf.Clamp(x * 256, 0, 255);
|
||||
var intY = (int)Mathf.Clamp(y * 256, 0, 255);
|
||||
return _heightData[intY * 256 + intX] * TileScale;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void SetLoadingTexture(Texture2D texture)
|
||||
{
|
||||
MeshRenderer.material.mainTexture = texture;
|
||||
}
|
||||
|
||||
public Texture2D GetRasterData()
|
||||
{
|
||||
return _rasterData;
|
||||
}
|
||||
|
||||
internal void AddTile(Tile tile)
|
||||
{
|
||||
_tiles.Add(tile);
|
||||
}
|
||||
|
||||
public void Cancel()
|
||||
{
|
||||
for (int i = 0, _tilesCount = _tiles.Count; i < _tilesCount; i++)
|
||||
{
|
||||
_tiles[i].Cancel();
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void OnDestroy()
|
||||
{
|
||||
Cancel();
|
||||
if (_heightTexture != null)
|
||||
{
|
||||
Destroy(_heightTexture);
|
||||
}
|
||||
if (_rasterData != null)
|
||||
{
|
||||
Destroy(_rasterData);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: def0795d2b7c06547bcfdb57ec6f201b
|
||||
timeCreated: 1478541275
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,124 @@
|
||||
namespace Mapbox.Unity.MeshGeneration.Data
|
||||
{
|
||||
using Mapbox.VectorTile;
|
||||
using System.Collections.Generic;
|
||||
using Mapbox.VectorTile.Geometry;
|
||||
using UnityEngine;
|
||||
using Mapbox.Utils;
|
||||
using Mapbox.Unity.Utilities;
|
||||
|
||||
public class VectorFeatureUnity
|
||||
{
|
||||
public VectorTileFeature Data;
|
||||
public Dictionary<string, object> Properties;
|
||||
public List<List<Vector3>> Points = new List<List<Vector3>>();
|
||||
public UnityTile Tile;
|
||||
|
||||
private double _rectSizex;
|
||||
private double _rectSizey;
|
||||
private int _geomCount;
|
||||
private int _pointCount;
|
||||
private List<Vector3> _newPoints = new List<Vector3>();
|
||||
private List<List<Point2d<float>>> _geom;
|
||||
|
||||
public VectorFeatureUnity()
|
||||
{
|
||||
Points = new List<List<Vector3>>();
|
||||
}
|
||||
|
||||
public VectorFeatureUnity(VectorTileFeature feature, UnityTile tile, float layerExtent, bool buildingsWithUniqueIds = false)
|
||||
{
|
||||
Data = feature;
|
||||
Properties = Data.GetProperties();
|
||||
Points.Clear();
|
||||
Tile = tile;
|
||||
|
||||
//this is a temp hack until we figure out how streets ids works
|
||||
if (buildingsWithUniqueIds == true) //ids from building dataset is big ulongs
|
||||
{
|
||||
_geom = feature.Geometry<float>(); //and we're not clipping by passing no parameters
|
||||
}
|
||||
else //streets ids, will require clipping
|
||||
{
|
||||
_geom = feature.Geometry<float>(0); //passing zero means clip at tile edge
|
||||
}
|
||||
|
||||
_rectSizex = tile.Rect.Size.x;
|
||||
_rectSizey = tile.Rect.Size.y;
|
||||
|
||||
_geomCount = _geom.Count;
|
||||
for (int i = 0; i < _geomCount; i++)
|
||||
{
|
||||
_pointCount = _geom[i].Count;
|
||||
_newPoints = new List<Vector3>(_pointCount);
|
||||
for (int j = 0; j < _pointCount; j++)
|
||||
{
|
||||
var point = _geom[i][j];
|
||||
_newPoints.Add(new Vector3((float)(point.X / layerExtent * _rectSizex - (_rectSizex / 2)) * tile.TileScale, 0, (float)((layerExtent - point.Y) / layerExtent * _rectSizey - (_rectSizey / 2)) * tile.TileScale));
|
||||
}
|
||||
Points.Add(_newPoints);
|
||||
}
|
||||
}
|
||||
|
||||
public VectorFeatureUnity(VectorTileFeature feature, List<List<Point2d<float>>> geom, UnityTile tile, float layerExtent, bool buildingsWithUniqueIds = false)
|
||||
{
|
||||
Data = feature;
|
||||
Properties = Data.GetProperties();
|
||||
Points.Clear();
|
||||
Tile = tile;
|
||||
_geom = geom;
|
||||
|
||||
_rectSizex = tile.Rect.Size.x;
|
||||
_rectSizey = tile.Rect.Size.y;
|
||||
|
||||
_geomCount = _geom.Count;
|
||||
for (int i = 0; i < _geomCount; i++)
|
||||
{
|
||||
_pointCount = _geom[i].Count;
|
||||
_newPoints = new List<Vector3>(_pointCount);
|
||||
for (int j = 0; j < _pointCount; j++)
|
||||
{
|
||||
var point = _geom[i][j];
|
||||
_newPoints.Add(new Vector3((float)(point.X / layerExtent * _rectSizex - (_rectSizex / 2)) * tile.TileScale, 0, (float)((layerExtent - point.Y) / layerExtent * _rectSizey - (_rectSizey / 2)) * tile.TileScale));
|
||||
}
|
||||
Points.Add(_newPoints);
|
||||
}
|
||||
}
|
||||
|
||||
public bool ContainsLatLon(Vector2d coord)
|
||||
{
|
||||
////first check tile
|
||||
var coordinateTileId = Conversions.LatitudeLongitudeToTileId(
|
||||
coord.x, coord.y, Tile.CurrentZoom);
|
||||
|
||||
if (Points.Count > 0)
|
||||
{
|
||||
var from = Conversions.LatLonToMeters(coord.x, coord.y);
|
||||
|
||||
var to = new Vector2d((Points[0][0].x / Tile.TileScale) + Tile.Rect.Center.x, (Points[0][0].z / Tile.TileScale) + Tile.Rect.Center.y);
|
||||
var dist = Vector2d.Distance(from, to);
|
||||
if (Mathd.Abs(dist) < 50)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//Debug.Log("Distance -> " + dist);
|
||||
{
|
||||
if ((!coordinateTileId.Canonical.Equals(Tile.CanonicalTileId)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//then check polygon
|
||||
var point = Conversions.LatitudeLongitudeToVectorTilePosition(coord, Tile.CurrentZoom);
|
||||
var output = PolygonUtils.PointInPolygon(new Point2d<float>(point.x, point.y), _geom);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 161842d5b19bcf24a842752aadb1da26
|
||||
timeCreated: 1485209878
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
9
Assets/Mapbox SDK/Mapbox/Unity/MeshGeneration/Enums.meta
Normal file
9
Assets/Mapbox SDK/Mapbox/Unity/MeshGeneration/Enums.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 665b654cd42daed428aacdad0c045d85
|
||||
folderAsset: yes
|
||||
timeCreated: 1485206853
|
||||
licenseType: Pro
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace Mapbox.Unity.MeshGeneration.Enums
|
||||
{
|
||||
public enum TilePropertyState
|
||||
{
|
||||
Unregistered,
|
||||
None,
|
||||
Loading,
|
||||
Loaded,
|
||||
Error,
|
||||
Cancelled,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 63187d4e1f6f0ad40ae6e6a5da6ebef1
|
||||
timeCreated: 1485206857
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ed0cb36528ae4584b9d01d98fee861f5
|
||||
folderAsset: yes
|
||||
timeCreated: 1494961030
|
||||
licenseType: Pro
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,140 @@
|
||||
namespace Mapbox.Unity.MeshGeneration.Factories
|
||||
{
|
||||
using Mapbox.Platform;
|
||||
using Mapbox.Unity.MeshGeneration.Data;
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using Mapbox.Unity.Map;
|
||||
using Mapbox.Map;
|
||||
using System.Collections.Generic;
|
||||
|
||||
/// <summary>
|
||||
/// Factories
|
||||
/// Factories corresponds to Mapbox Api endpoints for the most part.Terrain factory uses terrain rgb api,
|
||||
/// Image factory uses raster image api and vector tile factory uses vector data api.
|
||||
/// Only exception to this at the moment is Directions factory, which is a monobehaviour in Drive demo scene
|
||||
/// but we’ll probably rework that in the future as well.
|
||||
/// Factories do the api calls.They are responsible for reporting progress and logging/reporting any network issue.
|
||||
/// They can also keep track of tiles if necessary (i.e.update/refresh data after initial creation)
|
||||
/// Factories processes the received data in different ways.
|
||||
/// Terrain factories creates the mesh from the received data (or a flat terrain depending the settings)
|
||||
/// MapImage factory applies received image to tile game object.
|
||||
/// Vector Tile Factory deals with a much more complex and detailed data compared to other two so it doesn’t do the
|
||||
/// whole processing itself and uses some subclasses (LayerVisualizers) to do it.
|
||||
/// Creating a custom factory is a good idea if you want to fetch raw data and process is in a totally different
|
||||
/// custom way, like creating terrain and then cut off water areas from terrain mesh.
|
||||
/// Vector Tile factory, for example, is built to be flexible and work with multiple vector layers. But if you
|
||||
/// don’t need that all, you can create a custom version of it and process what you need in a much more concrete
|
||||
/// and performant way.
|
||||
/// Another example here would be custom terrain mesh. Current terrain factories work with a custom sized grid
|
||||
/// and apply height data on that. By creating a custom terrain factory, you can have a custom mesh instead of a grid,
|
||||
/// optimize and minimize vertex count etc.
|
||||
/// </summary>
|
||||
public abstract class AbstractTileFactory : ScriptableObject
|
||||
{
|
||||
protected IFileSource _fileSource;
|
||||
|
||||
protected LayerProperties _options;
|
||||
public LayerProperties Options
|
||||
{
|
||||
get
|
||||
{
|
||||
return _options;
|
||||
}
|
||||
}
|
||||
|
||||
protected HashSet<UnityTile> _tilesWaitingResponse;
|
||||
protected HashSet<UnityTile> _tilesWaitingProcessing;
|
||||
|
||||
/// <summary>
|
||||
/// The <c>OnTileError</c> event triggers when there's <c>Tile</c> error.
|
||||
/// Returns a <see cref="T:Mapbox.Map.TileErrorEventArgs"/> instance as a parameter, for the tile on which error occurred.
|
||||
/// </summary>
|
||||
|
||||
public virtual void SetOptions(LayerProperties options)
|
||||
{
|
||||
_options = options;
|
||||
}
|
||||
|
||||
protected virtual void OnErrorOccurred(UnityTile tile, TileErrorEventArgs e)
|
||||
{
|
||||
EventHandler<TileErrorEventArgs> handler = OnTileError;
|
||||
if (handler != null)
|
||||
{
|
||||
handler(this, e);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void Initialize(IFileSource fileSource)
|
||||
{
|
||||
_fileSource = fileSource;
|
||||
_tilesWaitingResponse = new HashSet<UnityTile>();
|
||||
_tilesWaitingProcessing = new HashSet<UnityTile>();
|
||||
OnInitialized();
|
||||
}
|
||||
|
||||
public virtual void Register(UnityTile tile)
|
||||
{
|
||||
OnRegistered(tile);
|
||||
}
|
||||
|
||||
public virtual void PostProcess(UnityTile tile)
|
||||
{
|
||||
OnPostProcess(tile);
|
||||
}
|
||||
public virtual void Unregister(UnityTile tile)
|
||||
{
|
||||
OnUnregistered(tile);
|
||||
}
|
||||
|
||||
public virtual void UnbindEvents()
|
||||
{
|
||||
OnUnbindEvents();
|
||||
}
|
||||
|
||||
public virtual void UpdateTileProperty(UnityTile tile, LayerUpdateArgs updateArgs)
|
||||
{
|
||||
updateArgs.property.UpdateProperty(tile);
|
||||
|
||||
if (updateArgs.property.NeedsForceUpdate())
|
||||
{
|
||||
Register(tile);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void Reset()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
protected abstract void OnInitialized();
|
||||
|
||||
protected abstract void OnRegistered(UnityTile tile);
|
||||
protected abstract void OnPostProcess(UnityTile tile);
|
||||
protected abstract void OnUnregistered(UnityTile tile);
|
||||
protected abstract void OnUnbindEvents();
|
||||
|
||||
#region Events
|
||||
public event EventHandler<TileErrorEventArgs> OnTileError;
|
||||
protected virtual void OnErrorOccurred(TileErrorEventArgs e)
|
||||
{
|
||||
EventHandler<TileErrorEventArgs> handler = OnTileError;
|
||||
if (handler != null)
|
||||
{
|
||||
handler(this, e);
|
||||
}
|
||||
}
|
||||
|
||||
public event EventHandler TileFactoryHasChanged;
|
||||
protected virtual void UpdateTileFactory(object sender, System.EventArgs args)
|
||||
{
|
||||
System.EventHandler handler = TileFactoryHasChanged;
|
||||
if (handler != null)
|
||||
{
|
||||
handler(this, args);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 14c4d9b107de24a028e1bd452df7ae29
|
||||
timeCreated: 1494972519
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,165 @@
|
||||
namespace Mapbox.Unity.MeshGeneration.Factories
|
||||
{
|
||||
using UnityEngine;
|
||||
using Mapbox.Directions;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Mapbox.Unity.Map;
|
||||
using Data;
|
||||
using Modifiers;
|
||||
using Mapbox.Utils;
|
||||
using Mapbox.Unity.Utilities;
|
||||
using System.Collections;
|
||||
|
||||
public class DirectionsFactory : MonoBehaviour
|
||||
{
|
||||
[SerializeField]
|
||||
AbstractMap _map;
|
||||
|
||||
[SerializeField]
|
||||
MeshModifier[] MeshModifiers;
|
||||
[SerializeField]
|
||||
Material _material;
|
||||
|
||||
[SerializeField]
|
||||
Transform[] _waypoints;
|
||||
private List<Vector3> _cachedWaypoints;
|
||||
|
||||
[SerializeField]
|
||||
[Range(1,10)]
|
||||
private float UpdateFrequency = 2;
|
||||
|
||||
|
||||
|
||||
private Directions _directions;
|
||||
private int _counter;
|
||||
|
||||
GameObject _directionsGO;
|
||||
private bool _recalculateNext;
|
||||
|
||||
protected virtual void Awake()
|
||||
{
|
||||
if (_map == null)
|
||||
{
|
||||
_map = FindObjectOfType<AbstractMap>();
|
||||
}
|
||||
_directions = MapboxAccess.Instance.Directions;
|
||||
_map.OnInitialized += Query;
|
||||
_map.OnUpdated += Query;
|
||||
}
|
||||
|
||||
public void Start()
|
||||
{
|
||||
_cachedWaypoints = new List<Vector3>(_waypoints.Length);
|
||||
foreach (var item in _waypoints)
|
||||
{
|
||||
_cachedWaypoints.Add(item.position);
|
||||
}
|
||||
_recalculateNext = false;
|
||||
|
||||
foreach (var modifier in MeshModifiers)
|
||||
{
|
||||
modifier.Initialize();
|
||||
}
|
||||
|
||||
StartCoroutine(QueryTimer());
|
||||
}
|
||||
|
||||
protected virtual void OnDestroy()
|
||||
{
|
||||
_map.OnInitialized -= Query;
|
||||
_map.OnUpdated -= Query;
|
||||
}
|
||||
|
||||
void Query()
|
||||
{
|
||||
var count = _waypoints.Length;
|
||||
var wp = new Vector2d[count];
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
wp[i] = _waypoints[i].GetGeoPosition(_map.CenterMercator, _map.WorldRelativeScale);
|
||||
}
|
||||
var _directionResource = new DirectionResource(wp, RoutingProfile.Driving);
|
||||
_directionResource.Steps = true;
|
||||
_directions.Query(_directionResource, HandleDirectionsResponse);
|
||||
}
|
||||
|
||||
public IEnumerator QueryTimer()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
yield return new WaitForSeconds(UpdateFrequency);
|
||||
for (int i = 0; i < _waypoints.Length; i++)
|
||||
{
|
||||
if (_waypoints[i].position != _cachedWaypoints[i])
|
||||
{
|
||||
_recalculateNext = true;
|
||||
_cachedWaypoints[i] = _waypoints[i].position;
|
||||
}
|
||||
}
|
||||
|
||||
if (_recalculateNext)
|
||||
{
|
||||
Query();
|
||||
_recalculateNext = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HandleDirectionsResponse(DirectionsResponse response)
|
||||
{
|
||||
if (response == null || null == response.Routes || response.Routes.Count < 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var meshData = new MeshData();
|
||||
var dat = new List<Vector3>();
|
||||
foreach (var point in response.Routes[0].Geometry)
|
||||
{
|
||||
dat.Add(Conversions.GeoToWorldPosition(point.x, point.y, _map.CenterMercator, _map.WorldRelativeScale).ToVector3xz());
|
||||
}
|
||||
|
||||
var feat = new VectorFeatureUnity();
|
||||
feat.Points.Add(dat);
|
||||
|
||||
foreach (MeshModifier mod in MeshModifiers.Where(x => x.Active))
|
||||
{
|
||||
mod.Run(feat, meshData, _map.WorldRelativeScale);
|
||||
}
|
||||
|
||||
CreateGameObject(meshData);
|
||||
}
|
||||
|
||||
GameObject CreateGameObject(MeshData data)
|
||||
{
|
||||
if (_directionsGO != null)
|
||||
{
|
||||
Destroy(_directionsGO);
|
||||
}
|
||||
_directionsGO = new GameObject("direction waypoint " + " entity");
|
||||
var mesh = _directionsGO.AddComponent<MeshFilter>().mesh;
|
||||
mesh.subMeshCount = data.Triangles.Count;
|
||||
|
||||
mesh.SetVertices(data.Vertices);
|
||||
_counter = data.Triangles.Count;
|
||||
for (int i = 0; i < _counter; i++)
|
||||
{
|
||||
var triangle = data.Triangles[i];
|
||||
mesh.SetTriangles(triangle, i);
|
||||
}
|
||||
|
||||
_counter = data.UV.Count;
|
||||
for (int i = 0; i < _counter; i++)
|
||||
{
|
||||
var uv = data.UV[i];
|
||||
mesh.SetUVs(i, uv);
|
||||
}
|
||||
|
||||
mesh.RecalculateNormals();
|
||||
_directionsGO.AddComponent<MeshRenderer>().material = _material;
|
||||
return _directionsGO;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 44d601772d842a94cb5ab933dca6bd4f
|
||||
timeCreated: 1485217963
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,54 @@
|
||||
using Mapbox.Map;
|
||||
using Mapbox.Unity.MeshGeneration.Data;
|
||||
using Mapbox.Unity.MeshGeneration.Enums;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
public class ImageDataFetcher : DataFetcher
|
||||
{
|
||||
public Action<UnityTile, RasterTile> DataRecieved = (t, s) => { };
|
||||
public Action<UnityTile, RasterTile, TileErrorEventArgs> FetchingError = (t, r, s) => { };
|
||||
|
||||
//tile here should be totally optional and used only not to have keep a dictionary in terrain factory base
|
||||
public override void FetchData(DataFetcherParameters parameters)
|
||||
{
|
||||
var imageDataParameters = parameters as ImageDataFetcherParameters;
|
||||
if(imageDataParameters == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
RasterTile rasterTile;
|
||||
if (imageDataParameters.mapid.StartsWith("mapbox://", StringComparison.Ordinal))
|
||||
{
|
||||
rasterTile = imageDataParameters.useRetina ? new RetinaRasterTile() : new RasterTile();
|
||||
}
|
||||
else
|
||||
{
|
||||
rasterTile = imageDataParameters.useRetina ? new ClassicRetinaRasterTile() : new ClassicRasterTile();
|
||||
}
|
||||
|
||||
if (imageDataParameters.tile != null)
|
||||
{
|
||||
imageDataParameters.tile.AddTile(rasterTile);
|
||||
}
|
||||
|
||||
rasterTile.Initialize(_fileSource, imageDataParameters.tile.CanonicalTileId, imageDataParameters.mapid, () =>
|
||||
{
|
||||
if (imageDataParameters.tile.CanonicalTileId != rasterTile.Id)
|
||||
{
|
||||
//this means tile object is recycled and reused. Returned data doesn't belong to this tile but probably the previous one. So we're trashing it.
|
||||
return;
|
||||
}
|
||||
|
||||
if (rasterTile.HasError)
|
||||
{
|
||||
FetchingError(imageDataParameters.tile, rasterTile, new TileErrorEventArgs(imageDataParameters.tile.CanonicalTileId, rasterTile.GetType(), imageDataParameters.tile, rasterTile.Exceptions));
|
||||
}
|
||||
else
|
||||
{
|
||||
DataRecieved(imageDataParameters.tile, rasterTile);
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6293dc8bcca4970409cf9f4e93d178b5
|
||||
timeCreated: 1524268181
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,166 @@
|
||||
namespace Mapbox.Unity.MeshGeneration.Factories
|
||||
{
|
||||
using System;
|
||||
using Mapbox.Map;
|
||||
using UnityEngine;
|
||||
using Mapbox.Unity.MeshGeneration.Enums;
|
||||
using Mapbox.Unity.MeshGeneration.Data;
|
||||
using Mapbox.Unity.Utilities;
|
||||
using Mapbox.Unity.Map;
|
||||
using System.Collections.Generic;
|
||||
|
||||
public enum MapImageType
|
||||
{
|
||||
BasicMapboxStyle,
|
||||
Custom,
|
||||
None
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Uses raster image services to create materials & textures for terrain
|
||||
/// </summary>
|
||||
[CreateAssetMenu(menuName = "Mapbox/Factories/Image Factory")]
|
||||
public class MapImageFactory : AbstractTileFactory
|
||||
{
|
||||
[SerializeField]
|
||||
ImageryLayerProperties _properties;
|
||||
protected ImageDataFetcher DataFetcher;
|
||||
|
||||
public ImageryLayerProperties Properties
|
||||
{
|
||||
get
|
||||
{
|
||||
return _properties;
|
||||
}
|
||||
}
|
||||
|
||||
public string MapId
|
||||
{
|
||||
get
|
||||
{
|
||||
return _properties.sourceOptions.Id;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
_properties.sourceOptions.Id = value;
|
||||
}
|
||||
}
|
||||
|
||||
#region UnityMethods
|
||||
protected virtual void OnDestroy()
|
||||
{
|
||||
//unregister events
|
||||
if (DataFetcher != null)
|
||||
{
|
||||
DataFetcher.DataRecieved -= OnImageRecieved;
|
||||
DataFetcher.FetchingError -= OnDataError;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region DataFetcherEvents
|
||||
private void OnImageRecieved(UnityTile tile, RasterTile rasterTile)
|
||||
{
|
||||
if (tile != null)
|
||||
{
|
||||
if (tile.RasterDataState != TilePropertyState.Unregistered)
|
||||
{
|
||||
_tilesWaitingResponse.Remove(tile);
|
||||
tile.SetRasterData(rasterTile.Data, _properties.rasterOptions.useMipMap, _properties.rasterOptions.useCompression);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//merge this with OnErrorOccurred?
|
||||
protected virtual void OnDataError(UnityTile tile, RasterTile rasterTile, TileErrorEventArgs e)
|
||||
{
|
||||
if (tile != null)
|
||||
{
|
||||
if (tile.RasterDataState != TilePropertyState.Unregistered)
|
||||
{
|
||||
tile.RasterDataState = TilePropertyState.Error;
|
||||
_tilesWaitingResponse.Remove(tile);
|
||||
OnErrorOccurred(e);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region AbstractFactoryOverrides
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
DataFetcher = ScriptableObject.CreateInstance<ImageDataFetcher>();
|
||||
DataFetcher.DataRecieved += OnImageRecieved;
|
||||
DataFetcher.FetchingError += OnDataError;
|
||||
}
|
||||
|
||||
public override void SetOptions(LayerProperties options)
|
||||
{
|
||||
_properties = (ImageryLayerProperties)options;
|
||||
}
|
||||
|
||||
protected override void OnRegistered(UnityTile tile)
|
||||
{
|
||||
if (_properties.sourceType == ImagerySourceType.None)
|
||||
{
|
||||
tile.SetRasterData(null);
|
||||
tile.RasterDataState = TilePropertyState.None;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
tile.RasterDataState = TilePropertyState.Loading;
|
||||
if (_properties.sourceType != ImagerySourceType.Custom)
|
||||
{
|
||||
_properties.sourceOptions.layerSource = MapboxDefaultImagery.GetParameters(_properties.sourceType);
|
||||
}
|
||||
ImageDataFetcherParameters parameters = new ImageDataFetcherParameters()
|
||||
{
|
||||
canonicalTileId = tile.CanonicalTileId,
|
||||
tile = tile,
|
||||
mapid = MapId,
|
||||
useRetina = _properties.rasterOptions.useRetina
|
||||
};
|
||||
DataFetcher.FetchData(parameters);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Method to be called when a tile error has occurred.
|
||||
/// </summary>
|
||||
/// <param name="e"><see cref="T:Mapbox.Map.TileErrorEventArgs"/> instance/</param>
|
||||
protected override void OnErrorOccurred(UnityTile tile, TileErrorEventArgs e)
|
||||
{
|
||||
base.OnErrorOccurred(tile, e);
|
||||
if (tile != null)
|
||||
{
|
||||
tile.RasterDataState = TilePropertyState.Error;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnUnregistered(UnityTile tile)
|
||||
{
|
||||
if (_tilesWaitingResponse != null && _tilesWaitingResponse.Contains(tile))
|
||||
{
|
||||
_tilesWaitingResponse.Remove(tile);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnPostProcess(UnityTile tile)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public override void UnbindEvents()
|
||||
{
|
||||
base.UnbindEvents();
|
||||
}
|
||||
|
||||
protected override void OnUnbindEvents()
|
||||
{
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2b10536479dade041b6db9893fdf723a
|
||||
timeCreated: 1481718773
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 2800000, guid: 2ac69ccfbca692443a02f68e1a689694, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,77 @@
|
||||
using Mapbox.Map;
|
||||
using Mapbox.Unity;
|
||||
using Mapbox.Unity.Map;
|
||||
using Mapbox.Unity.MeshGeneration.Data;
|
||||
using Mapbox.Unity.MeshGeneration.Enums;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
public class DataFetcherParameters
|
||||
{
|
||||
public CanonicalTileId canonicalTileId;
|
||||
public string mapid;
|
||||
public UnityTile tile;
|
||||
}
|
||||
|
||||
public class ImageDataFetcherParameters : DataFetcherParameters
|
||||
{
|
||||
public bool useRetina = true;
|
||||
}
|
||||
|
||||
public class TerrainDataFetcherParameters : DataFetcherParameters
|
||||
{
|
||||
}
|
||||
|
||||
public class VectorDataFetcherParameters : DataFetcherParameters
|
||||
{
|
||||
public bool useOptimizedStyle = false;
|
||||
public Style style = null;
|
||||
}
|
||||
|
||||
public abstract class DataFetcher : ScriptableObject
|
||||
{
|
||||
protected MapboxAccess _fileSource;
|
||||
|
||||
public void OnEnable()
|
||||
{
|
||||
_fileSource = MapboxAccess.Instance;
|
||||
}
|
||||
|
||||
public abstract void FetchData(DataFetcherParameters parameters);
|
||||
}
|
||||
|
||||
public class TerrainDataFetcher : DataFetcher
|
||||
{
|
||||
public Action<UnityTile, RawPngRasterTile> DataRecieved = (t, s) => { };
|
||||
public Action<UnityTile, RawPngRasterTile, TileErrorEventArgs> FetchingError = (t, r, s) => { };
|
||||
|
||||
//tile here should be totally optional and used only not to have keep a dictionary in terrain factory base
|
||||
public override void FetchData(DataFetcherParameters parameters)
|
||||
{
|
||||
var terrainDataParameters = parameters as TerrainDataFetcherParameters;
|
||||
if(terrainDataParameters == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var pngRasterTile = new RawPngRasterTile();
|
||||
pngRasterTile.Initialize(_fileSource, terrainDataParameters.canonicalTileId, terrainDataParameters.mapid, () =>
|
||||
{
|
||||
if (terrainDataParameters.tile.CanonicalTileId != pngRasterTile.Id)
|
||||
{
|
||||
//this means tile object is recycled and reused. Returned data doesn't belong to this tile but probably the previous one. So we're trashing it.
|
||||
return;
|
||||
}
|
||||
|
||||
if (pngRasterTile.HasError)
|
||||
{
|
||||
FetchingError(terrainDataParameters.tile, pngRasterTile, new TileErrorEventArgs(terrainDataParameters.canonicalTileId, pngRasterTile.GetType(), null, pngRasterTile.Exceptions));
|
||||
}
|
||||
else
|
||||
{
|
||||
DataRecieved(terrainDataParameters.tile, pngRasterTile);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c823321064d5b2f4db21c4fdbd6b1c1f
|
||||
timeCreated: 1523880895
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,170 @@
|
||||
using Mapbox.Unity.MeshGeneration.Factories;
|
||||
using System.Collections;
|
||||
using UnityEngine;
|
||||
using Mapbox.Unity.MeshGeneration.Data;
|
||||
using Mapbox.Unity.Map;
|
||||
using Mapbox.Map;
|
||||
using Mapbox.Unity.MeshGeneration.Enums;
|
||||
using Mapbox.Unity.MeshGeneration.Factories.TerrainStrategies;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Mapbox.Unity.MeshGeneration.Factories
|
||||
{
|
||||
public class TerrainFactoryBase : AbstractTileFactory
|
||||
{
|
||||
public TerrainStrategy Strategy;
|
||||
[SerializeField]
|
||||
protected ElevationLayerProperties _elevationOptions = new ElevationLayerProperties();
|
||||
protected TerrainDataFetcher DataFetcher;
|
||||
|
||||
public TerrainDataFetcher GetFetcher()
|
||||
{
|
||||
return DataFetcher;
|
||||
}
|
||||
|
||||
public ElevationLayerProperties Properties
|
||||
{
|
||||
get
|
||||
{
|
||||
return _elevationOptions;
|
||||
}
|
||||
}
|
||||
|
||||
#region UnityMethods
|
||||
private void OnDestroy()
|
||||
{
|
||||
if (DataFetcher != null)
|
||||
{
|
||||
DataFetcher.DataRecieved -= OnTerrainRecieved;
|
||||
DataFetcher.FetchingError -= OnDataError;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region AbstractFactoryOverrides
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
Strategy.Initialize(_elevationOptions);
|
||||
DataFetcher = ScriptableObject.CreateInstance<TerrainDataFetcher>();
|
||||
DataFetcher.DataRecieved += OnTerrainRecieved;
|
||||
DataFetcher.FetchingError += OnDataError;
|
||||
}
|
||||
|
||||
public override void SetOptions(LayerProperties options)
|
||||
{
|
||||
_elevationOptions = (ElevationLayerProperties)options;
|
||||
Strategy.Initialize(_elevationOptions);
|
||||
}
|
||||
|
||||
protected override void OnRegistered(UnityTile tile)
|
||||
{
|
||||
if (Properties.sourceType == ElevationSourceType.None)
|
||||
{
|
||||
tile.SetHeightData(null);
|
||||
tile.MeshFilter.mesh.Clear();
|
||||
tile.ElevationType = TileTerrainType.None;
|
||||
return;
|
||||
}
|
||||
|
||||
if (Strategy is IElevationBasedTerrainStrategy)
|
||||
{
|
||||
tile.HeightDataState = TilePropertyState.Loading;
|
||||
TerrainDataFetcherParameters parameters = new TerrainDataFetcherParameters()
|
||||
{
|
||||
canonicalTileId = tile.CanonicalTileId,
|
||||
mapid = _elevationOptions.sourceOptions.Id,
|
||||
tile = tile
|
||||
};
|
||||
DataFetcher.FetchData(parameters);
|
||||
}
|
||||
else
|
||||
{
|
||||
//reseting height data
|
||||
tile.SetHeightData(null);
|
||||
Strategy.RegisterTile(tile);
|
||||
tile.HeightDataState = TilePropertyState.Loaded;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnUnregistered(UnityTile tile)
|
||||
{
|
||||
if (_tilesWaitingResponse != null && _tilesWaitingResponse.Contains(tile))
|
||||
{
|
||||
_tilesWaitingResponse.Remove(tile);
|
||||
}
|
||||
Strategy.UnregisterTile(tile);
|
||||
}
|
||||
|
||||
protected override void OnPostProcess(UnityTile tile)
|
||||
{
|
||||
Strategy.PostProcessTile(tile);
|
||||
}
|
||||
|
||||
public override void UnbindEvents()
|
||||
{
|
||||
base.UnbindEvents();
|
||||
}
|
||||
|
||||
protected override void OnUnbindEvents()
|
||||
{
|
||||
}
|
||||
//public override void UpdateTileProperty(UnityTile tile, LayerUpdateArgs updateArgs)
|
||||
//{
|
||||
// updateArgs.property.UpdateProperty(tile);
|
||||
|
||||
// if (updateArgs.property.NeedsForceUpdate())
|
||||
// {
|
||||
// Register(tile);
|
||||
// }
|
||||
|
||||
// //if (updateArgs.property is TerrainColliderOptions)
|
||||
// //{
|
||||
// // var existingCollider = tileBundleValue.Collider;
|
||||
// // if (Properties.colliderOptions.addCollider)
|
||||
// // {
|
||||
// // if (existingCollider == null)
|
||||
// // {
|
||||
// // tileBundleValue.gameObject.AddComponent<MeshCollider>();
|
||||
// // }
|
||||
// // }
|
||||
// // else
|
||||
// // {
|
||||
// // Destroy(tileBundleValue.Collider);
|
||||
// // }
|
||||
// //}
|
||||
//}
|
||||
|
||||
#endregion
|
||||
|
||||
#region DataFetcherEvents
|
||||
private void OnTerrainRecieved(UnityTile tile, RawPngRasterTile pngRasterTile)
|
||||
{
|
||||
if (tile != null)
|
||||
{
|
||||
_tilesWaitingResponse.Remove(tile);
|
||||
if (tile.HeightDataState != TilePropertyState.Unregistered)
|
||||
{
|
||||
tile.SetHeightData(pngRasterTile.Data, _elevationOptions.requiredOptions.exaggerationFactor, _elevationOptions.modificationOptions.useRelativeHeight, _elevationOptions.colliderOptions.addCollider);
|
||||
Strategy.RegisterTile(tile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDataError(UnityTile tile, RawPngRasterTile rawTile, TileErrorEventArgs e)
|
||||
{
|
||||
base.OnErrorOccurred(tile, e);
|
||||
if (tile != null)
|
||||
{
|
||||
_tilesWaitingResponse.Remove(tile);
|
||||
if (tile.HeightDataState != TilePropertyState.Unregistered)
|
||||
{
|
||||
Strategy.DataErrorOccurred(tile, e);
|
||||
tile.HeightDataState = TilePropertyState.Error;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: af9867f09d360f2409d9d08a0ff16906
|
||||
timeCreated: 1522684959
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9a7ab3f3c54be5140aecaed29e7e4f65
|
||||
folderAsset: yes
|
||||
timeCreated: 1522755730
|
||||
licenseType: Free
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,401 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using Mapbox.Unity.MeshGeneration.Data;
|
||||
using Mapbox.Unity.Map;
|
||||
using Mapbox.Map;
|
||||
using Mapbox.Utils;
|
||||
|
||||
namespace Mapbox.Unity.MeshGeneration.Factories.TerrainStrategies
|
||||
{
|
||||
public class ElevatedTerrainStrategy : TerrainStrategy, IElevationBasedTerrainStrategy
|
||||
{
|
||||
Mesh _stitchTarget;
|
||||
|
||||
protected Dictionary<UnwrappedTileId, Mesh> _meshData;
|
||||
private MeshData _currentTileMeshData;
|
||||
private MeshData _stitchTargetMeshData;
|
||||
|
||||
private List<Vector3> _newVertexList;
|
||||
private List<Vector3> _newNormalList;
|
||||
private List<Vector2> _newUvList;
|
||||
private List<int> _newTriangleList;
|
||||
private Vector3 _newDir;
|
||||
private int _vertA, _vertB, _vertC;
|
||||
private int _counter;
|
||||
public override int RequiredVertexCount
|
||||
{
|
||||
get { return _elevationOptions.modificationOptions.sampleCount * _elevationOptions.modificationOptions.sampleCount; }
|
||||
}
|
||||
|
||||
public override void Initialize(ElevationLayerProperties elOptions)
|
||||
{
|
||||
base.Initialize(elOptions);
|
||||
|
||||
_meshData = new Dictionary<UnwrappedTileId, Mesh>();
|
||||
_currentTileMeshData = new MeshData();
|
||||
_stitchTargetMeshData = new MeshData();
|
||||
var sampleCountSquare = _elevationOptions.modificationOptions.sampleCount * _elevationOptions.modificationOptions.sampleCount;
|
||||
_newVertexList = new List<Vector3>(sampleCountSquare);
|
||||
_newNormalList = new List<Vector3>(sampleCountSquare);
|
||||
_newUvList = new List<Vector2>(sampleCountSquare);
|
||||
_newTriangleList = new List<int>();
|
||||
}
|
||||
|
||||
public override void RegisterTile(UnityTile tile)
|
||||
{
|
||||
if (_elevationOptions.unityLayerOptions.addToLayer && tile.gameObject.layer != _elevationOptions.unityLayerOptions.layerId)
|
||||
{
|
||||
tile.gameObject.layer = _elevationOptions.unityLayerOptions.layerId;
|
||||
}
|
||||
|
||||
if ((int)tile.ElevationType != (int)ElevationLayerType.TerrainWithElevation ||
|
||||
tile.MeshFilter.mesh.vertexCount != RequiredVertexCount)
|
||||
{
|
||||
tile.MeshFilter.mesh.Clear();
|
||||
CreateBaseMesh(tile);
|
||||
tile.ElevationType = TileTerrainType.Elevated;
|
||||
}
|
||||
|
||||
GenerateTerrainMesh(tile);
|
||||
}
|
||||
|
||||
public override void UnregisterTile(UnityTile tile)
|
||||
{
|
||||
_meshData.Remove(tile.UnwrappedTileId);
|
||||
}
|
||||
|
||||
public override void DataErrorOccurred(UnityTile t, TileErrorEventArgs e)
|
||||
{
|
||||
ResetToFlatMesh(t);
|
||||
}
|
||||
|
||||
public override void PostProcessTile(UnityTile tile)
|
||||
{
|
||||
//if (_meshData.ContainsKey(tile.UnwrappedTileId))
|
||||
//{
|
||||
// FixStitches(tile.UnwrappedTileId, _meshData[tile.UnwrappedTileId]);
|
||||
// tile.MeshFilter.mesh.RecalculateBounds();
|
||||
//}
|
||||
}
|
||||
#region mesh gen
|
||||
private void CreateBaseMesh(UnityTile tile)
|
||||
{
|
||||
//TODO use arrays instead of lists
|
||||
_newVertexList.Clear();
|
||||
_newNormalList.Clear();
|
||||
_newUvList.Clear();
|
||||
_newTriangleList.Clear();
|
||||
|
||||
var _sampleCount = _elevationOptions.modificationOptions.sampleCount;
|
||||
for (float y = 0; y < _sampleCount; y++)
|
||||
{
|
||||
var yrat = y / (_sampleCount - 1);
|
||||
for (float x = 0; x < _sampleCount; x++)
|
||||
{
|
||||
var xrat = x / (_sampleCount - 1);
|
||||
|
||||
var xx = Mathd.Lerp(tile.Rect.Min.x, tile.Rect.Max.x, xrat);
|
||||
var yy = Mathd.Lerp(tile.Rect.Min.y, tile.Rect.Max.y, yrat);
|
||||
|
||||
_newVertexList.Add(new Vector3(
|
||||
(float)(xx - tile.Rect.Center.x) * tile.TileScale,
|
||||
0,
|
||||
(float)(yy - tile.Rect.Center.y) * tile.TileScale));
|
||||
_newNormalList.Add(Mapbox.Unity.Constants.Math.Vector3Up);
|
||||
_newUvList.Add(new Vector2(x * 1f / (_sampleCount - 1), 1 - (y * 1f / (_sampleCount - 1))));
|
||||
}
|
||||
}
|
||||
|
||||
int vertA, vertB, vertC;
|
||||
for (int y = 0; y < _sampleCount - 1; y++)
|
||||
{
|
||||
for (int x = 0; x < _sampleCount - 1; x++)
|
||||
{
|
||||
vertA = (y * _sampleCount) + x;
|
||||
vertB = (y * _sampleCount) + x + _sampleCount + 1;
|
||||
vertC = (y * _sampleCount) + x + _sampleCount;
|
||||
_newTriangleList.Add(vertA);
|
||||
_newTriangleList.Add(vertB);
|
||||
_newTriangleList.Add(vertC);
|
||||
|
||||
vertA = (y * _sampleCount) + x;
|
||||
vertB = (y * _sampleCount) + x + 1;
|
||||
vertC = (y * _sampleCount) + x + _sampleCount + 1;
|
||||
_newTriangleList.Add(vertA);
|
||||
_newTriangleList.Add(vertB);
|
||||
_newTriangleList.Add(vertC);
|
||||
}
|
||||
}
|
||||
var mesh = tile.MeshFilter.mesh;
|
||||
mesh.SetVertices(_newVertexList);
|
||||
mesh.SetNormals(_newNormalList);
|
||||
mesh.SetUVs(0, _newUvList);
|
||||
mesh.SetTriangles(_newTriangleList, 0);
|
||||
}
|
||||
|
||||
private void GenerateTerrainMesh(UnityTile tile)
|
||||
{
|
||||
tile.MeshFilter.mesh.GetVertices(_currentTileMeshData.Vertices);
|
||||
tile.MeshFilter.mesh.GetNormals(_currentTileMeshData.Normals);
|
||||
|
||||
var _sampleCount = _elevationOptions.modificationOptions.sampleCount;
|
||||
for (float y = 0; y < _sampleCount; y++)
|
||||
{
|
||||
for (float x = 0; x < _sampleCount; x++)
|
||||
{
|
||||
_currentTileMeshData.Vertices[(int)(y * _sampleCount + x)] = new Vector3(
|
||||
_currentTileMeshData.Vertices[(int)(y * _sampleCount + x)].x,
|
||||
tile.QueryHeightData(x / (_sampleCount - 1), 1 - y / (_sampleCount - 1)),
|
||||
_currentTileMeshData.Vertices[(int)(y * _sampleCount + x)].z);
|
||||
_currentTileMeshData.Normals[(int)(y * _sampleCount + x)] = Mapbox.Unity.Constants.Math.Vector3Zero;
|
||||
}
|
||||
}
|
||||
|
||||
tile.MeshFilter.mesh.SetVertices(_currentTileMeshData.Vertices);
|
||||
|
||||
for (int y = 0; y < _sampleCount - 1; y++)
|
||||
{
|
||||
for (int x = 0; x < _sampleCount - 1; x++)
|
||||
{
|
||||
_vertA = (y * _sampleCount) + x;
|
||||
_vertB = (y * _sampleCount) + x + _sampleCount + 1;
|
||||
_vertC = (y * _sampleCount) + x + _sampleCount;
|
||||
_newDir = Vector3.Cross(_currentTileMeshData.Vertices[_vertB] - _currentTileMeshData.Vertices[_vertA], _currentTileMeshData.Vertices[_vertC] - _currentTileMeshData.Vertices[_vertA]);
|
||||
_currentTileMeshData.Normals[_vertA] += _newDir;
|
||||
_currentTileMeshData.Normals[_vertB] += _newDir;
|
||||
_currentTileMeshData.Normals[_vertC] += _newDir;
|
||||
|
||||
_vertA = (y * _sampleCount) + x;
|
||||
_vertB = (y * _sampleCount) + x + 1;
|
||||
_vertC = (y * _sampleCount) + x + _sampleCount + 1;
|
||||
_newDir = Vector3.Cross(_currentTileMeshData.Vertices[_vertB] - _currentTileMeshData.Vertices[_vertA], _currentTileMeshData.Vertices[_vertC] - _currentTileMeshData.Vertices[_vertA]);
|
||||
_currentTileMeshData.Normals[_vertA] += _newDir;
|
||||
_currentTileMeshData.Normals[_vertB] += _newDir;
|
||||
_currentTileMeshData.Normals[_vertC] += _newDir;
|
||||
}
|
||||
}
|
||||
|
||||
FixStitches(tile.UnwrappedTileId, _currentTileMeshData);
|
||||
|
||||
tile.MeshFilter.mesh.SetNormals(_currentTileMeshData.Normals);
|
||||
tile.MeshFilter.mesh.SetVertices(_currentTileMeshData.Vertices);
|
||||
|
||||
tile.MeshFilter.mesh.RecalculateBounds();
|
||||
|
||||
if (!_meshData.ContainsKey(tile.UnwrappedTileId))
|
||||
{
|
||||
_meshData.Add(tile.UnwrappedTileId, tile.MeshFilter.mesh);
|
||||
}
|
||||
|
||||
if (_elevationOptions.colliderOptions.addCollider)
|
||||
{
|
||||
var meshCollider = tile.Collider as MeshCollider;
|
||||
if (meshCollider)
|
||||
{
|
||||
meshCollider.sharedMesh = tile.MeshFilter.mesh;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ResetToFlatMesh(UnityTile tile)
|
||||
{
|
||||
if (tile.MeshFilter.mesh.vertexCount == 0)
|
||||
{
|
||||
CreateBaseMesh(tile);
|
||||
}
|
||||
else
|
||||
{
|
||||
tile.MeshFilter.mesh.GetVertices(_currentTileMeshData.Vertices);
|
||||
tile.MeshFilter.mesh.GetNormals(_currentTileMeshData.Normals);
|
||||
|
||||
_counter = _currentTileMeshData.Vertices.Count;
|
||||
for (int i = 0; i < _counter; i++)
|
||||
{
|
||||
_currentTileMeshData.Vertices[i] = new Vector3(
|
||||
_currentTileMeshData.Vertices[i].x,
|
||||
0,
|
||||
_currentTileMeshData.Vertices[i].z);
|
||||
_currentTileMeshData.Normals[i] = Mapbox.Unity.Constants.Math.Vector3Up;
|
||||
}
|
||||
|
||||
tile.MeshFilter.mesh.SetVertices(_currentTileMeshData.Vertices);
|
||||
tile.MeshFilter.mesh.SetNormals(_currentTileMeshData.Normals);
|
||||
|
||||
tile.MeshFilter.mesh.RecalculateBounds();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checkes all neighbours of the given tile and stitches the edges to achieve a smooth mesh surface.
|
||||
/// </summary>
|
||||
/// <param name="tileId">UnwrappedTileId of the tile being processed.</param>
|
||||
/// <param name="mesh"></param>
|
||||
private void FixStitches(UnwrappedTileId tileId, MeshData mesh)
|
||||
{
|
||||
var _sampleCount = _elevationOptions.modificationOptions.sampleCount;
|
||||
var meshVertCount = mesh.Vertices.Count;
|
||||
_stitchTarget = null;
|
||||
_meshData.TryGetValue(tileId.North, out _stitchTarget);
|
||||
|
||||
if (_stitchTarget != null)
|
||||
{
|
||||
_stitchTarget.GetVertices(_stitchTargetMeshData.Vertices);
|
||||
_stitchTarget.GetNormals(_stitchTargetMeshData.Normals);
|
||||
|
||||
for (int i = 0; i < _sampleCount; i++)
|
||||
{
|
||||
//just snapping the y because vertex pos is relative and we'll have to do tile pos + vertex pos for x&z otherwise
|
||||
mesh.Vertices[i] = new Vector3(
|
||||
mesh.Vertices[i].x,
|
||||
_stitchTargetMeshData.Vertices[meshVertCount - _sampleCount + i].y,
|
||||
mesh.Vertices[i].z);
|
||||
|
||||
mesh.Normals[i] = new Vector3(_stitchTargetMeshData.Normals[meshVertCount - _sampleCount + i].x,
|
||||
_stitchTargetMeshData.Normals[meshVertCount - _sampleCount + i].y,
|
||||
_stitchTargetMeshData.Normals[meshVertCount - _sampleCount + i].z);
|
||||
}
|
||||
}
|
||||
|
||||
_stitchTarget = null;
|
||||
_meshData.TryGetValue(tileId.South, out _stitchTarget);
|
||||
if (_stitchTarget != null)
|
||||
{
|
||||
_stitchTarget.GetVertices(_stitchTargetMeshData.Vertices);
|
||||
_stitchTarget.GetNormals(_stitchTargetMeshData.Normals);
|
||||
|
||||
for (int i = 0; i < _sampleCount; i++)
|
||||
{
|
||||
mesh.Vertices[meshVertCount - _sampleCount + i] = new Vector3(
|
||||
mesh.Vertices[meshVertCount - _sampleCount + i].x,
|
||||
_stitchTargetMeshData.Vertices[i].y,
|
||||
mesh.Vertices[meshVertCount - _sampleCount + i].z);
|
||||
|
||||
mesh.Normals[meshVertCount - _sampleCount + i] = new Vector3(
|
||||
_stitchTargetMeshData.Normals[i].x,
|
||||
_stitchTargetMeshData.Normals[i].y,
|
||||
_stitchTargetMeshData.Normals[i].z);
|
||||
}
|
||||
}
|
||||
|
||||
_stitchTarget = null;
|
||||
_meshData.TryGetValue(tileId.West, out _stitchTarget);
|
||||
if (_stitchTarget != null)
|
||||
{
|
||||
_stitchTarget.GetVertices(_stitchTargetMeshData.Vertices);
|
||||
_stitchTarget.GetNormals(_stitchTargetMeshData.Normals);
|
||||
|
||||
for (int i = 0; i < _sampleCount; i++)
|
||||
{
|
||||
mesh.Vertices[i * _sampleCount] = new Vector3(
|
||||
mesh.Vertices[i * _sampleCount].x,
|
||||
_stitchTargetMeshData.Vertices[i * _sampleCount + _sampleCount - 1].y,
|
||||
mesh.Vertices[i * _sampleCount].z);
|
||||
|
||||
mesh.Normals[i * _sampleCount] = new Vector3(
|
||||
_stitchTargetMeshData.Normals[i * _sampleCount + _sampleCount - 1].x,
|
||||
_stitchTargetMeshData.Normals[i * _sampleCount + _sampleCount - 1].y,
|
||||
_stitchTargetMeshData.Normals[i * _sampleCount + _sampleCount - 1].z);
|
||||
}
|
||||
}
|
||||
|
||||
_stitchTarget = null;
|
||||
_meshData.TryGetValue(tileId.East, out _stitchTarget);
|
||||
|
||||
if (_stitchTarget != null)
|
||||
{
|
||||
_stitchTarget.GetVertices(_stitchTargetMeshData.Vertices);
|
||||
_stitchTarget.GetNormals(_stitchTargetMeshData.Normals);
|
||||
|
||||
for (int i = 0; i < _sampleCount; i++)
|
||||
{
|
||||
mesh.Vertices[i * _sampleCount + _sampleCount - 1] = new Vector3(
|
||||
mesh.Vertices[i * _sampleCount + _sampleCount - 1].x,
|
||||
_stitchTargetMeshData.Vertices[i * _sampleCount].y,
|
||||
mesh.Vertices[i * _sampleCount + _sampleCount - 1].z);
|
||||
|
||||
mesh.Normals[i * _sampleCount + _sampleCount - 1] = new Vector3(
|
||||
_stitchTargetMeshData.Normals[i * _sampleCount].x,
|
||||
_stitchTargetMeshData.Normals[i * _sampleCount].y,
|
||||
_stitchTargetMeshData.Normals[i * _sampleCount].z);
|
||||
}
|
||||
}
|
||||
|
||||
_stitchTarget = null;
|
||||
_meshData.TryGetValue(tileId.NorthWest, out _stitchTarget);
|
||||
|
||||
if (_stitchTarget != null)
|
||||
{
|
||||
_stitchTarget.GetVertices(_stitchTargetMeshData.Vertices);
|
||||
_stitchTarget.GetNormals(_stitchTargetMeshData.Normals);
|
||||
|
||||
mesh.Vertices[0] = new Vector3(
|
||||
mesh.Vertices[0].x,
|
||||
_stitchTargetMeshData.Vertices[meshVertCount - 1].y,
|
||||
mesh.Vertices[0].z);
|
||||
|
||||
mesh.Normals[0] = new Vector3(
|
||||
_stitchTargetMeshData.Normals[meshVertCount - 1].x,
|
||||
_stitchTargetMeshData.Normals[meshVertCount - 1].y,
|
||||
_stitchTargetMeshData.Normals[meshVertCount - 1].z);
|
||||
}
|
||||
|
||||
_stitchTarget = null;
|
||||
_meshData.TryGetValue(tileId.NorthEast, out _stitchTarget);
|
||||
|
||||
if (_stitchTarget != null)
|
||||
{
|
||||
_stitchTarget.GetVertices(_stitchTargetMeshData.Vertices);
|
||||
_stitchTarget.GetNormals(_stitchTargetMeshData.Normals);
|
||||
|
||||
mesh.Vertices[_sampleCount - 1] = new Vector3(
|
||||
mesh.Vertices[_sampleCount - 1].x,
|
||||
_stitchTargetMeshData.Vertices[meshVertCount - _sampleCount].y,
|
||||
mesh.Vertices[_sampleCount - 1].z);
|
||||
|
||||
mesh.Normals[_sampleCount - 1] = new Vector3(
|
||||
_stitchTargetMeshData.Normals[meshVertCount - _sampleCount].x,
|
||||
_stitchTargetMeshData.Normals[meshVertCount - _sampleCount].y,
|
||||
_stitchTargetMeshData.Normals[meshVertCount - _sampleCount].z);
|
||||
}
|
||||
|
||||
_stitchTarget = null;
|
||||
_meshData.TryGetValue(tileId.SouthWest, out _stitchTarget);
|
||||
|
||||
if (_stitchTarget != null)
|
||||
{
|
||||
_stitchTarget.GetVertices(_stitchTargetMeshData.Vertices);
|
||||
_stitchTarget.GetNormals(_stitchTargetMeshData.Normals);
|
||||
|
||||
mesh.Vertices[meshVertCount - _sampleCount] = new Vector3(
|
||||
mesh.Vertices[meshVertCount - _sampleCount].x,
|
||||
_stitchTargetMeshData.Vertices[_sampleCount - 1].y,
|
||||
mesh.Vertices[meshVertCount - _sampleCount].z);
|
||||
|
||||
mesh.Normals[meshVertCount - _sampleCount] = new Vector3(
|
||||
_stitchTargetMeshData.Normals[_sampleCount - 1].x,
|
||||
_stitchTargetMeshData.Normals[_sampleCount - 1].y,
|
||||
_stitchTargetMeshData.Normals[_sampleCount - 1].z);
|
||||
}
|
||||
|
||||
_stitchTarget = null;
|
||||
_meshData.TryGetValue(tileId.SouthEast, out _stitchTarget);
|
||||
|
||||
if (_stitchTarget != null)
|
||||
{
|
||||
_stitchTarget.GetVertices(_stitchTargetMeshData.Vertices);
|
||||
_stitchTarget.GetNormals(_stitchTargetMeshData.Normals);
|
||||
|
||||
mesh.Vertices[meshVertCount - 1] = new Vector3(
|
||||
mesh.Vertices[meshVertCount - 1].x,
|
||||
_stitchTargetMeshData.Vertices[0].y,
|
||||
mesh.Vertices[meshVertCount - 1].z);
|
||||
|
||||
mesh.Normals[meshVertCount - 1] = new Vector3(
|
||||
_stitchTargetMeshData.Normals[0].x,
|
||||
_stitchTargetMeshData.Normals[0].y,
|
||||
_stitchTargetMeshData.Normals[0].z);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ea9b70aa9455c614f8701116ede4599d
|
||||
timeCreated: 1522755494
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,534 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using Mapbox.Unity.MeshGeneration.Data;
|
||||
using Mapbox.Unity.Map;
|
||||
using Mapbox.Map;
|
||||
using Mapbox.Utils;
|
||||
|
||||
namespace Mapbox.Unity.MeshGeneration.Factories.TerrainStrategies
|
||||
{
|
||||
public class ElevatedTerrainWithSidesStrategy : TerrainStrategy, IElevationBasedTerrainStrategy
|
||||
{
|
||||
Mesh _stitchTarget;
|
||||
|
||||
protected Dictionary<UnwrappedTileId, Mesh> _meshData;
|
||||
private MeshData _currentTileMeshData;
|
||||
private MeshData _stitchTargetMeshData;
|
||||
|
||||
private List<Vector3> _newVertexList;
|
||||
private List<Vector3> _newNormalList;
|
||||
private List<Vector2> _newUvList;
|
||||
private List<int> _newTriangleList;
|
||||
private Vector3 _newDir;
|
||||
private int _vertA, _vertB, _vertC;
|
||||
private int _counter;
|
||||
public override int RequiredVertexCount
|
||||
{
|
||||
get { return _elevationOptions.modificationOptions.sampleCount * _elevationOptions.modificationOptions.sampleCount
|
||||
+ (4 * _elevationOptions.modificationOptions.sampleCount); }
|
||||
}
|
||||
|
||||
public override void Initialize(ElevationLayerProperties elOptions)
|
||||
{
|
||||
_elevationOptions = elOptions;
|
||||
|
||||
_meshData = new Dictionary<UnwrappedTileId, Mesh>();
|
||||
_currentTileMeshData = new MeshData();
|
||||
_stitchTargetMeshData = new MeshData();
|
||||
var sampleCountSquare = _elevationOptions.modificationOptions.sampleCount * _elevationOptions.modificationOptions.sampleCount;
|
||||
_newVertexList = new List<Vector3>(sampleCountSquare);
|
||||
_newNormalList = new List<Vector3>(sampleCountSquare);
|
||||
_newUvList = new List<Vector2>(sampleCountSquare);
|
||||
_newTriangleList = new List<int>();
|
||||
}
|
||||
|
||||
public override void RegisterTile(UnityTile tile)
|
||||
{
|
||||
if (_elevationOptions.unityLayerOptions.addToLayer && tile.gameObject.layer != _elevationOptions.unityLayerOptions.layerId)
|
||||
{
|
||||
tile.gameObject.layer = _elevationOptions.unityLayerOptions.layerId;
|
||||
}
|
||||
|
||||
if (tile.RasterDataState != Enums.TilePropertyState.Loaded)
|
||||
{
|
||||
if (_elevationOptions.sideWallOptions.isActive)
|
||||
{
|
||||
var firstMat = tile.MeshRenderer.materials[0];
|
||||
tile.MeshRenderer.materials = new Material[2]
|
||||
{
|
||||
firstMat,
|
||||
_elevationOptions.sideWallOptions.wallMaterial
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (tile.MeshFilter.mesh.vertexCount != RequiredVertexCount)
|
||||
{
|
||||
CreateBaseMesh(tile);
|
||||
}
|
||||
|
||||
if (_elevationOptions.colliderOptions.addCollider && tile.Collider == null)
|
||||
{
|
||||
tile.gameObject.AddComponent<MeshCollider>();
|
||||
}
|
||||
|
||||
GenerateTerrainMesh(tile);
|
||||
}
|
||||
|
||||
public override void DataErrorOccurred(UnityTile t, TileErrorEventArgs e)
|
||||
{
|
||||
ResetToFlatMesh(t);
|
||||
}
|
||||
|
||||
private void CreateBaseMesh(UnityTile tile)
|
||||
{
|
||||
//TODO use arrays instead of lists
|
||||
_newVertexList.Clear();
|
||||
_newNormalList.Clear();
|
||||
_newUvList.Clear();
|
||||
_newTriangleList.Clear();
|
||||
|
||||
var _sampleCount = _elevationOptions.modificationOptions.sampleCount;
|
||||
for (float y = 0; y < _sampleCount; y++)
|
||||
{
|
||||
var yrat = y / (_sampleCount - 1);
|
||||
for (float x = 0; x < _sampleCount; x++)
|
||||
{
|
||||
var xrat = x / (_sampleCount - 1);
|
||||
|
||||
var xx = Mathd.Lerp(tile.Rect.Min.x, tile.Rect.Max.x, xrat);
|
||||
var yy = Mathd.Lerp(tile.Rect.Min.y, tile.Rect.Max.y, yrat);
|
||||
|
||||
_newVertexList.Add(new Vector3(
|
||||
(float)(xx - tile.Rect.Center.x) * tile.TileScale,
|
||||
0,
|
||||
(float)(yy - tile.Rect.Center.y) * tile.TileScale));
|
||||
_newNormalList.Add(Mapbox.Unity.Constants.Math.Vector3Up);
|
||||
_newUvList.Add(new Vector2(x * 1f / (_sampleCount - 1), 1 - (y * 1f / (_sampleCount - 1))));
|
||||
}
|
||||
}
|
||||
|
||||
int vertA, vertB, vertC;
|
||||
for (int y = 0; y < _sampleCount - 1; y++)
|
||||
{
|
||||
for (int x = 0; x < _sampleCount - 1; x++)
|
||||
{
|
||||
vertA = (y * _sampleCount) + x;
|
||||
vertB = (y * _sampleCount) + x + _sampleCount + 1;
|
||||
vertC = (y * _sampleCount) + x + _sampleCount;
|
||||
_newTriangleList.Add(vertA);
|
||||
_newTriangleList.Add(vertB);
|
||||
_newTriangleList.Add(vertC);
|
||||
|
||||
vertA = (y * _sampleCount) + x;
|
||||
vertB = (y * _sampleCount) + x + 1;
|
||||
vertC = (y * _sampleCount) + x + _sampleCount + 1;
|
||||
_newTriangleList.Add(vertA);
|
||||
_newTriangleList.Add(vertB);
|
||||
_newTriangleList.Add(vertC);
|
||||
}
|
||||
}
|
||||
|
||||
var sideVertBase = _newVertexList.Count;
|
||||
|
||||
var lastRow = (_sampleCount - 1) * _sampleCount;
|
||||
var baseTriList = new List<int>();
|
||||
for (int x = 0; x < _sampleCount; x++)
|
||||
{
|
||||
//side wall
|
||||
//024
|
||||
//135
|
||||
_newVertexList.Add(_newVertexList[x]);
|
||||
_newVertexList.Add(new Vector3(
|
||||
_newVertexList[x].x,
|
||||
-_elevationOptions.sideWallOptions.wallHeight,
|
||||
_newVertexList[x].z));
|
||||
_newNormalList.Add(Mapbox.Unity.Constants.Math.Vector3Forward);
|
||||
_newNormalList.Add(Mapbox.Unity.Constants.Math.Vector3Forward);
|
||||
_newUvList.Add(new Vector2(_newUvList[x * _sampleCount].y, 1));
|
||||
_newUvList.Add(new Vector2(_newUvList[x * _sampleCount].y, 0));
|
||||
|
||||
//---
|
||||
|
||||
_newVertexList.Add(_newVertexList[x * _sampleCount]);
|
||||
_newVertexList.Add(new Vector3(
|
||||
_newVertexList[x * _sampleCount].x,
|
||||
-_elevationOptions.sideWallOptions.wallHeight,
|
||||
_newVertexList[x * _sampleCount].z));
|
||||
_newNormalList.Add(Vector3.left);
|
||||
_newNormalList.Add(Vector3.left);
|
||||
_newUvList.Add(new Vector2(_newUvList[x * _sampleCount].y, 1));
|
||||
_newUvList.Add(new Vector2(_newUvList[x * _sampleCount].y, 0));
|
||||
|
||||
//---
|
||||
|
||||
_newVertexList.Add(_newVertexList[(x + 1) * _sampleCount - 1]);
|
||||
_newVertexList.Add(new Vector3(
|
||||
_newVertexList[(x + 1) * _sampleCount - 1].x,
|
||||
-_elevationOptions.sideWallOptions.wallHeight,
|
||||
_newVertexList[(x + 1) * _sampleCount - 1].z));
|
||||
_newNormalList.Add(Vector3.right);
|
||||
_newNormalList.Add(Vector3.right);
|
||||
_newUvList.Add(new Vector2(_newUvList[x * _sampleCount].y, 1));
|
||||
_newUvList.Add(new Vector2(_newUvList[x * _sampleCount].y, 0));
|
||||
|
||||
//---
|
||||
|
||||
_newVertexList.Add(_newVertexList[lastRow + x]);
|
||||
_newVertexList.Add(new Vector3(
|
||||
_newVertexList[lastRow + x].x,
|
||||
-_elevationOptions.sideWallOptions.wallHeight,
|
||||
_newVertexList[lastRow + x].z));
|
||||
_newNormalList.Add(Vector3.back);
|
||||
_newNormalList.Add(Vector3.back);
|
||||
_newUvList.Add(new Vector2(_newUvList[x * _sampleCount].y, 1));
|
||||
_newUvList.Add(new Vector2(_newUvList[x * _sampleCount].y, 0));
|
||||
|
||||
if (x > 0)
|
||||
{
|
||||
baseTriList.Add(sideVertBase + 8 * x);
|
||||
baseTriList.Add(sideVertBase + 8 * x - 8);
|
||||
baseTriList.Add(sideVertBase + 8 * x - 8 + 1);
|
||||
|
||||
baseTriList.Add(sideVertBase + 8 * x);
|
||||
baseTriList.Add(sideVertBase + 8 * x - 8 + 1);
|
||||
baseTriList.Add(sideVertBase + 8 * x + 1);
|
||||
|
||||
//---
|
||||
|
||||
baseTriList.Add(sideVertBase + 8 * x + 2);
|
||||
baseTriList.Add(sideVertBase + 8 * x - 8 + 1 + 2);
|
||||
baseTriList.Add(sideVertBase + 8 * x - 8 + 2);
|
||||
|
||||
baseTriList.Add(sideVertBase + 8 * x + 2);
|
||||
baseTriList.Add(sideVertBase + 8 * x + 1 + 2);
|
||||
baseTriList.Add(sideVertBase + 8 * x - 8 + 1 + 2);
|
||||
|
||||
//---
|
||||
|
||||
baseTriList.Add(sideVertBase + 8 * x + 4);
|
||||
baseTriList.Add(sideVertBase + 8 * x - 8 + 4);
|
||||
baseTriList.Add(sideVertBase + 8 * x - 8 + 1 + 4);
|
||||
|
||||
baseTriList.Add(sideVertBase + 8 * x + 4);
|
||||
baseTriList.Add(sideVertBase + 8 * x - 8 + 1 + 4);
|
||||
baseTriList.Add(sideVertBase + 8 * x + 1 + 4);
|
||||
|
||||
//---
|
||||
|
||||
baseTriList.Add(sideVertBase + 8 * x + 6);
|
||||
baseTriList.Add(sideVertBase + 8 * x - 8 + 1 + 6);
|
||||
baseTriList.Add(sideVertBase + 8 * x - 8 + 6);
|
||||
|
||||
baseTriList.Add(sideVertBase + 8 * x + 6);
|
||||
baseTriList.Add(sideVertBase + 8 * x + 1 + 6);
|
||||
baseTriList.Add(sideVertBase + 8 * x - 8 + 1 + 6);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var mesh = tile.MeshFilter.mesh;
|
||||
mesh.SetVertices(_newVertexList);
|
||||
mesh.SetNormals(_newNormalList);
|
||||
mesh.SetUVs(0, _newUvList);
|
||||
mesh.subMeshCount = 2;
|
||||
mesh.SetTriangles(_newTriangleList, 0);
|
||||
mesh.SetTriangles(baseTriList, 1);
|
||||
}
|
||||
|
||||
public override void UnregisterTile(UnityTile tile)
|
||||
{
|
||||
_meshData.Remove(tile.UnwrappedTileId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the non-flat terrain mesh, using a grid by defined resolution (_sampleCount). Vertex order goes right & up. Normals are calculated manually and UV map is fitted/stretched 1-1.
|
||||
/// Any additional scripts or logic, like MeshCollider or setting layer, can be done here.
|
||||
/// </summary>
|
||||
/// <param name="tile"></param>
|
||||
// <param name="heightMultiplier">Multiplier for queried height value</param>
|
||||
private void GenerateTerrainMesh(UnityTile tile)
|
||||
{
|
||||
tile.MeshFilter.mesh.GetVertices(_currentTileMeshData.Vertices);
|
||||
tile.MeshFilter.mesh.GetNormals(_currentTileMeshData.Normals);
|
||||
|
||||
var _sampleCount = _elevationOptions.modificationOptions.sampleCount;
|
||||
int sideStart = _sampleCount * _sampleCount;
|
||||
for (float y = 0; y < _sampleCount; y++)
|
||||
{
|
||||
for (float x = 0; x < _sampleCount; x++)
|
||||
{
|
||||
_currentTileMeshData.Vertices[(int)(y * _sampleCount + x)] = new Vector3(
|
||||
_currentTileMeshData.Vertices[(int)(y * _sampleCount + x)].x,
|
||||
tile.QueryHeightData(x / (_sampleCount - 1), 1 - y / (_sampleCount - 1)),
|
||||
_currentTileMeshData.Vertices[(int)(y * _sampleCount + x)].z);
|
||||
_currentTileMeshData.Normals[(int)(y * _sampleCount + x)] = Mapbox.Unity.Constants.Math.Vector3Zero;
|
||||
|
||||
if (y == 0)
|
||||
{
|
||||
_currentTileMeshData.Vertices[(int)(sideStart + 8 * x)] = _currentTileMeshData.Vertices[(int)(y * _sampleCount + x)];
|
||||
}
|
||||
else if (y == _sampleCount - 1)
|
||||
{
|
||||
_currentTileMeshData.Vertices[(int)(sideStart + 8 * x + 6)] = _currentTileMeshData.Vertices[(int)(y * _sampleCount + x)];
|
||||
}
|
||||
|
||||
if (x == 0)
|
||||
{
|
||||
_currentTileMeshData.Vertices[(int)(sideStart + 8 * y + 2)] = _currentTileMeshData.Vertices[(int)(y * _sampleCount + x)];
|
||||
}
|
||||
else if (x == _sampleCount - 1)
|
||||
{
|
||||
_currentTileMeshData.Vertices[(int)(sideStart + 8 * y + 4)] = _currentTileMeshData.Vertices[(int)(y * _sampleCount + x)];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tile.MeshFilter.mesh.SetVertices(_currentTileMeshData.Vertices);
|
||||
|
||||
for (int y = 0; y < _sampleCount - 1; y++)
|
||||
{
|
||||
for (int x = 0; x < _sampleCount - 1; x++)
|
||||
{
|
||||
_vertA = (y * _sampleCount) + x;
|
||||
_vertB = (y * _sampleCount) + x + _sampleCount + 1;
|
||||
_vertC = (y * _sampleCount) + x + _sampleCount;
|
||||
_newDir = Vector3.Cross(_currentTileMeshData.Vertices[_vertB] - _currentTileMeshData.Vertices[_vertA], _currentTileMeshData.Vertices[_vertC] - _currentTileMeshData.Vertices[_vertA]);
|
||||
_currentTileMeshData.Normals[_vertA] += _newDir;
|
||||
_currentTileMeshData.Normals[_vertB] += _newDir;
|
||||
_currentTileMeshData.Normals[_vertC] += _newDir;
|
||||
|
||||
_vertA = (y * _sampleCount) + x;
|
||||
_vertB = (y * _sampleCount) + x + 1;
|
||||
_vertC = (y * _sampleCount) + x + _sampleCount + 1;
|
||||
_newDir = Vector3.Cross(_currentTileMeshData.Vertices[_vertB] - _currentTileMeshData.Vertices[_vertA], _currentTileMeshData.Vertices[_vertC] - _currentTileMeshData.Vertices[_vertA]);
|
||||
_currentTileMeshData.Normals[_vertA] += _newDir;
|
||||
_currentTileMeshData.Normals[_vertB] += _newDir;
|
||||
_currentTileMeshData.Normals[_vertC] += _newDir;
|
||||
}
|
||||
}
|
||||
|
||||
FixStitches(tile.UnwrappedTileId, _currentTileMeshData);
|
||||
|
||||
tile.MeshFilter.mesh.SetNormals(_currentTileMeshData.Normals);
|
||||
tile.MeshFilter.mesh.SetVertices(_currentTileMeshData.Vertices);
|
||||
|
||||
tile.MeshFilter.mesh.RecalculateBounds();
|
||||
|
||||
if (!_meshData.ContainsKey(tile.UnwrappedTileId))
|
||||
{
|
||||
_meshData.Add(tile.UnwrappedTileId, tile.MeshFilter.mesh);
|
||||
}
|
||||
|
||||
if (_elevationOptions.colliderOptions.addCollider)
|
||||
{
|
||||
var meshCollider = tile.Collider as MeshCollider;
|
||||
if (meshCollider)
|
||||
{
|
||||
meshCollider.sharedMesh = tile.MeshFilter.mesh;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ResetToFlatMesh(UnityTile tile)
|
||||
{
|
||||
if (tile.MeshFilter.mesh.vertexCount == 0)
|
||||
{
|
||||
CreateBaseMesh(tile);
|
||||
}
|
||||
else
|
||||
{
|
||||
tile.MeshFilter.mesh.GetVertices(_currentTileMeshData.Vertices);
|
||||
tile.MeshFilter.mesh.GetNormals(_currentTileMeshData.Normals);
|
||||
|
||||
_counter = _currentTileMeshData.Vertices.Count;
|
||||
for (int i = 0; i < _counter; i++)
|
||||
{
|
||||
_currentTileMeshData.Vertices[i] = new Vector3(
|
||||
_currentTileMeshData.Vertices[i].x,
|
||||
0,
|
||||
_currentTileMeshData.Vertices[i].z);
|
||||
_currentTileMeshData.Normals[i] = Mapbox.Unity.Constants.Math.Vector3Up;
|
||||
}
|
||||
|
||||
tile.MeshFilter.mesh.SetVertices(_currentTileMeshData.Vertices);
|
||||
tile.MeshFilter.mesh.SetNormals(_currentTileMeshData.Normals);
|
||||
|
||||
tile.MeshFilter.mesh.RecalculateBounds();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checkes all neighbours of the given tile and stitches the edges to achieve a smooth mesh surface.
|
||||
/// </summary>
|
||||
/// <param name="tileId"></param>
|
||||
/// <param name="mesh"></param>
|
||||
private void FixStitches(UnwrappedTileId tileId, MeshData mesh)
|
||||
{
|
||||
var _sampleCount = _elevationOptions.modificationOptions.sampleCount;
|
||||
var meshVertCount = _sampleCount * _sampleCount;
|
||||
_stitchTarget = null;
|
||||
_meshData.TryGetValue(tileId.North, out _stitchTarget);
|
||||
if (_stitchTarget != null)
|
||||
{
|
||||
_stitchTarget.GetVertices(_stitchTargetMeshData.Vertices);
|
||||
_stitchTarget.GetNormals(_stitchTargetMeshData.Normals);
|
||||
for (int i = 0; i < _sampleCount; i++)
|
||||
{
|
||||
//just snapping the y because vertex pos is relative and we'll have to do tile pos + vertex pos for x&z otherwise
|
||||
mesh.Vertices[i] = new Vector3(
|
||||
mesh.Vertices[i].x,
|
||||
_stitchTargetMeshData.Vertices[meshVertCount - _sampleCount + i].y,
|
||||
mesh.Vertices[i].z);
|
||||
mesh.Vertices[meshVertCount + (8 * i)] = mesh.Vertices[i];
|
||||
|
||||
mesh.Normals[i] = new Vector3(_stitchTargetMeshData.Normals[meshVertCount - _sampleCount + i].x,
|
||||
_stitchTargetMeshData.Normals[meshVertCount - _sampleCount + i].y,
|
||||
_stitchTargetMeshData.Normals[meshVertCount - _sampleCount + i].z);
|
||||
}
|
||||
}
|
||||
|
||||
_stitchTarget = null;
|
||||
_meshData.TryGetValue(tileId.South, out _stitchTarget);
|
||||
if (_stitchTarget != null)
|
||||
{
|
||||
_stitchTarget.GetVertices(_stitchTargetMeshData.Vertices);
|
||||
_stitchTarget.GetNormals(_stitchTargetMeshData.Normals);
|
||||
for (int i = 0; i < _sampleCount; i++)
|
||||
{
|
||||
mesh.Vertices[meshVertCount - _sampleCount + i] = new Vector3(
|
||||
mesh.Vertices[meshVertCount - _sampleCount + i].x,
|
||||
_stitchTargetMeshData.Vertices[i].y,
|
||||
mesh.Vertices[meshVertCount - _sampleCount + i].z);
|
||||
mesh.Vertices[meshVertCount + 6 + (8 * i)] = mesh.Vertices[meshVertCount - _sampleCount + i];
|
||||
|
||||
mesh.Normals[meshVertCount - _sampleCount + i] = new Vector3(
|
||||
_stitchTargetMeshData.Normals[i].x,
|
||||
_stitchTargetMeshData.Normals[i].y,
|
||||
_stitchTargetMeshData.Normals[i].z);
|
||||
}
|
||||
}
|
||||
|
||||
_stitchTarget = null;
|
||||
_meshData.TryGetValue(tileId.West, out _stitchTarget);
|
||||
if (_stitchTarget != null)
|
||||
{
|
||||
_stitchTarget.GetVertices(_stitchTargetMeshData.Vertices);
|
||||
_stitchTarget.GetNormals(_stitchTargetMeshData.Normals);
|
||||
|
||||
for (int i = 0; i < _sampleCount; i++)
|
||||
{
|
||||
mesh.Vertices[i * _sampleCount] = new Vector3(
|
||||
mesh.Vertices[i * _sampleCount].x,
|
||||
_stitchTargetMeshData.Vertices[i * _sampleCount + _sampleCount - 1].y,
|
||||
mesh.Vertices[i * _sampleCount].z);
|
||||
mesh.Vertices[meshVertCount + 2 + (8 * i)] = mesh.Vertices[i * _sampleCount];
|
||||
|
||||
mesh.Normals[i * _sampleCount] = new Vector3(
|
||||
_stitchTargetMeshData.Normals[i * _sampleCount + _sampleCount - 1].x,
|
||||
_stitchTargetMeshData.Normals[i * _sampleCount + _sampleCount - 1].y,
|
||||
_stitchTargetMeshData.Normals[i * _sampleCount + _sampleCount - 1].z);
|
||||
}
|
||||
}
|
||||
|
||||
_stitchTarget = null;
|
||||
_meshData.TryGetValue(tileId.East, out _stitchTarget);
|
||||
|
||||
if (_stitchTarget != null)
|
||||
{
|
||||
_stitchTarget.GetVertices(_stitchTargetMeshData.Vertices);
|
||||
_stitchTarget.GetNormals(_stitchTargetMeshData.Normals);
|
||||
|
||||
for (int i = 0; i < _sampleCount; i++)
|
||||
{
|
||||
mesh.Vertices[i * _sampleCount + _sampleCount - 1] = new Vector3(
|
||||
mesh.Vertices[i * _sampleCount + _sampleCount - 1].x,
|
||||
_stitchTargetMeshData.Vertices[i * _sampleCount].y,
|
||||
mesh.Vertices[i * _sampleCount + _sampleCount - 1].z);
|
||||
mesh.Vertices[meshVertCount + 4 + (8 * i)] = mesh.Vertices[i * _sampleCount + _sampleCount - 1];
|
||||
|
||||
mesh.Normals[i * _sampleCount + _sampleCount - 1] = new Vector3(
|
||||
_stitchTargetMeshData.Normals[i * _sampleCount].x,
|
||||
_stitchTargetMeshData.Normals[i * _sampleCount].y,
|
||||
_stitchTargetMeshData.Normals[i * _sampleCount].z);
|
||||
}
|
||||
}
|
||||
|
||||
_stitchTarget = null;
|
||||
_meshData.TryGetValue(tileId.NorthWest, out _stitchTarget);
|
||||
|
||||
if (_stitchTarget != null)
|
||||
{
|
||||
_stitchTarget.GetVertices(_stitchTargetMeshData.Vertices);
|
||||
_stitchTarget.GetNormals(_stitchTargetMeshData.Normals);
|
||||
|
||||
mesh.Vertices[0] = new Vector3(
|
||||
mesh.Vertices[0].x,
|
||||
_stitchTargetMeshData.Vertices[meshVertCount - 1].y,
|
||||
mesh.Vertices[0].z);
|
||||
|
||||
mesh.Normals[0] = new Vector3(
|
||||
_stitchTargetMeshData.Normals[meshVertCount - 1].x,
|
||||
_stitchTargetMeshData.Normals[meshVertCount - 1].y,
|
||||
_stitchTargetMeshData.Normals[meshVertCount - 1].z);
|
||||
}
|
||||
|
||||
_stitchTarget = null;
|
||||
_meshData.TryGetValue(tileId.NorthEast, out _stitchTarget);
|
||||
|
||||
if (_stitchTarget != null)
|
||||
{
|
||||
_stitchTarget.GetVertices(_stitchTargetMeshData.Vertices);
|
||||
_stitchTarget.GetNormals(_stitchTargetMeshData.Normals);
|
||||
|
||||
mesh.Vertices[_sampleCount - 1] = new Vector3(
|
||||
mesh.Vertices[_sampleCount - 1].x,
|
||||
_stitchTargetMeshData.Vertices[meshVertCount - _sampleCount].y,
|
||||
mesh.Vertices[_sampleCount - 1].z);
|
||||
|
||||
mesh.Normals[_sampleCount - 1] = new Vector3(
|
||||
_stitchTargetMeshData.Normals[meshVertCount - _sampleCount].x,
|
||||
_stitchTargetMeshData.Normals[meshVertCount - _sampleCount].y,
|
||||
_stitchTargetMeshData.Normals[meshVertCount - _sampleCount].z);
|
||||
}
|
||||
|
||||
_stitchTarget = null;
|
||||
_meshData.TryGetValue(tileId.SouthWest, out _stitchTarget);
|
||||
|
||||
if (_stitchTarget != null)
|
||||
{
|
||||
_stitchTarget.GetVertices(_stitchTargetMeshData.Vertices);
|
||||
_stitchTarget.GetNormals(_stitchTargetMeshData.Normals);
|
||||
|
||||
mesh.Vertices[meshVertCount - _sampleCount] = new Vector3(
|
||||
mesh.Vertices[meshVertCount - _sampleCount].x,
|
||||
_stitchTargetMeshData.Vertices[_sampleCount - 1].y,
|
||||
mesh.Vertices[meshVertCount - _sampleCount].z);
|
||||
|
||||
mesh.Normals[meshVertCount - _sampleCount] = new Vector3(
|
||||
_stitchTargetMeshData.Normals[_sampleCount - 1].x,
|
||||
_stitchTargetMeshData.Normals[_sampleCount - 1].y,
|
||||
_stitchTargetMeshData.Normals[_sampleCount - 1].z);
|
||||
}
|
||||
|
||||
_stitchTarget = null;
|
||||
_meshData.TryGetValue(tileId.SouthEast, out _stitchTarget);
|
||||
|
||||
if (_stitchTarget != null)
|
||||
{
|
||||
_stitchTarget.GetVertices(_stitchTargetMeshData.Vertices);
|
||||
_stitchTarget.GetNormals(_stitchTargetMeshData.Normals);
|
||||
mesh.Vertices[meshVertCount - 1] = new Vector3(
|
||||
mesh.Vertices[meshVertCount - 1].x,
|
||||
_stitchTargetMeshData.Vertices[0].y,
|
||||
mesh.Vertices[meshVertCount - 1].z);
|
||||
|
||||
mesh.Normals[meshVertCount - 1] = new Vector3(
|
||||
_stitchTargetMeshData.Normals[0].x,
|
||||
_stitchTargetMeshData.Normals[0].y,
|
||||
_stitchTargetMeshData.Normals[0].z);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3d67aa1b17136294b92c1a5f4d9d867a
|
||||
timeCreated: 1522755493
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,4 @@
|
||||
public interface IElevationBasedTerrainStrategy
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0629e9d95db9602439cbd191b7c35260
|
||||
timeCreated: 1522756350
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,109 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using Mapbox.Unity.MeshGeneration.Data;
|
||||
using Mapbox.Unity.Map;
|
||||
using Mapbox.Utils;
|
||||
using Mapbox.Unity.Utilities;
|
||||
|
||||
namespace Mapbox.Unity.MeshGeneration.Factories.TerrainStrategies
|
||||
{
|
||||
|
||||
public class FlatSphereTerrainStrategy : TerrainStrategy
|
||||
{
|
||||
public float Radius { get { return _elevationOptions.modificationOptions.earthRadius; } }
|
||||
public override int RequiredVertexCount
|
||||
{
|
||||
get { return _elevationOptions.modificationOptions.sampleCount * _elevationOptions.modificationOptions.sampleCount; }
|
||||
}
|
||||
|
||||
public override void Initialize(ElevationLayerProperties elOptions)
|
||||
{
|
||||
_elevationOptions = elOptions;
|
||||
}
|
||||
|
||||
public override void RegisterTile(UnityTile tile)
|
||||
{
|
||||
if (_elevationOptions.unityLayerOptions.addToLayer && tile.gameObject.layer != _elevationOptions.unityLayerOptions.layerId)
|
||||
{
|
||||
tile.gameObject.layer = _elevationOptions.unityLayerOptions.layerId;
|
||||
}
|
||||
|
||||
if ((int)tile.ElevationType != (int)ElevationLayerType.GlobeTerrain ||
|
||||
tile.MeshFilter.mesh.vertexCount != RequiredVertexCount)
|
||||
{
|
||||
tile.MeshFilter.mesh.Clear();
|
||||
tile.ElevationType = TileTerrainType.Globe;
|
||||
}
|
||||
|
||||
GenerateTerrainMesh(tile);
|
||||
}
|
||||
|
||||
void GenerateTerrainMesh(UnityTile tile)
|
||||
{
|
||||
var verts = new List<Vector3>();
|
||||
var _sampleCount = _elevationOptions.modificationOptions.sampleCount;
|
||||
var _radius = _elevationOptions.modificationOptions.earthRadius;
|
||||
for (float x = 0; x < _sampleCount; x++)
|
||||
{
|
||||
for (float y = 0; y < _sampleCount; y++)
|
||||
{
|
||||
var xx = Mathf.Lerp((float)tile.Rect.Min.x, ((float)tile.Rect.Min.x + (float)tile.Rect.Size.x),
|
||||
x / (_sampleCount - 1));
|
||||
var yy = Mathf.Lerp((float)tile.Rect.Max.y, ((float)tile.Rect.Max.y + (float)tile.Rect.Size.y),
|
||||
y / (_sampleCount - 1));
|
||||
|
||||
var ll = Conversions.MetersToLatLon(new Vector2d(xx, yy));
|
||||
|
||||
var latitude = (float)(Mathf.Deg2Rad * ll.x);
|
||||
var longitude = (float)(Mathf.Deg2Rad * ll.y);
|
||||
|
||||
float xPos = (_radius) * Mathf.Cos(latitude) * Mathf.Cos(longitude);
|
||||
float zPos = (_radius) * Mathf.Cos(latitude) * Mathf.Sin(longitude);
|
||||
float yPos = (_radius) * Mathf.Sin(latitude);
|
||||
|
||||
var pp = new Vector3(xPos, yPos, zPos);
|
||||
verts.Add(pp);
|
||||
}
|
||||
}
|
||||
|
||||
var trilist = new List<int>();
|
||||
for (int y = 0; y < _sampleCount - 1; y++)
|
||||
{
|
||||
for (int x = 0; x < _sampleCount - 1; x++)
|
||||
{
|
||||
trilist.Add((y * _sampleCount) + x);
|
||||
trilist.Add((y * _sampleCount) + x + _sampleCount + 1);
|
||||
trilist.Add((y * _sampleCount) + x + _sampleCount);
|
||||
|
||||
trilist.Add((y * _sampleCount) + x);
|
||||
trilist.Add((y * _sampleCount) + x + 1);
|
||||
trilist.Add((y * _sampleCount) + x + _sampleCount + 1);
|
||||
}
|
||||
}
|
||||
|
||||
var uvlist = new List<Vector2>();
|
||||
var step = 1f / (_sampleCount - 1);
|
||||
for (int i = 0; i < _sampleCount; i++)
|
||||
{
|
||||
for (int j = 0; j < _sampleCount; j++)
|
||||
{
|
||||
uvlist.Add(new Vector2(i * step, (j * step)));
|
||||
}
|
||||
}
|
||||
|
||||
tile.MeshFilter.mesh.SetVertices(verts);
|
||||
tile.MeshFilter.mesh.SetTriangles(trilist, 0);
|
||||
tile.MeshFilter.mesh.SetUVs(0, uvlist);
|
||||
tile.MeshFilter.mesh.RecalculateBounds();
|
||||
tile.MeshFilter.mesh.RecalculateNormals();
|
||||
|
||||
tile.transform.localPosition = Mapbox.Unity.Constants.Math.Vector3Zero;
|
||||
}
|
||||
|
||||
public override void UnregisterTile(UnityTile tile)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 830e93932f0e72840a1f6d588dce1a4a
|
||||
timeCreated: 1522755493
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,168 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using Mapbox.Unity.MeshGeneration.Data;
|
||||
using Mapbox.Unity.Map;
|
||||
using Mapbox.Unity.Utilities;
|
||||
|
||||
namespace Mapbox.Unity.MeshGeneration.Factories.TerrainStrategies
|
||||
{
|
||||
public class FlatTerrainStrategy : TerrainStrategy
|
||||
{
|
||||
Mesh _cachedQuad;
|
||||
|
||||
public override int RequiredVertexCount
|
||||
{
|
||||
get { return 4; }
|
||||
}
|
||||
|
||||
public override void Initialize(ElevationLayerProperties elOptions)
|
||||
{
|
||||
_elevationOptions = elOptions;
|
||||
}
|
||||
|
||||
public override void RegisterTile(UnityTile tile)
|
||||
{
|
||||
if (_elevationOptions.unityLayerOptions.addToLayer && tile.gameObject.layer != _elevationOptions.unityLayerOptions.layerId)
|
||||
{
|
||||
tile.gameObject.layer = _elevationOptions.unityLayerOptions.layerId;
|
||||
}
|
||||
|
||||
if (tile.RasterDataState != Enums.TilePropertyState.Loaded ||
|
||||
tile.MeshFilter.mesh.vertexCount != RequiredVertexCount)
|
||||
{
|
||||
if (_elevationOptions.sideWallOptions.isActive)
|
||||
{
|
||||
var firstMat = tile.MeshRenderer.materials[0];
|
||||
tile.MeshRenderer.materials = new Material[2]
|
||||
{
|
||||
firstMat,
|
||||
_elevationOptions.sideWallOptions.wallMaterial
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if ((int)tile.ElevationType != (int)ElevationLayerType.FlatTerrain)
|
||||
{
|
||||
tile.MeshFilter.mesh.Clear();
|
||||
// HACK: This is here in to make the system trigger a finished state.
|
||||
tile.MeshFilter.sharedMesh = GetQuad(tile, _elevationOptions.sideWallOptions.isActive);
|
||||
tile.ElevationType = TileTerrainType.Flat;
|
||||
}
|
||||
}
|
||||
|
||||
private Mesh GetQuad(UnityTile tile, bool buildSide)
|
||||
{
|
||||
if (_cachedQuad != null)
|
||||
{
|
||||
return _cachedQuad;
|
||||
}
|
||||
|
||||
return buildSide ? BuildQuadWithSides(tile) : BuildQuad(tile);
|
||||
}
|
||||
|
||||
Mesh BuildQuad(UnityTile tile)
|
||||
{
|
||||
var unityMesh = new Mesh();
|
||||
var verts = new Vector3[4];
|
||||
var norms = new Vector3[4];
|
||||
verts[0] = tile.TileScale * ((tile.Rect.Min - tile.Rect.Center).ToVector3xz());
|
||||
verts[1] = tile.TileScale * (new Vector3((float)(tile.Rect.Max.x - tile.Rect.Center.x), 0, (float)(tile.Rect.Min.y - tile.Rect.Center.y)));
|
||||
verts[2] = tile.TileScale * ((tile.Rect.Max - tile.Rect.Center).ToVector3xz());
|
||||
verts[3] = tile.TileScale * (new Vector3((float)(tile.Rect.Min.x - tile.Rect.Center.x), 0, (float)(tile.Rect.Max.y - tile.Rect.Center.y)));
|
||||
norms[0] = Mapbox.Unity.Constants.Math.Vector3Up;
|
||||
norms[1] = Mapbox.Unity.Constants.Math.Vector3Up;
|
||||
norms[2] = Mapbox.Unity.Constants.Math.Vector3Up;
|
||||
norms[3] = Mapbox.Unity.Constants.Math.Vector3Up;
|
||||
|
||||
unityMesh.vertices = verts;
|
||||
unityMesh.normals = norms;
|
||||
|
||||
var trilist = new int[6] { 0, 1, 2, 0, 2, 3 };
|
||||
unityMesh.SetTriangles(trilist, 0);
|
||||
|
||||
var uvlist = new Vector2[4]
|
||||
{
|
||||
new Vector2(0,1),
|
||||
new Vector2(1,1),
|
||||
new Vector2(1,0),
|
||||
new Vector2(0,0)
|
||||
};
|
||||
unityMesh.uv = uvlist;
|
||||
tile.MeshFilter.sharedMesh = unityMesh;
|
||||
_cachedQuad = unityMesh;
|
||||
|
||||
return unityMesh;
|
||||
}
|
||||
|
||||
private Mesh BuildQuadWithSides(UnityTile tile)
|
||||
{
|
||||
var unityMesh = new Mesh();
|
||||
var verts = new Vector3[20];
|
||||
var norms = new Vector3[20];
|
||||
verts[0] = tile.TileScale * ((tile.Rect.Min - tile.Rect.Center).ToVector3xz());
|
||||
verts[1] = tile.TileScale * (new Vector3((float)(tile.Rect.Max.x - tile.Rect.Center.x), 0, (float)(tile.Rect.Min.y - tile.Rect.Center.y)));
|
||||
verts[2] = tile.TileScale * ((tile.Rect.Max - tile.Rect.Center).ToVector3xz());
|
||||
verts[3] = tile.TileScale * (new Vector3((float)(tile.Rect.Min.x - tile.Rect.Center.x), 0, (float)(tile.Rect.Max.y - tile.Rect.Center.y)));
|
||||
norms[0] = Mapbox.Unity.Constants.Math.Vector3Up;
|
||||
norms[1] = Mapbox.Unity.Constants.Math.Vector3Up;
|
||||
norms[2] = Mapbox.Unity.Constants.Math.Vector3Up;
|
||||
norms[3] = Mapbox.Unity.Constants.Math.Vector3Up;
|
||||
|
||||
//verts goes
|
||||
//01
|
||||
//32
|
||||
unityMesh.subMeshCount = 2;
|
||||
Vector3 norm = Mapbox.Unity.Constants.Math.Vector3Up;
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
verts[4 * (i + 1)] = verts[i];
|
||||
verts[4 * (i + 1) + 1] = verts[i + 1];
|
||||
verts[4 * (i + 1) + 2] = verts[i] + new Vector3(0, -_elevationOptions.sideWallOptions.wallHeight, 0);
|
||||
verts[4 * (i + 1) + 3] = verts[i + 1] + new Vector3(0, -_elevationOptions.sideWallOptions.wallHeight, 0);
|
||||
|
||||
norm = Vector3.Cross(verts[4 * (i + 1) + 1] - verts[4 * (i + 1) + 2], verts[4 * (i + 1)] - verts[4 * (i + 1) + 1]).normalized;
|
||||
norms[4 * (i + 1)] = norm;
|
||||
norms[4 * (i + 1) + 1] = norm;
|
||||
norms[4 * (i + 1) + 2] = norm;
|
||||
norms[4 * (i + 1) + 3] = norm;
|
||||
}
|
||||
|
||||
unityMesh.vertices = verts;
|
||||
unityMesh.normals = norms;
|
||||
|
||||
var trilist = new List<int>(6) { 0, 1, 2, 0, 2, 3 };
|
||||
unityMesh.SetTriangles(trilist, 0);
|
||||
|
||||
trilist = new List<int>(8);
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
trilist.Add(4 * (i + 1));
|
||||
trilist.Add(4 * (i + 1) + 2);
|
||||
trilist.Add(4 * (i + 1) + 1);
|
||||
|
||||
trilist.Add(4 * (i + 1) + 1);
|
||||
trilist.Add(4 * (i + 1) + 2);
|
||||
trilist.Add(4 * (i + 1) + 3);
|
||||
}
|
||||
unityMesh.SetTriangles(trilist, 1);
|
||||
|
||||
var uvlist = new Vector2[20];
|
||||
uvlist[0] = new Vector2(0, 1);
|
||||
uvlist[1] = new Vector2(1, 1);
|
||||
uvlist[2] = new Vector2(1, 0);
|
||||
uvlist[3] = new Vector2(0, 0);
|
||||
for (int i = 4; i < 20; i += 4)
|
||||
{
|
||||
uvlist[i] = new Vector2(1, 1);
|
||||
uvlist[i + 1] = new Vector2(0, 1);
|
||||
uvlist[i + 2] = new Vector2(1, 0);
|
||||
uvlist[i + 3] = new Vector2(0, 0);
|
||||
}
|
||||
unityMesh.uv = uvlist;
|
||||
tile.MeshFilter.sharedMesh = unityMesh;
|
||||
_cachedQuad = unityMesh;
|
||||
|
||||
return unityMesh;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dc77d6f0c5be2cb49bf8014f58feeb43
|
||||
timeCreated: 1522755493
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,403 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using Mapbox.Unity.MeshGeneration.Data;
|
||||
using Mapbox.Unity.Map;
|
||||
using Mapbox.Map;
|
||||
using Mapbox.Utils;
|
||||
using UnityEngine.Rendering;
|
||||
|
||||
namespace Mapbox.Unity.MeshGeneration.Factories.TerrainStrategies
|
||||
{
|
||||
public class LowPolyTerrainStrategy : TerrainStrategy, IElevationBasedTerrainStrategy
|
||||
{
|
||||
protected Dictionary<UnwrappedTileId, Mesh> _meshData;
|
||||
private Mesh _stitchTarget;
|
||||
private MeshData _currentTileMeshData;
|
||||
private MeshData _stitchTargetMeshData;
|
||||
private List<Vector3> _newVertexList;
|
||||
private List<Vector3> _newNormalList;
|
||||
private List<Vector2> _newUvList;
|
||||
private List<int> _newTriangleList;
|
||||
private Vector3 _newDir;
|
||||
private int _vertA, _vertB, _vertC;
|
||||
private int _counter;
|
||||
|
||||
|
||||
public override void Initialize(ElevationLayerProperties elOptions)
|
||||
{
|
||||
_elevationOptions = elOptions;
|
||||
_meshData = new Dictionary<UnwrappedTileId, Mesh>();
|
||||
_currentTileMeshData = new MeshData();
|
||||
_stitchTargetMeshData = new MeshData();
|
||||
var sampleCountSquare = _elevationOptions.modificationOptions.sampleCount * _elevationOptions.modificationOptions.sampleCount;
|
||||
_newVertexList = new List<Vector3>(sampleCountSquare);
|
||||
_newNormalList = new List<Vector3>(sampleCountSquare);
|
||||
_newUvList = new List<Vector2>(sampleCountSquare);
|
||||
_newTriangleList = new List<int>();
|
||||
}
|
||||
|
||||
public override void UnregisterTile(UnityTile tile)
|
||||
{
|
||||
_meshData.Remove(tile.UnwrappedTileId);
|
||||
}
|
||||
|
||||
public override void RegisterTile(UnityTile tile)
|
||||
{
|
||||
if (_elevationOptions.unityLayerOptions.addToLayer && tile.gameObject.layer != _elevationOptions.unityLayerOptions.layerId)
|
||||
{
|
||||
tile.gameObject.layer = _elevationOptions.unityLayerOptions.layerId;
|
||||
}
|
||||
if ((int)tile.ElevationType != (int)ElevationLayerType.LowPolygonTerrain ||
|
||||
tile.MeshFilter.mesh.vertexCount != RequiredVertexCount)
|
||||
{
|
||||
tile.MeshFilter.mesh.Clear();
|
||||
CreateBaseMesh(tile);
|
||||
tile.ElevationType = TileTerrainType.LowPoly;
|
||||
}
|
||||
|
||||
GenerateTerrainMesh(tile);
|
||||
}
|
||||
|
||||
private void CreateBaseMesh(UnityTile tile)
|
||||
{
|
||||
//TODO use arrays instead of lists
|
||||
_newVertexList.Clear();
|
||||
_newNormalList.Clear();
|
||||
_newUvList.Clear();
|
||||
_newTriangleList.Clear();
|
||||
|
||||
var cap = (_elevationOptions.modificationOptions.sampleCount - 1);
|
||||
for (float y = 0; y < cap; y++)
|
||||
{
|
||||
for (float x = 0; x < cap; x++)
|
||||
{
|
||||
var x1 = tile.TileScale * (float)(Mathd.Lerp(tile.Rect.Min.x, tile.Rect.Max.x, x / cap) - tile.Rect.Center.x);
|
||||
var y1 = tile.TileScale * (float)(Mathd.Lerp(tile.Rect.Min.y, tile.Rect.Max.y, y / cap) - tile.Rect.Center.y);
|
||||
var x2 = tile.TileScale * (float)(Mathd.Lerp(tile.Rect.Min.x, tile.Rect.Max.x, (x + 1) / cap) - tile.Rect.Center.x);
|
||||
var y2 = tile.TileScale * (float)(Mathd.Lerp(tile.Rect.Min.y, tile.Rect.Max.y, (y + 1) / cap) - tile.Rect.Center.y);
|
||||
|
||||
var triStart = _newVertexList.Count;
|
||||
_newVertexList.Add(new Vector3(x1, 0, y1));
|
||||
_newVertexList.Add(new Vector3(x2, 0, y1));
|
||||
_newVertexList.Add(new Vector3(x1, 0, y2));
|
||||
//--
|
||||
_newVertexList.Add(new Vector3(x2, 0, y1));
|
||||
_newVertexList.Add(new Vector3(x2, 0, y2));
|
||||
_newVertexList.Add(new Vector3(x1, 0, y2));
|
||||
|
||||
_newNormalList.Add(Mapbox.Unity.Constants.Math.Vector3Up);
|
||||
_newNormalList.Add(Mapbox.Unity.Constants.Math.Vector3Up);
|
||||
_newNormalList.Add(Mapbox.Unity.Constants.Math.Vector3Up);
|
||||
//--
|
||||
_newNormalList.Add(Mapbox.Unity.Constants.Math.Vector3Up);
|
||||
_newNormalList.Add(Mapbox.Unity.Constants.Math.Vector3Up);
|
||||
_newNormalList.Add(Mapbox.Unity.Constants.Math.Vector3Up);
|
||||
|
||||
|
||||
_newUvList.Add(new Vector2(x / cap, 1 - y / cap));
|
||||
_newUvList.Add(new Vector2((x + 1) / cap, 1 - y / cap));
|
||||
_newUvList.Add(new Vector2(x / cap, 1 - (y + 1) / cap));
|
||||
//--
|
||||
_newUvList.Add(new Vector2((x + 1) / cap, 1 - y / cap));
|
||||
_newUvList.Add(new Vector2((x + 1) / cap, 1 - (y + 1) / cap));
|
||||
_newUvList.Add(new Vector2(x / cap, 1 - (y + 1) / cap));
|
||||
|
||||
_newTriangleList.Add(triStart);
|
||||
_newTriangleList.Add(triStart + 1);
|
||||
_newTriangleList.Add(triStart + 2);
|
||||
//--
|
||||
_newTriangleList.Add(triStart + 3);
|
||||
_newTriangleList.Add(triStart + 4);
|
||||
_newTriangleList.Add(triStart + 5);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var mesh = tile.MeshFilter.mesh;
|
||||
mesh.indexFormat = IndexFormat.UInt32;
|
||||
mesh.SetVertices(_newVertexList);
|
||||
mesh.SetNormals(_newNormalList);
|
||||
mesh.SetUVs(0, _newUvList);
|
||||
mesh.SetTriangles(_newTriangleList, 0);
|
||||
mesh.RecalculateBounds();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Creates the non-flat terrain mesh, using a grid by defined resolution (_sampleCount). Vertex order goes right & up. Normals are calculated manually and UV map is fitted/stretched 1-1.
|
||||
/// Any additional scripts or logic, like MeshCollider or setting layer, can be done here.
|
||||
/// </summary>
|
||||
/// <param name="tile"></param>
|
||||
// <param name="heightMultiplier">Multiplier for queried height value</param>
|
||||
private void GenerateTerrainMesh(UnityTile tile)
|
||||
{
|
||||
tile.MeshFilter.mesh.GetVertices(_currentTileMeshData.Vertices);
|
||||
tile.MeshFilter.mesh.GetNormals(_currentTileMeshData.Normals);
|
||||
|
||||
var cap = (_elevationOptions.modificationOptions.sampleCount - 1);
|
||||
for (float y = 0; y < cap; y++)
|
||||
{
|
||||
for (float x = 0; x < cap; x++)
|
||||
{
|
||||
_currentTileMeshData.Vertices[(int)(y * cap + x) * 6] = new Vector3(
|
||||
_currentTileMeshData.Vertices[(int)(y * cap + x) * 6].x,
|
||||
tile.QueryHeightData(x / cap, 1 - y / cap),
|
||||
_currentTileMeshData.Vertices[(int)(y * cap + x) * 6].z);
|
||||
|
||||
_currentTileMeshData.Vertices[(int)(y * cap + x) * 6 + 1] = new Vector3(
|
||||
_currentTileMeshData.Vertices[(int)(y * cap + x) * 6 + 1].x,
|
||||
tile.QueryHeightData((x + 1) / cap, 1 - y / cap),
|
||||
_currentTileMeshData.Vertices[(int)(y * cap + x) * 6 + 1].z);
|
||||
|
||||
_currentTileMeshData.Vertices[(int)(y * cap + x) * 6 + 2] = new Vector3(
|
||||
_currentTileMeshData.Vertices[(int)(y * cap + x) * 6 + 2].x,
|
||||
tile.QueryHeightData(x / cap, 1 - (y + 1) / cap),
|
||||
_currentTileMeshData.Vertices[(int)(y * cap + x) * 6 + 2].z);
|
||||
|
||||
//--
|
||||
|
||||
_currentTileMeshData.Vertices[(int)(y * cap + x) * 6 + 3] = new Vector3(
|
||||
_currentTileMeshData.Vertices[(int)(y * cap + x) * 6 + 3].x,
|
||||
tile.QueryHeightData((x + 1) / cap, 1 - y / cap),
|
||||
_currentTileMeshData.Vertices[(int)(y * cap + x) * 6 + 3].z);
|
||||
|
||||
_currentTileMeshData.Vertices[(int)(y * cap + x) * 6 + 4] = new Vector3(
|
||||
_currentTileMeshData.Vertices[(int)(y * cap + x) * 6 + 4].x,
|
||||
tile.QueryHeightData((x + 1) / cap, 1 - (y + 1) / cap),
|
||||
_currentTileMeshData.Vertices[(int)(y * cap + x) * 6 + 4].z);
|
||||
|
||||
_currentTileMeshData.Vertices[(int)(y * cap + x) * 6 + 5] = new Vector3(
|
||||
_currentTileMeshData.Vertices[(int)(y * cap + x) * 6 + 5].x,
|
||||
tile.QueryHeightData(x / cap, 1 - (y + 1) / cap),
|
||||
_currentTileMeshData.Vertices[(int)(y * cap + x) * 6 + 5].z);
|
||||
|
||||
|
||||
|
||||
_newDir = Vector3.Cross(_currentTileMeshData.Vertices[(int)(y * cap + x) * 6 + 1] - _currentTileMeshData.Vertices[(int)(y * cap + x) * 6], _currentTileMeshData.Vertices[(int)(y * cap + x) * 6 + 2] - _currentTileMeshData.Vertices[(int)(y * cap + x) * 6]);
|
||||
_currentTileMeshData.Normals[(int)(y * cap + x) * 6 + 0] = _newDir;
|
||||
_currentTileMeshData.Normals[(int)(y * cap + x) * 6 + 1] = _newDir;
|
||||
_currentTileMeshData.Normals[(int)(y * cap + x) * 6 + 2] = _newDir;
|
||||
//--
|
||||
_newDir = Vector3.Cross(_currentTileMeshData.Vertices[(int)(y * cap + x) * 6 + 4] - _currentTileMeshData.Vertices[(int)(y * cap + x) * 6 + 3], _currentTileMeshData.Vertices[(int)(y * cap + x) * 6 + 5] - _currentTileMeshData.Vertices[(int)(y * cap + x) * 6 + 3]);
|
||||
_currentTileMeshData.Normals[(int)(y * cap + x) * 6 + 3] = _newDir;
|
||||
_currentTileMeshData.Normals[(int)(y * cap + x) * 6 + 4] = _newDir;
|
||||
_currentTileMeshData.Normals[(int)(y * cap + x) * 6 + 5] = _newDir;
|
||||
}
|
||||
}
|
||||
FixStitches(tile.UnwrappedTileId, _currentTileMeshData);
|
||||
tile.MeshFilter.mesh.SetVertices(_currentTileMeshData.Vertices);
|
||||
tile.MeshFilter.mesh.SetNormals(_currentTileMeshData.Normals);
|
||||
tile.MeshFilter.mesh.RecalculateBounds();
|
||||
|
||||
if (!_meshData.ContainsKey(tile.UnwrappedTileId))
|
||||
{
|
||||
_meshData.Add(tile.UnwrappedTileId, tile.MeshFilter.mesh);
|
||||
}
|
||||
|
||||
if (_elevationOptions.colliderOptions.addCollider)
|
||||
{
|
||||
var meshCollider = tile.Collider as MeshCollider;
|
||||
if (meshCollider)
|
||||
{
|
||||
meshCollider.sharedMesh = tile.MeshFilter.mesh;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ResetToFlatMesh(UnityTile tile)
|
||||
{
|
||||
tile.MeshFilter.mesh.GetVertices(_currentTileMeshData.Vertices);
|
||||
tile.MeshFilter.mesh.GetNormals(_currentTileMeshData.Normals);
|
||||
|
||||
_counter = _currentTileMeshData.Vertices.Count;
|
||||
for (int i = 0; i < _counter; i++)
|
||||
{
|
||||
_currentTileMeshData.Vertices[i] = new Vector3(
|
||||
_currentTileMeshData.Vertices[i].x,
|
||||
0,
|
||||
_currentTileMeshData.Vertices[i].z);
|
||||
_currentTileMeshData.Normals[i] = Mapbox.Unity.Constants.Math.Vector3Up;
|
||||
}
|
||||
|
||||
tile.MeshFilter.mesh.SetVertices(_currentTileMeshData.Vertices);
|
||||
tile.MeshFilter.mesh.SetNormals(_currentTileMeshData.Normals);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checkes all neighbours of the given tile and stitches the edges to achieve a smooth mesh surface.
|
||||
/// </summary>
|
||||
/// <param name="tileId"></param>
|
||||
/// <param name="mesh"></param>
|
||||
private void FixStitches(UnwrappedTileId tileId, MeshData mesh)
|
||||
{
|
||||
var meshVertCount = mesh.Vertices.Count;
|
||||
_stitchTarget = null;
|
||||
_meshData.TryGetValue(tileId.North, out _stitchTarget);
|
||||
var cap = _elevationOptions.modificationOptions.sampleCount - 1;
|
||||
if (_stitchTarget != null)
|
||||
{
|
||||
_stitchTarget.GetVertices(_stitchTargetMeshData.Vertices);
|
||||
_stitchTarget.GetNormals(_stitchTargetMeshData.Normals);
|
||||
|
||||
for (int i = 0; i < cap; i++)
|
||||
{
|
||||
mesh.Vertices[6 * i] = new Vector3(
|
||||
mesh.Vertices[6 * i].x,
|
||||
_stitchTargetMeshData.Vertices[6 * cap * (cap - 1) + 6 * i + 2].y,
|
||||
mesh.Vertices[6 * i].z);
|
||||
mesh.Vertices[6 * i + 1] = new Vector3(
|
||||
mesh.Vertices[6 * i + 1].x,
|
||||
_stitchTargetMeshData.Vertices[6 * cap * (cap - 1) + 6 * i + 4].y,
|
||||
mesh.Vertices[6 * i + 1].z);
|
||||
mesh.Vertices[6 * i + 3] = new Vector3(
|
||||
mesh.Vertices[6 * i + 3].x,
|
||||
_stitchTargetMeshData.Vertices[6 * cap * (cap - 1) + 6 * i + 4].y,
|
||||
mesh.Vertices[6 * i + 3].z);
|
||||
}
|
||||
}
|
||||
|
||||
_stitchTarget = null;
|
||||
_meshData.TryGetValue(tileId.South, out _stitchTarget);
|
||||
if (_stitchTarget != null)
|
||||
{
|
||||
_stitchTarget.GetVertices(_stitchTargetMeshData.Vertices);
|
||||
_stitchTarget.GetNormals(_stitchTargetMeshData.Normals);
|
||||
|
||||
for (int i = 0; i < cap; i++)
|
||||
{
|
||||
mesh.Vertices[6 * cap * (cap - 1) + 6 * i + 2] = new Vector3(
|
||||
mesh.Vertices[6 * cap * (cap - 1) + 6 * i + 2].x,
|
||||
_stitchTargetMeshData.Vertices[6 * i].y,
|
||||
mesh.Vertices[6 * cap * (cap - 1) + 6 * i + 2].z);
|
||||
mesh.Vertices[6 * cap * (cap - 1) + 6 * i + 5] = new Vector3(
|
||||
mesh.Vertices[6 * cap * (cap - 1) + 6 * i + 5].x,
|
||||
_stitchTargetMeshData.Vertices[6 * i].y,
|
||||
mesh.Vertices[6 * cap * (cap - 1) + 6 * i + 5].z);
|
||||
mesh.Vertices[6 * cap * (cap - 1) + 6 * i + 4] = new Vector3(
|
||||
mesh.Vertices[6 * cap * (cap - 1) + 6 * i + 4].x,
|
||||
_stitchTargetMeshData.Vertices[6 * i + 3].y,
|
||||
mesh.Vertices[6 * cap * (cap - 1) + 6 * i + 4].z);
|
||||
}
|
||||
}
|
||||
|
||||
_stitchTarget = null;
|
||||
_meshData.TryGetValue(tileId.West, out _stitchTarget);
|
||||
if (_stitchTarget != null)
|
||||
{
|
||||
_stitchTarget.GetVertices(_stitchTargetMeshData.Vertices);
|
||||
_stitchTarget.GetNormals(_stitchTargetMeshData.Normals);
|
||||
|
||||
for (int i = 0; i < cap; i++)
|
||||
{
|
||||
mesh.Vertices[6 * cap * i] = new Vector3(
|
||||
mesh.Vertices[6 * cap * i].x,
|
||||
_stitchTargetMeshData.Vertices[6 * cap * i + 6 * cap - 5].y,
|
||||
mesh.Vertices[6 * cap * i].z);
|
||||
|
||||
mesh.Vertices[6 * cap * i + 2] = new Vector3(
|
||||
mesh.Vertices[6 * cap * i + 2].x,
|
||||
_stitchTargetMeshData.Vertices[6 * cap * i + 6 * cap - 2].y,
|
||||
mesh.Vertices[6 * cap * i + 2].z);
|
||||
|
||||
mesh.Vertices[6 * cap * i + 5] = new Vector3(
|
||||
mesh.Vertices[6 * cap * i + 5].x,
|
||||
_stitchTargetMeshData.Vertices[6 * cap * i + 6 * cap - 2].y,
|
||||
mesh.Vertices[6 * cap * i + 5].z);
|
||||
}
|
||||
}
|
||||
|
||||
_stitchTarget = null;
|
||||
_meshData.TryGetValue(tileId.East, out _stitchTarget);
|
||||
|
||||
if (_stitchTarget != null)
|
||||
{
|
||||
_stitchTarget.GetVertices(_stitchTargetMeshData.Vertices);
|
||||
_stitchTarget.GetNormals(_stitchTargetMeshData.Normals);
|
||||
|
||||
for (int i = 0; i < cap; i++)
|
||||
{
|
||||
mesh.Vertices[6 * cap * i + 6 * cap - 5] = new Vector3(
|
||||
mesh.Vertices[6 * cap * i + 6 * cap - 5].x,
|
||||
_stitchTargetMeshData.Vertices[6 * cap * i].y,
|
||||
mesh.Vertices[6 * cap * i + 6 * cap - 5].z);
|
||||
|
||||
mesh.Vertices[6 * cap * i + 6 * cap - 3] = new Vector3(
|
||||
mesh.Vertices[6 * cap * i + 6 * cap - 3].x,
|
||||
_stitchTargetMeshData.Vertices[6 * cap * i].y,
|
||||
mesh.Vertices[6 * cap * i + 6 * cap - 3].z);
|
||||
|
||||
mesh.Vertices[6 * cap * i + 6 * cap - 2] = new Vector3(
|
||||
mesh.Vertices[6 * cap * i + 6 * cap - 2].x,
|
||||
_stitchTargetMeshData.Vertices[6 * cap * i + 5].y,
|
||||
mesh.Vertices[6 * cap * i + 6 * cap - 2].z);
|
||||
}
|
||||
}
|
||||
|
||||
_stitchTarget = null;
|
||||
_meshData.TryGetValue(tileId.NorthWest, out _stitchTarget);
|
||||
|
||||
if (_stitchTarget != null)
|
||||
{
|
||||
_stitchTarget.GetVertices(_stitchTargetMeshData.Vertices);
|
||||
_stitchTarget.GetNormals(_stitchTargetMeshData.Normals);
|
||||
|
||||
mesh.Vertices[0] = new Vector3(
|
||||
mesh.Vertices[0].x,
|
||||
_stitchTargetMeshData.Vertices[meshVertCount - 2].y,
|
||||
mesh.Vertices[0].z);
|
||||
}
|
||||
|
||||
_stitchTarget = null;
|
||||
_meshData.TryGetValue(tileId.NorthEast, out _stitchTarget);
|
||||
|
||||
if (_stitchTarget != null)
|
||||
{
|
||||
_stitchTarget.GetVertices(_stitchTargetMeshData.Vertices);
|
||||
_stitchTarget.GetNormals(_stitchTargetMeshData.Normals);
|
||||
|
||||
mesh.Vertices[6 * cap - 5] = new Vector3(
|
||||
mesh.Vertices[6 * cap - 5].x,
|
||||
_stitchTargetMeshData.Vertices[6 * (cap - 1) * cap + 2].y,
|
||||
mesh.Vertices[6 * cap - 5].z);
|
||||
|
||||
mesh.Vertices[6 * cap - 3] = new Vector3(
|
||||
mesh.Vertices[6 * cap - 3].x,
|
||||
_stitchTargetMeshData.Vertices[6 * (cap - 1) * cap + 2].y,
|
||||
mesh.Vertices[6 * cap - 3].z);
|
||||
}
|
||||
|
||||
_stitchTarget = null;
|
||||
_meshData.TryGetValue(tileId.SouthWest, out _stitchTarget);
|
||||
|
||||
if (_stitchTarget != null)
|
||||
{
|
||||
_stitchTarget.GetVertices(_stitchTargetMeshData.Vertices);
|
||||
_stitchTarget.GetNormals(_stitchTargetMeshData.Normals);
|
||||
|
||||
mesh.Vertices[6 * (cap - 1) * cap + 2] = new Vector3(
|
||||
mesh.Vertices[6 * (cap - 1) * cap + 2].x,
|
||||
_stitchTargetMeshData.Vertices[6 * cap - 5].y,
|
||||
mesh.Vertices[6 * (cap - 1) * cap + 2].z);
|
||||
|
||||
mesh.Vertices[6 * (cap - 1) * cap + 5] = new Vector3(
|
||||
mesh.Vertices[6 * (cap - 1) * cap + 5].x,
|
||||
_stitchTargetMeshData.Vertices[6 * cap - 5].y,
|
||||
mesh.Vertices[6 * (cap - 1) * cap + 5].z);
|
||||
}
|
||||
|
||||
_stitchTarget = null;
|
||||
_meshData.TryGetValue(tileId.SouthEast, out _stitchTarget);
|
||||
|
||||
if (_stitchTarget != null)
|
||||
{
|
||||
_stitchTarget.GetVertices(_stitchTargetMeshData.Vertices);
|
||||
_stitchTarget.GetNormals(_stitchTargetMeshData.Normals);
|
||||
|
||||
mesh.Vertices[6 * cap * cap - 2] = new Vector3(
|
||||
mesh.Vertices[6 * cap * cap - 2].x,
|
||||
_stitchTargetMeshData.Vertices[0].y,
|
||||
mesh.Vertices[6 * cap * cap - 2].z);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2e1d75acbfaded5459635c6030b0f915
|
||||
timeCreated: 1522755493
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,45 @@
|
||||
using UnityEngine;
|
||||
using Mapbox.Unity.MeshGeneration.Data;
|
||||
using System;
|
||||
using Mapbox.Unity.Map;
|
||||
using System.Collections.ObjectModel;
|
||||
using Mapbox.Map;
|
||||
|
||||
namespace Mapbox.Unity.MeshGeneration.Factories.TerrainStrategies
|
||||
{
|
||||
public class TerrainStrategy
|
||||
{
|
||||
[SerializeField]
|
||||
protected ElevationLayerProperties _elevationOptions = new ElevationLayerProperties();
|
||||
|
||||
public virtual int RequiredVertexCount
|
||||
{
|
||||
get { return 0; }
|
||||
}
|
||||
|
||||
public virtual void Initialize(ElevationLayerProperties elOptions)
|
||||
{
|
||||
_elevationOptions = elOptions;
|
||||
}
|
||||
|
||||
public virtual void RegisterTile(UnityTile tile)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public virtual void PostProcessTile(UnityTile tile)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public virtual void UnregisterTile(UnityTile tile)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public virtual void DataErrorOccurred(UnityTile tile, TileErrorEventArgs e)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 33b4040c296ec314793ac7182cc3e81c
|
||||
timeCreated: 1522755493
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,17 @@
|
||||
namespace Mapbox.Unity.MeshGeneration.Factories
|
||||
{
|
||||
using Mapbox.Unity.MeshGeneration.Data;
|
||||
using System;
|
||||
|
||||
public class TileProcessFinishedEventArgs : EventArgs
|
||||
{
|
||||
public AbstractTileFactory Factory;
|
||||
public UnityTile Tile;
|
||||
|
||||
public TileProcessFinishedEventArgs(AbstractTileFactory vectorTileFactory, UnityTile tile)
|
||||
{
|
||||
Factory = vectorTileFactory;
|
||||
Tile = tile;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2e9e504493544ff4baa22317cd678f87
|
||||
timeCreated: 1532126963
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,38 @@
|
||||
using Mapbox.Map;
|
||||
using Mapbox.Unity.Map;
|
||||
using Mapbox.Unity.MeshGeneration.Data;
|
||||
using Mapbox.Unity.MeshGeneration.Enums;
|
||||
using System;
|
||||
public class VectorDataFetcher : DataFetcher
|
||||
{
|
||||
public Action<UnityTile, VectorTile> DataRecieved = (t, s) => { };
|
||||
public Action<UnityTile, VectorTile, TileErrorEventArgs> FetchingError = (t, r, s) => { };
|
||||
|
||||
//tile here should be totally optional and used only not to have keep a dictionary in terrain factory base
|
||||
public override void FetchData(DataFetcherParameters parameters)
|
||||
{
|
||||
var vectorDaraParameters = parameters as VectorDataFetcherParameters;
|
||||
if(vectorDaraParameters == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var vectorTile = (vectorDaraParameters.useOptimizedStyle) ? new VectorTile(vectorDaraParameters.style.Id, vectorDaraParameters.style.Modified) : new VectorTile();
|
||||
vectorDaraParameters.tile.AddTile(vectorTile);
|
||||
vectorTile.Initialize(_fileSource, vectorDaraParameters.tile.CanonicalTileId, vectorDaraParameters.mapid, () =>
|
||||
{
|
||||
if (vectorDaraParameters.tile.CanonicalTileId != vectorTile.Id)
|
||||
{
|
||||
//this means tile object is recycled and reused. Returned data doesn't belong to this tile but probably the previous one. So we're trashing it.
|
||||
return;
|
||||
}
|
||||
if (vectorTile.HasError)
|
||||
{
|
||||
FetchingError(vectorDaraParameters.tile, vectorTile, new TileErrorEventArgs(vectorDaraParameters.tile.CanonicalTileId, vectorTile.GetType(), vectorDaraParameters.tile, vectorTile.Exceptions));
|
||||
}
|
||||
else
|
||||
{
|
||||
DataRecieved(vectorDaraParameters.tile, vectorTile);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b666b1dbd8d5d6444a8fc9945c0106ce
|
||||
timeCreated: 1524487271
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,475 @@
|
||||
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
|
||||
{
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
//[CreateAssetMenu(menuName = "Mapbox/Factories/Vector Tile Factory")]
|
||||
public class VectorTileFactory : AbstractTileFactory
|
||||
{
|
||||
#region Private/Protected Fields
|
||||
private Dictionary<string, List<LayerVisualizerBase>> _layerBuilder;
|
||||
private VectorLayerProperties _properties;
|
||||
private Dictionary<UnityTile, HashSet<LayerVisualizerBase>> _layerProgress;
|
||||
protected VectorDataFetcher DataFetcher;
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
public string MapId
|
||||
{
|
||||
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<VectorLayerVisualizer>();
|
||||
|
||||
//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<LayerVisualizerBase> { visualizer });
|
||||
}
|
||||
return visualizer;
|
||||
}
|
||||
|
||||
public virtual LayerVisualizerBase AddPOIVectorLayerVisualizer(PrefabItemOptions poiSubLayer)
|
||||
{
|
||||
LayerVisualizerBase visualizer = CreateInstance<LocationPrefabsLayerVisualizer>();
|
||||
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<LayerVisualizerBase>() { 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.ClearCaches();
|
||||
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
|
||||
/// <summary>
|
||||
/// Set up sublayers using VectorLayerVisualizers.
|
||||
/// </summary>
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
_layerProgress = new Dictionary<UnityTile, HashSet<LayerVisualizerBase>>();
|
||||
_layerBuilder = new Dictionary<string, List<LayerVisualizerBase>>();
|
||||
|
||||
DataFetcher = ScriptableObject.CreateInstance<VectorDataFetcher>();
|
||||
DataFetcher.DataRecieved += OnVectorDataRecieved;
|
||||
DataFetcher.FetchingError += OnDataError;
|
||||
|
||||
CreatePOILayerVisualizers();
|
||||
|
||||
CreateLayerVisualizers();
|
||||
}
|
||||
|
||||
protected override void OnRegistered(UnityTile tile)
|
||||
{
|
||||
if (string.IsNullOrEmpty(MapId) || _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,
|
||||
mapid = MapId,
|
||||
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);
|
||||
//visualizer.LayerVisualizerHasChanged -= UpdateTileFactory;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
foreach (var layerList in _layerBuilder.Values)
|
||||
{
|
||||
foreach (var layerVisualizerBase in layerList)
|
||||
{
|
||||
layerVisualizerBase.ClearCaches();
|
||||
}
|
||||
}
|
||||
_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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Method to be called when a tile error has occurred.
|
||||
/// </summary>
|
||||
/// <param name="e"><see cref="T:Mapbox.Map.TileErrorEventArgs"/> instance/</param>
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
foreach (var layerName in tile.VectorData.Data.LayerNames())
|
||||
{
|
||||
if (_layerBuilder.ContainsKey(layerName))
|
||||
{
|
||||
foreach (var builder in _layerBuilder[layerName])
|
||||
{
|
||||
CreateFeatureWithBuilder(tile, layerName, builder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//emptylayer for visualizers that don't depend on outside data sources
|
||||
string emptyLayer = "";
|
||||
if (_layerBuilder.ContainsKey(emptyLayer))
|
||||
{
|
||||
foreach (var builder in _layerBuilder[emptyLayer])
|
||||
{
|
||||
CreateFeatureWithBuilder(tile, emptyLayer, builder);
|
||||
}
|
||||
}
|
||||
|
||||
if (!_layerProgress.ContainsKey(tile))
|
||||
{
|
||||
tile.VectorDataState = TilePropertyState.Loaded;
|
||||
}
|
||||
}
|
||||
|
||||
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<LayerVisualizerBase> { 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.ClearCaches();
|
||||
}
|
||||
}
|
||||
_layerBuilder.Clear();
|
||||
}
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 63d97cb1cfb3f1b499c24763afa54873
|
||||
timeCreated: 1520300453
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 32e94cd9a75b25c439ca153c42fcb598
|
||||
folderAsset: yes
|
||||
timeCreated: 1485210114
|
||||
licenseType: Pro
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,26 @@
|
||||
namespace Mapbox.Unity.MeshGeneration.Filters
|
||||
{
|
||||
using UnityEngine;
|
||||
using Mapbox.Unity.MeshGeneration.Data;
|
||||
using System;
|
||||
|
||||
public interface ILayerFeatureFilterComparer
|
||||
{
|
||||
bool Try(VectorFeatureUnity feature);
|
||||
}
|
||||
|
||||
public class FilterBase : ILayerFeatureFilterComparer
|
||||
{
|
||||
public virtual string Key { get { return ""; } }
|
||||
|
||||
public virtual bool Try(VectorFeatureUnity feature)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public virtual void Initialize()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7495b32b05d97be4a8666ab6af32cdee
|
||||
timeCreated: 1485210126
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,32 @@
|
||||
namespace Mapbox.Unity.MeshGeneration.Filters
|
||||
{
|
||||
using UnityEngine;
|
||||
using Mapbox.Unity.MeshGeneration.Data;
|
||||
|
||||
public class HeightFilter : FilterBase
|
||||
{
|
||||
public enum HeightFilterOptions
|
||||
{
|
||||
Above,
|
||||
Below
|
||||
}
|
||||
|
||||
public override string Key { get { return "height"; } }
|
||||
[SerializeField]
|
||||
private float _height;
|
||||
[SerializeField]
|
||||
private HeightFilterOptions _type;
|
||||
|
||||
public override bool Try(VectorFeatureUnity feature)
|
||||
{
|
||||
var hg = System.Convert.ToSingle(feature.Properties[Key]);
|
||||
if (_type == HeightFilterOptions.Above && hg > _height)
|
||||
return true;
|
||||
if (_type == HeightFilterOptions.Below && hg < _height)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ae03e6a279b82f94a81883a2698db813
|
||||
timeCreated: 1485210126
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,580 @@
|
||||
namespace Mapbox.Unity.MeshGeneration.Filters
|
||||
{
|
||||
using UnityEngine;
|
||||
using Mapbox.Unity.MeshGeneration.Data;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using Mapbox.Unity.Map;
|
||||
|
||||
public class TypeFilter : FilterBase
|
||||
{
|
||||
public override string Key { get { return "type"; } }
|
||||
[SerializeField]
|
||||
private string[] _types;
|
||||
[SerializeField]
|
||||
private TypeFilterType _behaviour;
|
||||
|
||||
public override bool Try(VectorFeatureUnity feature)
|
||||
{
|
||||
var check = false;
|
||||
for (int i = 0; i < _types.Length; i++)
|
||||
{
|
||||
if (_types[i].ToLowerInvariant() == feature.Properties["type"].ToString().ToLowerInvariant())
|
||||
{
|
||||
check = true;
|
||||
}
|
||||
}
|
||||
return _behaviour == TypeFilterType.Include ? check : !check;
|
||||
}
|
||||
|
||||
public enum TypeFilterType
|
||||
{
|
||||
Include,
|
||||
Exclude
|
||||
}
|
||||
}
|
||||
|
||||
public enum LayerFilterOperationType
|
||||
{
|
||||
Contains,
|
||||
IsEqual,
|
||||
IsGreater,
|
||||
IsLess,
|
||||
IsInRange,
|
||||
}
|
||||
|
||||
public enum LayerFilterCombinerOperationType
|
||||
{
|
||||
Any,
|
||||
All,
|
||||
None,
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class LayerFilterCombiner : ILayerFeatureFilterComparer
|
||||
{
|
||||
public List<ILayerFeatureFilterComparer> Filters;
|
||||
|
||||
public LayerFilterCombinerOperationType Type;
|
||||
|
||||
public bool Try(VectorFeatureUnity feature)
|
||||
{
|
||||
switch (Type)
|
||||
{
|
||||
case LayerFilterCombinerOperationType.Any:
|
||||
return Filters.Any(m => m.Try(feature));
|
||||
case LayerFilterCombinerOperationType.All:
|
||||
return Filters.All(m => m.Try(feature));
|
||||
case LayerFilterCombinerOperationType.None:
|
||||
return !Filters.Any(m => m.Try(feature));
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class LayerFilterComparer : ILayerFeatureFilterComparer
|
||||
{
|
||||
public virtual bool Try(VectorFeatureUnity feature)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public static ILayerFeatureFilterComparer AnyOf(params ILayerFeatureFilterComparer[] filters)
|
||||
{
|
||||
return new LayerFilterCombiner
|
||||
{
|
||||
Type = LayerFilterCombinerOperationType.Any,
|
||||
Filters = filters.ToList(),
|
||||
};
|
||||
}
|
||||
|
||||
public static ILayerFeatureFilterComparer AllOf(params ILayerFeatureFilterComparer[] filters)
|
||||
{
|
||||
return new LayerFilterCombiner
|
||||
{
|
||||
Type = LayerFilterCombinerOperationType.All,
|
||||
Filters = filters.ToList(),
|
||||
};
|
||||
}
|
||||
|
||||
public static ILayerFeatureFilterComparer NoneOf(params ILayerFeatureFilterComparer[] filters)
|
||||
{
|
||||
return new LayerFilterCombiner
|
||||
{
|
||||
Type = LayerFilterCombinerOperationType.None,
|
||||
Filters = filters.ToList(),
|
||||
};
|
||||
}
|
||||
public static ILayerFeatureFilterComparer HasProperty(string property)
|
||||
{
|
||||
return new LayerHasPropertyFilterComparer
|
||||
{
|
||||
Key = property
|
||||
};
|
||||
}
|
||||
|
||||
public static ILayerFeatureFilterComparer HasPropertyInRange(string property, double min, double max)
|
||||
{
|
||||
return new LayerPropertyInRangeFilterComparer
|
||||
{
|
||||
Key = property,
|
||||
Min = min,
|
||||
Max = max
|
||||
};
|
||||
}
|
||||
|
||||
public static ILayerFeatureFilterComparer HasPropertyGreaterThan(string property, double min)
|
||||
{
|
||||
return new LayerPropertyIsGreaterFilterComparer
|
||||
{
|
||||
Key = property,
|
||||
Min = min,
|
||||
};
|
||||
}
|
||||
|
||||
public static ILayerFeatureFilterComparer HasPropertyLessThan(string property, double min)
|
||||
{
|
||||
return new LayerPropertyIsLessFilterComparer
|
||||
{
|
||||
Key = property,
|
||||
Min = min,
|
||||
};
|
||||
}
|
||||
|
||||
public static ILayerFeatureFilterComparer HasPropertyIsEqual(string property, double min)
|
||||
{
|
||||
return new LayerPropertyIsEqualFilterComparer
|
||||
{
|
||||
Key = property,
|
||||
Min = min,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
public static ILayerFeatureFilterComparer PropertyContainsValue(string property, params object[] values)
|
||||
{
|
||||
return new LayerPropertyContainsFilterComparer
|
||||
{
|
||||
Key = property,
|
||||
ValueSet = values.ToList()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class LayerHasPropertyFilterComparer : ILayerFeatureFilterComparer
|
||||
{
|
||||
public string Key;
|
||||
|
||||
public bool Try(VectorFeatureUnity feature)
|
||||
{
|
||||
object property;
|
||||
if (feature.Properties.TryGetValue(Key, out property))
|
||||
{
|
||||
return PropertyComparer(property);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected virtual bool PropertyComparer(object property)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class LayerPropertyInRangeFilterComparer : LayerHasPropertyFilterComparer
|
||||
{
|
||||
public double Min;
|
||||
public double Max;
|
||||
|
||||
protected override bool PropertyComparer(object property)
|
||||
{
|
||||
if (property == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
var propertyValue = Convert.ToDouble(property);
|
||||
if (propertyValue < Min)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (propertyValue >= Max)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class LayerPropertyIsGreaterFilterComparer : LayerHasPropertyFilterComparer
|
||||
{
|
||||
public double Min;
|
||||
|
||||
protected override bool PropertyComparer(object property)
|
||||
{
|
||||
var propertyValue = Convert.ToDouble(property);
|
||||
if (property == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (propertyValue > Min)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class LayerPropertyIsLessFilterComparer : LayerHasPropertyFilterComparer
|
||||
{
|
||||
public double Min;
|
||||
|
||||
protected override bool PropertyComparer(object property)
|
||||
{
|
||||
|
||||
if (property == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
var propertyValue = Convert.ToDouble(property);
|
||||
|
||||
if (propertyValue < Min)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class LayerPropertyIsEqualFilterComparer : LayerHasPropertyFilterComparer
|
||||
{
|
||||
public double Min;
|
||||
|
||||
protected override bool PropertyComparer(object property)
|
||||
{
|
||||
if (property == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var propertyValue = Convert.ToDouble(property);
|
||||
if (Math.Abs(propertyValue - Min) < Mapbox.Utils.Constants.EpsilonFloatingPoint)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class LayerPropertyContainsFilterComparer : LayerHasPropertyFilterComparer
|
||||
{
|
||||
public List<object> ValueSet;
|
||||
|
||||
protected override bool PropertyComparer(object property)
|
||||
{
|
||||
foreach (var value in ValueSet)
|
||||
{
|
||||
if (property.ToString().ToLower().Contains(value.ToString()))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class LayerFilter : MapboxDataProperty, ILayerFilter
|
||||
{
|
||||
[Tooltip("Name of the property to use as key. This property is case sensitive.")]
|
||||
public string Key;
|
||||
[SerializeField]
|
||||
[Tooltip("Description of the property defined as key.")]
|
||||
private string KeyDescription;
|
||||
[Tooltip("Value to match using the operator. ")]
|
||||
public string PropertyValue = string.Empty;
|
||||
[Tooltip("Value to match using the operator. ")]
|
||||
public float Min, Max;
|
||||
|
||||
[Tooltip("Filter operator to apply. ")]
|
||||
public LayerFilterOperationType filterOperator;
|
||||
private char[] _delimiters = new char[] { ',' };
|
||||
|
||||
public LayerFilter(LayerFilterOperationType filterOperation = LayerFilterOperationType.Contains)
|
||||
{
|
||||
filterOperator = filterOperation;
|
||||
}
|
||||
|
||||
public ILayerFeatureFilterComparer GetFilterComparer()
|
||||
{
|
||||
if (_delimiters == null)
|
||||
{
|
||||
_delimiters = new char[] { ',' };
|
||||
}
|
||||
ILayerFeatureFilterComparer filterComparer = new LayerFilterComparer();
|
||||
|
||||
switch (filterOperator)
|
||||
{
|
||||
case LayerFilterOperationType.IsEqual:
|
||||
filterComparer = LayerFilterComparer.HasPropertyIsEqual(Key, Min);
|
||||
break;
|
||||
case LayerFilterOperationType.IsGreater:
|
||||
filterComparer = LayerFilterComparer.HasPropertyGreaterThan(Key, Min);
|
||||
break;
|
||||
case LayerFilterOperationType.IsLess:
|
||||
filterComparer = LayerFilterComparer.HasPropertyLessThan(Key, Min);
|
||||
break;
|
||||
case LayerFilterOperationType.Contains:
|
||||
var matchList = PropertyValue.ToLower()
|
||||
.Split(_delimiters, StringSplitOptions.RemoveEmptyEntries)
|
||||
.Select(p => p.Trim())
|
||||
.Where(p => !string.IsNullOrEmpty(p))
|
||||
.ToArray();
|
||||
filterComparer = LayerFilterComparer.PropertyContainsValue(Key, matchList);
|
||||
break;
|
||||
case LayerFilterOperationType.IsInRange:
|
||||
filterComparer = LayerFilterComparer.HasPropertyInRange(Key, Min, Max);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return filterComparer;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the string contains.
|
||||
/// </summary>
|
||||
/// <param name="key">Key.</param>
|
||||
/// <param name="property">Property.</param>
|
||||
public virtual void SetStringContains(string key, string property)
|
||||
{
|
||||
filterOperator = LayerFilterOperationType.Contains;
|
||||
Key = key;
|
||||
PropertyValue = property;
|
||||
HasChanged = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the number is equal.
|
||||
/// </summary>
|
||||
/// <param name="key">Key.</param>
|
||||
/// <param name="value">Value.</param>
|
||||
public virtual void SetNumberIsEqual(string key, float value)
|
||||
{
|
||||
filterOperator = LayerFilterOperationType.IsEqual;
|
||||
Key = key;
|
||||
Min = value;
|
||||
HasChanged = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the number is less than.
|
||||
/// </summary>
|
||||
/// <param name="key">Key.</param>
|
||||
/// <param name="value">Value.</param>
|
||||
public virtual void SetNumberIsLessThan(string key, float value)
|
||||
{
|
||||
filterOperator = LayerFilterOperationType.IsLess;
|
||||
Key = key;
|
||||
Min = value;
|
||||
HasChanged = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the number is greater than.
|
||||
/// </summary>
|
||||
/// <param name="key">Key.</param>
|
||||
/// <param name="value">Value.</param>
|
||||
public virtual void SetNumberIsGreaterThan(string key, float value)
|
||||
{
|
||||
filterOperator = LayerFilterOperationType.IsGreater;
|
||||
Key = key;
|
||||
Min = value;
|
||||
HasChanged = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the number is in range.
|
||||
/// </summary>
|
||||
/// <param name="key">Key.</param>
|
||||
/// <param name="min">Minimum.</param>
|
||||
/// <param name="max">Max.</param>
|
||||
public virtual void SetNumberIsInRange(string key, float min, float max)
|
||||
{
|
||||
filterOperator = LayerFilterOperationType.IsInRange;
|
||||
Key = key;
|
||||
Min = min;
|
||||
Max = max;
|
||||
HasChanged = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the key.
|
||||
/// </summary>
|
||||
/// <returns>The key.</returns>
|
||||
public virtual string GetKey
|
||||
{
|
||||
get
|
||||
{
|
||||
return Key;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type of the filter operation.
|
||||
/// </summary>
|
||||
/// <returns>The filter operation type.</returns>
|
||||
public virtual LayerFilterOperationType GetFilterOperationType
|
||||
{
|
||||
get
|
||||
{
|
||||
return filterOperator;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the property value.
|
||||
/// </summary>
|
||||
/// <returns>The property value.</returns>
|
||||
public virtual string GetPropertyValue
|
||||
{
|
||||
get
|
||||
{
|
||||
return PropertyValue;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the minimum value.
|
||||
/// </summary>
|
||||
/// <returns>The minimum value.</returns>
|
||||
public virtual float GetNumberValue
|
||||
{
|
||||
get
|
||||
{
|
||||
return Min;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the minimum value.
|
||||
/// </summary>
|
||||
/// <returns>The minimum value.</returns>
|
||||
public virtual float GetMinValue
|
||||
{
|
||||
get
|
||||
{
|
||||
return Min;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the max value.
|
||||
/// </summary>
|
||||
/// <returns>The max value.</returns>
|
||||
public virtual float GetMaxValue
|
||||
{
|
||||
get
|
||||
{
|
||||
return Max;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if filter key contains a given string.
|
||||
/// </summary>
|
||||
/// <returns><c>true</c>, if key contains was filtered, <c>false</c> otherwise.</returns>
|
||||
/// <param name="key">Key.</param>
|
||||
public virtual bool FilterKeyContains(string key)
|
||||
{
|
||||
return Key.Contains(key);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if filter key matches a given string exactly.
|
||||
/// </summary>
|
||||
/// <returns><c>true</c>, if key matches exact was filtered, <c>false</c> otherwise.</returns>
|
||||
/// <param name="key">Key.</param>
|
||||
public virtual bool FilterKeyMatchesExact(string key)
|
||||
{
|
||||
return Key == key;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if filter uses a given operation type.
|
||||
/// </summary>
|
||||
/// <returns><c>true</c>, if uses operation type was filtered, <c>false</c> otherwise.</returns>
|
||||
/// <param name="layerFilterOperationType">Layer filter operation type.</param>
|
||||
public virtual bool FilterUsesOperationType(LayerFilterOperationType layerFilterOperationType)
|
||||
{
|
||||
return filterOperator == layerFilterOperationType;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if filter property contains a given string.
|
||||
/// </summary>
|
||||
/// <returns><c>true</c>, if property contains was filtered, <c>false</c> otherwise.</returns>
|
||||
/// <param name="property">Property.</param>
|
||||
public virtual bool FilterPropertyContains(string property)
|
||||
{
|
||||
return PropertyValue.Contains(property);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if filter property matches a given string exactly.
|
||||
/// </summary>
|
||||
/// <returns><c>true</c>, if property matches exact was filtered, <c>false</c> otherwise.</returns>
|
||||
/// <param name="property">Property.</param>
|
||||
public virtual bool FilterPropertyMatchesExact(string property)
|
||||
{
|
||||
return PropertyValue == property;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if filter number value is equal to a given number.
|
||||
/// </summary>
|
||||
/// <returns><c>true</c>, if number value equals was filtered, <c>false</c> otherwise.</returns>
|
||||
/// <param name="value">Value.</param>
|
||||
public virtual bool FilterNumberValueEquals(float value)
|
||||
{
|
||||
return Mathf.Approximately(Min, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if filter number value is greater than a given number.
|
||||
/// </summary>
|
||||
/// <returns><c>true</c>, if number value is greater than was filtered, <c>false</c> otherwise.</returns>
|
||||
/// <param name="value">Value.</param>
|
||||
public virtual bool FilterNumberValueIsGreaterThan(float value)
|
||||
{
|
||||
return Min > value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if filter number value is less than a given number.
|
||||
/// </summary>
|
||||
/// <returns><c>true</c>, if number value is less than was filtered, <c>false</c> otherwise.</returns>
|
||||
/// <param name="value">Value.</param>
|
||||
public virtual bool FilterNumberValueIsLessThan(float value)
|
||||
{
|
||||
return Min < value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if filter range values contain a given number.
|
||||
/// </summary>
|
||||
/// <returns><c>true</c>, if is in range value contains was filtered, <c>false</c> otherwise.</returns>
|
||||
/// <param name="value">Value.</param>
|
||||
public virtual bool FilterIsInRangeValueContains(float value)
|
||||
{
|
||||
return Min < value && value < Max;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cb49207f63da8544d9f0a3f790016544
|
||||
timeCreated: 1485210126
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e4ff53839a2a7b04bb09cc89f514267d
|
||||
folderAsset: yes
|
||||
timeCreated: 1483047390
|
||||
licenseType: Pro
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace Mapbox.Unity.MeshGeneration.Interfaces
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
|
||||
public interface IFeaturePropertySettable
|
||||
{
|
||||
void Set(Dictionary<string, object> props);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e4a4d2f716d05a5438b51569513fdc77
|
||||
timeCreated: 1483231201
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,64 @@
|
||||
namespace Mapbox.Unity.MeshGeneration.Interfaces
|
||||
{
|
||||
using Mapbox.VectorTile;
|
||||
using UnityEngine;
|
||||
using Mapbox.Unity.MeshGeneration.Data;
|
||||
using System;
|
||||
using Mapbox.Unity.Map;
|
||||
|
||||
/// <summary>
|
||||
/// Layer visualizers contains sytling logic and processes features
|
||||
/// </summary>
|
||||
public abstract class LayerVisualizerBase : ScriptableObject
|
||||
{
|
||||
public abstract bool Active { get; }
|
||||
public abstract string Key { get; set; }
|
||||
public abstract VectorSubLayerProperties SubLayerProperties { get; set; }
|
||||
//public event Action FeaturePreProcessEvent;
|
||||
//public event Action FeaturePostProcessEvent;
|
||||
public abstract void Create(VectorTileLayer layer, UnityTile tile, Action<UnityTile, LayerVisualizerBase> callback = null);
|
||||
|
||||
public event System.EventHandler LayerVisualizerHasChanged;
|
||||
|
||||
public virtual void Initialize()
|
||||
{
|
||||
|
||||
}
|
||||
public virtual void InitializeStack()
|
||||
{
|
||||
|
||||
}
|
||||
public virtual void SetProperties(VectorSubLayerProperties properties)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public virtual void ClearCaches()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void UnregisterTile(UnityTile tile)
|
||||
{
|
||||
OnUnregisterTile(tile);
|
||||
}
|
||||
|
||||
public virtual void OnUnregisterTile(UnityTile tile)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public virtual void UnbindSubLayerEvents()
|
||||
{
|
||||
|
||||
}
|
||||
protected virtual void OnUpdateLayerVisualizer(System.EventArgs e)
|
||||
{
|
||||
System.EventHandler handler = LayerVisualizerHasChanged;
|
||||
if (handler != null)
|
||||
{
|
||||
handler(this, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 79d5cc1929eb9984089bd16e403893b9
|
||||
timeCreated: 1483047390
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 563368f5fe3c51549bbbdd1ce2d6dd9a
|
||||
folderAsset: yes
|
||||
timeCreated: 1478551786
|
||||
licenseType: Free
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,388 @@
|
||||
using System.Linq;
|
||||
|
||||
namespace Mapbox.Unity.MeshGeneration.Interfaces
|
||||
{
|
||||
using Mapbox.Unity.Map;
|
||||
using Mapbox.Unity.MeshGeneration.Data;
|
||||
using Mapbox.Unity.MeshGeneration.Filters;
|
||||
using Mapbox.Unity.MeshGeneration.Modifiers;
|
||||
using Mapbox.Unity.Utilities;
|
||||
using Mapbox.VectorTile;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
|
||||
public class LocationPrefabsLayerVisualizer : VectorLayerVisualizer
|
||||
{
|
||||
private int maxDensity = 30; //This value is same as the density's max range value in PrefabItemOptions
|
||||
private PrefabModifier _prefabModifier;
|
||||
|
||||
public override bool Active
|
||||
{
|
||||
get
|
||||
{
|
||||
return SubLayerProperties.coreOptions.isActive;
|
||||
}
|
||||
}
|
||||
|
||||
public override void SetProperties(VectorSubLayerProperties properties)
|
||||
{
|
||||
var item = properties as PrefabItemOptions;
|
||||
SubLayerProperties = item;
|
||||
//Active = item.isActive;
|
||||
_performanceOptions = item.performanceOptions;
|
||||
|
||||
item.filterOptions.filters.Clear();
|
||||
|
||||
if (item.spawnPrefabOptions.prefab == null)
|
||||
{
|
||||
//item.spawnPrefabOptions.prefab = GameObject.CreatePrimitive(PrimitiveType.Cube);
|
||||
item.spawnPrefabOptions.prefab = Resources.Load<GameObject>("MapboxPin");
|
||||
Debug.LogError("No prefab found. Please assign a prefab to spawn it on the map");
|
||||
}
|
||||
|
||||
//These are fixed properties
|
||||
item.coreOptions.geometryType = item.primitiveType;
|
||||
item.extrusionOptions = new GeometryExtrusionOptions
|
||||
{
|
||||
extrusionType = item.extrusionType
|
||||
};
|
||||
|
||||
item.coreOptions.combineMeshes = item.combineMeshes;
|
||||
item.moveFeaturePositionTo = item._movePrefabFeaturePositionTo;
|
||||
|
||||
string layerName = "";
|
||||
if (item.layerNameFromFindByTypeDictionary.TryGetValue(item.findByType, out layerName))
|
||||
{
|
||||
item.coreOptions.layerName = layerName;
|
||||
base.Key = layerName;
|
||||
}
|
||||
|
||||
//These properties are dependent on user choices
|
||||
if (item.findByType != LocationPrefabFindBy.AddressOrLatLon)
|
||||
{
|
||||
if (item.findByType == LocationPrefabFindBy.MapboxCategory)
|
||||
{
|
||||
SetCategoryFilterOptions(item);
|
||||
}
|
||||
if (item.findByType == LocationPrefabFindBy.POIName)
|
||||
{
|
||||
SetNameFilters(item);
|
||||
}
|
||||
|
||||
SetDensityFilters(item);
|
||||
}
|
||||
|
||||
switch (item.coreOptions.geometryType)
|
||||
{
|
||||
case VectorPrimitiveType.Point:
|
||||
#if ENABLE_WINMD_SUPPORT
|
||||
if (typeof(PrefabItemOptions).GetTypeInfo().IsAssignableFrom(item.GetType().GetTypeInfo())) //to check that the instance is of type PrefabItemOptions
|
||||
#else
|
||||
if (typeof(PrefabItemOptions).IsAssignableFrom(item.GetType())) //to check that the instance is of type PrefabItemOptions
|
||||
#endif
|
||||
{
|
||||
PrefabItemOptions itemProperties = (PrefabItemOptions)item;
|
||||
|
||||
if (_defaultStack == null)
|
||||
{
|
||||
_defaultStack = ScriptableObject.CreateInstance<ModifierStack>();
|
||||
}
|
||||
|
||||
(_defaultStack as ModifierStack).moveFeaturePositionTo = item.moveFeaturePositionTo;
|
||||
if (itemProperties.snapToTerrain == true)
|
||||
{
|
||||
AddOrCreateMeshModifier<SnapTerrainModifier>();
|
||||
}
|
||||
|
||||
if (_defaultStack.GoModifiers == null)
|
||||
{
|
||||
_defaultStack.GoModifiers = new List<GameObjectModifier>();
|
||||
}
|
||||
|
||||
if (item.findByType == LocationPrefabFindBy.MapboxCategory)
|
||||
{
|
||||
if (_prefabModifier != null)
|
||||
{
|
||||
_prefabModifier.ClearCaches();
|
||||
}
|
||||
_defaultStack.GoModifiers.Clear();
|
||||
}
|
||||
|
||||
if ((item.findByType == LocationPrefabFindBy.MapboxCategory && item.categories == LocationPrefabCategories.None))
|
||||
{
|
||||
|
||||
itemProperties.spawnPrefabOptions.PropertyHasChanged += UpdatePois;
|
||||
}
|
||||
else
|
||||
{
|
||||
_prefabModifier = AddOrCreateGameObjectModifier<PrefabModifier>();
|
||||
_prefabModifier.SetProperties(itemProperties.spawnPrefabOptions);
|
||||
_prefabModifier.ModifierHasChanged += UpdatePois;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
item.coreOptions.PropertyHasChanged += UpdatePois;
|
||||
(SubLayerProperties as PrefabItemOptions).PropertyHasChanged += UpdatePois;
|
||||
}
|
||||
|
||||
private void UpdatePois(object sender, System.EventArgs eventArgs)
|
||||
{
|
||||
|
||||
VectorLayerUpdateArgs layerUpdateArgs = eventArgs as VectorLayerUpdateArgs;
|
||||
|
||||
layerUpdateArgs.visualizer = this;
|
||||
layerUpdateArgs.effectsVectorLayer = true;
|
||||
|
||||
if (layerUpdateArgs.modifier != null)
|
||||
{
|
||||
layerUpdateArgs.property.PropertyHasChanged -= layerUpdateArgs.modifier.UpdateModifier;
|
||||
layerUpdateArgs.modifier.ModifierHasChanged -= UpdatePois;
|
||||
}
|
||||
else if (layerUpdateArgs.property != null)
|
||||
{
|
||||
layerUpdateArgs.property.PropertyHasChanged -= UpdatePois;
|
||||
}
|
||||
|
||||
foreach (var modifier in _defaultStack.MeshModifiers)
|
||||
{
|
||||
modifier.UnbindProperties();
|
||||
modifier.ModifierHasChanged -= UpdatePois;
|
||||
}
|
||||
foreach (var modifier in _defaultStack.GoModifiers)
|
||||
{
|
||||
modifier.UnbindProperties();
|
||||
modifier.ModifierHasChanged -= UpdatePois;
|
||||
}
|
||||
|
||||
SubLayerProperties.coreOptions.PropertyHasChanged -= UpdatePois;
|
||||
(SubLayerProperties as PrefabItemOptions).PropertyHasChanged -= UpdatePois;
|
||||
|
||||
OnUpdateLayerVisualizer(layerUpdateArgs);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the category filter options.
|
||||
/// </summary>
|
||||
/// <param name="item">Item.</param>
|
||||
private void SetCategoryFilterOptions(PrefabItemOptions item)
|
||||
{
|
||||
string propertyName = "";
|
||||
item.categoryPropertyFromFindByTypeDictionary.TryGetValue(item.findByType, out propertyName);
|
||||
|
||||
string concatenatedString = "";
|
||||
if (item.findByType == LocationPrefabFindBy.MapboxCategory)
|
||||
{
|
||||
|
||||
List<LocationPrefabCategories> categoriesList = GetSelectedCategoriesList(item.categories);
|
||||
if (categoriesList == null || categoriesList.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
List<string> stringsList = new List<string>();
|
||||
foreach (LocationPrefabCategories category in categoriesList)
|
||||
{
|
||||
stringsList = LocationPrefabCategoryOptions.GetMakiListFromCategory(category);
|
||||
if (string.IsNullOrEmpty(concatenatedString))
|
||||
{
|
||||
concatenatedString = string.Join(",", stringsList.ToArray());
|
||||
}
|
||||
else
|
||||
{
|
||||
concatenatedString += "," + string.Join(",", stringsList.ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
LayerFilter filter = new LayerFilter(LayerFilterOperationType.Contains)
|
||||
{
|
||||
Key = propertyName,
|
||||
PropertyValue = concatenatedString
|
||||
};
|
||||
AddFilterToItem(item, filter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the density filters.
|
||||
/// </summary>
|
||||
/// <param name="item">Item.</param>
|
||||
private void SetDensityFilters(PrefabItemOptions item)
|
||||
{
|
||||
if (item.density >= maxDensity) // decided that the max value for density
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string propertyName = "";
|
||||
item.densityPropertyFromFindByTypeDictionary.TryGetValue(item.findByType, out propertyName);
|
||||
|
||||
if (item.findByType == LocationPrefabFindBy.MapboxCategory || item.findByType == LocationPrefabFindBy.POIName)
|
||||
{
|
||||
LayerFilter filter = new LayerFilter(LayerFilterOperationType.IsLess)
|
||||
{
|
||||
Key = propertyName,
|
||||
Min = item.density
|
||||
};
|
||||
AddFilterToItem(item, filter);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the name filters.
|
||||
/// </summary>
|
||||
/// <param name="item">Item.</param>
|
||||
private void SetNameFilters(PrefabItemOptions item)
|
||||
{
|
||||
if (string.IsNullOrEmpty(item.nameString))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string propertyName = "";
|
||||
item.namePropertyFromFindByTypeDictionary.TryGetValue(item.findByType, out propertyName);
|
||||
|
||||
if (item.findByType == LocationPrefabFindBy.POIName)
|
||||
{
|
||||
LayerFilter filter = new LayerFilter(LayerFilterOperationType.Contains)
|
||||
{
|
||||
Key = propertyName,
|
||||
PropertyValue = item.nameString
|
||||
};
|
||||
AddFilterToItem(item, filter);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Merges the filters with item filters.
|
||||
/// </summary>
|
||||
/// <param name="item">Item.</param>
|
||||
private void AddFilterToItem(PrefabItemOptions item, LayerFilter filter)
|
||||
{
|
||||
if (item.filterOptions == null)
|
||||
{
|
||||
item.filterOptions = new VectorFilterOptions();
|
||||
}
|
||||
|
||||
item.filterOptions.filters.Add(filter);
|
||||
item.filterOptions.combinerType = item._combinerType;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of categories selected through the dropdown
|
||||
/// </summary>
|
||||
/// <returns>The selected categories list.</returns>
|
||||
/// <param name="selectedCategories">Cat.</param>
|
||||
private List<LocationPrefabCategories> GetSelectedCategoriesList(LocationPrefabCategories selectedCategories)
|
||||
{
|
||||
List<LocationPrefabCategories> containingCategories = new List<LocationPrefabCategories>();
|
||||
|
||||
Array eligibleValues = Enum.GetValues(typeof(LocationPrefabCategories));
|
||||
if (selectedCategories == LocationPrefabCategories.None)
|
||||
{
|
||||
return containingCategories;
|
||||
}
|
||||
|
||||
//For any other categories other than None and Any
|
||||
foreach (object value in eligibleValues)
|
||||
{
|
||||
LocationPrefabCategories category = (LocationPrefabCategories)value;
|
||||
|
||||
if (category == LocationPrefabCategories.AnyCategory || category == LocationPrefabCategories.None)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((category & selectedCategories) != 0) //to check if category is contained in cat
|
||||
{
|
||||
containingCategories.Add(category);
|
||||
}
|
||||
}
|
||||
|
||||
return containingCategories;
|
||||
}
|
||||
|
||||
public override void Create(VectorTileLayer layer, UnityTile tile, Action<UnityTile, LayerVisualizerBase> callback)
|
||||
{
|
||||
//for layers using specific locations, ignore VectorTileLayer and
|
||||
//pass coordinates to the modifierstack using BuildFeatureFromLatLon.
|
||||
if ((SubLayerProperties as PrefabItemOptions).findByType == LocationPrefabFindBy.AddressOrLatLon)
|
||||
{
|
||||
BuildFeatureFromLatLon(layer, tile);
|
||||
if (callback != null)
|
||||
{
|
||||
callback(tile, this);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var item = (SubLayerProperties as PrefabItemOptions);
|
||||
bool isCategoryNone = (item.findByType == LocationPrefabFindBy.MapboxCategory && item.categories == LocationPrefabCategories.None);
|
||||
if (!isCategoryNone)
|
||||
{
|
||||
base.Create(layer, tile, callback);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a vector feature from lat lon and builds that feature using the modifier stack.
|
||||
/// </summary>
|
||||
/// <param name="layer">Layer.</param>
|
||||
/// <param name="tile">Tile.</param>
|
||||
private void BuildFeatureFromLatLon(VectorTileLayer layer, UnityTile tile)
|
||||
{
|
||||
if (tile.TileState != Enums.TilePropertyState.Unregistered)
|
||||
{
|
||||
string[] coordinates = (SubLayerProperties as PrefabItemOptions).coordinates;
|
||||
|
||||
for (int i = 0; i < coordinates.Length; i++)
|
||||
{
|
||||
if (string.IsNullOrEmpty(coordinates[i]))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//check if the coordinate is in the tile
|
||||
Utils.Vector2d coordinate = Conversions.StringToLatLon(coordinates[i]);
|
||||
Mapbox.Map.UnwrappedTileId coordinateTileId = Conversions.LatitudeLongitudeToTileId(
|
||||
coordinate.x, coordinate.y, tile.InitialZoom);
|
||||
|
||||
if (coordinateTileId.Canonical.Equals(tile.CanonicalTileId))
|
||||
{
|
||||
if (String.IsNullOrEmpty(coordinates[i]))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//create new vector feature
|
||||
VectorFeatureUnity feature = new VectorFeatureUnity();
|
||||
feature.Properties = new Dictionary<string, object>();
|
||||
feature.Points = new List<List<Vector3>>();
|
||||
|
||||
//create submesh for feature
|
||||
List<Vector3> latLonPoint = new List<Vector3>();
|
||||
//add point to submesh, and submesh to feature
|
||||
latLonPoint.Add(Conversions.LatitudeLongitudeToUnityTilePosition(coordinate, tile.CurrentZoom, tile.TileScale, layer.Extent).ToVector3xz());
|
||||
feature.Points.Add(latLonPoint);
|
||||
|
||||
//pass valid feature.Data to modifiers
|
||||
//this data has no relation to the features being drawn
|
||||
feature.Data = layer.GetFeature(0);
|
||||
|
||||
//pass the feature to the mod stack
|
||||
base.Build(feature, tile, tile.gameObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b250e20966143455d807eddf832c7362
|
||||
timeCreated: 1524037447
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,766 @@
|
||||
using Mapbox.VectorTile.Geometry;
|
||||
|
||||
namespace Mapbox.Unity.MeshGeneration.Interfaces
|
||||
{
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Mapbox.Unity.MeshGeneration.Data;
|
||||
using Mapbox.Unity.MeshGeneration.Modifiers;
|
||||
using Mapbox.VectorTile;
|
||||
using UnityEngine;
|
||||
using Mapbox.Unity.Map;
|
||||
using Mapbox.Unity.Utilities;
|
||||
using Mapbox.Unity.MeshGeneration.Filters;
|
||||
using Mapbox.Map;
|
||||
|
||||
public class VectorLayerVisualizerProperties
|
||||
{
|
||||
public FeatureProcessingStage featureProcessingStage;
|
||||
public bool buildingsWithUniqueIds = false;
|
||||
public VectorTileLayer vectorTileLayer;
|
||||
public ILayerFeatureFilterComparer[] layerFeatureFilters;
|
||||
public ILayerFeatureFilterComparer layerFeatureFilterCombiner;
|
||||
}
|
||||
|
||||
|
||||
public class VectorLayerVisualizer : LayerVisualizerBase
|
||||
{
|
||||
VectorSubLayerProperties _layerProperties;
|
||||
public override VectorSubLayerProperties SubLayerProperties
|
||||
{
|
||||
get
|
||||
{
|
||||
return _layerProperties;
|
||||
}
|
||||
set
|
||||
{
|
||||
_layerProperties = value;
|
||||
}
|
||||
}
|
||||
|
||||
public ModifierStackBase DefaultModifierStack
|
||||
{
|
||||
get
|
||||
{
|
||||
return _defaultStack;
|
||||
}
|
||||
set
|
||||
{
|
||||
_defaultStack = value;
|
||||
}
|
||||
}
|
||||
|
||||
//public event System.EventHandler VectorHasChanged;
|
||||
|
||||
protected LayerPerformanceOptions _performanceOptions;
|
||||
protected Dictionary<UnityTile, List<int>> _activeCoroutines;
|
||||
int _entityInCurrentCoroutine = 0;
|
||||
|
||||
protected ModifierStackBase _defaultStack;
|
||||
private HashSet<ulong> _activeIds;
|
||||
private Dictionary<UnityTile, List<ulong>> _idPool; //necessary to keep _activeIds list up to date when unloading tiles
|
||||
private string _key;
|
||||
|
||||
public override string Key
|
||||
{
|
||||
get { return _layerProperties.coreOptions.layerName; }
|
||||
set { _layerProperties.coreOptions.layerName = value; }
|
||||
}
|
||||
|
||||
public T FindMeshModifier<T>() where T : MeshModifier
|
||||
{
|
||||
MeshModifier mod = _defaultStack.MeshModifiers.FirstOrDefault(x => x.GetType() == typeof(T));
|
||||
return (T)mod;
|
||||
}
|
||||
|
||||
public T FindGameObjectModifier<T>() where T : GameObjectModifier
|
||||
{
|
||||
GameObjectModifier mod = _defaultStack.GoModifiers.FirstOrDefault(x => x.GetType() == typeof(T));
|
||||
return (T)mod;
|
||||
}
|
||||
|
||||
public T AddOrCreateMeshModifier<T>() where T : MeshModifier
|
||||
{
|
||||
MeshModifier mod = _defaultStack.MeshModifiers.FirstOrDefault(x => x.GetType() == typeof(T));
|
||||
if (mod == null)
|
||||
{
|
||||
mod = (MeshModifier)CreateInstance(typeof(T));
|
||||
_defaultStack.MeshModifiers.Add(mod);
|
||||
}
|
||||
return (T)mod;
|
||||
}
|
||||
|
||||
public T AddOrCreateGameObjectModifier<T>() where T : GameObjectModifier
|
||||
{
|
||||
GameObjectModifier mod = _defaultStack.GoModifiers.FirstOrDefault(x => x.GetType() == typeof(T));
|
||||
if (mod == null)
|
||||
{
|
||||
mod = (GameObjectModifier)CreateInstance(typeof(T));
|
||||
_defaultStack.GoModifiers.Add(mod);
|
||||
}
|
||||
return (T)mod;
|
||||
}
|
||||
|
||||
private void UpdateVector(object sender, System.EventArgs eventArgs)
|
||||
{
|
||||
VectorLayerUpdateArgs layerUpdateArgs = eventArgs as VectorLayerUpdateArgs;
|
||||
|
||||
layerUpdateArgs.visualizer = this;
|
||||
layerUpdateArgs.effectsVectorLayer = true;
|
||||
|
||||
if (layerUpdateArgs.modifier != null)
|
||||
{
|
||||
layerUpdateArgs.property.PropertyHasChanged -= layerUpdateArgs.modifier.UpdateModifier;
|
||||
layerUpdateArgs.modifier.ModifierHasChanged -= UpdateVector;
|
||||
}
|
||||
else if (layerUpdateArgs.property != null)
|
||||
{
|
||||
layerUpdateArgs.property.PropertyHasChanged -= UpdateVector;
|
||||
}
|
||||
UnbindSubLayerEvents();
|
||||
|
||||
OnUpdateLayerVisualizer(layerUpdateArgs);
|
||||
}
|
||||
|
||||
public override void UnbindSubLayerEvents()
|
||||
{
|
||||
foreach (var modifier in _defaultStack.MeshModifiers)
|
||||
{
|
||||
modifier.UnbindProperties();
|
||||
modifier.ModifierHasChanged -= UpdateVector;
|
||||
}
|
||||
foreach (var modifier in _defaultStack.GoModifiers)
|
||||
{
|
||||
modifier.UnbindProperties();
|
||||
modifier.ModifierHasChanged -= UpdateVector;
|
||||
}
|
||||
|
||||
_layerProperties.extrusionOptions.PropertyHasChanged -= UpdateVector;
|
||||
_layerProperties.coreOptions.PropertyHasChanged -= UpdateVector;
|
||||
_layerProperties.filterOptions.PropertyHasChanged -= UpdateVector;
|
||||
_layerProperties.filterOptions.UnRegisterFilters();
|
||||
_layerProperties.materialOptions.PropertyHasChanged -= UpdateVector;
|
||||
|
||||
_layerProperties.PropertyHasChanged -= UpdateVector;
|
||||
}
|
||||
|
||||
public override void SetProperties(VectorSubLayerProperties properties)
|
||||
{
|
||||
if (_layerProperties == null && properties != null)
|
||||
{
|
||||
_layerProperties = properties;
|
||||
if (_performanceOptions == null && properties.performanceOptions != null)
|
||||
{
|
||||
_performanceOptions = properties.performanceOptions;
|
||||
}
|
||||
}
|
||||
|
||||
if (_layerProperties.coreOptions.combineMeshes)
|
||||
{
|
||||
if (_defaultStack == null || !(_defaultStack is MergedModifierStack))
|
||||
{
|
||||
_defaultStack = ScriptableObject.CreateInstance<MergedModifierStack>();
|
||||
}
|
||||
else
|
||||
{
|
||||
// HACK - to clean out the Modifiers.
|
||||
// Will this trigger GC that we could avoid ??
|
||||
_defaultStack.MeshModifiers.Clear();
|
||||
_defaultStack.GoModifiers.Clear();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_defaultStack == null || !(_defaultStack is ModifierStack))
|
||||
{
|
||||
_defaultStack = ScriptableObject.CreateInstance<ModifierStack>();
|
||||
((ModifierStack)_defaultStack).moveFeaturePositionTo = _layerProperties.moveFeaturePositionTo;
|
||||
}
|
||||
else
|
||||
{
|
||||
// HACK - to clean out the Modifiers.
|
||||
// Will this trigger GC that we could avoid ??
|
||||
_defaultStack.MeshModifiers.Clear();
|
||||
_defaultStack.GoModifiers.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
//Add any additional modifiers that were added.
|
||||
if (_defaultStack.MeshModifiers == null)
|
||||
{
|
||||
_defaultStack.MeshModifiers = new List<MeshModifier>();
|
||||
}
|
||||
if (_defaultStack.GoModifiers == null)
|
||||
{
|
||||
_defaultStack.GoModifiers = new List<GameObjectModifier>();
|
||||
}
|
||||
|
||||
// Setup material options.
|
||||
_layerProperties.materialOptions.SetDefaultMaterialOptions();
|
||||
|
||||
switch (_layerProperties.coreOptions.geometryType)
|
||||
{
|
||||
case VectorPrimitiveType.Point:
|
||||
case VectorPrimitiveType.Custom:
|
||||
{
|
||||
// Let the user add anything that they want
|
||||
if (_layerProperties.coreOptions.snapToTerrain == true)
|
||||
{
|
||||
//defaultMeshModifierStack.Add(CreateInstance<SnapTerrainModifier>());
|
||||
AddOrCreateMeshModifier<SnapTerrainModifier>();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case VectorPrimitiveType.Line:
|
||||
{
|
||||
if (_layerProperties.coreOptions.snapToTerrain == true)
|
||||
{
|
||||
AddOrCreateMeshModifier<SnapTerrainModifier>();
|
||||
}
|
||||
|
||||
var lineMeshMod = AddOrCreateMeshModifier<LineMeshModifier>();
|
||||
lineMeshMod.SetProperties(_layerProperties.lineGeometryOptions);
|
||||
lineMeshMod.ModifierHasChanged += UpdateVector;
|
||||
|
||||
if (_layerProperties.extrusionOptions.extrusionType != Map.ExtrusionType.None)
|
||||
{
|
||||
var heightMod = AddOrCreateMeshModifier<HeightModifier>();
|
||||
heightMod.SetProperties(_layerProperties.extrusionOptions);
|
||||
heightMod.ModifierHasChanged += UpdateVector;
|
||||
}
|
||||
else
|
||||
{
|
||||
_layerProperties.extrusionOptions.PropertyHasChanged += UpdateVector;
|
||||
}
|
||||
|
||||
//collider modifier options
|
||||
var lineColliderMod = AddOrCreateGameObjectModifier<ColliderModifier>();
|
||||
lineColliderMod.SetProperties(_layerProperties.colliderOptions);
|
||||
lineColliderMod.ModifierHasChanged += UpdateVector;
|
||||
|
||||
var lineStyleMod = AddOrCreateGameObjectModifier<MaterialModifier>();
|
||||
lineStyleMod.SetProperties(_layerProperties.materialOptions);
|
||||
lineStyleMod.ModifierHasChanged += UpdateVector;
|
||||
|
||||
break;
|
||||
}
|
||||
case VectorPrimitiveType.Polygon:
|
||||
{
|
||||
if (_layerProperties.coreOptions.snapToTerrain == true)
|
||||
{
|
||||
AddOrCreateMeshModifier<SnapTerrainModifier>();
|
||||
}
|
||||
|
||||
AddOrCreateMeshModifier<PolygonMeshModifier>();
|
||||
|
||||
UVModifierOptions uvModOptions = new UVModifierOptions();
|
||||
uvModOptions.texturingType = (_layerProperties.materialOptions.style == StyleTypes.Custom) ? _layerProperties.materialOptions.customStyleOptions.texturingType : _layerProperties.materialOptions.texturingType;
|
||||
uvModOptions.atlasInfo = (_layerProperties.materialOptions.style == StyleTypes.Custom) ? _layerProperties.materialOptions.customStyleOptions.atlasInfo : _layerProperties.materialOptions.atlasInfo;
|
||||
uvModOptions.style = _layerProperties.materialOptions.style;
|
||||
|
||||
var uvMod = AddOrCreateMeshModifier<UvModifier>();
|
||||
uvMod.SetProperties(uvModOptions);
|
||||
|
||||
if (_layerProperties.extrusionOptions.extrusionType != Map.ExtrusionType.None)
|
||||
{
|
||||
//replace materialOptions with styleOptions
|
||||
bool useTextureSideWallModifier =
|
||||
(_layerProperties.materialOptions.style == StyleTypes.Custom) ?
|
||||
(_layerProperties.materialOptions.customStyleOptions.texturingType == UvMapType.Atlas || _layerProperties.materialOptions.customStyleOptions.texturingType == UvMapType.AtlasWithColorPalette)
|
||||
: (_layerProperties.materialOptions.texturingType == UvMapType.Atlas || _layerProperties.materialOptions.texturingType == UvMapType.AtlasWithColorPalette);
|
||||
|
||||
if (useTextureSideWallModifier)
|
||||
{
|
||||
var atlasMod = AddOrCreateMeshModifier<TextureSideWallModifier>();
|
||||
GeometryExtrusionWithAtlasOptions atlasOptions = new GeometryExtrusionWithAtlasOptions(_layerProperties.extrusionOptions, uvModOptions);
|
||||
atlasMod.SetProperties(atlasOptions);
|
||||
_layerProperties.extrusionOptions.PropertyHasChanged += UpdateVector;
|
||||
}
|
||||
else
|
||||
{
|
||||
var heightMod = AddOrCreateMeshModifier<HeightModifier>();
|
||||
heightMod.SetProperties(_layerProperties.extrusionOptions);
|
||||
heightMod.ModifierHasChanged += UpdateVector;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_layerProperties.extrusionOptions.PropertyHasChanged += UpdateVector;
|
||||
}
|
||||
|
||||
//collider modifier options
|
||||
var polyColliderMod = AddOrCreateGameObjectModifier<ColliderModifier>();
|
||||
polyColliderMod.SetProperties(_layerProperties.colliderOptions);
|
||||
polyColliderMod.ModifierHasChanged += UpdateVector;
|
||||
|
||||
var styleMod = AddOrCreateGameObjectModifier<MaterialModifier>();
|
||||
styleMod.SetProperties(_layerProperties.materialOptions);
|
||||
styleMod.ModifierHasChanged += UpdateVector;
|
||||
|
||||
|
||||
bool isCustomStyle = (_layerProperties.materialOptions.style == StyleTypes.Custom);
|
||||
if ((isCustomStyle) ? (_layerProperties.materialOptions.customStyleOptions.texturingType == UvMapType.AtlasWithColorPalette)
|
||||
: (_layerProperties.materialOptions.texturingType == UvMapType.AtlasWithColorPalette))
|
||||
{
|
||||
var colorPaletteMod = AddOrCreateGameObjectModifier<MapboxStylesColorModifier>();
|
||||
colorPaletteMod.m_scriptablePalette = (isCustomStyle) ? _layerProperties.materialOptions.customStyleOptions.colorPalette : _layerProperties.materialOptions.colorPalette;
|
||||
_layerProperties.materialOptions.PropertyHasChanged += UpdateVector;
|
||||
//TODO: Add SetProperties Method to MapboxStylesColorModifier
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
_layerProperties.coreOptions.PropertyHasChanged += UpdateVector;
|
||||
_layerProperties.filterOptions.PropertyHasChanged += UpdateVector;
|
||||
|
||||
_layerProperties.filterOptions.RegisterFilters();
|
||||
if (_layerProperties.MeshModifiers != null)
|
||||
{
|
||||
_defaultStack.MeshModifiers.AddRange(_layerProperties.MeshModifiers);
|
||||
}
|
||||
if (_layerProperties.GoModifiers != null)
|
||||
{
|
||||
_defaultStack.GoModifiers.AddRange(_layerProperties.GoModifiers);
|
||||
}
|
||||
//Adding filters from the types dropdown
|
||||
|
||||
//if ((MapboxSpecialLayerParameters.LayerNameTypeProperty.ContainsKey(properties.coreOptions.layerName)) && !string.IsNullOrEmpty(properties.selectedTypes))
|
||||
//{
|
||||
// LayerFilter filter = new LayerFilter(LayerFilterOperationType.Contains);
|
||||
|
||||
// filter.Key = MapboxSpecialLayerParameters.LayerNameTypeProperty[properties.coreOptions.layerName];
|
||||
// filter.PropertyValue = properties.selectedTypes;
|
||||
|
||||
// //if (properties.coreOptions.layerName == properties.roadLayer)
|
||||
// //{
|
||||
// // filter.Key = properties.roadLayer_TypeProperty;
|
||||
// // filter.PropertyValue = properties.selectedTypes;
|
||||
// //}
|
||||
// //else if (properties.coreOptions.layerName == "landuse")
|
||||
// //{
|
||||
// // filter.Key = properties.landuseLayer_TypeProperty;
|
||||
// // filter.PropertyValue = properties.selectedTypes;
|
||||
// //}
|
||||
// properties.filterOptions.filters.Add(filter);
|
||||
//}
|
||||
|
||||
_layerProperties.PropertyHasChanged += UpdateVector;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add the replacement criteria to any mesh modifiers implementing IReplaceable
|
||||
/// </summary>
|
||||
/// <param name="criteria">Criteria.</param>
|
||||
protected void SetReplacementCriteria(IReplacementCriteria criteria)
|
||||
{
|
||||
foreach (var meshMod in _defaultStack.MeshModifiers)
|
||||
{
|
||||
if (meshMod is IReplaceable)
|
||||
{
|
||||
((IReplaceable)meshMod).Criteria.Add(criteria);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#region Private Helper Methods
|
||||
/// <summary>
|
||||
/// Convenience function to add feature to Tile object pool.
|
||||
/// </summary>
|
||||
/// <param name="feature">Feature to be added to the pool.</param>
|
||||
/// <param name="tile">Tile currently being processed.</param>
|
||||
private void AddFeatureToTileObjectPool(VectorFeatureUnity feature, UnityTile tile)
|
||||
{
|
||||
_activeIds.Add(feature.Data.Id);
|
||||
if (!_idPool.ContainsKey(tile))
|
||||
{
|
||||
_idPool.Add(tile, new List<ulong>());
|
||||
}
|
||||
else
|
||||
{
|
||||
_idPool[tile].Add(feature.Data.Id);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Apply filters to the layer and check if the current feature is eleigible for rendering.
|
||||
/// </summary>
|
||||
/// <returns><c>true</c>, if feature eligible after filtering was applied, <c>false</c> otherwise.</returns>
|
||||
/// <param name="feature">Feature.</param>
|
||||
private bool IsFeatureEligibleAfterFiltering(VectorFeatureUnity feature, UnityTile tile, VectorLayerVisualizerProperties layerProperties)
|
||||
{
|
||||
if (layerProperties.layerFeatureFilters.Count() == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// build features only if the filter returns true.
|
||||
if (layerProperties.layerFeatureFilterCombiner.Try(feature))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Function to fetch feature in vector tile at the index specified.
|
||||
/// </summary>
|
||||
/// <returns>The feature in tile at the index requested.</returns>
|
||||
/// <param name="tile">Unity Tile containing the feature.</param>
|
||||
/// <param name="index">Index of the vector feature being requested.</param>
|
||||
private VectorFeatureUnity GetFeatureinTileAtIndex(int index, UnityTile tile, VectorLayerVisualizerProperties layerProperties)
|
||||
{
|
||||
return new VectorFeatureUnity(layerProperties.vectorTileLayer.GetFeature(index),
|
||||
tile,
|
||||
layerProperties.vectorTileLayer.Extent,
|
||||
layerProperties.buildingsWithUniqueIds);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Function to check if the feature is already in the active Id pool, features already in active Id pool should be skipped from processing.
|
||||
/// </summary>
|
||||
/// <returns><c>true</c>, if feature is already in activeId pool or if the layer has buildingsWithUniqueId flag set to <see langword="true"/>, <c>false</c> otherwise.</returns>
|
||||
/// <param name="featureId">Feature identifier.</param>
|
||||
private bool ShouldSkipProcessingFeatureWithId(ulong featureId, UnityTile tile, VectorLayerVisualizerProperties layerProperties)
|
||||
{
|
||||
return (layerProperties.buildingsWithUniqueIds && _activeIds.Contains(featureId));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this entity per coroutine bucket is full.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if coroutine bucket is full; otherwise, <c>false</c>.</value>
|
||||
private bool IsCoroutineBucketFull
|
||||
{
|
||||
get
|
||||
{
|
||||
return (_performanceOptions != null && _performanceOptions.isEnabled && _entityInCurrentCoroutine >= _performanceOptions.entityPerCoroutine);
|
||||
}
|
||||
}
|
||||
|
||||
public override bool Active
|
||||
{
|
||||
get
|
||||
{
|
||||
return _layerProperties.coreOptions.isActive;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
_entityInCurrentCoroutine = 0;
|
||||
_activeCoroutines = new Dictionary<UnityTile, List<int>>();
|
||||
_activeIds = new HashSet<ulong>();
|
||||
_idPool = new Dictionary<UnityTile, List<ulong>>();
|
||||
|
||||
if (_defaultStack != null)
|
||||
{
|
||||
_defaultStack.Initialize();
|
||||
}
|
||||
}
|
||||
|
||||
public override void InitializeStack()
|
||||
{
|
||||
if (_defaultStack != null)
|
||||
{
|
||||
_defaultStack.Initialize();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public override void Create(VectorTileLayer layer, UnityTile tile, Action<UnityTile, LayerVisualizerBase> callback)
|
||||
{
|
||||
if (!_activeCoroutines.ContainsKey(tile))
|
||||
_activeCoroutines.Add(tile, new List<int>());
|
||||
_activeCoroutines[tile].Add(Runnable.Run(ProcessLayer(layer, tile, tile.UnwrappedTileId, callback)));
|
||||
}
|
||||
|
||||
protected IEnumerator ProcessLayer(VectorTileLayer layer, UnityTile tile, UnwrappedTileId tileId, Action<UnityTile, LayerVisualizerBase> callback = null)
|
||||
{
|
||||
//HACK to prevent request finishing on same frame which breaks modules started/finished events
|
||||
yield return null;
|
||||
|
||||
if (tile == null)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
|
||||
VectorLayerVisualizerProperties tempLayerProperties = new VectorLayerVisualizerProperties();
|
||||
tempLayerProperties.vectorTileLayer = layer;
|
||||
tempLayerProperties.featureProcessingStage = FeatureProcessingStage.PreProcess;
|
||||
|
||||
//Get all filters in the array.
|
||||
tempLayerProperties.layerFeatureFilters = _layerProperties.filterOptions.filters.Select(m => m.GetFilterComparer()).ToArray();
|
||||
|
||||
// Pass them to the combiner
|
||||
tempLayerProperties.layerFeatureFilterCombiner = new Filters.LayerFilterComparer();
|
||||
switch (_layerProperties.filterOptions.combinerType)
|
||||
{
|
||||
case Filters.LayerFilterCombinerOperationType.Any:
|
||||
tempLayerProperties.layerFeatureFilterCombiner = Filters.LayerFilterComparer.AnyOf(tempLayerProperties.layerFeatureFilters);
|
||||
break;
|
||||
case Filters.LayerFilterCombinerOperationType.All:
|
||||
tempLayerProperties.layerFeatureFilterCombiner = Filters.LayerFilterComparer.AllOf(tempLayerProperties.layerFeatureFilters);
|
||||
break;
|
||||
case Filters.LayerFilterCombinerOperationType.None:
|
||||
tempLayerProperties.layerFeatureFilterCombiner = Filters.LayerFilterComparer.NoneOf(tempLayerProperties.layerFeatureFilters);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
tempLayerProperties.buildingsWithUniqueIds = (_layerProperties.honorBuildingIdSetting) && _layerProperties.buildingsWithUniqueIds;
|
||||
|
||||
////find any replacement criteria and assign them
|
||||
foreach (var goModifier in _defaultStack.GoModifiers)
|
||||
{
|
||||
if (goModifier is IReplacementCriteria && goModifier.Active)
|
||||
{
|
||||
SetReplacementCriteria((IReplacementCriteria)goModifier);
|
||||
}
|
||||
}
|
||||
|
||||
#region PreProcess & Process.
|
||||
|
||||
var featureCount = (tempLayerProperties.vectorTileLayer == null) ? 0 : tempLayerProperties.vectorTileLayer.FeatureCount();
|
||||
do
|
||||
{
|
||||
for (int i = 0; i < featureCount; i++)
|
||||
{
|
||||
//checking if tile is recycled and changed
|
||||
if (tile.UnwrappedTileId != tileId || !_activeCoroutines.ContainsKey(tile) || tile.TileState == Enums.TilePropertyState.Unregistered)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
|
||||
ProcessFeature(i, tile, tempLayerProperties, layer.Extent);
|
||||
|
||||
if (IsCoroutineBucketFull)
|
||||
{
|
||||
//Reset bucket..
|
||||
_entityInCurrentCoroutine = 0;
|
||||
yield return null;
|
||||
}
|
||||
}
|
||||
// move processing to next stage.
|
||||
tempLayerProperties.featureProcessingStage++;
|
||||
} while (tempLayerProperties.featureProcessingStage == FeatureProcessingStage.PreProcess
|
||||
|| tempLayerProperties.featureProcessingStage == FeatureProcessingStage.Process);
|
||||
|
||||
#endregion
|
||||
|
||||
#region PostProcess
|
||||
// TODO : Clean this up to follow the same pattern.
|
||||
var mergedStack = _defaultStack as MergedModifierStack;
|
||||
if (mergedStack != null && tile != null)
|
||||
{
|
||||
mergedStack.End(tile, tile.gameObject, layer.Name);
|
||||
}
|
||||
#endregion
|
||||
|
||||
if (callback != null)
|
||||
callback(tile, this);
|
||||
}
|
||||
|
||||
private bool ProcessFeature(int index, UnityTile tile, VectorLayerVisualizerProperties layerProperties, float layerExtent)
|
||||
{
|
||||
var fe = layerProperties.vectorTileLayer.GetFeature(index);
|
||||
List<List<Point2d<float>>> geom;
|
||||
if (layerProperties.buildingsWithUniqueIds == true) //ids from building dataset is big ulongs
|
||||
{
|
||||
geom = fe.Geometry<float>(); //and we're not clipping by passing no parameters
|
||||
|
||||
if (geom[0][0].X < 0 || geom[0][0].X > layerExtent || geom[0][0].Y < 0 || geom[0][0].Y > layerExtent)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else //streets ids, will require clipping
|
||||
{
|
||||
geom = fe.Geometry<float>(0); //passing zero means clip at tile edge
|
||||
}
|
||||
|
||||
var feature = new VectorFeatureUnity(layerProperties.vectorTileLayer.GetFeature(index),
|
||||
geom,
|
||||
tile,
|
||||
layerProperties.vectorTileLayer.Extent,
|
||||
layerProperties.buildingsWithUniqueIds);
|
||||
|
||||
|
||||
if (IsFeatureEligibleAfterFiltering(feature, tile, layerProperties))
|
||||
{
|
||||
if (tile != null && tile.gameObject != null && tile.VectorDataState != Enums.TilePropertyState.Cancelled)
|
||||
{
|
||||
switch (layerProperties.featureProcessingStage)
|
||||
{
|
||||
case FeatureProcessingStage.PreProcess:
|
||||
//pre process features.
|
||||
PreProcessFeatures(feature, tile, tile.gameObject);
|
||||
break;
|
||||
case FeatureProcessingStage.Process:
|
||||
//skip existing features, only works on tilesets with unique ids
|
||||
if (ShouldSkipProcessingFeatureWithId(feature.Data.Id, tile, layerProperties))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
//feature not skipped. Add to pool only if features are in preprocess stage.
|
||||
AddFeatureToTileObjectPool(feature, tile);
|
||||
Build(feature, tile, tile.gameObject);
|
||||
break;
|
||||
case FeatureProcessingStage.PostProcess:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
_entityInCurrentCoroutine++;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Preprocess features, finds the relevant modifier stack and passes the feature to that stack
|
||||
/// </summary>
|
||||
/// <param name="feature"></param>
|
||||
/// <param name="tile"></param>
|
||||
/// <param name="parent"></param>
|
||||
private bool IsFeatureValid(VectorFeatureUnity feature)
|
||||
{
|
||||
if (feature.Properties.ContainsKey("extrude") && !bool.Parse(feature.Properties["extrude"].ToString()))
|
||||
return false;
|
||||
|
||||
if (feature.Points.Count < 1)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected void PreProcessFeatures(VectorFeatureUnity feature, UnityTile tile, GameObject parent)
|
||||
{
|
||||
////find any replacement criteria and assign them
|
||||
foreach (var goModifier in _defaultStack.GoModifiers)
|
||||
{
|
||||
if (goModifier is IReplacementCriteria && goModifier.Active)
|
||||
{
|
||||
goModifier.FeaturePreProcess(feature);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void Build(VectorFeatureUnity feature, UnityTile tile, GameObject parent)
|
||||
{
|
||||
if (feature.Properties.ContainsKey("extrude") && !Convert.ToBoolean(feature.Properties["extrude"]))
|
||||
return;
|
||||
|
||||
if (feature.Points.Count < 1)
|
||||
return;
|
||||
|
||||
//this will be improved in next version and will probably be replaced by filters
|
||||
var styleSelectorKey = _layerProperties.coreOptions.sublayerName;
|
||||
|
||||
var meshData = new MeshData();
|
||||
meshData.TileRect = tile.Rect;
|
||||
|
||||
//and finally, running the modifier stack on the feature
|
||||
var processed = false;
|
||||
|
||||
if (!processed)
|
||||
{
|
||||
if (_defaultStack != null)
|
||||
{
|
||||
_defaultStack.Execute(tile, feature, meshData, parent, styleSelectorKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void PostProcessFeatures(VectorFeatureUnity feature, UnityTile tile, GameObject parent)
|
||||
{
|
||||
//var mergedStack = _defaultStack as MergedModifierStack;
|
||||
//if (mergedStack != null && tile != null)
|
||||
//{
|
||||
// mergedStack.End(tile, tile.gameObject, _vectorFeaturesPerTile[tile].vectorTileLayer.Name);
|
||||
//}
|
||||
}
|
||||
private string FindSelectorKey(VectorFeatureUnity feature)
|
||||
{
|
||||
// TODO: FIX THIS!!
|
||||
//if (string.IsNullOrEmpty(_classificationKey))
|
||||
//{
|
||||
// if (feature.Properties.ContainsKey("type"))
|
||||
// {
|
||||
// return feature.Properties["type"].ToString().ToLowerInvariant();
|
||||
// }
|
||||
// else if (feature.Properties.ContainsKey("class"))
|
||||
// {
|
||||
// return feature.Properties["class"].ToString().ToLowerInvariant();
|
||||
// }
|
||||
//}
|
||||
//else
|
||||
//TODO: Come back to this.
|
||||
//var size = _layerProperties.coreOptions.propertyValuePairs.Count;
|
||||
//for (int i = 0; i < size; i++)
|
||||
//{
|
||||
// var key = _layerProperties.coreOptions.propertyValuePairs[i].featureKey;
|
||||
// if (feature.Properties.ContainsKey(key))
|
||||
// {
|
||||
// if (feature.Properties.ContainsKey(key))
|
||||
// {
|
||||
// return feature.Properties[key].ToString().ToLowerInvariant();
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
|
||||
return Key;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle tile destruction event and propagate it to modifier stacks
|
||||
/// </summary>
|
||||
/// <param name="tile">Destroyed tile object</param>
|
||||
public override void OnUnregisterTile(UnityTile tile)
|
||||
{
|
||||
base.OnUnregisterTile(tile);
|
||||
if (_activeCoroutines.ContainsKey(tile))
|
||||
{
|
||||
foreach (var cor in _activeCoroutines[tile])
|
||||
{
|
||||
Runnable.Stop(cor);
|
||||
}
|
||||
}
|
||||
_activeCoroutines.Remove(tile);
|
||||
|
||||
if (_defaultStack != null)
|
||||
{
|
||||
_defaultStack.UnregisterTile(tile);
|
||||
}
|
||||
|
||||
//removing ids from activeIds list so they'll be recreated next time tile loads (necessary when you're unloading/loading tiles)
|
||||
if (_idPool.ContainsKey(tile))
|
||||
{
|
||||
foreach (var item in _idPool[tile])
|
||||
{
|
||||
_activeIds.Remove(item);
|
||||
}
|
||||
_idPool[tile].Clear();
|
||||
}
|
||||
//UnbindSubLayerEvents();
|
||||
}
|
||||
|
||||
public override void ClearCaches()
|
||||
{
|
||||
_idPool.Clear();
|
||||
_defaultStack.ClearCaches();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 120e43a7012622e49aca0c9217acd1f9
|
||||
timeCreated: 1520376260
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 25cf434b1eb73654b87eeb87c3dd3874
|
||||
folderAsset: yes
|
||||
timeCreated: 1478551853
|
||||
licenseType: Free
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,37 @@
|
||||
namespace Mapbox.Unity.MeshGeneration.Modifiers
|
||||
{
|
||||
using Mapbox.Unity.MeshGeneration.Data;
|
||||
using Mapbox.Unity.MeshGeneration.Components;
|
||||
using System;
|
||||
|
||||
/// <summary>
|
||||
/// GameObject Modifiers
|
||||
/// Game object modifiers ran after the mesh modifiers and game object creation.Their main purpose is to work on
|
||||
/// game object and decorate/improve them in their own ways.They ran for each game object individually.
|
||||
/// It's possible to do lots of different things with GameObject Modifiers.A simple example would be MaterialModifier,
|
||||
/// which simply sets random materials to gameobject and submeshes.A more complicated example would be
|
||||
/// SpawnInside Modifier which instantiates prefabs in a polygon, like trees in a park.
|
||||
/// Any operation, you want to perform on generated entity, that would require a game object is a good candidate
|
||||
/// for game object modifiers. For example, things like adding a collider or animation would require a gameobject
|
||||
/// hence cannot be done in mesh modifier.
|
||||
/// Game object modifiers is the suggested way of customizing generated game object and we expect developers to
|
||||
/// fully utilize this by creating their own custom game object modifiers.
|
||||
/// </summary>
|
||||
public class GameObjectModifier : ModifierBase
|
||||
{
|
||||
public virtual void Run(VectorEntity ve, UnityTile tile)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public virtual void OnPoolItem(VectorEntity vectorEntity)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public virtual void ClearCaches()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 04073a22e6d61d442b88733b4bb8c32d
|
||||
timeCreated: 1478551946
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1db53dec60e777144b02910fea4ee2c3
|
||||
folderAsset: yes
|
||||
timeCreated: 1485207977
|
||||
licenseType: Pro
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,39 @@
|
||||
namespace Mapbox.Unity.MeshGeneration.Modifiers
|
||||
{
|
||||
using Mapbox.Unity.MeshGeneration.Data;
|
||||
using Mapbox.Unity.MeshGeneration.Components;
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
|
||||
[CreateAssetMenu(menuName = "Mapbox/Modifiers/Add Monobehaviours Modifier")]
|
||||
public class AddMonoBehavioursModifier : GameObjectModifier
|
||||
{
|
||||
[SerializeField]
|
||||
AddMonoBehavioursModifierType[] _types;
|
||||
private HashSet<string> _scripts;
|
||||
private string _tempId;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
if (_scripts == null)
|
||||
{
|
||||
_scripts = new HashSet<string>();
|
||||
_tempId = string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Run(VectorEntity ve, UnityTile tile)
|
||||
{
|
||||
foreach (var t in _types)
|
||||
{
|
||||
_tempId = ve.GameObject.GetInstanceID() + t.Type.FullName;
|
||||
if (!_scripts.Contains(_tempId))
|
||||
{
|
||||
ve.GameObject.AddComponent(t.Type);
|
||||
_scripts.Add(_tempId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c3b6ec6b924494ffda604db0a299df8d
|
||||
timeCreated: 1499897465
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,35 @@
|
||||
namespace Mapbox.Unity.MeshGeneration.Modifiers
|
||||
{
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
|
||||
[Serializable]
|
||||
public class AddMonoBehavioursModifierType
|
||||
{
|
||||
[SerializeField]
|
||||
string _typeString;
|
||||
|
||||
Type _type;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
[SerializeField]
|
||||
MonoScript _script;
|
||||
#endif
|
||||
|
||||
public Type Type
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_type == null)
|
||||
{
|
||||
_type = Type.GetType(_typeString);
|
||||
}
|
||||
return _type;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 90b119b6678da4c80a69a3a4d8793d7f
|
||||
timeCreated: 1499897465
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,24 @@
|
||||
namespace Mapbox.Unity.MeshGeneration.Modifiers
|
||||
{
|
||||
using UnityEngine;
|
||||
using Mapbox.Unity.MeshGeneration.Components;
|
||||
using Mapbox.Unity.MeshGeneration.Data;
|
||||
|
||||
[CreateAssetMenu(menuName = "Mapbox/Modifiers/Add To Collection Modifier")]
|
||||
public class AddToCollectionModifier : GameObjectModifier
|
||||
{
|
||||
[SerializeField]
|
||||
private FeatureCollectionBase _collection;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
_collection.Initialize();
|
||||
}
|
||||
|
||||
public override void Run(VectorEntity ve, UnityTile tile)
|
||||
{
|
||||
_collection.AddFeature(new double[] { ve.Transform.position.x, ve.Transform.position.z }, ve);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4538b91da572dfa41adf689573eaba4b
|
||||
timeCreated: 1519739956
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,176 @@
|
||||
namespace Mapbox.Unity.MeshGeneration.Modifiers
|
||||
{
|
||||
using Mapbox.Unity.MeshGeneration.Data;
|
||||
using UnityEngine;
|
||||
using Mapbox.Unity.MeshGeneration.Components;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using Mapbox.Unity.Map;
|
||||
|
||||
[CreateAssetMenu(menuName = "Mapbox/Modifiers/Collider Modifier")]
|
||||
public class ColliderModifier : GameObjectModifier
|
||||
{
|
||||
//[SerializeField]
|
||||
//private ColliderType _colliderType;
|
||||
private IColliderStrategy _colliderStrategy;
|
||||
|
||||
[SerializeField]
|
||||
ColliderOptions _options;
|
||||
|
||||
|
||||
public override void SetProperties(ModifierProperties properties)
|
||||
{
|
||||
_options = (ColliderOptions)properties;
|
||||
_options.PropertyHasChanged += UpdateModifier;
|
||||
}
|
||||
|
||||
public override void UnbindProperties()
|
||||
{
|
||||
_options.PropertyHasChanged -= UpdateModifier;
|
||||
}
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
//no need to reset strategy objects on map reinit as we're caching feature game objects as well
|
||||
//creating a new one iff we don't already have one. if you want to reset/recreate you have to clear stuff inside current/old one first.
|
||||
|
||||
switch (_options.colliderType)
|
||||
{
|
||||
case ColliderType.None:
|
||||
_colliderStrategy = null;
|
||||
break;
|
||||
case ColliderType.BoxCollider:
|
||||
_colliderStrategy = new BoxColliderStrategy();
|
||||
break;
|
||||
case ColliderType.MeshCollider:
|
||||
_colliderStrategy = new MeshColliderStrategy();
|
||||
break;
|
||||
case ColliderType.SphereCollider:
|
||||
_colliderStrategy = new SphereColliderStrategy();
|
||||
break;
|
||||
default:
|
||||
_colliderStrategy = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Run(VectorEntity ve, UnityTile tile)
|
||||
{
|
||||
// if collider exists. remove it.
|
||||
RemoveColliderFrom(ve);
|
||||
if (_colliderStrategy != null)
|
||||
{
|
||||
_colliderStrategy.AddColliderTo(ve);
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveColliderFrom(VectorEntity ve)
|
||||
{
|
||||
var existingCollider = ve.GameObject.GetComponent<Collider>();
|
||||
if (existingCollider != null)
|
||||
{
|
||||
UnityEngine.Object.Destroy(existingCollider);
|
||||
if (_colliderStrategy != null)
|
||||
{
|
||||
_colliderStrategy.Reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class BoxColliderStrategy : IColliderStrategy
|
||||
{
|
||||
private Dictionary<GameObject, BoxCollider> _colliders;
|
||||
|
||||
public BoxColliderStrategy()
|
||||
{
|
||||
_colliders = new Dictionary<GameObject, BoxCollider>();
|
||||
}
|
||||
|
||||
public void AddColliderTo(VectorEntity ve)
|
||||
{
|
||||
if (_colliders.ContainsKey(ve.GameObject))
|
||||
{
|
||||
_colliders[ve.GameObject].center = ve.Mesh.bounds.center;
|
||||
_colliders[ve.GameObject].size = ve.Mesh.bounds.size;
|
||||
}
|
||||
else
|
||||
{
|
||||
_colliders.Add(ve.GameObject, ve.GameObject.AddComponent<BoxCollider>());
|
||||
}
|
||||
}
|
||||
public void Reset()
|
||||
{
|
||||
if (_colliders != null)
|
||||
{
|
||||
_colliders.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class MeshColliderStrategy : IColliderStrategy
|
||||
{
|
||||
private Dictionary<GameObject, MeshCollider> _colliders;
|
||||
|
||||
public MeshColliderStrategy()
|
||||
{
|
||||
_colliders = new Dictionary<GameObject, MeshCollider>();
|
||||
}
|
||||
|
||||
public void AddColliderTo(VectorEntity ve)
|
||||
{
|
||||
if (_colliders.ContainsKey(ve.GameObject))
|
||||
{
|
||||
_colliders[ve.GameObject].sharedMesh = ve.Mesh;
|
||||
}
|
||||
else
|
||||
{
|
||||
_colliders.Add(ve.GameObject, ve.GameObject.AddComponent<MeshCollider>());
|
||||
}
|
||||
}
|
||||
public void Reset()
|
||||
{
|
||||
if (_colliders != null)
|
||||
{
|
||||
_colliders.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class SphereColliderStrategy : IColliderStrategy
|
||||
{
|
||||
private Dictionary<GameObject, SphereCollider> _colliders;
|
||||
|
||||
public SphereColliderStrategy()
|
||||
{
|
||||
_colliders = new Dictionary<GameObject, SphereCollider>();
|
||||
}
|
||||
|
||||
public void AddColliderTo(VectorEntity ve)
|
||||
{
|
||||
if (_colliders.ContainsKey(ve.GameObject))
|
||||
{
|
||||
_colliders[ve.GameObject].center = ve.Mesh.bounds.center;
|
||||
_colliders[ve.GameObject].radius = ve.Mesh.bounds.extents.magnitude;
|
||||
}
|
||||
else
|
||||
{
|
||||
_colliders.Add(ve.GameObject, ve.GameObject.AddComponent<SphereCollider>());
|
||||
}
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
if (_colliders != null)
|
||||
{
|
||||
_colliders.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public interface IColliderStrategy
|
||||
{
|
||||
void AddColliderTo(VectorEntity ve);
|
||||
void Reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user