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

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