[TASK] Initial commit with basic product setup

This commit is contained in:
2019-08-18 13:50:14 +02:00
commit 01a66a8e1f
2548 changed files with 167528 additions and 0 deletions

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: b418c0cc9fb9f92448750e73736cb2e2
folderAsset: yes
timeCreated: 1485208035
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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;
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 1dd19505b960fb645953659207ca4bef
timeCreated: 1485208038
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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)]);
//}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 97464c54f6d369444b297e02ee300633
timeCreated: 1485208041
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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";
//}
}
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 74c6dd1076bc8954dad41ed4206c598d
timeCreated: 1499870729
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: c4b8220219b204a288d4b67600489e3f
timeCreated: 1499870729
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 4b95895b4bbc4844cade47f970759aa5
folderAsset: yes
timeCreated: 1485207080
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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;
}
}
}
}
}

View File

@@ -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:

View File

@@ -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)
{
}
}
}

View File

@@ -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:

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 4f15de9092560a644913bb912f5642ec
folderAsset: yes
timeCreated: 1519738933
licenseType: Pro
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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;
}
}
}

View File

@@ -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:

View File

@@ -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;
}
}
}
}
}

View File

@@ -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:

View File

@@ -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
}
}

View File

@@ -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:

View File

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

View File

@@ -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:

View File

@@ -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;
}
}
}
}
}

View File

@@ -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:

View File

@@ -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;
}
}
}

View File

@@ -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:

View File

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

View File

@@ -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:

View File

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

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 2dc52b40d4a914f4d88ef8d5547272b5
timeCreated: 1478552106
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

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

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: def0795d2b7c06547bcfdb57ec6f201b
timeCreated: 1478541275
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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;
}
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 161842d5b19bcf24a842752aadb1da26
timeCreated: 1485209878
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 665b654cd42daed428aacdad0c045d85
folderAsset: yes
timeCreated: 1485206853
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,12 @@
namespace Mapbox.Unity.MeshGeneration.Enums
{
public enum TilePropertyState
{
Unregistered,
None,
Loading,
Loaded,
Error,
Cancelled,
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 63187d4e1f6f0ad40ae6e6a5da6ebef1
timeCreated: 1485206857
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: ed0cb36528ae4584b9d01d98fee861f5
folderAsset: yes
timeCreated: 1494961030
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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 well 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 doesnt 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
/// dont 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
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 14c4d9b107de24a028e1bd452df7ae29
timeCreated: 1494972519
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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;
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 44d601772d842a94cb5ab933dca6bd4f
timeCreated: 1485217963
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

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

View File

@@ -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:

View File

@@ -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
}
}

View File

@@ -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:

View File

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

View File

@@ -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:

View File

@@ -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
}
}

View File

@@ -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:

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 9a7ab3f3c54be5140aecaed29e7e4f65
folderAsset: yes
timeCreated: 1522755730
licenseType: Free
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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
}
}

View File

@@ -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:

View File

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

View File

@@ -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:

View File

@@ -0,0 +1,4 @@
public interface IElevationBasedTerrainStrategy
{
}

View File

@@ -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:

View File

@@ -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)
{
}
}
}

View File

@@ -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:

View File

@@ -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;
}
}
}

View File

@@ -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:

View File

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

View File

@@ -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:

View File

@@ -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)
{
}
}
}

View File

@@ -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:

View File

@@ -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;
}
}
}

View File

@@ -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:

View File

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

View File

@@ -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:

View File

@@ -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
}
}

View File

@@ -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:

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 32e94cd9a75b25c439ca153c42fcb598
folderAsset: yes
timeCreated: 1485210114
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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()
{
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 7495b32b05d97be4a8666ab6af32cdee
timeCreated: 1485210126
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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;
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: ae03e6a279b82f94a81883a2698db813
timeCreated: 1485210126
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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;
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: cb49207f63da8544d9f0a3f790016544
timeCreated: 1485210126
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: e4ff53839a2a7b04bb09cc89f514267d
folderAsset: yes
timeCreated: 1483047390
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,9 @@
namespace Mapbox.Unity.MeshGeneration.Interfaces
{
using System.Collections.Generic;
public interface IFeaturePropertySettable
{
void Set(Dictionary<string, object> props);
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: e4a4d2f716d05a5438b51569513fdc77
timeCreated: 1483231201
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

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

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 79d5cc1929eb9984089bd16e403893b9
timeCreated: 1483047390
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 563368f5fe3c51549bbbdd1ce2d6dd9a
folderAsset: yes
timeCreated: 1478551786
licenseType: Free
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

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

View File

@@ -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:

View File

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

View File

@@ -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:

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 25cf434b1eb73654b87eeb87c3dd3874
folderAsset: yes
timeCreated: 1478551853
licenseType: Free
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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()
{
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 04073a22e6d61d442b88733b4bb8c32d
timeCreated: 1478551946
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 1db53dec60e777144b02910fea4ee2c3
folderAsset: yes
timeCreated: 1485207977
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

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

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: c3b6ec6b924494ffda604db0a299df8d
timeCreated: 1499897465
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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;
}
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 90b119b6678da4c80a69a3a4d8793d7f
timeCreated: 1499897465
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

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

View File

@@ -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:

View File

@@ -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