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.
413 lines
12 KiB
413 lines
12 KiB
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);
|
|
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|