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

288 lines
7.9 KiB

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;
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);
if (_options.extrusionGeometryType == ExtrusionGeometryType.SideOnly)
{
md.Triangles[0].Clear();
}
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;
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)
{
_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);
}
}
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"]);
}
}
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;
}
}
}
}