using Mapbox.Unity.Map;
namespace Mapbox.Unity.MeshGeneration.Modifiers
{
using System.Collections.Generic;
using UnityEngine;
using Mapbox.Unity.MeshGeneration.Data;
///
/// 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.
///
[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();
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());
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);
}
}
}