[TASK] Initial commit with basic product setup
This commit is contained in:
@@ -0,0 +1,37 @@
|
||||
namespace Mapbox.Unity.MeshGeneration.Modifiers
|
||||
{
|
||||
using Mapbox.Unity.MeshGeneration.Data;
|
||||
using Mapbox.Unity.MeshGeneration.Components;
|
||||
using System;
|
||||
|
||||
/// <summary>
|
||||
/// GameObject Modifiers
|
||||
/// Game object modifiers ran after the mesh modifiers and game object creation.Their main purpose is to work on
|
||||
/// game object and decorate/improve them in their own ways.They ran for each game object individually.
|
||||
/// It's possible to do lots of different things with GameObject Modifiers.A simple example would be MaterialModifier,
|
||||
/// which simply sets random materials to gameobject and submeshes.A more complicated example would be
|
||||
/// SpawnInside Modifier which instantiates prefabs in a polygon, like trees in a park.
|
||||
/// Any operation, you want to perform on generated entity, that would require a game object is a good candidate
|
||||
/// for game object modifiers. For example, things like adding a collider or animation would require a gameobject
|
||||
/// hence cannot be done in mesh modifier.
|
||||
/// Game object modifiers is the suggested way of customizing generated game object and we expect developers to
|
||||
/// fully utilize this by creating their own custom game object modifiers.
|
||||
/// </summary>
|
||||
public class GameObjectModifier : ModifierBase
|
||||
{
|
||||
public virtual void Run(VectorEntity ve, UnityTile tile)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public virtual void OnPoolItem(VectorEntity vectorEntity)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public virtual void ClearCaches()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 04073a22e6d61d442b88733b4bb8c32d
|
||||
timeCreated: 1478551946
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1db53dec60e777144b02910fea4ee2c3
|
||||
folderAsset: yes
|
||||
timeCreated: 1485207977
|
||||
licenseType: Pro
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,39 @@
|
||||
namespace Mapbox.Unity.MeshGeneration.Modifiers
|
||||
{
|
||||
using Mapbox.Unity.MeshGeneration.Data;
|
||||
using Mapbox.Unity.MeshGeneration.Components;
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
|
||||
[CreateAssetMenu(menuName = "Mapbox/Modifiers/Add Monobehaviours Modifier")]
|
||||
public class AddMonoBehavioursModifier : GameObjectModifier
|
||||
{
|
||||
[SerializeField]
|
||||
AddMonoBehavioursModifierType[] _types;
|
||||
private HashSet<string> _scripts;
|
||||
private string _tempId;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
if (_scripts == null)
|
||||
{
|
||||
_scripts = new HashSet<string>();
|
||||
_tempId = string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Run(VectorEntity ve, UnityTile tile)
|
||||
{
|
||||
foreach (var t in _types)
|
||||
{
|
||||
_tempId = ve.GameObject.GetInstanceID() + t.Type.FullName;
|
||||
if (!_scripts.Contains(_tempId))
|
||||
{
|
||||
ve.GameObject.AddComponent(t.Type);
|
||||
_scripts.Add(_tempId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c3b6ec6b924494ffda604db0a299df8d
|
||||
timeCreated: 1499897465
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,35 @@
|
||||
namespace Mapbox.Unity.MeshGeneration.Modifiers
|
||||
{
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
|
||||
[Serializable]
|
||||
public class AddMonoBehavioursModifierType
|
||||
{
|
||||
[SerializeField]
|
||||
string _typeString;
|
||||
|
||||
Type _type;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
[SerializeField]
|
||||
MonoScript _script;
|
||||
#endif
|
||||
|
||||
public Type Type
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_type == null)
|
||||
{
|
||||
_type = Type.GetType(_typeString);
|
||||
}
|
||||
return _type;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 90b119b6678da4c80a69a3a4d8793d7f
|
||||
timeCreated: 1499897465
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,24 @@
|
||||
namespace Mapbox.Unity.MeshGeneration.Modifiers
|
||||
{
|
||||
using UnityEngine;
|
||||
using Mapbox.Unity.MeshGeneration.Components;
|
||||
using Mapbox.Unity.MeshGeneration.Data;
|
||||
|
||||
[CreateAssetMenu(menuName = "Mapbox/Modifiers/Add To Collection Modifier")]
|
||||
public class AddToCollectionModifier : GameObjectModifier
|
||||
{
|
||||
[SerializeField]
|
||||
private FeatureCollectionBase _collection;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
_collection.Initialize();
|
||||
}
|
||||
|
||||
public override void Run(VectorEntity ve, UnityTile tile)
|
||||
{
|
||||
_collection.AddFeature(new double[] { ve.Transform.position.x, ve.Transform.position.z }, ve);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4538b91da572dfa41adf689573eaba4b
|
||||
timeCreated: 1519739956
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,176 @@
|
||||
namespace Mapbox.Unity.MeshGeneration.Modifiers
|
||||
{
|
||||
using Mapbox.Unity.MeshGeneration.Data;
|
||||
using UnityEngine;
|
||||
using Mapbox.Unity.MeshGeneration.Components;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using Mapbox.Unity.Map;
|
||||
|
||||
[CreateAssetMenu(menuName = "Mapbox/Modifiers/Collider Modifier")]
|
||||
public class ColliderModifier : GameObjectModifier
|
||||
{
|
||||
//[SerializeField]
|
||||
//private ColliderType _colliderType;
|
||||
private IColliderStrategy _colliderStrategy;
|
||||
|
||||
[SerializeField]
|
||||
ColliderOptions _options;
|
||||
|
||||
|
||||
public override void SetProperties(ModifierProperties properties)
|
||||
{
|
||||
_options = (ColliderOptions)properties;
|
||||
_options.PropertyHasChanged += UpdateModifier;
|
||||
}
|
||||
|
||||
public override void UnbindProperties()
|
||||
{
|
||||
_options.PropertyHasChanged -= UpdateModifier;
|
||||
}
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
//no need to reset strategy objects on map reinit as we're caching feature game objects as well
|
||||
//creating a new one iff we don't already have one. if you want to reset/recreate you have to clear stuff inside current/old one first.
|
||||
|
||||
switch (_options.colliderType)
|
||||
{
|
||||
case ColliderType.None:
|
||||
_colliderStrategy = null;
|
||||
break;
|
||||
case ColliderType.BoxCollider:
|
||||
_colliderStrategy = new BoxColliderStrategy();
|
||||
break;
|
||||
case ColliderType.MeshCollider:
|
||||
_colliderStrategy = new MeshColliderStrategy();
|
||||
break;
|
||||
case ColliderType.SphereCollider:
|
||||
_colliderStrategy = new SphereColliderStrategy();
|
||||
break;
|
||||
default:
|
||||
_colliderStrategy = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Run(VectorEntity ve, UnityTile tile)
|
||||
{
|
||||
// if collider exists. remove it.
|
||||
RemoveColliderFrom(ve);
|
||||
if (_colliderStrategy != null)
|
||||
{
|
||||
_colliderStrategy.AddColliderTo(ve);
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveColliderFrom(VectorEntity ve)
|
||||
{
|
||||
var existingCollider = ve.GameObject.GetComponent<Collider>();
|
||||
if (existingCollider != null)
|
||||
{
|
||||
UnityEngine.Object.Destroy(existingCollider);
|
||||
if (_colliderStrategy != null)
|
||||
{
|
||||
_colliderStrategy.Reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class BoxColliderStrategy : IColliderStrategy
|
||||
{
|
||||
private Dictionary<GameObject, BoxCollider> _colliders;
|
||||
|
||||
public BoxColliderStrategy()
|
||||
{
|
||||
_colliders = new Dictionary<GameObject, BoxCollider>();
|
||||
}
|
||||
|
||||
public void AddColliderTo(VectorEntity ve)
|
||||
{
|
||||
if (_colliders.ContainsKey(ve.GameObject))
|
||||
{
|
||||
_colliders[ve.GameObject].center = ve.Mesh.bounds.center;
|
||||
_colliders[ve.GameObject].size = ve.Mesh.bounds.size;
|
||||
}
|
||||
else
|
||||
{
|
||||
_colliders.Add(ve.GameObject, ve.GameObject.AddComponent<BoxCollider>());
|
||||
}
|
||||
}
|
||||
public void Reset()
|
||||
{
|
||||
if (_colliders != null)
|
||||
{
|
||||
_colliders.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class MeshColliderStrategy : IColliderStrategy
|
||||
{
|
||||
private Dictionary<GameObject, MeshCollider> _colliders;
|
||||
|
||||
public MeshColliderStrategy()
|
||||
{
|
||||
_colliders = new Dictionary<GameObject, MeshCollider>();
|
||||
}
|
||||
|
||||
public void AddColliderTo(VectorEntity ve)
|
||||
{
|
||||
if (_colliders.ContainsKey(ve.GameObject))
|
||||
{
|
||||
_colliders[ve.GameObject].sharedMesh = ve.Mesh;
|
||||
}
|
||||
else
|
||||
{
|
||||
_colliders.Add(ve.GameObject, ve.GameObject.AddComponent<MeshCollider>());
|
||||
}
|
||||
}
|
||||
public void Reset()
|
||||
{
|
||||
if (_colliders != null)
|
||||
{
|
||||
_colliders.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class SphereColliderStrategy : IColliderStrategy
|
||||
{
|
||||
private Dictionary<GameObject, SphereCollider> _colliders;
|
||||
|
||||
public SphereColliderStrategy()
|
||||
{
|
||||
_colliders = new Dictionary<GameObject, SphereCollider>();
|
||||
}
|
||||
|
||||
public void AddColliderTo(VectorEntity ve)
|
||||
{
|
||||
if (_colliders.ContainsKey(ve.GameObject))
|
||||
{
|
||||
_colliders[ve.GameObject].center = ve.Mesh.bounds.center;
|
||||
_colliders[ve.GameObject].radius = ve.Mesh.bounds.extents.magnitude;
|
||||
}
|
||||
else
|
||||
{
|
||||
_colliders.Add(ve.GameObject, ve.GameObject.AddComponent<SphereCollider>());
|
||||
}
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
if (_colliders != null)
|
||||
{
|
||||
_colliders.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public interface IColliderStrategy
|
||||
{
|
||||
void AddColliderTo(VectorEntity ve);
|
||||
void Reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 288d05a2dbabb0f46a5fa6c85102780a
|
||||
timeCreated: 1494350918
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,14 @@
|
||||
namespace Mapbox.Unity.MeshGeneration.Modifiers
|
||||
{
|
||||
using Mapbox.Unity.MeshGeneration.Data;
|
||||
using UnityEngine;
|
||||
|
||||
[CreateAssetMenu(menuName = "Mapbox/Modifiers/Disable Mesh Renderer Modifier")]
|
||||
public class DisableMeshRendererModifier : GameObjectModifier
|
||||
{
|
||||
public override void Run(VectorEntity ve, UnityTile tile)
|
||||
{
|
||||
ve.MeshRenderer.enabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a0fe0aeb4b2814799b34ce6cfd6308a1
|
||||
timeCreated: 1508781994
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,36 @@
|
||||
namespace Mapbox.Unity.MeshGeneration.Modifiers
|
||||
{
|
||||
using Mapbox.Unity.MeshGeneration.Data;
|
||||
using Mapbox.Unity.MeshGeneration.Components;
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
|
||||
[CreateAssetMenu(menuName = "Mapbox/Modifiers/Add Feature Behaviour Modifier")]
|
||||
public class FeatureBehaviourModifier : GameObjectModifier
|
||||
{
|
||||
private Dictionary<GameObject, FeatureBehaviour> _features;
|
||||
private FeatureBehaviour _tempFeature;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
if (_features == null)
|
||||
{
|
||||
_features = new Dictionary<GameObject, FeatureBehaviour>();
|
||||
}
|
||||
}
|
||||
|
||||
public override void Run(VectorEntity ve, UnityTile tile)
|
||||
{
|
||||
if (_features.ContainsKey(ve.GameObject))
|
||||
{
|
||||
_features[ve.GameObject].Initialize(ve);
|
||||
}
|
||||
else
|
||||
{
|
||||
_tempFeature = ve.GameObject.AddComponent<FeatureBehaviour>();
|
||||
_features.Add(ve.GameObject, _tempFeature);
|
||||
_tempFeature.Initialize(ve);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fdf79806a4f74da4aa48339c010a208c
|
||||
timeCreated: 1499897465
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,18 @@
|
||||
namespace Mapbox.Unity.MeshGeneration.Modifiers
|
||||
{
|
||||
using UnityEngine;
|
||||
using Mapbox.Unity.MeshGeneration.Components;
|
||||
using Mapbox.Unity.MeshGeneration.Data;
|
||||
|
||||
[CreateAssetMenu(menuName = "Mapbox/Modifiers/Layer Modifier")]
|
||||
public class LayerModifier : GameObjectModifier
|
||||
{
|
||||
[SerializeField]
|
||||
private int _layerId;
|
||||
|
||||
public override void Run(VectorEntity ve, UnityTile tile)
|
||||
{
|
||||
ve.GameObject.layer = _layerId;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a4644f4fa4408d144a9c7927401f3807
|
||||
timeCreated: 1494349551
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,64 @@
|
||||
namespace Mapbox.Unity.MeshGeneration.Modifiers
|
||||
{
|
||||
using Mapbox.Unity.MeshGeneration.Data;
|
||||
using UnityEngine.Assertions;
|
||||
using UnityEngine;
|
||||
|
||||
public class MapboxStylesColorModifier : GameObjectModifier
|
||||
{
|
||||
|
||||
public ScriptablePalette m_scriptablePalette;
|
||||
|
||||
private const string _BASE_COLOR_NAME = "_BaseColor";
|
||||
private const string _DETAIL_ONE_COLOR_NAME = "_DetailColor1";
|
||||
private const string _DETAIL_TWO_COLOR_NAME = "_DetailColor2";
|
||||
|
||||
private int _baseColorId;
|
||||
private int _detailOneColorId;
|
||||
private int _detailTWoColorId;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
if (m_scriptablePalette == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_baseColorId = Shader.PropertyToID(_BASE_COLOR_NAME);
|
||||
_detailOneColorId = Shader.PropertyToID(_DETAIL_ONE_COLOR_NAME);
|
||||
_detailTWoColorId = Shader.PropertyToID(_DETAIL_TWO_COLOR_NAME);
|
||||
}
|
||||
|
||||
private Color GetRandomColorFromPalette()
|
||||
{
|
||||
Color color = Color.white;
|
||||
if (m_scriptablePalette.m_colors.Length > 0)
|
||||
{
|
||||
color = m_scriptablePalette.m_colors[Random.Range(0, m_scriptablePalette.m_colors.Length)];
|
||||
}
|
||||
return color;
|
||||
}
|
||||
|
||||
public override void Run(VectorEntity ve, UnityTile tile)
|
||||
{
|
||||
if (m_scriptablePalette == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
MaterialPropertyBlock propBlock = new MaterialPropertyBlock();
|
||||
|
||||
ve.MeshRenderer.GetPropertyBlock(propBlock);
|
||||
|
||||
Color baseColor = (m_scriptablePalette.m_setBaseColor_Override) ? m_scriptablePalette.m_baseColor_Override : GetRandomColorFromPalette();
|
||||
Color detailColor1 = (m_scriptablePalette.m_setDetailColor1_Override) ? m_scriptablePalette.m_detailColor1_Override : GetRandomColorFromPalette();
|
||||
Color detailColor2 = (m_scriptablePalette.m_setDetailColor2_Override) ? m_scriptablePalette.m_detailColor2_Override : GetRandomColorFromPalette();
|
||||
|
||||
propBlock.SetColor(_baseColorId, baseColor);
|
||||
propBlock.SetColor(_detailOneColorId, detailColor1);
|
||||
propBlock.SetColor(_detailTWoColorId, detailColor2);
|
||||
|
||||
ve.MeshRenderer.SetPropertyBlock(propBlock);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 56e8568e69143416293940ecb62ec734
|
||||
timeCreated: 1520460892
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,95 @@
|
||||
namespace Mapbox.Unity.MeshGeneration.Modifiers
|
||||
{
|
||||
using UnityEngine;
|
||||
using Mapbox.Unity.MeshGeneration.Components;
|
||||
using Mapbox.Unity.MeshGeneration.Data;
|
||||
using Mapbox.Unity.Map;
|
||||
using System;
|
||||
|
||||
/// <summary>
|
||||
/// Texture Modifier is a basic modifier which simply adds a TextureSelector script to the features.
|
||||
/// Logic is all pushed into this TextureSelector mono behaviour to make it's easier to change it in runtime.
|
||||
/// </summary>
|
||||
[CreateAssetMenu(menuName = "Mapbox/Modifiers/Material Modifier")]
|
||||
public class MaterialModifier : GameObjectModifier
|
||||
{
|
||||
[SerializeField]
|
||||
GeometryMaterialOptions _options;
|
||||
|
||||
public override void SetProperties(ModifierProperties properties)
|
||||
{
|
||||
_options = (GeometryMaterialOptions)properties;
|
||||
_options.PropertyHasChanged += UpdateModifier;
|
||||
}
|
||||
|
||||
public override void UnbindProperties()
|
||||
{
|
||||
_options.PropertyHasChanged -= UpdateModifier;
|
||||
}
|
||||
|
||||
private float GetRenderMode(float val)
|
||||
{
|
||||
return Mathf.Approximately(val, 1.0f) ? 0f : 3f;
|
||||
}
|
||||
|
||||
public override void Run(VectorEntity ve, UnityTile tile)
|
||||
{
|
||||
var min = Math.Min(_options.materials.Length, ve.MeshFilter.mesh.subMeshCount);
|
||||
var mats = new Material[min];
|
||||
|
||||
if (_options.style == StyleTypes.Custom)
|
||||
{
|
||||
for (int i = 0; i < min; i++)
|
||||
{
|
||||
mats[i] = _options.customStyleOptions.materials[i].Materials[UnityEngine.Random.Range(0, _options.customStyleOptions.materials[i].Materials.Length)];
|
||||
}
|
||||
}
|
||||
else if (_options.style == StyleTypes.Satellite)
|
||||
{
|
||||
for (int i = 0; i < min; i++)
|
||||
{
|
||||
mats[i] = _options.materials[i].Materials[UnityEngine.Random.Range(0, _options.materials[i].Materials.Length)];
|
||||
}
|
||||
|
||||
mats[0].mainTexture = tile.GetRasterData();
|
||||
mats[0].mainTextureScale = new Vector2(1f, 1f);
|
||||
}
|
||||
else
|
||||
{
|
||||
float renderMode = 0.0f;
|
||||
switch (_options.style)
|
||||
{
|
||||
case StyleTypes.Light:
|
||||
renderMode = GetRenderMode(_options.lightStyleOpacity);
|
||||
break;
|
||||
case StyleTypes.Dark:
|
||||
renderMode = GetRenderMode(_options.darkStyleOpacity);
|
||||
break;
|
||||
case StyleTypes.Color:
|
||||
renderMode = GetRenderMode(_options.colorStyleColor.a);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
for (int i = 0; i < min; i++)
|
||||
{
|
||||
mats[i] = _options.materials[i].Materials[UnityEngine.Random.Range(0, _options.materials[i].Materials.Length)];
|
||||
mats[i].SetFloat("_Mode", renderMode);
|
||||
}
|
||||
}
|
||||
ve.MeshRenderer.materials = mats;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class MaterialList
|
||||
{
|
||||
[SerializeField]
|
||||
public Material[] Materials;
|
||||
|
||||
public MaterialList()
|
||||
{
|
||||
Materials = new Material[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 76c93f3debdba4e48adb1b99cad0a0d2
|
||||
timeCreated: 1508469304
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,19 @@
|
||||
namespace Mapbox.Unity.MeshGeneration.Modifiers
|
||||
{
|
||||
using UnityEngine;
|
||||
using Mapbox.Unity.MeshGeneration.Components;
|
||||
using Mapbox.Unity.MeshGeneration.Data;
|
||||
|
||||
[CreateAssetMenu(menuName = "Mapbox/Modifiers/Noise Offset Modifier")]
|
||||
public class NoiseOffsetModifier : GameObjectModifier
|
||||
{
|
||||
public override void Run(VectorEntity ve, UnityTile tile)
|
||||
{
|
||||
//create a very small random offset to avoid z-fighting
|
||||
Vector3 randomOffset = Random.insideUnitSphere;
|
||||
randomOffset *= 0.01f;
|
||||
|
||||
ve.GameObject.transform.localPosition += randomOffset;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fa6f0c874a917480ab42ea3d79edf819
|
||||
timeCreated: 1532995020
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,116 @@
|
||||
namespace Mapbox.Unity.MeshGeneration.Modifiers
|
||||
{
|
||||
using UnityEngine;
|
||||
using Mapbox.Unity.MeshGeneration.Data;
|
||||
using Mapbox.Unity.MeshGeneration.Components;
|
||||
using Mapbox.Unity.MeshGeneration.Interfaces;
|
||||
using System.Collections.Generic;
|
||||
using Mapbox.Unity.Map;
|
||||
using System;
|
||||
|
||||
[CreateAssetMenu(menuName = "Mapbox/Modifiers/Prefab Modifier")]
|
||||
public class PrefabModifier : GameObjectModifier
|
||||
{
|
||||
private Dictionary<GameObject, GameObject> _objects;
|
||||
[SerializeField]
|
||||
private SpawnPrefabOptions _options;
|
||||
private List<GameObject> _prefabList = new List<GameObject>();
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
if (_objects == null)
|
||||
{
|
||||
_objects = new Dictionary<GameObject, GameObject>();
|
||||
}
|
||||
}
|
||||
|
||||
public override void SetProperties(ModifierProperties properties)
|
||||
{
|
||||
_options = (SpawnPrefabOptions)properties;
|
||||
_options.PropertyHasChanged += UpdateModifier;
|
||||
}
|
||||
|
||||
public override void Run(VectorEntity ve, UnityTile tile)
|
||||
{
|
||||
if (_options.prefab == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
GameObject go = null;
|
||||
|
||||
if (_objects.ContainsKey(ve.GameObject))
|
||||
{
|
||||
go = _objects[ve.GameObject];
|
||||
}
|
||||
else
|
||||
{
|
||||
go = Instantiate(_options.prefab);
|
||||
_prefabList.Add(go);
|
||||
_objects.Add(ve.GameObject, go);
|
||||
go.transform.SetParent(ve.GameObject.transform, false);
|
||||
}
|
||||
|
||||
PositionScaleRectTransform(ve, tile, go);
|
||||
|
||||
if (_options.AllPrefabsInstatiated != null)
|
||||
{
|
||||
_options.AllPrefabsInstatiated(_prefabList);
|
||||
}
|
||||
}
|
||||
|
||||
public void PositionScaleRectTransform(VectorEntity ve, UnityTile tile, GameObject go)
|
||||
{
|
||||
RectTransform goRectTransform;
|
||||
IFeaturePropertySettable settable = null;
|
||||
var centroidVector = new Vector3();
|
||||
foreach (var point in ve.Feature.Points[0])
|
||||
{
|
||||
centroidVector += point;
|
||||
}
|
||||
centroidVector = centroidVector / ve.Feature.Points[0].Count;
|
||||
|
||||
go.name = ve.Feature.Data.Id.ToString();
|
||||
|
||||
goRectTransform = go.GetComponent<RectTransform>();
|
||||
if (goRectTransform == null)
|
||||
{
|
||||
go.transform.localPosition = centroidVector;
|
||||
if (_options.scaleDownWithWorld)
|
||||
{
|
||||
go.transform.localScale = _options.prefab.transform.localScale * (tile.TileScale);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
goRectTransform.anchoredPosition3D = centroidVector;
|
||||
if (_options.scaleDownWithWorld)
|
||||
{
|
||||
goRectTransform.localScale = _options.prefab.transform.localScale * (tile.TileScale);
|
||||
}
|
||||
}
|
||||
|
||||
//go.transform.localScale = Constants.Math.Vector3One;
|
||||
|
||||
settable = go.GetComponent<IFeaturePropertySettable>();
|
||||
if (settable != null)
|
||||
{
|
||||
settable.Set(ve.Feature.Properties);
|
||||
}
|
||||
}
|
||||
|
||||
public override void ClearCaches()
|
||||
{
|
||||
base.ClearCaches();
|
||||
foreach (var gameObject in _objects.Values)
|
||||
{
|
||||
Destroy(gameObject);
|
||||
}
|
||||
|
||||
foreach (var gameObject in _prefabList)
|
||||
{
|
||||
Destroy(gameObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dd8aacc1880ff504d9531b4d61c86458
|
||||
timeCreated: 1494349551
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,154 @@
|
||||
// HACK:
|
||||
// This will work out of the box, but it's intended to be an example of how to approach
|
||||
// procedural decoration like this.
|
||||
// A better approach would be to operate on the geometry itself.
|
||||
|
||||
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/Spawn Inside Modifier")]
|
||||
public class SpawnInsideModifier : GameObjectModifier
|
||||
{
|
||||
[SerializeField]
|
||||
int _spawnRateInSquareMeters;
|
||||
|
||||
[SerializeField]
|
||||
int _maxSpawn = 1000;
|
||||
|
||||
[SerializeField]
|
||||
GameObject[] _prefabs;
|
||||
|
||||
[SerializeField]
|
||||
LayerMask _layerMask;
|
||||
|
||||
[SerializeField]
|
||||
bool _scaleDownWithWorld;
|
||||
|
||||
[SerializeField]
|
||||
bool _randomizeScale;
|
||||
|
||||
[SerializeField]
|
||||
bool _randomizeRotation;
|
||||
|
||||
int _spawnedCount;
|
||||
|
||||
private Dictionary<GameObject, List<GameObject>> _objects;
|
||||
private Queue<GameObject> _pool;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
if (_objects == null || _pool == null)
|
||||
{
|
||||
_objects = new Dictionary<GameObject, List<GameObject>>();
|
||||
_pool = new Queue<GameObject>();
|
||||
}
|
||||
}
|
||||
|
||||
public override void Run(VectorEntity ve, UnityTile tile)
|
||||
{
|
||||
_spawnedCount = 0;
|
||||
var collider = ve.GameObject.GetComponent<Collider>();
|
||||
var bounds = collider.bounds;
|
||||
var center = bounds.center;
|
||||
center.y = 0;
|
||||
|
||||
var area = (int)(bounds.size.x * bounds.size.z);
|
||||
int spawnCount = Mathf.Min(area / _spawnRateInSquareMeters, _maxSpawn);
|
||||
while (_spawnedCount < spawnCount)
|
||||
{
|
||||
var x = UnityEngine.Random.Range(-bounds.extents.x, bounds.extents.x);
|
||||
var z = UnityEngine.Random.Range(-bounds.extents.z, bounds.extents.z);
|
||||
var ray = new Ray(bounds.center + new Vector3(x, 100, z), Vector3.down * 2000);
|
||||
|
||||
RaycastHit hit;
|
||||
//Debug.DrawRay(ray.origin, ray.direction * 1000, Color.yellow, 1000);
|
||||
if (Physics.Raycast(ray, out hit, 150, _layerMask))
|
||||
{
|
||||
//Debug.DrawLine(ray.origin, hit.point, Color.red, 1000);
|
||||
var index = UnityEngine.Random.Range(0, _prefabs.Length);
|
||||
var transform = GetObject(index, ve.GameObject).transform;
|
||||
transform.position = hit.point;
|
||||
if (_randomizeRotation)
|
||||
{
|
||||
transform.localEulerAngles = new Vector3(0, UnityEngine.Random.Range(-180f, 180f), 0);
|
||||
}
|
||||
if (!_scaleDownWithWorld)
|
||||
{
|
||||
transform.localScale = Vector3.one / tile.TileScale;
|
||||
}
|
||||
|
||||
if (_randomizeScale)
|
||||
{
|
||||
var scale = transform.localScale;
|
||||
var y = UnityEngine.Random.Range(scale.y * .7f, scale.y * 1.3f);
|
||||
scale.y = y;
|
||||
transform.localScale = scale;
|
||||
}
|
||||
|
||||
}
|
||||
_spawnedCount++;
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnPoolItem(VectorEntity vectorEntity)
|
||||
{
|
||||
if(_objects.ContainsKey(vectorEntity.GameObject))
|
||||
{
|
||||
foreach (var item in _objects[vectorEntity.GameObject])
|
||||
{
|
||||
item.SetActive(false);
|
||||
_pool.Enqueue(item);
|
||||
}
|
||||
|
||||
_objects[vectorEntity.GameObject].Clear();
|
||||
_objects.Remove(vectorEntity.GameObject);
|
||||
}
|
||||
}
|
||||
|
||||
public override void ClearCaches()
|
||||
{
|
||||
foreach (var go in _pool)
|
||||
{
|
||||
Destroy(go);
|
||||
}
|
||||
_pool.Clear();
|
||||
foreach (var tileObject in _objects)
|
||||
{
|
||||
foreach (var go in tileObject.Value)
|
||||
{
|
||||
Destroy(go);
|
||||
}
|
||||
}
|
||||
_objects.Clear();
|
||||
}
|
||||
|
||||
private GameObject GetObject(int index, GameObject go)
|
||||
{
|
||||
GameObject ob;
|
||||
if (_pool.Count > 0)
|
||||
{
|
||||
ob = _pool.Dequeue();
|
||||
ob.SetActive(true);
|
||||
ob.transform.SetParent(go.transform);
|
||||
}
|
||||
else
|
||||
{
|
||||
ob = ((GameObject)Instantiate(_prefabs[index], go.transform, false));
|
||||
}
|
||||
if (_objects.ContainsKey(go))
|
||||
{
|
||||
_objects[go].Add(ob);
|
||||
}
|
||||
else
|
||||
{
|
||||
_objects.Add(go, new List<GameObject>() { ob });
|
||||
}
|
||||
return ob;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 420705c61d1b6435c8aa7b1cbd0ce80a
|
||||
timeCreated: 1499974418
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,18 @@
|
||||
namespace Mapbox.Unity.MeshGeneration.Modifiers
|
||||
{
|
||||
using UnityEngine;
|
||||
using Mapbox.Unity.MeshGeneration.Components;
|
||||
using Mapbox.Unity.MeshGeneration.Data;
|
||||
|
||||
[CreateAssetMenu(menuName = "Mapbox/Modifiers/Tag Modifier")]
|
||||
public class TagModifier : GameObjectModifier
|
||||
{
|
||||
[SerializeField]
|
||||
private string _tag;
|
||||
|
||||
public override void Run(VectorEntity ve, UnityTile tile)
|
||||
{
|
||||
ve.GameObject.tag = _tag;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 05ccea17608e94fb994579409c52fb80
|
||||
timeCreated: 1513280866
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,55 @@
|
||||
namespace Mapbox.Unity.MeshGeneration.Modifiers
|
||||
{
|
||||
using UnityEngine;
|
||||
using Mapbox.Unity.MeshGeneration.Components;
|
||||
using Mapbox.Unity.MeshGeneration.Data;
|
||||
using System;
|
||||
|
||||
/// <summary>
|
||||
/// Texture Modifier is a basic modifier which simply adds a TextureSelector script to the features.
|
||||
/// Logic is all pushed into this TextureSelector mono behaviour to make it's easier to change it in runtime.
|
||||
/// </summary>
|
||||
|
||||
[Obsolete("Texture Modifier is obsolete. Please use Material Modifier.")]
|
||||
[CreateAssetMenu(menuName = "Mapbox/Modifiers/Texture Modifier")]
|
||||
public class TextureModifier : GameObjectModifier
|
||||
{
|
||||
[SerializeField]
|
||||
private bool _textureTop;
|
||||
[SerializeField]
|
||||
private bool _useSatelliteTexture;
|
||||
[SerializeField]
|
||||
private Material[] _topMaterials;
|
||||
|
||||
[SerializeField]
|
||||
private bool _textureSides;
|
||||
[SerializeField]
|
||||
private Material[] _sideMaterials;
|
||||
|
||||
public override void Run(VectorEntity ve, UnityTile tile)
|
||||
{
|
||||
var _meshRenderer = ve.MeshRenderer;
|
||||
if (_textureSides && _sideMaterials.Length > 0)
|
||||
{
|
||||
_meshRenderer.materials = new Material[2]
|
||||
{
|
||||
_topMaterials[UnityEngine.Random.Range(0, _topMaterials.Length)],
|
||||
_sideMaterials[UnityEngine.Random.Range(0, _sideMaterials.Length)]
|
||||
};
|
||||
}
|
||||
else if (_textureTop)
|
||||
{
|
||||
_meshRenderer.materials = new Material[1]
|
||||
{
|
||||
_topMaterials[UnityEngine.Random.Range(0, _topMaterials.Length)]
|
||||
};
|
||||
}
|
||||
|
||||
if (_useSatelliteTexture)
|
||||
{
|
||||
_meshRenderer.materials[0].mainTexture = tile.GetRasterData();
|
||||
_meshRenderer.materials[0].mainTextureScale = new Vector2(1f, 1f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7d86f7dc89b3e33408563e4ada9d1c74
|
||||
timeCreated: 1478553093
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,32 @@
|
||||
namespace Mapbox.Unity.MeshGeneration.Modifiers
|
||||
{
|
||||
using UnityEngine;
|
||||
using Mapbox.Unity.MeshGeneration.Components;
|
||||
using Mapbox.Unity.MeshGeneration.Data;
|
||||
|
||||
/// <summary>
|
||||
/// Texture Modifier is a basic modifier which simply adds a TextureSelector script to the features.
|
||||
/// Logic is all pushed into this TextureSelector mono behaviour to make it's easier to change it in runtime.
|
||||
/// </summary>
|
||||
[CreateAssetMenu(menuName = "Mapbox/Modifiers/Texture MonoBehaviour Modifier")]
|
||||
public class TextureMonoBehaviourModifier : GameObjectModifier
|
||||
{
|
||||
[SerializeField]
|
||||
private bool _textureTop;
|
||||
[SerializeField]
|
||||
private bool _useSatelliteTexture;
|
||||
[SerializeField]
|
||||
private Material[] _topMaterials;
|
||||
|
||||
[SerializeField]
|
||||
private bool _textureSides;
|
||||
[SerializeField]
|
||||
private Material[] _sideMaterials;
|
||||
|
||||
public override void Run(VectorEntity ve, UnityTile tile)
|
||||
{
|
||||
var ts = ve.GameObject.AddComponent<TextureSelector>();
|
||||
ts.Initialize(ve, _textureTop, _useSatelliteTexture, _topMaterials, _textureSides, _sideMaterials);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 13b8ea9d54863b642b04d243f6e54eb6
|
||||
timeCreated: 1478553093
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,10 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Mapbox.Unity.MeshGeneration.Modifiers
|
||||
{
|
||||
public interface IReplaceable
|
||||
{
|
||||
HashSet<IReplacementCriteria> Criteria { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5548109d5836140a0bc45d1b5fc881e2
|
||||
timeCreated: 1526594257
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace Mapbox.Unity.MeshGeneration.Modifiers
|
||||
{
|
||||
using Mapbox.Unity.MeshGeneration.Data;
|
||||
|
||||
public interface IReplacementCriteria
|
||||
{
|
||||
bool ShouldReplaceFeature(VectorFeatureUnity feature);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f3eb10303f9b149e5980a3508a9f1039
|
||||
timeCreated: 1526586060
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,261 @@
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using Mapbox.Map;
|
||||
using Mapbox.Unity.MeshGeneration.Modifiers;
|
||||
using Mapbox.Unity.MeshGeneration.Data;
|
||||
using Mapbox.Unity.MeshGeneration.Components;
|
||||
|
||||
namespace Mapbox.Unity.MeshGeneration.Modifiers
|
||||
{
|
||||
/// <summary>
|
||||
/// Merged Modifier Stack, just like regular Modifier stack, creates a game object from features. But the difference is, regular modifier stack creates a game object for each given faeture meanwhile Merged Modifier Stack merges meshes and creates one game object for all features (until the 65k vertex limit).
|
||||
/// It has extremely higher performance compared to regular modifier stack but since it merged all entities together, it also loses all individual entity data & makes it harder to interact with them.
|
||||
/// It pools and merges objects based on the tile contains them.
|
||||
/// </summary>
|
||||
[CreateAssetMenu(menuName = "Mapbox/Modifiers/Merged Modifier Stack")]
|
||||
public class MergedModifierStack : ModifierStackBase
|
||||
{
|
||||
//[NodeEditorElement("Mesh Modifiers")] public List<MeshModifier> MeshModifiers;
|
||||
//[NodeEditorElement("Game Object Modifiers")] public List<GameObjectModifier> GoModifiers;
|
||||
|
||||
private Dictionary<UnityTile, int> _cacheVertexCount = new Dictionary<UnityTile, int>();
|
||||
private Dictionary<UnityTile, List<MeshData>> _cached = new Dictionary<UnityTile, List<MeshData>>();
|
||||
private Dictionary<UnityTile, int> _buildingCount = new Dictionary<UnityTile, int>();
|
||||
|
||||
private Dictionary<UnityTile, List<VectorEntity>> _activeObjects = new Dictionary<UnityTile, List<VectorEntity>>();
|
||||
private MeshData _tempMeshData;
|
||||
private MeshFilter _tempMeshFilter;
|
||||
private GameObject _tempGameObject;
|
||||
private VectorEntity _tempVectorEntity;
|
||||
private MeshData _temp2MeshData;
|
||||
private ObjectPool<VectorEntity> _pool;
|
||||
private ObjectPool<List<VectorEntity>> _listPool;
|
||||
private ObjectPool<List<MeshData>> _meshDataPool;
|
||||
|
||||
private int _counter, _counter2;
|
||||
|
||||
protected virtual void OnEnable()
|
||||
{
|
||||
//we'll use this to concat building data until it reaches 65000 verts
|
||||
_pool = new ObjectPool<VectorEntity>(() =>
|
||||
{
|
||||
_tempGameObject = new GameObject();
|
||||
_tempMeshFilter = _tempGameObject.AddComponent<MeshFilter>();
|
||||
_tempMeshFilter.mesh.MarkDynamic();
|
||||
_tempVectorEntity = new VectorEntity()
|
||||
{
|
||||
GameObject = _tempGameObject,
|
||||
Transform = _tempGameObject.transform,
|
||||
MeshFilter = _tempMeshFilter,
|
||||
MeshRenderer = _tempGameObject.AddComponent<MeshRenderer>(),
|
||||
Mesh = _tempMeshFilter.mesh
|
||||
};
|
||||
return _tempVectorEntity;
|
||||
});
|
||||
_listPool = new ObjectPool<List<VectorEntity>>(() => { return new List<VectorEntity>(); });
|
||||
_meshDataPool = new ObjectPool<List<MeshData>>(() => { return new List<MeshData>(); });
|
||||
_tempMeshData = new MeshData();
|
||||
}
|
||||
|
||||
public override void OnUnregisterTile(UnityTile tile)
|
||||
{
|
||||
//removing all caches
|
||||
if (_activeObjects.ContainsKey(tile))
|
||||
{
|
||||
_counter = _activeObjects[tile].Count;
|
||||
for (int i = 0; i < _counter; i++)
|
||||
{
|
||||
if (null != _activeObjects[tile][i].GameObject)
|
||||
{
|
||||
_activeObjects[tile][i].GameObject.SetActive(false);
|
||||
}
|
||||
_pool.Put(_activeObjects[tile][i]);
|
||||
}
|
||||
_activeObjects[tile].Clear();
|
||||
//pooling these lists as they'll reused anyway, saving hundreds of list instantiations
|
||||
_listPool.Put(_activeObjects[tile]);
|
||||
_activeObjects.Remove(tile);
|
||||
}
|
||||
|
||||
//reset all counters
|
||||
if (_cacheVertexCount.ContainsKey(tile))
|
||||
{
|
||||
_cacheVertexCount.Remove(tile);
|
||||
}
|
||||
if (_cached.ContainsKey(tile))
|
||||
{
|
||||
_cached[tile].Clear();
|
||||
_meshDataPool.Put(_cached[tile]);
|
||||
_cached.Remove(tile);
|
||||
}
|
||||
if (_buildingCount.ContainsKey(tile))
|
||||
{
|
||||
_buildingCount.Remove(tile);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
//init is also used for reloading map/ location change, so reseting everything here
|
||||
_cacheVertexCount.Clear();
|
||||
_cached.Clear();
|
||||
_buildingCount.Clear();
|
||||
_pool.Clear();
|
||||
|
||||
_counter = MeshModifiers.Count;
|
||||
for (int i = 0; i < _counter; i++)
|
||||
{
|
||||
MeshModifiers[i].Initialize();
|
||||
}
|
||||
|
||||
_counter = GoModifiers.Count;
|
||||
for (int i = 0; i < _counter; i++)
|
||||
{
|
||||
GoModifiers[i].Initialize();
|
||||
}
|
||||
}
|
||||
|
||||
public override GameObject Execute(UnityTile tile, VectorFeatureUnity feature, MeshData meshData, GameObject parent = null, string type = "")
|
||||
{
|
||||
base.Execute(tile, feature, meshData, parent, type);
|
||||
|
||||
if (!_cacheVertexCount.ContainsKey(tile))
|
||||
{
|
||||
_cacheVertexCount.Add(tile, 0);
|
||||
_cached.Add(tile, _meshDataPool.GetObject());
|
||||
_buildingCount.Add(tile, 0);
|
||||
}
|
||||
|
||||
_buildingCount[tile]++;
|
||||
_counter = MeshModifiers.Count;
|
||||
for (int i = 0; i < _counter; i++)
|
||||
{
|
||||
if (MeshModifiers[i] != null && MeshModifiers[i].Active)
|
||||
{
|
||||
MeshModifiers[i].Run(feature, meshData, tile);
|
||||
}
|
||||
}
|
||||
|
||||
GameObject go = null;
|
||||
//65000 is the vertex limit for meshes, keep stashing it until that
|
||||
_counter = meshData.Vertices.Count;
|
||||
if (_cacheVertexCount[tile] + _counter < 65000)
|
||||
{
|
||||
_cacheVertexCount[tile] += _counter;
|
||||
_cached[tile].Add(meshData);
|
||||
}
|
||||
else
|
||||
{
|
||||
go = End(tile, parent, type);
|
||||
}
|
||||
|
||||
return go;
|
||||
}
|
||||
|
||||
|
||||
public GameObject End(UnityTile tile, GameObject parent, string name = "")
|
||||
{
|
||||
var c2 = 0;
|
||||
if (_cached.ContainsKey(tile))
|
||||
{
|
||||
_tempMeshData.Clear();
|
||||
|
||||
//concat mesh data into _tempMeshData
|
||||
_counter = _cached[tile].Count;
|
||||
for (int i = 0; i < _counter; i++)
|
||||
{
|
||||
_temp2MeshData = _cached[tile][i];
|
||||
if (_temp2MeshData.Vertices.Count <= 3)
|
||||
continue;
|
||||
|
||||
var st = _tempMeshData.Vertices.Count;
|
||||
_tempMeshData.Vertices.AddRange(_temp2MeshData.Vertices);
|
||||
_tempMeshData.Normals.AddRange(_temp2MeshData.Normals);
|
||||
|
||||
c2 = _temp2MeshData.UV.Count;
|
||||
for (int j = 0; j < c2; j++)
|
||||
{
|
||||
if (_tempMeshData.UV.Count <= j)
|
||||
{
|
||||
_tempMeshData.UV.Add(new List<Vector2>(_temp2MeshData.UV[j].Count));
|
||||
}
|
||||
}
|
||||
|
||||
c2 = _temp2MeshData.UV.Count;
|
||||
for (int j = 0; j < c2; j++)
|
||||
{
|
||||
_tempMeshData.UV[j].AddRange(_temp2MeshData.UV[j]);
|
||||
}
|
||||
|
||||
c2 = _temp2MeshData.Triangles.Count;
|
||||
for (int j = 0; j < c2; j++)
|
||||
{
|
||||
if (_tempMeshData.Triangles.Count <= j)
|
||||
{
|
||||
_tempMeshData.Triangles.Add(new List<int>(_temp2MeshData.Triangles[j].Count));
|
||||
}
|
||||
}
|
||||
|
||||
for (int j = 0; j < c2; j++)
|
||||
{
|
||||
for (int k = 0; k < _temp2MeshData.Triangles[j].Count; k++)
|
||||
{
|
||||
_tempMeshData.Triangles[j].Add(_temp2MeshData.Triangles[j][k] + st);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//update pooled vector entity with new data
|
||||
if (_tempMeshData.Vertices.Count > 3)
|
||||
{
|
||||
_cached[tile].Clear();
|
||||
_cacheVertexCount[tile] = 0;
|
||||
_tempVectorEntity = null;
|
||||
_tempVectorEntity = _pool.GetObject();
|
||||
_tempVectorEntity.GameObject.SetActive(true);
|
||||
_tempVectorEntity.Mesh.Clear();
|
||||
|
||||
_tempVectorEntity.GameObject.name = name;
|
||||
_tempVectorEntity.Mesh.subMeshCount = _tempMeshData.Triangles.Count;
|
||||
_tempVectorEntity.Mesh.SetVertices(_tempMeshData.Vertices);
|
||||
_tempVectorEntity.Mesh.SetNormals(_tempMeshData.Normals);
|
||||
|
||||
_counter = _tempMeshData.Triangles.Count;
|
||||
for (int i = 0; i < _counter; i++)
|
||||
{
|
||||
_tempVectorEntity.Mesh.SetTriangles(_tempMeshData.Triangles[i], i);
|
||||
}
|
||||
|
||||
_counter = _tempMeshData.UV.Count;
|
||||
for (int i = 0; i < _counter; i++)
|
||||
{
|
||||
_tempVectorEntity.Mesh.SetUVs(i, _tempMeshData.UV[i]);
|
||||
}
|
||||
|
||||
_tempVectorEntity.GameObject.transform.SetParent(tile.transform, false);
|
||||
|
||||
if (!_activeObjects.ContainsKey(tile))
|
||||
{
|
||||
_activeObjects.Add(tile, _listPool.GetObject());
|
||||
}
|
||||
_activeObjects[tile].Add(_tempVectorEntity);
|
||||
|
||||
_counter = GoModifiers.Count;
|
||||
for (int i = 0; i < _counter; i++)
|
||||
{
|
||||
if (GoModifiers[i].Active)
|
||||
{
|
||||
GoModifiers[i].Run(_tempVectorEntity, tile);
|
||||
}
|
||||
}
|
||||
|
||||
return _tempVectorEntity.GameObject;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ef7239c46ddb6ab469994291c9603c01
|
||||
timeCreated: 1491774659
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,14 @@
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using Mapbox.Unity.MeshGeneration.Modifiers;
|
||||
using System.Collections.Generic;
|
||||
|
||||
public class MeshGenerationBase : MeshModifier, IReplaceable
|
||||
{
|
||||
public HashSet<IReplacementCriteria> Criteria { get; set; }
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
Criteria = new HashSet<IReplacementCriteria>();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b5a0bb09f203344eab30606cb0f57438
|
||||
timeCreated: 1526508352
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,47 @@
|
||||
namespace Mapbox.Unity.MeshGeneration.Modifiers
|
||||
{
|
||||
using Mapbox.Unity.MeshGeneration.Data;
|
||||
|
||||
public enum ModifierType
|
||||
{
|
||||
Preprocess,
|
||||
Postprocess
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Mesh Data class and Mesh Modifier
|
||||
/// MeshData class is used to keep data required for a mesh.Mesh modifiers recieve raw feature data and a mesh modifier,
|
||||
/// generate their data and keep it inside MeshData. i.e.PolygonMeshModifier is responsible for triangulating the polygon,
|
||||
/// so it calculates the triangle indices using the vertices and save it into MeshData.Triangles list.There's no control
|
||||
/// over which field a mesh modifier fills or overrides but some of the basic mesh modifier goes like this;
|
||||
/// Polygon mesh modifier - Vertices and triangles
|
||||
/// Line Mesh modifier - Vertices, triangles and uvs
|
||||
/// UV modifier - uvs(only used with polygon mesh modifier)
|
||||
/// height modifier - vertices(adds new points), triangles(for side walls), uvs(for side walls)
|
||||
/// After running all mesh modifiers, mesh data is expected to have at least vertices and triangles set as
|
||||
/// they are the bare minimum to create a unity mesh object.
|
||||
/// So the main purpose of the mesh modifiers is to fill up the mesh data class but they aren't limited to that either.
|
||||
/// You can always create gameobjects and debug lines/spheres inside them for debugging purposes whenever you have a problem.
|
||||
/// MeshData class also has some extra fields inside for data transfer purposes(between modifiers). i.e.Edges list
|
||||
/// inside mesh data isn't used for mesh itself, but it's calculated by PolygonMeshModifier and used by HeightModifier
|
||||
/// so to avoid calculating it twice, we're keeping it in the mesh data object. You can also extend mesh data like this
|
||||
/// if you ever need to save data or share information between mesh modifiers.
|
||||
/// We fully expect developers to create their own mesh modifiers to customize the look of their world.It would probably
|
||||
/// take a little experience with mesh generation to be able to do this but it's the suggested way to create
|
||||
/// custom world objects, like blobly toonish buildings, or wobbly roads.
|
||||
/// </summary>
|
||||
public class MeshModifier : ModifierBase
|
||||
{
|
||||
public virtual ModifierType Type { get { return ModifierType.Preprocess; } }
|
||||
|
||||
public virtual void Run(VectorFeatureUnity feature, MeshData md, float scale)
|
||||
{
|
||||
Run(feature, md);
|
||||
}
|
||||
|
||||
public virtual void Run(VectorFeatureUnity feature, MeshData md, UnityTile tile = null)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d61d35af106e04d44b5fc8b635f4dc55
|
||||
timeCreated: 1478551903
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 44adfb267cbe6d449aa09f91c8851834
|
||||
folderAsset: yes
|
||||
timeCreated: 1485207986
|
||||
licenseType: Pro
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,413 @@
|
||||
namespace Mapbox.Unity.MeshGeneration.Modifiers
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using Mapbox.Unity.MeshGeneration.Data;
|
||||
using System;
|
||||
|
||||
[CreateAssetMenu(menuName = "Mapbox/Modifiers/Smooth Height for Buildings Modifier")]
|
||||
public class ChamferHeightModifier : MeshModifier
|
||||
{
|
||||
[SerializeField]
|
||||
[Tooltip("Flatten top polygons to prevent unwanted slanted roofs because of the bumpy terrain")]
|
||||
private bool _flatTops;
|
||||
[SerializeField]
|
||||
[Tooltip("Fixed height value for ForceHeight option")]
|
||||
private float _height;
|
||||
[SerializeField]
|
||||
[Tooltip("Fix all features to certain height, suggested to be used for pushing roads above terrain level to prevent z-fighting.")]
|
||||
private bool _forceHeight;
|
||||
[SerializeField]
|
||||
[Range(0.1f,2)]
|
||||
[Tooltip("Chamfer width value")]
|
||||
private float _offset = 0.2f;
|
||||
|
||||
public override ModifierType Type { get { return ModifierType.Preprocess; } }
|
||||
|
||||
public override void Run(VectorFeatureUnity feature, MeshData md, UnityTile tile = null)
|
||||
{
|
||||
if (md.Vertices.Count == 0 || feature == null || feature.Points.Count < 1)
|
||||
return;
|
||||
|
||||
var minHeight = 0f;
|
||||
float hf = _height;
|
||||
|
||||
if (!_forceHeight)
|
||||
{
|
||||
GetHeightData(feature, tile.TileScale, ref minHeight, ref hf);
|
||||
}
|
||||
|
||||
var max = md.Vertices[0].y;
|
||||
var min = md.Vertices[0].y;
|
||||
if (_flatTops)
|
||||
{
|
||||
FlattenTops(md, minHeight, ref hf, ref max, ref min);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < md.Vertices.Count; i++)
|
||||
{
|
||||
md.Vertices[i] = new Vector3(md.Vertices[i].x, md.Vertices[i].y + minHeight + hf, md.Vertices[i].z);
|
||||
}
|
||||
}
|
||||
|
||||
var originalVertexCount = md.Vertices.Count;
|
||||
Chamfer(feature, md, tile);
|
||||
|
||||
Sides(feature, md, hf, originalVertexCount);
|
||||
}
|
||||
|
||||
private void Sides(VectorFeatureUnity feature, MeshData meshData, float hf, int originalVertexCount)
|
||||
{
|
||||
|
||||
|
||||
float d = 0f;
|
||||
Vector3 v1;
|
||||
Vector3 v2 = Mapbox.Unity.Constants.Math.Vector3Zero;
|
||||
int ind = 0;
|
||||
|
||||
var wallTri = new List<int>();
|
||||
var wallUv = new List<Vector2>();
|
||||
meshData.Vertices.Add(new Vector3(meshData.Vertices[originalVertexCount - 1].x, meshData.Vertices[originalVertexCount - 1].y - hf, meshData.Vertices[originalVertexCount - 1].z));
|
||||
meshData.Tangents.Add(meshData.Tangents[originalVertexCount - 1]);
|
||||
wallUv.Add(new Vector2(0, -hf));
|
||||
meshData.Normals.Add(meshData.Normals[originalVertexCount - 1]);
|
||||
|
||||
for (int i = 0; i < meshData.Edges.Count; i += 2)
|
||||
{
|
||||
v1 = meshData.Vertices[meshData.Edges[i]];
|
||||
v2 = meshData.Vertices[meshData.Edges[i + 1]];
|
||||
ind = meshData.Vertices.Count;
|
||||
meshData.Vertices.Add(v1);
|
||||
meshData.Vertices.Add(v2);
|
||||
meshData.Vertices.Add(new Vector3(v1.x, v1.y - hf, v1.z));
|
||||
meshData.Vertices.Add(new Vector3(v2.x, v2.y - hf, v2.z));
|
||||
|
||||
meshData.Normals.Add(meshData.Normals[meshData.Edges[i]]);
|
||||
meshData.Normals.Add(meshData.Normals[meshData.Edges[i + 1]]);
|
||||
meshData.Normals.Add(meshData.Normals[meshData.Edges[i]]);
|
||||
meshData.Normals.Add(meshData.Normals[meshData.Edges[i + 1]]);
|
||||
|
||||
meshData.Tangents.Add(v2 - v1.normalized);
|
||||
meshData.Tangents.Add(v2 - v1.normalized);
|
||||
meshData.Tangents.Add(v2 - v1.normalized);
|
||||
meshData.Tangents.Add(v2 - v1.normalized);
|
||||
|
||||
d = (v2 - v1).magnitude;
|
||||
|
||||
wallUv.Add(new Vector2(0, 0));
|
||||
wallUv.Add(new Vector2(d, 0));
|
||||
wallUv.Add(new Vector2(0, -hf));
|
||||
wallUv.Add(new Vector2(d, -hf));
|
||||
|
||||
wallTri.Add(ind);
|
||||
wallTri.Add(ind + 1);
|
||||
wallTri.Add(ind + 2);
|
||||
|
||||
wallTri.Add(ind + 1);
|
||||
wallTri.Add(ind + 3);
|
||||
wallTri.Add(ind + 2);
|
||||
}
|
||||
|
||||
meshData.Triangles.Add(wallTri);
|
||||
meshData.UV[0].AddRange(wallUv);
|
||||
}
|
||||
|
||||
private static void FlattenTops(MeshData meshData, float minHeight, ref float hf, ref float max, ref float min)
|
||||
{
|
||||
for (int i = 0; i < meshData.Vertices.Count; i++)
|
||||
{
|
||||
if (meshData.Vertices[i].y > max)
|
||||
max = meshData.Vertices[i].y;
|
||||
else if (meshData.Vertices[i].y < min)
|
||||
min = meshData.Vertices[i].y;
|
||||
}
|
||||
for (int i = 0; i < meshData.Vertices.Count; i++)
|
||||
{
|
||||
meshData.Vertices[i] = new Vector3(meshData.Vertices[i].x, max + minHeight + hf, meshData.Vertices[i].z);
|
||||
}
|
||||
hf += max - min;
|
||||
}
|
||||
|
||||
private static void GetHeightData(VectorFeatureUnity feature, float scale, ref float minHeight, ref float hf)
|
||||
{
|
||||
if (feature.Properties.ContainsKey("height"))
|
||||
{
|
||||
hf = Convert.ToSingle(feature.Properties["height"]);
|
||||
hf *= scale;
|
||||
if (feature.Properties.ContainsKey("min_height"))
|
||||
{
|
||||
minHeight = Convert.ToSingle(feature.Properties["min_height"]) * scale;
|
||||
hf -= minHeight;
|
||||
}
|
||||
|
||||
}
|
||||
if (feature.Properties.ContainsKey("ele"))
|
||||
{
|
||||
hf = Convert.ToSingle(feature.Properties["ele"]);
|
||||
hf *= scale;
|
||||
}
|
||||
}
|
||||
|
||||
public void Chamfer(VectorFeatureUnity feature, MeshData md, UnityTile tile = null)
|
||||
{
|
||||
if (md.Vertices.Count == 0 || feature.Points.Count < 1)
|
||||
return;
|
||||
|
||||
List<Vector3> newVertices = new List<Vector3>();
|
||||
List<Vector2> newUV = new List<Vector2>();
|
||||
md.Normals.Clear();
|
||||
md.Edges.Clear();
|
||||
md.Tangents.Clear();
|
||||
|
||||
for (int t = 0; t < md.Triangles[0].Count; t++)
|
||||
{
|
||||
md.Triangles[0][t] *= 3;
|
||||
}
|
||||
|
||||
var next = 0; var current = 0; var prev = 0;
|
||||
Vector3 v1, v2, n1, n2, pij1, pij2, pjk1, pjk2;
|
||||
Vector3 poi, close1, close2;
|
||||
|
||||
var start = 0;
|
||||
for (int i = 0; i < feature.Points.Count; i++)
|
||||
{
|
||||
var count = feature.Points[i].Count;
|
||||
var cst = newVertices.Count;
|
||||
for (int j = 0; j < count; j++)
|
||||
{
|
||||
if (j == count - 1)
|
||||
{
|
||||
newVertices.Add(newVertices[cst]);
|
||||
newVertices.Add(newVertices[cst + 1]);
|
||||
newVertices.Add(newVertices[cst + 2]);
|
||||
newUV.Add(newUV[cst]);
|
||||
newUV.Add(newUV[cst + 1]);
|
||||
newUV.Add(newUV[cst + 2]);
|
||||
md.Normals.Add(md.Normals[cst]);
|
||||
md.Normals.Add(md.Normals[cst + 1]);
|
||||
md.Normals.Add(md.Normals[cst + 2]);
|
||||
|
||||
md.Tangents.Add(md.Tangents[cst]);
|
||||
md.Tangents.Add(md.Tangents[cst + 1]);
|
||||
md.Tangents.Add(md.Tangents[cst + 2]);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
current = start + j;
|
||||
if (j > 0)
|
||||
next = start + j - 1;
|
||||
else
|
||||
next = start + j - 1 + count - 1; //another -1 as last item equals first
|
||||
prev = start + j + 1;
|
||||
|
||||
|
||||
v1 = new Vector3(
|
||||
md.Vertices[current].x - md.Vertices[next].x, 0,
|
||||
md.Vertices[current].z - md.Vertices[next].z);
|
||||
v1.Normalize();
|
||||
v1 *= -_offset;
|
||||
n1 = new Vector3(-v1.z, 0, v1.x);
|
||||
|
||||
pij1 = new Vector3(
|
||||
(float)(md.Vertices[next].x + n1.x), 0,
|
||||
(float)(md.Vertices[next].z + n1.z));
|
||||
pij2 = new Vector3(
|
||||
(float)(md.Vertices[current].x + n1.x), 0,
|
||||
(float)(md.Vertices[current].z + n1.z));
|
||||
|
||||
v2 = new Vector3(
|
||||
md.Vertices[prev].x - md.Vertices[current].x, 0,
|
||||
md.Vertices[prev].z - md.Vertices[current].z);
|
||||
|
||||
v2.Normalize();
|
||||
v2 *= -_offset;
|
||||
n2 = new Vector3(-v2.z, 0, v2.x);
|
||||
pjk1 = new Vector3(
|
||||
(float)(md.Vertices[current].x + n2.x), 0,
|
||||
(float)(md.Vertices[current].z + n2.z));
|
||||
pjk2 = new Vector3(
|
||||
(float)(md.Vertices[prev].x + n2.x), 0,
|
||||
(float)(md.Vertices[prev].z + n2.z));
|
||||
|
||||
// See where the shifted lines ij and jk intersect.
|
||||
bool lines_intersect, segments_intersect;
|
||||
|
||||
FindIntersection(pij1, pij2, pjk1, pjk2,
|
||||
out lines_intersect, out segments_intersect,
|
||||
out poi, out close1, out close2);
|
||||
|
||||
var d = Vector3.Distance(poi, pij2);
|
||||
if (d > 10 * _offset)
|
||||
{
|
||||
poi = new Vector3((md.Vertices[current].x + (poi - (-v1 - v2)).normalized.x), 0,
|
||||
(md.Vertices[current].z + (poi - (-v1 - v2)).normalized.z));
|
||||
}
|
||||
|
||||
newVertices.Add(new Vector3(poi.x, poi.y + _offset + md.Vertices[current].y, poi.z));
|
||||
newVertices.Add(md.Vertices[current] + v1);
|
||||
newVertices.Add(md.Vertices[current] - v2);
|
||||
|
||||
md.Normals.Add(Constants.Math.Vector3Up);
|
||||
md.Normals.Add(-n1);
|
||||
md.Normals.Add(-n2);
|
||||
|
||||
md.Tangents.Add(v1 - v2);
|
||||
md.Tangents.Add(v1 - v2);
|
||||
md.Tangents.Add(v1 - v2);
|
||||
|
||||
newUV.Add(md.UV[0][current]);
|
||||
newUV.Add(md.UV[0][current]);
|
||||
newUV.Add(md.UV[0][current]);
|
||||
|
||||
md.Triangles[0].Add(3 * current);
|
||||
md.Triangles[0].Add(3 * current + 1);
|
||||
md.Triangles[0].Add(3 * current + 2);
|
||||
|
||||
md.Edges.Add(3 * current + 2);
|
||||
md.Edges.Add(3 * current + 1);
|
||||
|
||||
md.Triangles[0].Add(3 * prev);
|
||||
md.Triangles[0].Add(3 * current + 2);
|
||||
md.Triangles[0].Add(3 * prev + 1);
|
||||
|
||||
md.Triangles[0].Add(3 * current);
|
||||
md.Triangles[0].Add(3 * current + 2);
|
||||
md.Triangles[0].Add(3 * prev);
|
||||
//Debug.Log(i + " - " + j + " - " + k);
|
||||
md.Edges.Add(3 * prev + 1);
|
||||
md.Edges.Add(3 * current + 2);
|
||||
}
|
||||
start += count;
|
||||
}
|
||||
|
||||
md.Vertices = newVertices;
|
||||
md.UV[0] = newUV;
|
||||
}
|
||||
|
||||
private List<Vector3> GetEnlargedPolygon(List<Vector3> old_points, float offset)
|
||||
{
|
||||
List<Vector3> enlarged_points = new List<Vector3>();
|
||||
int num_points = old_points.Count;
|
||||
for (int j = 0; j < num_points; j++)
|
||||
{
|
||||
// Find the new location for point j.
|
||||
// Find the points before and after j.
|
||||
int i = (j - 1);
|
||||
if (i < 0) i += num_points;
|
||||
int k = (j + 1) % num_points;
|
||||
|
||||
// Move the points by the offset.
|
||||
Vector3 v1 = new Vector3(
|
||||
old_points[j].x - old_points[i].x, 0,
|
||||
old_points[j].z - old_points[i].z);
|
||||
v1.Normalize();
|
||||
v1 *= offset;
|
||||
Vector3 n1 = new Vector3(-v1.z, 0, v1.x);
|
||||
|
||||
Vector3 pij1 = new Vector3(
|
||||
(float)(old_points[i].x + n1.x), 0,
|
||||
(float)(old_points[i].z + n1.z));
|
||||
Vector3 pij2 = new Vector3(
|
||||
(float)(old_points[j].x + n1.x), 0,
|
||||
(float)(old_points[j].z + n1.z));
|
||||
|
||||
Vector3 v2 = new Vector3(
|
||||
old_points[k].x - old_points[j].x, 0,
|
||||
old_points[k].z - old_points[j].z);
|
||||
v2.Normalize();
|
||||
v2 *= offset;
|
||||
Vector3 n2 = new Vector3(-v2.z, 0, v2.x);
|
||||
|
||||
Vector3 pjk1 = new Vector3(
|
||||
(float)(old_points[j].x + n2.x), 0,
|
||||
(float)(old_points[j].z + n2.z));
|
||||
Vector3 pjk2 = new Vector3(
|
||||
(float)(old_points[k].x + n2.x), 0,
|
||||
(float)(old_points[k].z + n2.z));
|
||||
|
||||
// See where the shifted lines ij and jk intersect.
|
||||
bool lines_intersect, segments_intersect;
|
||||
Vector3 poi, close1, close2;
|
||||
FindIntersection(pij1, pij2, pjk1, pjk2,
|
||||
out lines_intersect, out segments_intersect,
|
||||
out poi, out close1, out close2);
|
||||
Debug.Assert(lines_intersect,
|
||||
"Edges " + i + "-->" + j + " and " +
|
||||
j + "-->" + k + " are parallel");
|
||||
|
||||
enlarged_points.Add(poi);
|
||||
}
|
||||
|
||||
return enlarged_points;
|
||||
}
|
||||
|
||||
// Find the point of intersection between
|
||||
// the lines p1 --> p2 and p3 --> p4.
|
||||
private void FindIntersection(
|
||||
Vector3 p1, Vector3 p2, Vector3 p3, Vector3 p4,
|
||||
out bool lines_intersect, out bool segments_intersect,
|
||||
out Vector3 intersection,
|
||||
out Vector3 close_p1, out Vector3 close_p2)
|
||||
{
|
||||
// Get the segments' parameters.
|
||||
float dx12 = p2.x - p1.x;
|
||||
float dy12 = p2.z - p1.z;
|
||||
float dx34 = p4.x - p3.x;
|
||||
float dy34 = p4.z - p3.z;
|
||||
|
||||
// Solve for t1 and t2
|
||||
float denominator = (dy12 * dx34 - dx12 * dy34);
|
||||
|
||||
float t1 =
|
||||
((p1.x - p3.x) * dy34 + (p3.z - p1.z) * dx34)
|
||||
/ denominator;
|
||||
if (float.IsInfinity(t1))
|
||||
{
|
||||
// The lines are parallel (or close enough to it).
|
||||
lines_intersect = false;
|
||||
segments_intersect = false;
|
||||
intersection = new Vector3(float.NaN, 0, float.NaN);
|
||||
close_p1 = new Vector3(float.NaN, 0, float.NaN);
|
||||
close_p2 = new Vector3(float.NaN, 0, float.NaN);
|
||||
return;
|
||||
}
|
||||
lines_intersect = true;
|
||||
|
||||
float t2 =
|
||||
((p3.x - p1.x) * dy12 + (p1.z - p3.z) * dx12)
|
||||
/ -denominator;
|
||||
|
||||
// Find the point of intersection.
|
||||
intersection = new Vector3(p1.x + dx12 * t1, 0, p1.z + dy12 * t1);
|
||||
|
||||
// The segments intersect if t1 and t2 are between 0 and 1.
|
||||
segments_intersect =
|
||||
((t1 >= 0) && (t1 <= 1) &&
|
||||
(t2 >= 0) && (t2 <= 1));
|
||||
|
||||
// Find the closest points on the segments.
|
||||
if (t1 < 0)
|
||||
{
|
||||
t1 = 0;
|
||||
}
|
||||
else if (t1 > 1)
|
||||
{
|
||||
t1 = 1;
|
||||
}
|
||||
|
||||
if (t2 < 0)
|
||||
{
|
||||
t2 = 0;
|
||||
}
|
||||
else if (t2 > 1)
|
||||
{
|
||||
t2 = 1;
|
||||
}
|
||||
|
||||
close_p1 = new Vector3(p1.x + dx12 * t1, 0, p1.z + dy12 * t1);
|
||||
close_p2 = new Vector3(p3.x + dx34 * t2, 0, p3.z + dy34 * t2);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 232ffe870b9f2df408d25ad59dbfe77c
|
||||
timeCreated: 1478553093
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,766 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Mapbox.VectorTile.Geometry;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Assets.Mapbox.Unity.MeshGeneration.Modifiers.MeshModifiers
|
||||
{
|
||||
public static class EarcutLibrary
|
||||
{
|
||||
public static List<int> Earcut(List<float> data, List<int> holeIndices, int dim)
|
||||
{
|
||||
dim = Math.Max(dim, 2);
|
||||
|
||||
var hasHoles = holeIndices.Count;
|
||||
var outerLen = hasHoles > 0 ? holeIndices[0] * dim : data.Count;
|
||||
var outerNode = linkedList(data, 0, outerLen, dim, true);
|
||||
var triangles = new List<int>((int)(outerNode.i * 1.5));
|
||||
|
||||
if (outerNode == null) return triangles;
|
||||
var minX = 0f;
|
||||
var minY = 0f;
|
||||
var maxX = 0f;
|
||||
var maxY = 0f;
|
||||
var x = 0f;
|
||||
var y = 0f;
|
||||
var size = 0f;
|
||||
|
||||
if (hasHoles > 0) outerNode = EliminateHoles(data, holeIndices, outerNode, dim);
|
||||
|
||||
// if the shape is not too simple, we'll use z-order curve hash later; calculate polygon bbox
|
||||
if (data.Count > 80 * dim)
|
||||
{
|
||||
minX = maxX = data[0];
|
||||
minY = maxY = data[1];
|
||||
|
||||
for (var i = dim; i < outerLen; i += dim)
|
||||
{
|
||||
x = data[i];
|
||||
y = data[i + 1];
|
||||
if (x < minX) minX = x;
|
||||
if (y < minY) minY = y;
|
||||
if (x > maxX) maxX = x;
|
||||
if (y > maxY) maxY = y;
|
||||
}
|
||||
|
||||
// minX, minY and size are later used to transform coords into integers for z-order calculation
|
||||
size = Math.Max(maxX - minX, maxY - minY);
|
||||
}
|
||||
|
||||
earcutLinked(outerNode, triangles, dim, minX, minY, size);
|
||||
|
||||
return triangles;
|
||||
}
|
||||
|
||||
private static void earcutLinked(Node ear, List<int> triangles, int dim, float minX, float minY, float size, int pass = 0)
|
||||
{
|
||||
if (ear == null) return;
|
||||
|
||||
// interlink polygon nodes in z-order
|
||||
if (pass == 0 && size > 0) indexCurve(ear, minX, minY, size);
|
||||
|
||||
var stop = ear;
|
||||
Node prev;
|
||||
Node next;
|
||||
|
||||
// iterate through ears, slicing them one by one
|
||||
while (ear.prev != ear.next)
|
||||
{
|
||||
prev = ear.prev;
|
||||
next = ear.next;
|
||||
|
||||
if (size > 0 ? isEarHashed(ear, minX, minY, size) : isEar(ear))
|
||||
{
|
||||
// cut off the triangle
|
||||
triangles.Add(prev.i / dim);
|
||||
triangles.Add(next.i / dim);
|
||||
triangles.Add(ear.i / dim);
|
||||
|
||||
removeNode(ear);
|
||||
|
||||
// skipping the next vertice leads to less sliver triangles
|
||||
ear = next.next;
|
||||
stop = next.next;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
ear = next;
|
||||
|
||||
// if we looped through the whole remaining polygon and can't find any more ears
|
||||
if (ear == stop)
|
||||
{
|
||||
// try filtering points and slicing again
|
||||
if (pass == 0)
|
||||
{
|
||||
earcutLinked(FilterPoints(ear, null), triangles, dim, minX, minY, size, 1);
|
||||
|
||||
// if this didn't work, try curing all small self-intersections locally
|
||||
}
|
||||
else if (pass == 1)
|
||||
{
|
||||
ear = cureLocalIntersections(ear, triangles, dim);
|
||||
earcutLinked(ear, triangles, dim, minX, minY, size, 2);
|
||||
|
||||
// as a last resort, try splitting the remaining polygon into two
|
||||
}
|
||||
else if (pass == 2)
|
||||
{
|
||||
splitEarcut(ear, triangles, dim, minX, minY, size);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static bool isEarHashed(Node ear, float minX, float minY, float size)
|
||||
{
|
||||
var a = ear.prev;
|
||||
var b = ear;
|
||||
var c = ear.next;
|
||||
|
||||
if (area(a, b, c) >= 0) return false; // reflex, can't be an ear
|
||||
|
||||
// triangle bbox; min & max are calculated like this for speed
|
||||
var minTX = a.x < b.x ? (a.x < c.x ? a.x : c.x) : (b.x < c.x ? b.x : c.x);
|
||||
var minTY = a.y < b.y ? (a.y < c.y ? a.y : c.y) : (b.y < c.y ? b.y : c.y);
|
||||
var maxTX = a.x > b.x ? (a.x > c.x ? a.x : c.x) : (b.x > c.x ? b.x : c.x);
|
||||
var maxTY = a.y > b.y ? (a.y > c.y ? a.y : c.y) : (b.y > c.y ? b.y : c.y);
|
||||
|
||||
// z-order range for the current triangle bbox;
|
||||
var minZ = zOrder(minTX, minTY, minX, minY, size);
|
||||
var maxZ = zOrder(maxTX, maxTY, minX, minY, size);
|
||||
|
||||
// first look for points inside the triangle in increasing z-order
|
||||
var p = ear.nextZ;
|
||||
|
||||
while (p != null && p.mZOrder <= maxZ)
|
||||
{
|
||||
if (p != ear.prev && p != ear.next &&
|
||||
pointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y) &&
|
||||
area(p.prev, p, p.next) >= 0) return false;
|
||||
p = p.nextZ;
|
||||
}
|
||||
|
||||
// then look for points in decreasing z-order
|
||||
p = ear.prevZ;
|
||||
|
||||
while (p != null && p.mZOrder >= minZ)
|
||||
{
|
||||
if (p != ear.prev && p != ear.next &&
|
||||
pointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y) &&
|
||||
area(p.prev, p, p.next) >= 0) return false;
|
||||
p = p.prevZ;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static int zOrder(float x, float y, float minX, float minY, float size)
|
||||
{
|
||||
//TODO casting here might be wrong
|
||||
x = 32767 * (x - minX) / size;
|
||||
y = 32767 * (y - minY) / size;
|
||||
|
||||
x = ((int)x | ((int)x << 8)) & 0x00FF00FF;
|
||||
x = ((int)x | ((int)x << 4)) & 0x0F0F0F0F;
|
||||
x = ((int)x | ((int)x << 2)) & 0x33333333;
|
||||
x = ((int)x | ((int)x << 1)) & 0x55555555;
|
||||
|
||||
y = ((int)y | ((int)y << 8)) & 0x00FF00FF;
|
||||
y = ((int)y | ((int)y << 4)) & 0x0F0F0F0F;
|
||||
y = ((int)y | ((int)y << 2)) & 0x33333333;
|
||||
y = ((int)y | ((int)y << 1)) & 0x55555555;
|
||||
|
||||
return (int)x | ((int)y << 1);
|
||||
}
|
||||
|
||||
private static void splitEarcut(Node start, List<int> triangles, int dim, float minX, float minY, float size)
|
||||
{
|
||||
var a = start;
|
||||
do
|
||||
{
|
||||
var b = a.next.next;
|
||||
while (b != a.prev)
|
||||
{
|
||||
if (a.i != b.i && isValidDiagonal(a, b))
|
||||
{
|
||||
// split the polygon in two by the diagonal
|
||||
var c = SplitPolygon(a, b);
|
||||
|
||||
// filter colinear points around the cuts
|
||||
a = FilterPoints(a, a.next);
|
||||
c = FilterPoints(c, c.next);
|
||||
|
||||
// run earcut on each half
|
||||
earcutLinked(a, triangles, dim, minX, minY, size);
|
||||
earcutLinked(c, triangles, dim, minX, minY, size);
|
||||
return;
|
||||
}
|
||||
b = b.next;
|
||||
}
|
||||
a = a.next;
|
||||
} while (a != start);
|
||||
}
|
||||
|
||||
private static bool isValidDiagonal(Node a, Node b)
|
||||
{
|
||||
return a.next.i != b.i && a.prev.i != b.i && !intersectsPolygon(a, b) &&
|
||||
locallyInside(a, b) && locallyInside(b, a) && middleInside(a, b);
|
||||
}
|
||||
|
||||
private static bool middleInside(Node a, Node b)
|
||||
{
|
||||
var p = a;
|
||||
var inside = false;
|
||||
var px = (a.x + b.x) / 2;
|
||||
var py = (a.y + b.y) / 2;
|
||||
|
||||
do
|
||||
{
|
||||
if (((p.y > py) != (p.next.y > py)) && p.next.y != p.y &&
|
||||
(px < (p.next.x - p.x) * (py - p.y) / (p.next.y - p.y) + p.x))
|
||||
inside = !inside;
|
||||
p = p.next;
|
||||
} while (p != a);
|
||||
|
||||
return inside;
|
||||
}
|
||||
|
||||
private static bool intersectsPolygon(Node a, Node b)
|
||||
{
|
||||
var p = a;
|
||||
do
|
||||
{
|
||||
if (p.i != a.i && p.next.i != a.i && p.i != b.i && p.next.i != b.i &&
|
||||
intersects(p, p.next, a, b)) return true;
|
||||
p = p.next;
|
||||
} while (p != a);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static Node cureLocalIntersections(Node start, List<int> triangles, int dim)
|
||||
{
|
||||
var p = start;
|
||||
do
|
||||
{
|
||||
var a = p.prev;
|
||||
var b = p.next.next;
|
||||
|
||||
if (!equals(a, b) && intersects(a, p, p.next, b) && locallyInside(a, b) && locallyInside(b, a))
|
||||
{
|
||||
|
||||
triangles.Add(a.i / dim);
|
||||
triangles.Add(p.i / dim);
|
||||
triangles.Add(b.i / dim);
|
||||
|
||||
// remove two nodes involved
|
||||
removeNode(p);
|
||||
removeNode(p.next);
|
||||
|
||||
p = start = b;
|
||||
}
|
||||
p = p.next;
|
||||
} while (p != start);
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
private static bool intersects(Node p1, Node q1, Node p2, Node q2)
|
||||
{
|
||||
if ((equals(p1, q1) && equals(p2, q2)) ||
|
||||
(equals(p1, q2) && equals(p2, q1))) return true;
|
||||
return area(p1, q1, p2) > 0 != area(p1, q1, q2) > 0 &&
|
||||
area(p2, q2, p1) > 0 != area(p2, q2, q1) > 0;
|
||||
}
|
||||
|
||||
private static bool isEar(Node ear)
|
||||
{
|
||||
var a = ear.prev;
|
||||
var b = ear;
|
||||
var c = ear.next;
|
||||
|
||||
if (area(a, b, c) >= 0) return false; // reflex, can't be an ear
|
||||
|
||||
// now make sure we don't have other points inside the potential ear
|
||||
var p = ear.next.next;
|
||||
|
||||
while (p != ear.prev)
|
||||
{
|
||||
if (pointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y) &&
|
||||
area(p.prev, p, p.next) >= 0) return false;
|
||||
p = p.next;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void indexCurve(Node start, float minX, float minY, float size)
|
||||
{
|
||||
var p = start;
|
||||
do
|
||||
{
|
||||
if (p.mZOrder == 0) p.mZOrder = zOrder(p.x, p.y, minX, minY, size);
|
||||
p.prevZ = p.prev;
|
||||
p.nextZ = p.next;
|
||||
p = p.next;
|
||||
} while (p != start);
|
||||
|
||||
p.prevZ.nextZ = null;
|
||||
p.prevZ = null;
|
||||
|
||||
sortLinked(p);
|
||||
}
|
||||
|
||||
private static Node sortLinked(Node list)
|
||||
{
|
||||
var i = 0;
|
||||
Node p;
|
||||
Node q;
|
||||
Node e;
|
||||
Node tail;
|
||||
var numMerges = 0; ;
|
||||
var pSize = 0;
|
||||
var qSize = 0;
|
||||
var inSize = 1;
|
||||
|
||||
do
|
||||
{
|
||||
p = list;
|
||||
list = null;
|
||||
tail = null;
|
||||
numMerges = 0;
|
||||
|
||||
while (p != null)
|
||||
{
|
||||
numMerges++;
|
||||
q = p;
|
||||
pSize = 0;
|
||||
for (i = 0; i < inSize; i++)
|
||||
{
|
||||
pSize++;
|
||||
q = q.nextZ;
|
||||
if (q == null) break;
|
||||
}
|
||||
qSize = inSize;
|
||||
|
||||
while (pSize > 0 || (qSize > 0 && q != null))
|
||||
{
|
||||
|
||||
if (pSize != 0 && (qSize == 0 || q == null || p.mZOrder <= q.mZOrder))
|
||||
{
|
||||
e = p;
|
||||
p = p.nextZ;
|
||||
pSize--;
|
||||
}
|
||||
else
|
||||
{
|
||||
e = q;
|
||||
q = q.nextZ;
|
||||
qSize--;
|
||||
}
|
||||
|
||||
if (tail != null) tail.nextZ = e;
|
||||
else list = e;
|
||||
|
||||
e.prevZ = tail;
|
||||
tail = e;
|
||||
}
|
||||
|
||||
p = q;
|
||||
}
|
||||
|
||||
tail.nextZ = null;
|
||||
inSize *= 2;
|
||||
|
||||
} while (numMerges > 1);
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
private static Node EliminateHoles(List<float> data, List<int> holeIndices, Node outerNode, int dim)
|
||||
{
|
||||
var i = 0;
|
||||
var len = holeIndices.Count;
|
||||
var start = 0;
|
||||
var end = 0;
|
||||
Node list = null;
|
||||
var queue = new List<Node>(len);
|
||||
for (i = 0; i < len; i++)
|
||||
{
|
||||
start = holeIndices[i] * dim;
|
||||
end = i < len - 1 ? holeIndices[i + 1] * dim : data.Count;
|
||||
list = linkedList(data, start, end, dim, false);
|
||||
if (list == list.next) list.steiner = true;
|
||||
queue.Add(getLeftmost(list));
|
||||
}
|
||||
|
||||
queue.Sort(delegate (Node a, Node b)
|
||||
{
|
||||
return (int)Math.Ceiling(a.x - b.x);
|
||||
});
|
||||
|
||||
// process holes from left to right
|
||||
for (i = 0; i < queue.Count; i++)
|
||||
{
|
||||
EliminateHole(queue[i], outerNode);
|
||||
outerNode = FilterPoints(outerNode, outerNode.next);
|
||||
}
|
||||
|
||||
return outerNode;
|
||||
}
|
||||
|
||||
private static void EliminateHole(Node hole, Node outerNode)
|
||||
{
|
||||
outerNode = FindHoleBridge(hole, outerNode);
|
||||
if (outerNode != null)
|
||||
{
|
||||
var b = SplitPolygon(outerNode, hole);
|
||||
FilterPoints(b, b.next);
|
||||
}
|
||||
}
|
||||
|
||||
private static Node FilterPoints(Node start, Node end)
|
||||
{
|
||||
if (start == null) return start;
|
||||
if (end == null) end = start;
|
||||
|
||||
var p = start;
|
||||
bool again = true;
|
||||
do
|
||||
{
|
||||
again = false;
|
||||
|
||||
if (!p.steiner && (equals(p, p.next) || area(p.prev, p, p.next) == 0))
|
||||
{
|
||||
removeNode(p);
|
||||
p = end = p.prev;
|
||||
if (p == p.next) return null;
|
||||
again = true;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
p = p.next;
|
||||
}
|
||||
} while (again || p != end);
|
||||
|
||||
return end;
|
||||
}
|
||||
|
||||
private static Node SplitPolygon(Node a, Node b)
|
||||
{
|
||||
var a2 = new Node(a.i, a.x, a.y);
|
||||
var b2 = new Node(b.i, b.x, b.y);
|
||||
var an = a.next;
|
||||
var bp = b.prev;
|
||||
|
||||
a.next = b;
|
||||
b.prev = a;
|
||||
|
||||
a2.next = an;
|
||||
an.prev = a2;
|
||||
|
||||
b2.next = a2;
|
||||
a2.prev = b2;
|
||||
|
||||
bp.next = b2;
|
||||
b2.prev = bp;
|
||||
|
||||
return b2;
|
||||
}
|
||||
|
||||
private static Node FindHoleBridge(Node hole, Node outerNode)
|
||||
{
|
||||
var p = outerNode;
|
||||
var hx = hole.x;
|
||||
var hy = hole.y;
|
||||
var qx = float.MinValue;
|
||||
Node m = null;
|
||||
|
||||
// find a segment intersected by a ray from the hole's leftmost point to the left;
|
||||
// segment's endpoint with lesser x will be potential connection point
|
||||
do
|
||||
{
|
||||
if (hy <= p.y && hy >= p.next.y && p.next.y != p.y)
|
||||
{
|
||||
var x = p.x + (hy - p.y) * (p.next.x - p.x) / (p.next.y - p.y);
|
||||
if (x <= hx && x > qx)
|
||||
{
|
||||
qx = x;
|
||||
if (x == hx)
|
||||
{
|
||||
if (hy == p.y) return p;
|
||||
if (hy == p.next.y) return p.next;
|
||||
}
|
||||
m = p.x < p.next.x ? p : p.next;
|
||||
}
|
||||
}
|
||||
p = p.next;
|
||||
} while (p != outerNode);
|
||||
|
||||
if (m == null) return null;
|
||||
|
||||
if (hx == qx) return m.prev; // hole touches outer segment; pick lower endpoint
|
||||
|
||||
// look for points inside the triangle of hole point, segment intersection and endpoint;
|
||||
// if there are no points found, we have a valid connection;
|
||||
// otherwise choose the point of the minimum angle with the ray as connection point
|
||||
|
||||
var stop = m;
|
||||
var mx = m.x;
|
||||
var my = m.y;
|
||||
var tanMin = float.MaxValue;
|
||||
float tan = 0f;
|
||||
|
||||
p = m.next;
|
||||
|
||||
while (p != stop)
|
||||
{
|
||||
if (hx >= p.x && p.x >= mx && hx != p.x &&
|
||||
pointInTriangle(hy < my ? hx : qx, hy, mx, my, hy < my ? qx : hx, hy, p.x, p.y))
|
||||
{
|
||||
|
||||
tan = Math.Abs(hy - p.y) / (hx - p.x); // tangential
|
||||
|
||||
if ((tan < tanMin || (tan == tanMin && p.x > m.x)) && locallyInside(p, hole))
|
||||
{
|
||||
m = p;
|
||||
tanMin = tan;
|
||||
}
|
||||
}
|
||||
|
||||
p = p.next;
|
||||
}
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
private static bool locallyInside(Node a, Node b)
|
||||
{
|
||||
return area(a.prev, a, a.next) < 0 ?
|
||||
area(a, b, a.next) >= 0 && area(a, a.prev, b) >= 0 :
|
||||
area(a, b, a.prev) < 0 || area(a, a.next, b) < 0;
|
||||
}
|
||||
|
||||
private static float area(Node p, Node q, Node r)
|
||||
{
|
||||
return (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y);
|
||||
}
|
||||
|
||||
private static bool pointInTriangle(float ax, float ay, float bx, float by, float cx, float cy, float px, float py)
|
||||
{
|
||||
return (cx - px) * (ay - py) - (ax - px) * (cy - py) >= 0 &&
|
||||
(ax - px) * (by - py) - (bx - px) * (ay - py) >= 0 &&
|
||||
(bx - px) * (cy - py) - (cx - px) * (by - py) >= 0;
|
||||
}
|
||||
|
||||
private static Node getLeftmost(Node start)
|
||||
{
|
||||
var p = start;
|
||||
var leftmost = start;
|
||||
do
|
||||
{
|
||||
if (p.x < leftmost.x) leftmost = p;
|
||||
p = p.next;
|
||||
} while (p != start);
|
||||
|
||||
return leftmost;
|
||||
}
|
||||
|
||||
// create a circular doubly linked list from polygon points in the specified winding order
|
||||
private static Node linkedList(List<float> data, int start, int end, int dim, bool clockwise)
|
||||
{
|
||||
var i = 0;
|
||||
Node last = null;
|
||||
|
||||
if (clockwise == (signedArea(data, start, end, dim) > 0))
|
||||
{
|
||||
for (i = start; i < end; i += dim) last = insertNode(i, data[i], data[i + 1], last);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i = end - dim; i >= start; i -= dim) last = insertNode(i, data[i], data[i + 1], last);
|
||||
}
|
||||
|
||||
if (last != null && equals(last, last.next))
|
||||
{
|
||||
removeNode(last);
|
||||
last = last.next;
|
||||
}
|
||||
|
||||
return last;
|
||||
}
|
||||
|
||||
private static void removeNode(Node p)
|
||||
{
|
||||
p.next.prev = p.prev;
|
||||
p.prev.next = p.next;
|
||||
|
||||
if (p.prevZ != null) p.prevZ.nextZ = p.nextZ;
|
||||
if (p.nextZ != null) p.nextZ.prevZ = p.prevZ;
|
||||
}
|
||||
|
||||
private static bool equals(Node p1, Node p2)
|
||||
{
|
||||
return p1.x == p2.x && p1.y == p2.y;
|
||||
}
|
||||
|
||||
private static float signedArea(List<float> data, int start, int end, int dim)
|
||||
{
|
||||
var sum = 0f;
|
||||
var j = end - dim;
|
||||
for (var i = start; i < end; i += dim)
|
||||
{
|
||||
sum += (data[j] - data[i]) * (data[i + 1] + data[j + 1]);
|
||||
j = i;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
private static Node insertNode(int i, float x, float y, Node last)
|
||||
{
|
||||
var p = new Node(i, x, y);
|
||||
|
||||
if (last == null)
|
||||
{
|
||||
p.prev = p;
|
||||
p.next = p;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
p.next = last.next;
|
||||
p.prev = last;
|
||||
last.next.prev = p;
|
||||
last.next = p;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
public static Data Flatten(List<List<Vector3>> data)
|
||||
{
|
||||
var dataCount = data.Count;
|
||||
var totalVertCount = 0;
|
||||
for (int i = 0; i < dataCount; i++)
|
||||
{
|
||||
totalVertCount += data[i].Count;
|
||||
}
|
||||
|
||||
var result = new Data() { Dim = 2 };
|
||||
result.Vertices = new List<float>(totalVertCount * 2);
|
||||
var holeIndex = 0;
|
||||
|
||||
for (var i = 0; i < dataCount; i++)
|
||||
{
|
||||
var subCount = data[i].Count;
|
||||
for (var j = 0; j < subCount; j++)
|
||||
{
|
||||
result.Vertices.Add(data[i][j][0]);
|
||||
result.Vertices.Add(data[i][j][2]);
|
||||
}
|
||||
if (i > 0)
|
||||
{
|
||||
holeIndex += data[i - 1].Count;
|
||||
result.Holes.Add(holeIndex);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public class Data
|
||||
{
|
||||
public List<float> Vertices;
|
||||
public List<int> Holes;
|
||||
public int Dim;
|
||||
|
||||
public Data()
|
||||
{
|
||||
Holes = new List<int>();
|
||||
Dim = 2;
|
||||
}
|
||||
}
|
||||
|
||||
public class Node
|
||||
{
|
||||
|
||||
/* Member Variables. */
|
||||
public int i;
|
||||
public float x;
|
||||
public float y;
|
||||
public int mZOrder;
|
||||
public Node prev;
|
||||
public Node next;
|
||||
public Node prevZ;
|
||||
public Node nextZ;
|
||||
public bool steiner;
|
||||
|
||||
public Node(int ind, float pX, float pY)
|
||||
{
|
||||
/* Initialize Member Variables. */
|
||||
this.i = ind;
|
||||
this.x = pX;
|
||||
this.y = pY;
|
||||
this.mZOrder = 0;
|
||||
this.prev = null;
|
||||
this.next = null;
|
||||
this.prevZ = null;
|
||||
this.nextZ = null;
|
||||
}
|
||||
|
||||
protected void setPreviousNode(Node pNode)
|
||||
{
|
||||
this.prev = pNode;
|
||||
}
|
||||
|
||||
protected Node getPreviousNode()
|
||||
{
|
||||
return this.prev;
|
||||
}
|
||||
|
||||
protected void setNextNode(Node pNode)
|
||||
{
|
||||
this.next = pNode;
|
||||
}
|
||||
|
||||
protected Node getNextNode()
|
||||
{
|
||||
return this.next;
|
||||
}
|
||||
|
||||
protected void setZOrder(int pZOrder)
|
||||
{
|
||||
this.mZOrder = pZOrder;
|
||||
}
|
||||
|
||||
protected int getZOrder()
|
||||
{
|
||||
return this.mZOrder;
|
||||
}
|
||||
|
||||
protected void setPreviousZNode(Node pNode)
|
||||
{
|
||||
this.prevZ = pNode;
|
||||
}
|
||||
|
||||
protected Node getPreviousZNode()
|
||||
{
|
||||
return this.prevZ;
|
||||
}
|
||||
|
||||
protected void setNextZNode(Node pNode)
|
||||
{
|
||||
this.nextZ = pNode;
|
||||
}
|
||||
|
||||
protected Node getNextZNode()
|
||||
{
|
||||
return this.nextZ;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e7b04bd1ddccfc64aa5e47acd098a3ef
|
||||
timeCreated: 1499344109
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,293 @@
|
||||
namespace Mapbox.Unity.MeshGeneration.Modifiers
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using Mapbox.Unity.MeshGeneration.Data;
|
||||
using System;
|
||||
using Mapbox.Unity.Map;
|
||||
|
||||
public class MinMaxPair
|
||||
{
|
||||
public float min, max;
|
||||
|
||||
public static MinMaxPair GetMinMaxHeight(List<Vector3> vertices)
|
||||
{
|
||||
int counter = vertices.Count;
|
||||
MinMaxPair returnValue = new MinMaxPair
|
||||
{
|
||||
max = float.MinValue,
|
||||
min = float.MaxValue
|
||||
};
|
||||
|
||||
for (int i = 0; i < counter; i++)
|
||||
{
|
||||
if (vertices[i].y > returnValue.max)
|
||||
returnValue.max = vertices[i].y;
|
||||
else if (vertices[i].y < returnValue.min)
|
||||
returnValue.min = vertices[i].y;
|
||||
}
|
||||
|
||||
return returnValue;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Height Modifier is responsible for the y axis placement of the feature. It pushes the original vertices upwards by "height" value and creates side walls around that new polygon down to "min_height" value.
|
||||
/// It also checkes for "ele" (elevation) value used for contour lines in Mapbox Terrain data.
|
||||
/// Height Modifier also creates a continuous UV mapping for side walls.
|
||||
/// </summary>
|
||||
[CreateAssetMenu(menuName = "Mapbox/Modifiers/Height Modifier")]
|
||||
public class HeightModifier : MeshModifier
|
||||
{
|
||||
private float _scale = 1;
|
||||
|
||||
//[SerializeField]
|
||||
//[Tooltip("Create side walls from calculated height down to terrain level. Suggested for buildings, not suggested for roads.")]
|
||||
//private bool _createSideWalls = true;
|
||||
|
||||
GeometryExtrusionOptions _options;
|
||||
|
||||
[SerializeField]
|
||||
[Tooltip("Create side walls as separate submesh.")]
|
||||
private bool _separateSubmesh = true;
|
||||
|
||||
public override ModifierType Type { get { return ModifierType.Preprocess; } }
|
||||
|
||||
private int _counter = 0;
|
||||
float height = 0.0f;
|
||||
|
||||
public override void SetProperties(ModifierProperties properties)
|
||||
{
|
||||
_options = (GeometryExtrusionOptions)properties;
|
||||
_options.PropertyHasChanged += UpdateModifier;
|
||||
}
|
||||
public override void UnbindProperties()
|
||||
{
|
||||
_options.PropertyHasChanged -= UpdateModifier;
|
||||
}
|
||||
|
||||
public override void Run(VectorFeatureUnity feature, MeshData md, float scale)
|
||||
{
|
||||
_scale = scale;
|
||||
Run(feature, md);
|
||||
}
|
||||
|
||||
public override void Run(VectorFeatureUnity feature, MeshData md, UnityTile tile = null)
|
||||
{
|
||||
_counter = 0;
|
||||
if (md.Vertices.Count == 0 || feature == null || feature.Points.Count < 1)
|
||||
return;
|
||||
|
||||
if (tile != null)
|
||||
_scale = tile.TileScale;
|
||||
|
||||
float maxHeight = 1.0f;
|
||||
float minHeight = 0.0f;
|
||||
|
||||
QueryHeight(feature, md, tile, out maxHeight, out minHeight);
|
||||
|
||||
maxHeight = maxHeight * _options.extrusionScaleFactor * _scale;
|
||||
minHeight = minHeight * _options.extrusionScaleFactor * _scale;
|
||||
height = (maxHeight - minHeight);
|
||||
|
||||
//Set roof height
|
||||
GenerateRoofMesh(md, minHeight, maxHeight);
|
||||
|
||||
GenerateWallMesh(md);
|
||||
|
||||
}
|
||||
|
||||
protected virtual void GenerateWallMesh(MeshData md)
|
||||
{
|
||||
md.Vertices.Capacity = _counter + md.Edges.Count * 2;
|
||||
float d = 0f;
|
||||
Vector3 v1;
|
||||
Vector3 v2;
|
||||
int ind = 0;
|
||||
Vector3 wallDir;
|
||||
|
||||
if (_options.extrusionGeometryType != ExtrusionGeometryType.RoofOnly)
|
||||
{
|
||||
_counter = md.Edges.Count;
|
||||
var wallTri = new List<int>(_counter * 3);
|
||||
var wallUv = new List<Vector2>(_counter * 2);
|
||||
Vector3 norm = Constants.Math.Vector3Zero;
|
||||
|
||||
md.Vertices.Capacity = md.Vertices.Count + _counter * 2;
|
||||
md.Normals.Capacity = md.Normals.Count + _counter * 2;
|
||||
|
||||
for (int i = 0; i < _counter; i += 2)
|
||||
{
|
||||
v1 = md.Vertices[md.Edges[i]];
|
||||
v2 = md.Vertices[md.Edges[i + 1]];
|
||||
ind = md.Vertices.Count;
|
||||
md.Vertices.Add(v1);
|
||||
md.Vertices.Add(v2);
|
||||
md.Vertices.Add(new Vector3(v1.x, v1.y - height, v1.z));
|
||||
md.Vertices.Add(new Vector3(v2.x, v2.y - height, v2.z));
|
||||
|
||||
//d = (v2 - v1).magnitude;
|
||||
d = Mathf.Sqrt((v2.x - v1.x) + (v2.y - v1.y) + (v2.z - v1.z));
|
||||
norm = Vector3.Normalize(Vector3.Cross(v2 - v1, md.Vertices[ind + 2] - v1));
|
||||
md.Normals.Add(norm);
|
||||
md.Normals.Add(norm);
|
||||
md.Normals.Add(norm);
|
||||
md.Normals.Add(norm);
|
||||
|
||||
wallDir = (v2 - v1).normalized;
|
||||
md.Tangents.Add(wallDir);
|
||||
md.Tangents.Add(wallDir);
|
||||
md.Tangents.Add(wallDir);
|
||||
md.Tangents.Add(wallDir);
|
||||
|
||||
wallUv.Add(new Vector2(0, 0));
|
||||
wallUv.Add(new Vector2(d, 0));
|
||||
wallUv.Add(new Vector2(0, -height));
|
||||
wallUv.Add(new Vector2(d, -height));
|
||||
|
||||
wallTri.Add(ind);
|
||||
wallTri.Add(ind + 1);
|
||||
wallTri.Add(ind + 2);
|
||||
|
||||
wallTri.Add(ind + 1);
|
||||
wallTri.Add(ind + 3);
|
||||
wallTri.Add(ind + 2);
|
||||
}
|
||||
|
||||
// TODO: Do we really need this?
|
||||
if (_separateSubmesh)
|
||||
{
|
||||
md.Triangles.Add(wallTri);
|
||||
}
|
||||
else
|
||||
{
|
||||
md.Triangles.Capacity = md.Triangles.Count + wallTri.Count;
|
||||
md.Triangles[0].AddRange(wallTri);
|
||||
}
|
||||
md.UV[0].AddRange(wallUv);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void GenerateRoofMesh(MeshData md, float minHeight, float maxHeight)
|
||||
{
|
||||
if (_options.extrusionGeometryType != ExtrusionGeometryType.SideOnly)
|
||||
{
|
||||
_counter = md.Vertices.Count;
|
||||
switch (_options.extrusionType)
|
||||
{
|
||||
case ExtrusionType.None:
|
||||
break;
|
||||
case ExtrusionType.PropertyHeight:
|
||||
for (int i = 0; i < _counter; i++)
|
||||
{
|
||||
md.Vertices[i] = new Vector3(md.Vertices[i].x, md.Vertices[i].y + maxHeight, md.Vertices[i].z);
|
||||
}
|
||||
break;
|
||||
case ExtrusionType.MinHeight:
|
||||
{
|
||||
var minmax = MinMaxPair.GetMinMaxHeight(md.Vertices);
|
||||
for (int i = 0; i < _counter; i++)
|
||||
{
|
||||
md.Vertices[i] = new Vector3(md.Vertices[i].x, minmax.min + maxHeight, md.Vertices[i].z);
|
||||
}
|
||||
}
|
||||
//hf += max - min;
|
||||
break;
|
||||
case ExtrusionType.MaxHeight:
|
||||
{
|
||||
var minmax = MinMaxPair.GetMinMaxHeight(md.Vertices);
|
||||
for (int i = 0; i < _counter; i++)
|
||||
{
|
||||
md.Vertices[i] = new Vector3(md.Vertices[i].x, minmax.max + maxHeight, md.Vertices[i].z);
|
||||
}
|
||||
height += (minmax.max - minmax.min);
|
||||
}
|
||||
break;
|
||||
case ExtrusionType.RangeHeight:
|
||||
for (int i = 0; i < _counter; i++)
|
||||
{
|
||||
md.Vertices[i] = new Vector3(md.Vertices[i].x, md.Vertices[i].y + maxHeight, md.Vertices[i].z);
|
||||
}
|
||||
break;
|
||||
case ExtrusionType.AbsoluteHeight:
|
||||
for (int i = 0; i < _counter; i++)
|
||||
{
|
||||
md.Vertices[i] = new Vector3(md.Vertices[i].x, md.Vertices[i].y + maxHeight, md.Vertices[i].z);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void QueryHeight(VectorFeatureUnity feature, MeshData md, UnityTile tile, out float maxHeight, out float minHeight)
|
||||
{
|
||||
minHeight = 0.0f;
|
||||
maxHeight = 0.0f;
|
||||
|
||||
switch (_options.extrusionType)
|
||||
{
|
||||
case ExtrusionType.None:
|
||||
break;
|
||||
case ExtrusionType.PropertyHeight:
|
||||
case ExtrusionType.MinHeight:
|
||||
case ExtrusionType.MaxHeight:
|
||||
if (feature.Properties.ContainsKey(_options.propertyName))
|
||||
{
|
||||
try
|
||||
{
|
||||
maxHeight = Convert.ToSingle(feature.Properties[_options.propertyName]);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
Debug.LogError("Property: '" + _options.propertyName + "' must contain a numerical value for extrusion.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (feature.Properties.ContainsKey("min_height"))
|
||||
{
|
||||
minHeight = Convert.ToSingle(feature.Properties["min_height"]);
|
||||
//maxHeight -= minHeight;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ExtrusionType.RangeHeight:
|
||||
if (feature.Properties.ContainsKey(_options.propertyName))
|
||||
{
|
||||
if (_options.minimumHeight > _options.maximumHeight)
|
||||
{
|
||||
Debug.LogError("Maximum Height less than Minimum Height.Swapping values for extrusion.");
|
||||
var temp = _options.minimumHeight;
|
||||
_options.minimumHeight = _options.maximumHeight;
|
||||
_options.maximumHeight = temp;
|
||||
}
|
||||
|
||||
float featureHeight;
|
||||
try
|
||||
{
|
||||
featureHeight = Convert.ToSingle(feature.Properties[_options.propertyName]);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
Debug.LogError("Property: '" + _options.propertyName + "' must contain a numerical value for extrusion.");
|
||||
return;
|
||||
}
|
||||
|
||||
maxHeight = Math.Min(Math.Max(_options.minimumHeight, featureHeight), _options.maximumHeight);
|
||||
if (feature.Properties.ContainsKey("min_height"))
|
||||
{
|
||||
var featureMinHeight = Convert.ToSingle(feature.Properties["min_height"]);
|
||||
minHeight = Math.Min(featureMinHeight, _options.maximumHeight);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ExtrusionType.AbsoluteHeight:
|
||||
maxHeight = _options.maximumHeight;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a7dfb85d33966074ebf2acd2de263474
|
||||
timeCreated: 1478553093
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,149 @@
|
||||
using Mapbox.Unity.Map;
|
||||
|
||||
namespace Mapbox.Unity.MeshGeneration.Modifiers
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using Mapbox.Unity.MeshGeneration.Data;
|
||||
|
||||
/// <summary>
|
||||
/// Line Mesh Modifier creates line polygons from a list of vertices. It offsets the original vertices to both sides using Width parameter and triangulates them manually.
|
||||
/// It also creates tiled UV mapping using the line length.
|
||||
/// MergeStartEnd parameter connects both edges of the line segment and creates a closed loop which is useful for some cases like pavements around a building block.
|
||||
/// </summary>
|
||||
[CreateAssetMenu(menuName = "Mapbox/Modifiers/Line Mesh Modifier")]
|
||||
public class LineMeshModifier : MeshModifier
|
||||
{
|
||||
[SerializeField]
|
||||
LineGeometryOptions _options;
|
||||
|
||||
private float _scaledWidth;
|
||||
public override ModifierType Type { get { return ModifierType.Preprocess; } }
|
||||
|
||||
public override void SetProperties(ModifierProperties properties)
|
||||
{
|
||||
_options = (LineGeometryOptions)properties;
|
||||
_options.PropertyHasChanged += UpdateModifier;
|
||||
|
||||
}
|
||||
|
||||
public override void Run(VectorFeatureUnity feature, MeshData md, float scale)
|
||||
{
|
||||
_scaledWidth = _options.Width * scale;
|
||||
ExtureLine(feature, md);
|
||||
}
|
||||
|
||||
public override void Run(VectorFeatureUnity feature, MeshData md, UnityTile tile = null)
|
||||
{
|
||||
_scaledWidth = tile != null ? _options.Width * tile.TileScale : _options.Width;
|
||||
ExtureLine(feature, md);
|
||||
}
|
||||
|
||||
private void ExtureLine(VectorFeatureUnity feature, MeshData md)
|
||||
{
|
||||
if (feature.Points.Count < 1)
|
||||
return;
|
||||
|
||||
foreach (var roadSegment in feature.Points)
|
||||
{
|
||||
var mdVertexCount = md.Vertices.Count;
|
||||
var roadSegmentCount = roadSegment.Count;
|
||||
for (int i = 1; i < roadSegmentCount * 2; i++)
|
||||
{
|
||||
md.Edges.Add(mdVertexCount + i);
|
||||
md.Edges.Add(mdVertexCount + i - 1);
|
||||
}
|
||||
md.Edges.Add(mdVertexCount);
|
||||
md.Edges.Add(mdVertexCount + (roadSegmentCount * 2) - 1);
|
||||
|
||||
var newVerticeList = new Vector3[roadSegmentCount * 2];
|
||||
var newNorms = new Vector3[roadSegmentCount * 2];
|
||||
var uvList = new Vector2[roadSegmentCount * 2];
|
||||
var newTangents = new Vector4[roadSegmentCount * 2];
|
||||
Vector3 norm;
|
||||
var lastUv = 0f;
|
||||
var p1 = Constants.Math.Vector3Zero;
|
||||
var p2 = Constants.Math.Vector3Zero;
|
||||
var p3 = Constants.Math.Vector3Zero;
|
||||
for (int i = 1; i < roadSegmentCount; i++)
|
||||
{
|
||||
p1 = roadSegment[i - 1];
|
||||
p2 = roadSegment[i];
|
||||
p3 = p2;
|
||||
if (i + 1 < roadSegmentCount)
|
||||
p3 = roadSegment[i + 1];
|
||||
|
||||
if (i == 1)
|
||||
{
|
||||
norm = GetNormal(p1, p1, p2) * _scaledWidth; //road width
|
||||
newVerticeList[0] = (p1 + norm);
|
||||
newVerticeList[roadSegmentCount * 2 - 1] = (p1 - norm);
|
||||
newNorms[0] = Constants.Math.Vector3Up;
|
||||
newNorms[roadSegmentCount * 2 - 1] = Constants.Math.Vector3Up;
|
||||
uvList[0] = new Vector2(0, 0);
|
||||
uvList[roadSegmentCount * 2 - 1] = new Vector2(1, 0);
|
||||
newTangents[0] = new Vector4(p2.x - p1.x, p2.y - p1.y, p2.z - p1.z, 1).normalized;
|
||||
newTangents[roadSegmentCount * 2 - 1] = newTangents[0];
|
||||
}
|
||||
|
||||
var dist = Vector3.Distance(p1, p2);
|
||||
lastUv += dist;
|
||||
norm = GetNormal(p1, p2, p3) * _scaledWidth;
|
||||
newVerticeList[i] = (p2 + norm);
|
||||
newVerticeList[2 * roadSegmentCount - 1 - i] = (p2 - norm);
|
||||
newNorms[i] = Constants.Math.Vector3Up;
|
||||
newNorms[2 * roadSegmentCount - 1 - i] = Constants.Math.Vector3Up;
|
||||
|
||||
uvList[i] = new Vector2(0, lastUv);
|
||||
uvList[2 * roadSegmentCount - 1 - i] = new Vector2(1, lastUv);
|
||||
|
||||
newTangents[i] = new Vector4(p2.x - p1.x, p2.y - p1.y, p2.z - p1.z, 1).normalized;
|
||||
newTangents[2 * roadSegmentCount - 1 - i] = newTangents[i];
|
||||
}
|
||||
|
||||
md.Vertices.AddRange(newVerticeList);
|
||||
md.Normals.AddRange(newNorms);
|
||||
md.UV[0].AddRange(uvList);
|
||||
md.Tangents.AddRange(newTangents);
|
||||
var lineTri = new List<int>();
|
||||
var n = roadSegmentCount;
|
||||
|
||||
for (int i = 0; i < n - 1; i++)
|
||||
{
|
||||
lineTri.Add(mdVertexCount + i);
|
||||
lineTri.Add(mdVertexCount + i + 1);
|
||||
lineTri.Add(mdVertexCount + 2 * n - 1 - i);
|
||||
|
||||
lineTri.Add(mdVertexCount + i + 1);
|
||||
lineTri.Add(mdVertexCount + 2 * n - i - 2);
|
||||
lineTri.Add(mdVertexCount + 2 * n - i - 1);
|
||||
}
|
||||
|
||||
if (md.Triangles.Count < 1)
|
||||
md.Triangles.Add(new List<int>());
|
||||
md.Triangles[0].AddRange(lineTri);
|
||||
}
|
||||
}
|
||||
|
||||
private Vector3 GetNormal(Vector3 p1, Vector3 newPos, Vector3 p2)
|
||||
{
|
||||
if (newPos == p1 || newPos == p2)
|
||||
{
|
||||
var n = (p2 - p1).normalized;
|
||||
return new Vector3(-n.z, 0, n.x);
|
||||
}
|
||||
|
||||
var b = (p2 - newPos).normalized + newPos;
|
||||
var a = (p1 - newPos).normalized + newPos;
|
||||
var t = (b - a).normalized;
|
||||
|
||||
if (t == Mapbox.Unity.Constants.Math.Vector3Zero)
|
||||
{
|
||||
var n = (p2 - p1).normalized;
|
||||
return new Vector3(-n.z, 0, n.x);
|
||||
}
|
||||
|
||||
return new Vector3(-t.z, 0, t.x);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9e1e9c060b6589e4e8b990a9c709bcdd
|
||||
timeCreated: 1478553093
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,172 @@
|
||||
namespace Mapbox.Unity.MeshGeneration.Modifiers
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using Mapbox.Unity.MeshGeneration.Data;
|
||||
using Assets.Mapbox.Unity.MeshGeneration.Modifiers.MeshModifiers;
|
||||
|
||||
[CreateAssetMenu(menuName = "Mapbox/Modifiers/Loft Modifier")]
|
||||
public class LoftModifier : MeshModifier
|
||||
{
|
||||
public override ModifierType Type { get { return ModifierType.Preprocess; } }
|
||||
public GameObject Slice;
|
||||
public bool _closeEdges = false;
|
||||
private int _counter = 0;
|
||||
|
||||
private List<Vector3> _slice;
|
||||
private int _sliceCount;
|
||||
private float _sliceTotalMagnitude;
|
||||
private Vector2[] _sliceUvs;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
_slice = new List<Vector3>();
|
||||
foreach (Transform tr in Slice.transform)
|
||||
{
|
||||
_slice.Add(tr.position);
|
||||
}
|
||||
_sliceCount = _slice.Count;
|
||||
|
||||
_sliceTotalMagnitude = 0;
|
||||
for (int i = 0; i < _sliceCount - 1; i++)
|
||||
{
|
||||
_sliceTotalMagnitude += (_slice[i + 1] - _slice[i]).magnitude;
|
||||
}
|
||||
_sliceUvs = new Vector2[_sliceCount];
|
||||
_sliceUvs[0] = new Vector2(0, 0);
|
||||
for (int i = 0; i < _sliceCount - 1; i++)
|
||||
{
|
||||
_sliceUvs[i + 1] = new Vector2(0, _sliceUvs[i].y + (_slice[i + 1] - _slice[i]).magnitude / _sliceTotalMagnitude);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Run(VectorFeatureUnity feature, MeshData md, UnityTile tile = null)
|
||||
{
|
||||
if (feature.Points.Count < 1)
|
||||
return;
|
||||
|
||||
foreach (var roadSegment in feature.Points)
|
||||
{
|
||||
_counter = roadSegment.Count;
|
||||
if (_counter <= 1)
|
||||
continue;
|
||||
|
||||
var vl = new List<Vector3>(_sliceCount * _counter);
|
||||
var edges = new List<Vector3>(_counter);
|
||||
int co = 0;
|
||||
|
||||
for (int j = 0; j < _counter; j++)
|
||||
{
|
||||
var current = Constants.Math.Vector3Zero;
|
||||
|
||||
current = roadSegment[j];
|
||||
Vector3 dirCurrent, dir1, dir2;
|
||||
if (j > 0 && j < (_counter - 1))
|
||||
{
|
||||
dir1 = (roadSegment[j] - roadSegment[j - 1]).normalized;
|
||||
dir2 = (roadSegment[j + 1] - roadSegment[j]).normalized;
|
||||
dirCurrent = (dir2 + dir1).normalized;
|
||||
}
|
||||
else if (j == 0) //first
|
||||
{
|
||||
dirCurrent = (roadSegment[j + 1] - roadSegment[j]).normalized;
|
||||
}
|
||||
else //last
|
||||
{
|
||||
dirCurrent = (roadSegment[j] - roadSegment[j - 1]).normalized;
|
||||
}
|
||||
var q = Quaternion.LookRotation(dirCurrent);
|
||||
|
||||
co = _slice.Count;
|
||||
for (int i = 0; i < co; i++)
|
||||
{
|
||||
var p = q * _slice[i];
|
||||
vl.Add(p + current);
|
||||
if (i == co - 1) //last item capped
|
||||
{
|
||||
edges.Add(p + current);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (md.Triangles.Count == 0)
|
||||
{
|
||||
md.Triangles.Add(new List<int>());
|
||||
}
|
||||
md.Vertices.Capacity = md.Vertices.Count + (vl.Count - _sliceCount) * 4;
|
||||
md.Normals.Capacity = md.Normals.Count + (vl.Count - _sliceCount) * 4;
|
||||
md.Triangles.Capacity = md.Triangles.Count + (vl.Count - _sliceCount) * 6;
|
||||
|
||||
var uvDist = 0f;
|
||||
float edMag = 0f, h = 0f;
|
||||
co = 0;
|
||||
Vector3 norm;
|
||||
|
||||
for (int i = 0; i < _counter - 1; i++)
|
||||
{
|
||||
for (int j = 0; j < _sliceCount - 1; j++)
|
||||
{
|
||||
var ind = i * _sliceCount + j;
|
||||
var ed = vl[ind + _sliceCount] - vl[ind];
|
||||
edMag = ed.magnitude;
|
||||
co = md.Vertices.Count;
|
||||
norm = Vector3.Cross(vl[ind] - vl[ind + 1], vl[ind + _sliceCount] - vl[ind]).normalized;
|
||||
md.Vertices.Add(vl[ind]);
|
||||
md.Vertices.Add(vl[ind + 1]);
|
||||
md.Vertices.Add(vl[ind + _sliceCount]);
|
||||
md.Vertices.Add(vl[ind + _sliceCount + 1]);
|
||||
|
||||
//h = (vl[ind + 1] - vl[ind]).magnitude;
|
||||
h = (float)j / _sliceCount;
|
||||
|
||||
md.UV[0].Add(new Vector2(uvDist, ((float)j - 1) / _sliceCount));
|
||||
md.UV[0].Add(new Vector2(uvDist, h));
|
||||
md.UV[0].Add(new Vector2(uvDist + edMag, ((float)j - 1) / _sliceCount));
|
||||
md.UV[0].Add(new Vector2(uvDist + edMag, h));
|
||||
|
||||
md.Tangents.Add(new Vector4(ed.normalized.x, ed.normalized.y, ed.normalized.z, 1));
|
||||
md.Tangents.Add(new Vector4(ed.normalized.x, ed.normalized.y, ed.normalized.z, 1));
|
||||
md.Tangents.Add(new Vector4(ed.normalized.x, ed.normalized.y, ed.normalized.z, 1));
|
||||
md.Tangents.Add(new Vector4(ed.normalized.x, ed.normalized.y, ed.normalized.z, 1));
|
||||
|
||||
md.Normals.Add(norm);
|
||||
md.Normals.Add(norm);
|
||||
md.Normals.Add(norm);
|
||||
md.Normals.Add(norm);
|
||||
|
||||
md.Triangles[0].Add(co);
|
||||
md.Triangles[0].Add(co + 2);
|
||||
md.Triangles[0].Add(co + 1);
|
||||
|
||||
md.Triangles[0].Add(co + 1);
|
||||
md.Triangles[0].Add(co + 2);
|
||||
md.Triangles[0].Add(co + 3);
|
||||
}
|
||||
uvDist += edMag;
|
||||
}
|
||||
|
||||
if (_closeEdges && edges.Count > 2)
|
||||
{
|
||||
if (md.Triangles.Count < 2)
|
||||
{
|
||||
md.Triangles.Add(new List<int>());
|
||||
}
|
||||
|
||||
var flatData = EarcutLibrary.Flatten(new List<List<Vector3>>() { edges });
|
||||
var result = EarcutLibrary.Earcut(flatData.Vertices, flatData.Holes, flatData.Dim);
|
||||
|
||||
md.Triangles[1].AddRange(result.Select(x => md.Vertices.Count + x).ToList());
|
||||
for (int i = 0; i < edges.Count; i++)
|
||||
{
|
||||
md.Vertices.Add(edges[i]);
|
||||
md.Normals.Add(Constants.Math.Vector3Up);
|
||||
md.UV[0].Add(new Vector2(edges[i].x, edges[i].z));
|
||||
md.Tangents.Add(new Vector4(1,0,0,1));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 634e53b543236f246833fc88fea03aa0
|
||||
timeCreated: 1507750860
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,121 @@
|
||||
namespace Mapbox.Unity.MeshGeneration.Modifiers
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using Mapbox.Unity.MeshGeneration.Data;
|
||||
using Assets.Mapbox.Unity.MeshGeneration.Modifiers.MeshModifiers;
|
||||
using System;
|
||||
|
||||
/// <summary>
|
||||
/// Polygon modifier creates the polygon (vertex&triangles) using the original vertex list.
|
||||
/// Currently uses Triangle.Net for triangulation, which occasionally adds extra vertices to maintain a good triangulation so output vertex list might not be exactly same as the original vertex list.
|
||||
/// </summary>
|
||||
[CreateAssetMenu(menuName = "Mapbox/Modifiers/Polygon Mesh Modifier")]
|
||||
public class PolygonMeshModifier : MeshGenerationBase
|
||||
{
|
||||
public override ModifierType Type { get { return ModifierType.Preprocess; } }
|
||||
|
||||
public bool IsClockwise(IList<Vector3> vertices)
|
||||
{
|
||||
double sum = 0.0;
|
||||
_counter = vertices.Count;
|
||||
for (int i = 0; i < _counter; i++)
|
||||
{
|
||||
_v1 = vertices[i];
|
||||
_v2 = vertices[(i + 1) % _counter];
|
||||
sum += (_v2.x - _v1.x) * (_v2.z + _v1.z);
|
||||
}
|
||||
return sum > 0.0;
|
||||
}
|
||||
|
||||
private int _counter, _secondCounter;
|
||||
private Vector3 _v1, _v2;
|
||||
|
||||
public override void Run(VectorFeatureUnity feature, MeshData md, UnityTile tile = null)
|
||||
{
|
||||
if(Criteria!=null && Criteria.Count > 0)
|
||||
{
|
||||
foreach(var criterion in Criteria)
|
||||
{
|
||||
if(criterion.ShouldReplaceFeature(feature))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_secondCounter = feature.Points.Count;
|
||||
var subset = new List<List<Vector3>>(_secondCounter);
|
||||
Data flatData = null;
|
||||
List<int> result = null;
|
||||
var currentIndex = 0;
|
||||
int vertCount = 0, c2 = 0;
|
||||
List<int> triList = null;
|
||||
List<Vector3> sub = null;
|
||||
|
||||
for (int i = 0; i < _secondCounter; i++)
|
||||
{
|
||||
sub = feature.Points[i];
|
||||
//earcut is built to handle one polygon with multiple holes
|
||||
//point data can contain multiple polygons though, so we're handling them separately here
|
||||
|
||||
vertCount = md.Vertices.Count;
|
||||
if (IsClockwise(sub) && vertCount > 0)
|
||||
{
|
||||
flatData = EarcutLibrary.Flatten(subset);
|
||||
result = EarcutLibrary.Earcut(flatData.Vertices, flatData.Holes, flatData.Dim);
|
||||
c2 = result.Count;
|
||||
if (triList == null)
|
||||
{
|
||||
triList = new List<int>(c2);
|
||||
}
|
||||
else
|
||||
{
|
||||
triList.Capacity = triList.Count + c2;
|
||||
}
|
||||
|
||||
for (int j = 0; j < c2; j++)
|
||||
{
|
||||
triList.Add(result[j] + currentIndex);
|
||||
}
|
||||
currentIndex = vertCount;
|
||||
subset.Clear();
|
||||
}
|
||||
|
||||
subset.Add(sub);
|
||||
|
||||
c2 = sub.Count;
|
||||
md.Vertices.Capacity = md.Vertices.Count + c2;
|
||||
md.Normals.Capacity = md.Normals.Count + c2;
|
||||
md.Edges.Capacity = md.Edges.Count + c2 * 2;
|
||||
for (int j = 0; j < c2; j++)
|
||||
{
|
||||
md.Edges.Add(vertCount + ((j+ 1) % c2));
|
||||
md.Edges.Add(vertCount + j);
|
||||
md.Vertices.Add(sub[j]);
|
||||
md.Tangents.Add(Constants.Math.Vector3Forward);
|
||||
md.Normals.Add(Constants.Math.Vector3Up);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
flatData = EarcutLibrary.Flatten(subset);
|
||||
result = EarcutLibrary.Earcut(flatData.Vertices, flatData.Holes, flatData.Dim);
|
||||
c2 = result.Count;
|
||||
if (triList == null)
|
||||
{
|
||||
triList = new List<int>(c2);
|
||||
}
|
||||
else
|
||||
{
|
||||
triList.Capacity = triList.Count + c2;
|
||||
}
|
||||
for (int i = 0; i < c2; i++)
|
||||
{
|
||||
triList.Add(result[i] + currentIndex);
|
||||
}
|
||||
|
||||
md.Triangles.Add(triList);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6c7ba03c743d46c4a99f4971c74f7cbd
|
||||
timeCreated: 1478553093
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,148 @@
|
||||
namespace Mapbox.Unity.MeshGeneration.Modifiers
|
||||
{
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
using Mapbox.Unity.MeshGeneration.Data;
|
||||
using Mapbox.Unity.Utilities;
|
||||
using Mapbox.Unity.Map;
|
||||
using System;
|
||||
|
||||
[System.Serializable]
|
||||
public class FeatureBundle
|
||||
{
|
||||
//public name param will be displayed in inspector list ui instead of element x...
|
||||
[HideInInspector] public string Name;
|
||||
|
||||
public bool active;
|
||||
|
||||
public GameObject prefab;
|
||||
public bool scaleDownWithWorld = true;
|
||||
|
||||
[Geocode] public List<string> _prefabLocations = new List<string>();
|
||||
|
||||
public List<string> _explicitlyBlockedFeatureIds = new List<string>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ReplaceFeatureCollectionModifier aggregates multiple ReplaceFeatureModifier objects into one modifier.
|
||||
/// </summary>
|
||||
[CreateAssetMenu(menuName = "Mapbox/Modifiers/Replace Feature Collection Modifier")]
|
||||
public class ReplaceFeatureCollectionModifier : GameObjectModifier, IReplacementCriteria
|
||||
{
|
||||
public List<FeatureBundle> features = new List<FeatureBundle>();
|
||||
|
||||
private List<ReplaceFeatureModifier> _replaceFeatureModifiers;
|
||||
|
||||
//update all names to make inspector look better...
|
||||
private void OnValidate()
|
||||
{
|
||||
for (int i = 0; i < features.Count; i++)
|
||||
{
|
||||
features[i].Name = (features[i].prefab == null) ? "Feature" : features[i].prefab.name;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
if (_replaceFeatureModifiers != null && _replaceFeatureModifiers.Count > 0)
|
||||
{
|
||||
foreach (var replaceFeatureModifier in _replaceFeatureModifiers)
|
||||
{
|
||||
if (replaceFeatureModifier != null)
|
||||
{
|
||||
replaceFeatureModifier.ClearCaches();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
_replaceFeatureModifiers = new List<ReplaceFeatureModifier>();
|
||||
foreach (FeatureBundle feature in features)
|
||||
{
|
||||
ReplaceFeatureModifier replaceFeatureModifier = ScriptableObject.CreateInstance<ReplaceFeatureModifier>();
|
||||
|
||||
replaceFeatureModifier.Active = feature.active;
|
||||
replaceFeatureModifier.SpawnPrefabOptions = new SpawnPrefabOptions()
|
||||
{
|
||||
prefab = feature.prefab,
|
||||
scaleDownWithWorld = feature.scaleDownWithWorld
|
||||
};
|
||||
replaceFeatureModifier.PrefabLocations = new List<string>(feature._prefabLocations);
|
||||
replaceFeatureModifier.BlockedIds = new List<string>(feature._explicitlyBlockedFeatureIds);
|
||||
replaceFeatureModifier.Initialize();
|
||||
|
||||
_replaceFeatureModifiers.Add(replaceFeatureModifier);
|
||||
}
|
||||
}
|
||||
|
||||
public override void FeaturePreProcess(VectorFeatureUnity feature)
|
||||
{
|
||||
foreach (ReplaceFeatureModifier modifier in _replaceFeatureModifiers)
|
||||
{
|
||||
if (modifier == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
modifier.FeaturePreProcess(feature);
|
||||
}
|
||||
}
|
||||
|
||||
public override void SetProperties(ModifierProperties properties)
|
||||
{
|
||||
foreach (ReplaceFeatureModifier modifier in _replaceFeatureModifiers)
|
||||
{
|
||||
if (modifier == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
modifier.SetProperties(properties);
|
||||
}
|
||||
}
|
||||
|
||||
public bool ShouldReplaceFeature(VectorFeatureUnity feature)
|
||||
{
|
||||
foreach (ReplaceFeatureModifier modifier in _replaceFeatureModifiers)
|
||||
{
|
||||
if (modifier == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (modifier.ShouldReplaceFeature(feature))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public override void Run(VectorEntity ve, UnityTile tile)
|
||||
{
|
||||
foreach (ReplaceFeatureModifier modifier in _replaceFeatureModifiers)
|
||||
{
|
||||
modifier.Run(ve, tile);
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnPoolItem(VectorEntity vectorEntity)
|
||||
{
|
||||
foreach (ReplaceFeatureModifier modifier in _replaceFeatureModifiers)
|
||||
{
|
||||
modifier.OnPoolItem(vectorEntity);
|
||||
}
|
||||
}
|
||||
|
||||
public override void ClearCaches()
|
||||
{
|
||||
foreach (var subModules in _replaceFeatureModifiers)
|
||||
{
|
||||
subModules.ClearCaches();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 13ade7ea2a146442b9575619cf724737
|
||||
timeCreated: 1530570387
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,319 @@
|
||||
namespace Mapbox.Unity.MeshGeneration.Modifiers
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using Mapbox.Unity.MeshGeneration.Data;
|
||||
using System;
|
||||
using Mapbox.Unity.Map;
|
||||
using Mapbox.Utils;
|
||||
using Mapbox.Unity.Utilities;
|
||||
using Mapbox.VectorTile.Geometry;
|
||||
using Mapbox.Unity.MeshGeneration.Interfaces;
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// ReplaceBuildingFeatureModifier takes in POIs and checks if the feature layer has those points and deletes them
|
||||
/// </summary>
|
||||
[CreateAssetMenu(menuName = "Mapbox/Modifiers/Replace Feature Modifier")]
|
||||
public class ReplaceFeatureModifier : GameObjectModifier, IReplacementCriteria
|
||||
{
|
||||
|
||||
private List<Vector2d> _latLonToSpawn;
|
||||
|
||||
private Dictionary<ulong, GameObject> _objects;
|
||||
private Dictionary<ulong, Vector2d> _objectPosition;
|
||||
private GameObject _poolGameObject;
|
||||
[SerializeField]
|
||||
private SpawnPrefabOptions _options;
|
||||
private List<GameObject> _prefabList = new List<GameObject>();
|
||||
|
||||
[SerializeField]
|
||||
[Geocode]
|
||||
private List<string> _prefabLocations;
|
||||
|
||||
[SerializeField]
|
||||
private List<string> _explicitlyBlockedFeatureIds;
|
||||
//maximum distance to trigger feature replacement ( in tile space )
|
||||
private const float _maxDistanceToBlockFeature_tilespace = 1000f;
|
||||
|
||||
/// <summary>
|
||||
/// List of featureIds to test against.
|
||||
/// We need a list of featureIds per location.
|
||||
/// A list is required since buildings on tile boundary will have multiple id's for the same feature.
|
||||
/// </summary>
|
||||
private List<List<string>> _featureId;
|
||||
private string _tempFeatureId;
|
||||
|
||||
public SpawnPrefabOptions SpawnPrefabOptions
|
||||
{
|
||||
set
|
||||
{
|
||||
_options = value;
|
||||
}
|
||||
}
|
||||
|
||||
public List<string> PrefabLocations
|
||||
{
|
||||
set
|
||||
{
|
||||
_prefabLocations = value;
|
||||
}
|
||||
}
|
||||
|
||||
public List<string> BlockedIds
|
||||
{
|
||||
set
|
||||
{
|
||||
_explicitlyBlockedFeatureIds = value;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
//duplicate the list of lat/lons to track which coordinates have already been spawned
|
||||
|
||||
_featureId = new List<List<string>>();
|
||||
|
||||
for (int i = 0; i < _prefabLocations.Count; i++)
|
||||
{
|
||||
_featureId.Add(new List<string>());
|
||||
}
|
||||
if (_objects == null)
|
||||
{
|
||||
_objects = new Dictionary<ulong, GameObject>();
|
||||
_objectPosition = new Dictionary<ulong, Vector2d>();
|
||||
_poolGameObject = new GameObject("_inactive_prefabs_pool");
|
||||
}
|
||||
_latLonToSpawn = new List<Vector2d>();
|
||||
foreach (var loc in _prefabLocations)
|
||||
{
|
||||
_latLonToSpawn.Add(Conversions.StringToLatLon(loc));
|
||||
}
|
||||
}
|
||||
|
||||
public override void SetProperties(ModifierProperties properties)
|
||||
{
|
||||
_options = (SpawnPrefabOptions)properties;
|
||||
}
|
||||
|
||||
public override void FeaturePreProcess(VectorFeatureUnity feature)
|
||||
{
|
||||
int index = -1;
|
||||
foreach (var point in _prefabLocations)
|
||||
{
|
||||
try
|
||||
{
|
||||
index++;
|
||||
var coord = Conversions.StringToLatLon(point);
|
||||
if (feature.ContainsLatLon(coord) && (feature.Data.Id != 0))
|
||||
{
|
||||
_featureId[index] = (_featureId[index] == null) ? new List<string>() : _featureId[index];
|
||||
_tempFeatureId = feature.Data.Id.ToString();
|
||||
string idCandidate = (_tempFeatureId.Length <= 3) ? _tempFeatureId : _tempFeatureId.Substring(0, _tempFeatureId.Length - 3);
|
||||
_featureId[index].Add(idCandidate);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check the feature against the list of lat/lons in the modifier
|
||||
/// </summary>
|
||||
/// <returns><c>true</c>, if the feature overlaps with a lat/lon in the modifier <c>false</c> otherwise.</returns>
|
||||
/// <param name="feature">Feature.</param>
|
||||
public bool ShouldReplaceFeature(VectorFeatureUnity feature)
|
||||
{
|
||||
int index = -1;
|
||||
|
||||
//preventing spawning of explicitly blocked features
|
||||
foreach (var blockedId in _explicitlyBlockedFeatureIds)
|
||||
{
|
||||
if (feature.Data.Id.ToString() == blockedId)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var point in _prefabLocations)
|
||||
{
|
||||
try
|
||||
{
|
||||
index++;
|
||||
if (_featureId[index] != null)
|
||||
{
|
||||
foreach (var featureId in _featureId[index])
|
||||
{
|
||||
var latlngVector = Conversions.StringToLatLon(point);
|
||||
var from = Conversions.LatLonToMeters(latlngVector.x, latlngVector.y);
|
||||
var to = new Vector2d((feature.Points[0][0].x / feature.Tile.TileScale) + feature.Tile.Rect.Center.x, (feature.Points[0][0].z / feature.Tile.TileScale) + feature.Tile.Rect.Center.y);
|
||||
var dist = Vector2d.Distance(from, to);
|
||||
if (dist > 500)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (feature.Data.Id.ToString().StartsWith(featureId, StringComparison.CurrentCulture))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogException(e);
|
||||
}
|
||||
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public override void Run(VectorEntity ve, UnityTile tile)
|
||||
{
|
||||
//replace the feature only once per lat/lon
|
||||
Vector2d latLong = Vector2d.zero;
|
||||
if (ShouldSpawnFeature(ve.Feature, out latLong))
|
||||
{
|
||||
SpawnPrefab(ve, tile, latLong);
|
||||
}
|
||||
}
|
||||
|
||||
private void SpawnPrefab(VectorEntity ve, UnityTile tile, Vector2d latLong)
|
||||
{
|
||||
GameObject go;
|
||||
|
||||
var featureId = ve.Feature.Data.Id;
|
||||
if (_objects.ContainsKey(featureId))
|
||||
{
|
||||
go = _objects[featureId];
|
||||
go.SetActive(true);
|
||||
go.transform.SetParent(ve.GameObject.transform, false);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
go = Instantiate(_options.prefab);
|
||||
_prefabList.Add(go);
|
||||
_objects.Add(featureId, go);
|
||||
_objectPosition.Add(featureId, latLong);
|
||||
go.transform.SetParent(ve.GameObject.transform, false);
|
||||
}
|
||||
|
||||
PositionScaleRectTransform(ve, tile, go, latLong);
|
||||
|
||||
if (_options.AllPrefabsInstatiated != null)
|
||||
{
|
||||
_options.AllPrefabsInstatiated(_prefabList);
|
||||
}
|
||||
}
|
||||
|
||||
public void PositionScaleRectTransform(VectorEntity ve, UnityTile tile, GameObject go, Vector2d latLong)
|
||||
{
|
||||
go.transform.localScale = _options.prefab.transform.localScale;
|
||||
RectTransform goRectTransform;
|
||||
IFeaturePropertySettable settable = null;
|
||||
var latLongPosition = new Vector3();
|
||||
var centroidVector = new Vector3();
|
||||
foreach (var point in ve.Feature.Points[0])
|
||||
{
|
||||
centroidVector += point;
|
||||
}
|
||||
centroidVector = centroidVector / ve.Feature.Points[0].Count;
|
||||
|
||||
latLongPosition = Conversions.LatitudeLongitudeToUnityTilePosition(latLong, tile.CurrentZoom, tile.TileScale, 4096).ToVector3xz();
|
||||
latLongPosition.y = centroidVector.y;
|
||||
|
||||
go.name = ve.Feature.Data.Id.ToString();
|
||||
|
||||
goRectTransform = go.GetComponent<RectTransform>();
|
||||
if (goRectTransform == null)
|
||||
{
|
||||
go.transform.localPosition = centroidVector;
|
||||
}
|
||||
else
|
||||
{
|
||||
goRectTransform.anchoredPosition3D = centroidVector;
|
||||
}
|
||||
//go.transform.localScale = Constants.Math.Vector3One;
|
||||
|
||||
settable = go.GetComponent<IFeaturePropertySettable>();
|
||||
if (settable != null)
|
||||
{
|
||||
settable.Set(ve.Feature.Properties);
|
||||
}
|
||||
|
||||
if (_options.scaleDownWithWorld)
|
||||
{
|
||||
go.transform.localScale = (go.transform.localScale * (tile.TileScale));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the feature should be used to spawn a prefab, once per lat/lon
|
||||
/// </summary>
|
||||
/// <returns><c>true</c>, if the feature should be spawned <c>false</c> otherwise.</returns>
|
||||
/// <param name="feature">Feature.</param>
|
||||
private bool ShouldSpawnFeature(VectorFeatureUnity feature, out Vector2d latLong)
|
||||
{
|
||||
latLong = Vector2d.zero;
|
||||
if (feature == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_objects.ContainsKey(feature.Data.Id))
|
||||
{
|
||||
_objectPosition.TryGetValue(feature.Data.Id, out latLong);
|
||||
_latLonToSpawn.Remove(latLong);
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach (var point in _latLonToSpawn)
|
||||
{
|
||||
if (feature.ContainsLatLon(point))
|
||||
{
|
||||
_latLonToSpawn.Remove(point);
|
||||
latLong = point;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
public override void OnPoolItem(VectorEntity vectorEntity)
|
||||
{
|
||||
base.OnPoolItem(vectorEntity);
|
||||
var featureId = vectorEntity.Feature.Data.Id;
|
||||
|
||||
if (!_objects.ContainsKey(featureId))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var go = _objects[featureId];
|
||||
if (go == null || _poolGameObject == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
go.SetActive(false);
|
||||
go.transform.SetParent(_poolGameObject.transform, false);
|
||||
}
|
||||
|
||||
public override void ClearCaches()
|
||||
{
|
||||
foreach (var gameObject in _objects.Values)
|
||||
{
|
||||
Destroy(gameObject);
|
||||
}
|
||||
_objects.Clear();
|
||||
_objectPosition.Clear();
|
||||
Destroy(_poolGameObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f386309a8f86f48aaabbbd02b9f95059
|
||||
timeCreated: 1525817443
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,46 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using Mapbox.Unity.MeshGeneration.Data;
|
||||
|
||||
namespace Mapbox.Unity.MeshGeneration.Modifiers
|
||||
{
|
||||
[CreateAssetMenu(menuName = "Mapbox/Modifiers/Smooth Line Modifier")]
|
||||
public class SmoothLineModifier : MeshModifier
|
||||
{
|
||||
public override ModifierType Type { get { return ModifierType.Preprocess; } }
|
||||
|
||||
public int _maxEdgeSectionCount = 40;
|
||||
public int _preferredEdgeSectionLength = 10;
|
||||
private int _counter, _counter2;
|
||||
|
||||
public override void Run(VectorFeatureUnity feature, MeshData md, UnityTile tile = null)
|
||||
{
|
||||
_counter = feature.Points.Count;
|
||||
|
||||
for (int i = 0; i < _counter; i++)
|
||||
{
|
||||
var nl = new List<Vector3>();
|
||||
_counter2 = feature.Points[i].Count;
|
||||
for (int j = 1; j < _counter2; j++)
|
||||
{
|
||||
nl.Add(feature.Points[i][j - 1]);
|
||||
var dist = Vector3.Distance(feature.Points[i][j - 1], feature.Points[i][j]);
|
||||
var step = Math.Min(_maxEdgeSectionCount, dist / _preferredEdgeSectionLength);
|
||||
if (step > 1)
|
||||
{
|
||||
var counter = 1;
|
||||
while (counter < step)
|
||||
{
|
||||
var nv = Vector3.Lerp(feature.Points[i][j - 1], feature.Points[i][j], Mathf.Min(1, counter / step));
|
||||
nl.Add(nv);
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
nl.Add(feature.Points[i][j]);
|
||||
}
|
||||
feature.Points[i] = nl;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9558f79dbf9b00d488e061a68f8394aa
|
||||
timeCreated: 1492731089
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,46 @@
|
||||
namespace Mapbox.Unity.MeshGeneration.Modifiers
|
||||
{
|
||||
using UnityEngine;
|
||||
using Mapbox.Unity.MeshGeneration.Data;
|
||||
|
||||
[CreateAssetMenu(menuName = "Mapbox/Modifiers/Snap Terrain Modifier")]
|
||||
public class SnapTerrainModifier : MeshModifier
|
||||
{
|
||||
public override ModifierType Type { get { return ModifierType.Preprocess; } }
|
||||
|
||||
private double scaledX;
|
||||
private double scaledY;
|
||||
private int _counter;
|
||||
|
||||
public override void Run(VectorFeatureUnity feature, MeshData md, UnityTile tile = null)
|
||||
{
|
||||
scaledX = tile.Rect.Size.x * tile.TileScale;
|
||||
scaledY = tile.Rect.Size.y * tile.TileScale;
|
||||
_counter = md.Vertices.Count;
|
||||
if (_counter > 0)
|
||||
{
|
||||
for (int i = 0; i < _counter; i++)
|
||||
{
|
||||
var h = tile.QueryHeightData(
|
||||
(float)((md.Vertices[i].x + md.PositionInTile.x + scaledX / 2) / scaledX),
|
||||
(float)((md.Vertices[i].z + md.PositionInTile.z + scaledY / 2) / scaledY));
|
||||
md.Vertices[i] += new Vector3(0, h, 0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var sub in feature.Points)
|
||||
{
|
||||
_counter = sub.Count;
|
||||
for (int i = 0; i < _counter; i++)
|
||||
{
|
||||
var h = tile.QueryHeightData(
|
||||
(float)((sub[i].x + md.PositionInTile.x + scaledX / 2) / scaledX),
|
||||
(float)((sub[i].z + md.PositionInTile.z + scaledY / 2) / scaledY));
|
||||
sub[i] += new Vector3(0, h, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 40bee3110109c0848bb86c9b5c436039
|
||||
timeCreated: 1492732222
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,54 @@
|
||||
namespace Mapbox.Unity.MeshGeneration.Modifiers
|
||||
{
|
||||
using Mapbox.Unity.Map;
|
||||
using Mapbox.Unity.MeshGeneration.Data;
|
||||
using UnityEngine;
|
||||
|
||||
[CreateAssetMenu(menuName = "Mapbox/Modifiers/Snap Terrain Raycast Modifier")]
|
||||
public class SnapTerrainRaycastModifier : MeshModifier
|
||||
{
|
||||
private const int RAY_LENGTH = 50;
|
||||
|
||||
[SerializeField]
|
||||
private LayerMask _terrainMask;
|
||||
private double scaledX;
|
||||
private double scaledY;
|
||||
|
||||
public override ModifierType Type
|
||||
{
|
||||
get { return ModifierType.Preprocess; }
|
||||
}
|
||||
|
||||
public override void Run(VectorFeatureUnity feature, MeshData md, UnityTile tile = null)
|
||||
{
|
||||
scaledX = tile.Rect.Size.x * tile.TileScale;
|
||||
scaledY = tile.Rect.Size.y * tile.TileScale;
|
||||
|
||||
foreach (var sub in feature.Points)
|
||||
{
|
||||
for (int i = 0; i < sub.Count; i++)
|
||||
{
|
||||
var h = tile.QueryHeightData((float)((sub[i].x + md.PositionInTile.x + scaledX / 2) / scaledX), (float)((sub[i].z + md.PositionInTile.z + scaledY / 2) / scaledY));
|
||||
|
||||
RaycastHit hit;
|
||||
Vector3 rayCenter =
|
||||
new Vector3(sub[i].x + md.PositionInTile.x + tile.transform.position.x,
|
||||
h + RAY_LENGTH / 2,
|
||||
sub[i].z + md.PositionInTile.z + tile.transform.position.z);
|
||||
|
||||
|
||||
|
||||
if (Physics.Raycast(rayCenter, Vector3.down, out hit, RAY_LENGTH * 5, _terrainMask))
|
||||
{
|
||||
sub[i] += new Vector3(0, hit.point.y + md.PositionInTile.y - tile.transform.position.y, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Raycasting sometimes fails at terrain boundaries, fallback to tile height data.
|
||||
sub[i] += new Vector3(0, h, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 41aa014e797344c2c99c979bad017646
|
||||
timeCreated: 1492732222
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,124 @@
|
||||
namespace Mapbox.Unity.MeshGeneration.Modifiers
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using Mapbox.Unity.MeshGeneration.Data;
|
||||
using System;
|
||||
using Mapbox.Unity.Map;
|
||||
using Mapbox.Utils;
|
||||
|
||||
/// <summary>
|
||||
/// UV Modifier works only with (and right after) Polygon Modifier and not with Line Mesh Modifier.
|
||||
/// If UseSatelliteRoof parameter is false, it creates a tiled UV map, otherwise it creates a stretched UV map.
|
||||
/// </summary>
|
||||
[CreateAssetMenu(menuName = "Mapbox/Modifiers/UV Modifier")]
|
||||
public class UvModifier : MeshModifier
|
||||
{
|
||||
UVModifierOptions _options;
|
||||
//public UvMapType UvType;
|
||||
public override ModifierType Type { get { return ModifierType.Preprocess; } }
|
||||
|
||||
private int _mdVertexCount;
|
||||
private Vector2d _size;
|
||||
private Vector3 _vert;
|
||||
private List<Vector2> _uv = new List<Vector2>();
|
||||
|
||||
#region Atlas Fields
|
||||
//texture uv fields
|
||||
//public AtlasInfo AtlasInfo;
|
||||
private AtlasEntity _currentFacade;
|
||||
private Quaternion _textureDirection;
|
||||
private Vector2[] _textureUvCoordinates;
|
||||
private Vector3 _vertexRelativePos;
|
||||
private Vector3 _firstVert;
|
||||
|
||||
private float minx;
|
||||
private float miny;
|
||||
private float maxx;
|
||||
private float maxy;
|
||||
#endregion
|
||||
|
||||
public override void SetProperties(ModifierProperties properties)
|
||||
{
|
||||
_options = (UVModifierOptions)properties;
|
||||
_options.PropertyHasChanged += UpdateModifier;
|
||||
}
|
||||
|
||||
public override void UnbindProperties()
|
||||
{
|
||||
_options.PropertyHasChanged -= UpdateModifier;
|
||||
}
|
||||
|
||||
public override void Run(VectorFeatureUnity feature, MeshData md, UnityTile tile = null)
|
||||
{
|
||||
if (md.Vertices.Count == 0 || feature == null || feature.Points.Count < 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_uv.Clear();
|
||||
_mdVertexCount = md.Vertices.Count;
|
||||
_size = md.TileRect.Size;
|
||||
|
||||
if (_options.texturingType != UvMapType.Atlas && _options.texturingType != UvMapType.AtlasWithColorPalette)
|
||||
{
|
||||
for (int i = 0; i < _mdVertexCount; i++)
|
||||
{
|
||||
_vert = md.Vertices[i];
|
||||
|
||||
if (_options.style == StyleTypes.Satellite)
|
||||
{
|
||||
var fromBottomLeft = new Vector2((float)(((_vert.x + md.PositionInTile.x) / tile.TileScale + _size.x / 2) / _size.x),
|
||||
(float)(((_vert.z + md.PositionInTile.z) / tile.TileScale + _size.x / 2) / _size.x));
|
||||
_uv.Add(fromBottomLeft);
|
||||
}
|
||||
else if (_options.texturingType == UvMapType.Tiled)
|
||||
{
|
||||
_uv.Add(new Vector2(_vert.x, _vert.z));
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (_options.texturingType == UvMapType.Atlas || _options.texturingType == UvMapType.AtlasWithColorPalette)
|
||||
{
|
||||
_currentFacade = _options.atlasInfo.Roofs[UnityEngine.Random.Range(0, _options.atlasInfo.Roofs.Count)];
|
||||
|
||||
minx = float.MaxValue;
|
||||
miny = float.MaxValue;
|
||||
maxx = float.MinValue;
|
||||
maxy = float.MinValue;
|
||||
|
||||
_textureUvCoordinates = new Vector2[_mdVertexCount];
|
||||
_textureDirection = Quaternion.FromToRotation((md.Vertices[0] - md.Vertices[1]), Mapbox.Unity.Constants.Math.Vector3Right);
|
||||
_textureUvCoordinates[0] = new Vector2(0, 0);
|
||||
_firstVert = md.Vertices[0];
|
||||
for (int i = 1; i < _mdVertexCount; i++)
|
||||
{
|
||||
_vert = md.Vertices[i];
|
||||
_vertexRelativePos = _vert - _firstVert;
|
||||
_vertexRelativePos = _textureDirection * _vertexRelativePos;
|
||||
_textureUvCoordinates[i] = new Vector2(_vertexRelativePos.x, _vertexRelativePos.z);
|
||||
if (_vertexRelativePos.x < minx)
|
||||
minx = _vertexRelativePos.x;
|
||||
if (_vertexRelativePos.x > maxx)
|
||||
maxx = _vertexRelativePos.x;
|
||||
if (_vertexRelativePos.z < miny)
|
||||
miny = _vertexRelativePos.z;
|
||||
if (_vertexRelativePos.z > maxy)
|
||||
maxy = _vertexRelativePos.z;
|
||||
}
|
||||
|
||||
var width = maxx - minx;
|
||||
var height = maxy - miny;
|
||||
|
||||
for (int i = 0; i < _mdVertexCount; i++)
|
||||
{
|
||||
_uv.Add(new Vector2(
|
||||
(((_textureUvCoordinates[i].x - minx) / width) * _currentFacade.TextureRect.width) + _currentFacade.TextureRect.x,
|
||||
(((_textureUvCoordinates[i].y - miny) / height) * _currentFacade.TextureRect.height) + _currentFacade.TextureRect.y));
|
||||
}
|
||||
}
|
||||
|
||||
md.UV[0].AddRange(_uv);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cf1a49b7a5d29964093712def3d56475
|
||||
timeCreated: 1478553093
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,70 @@
|
||||
namespace Mapbox.Unity.MeshGeneration.Modifiers
|
||||
{
|
||||
using UnityEngine;
|
||||
using System;
|
||||
using Mapbox.Unity.MeshGeneration.Data;
|
||||
using Mapbox.Unity.Map;
|
||||
using Mapbox.Unity.MeshGeneration.Interfaces;
|
||||
|
||||
[Serializable]
|
||||
public abstract class ModifierProperties : MapboxDataProperty
|
||||
{
|
||||
public abstract Type ModifierType
|
||||
{
|
||||
get;
|
||||
}
|
||||
public virtual void UpdateProperty(LayerVisualizerBase layerVisualizer)
|
||||
{
|
||||
|
||||
}
|
||||
public override bool HasChanged
|
||||
{
|
||||
set
|
||||
{
|
||||
if (value == true)
|
||||
{
|
||||
OnPropertyHasChanged(new VectorLayerUpdateArgs { property = this });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class ModifierBase : ScriptableObject
|
||||
{
|
||||
[SerializeField]
|
||||
public bool Active = true;
|
||||
|
||||
public virtual void SetProperties(ModifierProperties properties)
|
||||
{
|
||||
|
||||
}
|
||||
public virtual void Initialize()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public virtual void FeaturePreProcess(VectorFeatureUnity feature)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public virtual void UnbindProperties()
|
||||
{
|
||||
|
||||
}
|
||||
public virtual void UpdateModifier(object sender, System.EventArgs layerArgs)
|
||||
{
|
||||
NotifyUpdateModifier(new VectorLayerUpdateArgs { property = sender as MapboxDataProperty, modifier = this });
|
||||
}
|
||||
|
||||
public event System.EventHandler ModifierHasChanged;
|
||||
protected virtual void NotifyUpdateModifier(VectorLayerUpdateArgs layerUpdateArgs)
|
||||
{
|
||||
System.EventHandler handler = ModifierHasChanged;
|
||||
if (handler != null)
|
||||
{
|
||||
handler(this, layerUpdateArgs);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b4cd699dce20d234abbc3b161587b6dc
|
||||
timeCreated: 1483668585
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,257 @@
|
||||
namespace Mapbox.Unity.MeshGeneration.Modifiers
|
||||
{
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
using Mapbox.Unity.MeshGeneration.Data;
|
||||
using Mapbox.Unity.MeshGeneration.Components;
|
||||
using System;
|
||||
|
||||
public enum PositionTargetType
|
||||
{
|
||||
TileCenter,
|
||||
FirstVertex,
|
||||
CenterOfVertices
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Modifier Stacks
|
||||
/// Modifier Stack can be thought as styles as as they contain all the data/settings for how the feature will be visualized.
|
||||
/// They also create the game objects in default implementations in the sdk.
|
||||
/// Currently there's two implementations of this; Modifier Stack and Merged Modifier Stack.They work almost exactly same
|
||||
/// (logically) with one difference; modifier stacks creates a game object for each feature while merged modifier stack,
|
||||
/// merges them up as the name suggest and create one game object for multiple(as many as possible) features.Both have
|
||||
/// their advantages but the main factor here is the performance.Regular modifier stack creates individual game object so
|
||||
/// it's easier to interact, move, animate etc features.But if you want to visualize whole San Francisco, that would mean
|
||||
/// just 200k-300k buildings which would hit performance really hard. In such a case, especially if you don't need
|
||||
/// individual interaction or something, you can use merged modifier stack, which will probably be able to create whole
|
||||
/// SF around a few hundred game objects.
|
||||
/// They contain two lists; mesh modifier list and game object modifier list.These modifiers are used to create and
|
||||
/// decorate game objects.
|
||||
/// Mesh modifiers generate data required for the game objects mesh. I.e.polygon mesh modifier triangulates the polygn,
|
||||
/// height modifier extrudes the polygon and adds volume etc, uv modifier changes UV mapping etc.
|
||||
/// Game object modifiers decorate created game objects, like settings material, interaction scripts, animations etc.
|
||||
/// i.e.Material modifier sets materials to mesh and submeshes, highlight modifier adds mouse highlight to features,
|
||||
/// feature behaviour adds a script to keep feature data on game objects etc.
|
||||
/// So the idea here is; run all mesh modifiers first, generate all the data required for mesh.Create game object
|
||||
/// using that mesh data.Run all game object modifiers to decorate that game object.
|
||||
/// </summary>
|
||||
[CreateAssetMenu(menuName = "Mapbox/Modifiers/Modifier Stack")]
|
||||
public class ModifierStack : ModifierStackBase
|
||||
{
|
||||
[SerializeField] public PositionTargetType moveFeaturePositionTo;
|
||||
|
||||
|
||||
[NonSerialized] private int vertexIndex = 1;
|
||||
[NonSerialized] private Dictionary<UnityTile, List<VectorEntity>> _activeObjects;
|
||||
[NonSerialized] private ObjectPool<VectorEntity> _pool;
|
||||
|
||||
[NonSerialized] private Vector3 _tempPoint;
|
||||
[NonSerialized] private VectorEntity _tempVectorEntity;
|
||||
[NonSerialized] private ObjectPool<List<VectorEntity>> _listPool;
|
||||
|
||||
[NonSerialized] private int _counter;
|
||||
[NonSerialized] private int _secondCounter;
|
||||
protected virtual void OnEnable()
|
||||
{
|
||||
_pool = new ObjectPool<VectorEntity>(() =>
|
||||
{
|
||||
var go = new GameObject();
|
||||
var mf = go.AddComponent<MeshFilter>();
|
||||
var mr = go.AddComponent<MeshRenderer>();
|
||||
_tempVectorEntity = new VectorEntity()
|
||||
{
|
||||
GameObject = go,
|
||||
Transform = go.transform,
|
||||
MeshFilter = mf,
|
||||
MeshRenderer = mr,
|
||||
Mesh = mf.mesh
|
||||
};
|
||||
return _tempVectorEntity;
|
||||
});
|
||||
_listPool = new ObjectPool<List<VectorEntity>>(() => { return new List<VectorEntity>(); });
|
||||
_activeObjects = new Dictionary<UnityTile, List<VectorEntity>>();
|
||||
}
|
||||
|
||||
public override void OnUnregisterTile(UnityTile tile)
|
||||
{
|
||||
if (_activeObjects.ContainsKey(tile))
|
||||
{
|
||||
_counter = _activeObjects[tile].Count;
|
||||
for (int i = 0; i < _counter; i++)
|
||||
{
|
||||
foreach (var item in GoModifiers)
|
||||
{
|
||||
item.OnPoolItem(_activeObjects[tile][i]);
|
||||
}
|
||||
if (null != _activeObjects[tile][i].GameObject)
|
||||
{
|
||||
_activeObjects[tile][i].GameObject.SetActive(false);
|
||||
}
|
||||
_pool.Put(_activeObjects[tile][i]);
|
||||
}
|
||||
_activeObjects[tile].Clear();
|
||||
|
||||
//pooling these lists as they'll reused anyway, saving hundreds of list instantiations
|
||||
_listPool.Put(_activeObjects[tile]);
|
||||
_activeObjects.Remove(tile);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
_counter = MeshModifiers.Count;
|
||||
for (int i = 0; i < _counter; i++)
|
||||
{
|
||||
MeshModifiers[i].Initialize();
|
||||
}
|
||||
|
||||
_counter = GoModifiers.Count;
|
||||
for (int i = 0; i < _counter; i++)
|
||||
{
|
||||
GoModifiers[i].Initialize();
|
||||
}
|
||||
}
|
||||
|
||||
public override GameObject Execute(UnityTile tile, VectorFeatureUnity feature, MeshData meshData, GameObject parent = null, string type = "")
|
||||
{
|
||||
_counter = feature.Points.Count;
|
||||
_secondCounter = 0;
|
||||
|
||||
if (moveFeaturePositionTo != PositionTargetType.TileCenter)
|
||||
{
|
||||
_tempPoint = Constants.Math.Vector3Zero;
|
||||
if (moveFeaturePositionTo == PositionTargetType.FirstVertex)
|
||||
{
|
||||
_tempPoint = feature.Points[0][0];
|
||||
}
|
||||
else if (moveFeaturePositionTo == PositionTargetType.CenterOfVertices)
|
||||
{
|
||||
//this is not precisely the center because of the duplicates (first/last vertex) but close to center
|
||||
_tempPoint = feature.Points[0][0];
|
||||
vertexIndex = 1;
|
||||
|
||||
for (int i = 0; i < _counter; i++)
|
||||
{
|
||||
_secondCounter = feature.Points[i].Count;
|
||||
for (int j = 0; j < _secondCounter; j++)
|
||||
{
|
||||
_tempPoint += feature.Points[i][j];
|
||||
vertexIndex++;
|
||||
}
|
||||
}
|
||||
_tempPoint /= vertexIndex;
|
||||
}
|
||||
|
||||
for (int i = 0; i < _counter; i++)
|
||||
{
|
||||
_secondCounter = feature.Points[i].Count;
|
||||
for (int j = 0; j < _secondCounter; j++)
|
||||
{
|
||||
feature.Points[i][j] = new Vector3(feature.Points[i][j].x - _tempPoint.x, 0, feature.Points[i][j].z - _tempPoint.z);
|
||||
}
|
||||
}
|
||||
meshData.PositionInTile = _tempPoint;
|
||||
}
|
||||
|
||||
meshData.PositionInTile = _tempPoint;
|
||||
_counter = MeshModifiers.Count;
|
||||
for (int i = 0; i < _counter; i++)
|
||||
{
|
||||
if (MeshModifiers[i] != null && MeshModifiers[i].Active)
|
||||
{
|
||||
MeshModifiers[i].Run(feature, meshData, tile);
|
||||
}
|
||||
}
|
||||
|
||||
_tempVectorEntity = _pool.GetObject();
|
||||
|
||||
// It is possible that we changed scenes in the middle of map generation.
|
||||
// This object can be null as a result of Unity cleaning up game objects in the scene.
|
||||
// Let's bail if we don't have our object.
|
||||
if (_tempVectorEntity.GameObject == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
_tempVectorEntity.GameObject.SetActive(true);
|
||||
_tempVectorEntity.Mesh.Clear();
|
||||
_tempVectorEntity.Feature = feature;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
if (feature.Data != null)
|
||||
{
|
||||
_tempVectorEntity.GameObject.name = type + " - " + feature.Data.Id;
|
||||
}
|
||||
else
|
||||
{
|
||||
_tempVectorEntity.GameObject.name = type;
|
||||
}
|
||||
#endif
|
||||
_tempVectorEntity.Mesh.subMeshCount = meshData.Triangles.Count;
|
||||
_tempVectorEntity.Mesh.SetVertices(meshData.Vertices);
|
||||
_tempVectorEntity.Mesh.SetNormals(meshData.Normals);
|
||||
if (meshData.Tangents.Count > 0)
|
||||
{
|
||||
_tempVectorEntity.Mesh.SetTangents(meshData.Tangents);
|
||||
}
|
||||
|
||||
_counter = meshData.Triangles.Count;
|
||||
for (int i = 0; i < _counter; i++)
|
||||
{
|
||||
_tempVectorEntity.Mesh.SetTriangles(meshData.Triangles[i], i);
|
||||
}
|
||||
_counter = meshData.UV.Count;
|
||||
for (int i = 0; i < _counter; i++)
|
||||
{
|
||||
_tempVectorEntity.Mesh.SetUVs(i, meshData.UV[i]);
|
||||
}
|
||||
|
||||
_tempVectorEntity.Transform.SetParent(parent.transform, false);
|
||||
|
||||
if (!_activeObjects.ContainsKey(tile))
|
||||
{
|
||||
_activeObjects.Add(tile, _listPool.GetObject());
|
||||
}
|
||||
_activeObjects[tile].Add(_tempVectorEntity);
|
||||
|
||||
|
||||
_tempVectorEntity.Transform.localPosition = meshData.PositionInTile;
|
||||
|
||||
_counter = GoModifiers.Count;
|
||||
for (int i = 0; i < _counter; i++)
|
||||
{
|
||||
if (GoModifiers[i].Active)
|
||||
{
|
||||
GoModifiers[i].Run(_tempVectorEntity, tile);
|
||||
}
|
||||
}
|
||||
|
||||
return _tempVectorEntity.GameObject;
|
||||
}
|
||||
|
||||
public override void ClearCaches()
|
||||
{
|
||||
foreach (var modifier in GoModifiers)
|
||||
{
|
||||
modifier.ClearCaches();
|
||||
}
|
||||
foreach (var vectorEntity in _pool.GetQueue())
|
||||
{
|
||||
Destroy(vectorEntity.GameObject);
|
||||
}
|
||||
|
||||
foreach (var tileTuple in _activeObjects)
|
||||
{
|
||||
foreach (var vectorEntity in tileTuple.Value)
|
||||
{
|
||||
Destroy(vectorEntity.GameObject);
|
||||
}
|
||||
}
|
||||
_pool.Clear();
|
||||
_activeObjects.Clear();
|
||||
_pool.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 25f09effe9db3dd4e9e825475d698ca3
|
||||
timeCreated: 1485209828
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,41 @@
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using Mapbox.Map;
|
||||
using Mapbox.Unity.MeshGeneration.Data;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using Mapbox.Unity.Utilities;
|
||||
|
||||
namespace Mapbox.Unity.MeshGeneration.Modifiers
|
||||
{
|
||||
public class ModifierStackBase : ScriptableObject
|
||||
{
|
||||
[NodeEditorElement("Mesh Modifiers")] public List<MeshModifier> MeshModifiers = new List<MeshModifier>();
|
||||
[NodeEditorElement("Game Object Modifiers")] public List<GameObjectModifier> GoModifiers = new List<GameObjectModifier>();
|
||||
|
||||
public virtual GameObject Execute(UnityTile tile, VectorFeatureUnity feature, MeshData meshData, GameObject parent = null, string type = "")
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public virtual void Initialize()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void UnregisterTile(UnityTile tile)
|
||||
{
|
||||
OnUnregisterTile(tile);
|
||||
}
|
||||
|
||||
public virtual void OnUnregisterTile(UnityTile tile)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public virtual void ClearCaches()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1d1f69f648851b7449e643a82076ce8c
|
||||
timeCreated: 1491774659
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,592 @@
|
||||
namespace Mapbox.Unity.MeshGeneration.Modifiers
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using Mapbox.Unity.MeshGeneration.Data;
|
||||
using Mapbox.Unity.Map;
|
||||
using System;
|
||||
|
||||
[CreateAssetMenu(menuName = "Mapbox/Modifiers/Textured Side Wall Modifier")]
|
||||
public class TextureSideWallModifier : MeshModifier
|
||||
{
|
||||
#region ModifierOptions
|
||||
|
||||
private float _scaledFirstFloorHeight = 0;
|
||||
|
||||
private float _scaledTopFloorHeight = 0;
|
||||
|
||||
//private int _maxEdgeSectionCount = 40;
|
||||
private float _scaledPreferredWallLength;
|
||||
[SerializeField] private bool _centerSegments = true;
|
||||
[SerializeField] private bool _separateSubmesh = true;
|
||||
|
||||
#endregion
|
||||
|
||||
float currentWallLength = 0;
|
||||
Vector3 start = Constants.Math.Vector3Zero;
|
||||
Vector3 wallDirection = Constants.Math.Vector3Zero;
|
||||
|
||||
Vector3 wallSegmentFirstVertex;
|
||||
Vector3 wallSegmentSecondVertex;
|
||||
Vector3 wallSegmentDirection;
|
||||
float wallSegmentLength;
|
||||
|
||||
//public AtlasInfo AtlasInfo;
|
||||
private AtlasEntity _currentFacade;
|
||||
private Rect _currentTextureRect;
|
||||
|
||||
private float finalFirstHeight;
|
||||
private float finalTopHeight;
|
||||
private float finalMidHeight;
|
||||
private float finalLeftOverRowHeight;
|
||||
private float _scaledFloorHeight;
|
||||
private int triIndex;
|
||||
private Vector3 wallNormal;
|
||||
private List<int> wallTriangles;
|
||||
private float columnScaleRatio;
|
||||
private float rightOfEdgeUv;
|
||||
|
||||
private float currentY1;
|
||||
private float currentY2;
|
||||
private float _wallSizeEpsilon = 0.99f;
|
||||
private float _narrowWallWidthDelta = 0.01f;
|
||||
private float _shortRowHeightDelta = 0.015f;
|
||||
|
||||
GeometryExtrusionWithAtlasOptions _options;
|
||||
private int _counter = 0;
|
||||
private float height = 0.0f;
|
||||
private float _scale = 1f;
|
||||
private float _minWallLength;
|
||||
private float _singleFloorHeight;
|
||||
private float _currentMidHeight;
|
||||
private float _midUvInCurrentStep;
|
||||
private float _singleColumnLength;
|
||||
private float _leftOverColumnLength;
|
||||
|
||||
public override void SetProperties(ModifierProperties properties)
|
||||
{
|
||||
if (properties is GeometryExtrusionWithAtlasOptions)
|
||||
{
|
||||
_options = (GeometryExtrusionWithAtlasOptions)properties;
|
||||
}
|
||||
else if (properties is GeometryExtrusionOptions)
|
||||
{
|
||||
_options = ((GeometryExtrusionOptions)properties).ToGeometryExtrusionWithAtlasOptions();
|
||||
}
|
||||
else if (properties is UVModifierOptions)
|
||||
{
|
||||
_options = ((UVModifierOptions)properties).ToGeometryExtrusionWithAtlasOptions();
|
||||
}
|
||||
}
|
||||
|
||||
public override void UnbindProperties()
|
||||
{
|
||||
_options.PropertyHasChanged -= UpdateModifier;
|
||||
}
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
foreach (var atlasEntity in _options.atlasInfo.Textures)
|
||||
{
|
||||
atlasEntity.CalculateParameters();
|
||||
}
|
||||
}
|
||||
|
||||
public override void UpdateModifier(object sender, System.EventArgs layerArgs)
|
||||
{
|
||||
SetProperties((ModifierProperties)sender);
|
||||
NotifyUpdateModifier(new VectorLayerUpdateArgs { property = sender as MapboxDataProperty, modifier = this });
|
||||
}
|
||||
|
||||
public override void Run(VectorFeatureUnity feature, MeshData md, UnityTile tile = null)
|
||||
{
|
||||
if (md.Vertices.Count == 0 || feature == null || feature.Points.Count < 1)
|
||||
return;
|
||||
|
||||
if (tile != null)
|
||||
_scale = tile.TileScale;
|
||||
|
||||
//facade texture to decorate this building
|
||||
_currentFacade =
|
||||
_options.atlasInfo.Textures[UnityEngine.Random.Range(0, _options.atlasInfo.Textures.Count)];
|
||||
//rect is a struct so we're caching this
|
||||
_currentTextureRect = _currentFacade.TextureRect;
|
||||
|
||||
//this can be moved to initialize or in an if clause if you're sure all your tiles will be same level/scale
|
||||
_singleFloorHeight = (tile.TileScale * _currentFacade.FloorHeight) / _currentFacade.MidFloorCount;
|
||||
_scaledFirstFloorHeight = tile.TileScale * _currentFacade.FirstFloorHeight;
|
||||
_scaledTopFloorHeight = tile.TileScale * _currentFacade.TopFloorHeight;
|
||||
_scaledPreferredWallLength = tile.TileScale * _currentFacade.PreferredEdgeSectionLength;
|
||||
_scaledFloorHeight = _scaledPreferredWallLength * _currentFacade.WallToFloorRatio;
|
||||
_singleColumnLength = _scaledPreferredWallLength / _currentFacade.ColumnCount;
|
||||
|
||||
//read or force height
|
||||
float maxHeight = 1, minHeight = 0;
|
||||
|
||||
//query height and push polygon up to create roof
|
||||
//can we do this vice versa and create roof at last?
|
||||
QueryHeight(feature, md, tile, out maxHeight, out minHeight);
|
||||
maxHeight = maxHeight * _options.extrusionScaleFactor * _scale;
|
||||
minHeight = minHeight * _options.extrusionScaleFactor * _scale;
|
||||
height = (maxHeight - minHeight);
|
||||
GenerateRoofMesh(md, minHeight, maxHeight);
|
||||
|
||||
if (_options.extrusionGeometryType != ExtrusionGeometryType.RoofOnly)
|
||||
{
|
||||
//limiting section heights, first floor gets priority, then we draw top floor, then mid if we still have space
|
||||
finalFirstHeight = Mathf.Min(height, _scaledFirstFloorHeight);
|
||||
finalTopHeight = (height - finalFirstHeight) < _scaledTopFloorHeight ? 0 : _scaledTopFloorHeight;
|
||||
finalMidHeight = Mathf.Max(0, height - (finalFirstHeight + finalTopHeight));
|
||||
//scaledFloorHeight = midHeight / floorCount;
|
||||
wallTriangles = new List<int>();
|
||||
|
||||
//cuts long edges into smaller ones using PreferredEdgeSectionLength
|
||||
currentWallLength = 0;
|
||||
start = Constants.Math.Vector3Zero;
|
||||
wallSegmentDirection = Constants.Math.Vector3Zero;
|
||||
|
||||
finalLeftOverRowHeight = 0f;
|
||||
if (finalMidHeight > 0)
|
||||
{
|
||||
finalLeftOverRowHeight = finalMidHeight;
|
||||
finalLeftOverRowHeight = finalLeftOverRowHeight % _singleFloorHeight;
|
||||
finalMidHeight -= finalLeftOverRowHeight;
|
||||
}
|
||||
else
|
||||
{
|
||||
finalLeftOverRowHeight = finalTopHeight;
|
||||
}
|
||||
|
||||
for (int i = 0; i < md.Edges.Count; i += 2)
|
||||
{
|
||||
var v1 = md.Vertices[md.Edges[i]];
|
||||
var v2 = md.Vertices[md.Edges[i + 1]];
|
||||
|
||||
wallDirection = v2 - v1;
|
||||
|
||||
currentWallLength = Vector3.Distance(v1, v2);
|
||||
_leftOverColumnLength = currentWallLength % _singleColumnLength;
|
||||
start = v1;
|
||||
wallSegmentDirection = (v2 - v1).normalized;
|
||||
|
||||
//half of leftover column (if _centerSegments ofc) at the begining
|
||||
if (_centerSegments && currentWallLength > _singleColumnLength)
|
||||
{
|
||||
//save left,right vertices and wall length
|
||||
wallSegmentFirstVertex = start;
|
||||
wallSegmentLength = (_leftOverColumnLength / 2);
|
||||
start += wallSegmentDirection * wallSegmentLength;
|
||||
wallSegmentSecondVertex = start;
|
||||
|
||||
_leftOverColumnLength = _leftOverColumnLength / 2;
|
||||
CreateWall(md);
|
||||
}
|
||||
|
||||
while (currentWallLength > _singleColumnLength)
|
||||
{
|
||||
wallSegmentFirstVertex = start;
|
||||
//columns fitting wall / max column we have in texture
|
||||
var stepRatio =
|
||||
(float)Math.Min(_currentFacade.ColumnCount,
|
||||
Math.Floor(currentWallLength / _singleColumnLength)) / _currentFacade.ColumnCount;
|
||||
wallSegmentLength = stepRatio * _scaledPreferredWallLength;
|
||||
start += wallSegmentDirection * wallSegmentLength;
|
||||
wallSegmentSecondVertex = start;
|
||||
|
||||
currentWallLength -= (stepRatio * _scaledPreferredWallLength);
|
||||
CreateWall(md);
|
||||
}
|
||||
|
||||
//left over column at the end
|
||||
if (_leftOverColumnLength > 0)
|
||||
{
|
||||
wallSegmentFirstVertex = start;
|
||||
wallSegmentSecondVertex = v2;
|
||||
wallSegmentLength = _leftOverColumnLength;
|
||||
CreateWall(md);
|
||||
}
|
||||
}
|
||||
|
||||
//this first loop is for columns
|
||||
if (_separateSubmesh)
|
||||
{
|
||||
md.Triangles.Add(wallTriangles);
|
||||
}
|
||||
else
|
||||
{
|
||||
md.Triangles.Capacity = md.Triangles.Count + wallTriangles.Count;
|
||||
md.Triangles[0].AddRange(wallTriangles);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateWall(MeshData md)
|
||||
{
|
||||
//need to keep track of this for triangulation indices
|
||||
triIndex = md.Vertices.Count;
|
||||
|
||||
//this part minimizes stretching for narrow columns
|
||||
//if texture has 3 columns, 33% (of preferred edge length) wide walls will get 1 window.
|
||||
//0-33% gets 1 window, 33-66 gets 2, 66-100 gets all three
|
||||
//we're not wrapping/repeating texture as it won't work with atlases
|
||||
columnScaleRatio = Math.Min(1, wallSegmentLength / _scaledPreferredWallLength);
|
||||
rightOfEdgeUv =
|
||||
_currentTextureRect.xMin +
|
||||
_currentTextureRect.size.x *
|
||||
columnScaleRatio; // Math.Min(1, ((float)(Math.Floor(columnScaleRatio * _currentFacade.ColumnCount) + 1) / _currentFacade.ColumnCount));
|
||||
|
||||
_minWallLength = (_scaledPreferredWallLength / _currentFacade.ColumnCount) * _wallSizeEpsilon;
|
||||
//common for all top/mid/bottom segments
|
||||
wallNormal = new Vector3(-(wallSegmentFirstVertex.z - wallSegmentSecondVertex.z), 0,
|
||||
(wallSegmentFirstVertex.x - wallSegmentSecondVertex.x)).normalized;
|
||||
//height of the left/right edges
|
||||
currentY1 = wallSegmentFirstVertex.y;
|
||||
currentY2 = wallSegmentSecondVertex.y;
|
||||
|
||||
//moving leftover row to top
|
||||
LeftOverRow(md, finalLeftOverRowHeight);
|
||||
|
||||
FirstFloor(md, height);
|
||||
TopFloor(md, finalLeftOverRowHeight);
|
||||
MidFloors(md);
|
||||
}
|
||||
|
||||
private void LeftOverRow(MeshData md, float leftOver)
|
||||
{
|
||||
//leftover. we're moving small leftover row to top of the building
|
||||
if (leftOver > 0)
|
||||
{
|
||||
md.Vertices.Add(new Vector3(wallSegmentFirstVertex.x, currentY1, wallSegmentFirstVertex.z));
|
||||
md.Vertices.Add(new Vector3(wallSegmentSecondVertex.x, currentY2, wallSegmentSecondVertex.z));
|
||||
//move offsets bottom
|
||||
currentY1 -= leftOver;
|
||||
currentY2 -= leftOver;
|
||||
//bottom two vertices
|
||||
md.Vertices.Add(new Vector3(wallSegmentFirstVertex.x, currentY1, wallSegmentFirstVertex.z));
|
||||
md.Vertices.Add(new Vector3(wallSegmentSecondVertex.x, currentY2, wallSegmentSecondVertex.z));
|
||||
|
||||
if (wallSegmentLength >= _minWallLength)
|
||||
{
|
||||
md.UV[0].Add(new Vector2(_currentTextureRect.xMin, _currentTextureRect.yMax));
|
||||
md.UV[0].Add(new Vector2(rightOfEdgeUv, _currentTextureRect.yMax));
|
||||
md.UV[0].Add(new Vector2(_currentTextureRect.xMin,
|
||||
_currentTextureRect.yMax - _shortRowHeightDelta));
|
||||
md.UV[0].Add(new Vector2(rightOfEdgeUv, _currentTextureRect.yMax - _shortRowHeightDelta));
|
||||
}
|
||||
else
|
||||
{
|
||||
md.UV[0].Add(new Vector2(_currentTextureRect.xMin, _currentTextureRect.yMax));
|
||||
md.UV[0].Add(
|
||||
new Vector2(_currentTextureRect.xMin + _narrowWallWidthDelta, _currentTextureRect.yMax));
|
||||
md.UV[0].Add(new Vector2(_currentTextureRect.xMin,
|
||||
_currentTextureRect.yMax - _shortRowHeightDelta));
|
||||
md.UV[0].Add(new Vector2(_currentTextureRect.xMin + _narrowWallWidthDelta,
|
||||
_currentTextureRect.yMax - _shortRowHeightDelta));
|
||||
}
|
||||
|
||||
md.Normals.Add(wallNormal);
|
||||
md.Normals.Add(wallNormal);
|
||||
md.Normals.Add(wallNormal);
|
||||
md.Normals.Add(wallNormal);
|
||||
|
||||
md.Tangents.Add(wallDirection);
|
||||
md.Tangents.Add(wallDirection);
|
||||
md.Tangents.Add(wallDirection);
|
||||
md.Tangents.Add(wallDirection);
|
||||
|
||||
wallTriangles.Add(triIndex);
|
||||
wallTriangles.Add(triIndex + 1);
|
||||
wallTriangles.Add(triIndex + 2);
|
||||
|
||||
wallTriangles.Add(triIndex + 1);
|
||||
wallTriangles.Add(triIndex + 3);
|
||||
wallTriangles.Add(triIndex + 2);
|
||||
|
||||
triIndex += 4;
|
||||
}
|
||||
}
|
||||
|
||||
private void MidFloors(MeshData md)
|
||||
{
|
||||
_currentMidHeight = finalMidHeight;
|
||||
while (_currentMidHeight >= _singleFloorHeight - 0.01f)
|
||||
{
|
||||
//first part is the number of floors fitting current wall segment. You can fit max of "row count in mid". Or if wall
|
||||
//is smaller and it can only fit i.e. 3 floors instead of 5; we use 3/5 of the mid section texture as well.
|
||||
_midUvInCurrentStep =
|
||||
((float)Math.Min(_currentFacade.MidFloorCount,
|
||||
Math.Round(_currentMidHeight / _singleFloorHeight))) / _currentFacade.MidFloorCount;
|
||||
|
||||
//top two vertices
|
||||
md.Vertices.Add(new Vector3(wallSegmentFirstVertex.x, currentY1, wallSegmentFirstVertex.z));
|
||||
md.Vertices.Add(new Vector3(wallSegmentSecondVertex.x, currentY2, wallSegmentSecondVertex.z));
|
||||
//move offsets bottom
|
||||
currentY1 -= (_scaledFloorHeight * _midUvInCurrentStep);
|
||||
currentY2 -= (_scaledFloorHeight * _midUvInCurrentStep);
|
||||
//bottom two vertices
|
||||
md.Vertices.Add(new Vector3(wallSegmentFirstVertex.x, currentY1, wallSegmentFirstVertex.z));
|
||||
md.Vertices.Add(new Vector3(wallSegmentSecondVertex.x, currentY2, wallSegmentSecondVertex.z));
|
||||
|
||||
//we uv narrow walls different so they won't have condensed windows
|
||||
if (wallSegmentLength >= _minWallLength)
|
||||
{
|
||||
md.UV[0].Add(new Vector2(_currentTextureRect.xMin, _currentFacade.topOfMidUv));
|
||||
md.UV[0].Add(new Vector2(rightOfEdgeUv, _currentFacade.topOfMidUv));
|
||||
md.UV[0].Add(new Vector2(_currentTextureRect.xMin,
|
||||
_currentFacade.topOfMidUv - _currentFacade.midUvHeight * _midUvInCurrentStep));
|
||||
md.UV[0].Add(new Vector2(rightOfEdgeUv,
|
||||
_currentFacade.topOfMidUv - _currentFacade.midUvHeight * _midUvInCurrentStep));
|
||||
}
|
||||
else
|
||||
{
|
||||
md.UV[0].Add(new Vector2(_currentTextureRect.xMin, _currentFacade.topOfMidUv));
|
||||
md.UV[0].Add(new Vector2(_currentTextureRect.xMin + _narrowWallWidthDelta,
|
||||
_currentFacade.topOfMidUv));
|
||||
md.UV[0].Add(new Vector2(_currentTextureRect.xMin,
|
||||
_currentFacade.topOfMidUv - _currentFacade.midUvHeight * _midUvInCurrentStep));
|
||||
md.UV[0].Add(new Vector2(_currentTextureRect.xMin + _narrowWallWidthDelta,
|
||||
_currentFacade.topOfMidUv - _currentFacade.midUvHeight * _midUvInCurrentStep));
|
||||
}
|
||||
|
||||
md.Normals.Add(wallNormal);
|
||||
md.Normals.Add(wallNormal);
|
||||
md.Normals.Add(wallNormal);
|
||||
md.Normals.Add(wallNormal);
|
||||
|
||||
md.Tangents.Add(wallDirection);
|
||||
md.Tangents.Add(wallDirection);
|
||||
md.Tangents.Add(wallDirection);
|
||||
md.Tangents.Add(wallDirection);
|
||||
|
||||
wallTriangles.Add(triIndex);
|
||||
wallTriangles.Add(triIndex + 1);
|
||||
wallTriangles.Add(triIndex + 2);
|
||||
|
||||
wallTriangles.Add(triIndex + 1);
|
||||
wallTriangles.Add(triIndex + 3);
|
||||
wallTriangles.Add(triIndex + 2);
|
||||
|
||||
triIndex += 4;
|
||||
_currentMidHeight -= Math.Max(0.1f, (_scaledFloorHeight * _midUvInCurrentStep));
|
||||
}
|
||||
}
|
||||
|
||||
private void TopFloor(MeshData md, float leftOver)
|
||||
{
|
||||
//top floor start
|
||||
currentY1 -= finalTopHeight;
|
||||
currentY2 -= finalTopHeight;
|
||||
md.Vertices.Add(new Vector3(wallSegmentFirstVertex.x, wallSegmentFirstVertex.y - leftOver,
|
||||
wallSegmentFirstVertex.z));
|
||||
md.Vertices.Add(new Vector3(wallSegmentSecondVertex.x, wallSegmentSecondVertex.y - leftOver,
|
||||
wallSegmentSecondVertex.z));
|
||||
md.Vertices.Add(new Vector3(wallSegmentFirstVertex.x, wallSegmentFirstVertex.y - leftOver - finalTopHeight,
|
||||
wallSegmentFirstVertex.z));
|
||||
md.Vertices.Add(new Vector3(wallSegmentSecondVertex.x,
|
||||
wallSegmentSecondVertex.y - leftOver - finalTopHeight, wallSegmentSecondVertex.z));
|
||||
|
||||
if (wallSegmentLength >= _minWallLength)
|
||||
{
|
||||
md.UV[0].Add(new Vector2(_currentTextureRect.xMin, _currentTextureRect.yMax));
|
||||
md.UV[0].Add(new Vector2(rightOfEdgeUv, _currentTextureRect.yMax));
|
||||
md.UV[0].Add(new Vector2(_currentTextureRect.xMin, _currentFacade.bottomOfTopUv));
|
||||
md.UV[0].Add(new Vector2(rightOfEdgeUv, _currentFacade.bottomOfTopUv));
|
||||
}
|
||||
else
|
||||
{
|
||||
md.UV[0].Add(new Vector2(_currentTextureRect.xMin, _currentTextureRect.yMax));
|
||||
md.UV[0].Add(new Vector2(_currentTextureRect.xMin + _narrowWallWidthDelta, _currentTextureRect.yMax));
|
||||
md.UV[0].Add(new Vector2(_currentTextureRect.xMin, _currentFacade.bottomOfTopUv));
|
||||
md.UV[0].Add(
|
||||
new Vector2(_currentTextureRect.xMin + _narrowWallWidthDelta, _currentFacade.bottomOfTopUv));
|
||||
}
|
||||
|
||||
md.Normals.Add(wallNormal);
|
||||
md.Normals.Add(wallNormal);
|
||||
md.Normals.Add(wallNormal);
|
||||
md.Normals.Add(wallNormal);
|
||||
|
||||
|
||||
md.Tangents.Add(wallDirection);
|
||||
md.Tangents.Add(wallDirection);
|
||||
md.Tangents.Add(wallDirection);
|
||||
md.Tangents.Add(wallDirection);
|
||||
|
||||
wallTriangles.Add(triIndex);
|
||||
wallTriangles.Add(triIndex + 1);
|
||||
wallTriangles.Add(triIndex + 2);
|
||||
|
||||
wallTriangles.Add(triIndex + 1);
|
||||
wallTriangles.Add(triIndex + 3);
|
||||
wallTriangles.Add(triIndex + 2);
|
||||
|
||||
triIndex += 4;
|
||||
}
|
||||
|
||||
private void FirstFloor(MeshData md, float hf)
|
||||
{
|
||||
md.Vertices.Add(new Vector3(wallSegmentFirstVertex.x, wallSegmentFirstVertex.y - hf + finalFirstHeight,
|
||||
wallSegmentFirstVertex.z));
|
||||
md.Vertices.Add(new Vector3(wallSegmentSecondVertex.x, wallSegmentSecondVertex.y - hf + finalFirstHeight,
|
||||
wallSegmentSecondVertex.z));
|
||||
md.Vertices.Add(new Vector3(wallSegmentFirstVertex.x, wallSegmentFirstVertex.y - hf,
|
||||
wallSegmentFirstVertex.z));
|
||||
md.Vertices.Add(new Vector3(wallSegmentSecondVertex.x, wallSegmentSecondVertex.y - hf,
|
||||
wallSegmentSecondVertex.z));
|
||||
|
||||
md.Normals.Add(wallNormal);
|
||||
md.Normals.Add(wallNormal);
|
||||
md.Normals.Add(wallNormal);
|
||||
md.Normals.Add(wallNormal);
|
||||
md.Tangents.Add(wallDirection);
|
||||
md.Tangents.Add(wallDirection);
|
||||
md.Tangents.Add(wallDirection);
|
||||
md.Tangents.Add(wallDirection);
|
||||
|
||||
if (wallSegmentLength >= _minWallLength)
|
||||
{
|
||||
md.UV[0].Add(new Vector2(_currentTextureRect.xMin, _currentFacade.topOfBottomUv));
|
||||
md.UV[0].Add(new Vector2(rightOfEdgeUv, _currentFacade.topOfBottomUv));
|
||||
md.UV[0].Add(new Vector2(_currentTextureRect.xMin, _currentTextureRect.yMin));
|
||||
md.UV[0].Add(new Vector2(rightOfEdgeUv, _currentTextureRect.yMin));
|
||||
}
|
||||
else
|
||||
{
|
||||
md.UV[0].Add(new Vector2(_currentTextureRect.xMin, _currentFacade.topOfBottomUv));
|
||||
md.UV[0].Add(
|
||||
new Vector2(_currentTextureRect.xMin + _narrowWallWidthDelta, _currentFacade.topOfBottomUv));
|
||||
md.UV[0].Add(new Vector2(_currentTextureRect.xMin, _currentTextureRect.yMin));
|
||||
md.UV[0].Add(new Vector2(_currentTextureRect.xMin + _narrowWallWidthDelta, _currentTextureRect.yMin));
|
||||
}
|
||||
|
||||
wallTriangles.Add(triIndex);
|
||||
wallTriangles.Add(triIndex + 1);
|
||||
wallTriangles.Add(triIndex + 2);
|
||||
|
||||
wallTriangles.Add(triIndex + 1);
|
||||
wallTriangles.Add(triIndex + 3);
|
||||
wallTriangles.Add(triIndex + 2);
|
||||
|
||||
triIndex += 4;
|
||||
}
|
||||
|
||||
private void CalculateEdgeList(MeshData md, UnityTile tile, float preferredEdgeSectionLength)
|
||||
{
|
||||
}
|
||||
|
||||
private void GenerateRoofMesh(MeshData md, float minHeight, float maxHeight)
|
||||
{
|
||||
if (_options.extrusionGeometryType != ExtrusionGeometryType.SideOnly)
|
||||
{
|
||||
_counter = md.Vertices.Count;
|
||||
switch (_options.extrusionType)
|
||||
{
|
||||
case ExtrusionType.None:
|
||||
break;
|
||||
case ExtrusionType.PropertyHeight:
|
||||
for (int i = 0; i < _counter; i++)
|
||||
{
|
||||
md.Vertices[i] = new Vector3(md.Vertices[i].x, md.Vertices[i].y + maxHeight,
|
||||
md.Vertices[i].z);
|
||||
}
|
||||
|
||||
break;
|
||||
case ExtrusionType.MinHeight:
|
||||
{
|
||||
var minmax = MinMaxPair.GetMinMaxHeight(md.Vertices);
|
||||
for (int i = 0; i < _counter; i++)
|
||||
{
|
||||
md.Vertices[i] = new Vector3(md.Vertices[i].x, minmax.min + maxHeight, md.Vertices[i].z);
|
||||
}
|
||||
}
|
||||
//hf += max - min;
|
||||
break;
|
||||
case ExtrusionType.MaxHeight:
|
||||
{
|
||||
var minmax = MinMaxPair.GetMinMaxHeight(md.Vertices);
|
||||
for (int i = 0; i < _counter; i++)
|
||||
{
|
||||
md.Vertices[i] = new Vector3(md.Vertices[i].x, minmax.max + maxHeight, md.Vertices[i].z);
|
||||
}
|
||||
|
||||
height += minmax.max - minmax.min;
|
||||
}
|
||||
break;
|
||||
case ExtrusionType.RangeHeight:
|
||||
for (int i = 0; i < _counter; i++)
|
||||
{
|
||||
md.Vertices[i] = new Vector3(md.Vertices[i].x, md.Vertices[i].y + maxHeight,
|
||||
md.Vertices[i].z);
|
||||
}
|
||||
|
||||
break;
|
||||
case ExtrusionType.AbsoluteHeight:
|
||||
for (int i = 0; i < _counter; i++)
|
||||
{
|
||||
md.Vertices[i] = new Vector3(md.Vertices[i].x, md.Vertices[i].y + maxHeight,
|
||||
md.Vertices[i].z);
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void QueryHeight(VectorFeatureUnity feature, MeshData md, UnityTile tile, out float maxHeight,
|
||||
out float minHeight)
|
||||
{
|
||||
minHeight = 0.0f;
|
||||
maxHeight = 0.0f;
|
||||
|
||||
switch (_options.extrusionType)
|
||||
{
|
||||
case ExtrusionType.None:
|
||||
break;
|
||||
case ExtrusionType.PropertyHeight:
|
||||
case ExtrusionType.MinHeight:
|
||||
case ExtrusionType.MaxHeight:
|
||||
if (feature.Properties.ContainsKey(_options.propertyName))
|
||||
{
|
||||
maxHeight = Convert.ToSingle(feature.Properties[_options.propertyName]);
|
||||
if (feature.Properties.ContainsKey("min_height"))
|
||||
{
|
||||
minHeight = Convert.ToSingle(feature.Properties["min_height"]);
|
||||
//hf -= minHeight;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
case ExtrusionType.RangeHeight:
|
||||
if (feature.Properties.ContainsKey(_options.propertyName))
|
||||
{
|
||||
if (_options.minimumHeight > _options.maximumHeight)
|
||||
{
|
||||
Debug.LogError("Maximum Height less than Minimum Height.Swapping values for extrusion.");
|
||||
var temp = _options.minimumHeight;
|
||||
_options.minimumHeight = _options.maximumHeight;
|
||||
_options.maximumHeight = temp;
|
||||
}
|
||||
|
||||
var featureHeight = Convert.ToSingle(feature.Properties[_options.propertyName]);
|
||||
maxHeight = Math.Min(Math.Max(_options.minimumHeight, featureHeight), _options.maximumHeight);
|
||||
if (feature.Properties.ContainsKey("min_height"))
|
||||
{
|
||||
var featureMinHeight = Convert.ToSingle(feature.Properties["min_height"]);
|
||||
minHeight = Math.Min(featureMinHeight, _options.maximumHeight);
|
||||
//maxHeight -= minHeight;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
case ExtrusionType.AbsoluteHeight:
|
||||
maxHeight = _options.maximumHeight;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fa21b5f2e2cb4a347ae152f6b6ec1e68
|
||||
timeCreated: 1515082451
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user