rabidus-test/Assets/Dreamteck/Splines/Components/SurfaceGenerator.cs

548 lines
20 KiB
C#
Raw Permalink Normal View History

2023-07-24 16:38:13 +03:00
using UnityEngine;
using System.Collections;
namespace Dreamteck.Splines
{
[RequireComponent(typeof(MeshFilter))]
[RequireComponent(typeof(MeshRenderer))]
[AddComponentMenu("Dreamteck/Splines/Users/Surface Generator")]
public class SurfaceGenerator : MeshGenerator
{
public float expand
{
get { return _expand; }
set
{
if (value != _expand)
{
_expand = value;
Rebuild();
}
}
}
public float extrude
{
get { return _extrude; }
set
{
if (value != _extrude)
{
_extrude = value;
Rebuild();
}
}
}
public double extrudeClipFrom
{
get { return _extrudeFrom; }
set
{
if (value != _extrudeFrom)
{
_extrudeFrom = value;
Rebuild();
}
}
}
public double extrudeClipTo
{
get { return _extrudeTo; }
set
{
if (value != _extrudeTo)
{
_extrudeTo = value;
Rebuild();
}
}
}
public Vector2 sideUvScale
{
get { return _sideUvScale; }
set
{
if (value != _sideUvScale)
{
_sideUvScale = value;
Rebuild();
}
else
{
_sideUvScale = value;
}
}
}
public Vector2 sideUvOffset
{
get { return _sideUvOffset; }
set
{
if (value != _sideUvOffset)
{
_sideUvOffset = value;
Rebuild();
}
else
{
_sideUvOffset = value;
}
}
}
public float sideUvRotation
{
get { return _sideUvRotation; }
set
{
if (value != _sideUvRotation)
{
_sideUvRotation = value;
Rebuild();
}
else
{
_sideUvRotation = value;
}
}
}
public SplineComputer extrudeSpline
{
get { return _extrudeSpline; }
set
{
if (value != _extrudeSpline)
{
if (_extrudeSpline != null)
{
_extrudeSpline.Unsubscribe(this);
}
_extrudeSpline = value;
if (value != null)
{
_extrudeSpline.Subscribe(this);
}
Rebuild();
}
}
}
public Vector3 extrudeOffset
{
get { return _extrudeOffset; }
set {
if(value != _extrudeOffset)
{
_extrudeOffset = value;
Rebuild();
}
}
}
public bool uniformUvs
{
get { return _uniformUvs; }
set
{
if (value != _uniformUvs)
{
_uniformUvs = value;
Rebuild();
}
}
}
[SerializeField]
[HideInInspector]
private float _expand = 0f;
[SerializeField]
[HideInInspector]
private float _extrude = 0f;
[SerializeField]
[HideInInspector]
private Vector2 _sideUvScale = Vector2.one;
[SerializeField]
[HideInInspector]
private Vector2 _sideUvOffset = Vector2.zero;
[SerializeField]
[HideInInspector]
private float _sideUvRotation = 0f;
[SerializeField]
[HideInInspector]
private SplineComputer _extrudeSpline;
[SerializeField]
[HideInInspector]
private Vector3 _extrudeOffset = Vector3.zero;
[SerializeField]
[HideInInspector]
private SplineSample[] extrudeResults = new SplineSample[0];
[SerializeField]
[HideInInspector]
private Vector3[] identityVertices = new Vector3[0];
[SerializeField]
[HideInInspector]
private Vector3[] identityNormals = new Vector3[0];
[SerializeField]
[HideInInspector]
private Vector2[] projectedVerts = new Vector2[0];
[SerializeField]
[HideInInspector]
private int[] surfaceTris = new int[0];
[SerializeField]
[HideInInspector]
private int[] wallTris = new int[0];
[SerializeField]
[HideInInspector]
private double _extrudeFrom = 0.0;
[SerializeField]
[HideInInspector]
private double _extrudeTo = 1.0;
[SerializeField]
[HideInInspector]
private bool _uniformUvs = false;
private Vector3 _trsRight = Vector3.right;
private Vector3 _trsUp = Vector3.up;
private Vector3 _trsForward = Vector3.forward;
protected override string meshName => "Surface";
protected override void Awake()
{
base.Awake();
_trsRight = trs.right;
_trsUp = trs.up;
_trsForward = trs.forward;
}
protected override void BuildMesh()
{
if (spline.pointCount == 0) return;
base.BuildMesh();
Generate();
}
private void LateUpdate()
{
if (multithreaded && trs.hasChanged)
{
_trsRight = trs.right;
_trsUp = trs.up;
_trsForward = trs.forward;
}
}
public void Generate()
{
if (!multithreaded)
{
_trsRight = trs.right;
_trsUp = trs.up;
_trsForward = trs.forward;
}
int surfaceVertexCount = sampleCount;
if (spline.isClosed) surfaceVertexCount--;
int vertexCount = surfaceVertexCount;
bool pathExtrude = false;
if (_extrudeSpline != null)
{
_extrudeSpline.Evaluate(ref extrudeResults, _extrudeFrom, _extrudeTo);
pathExtrude = extrudeResults.Length > 0;
} else if(extrudeResults.Length > 0)
{
extrudeResults = new SplineSample[0];
}
bool simpleExtrude = !pathExtrude && _extrude != 0f;
if (pathExtrude)
{
vertexCount *= 2;
vertexCount += sampleCount * extrudeResults.Length;
}
else if (simpleExtrude)
{
vertexCount *= 4;
vertexCount += 2;
}
Vector3 center, normal;
GetProjectedVertices(surfaceVertexCount, out center, out normal);
bool clockwise = IsClockwise(projectedVerts);
bool flipCap = false;
bool flipSide = false;
if (!clockwise) flipSide = !flipSide;
if (simpleExtrude && _extrude < 0f)
{
flipCap = !flipCap;
flipSide = !flipSide;
}
GenerateSurfaceTris(flipCap);
int totalTrisCount = surfaceTris.Length;
if (simpleExtrude)
{
totalTrisCount *= 2;
totalTrisCount += 2 * sampleCount * 2 * 3;
} else
{
totalTrisCount *= 2;
totalTrisCount += extrudeResults .Length * sampleCount * 2 * 3;
}
AllocateMesh(vertexCount, totalTrisCount);
Vector3 off = _trsRight * offset.x + _trsUp * offset.y + _trsForward * offset.z;
for (int i = 0; i < surfaceVertexCount; i++)
{
GetSample(i, ref evalResult);
_tsMesh.vertices[i] = evalResult.position + off;
_tsMesh.normals[i] = evalResult.up;
_tsMesh.colors[i] = evalResult.color * color;
}
#region UVs
Vector2 min = projectedVerts[0];
Vector2 max = projectedVerts[0];
for (int i = 1; i < projectedVerts.Length; i++)
{
if (min.x < projectedVerts[i].x) min.x = projectedVerts[i].x;
if (min.y < projectedVerts[i].y) min.y = projectedVerts[i].y;
if (max.x > projectedVerts[i].x) max.x = projectedVerts[i].x;
if (max.y > projectedVerts[i].y) max.y = projectedVerts[i].y;
}
for (int i = 0; i < projectedVerts.Length; i++)
{
_tsMesh.uv[i].x = Mathf.InverseLerp(max.x, min.x, projectedVerts[i].x) * uvScale.x - uvScale.x * 0.5f + uvOffset.x + 0.5f;
_tsMesh.uv[i].y = Mathf.InverseLerp(min.y, max.y, projectedVerts[i].y) * uvScale.y - uvScale.y * 0.5f + uvOffset.y + 0.5f;
_tsMesh.uv[i] = Quaternion.AngleAxis(uvRotation, Vector3.forward) * _tsMesh.uv[i];
}
#endregion
if (flipCap)
{
for (int i = 0; i < surfaceVertexCount; i++)
{
_tsMesh.normals[i] *= -1f;
}
}
if (_expand != 0f)
{
for (int i = 0; i < surfaceVertexCount; i++)
{
GetSample(i, ref evalResult);
_tsMesh.vertices[i] += (clockwise ? -evalResult.right : evalResult.right) * _expand;
}
}
if (pathExtrude)
{
GetIdentityVerts(center, normal, clockwise);
//Generate cap vertices with flipped normals
for (int i = 0; i < surfaceVertexCount; i++)
{
Vector3 vertexOffset = TransformOffset(extrudeResults[0], _extrudeOffset);
_tsMesh.vertices[i + surfaceVertexCount] = extrudeResults[0].position + (extrudeResults[0].rotation * identityVertices[i] + off) + vertexOffset;
_tsMesh.normals[i + surfaceVertexCount] = -extrudeResults[0].forward;
_tsMesh.colors[i + surfaceVertexCount] = _tsMesh.colors[i] * extrudeResults[0].color;
_tsMesh.uv[i + surfaceVertexCount] = new Vector2(1f - _tsMesh.uv[i].x, _tsMesh.uv[i].y);
vertexOffset = TransformOffset(extrudeResults[extrudeResults.Length - 1], _extrudeOffset);
_tsMesh.vertices[i] = extrudeResults[extrudeResults.Length - 1].position + (extrudeResults[extrudeResults.Length - 1].rotation * identityVertices[i] + off) + vertexOffset;
_tsMesh.normals[i] = extrudeResults[extrudeResults.Length - 1].forward;
_tsMesh.colors[i] *= extrudeResults[extrudeResults.Length - 1].color;
}
//Add wall vertices
float totalLength = 0f;
for (int i = 0; i < extrudeResults.Length; i++)
{
if (_uniformUvs && i > 0) totalLength += Vector3.Distance(extrudeResults[i].position, extrudeResults[i - 1].position);
int startIndex = surfaceVertexCount * 2 + i * sampleCount;
for (int n = 0; n < identityVertices.Length; n++)
{
Vector3 vertexOffset = TransformOffset(extrudeResults[i], _extrudeOffset);
_tsMesh.vertices[startIndex + n] = extrudeResults[i].position + (extrudeResults[i].rotation * identityVertices[n] + off) + vertexOffset;
_tsMesh.normals[startIndex + n] = extrudeResults[i].rotation * identityNormals[n];
if (_uniformUvs)
{
_tsMesh.uv[startIndex + n] = new Vector2((float)n / (identityVertices.Length - 1) * _sideUvScale.x + _sideUvOffset.x, totalLength * _sideUvScale.y + _sideUvOffset.y);
}
else
{
_tsMesh.uv[startIndex + n] = new Vector2((float)n / (identityVertices.Length - 1) * _sideUvScale.x + _sideUvOffset.x, (float)i / (extrudeResults.Length - 1) * _sideUvScale.y + _sideUvOffset.y);
}
if (_sideUvRotation != 0f)
{
_tsMesh.uv[startIndex + n] = Quaternion.AngleAxis(_sideUvRotation, Vector3.forward) * _tsMesh.uv[startIndex + n];
}
if (clockwise)
{
_tsMesh.uv[startIndex + n].x = 1f - _tsMesh.uv[startIndex + n].x;
}
}
}
int written = WriteTris(ref surfaceTris, ref _tsMesh.triangles, 0, 0, false);
written = WriteTris(ref surfaceTris, ref _tsMesh.triangles, surfaceVertexCount, written, true);
MeshUtility.GeneratePlaneTriangles(ref wallTris, sampleCount - 1, extrudeResults.Length, flipSide, 0, 0, true);
WriteTris(ref wallTris, ref _tsMesh.triangles, surfaceVertexCount * 2, written, false);
}
else if (simpleExtrude)
{
//Duplicate cap vertices with flipped normals
for (int i = 0; i < surfaceVertexCount; i++)
{
_tsMesh.vertices[i + surfaceVertexCount] = _tsMesh.vertices[i];
_tsMesh.normals[i + surfaceVertexCount] = -_tsMesh.normals[i];
_tsMesh.colors[i + surfaceVertexCount] = _tsMesh.colors[i];
_tsMesh.uv[i + surfaceVertexCount] = new Vector2(1f - _tsMesh.uv[i].x, _tsMesh.uv[i].y);
_tsMesh.vertices[i] += normal * _extrude;
}
//Add wall vertices
for (int i = 0; i < surfaceVertexCount + 1; i++)
{
int index = i;
if (i >= surfaceVertexCount) index = i - surfaceVertexCount;
GetSample(index, ref evalResult);
_tsMesh.vertices[i + surfaceVertexCount * 2] = _tsMesh.vertices[index] - normal * _extrude;
_tsMesh.normals[i + surfaceVertexCount * 2] = clockwise ? -evalResult.right : evalResult.right;
_tsMesh.colors[i + surfaceVertexCount * 2] = _tsMesh.colors[index];
_tsMesh.uv[i + surfaceVertexCount * 2] = new Vector2((float)i / (surfaceVertexCount - 1) * _sideUvScale.x + _sideUvOffset.x, 0f + _sideUvOffset.y);
if (clockwise)
{
_tsMesh.uv[i + surfaceVertexCount * 2].x = 1f - _tsMesh.uv[i + surfaceVertexCount * 2].x;
}
int offsetIndex = i + surfaceVertexCount * 3 + 1;
_tsMesh.vertices[offsetIndex] = _tsMesh.vertices[index];
_tsMesh.normals[offsetIndex] = _tsMesh.normals[i + surfaceVertexCount * 2];
_tsMesh.colors[offsetIndex] = _tsMesh.colors[index];
if (_uniformUvs)
{
_tsMesh.uv[offsetIndex] = new Vector2((float)i / surfaceVertexCount * _sideUvScale.x + _sideUvOffset.x, _extrude * _sideUvScale.y + _sideUvOffset.y);
}
else
{
_tsMesh.uv[offsetIndex] = new Vector2((float)i / surfaceVertexCount * _sideUvScale.x + _sideUvOffset.x, 1f * _sideUvScale.y + _sideUvOffset.y);
}
if (_sideUvRotation != 0f)
{
_tsMesh.uv[offsetIndex] = Quaternion.AngleAxis(_sideUvRotation, Vector3.forward) * _tsMesh.uv[offsetIndex];
}
if (clockwise)
{
_tsMesh.uv[offsetIndex].x = 1f - _tsMesh.uv[offsetIndex].x;
}
}
int written = WriteTris(ref surfaceTris, ref _tsMesh.triangles, 0, 0, false);
written = WriteTris(ref surfaceTris, ref _tsMesh.triangles, surfaceVertexCount, written, true);
MeshUtility.GeneratePlaneTriangles(ref wallTris, sampleCount - 1, 2, flipSide, 0, 0, true);
WriteTris(ref wallTris, ref _tsMesh.triangles, surfaceVertexCount * 2, written, false);
}
else
{
WriteTris(ref surfaceTris, ref _tsMesh.triangles, 0, 0, false);
}
}
private void GenerateSurfaceTris(bool flip)
{
MeshUtility.Triangulate(projectedVerts, ref surfaceTris);
if (flip) MeshUtility.FlipTriangles(ref surfaceTris);
}
private int WriteTris(ref int[] tris, ref int[] target, int vertexOffset, int trisOffset, bool flip)
{
for (int i = trisOffset; i < trisOffset + tris.Length; i += 3)
{
if (flip)
{
target[i] = tris[i + 2 - trisOffset] + vertexOffset;
target[i + 1] = tris[i + 1 - trisOffset] + vertexOffset;
target[i + 2] = tris[i - trisOffset] + vertexOffset;
}
else
{
target[i] = tris[i - trisOffset] + vertexOffset;
target[i + 1] = tris[i + 1 - trisOffset] + vertexOffset;
target[i + 2] = tris[i + 2 - trisOffset] + vertexOffset;
}
}
return trisOffset + tris.Length;
}
bool IsClockwise(Vector2[] points2D)
{
float sum = 0f;
for (int i = 1; i < points2D.Length; i++)
{
Vector2 v1 = points2D[i];
Vector2 v2 = points2D[(i + 1) % points2D.Length];
sum += (v2.x - v1.x) * (v2.y + v1.y);
}
sum += (points2D[0].x - points2D[points2D.Length - 1].x) * (points2D[0].y + points2D[points2D.Length - 1].y);
return sum <= 0f;
}
void GetIdentityVerts(Vector3 center, Vector3 normal, bool clockwise)
{
Quaternion vertsRotation = Quaternion.Inverse(Quaternion.LookRotation(normal));
if (identityVertices.Length != sampleCount)
{
identityVertices = new Vector3[sampleCount];
identityNormals = new Vector3[sampleCount];
}
for (int i = 0; i < sampleCount; i++)
{
GetSampleRaw(i, ref evalResult);
Vector3 right = evalResult.right;
identityVertices[i] = vertsRotation * (evalResult.position - center + (clockwise ? -right : right) * _expand);
identityNormals[i] = vertsRotation * (clockwise ? -right : right);
}
}
void GetProjectedVertices(int count, out Vector3 center, out Vector3 normal)
{
center = Vector3.zero;
normal = Vector3.zero;
Vector3 off = _trsRight * offset.x + _trsUp * offset.y + _trsForward * offset.z;
for (int i = 0; i < count; i++)
{
GetSampleRaw(i, ref evalResult);
center += evalResult.position + off;
normal += evalResult.up;
}
normal.Normalize();
center /= count;
Quaternion rot = Quaternion.LookRotation(normal, Vector3.up);
Vector3 up = rot * Vector3.up;
Vector3 right = rot * Vector3.right;
if (projectedVerts.Length != count) projectedVerts = new Vector2[count];
for (int i = 0; i < count; i++)
{
GetSampleRaw(i, ref evalResult);
Vector3 point = evalResult.position + off - center;
float projectionPointX = Vector3.Project(point, right).magnitude;
if (Vector3.Dot(point, right) < 0.0f) projectionPointX *= -1f;
float projectionPointY = Vector3.Project(point, up).magnitude;
if (Vector3.Dot(point, up) < 0.0f) projectionPointY *= -1f;
projectedVerts[i].x = projectionPointX;
projectedVerts[i].y = projectionPointY;
}
}
}
}