385 lines
15 KiB
C#
385 lines
15 KiB
C#
using UnityEngine;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Threading;
|
|
|
|
namespace Dreamteck.Splines
|
|
{
|
|
[RequireComponent(typeof(MeshFilter))]
|
|
[RequireComponent(typeof(MeshRenderer))]
|
|
[AddComponentMenu("Dreamteck/Splines/Users/Tube Generator")]
|
|
public class TubeGenerator : MeshGenerator
|
|
{
|
|
public enum CapMethod { None, Flat, Round }
|
|
|
|
public int sides
|
|
{
|
|
get { return _sides; }
|
|
set
|
|
{
|
|
if (value != _sides)
|
|
{
|
|
if (value < 3) value = 3;
|
|
_sides = value;
|
|
Rebuild();
|
|
}
|
|
}
|
|
}
|
|
|
|
public CapMethod capMode
|
|
{
|
|
get { return _capMode; }
|
|
set
|
|
{
|
|
if (value != _capMode)
|
|
{
|
|
_capMode = value;
|
|
Rebuild();
|
|
}
|
|
}
|
|
}
|
|
|
|
public int roundCapLatitude
|
|
{
|
|
get { return _roundCapLatitude; }
|
|
set
|
|
{
|
|
if (value < 1) value = 1;
|
|
if (value != _roundCapLatitude)
|
|
{
|
|
_roundCapLatitude = value;
|
|
if(_capMode == CapMethod.Round) Rebuild();
|
|
}
|
|
}
|
|
}
|
|
|
|
public float revolve
|
|
{
|
|
get { return _revolve; }
|
|
set
|
|
{
|
|
if (value != _revolve)
|
|
{
|
|
_revolve = value;
|
|
Rebuild();
|
|
}
|
|
}
|
|
}
|
|
|
|
public float capUVScale
|
|
{
|
|
get { return _capUVScale; }
|
|
set
|
|
{
|
|
if (value != _capUVScale)
|
|
{
|
|
_capUVScale = value;
|
|
Rebuild();
|
|
}
|
|
}
|
|
}
|
|
|
|
public float uvTwist
|
|
{
|
|
get { return _uvTwist; }
|
|
set
|
|
{
|
|
if (value != _uvTwist)
|
|
{
|
|
_uvTwist = value;
|
|
Rebuild();
|
|
}
|
|
}
|
|
}
|
|
|
|
[SerializeField]
|
|
[HideInInspector]
|
|
private int _sides = 12;
|
|
[SerializeField]
|
|
[HideInInspector]
|
|
private int _roundCapLatitude = 6;
|
|
[SerializeField]
|
|
[HideInInspector]
|
|
private CapMethod _capMode = CapMethod.None;
|
|
[SerializeField]
|
|
[HideInInspector]
|
|
[Range(0f, 360f)]
|
|
private float _revolve = 360f;
|
|
[SerializeField]
|
|
[HideInInspector]
|
|
private float _capUVScale = 1f;
|
|
[SerializeField]
|
|
[HideInInspector]
|
|
private float _uvTwist = 0f;
|
|
|
|
private bool useCap
|
|
{
|
|
get
|
|
{
|
|
bool isCapSet = _capMode != CapMethod.None;
|
|
if (spline != null) return isCapSet && (!spline.isClosed || span < 1f);
|
|
return isCapSet;
|
|
}
|
|
}
|
|
|
|
protected override string meshName => "Tube";
|
|
|
|
private int bodyVertexCount = 0;
|
|
private int bodyTrisCount = 0;
|
|
private int capVertexCount = 0;
|
|
private int capTrisCount = 0;
|
|
|
|
protected override void Reset()
|
|
{
|
|
base.Reset();
|
|
}
|
|
|
|
protected override void BuildMesh()
|
|
{
|
|
if (_sides <= 2) return;
|
|
base.BuildMesh();
|
|
bodyVertexCount = (_sides + 1) * sampleCount;
|
|
CapMethod _capModeFinal = _capMode;
|
|
if (!useCap)
|
|
{
|
|
_capModeFinal = CapMethod.None;
|
|
}
|
|
switch (_capModeFinal)
|
|
{
|
|
case CapMethod.Flat: capVertexCount = _sides + 1; break;
|
|
case CapMethod.Round: capVertexCount = _roundCapLatitude * (sides + 1); break;
|
|
default: capVertexCount = 0; break;
|
|
}
|
|
int vertexCount = bodyVertexCount + capVertexCount * 2;
|
|
|
|
bodyTrisCount = _sides * (sampleCount - 1) * 2 * 3;
|
|
switch (_capModeFinal)
|
|
{
|
|
case CapMethod.Flat: capTrisCount = (_sides - 1) * 3 * 2; break;
|
|
case CapMethod.Round: capTrisCount = _sides * _roundCapLatitude * 6; break;
|
|
default: capTrisCount = 0; break;
|
|
}
|
|
AllocateMesh(vertexCount, bodyTrisCount + capTrisCount * 2);
|
|
|
|
Generate();
|
|
switch (_capModeFinal)
|
|
{
|
|
case CapMethod.Flat: GenerateFlatCaps(); break;
|
|
case CapMethod.Round: GenerateRoundCaps(); break;
|
|
}
|
|
}
|
|
|
|
void Generate()
|
|
{
|
|
int vertexIndex = 0;
|
|
ResetUVDistance();
|
|
bool hasOffset = offset != Vector3.zero;
|
|
for (int i = 0; i < sampleCount; i++)
|
|
{
|
|
GetSample(i, ref evalResult);
|
|
Vector3 center = evalResult.position;
|
|
Vector3 right = evalResult.right;
|
|
float resultSize = GetBaseSize(evalResult);
|
|
if (hasOffset)
|
|
{
|
|
center += (offset.x * resultSize) * right + (offset.y * resultSize) * evalResult.up + (offset.z * resultSize) * evalResult.forward;
|
|
}
|
|
if (uvMode == UVMode.UniformClamp || uvMode == UVMode.UniformClip)
|
|
{
|
|
AddUVDistance(i);
|
|
}
|
|
Color vertexColor = GetBaseColor(evalResult) * color;
|
|
for (int n = 0; n < _sides + 1; n++)
|
|
{
|
|
float anglePercent = (float)(n) / _sides;
|
|
Quaternion rot = Quaternion.AngleAxis(_revolve * anglePercent + rotation + 180f, evalResult.forward);
|
|
_tsMesh.vertices[vertexIndex] = center + rot * right * (size * resultSize * 0.5f);
|
|
CalculateUVs(evalResult.percent, anglePercent);
|
|
_tsMesh.uv[vertexIndex] = Vector2.one * 0.5f + (Vector2)(Quaternion.AngleAxis(uvRotation + 180f, Vector3.forward) * (Vector2.one * 0.5f - (__uvs + Vector2.right * ((float)evalResult.percent * _uvTwist))));
|
|
_tsMesh.normals[vertexIndex] = Vector3.Normalize(_tsMesh.vertices[vertexIndex] - center);
|
|
_tsMesh.colors[vertexIndex] = vertexColor;
|
|
vertexIndex++;
|
|
}
|
|
}
|
|
MeshUtility.GeneratePlaneTriangles(ref _tsMesh.triangles, _sides, sampleCount, false);
|
|
}
|
|
|
|
void GenerateFlatCaps()
|
|
{
|
|
//Start Cap
|
|
|
|
GetSample(0, ref evalResult);
|
|
for (int i = 0; i < _sides+1; i++)
|
|
{
|
|
int index = bodyVertexCount + i;
|
|
_tsMesh.vertices[index] = _tsMesh.vertices[i];
|
|
_tsMesh.normals[index] = -evalResult.forward;
|
|
_tsMesh.colors[index] = _tsMesh.colors[i];
|
|
_tsMesh.uv[index] = Quaternion.AngleAxis(_revolve * (((float)i) / (_sides - 1)), Vector3.forward) * Vector2.right * (0.5f * capUVScale) + Vector3.right * 0.5f + Vector3.up * 0.5f;
|
|
}
|
|
|
|
//End Cap
|
|
GetSample(sampleCount - 1, ref evalResult);
|
|
for (int i = 0; i < _sides + 1; i++)
|
|
{
|
|
int index = bodyVertexCount + (_sides + 1) + i;
|
|
int bodyIndex = bodyVertexCount - (_sides + 1) + i;
|
|
_tsMesh.vertices[index] = _tsMesh.vertices[bodyIndex];
|
|
_tsMesh.normals[index] = evalResult.forward;
|
|
_tsMesh.colors[index] = _tsMesh.colors[bodyIndex];
|
|
_tsMesh.uv[index] = Quaternion.AngleAxis(_revolve * ((float)(bodyIndex) / (_sides - 1)), Vector3.forward) * Vector2.right * (0.5f * capUVScale) + Vector3.right * 0.5f + Vector3.up * 0.5f;
|
|
}
|
|
|
|
int t = bodyTrisCount;
|
|
bool fullIntegrity = _revolve == 360f;
|
|
int finalSides = fullIntegrity ? _sides - 1 : _sides;
|
|
//Start cap
|
|
for (int i = 0; i < finalSides - 1; i++)
|
|
{
|
|
_tsMesh.triangles[t++] = i + bodyVertexCount + 2;
|
|
_tsMesh.triangles[t++] = i + +bodyVertexCount + 1;
|
|
_tsMesh.triangles[t++] = bodyVertexCount;
|
|
}
|
|
|
|
//End cap
|
|
for (int i = 0; i < finalSides - 1; i++)
|
|
{
|
|
_tsMesh.triangles[t++] = bodyVertexCount + (_sides + 1);
|
|
_tsMesh.triangles[t++] = i + 1 + bodyVertexCount + (_sides + 1);
|
|
_tsMesh.triangles[t++] = i + 2 + bodyVertexCount + (_sides + 1);
|
|
}
|
|
}
|
|
|
|
void GenerateRoundCaps()
|
|
{
|
|
//Start Cap
|
|
GetSample(0, ref evalResult);
|
|
Vector3 center = evalResult.position;
|
|
bool hasOffset = offset != Vector3.zero;
|
|
float resultSize = GetBaseSize(evalResult);
|
|
if (hasOffset)
|
|
{
|
|
center += (offset.x * resultSize) * evalResult.right + (offset.y * resultSize) * evalResult.up + (offset.z * resultSize) * evalResult.forward;
|
|
}
|
|
Quaternion lookRot = Quaternion.LookRotation(-evalResult.forward, evalResult.up);
|
|
float startV = 0f;
|
|
float capLengthPercent = 0f;
|
|
switch (uvMode)
|
|
{
|
|
case UVMode.Clip: startV = (float)evalResult.percent;
|
|
capLengthPercent = (size * 0.5f) / spline.CalculateLength(); break;
|
|
case UVMode.UniformClip:
|
|
startV = spline.CalculateLength(0.0, evalResult.percent);
|
|
capLengthPercent = size * 0.5f; break;
|
|
case UVMode.UniformClamp:
|
|
startV = 0f;
|
|
capLengthPercent = size * 0.5f / (float)span;
|
|
break;
|
|
case UVMode.Clamp: capLengthPercent = (size * 0.5f) / spline.CalculateLength(clipFrom, clipTo); break;
|
|
}
|
|
|
|
Color vertexColor = GetBaseColor(evalResult) * color;
|
|
for (int lat = 1; lat < _roundCapLatitude+1; lat++)
|
|
{
|
|
float latitudePercent = ((float)lat / _roundCapLatitude);
|
|
float latAngle = 90f * latitudePercent;
|
|
for (int lon = 0; lon <= sides; lon++)
|
|
{
|
|
float anglePercent = (float)lon / sides;
|
|
int index = bodyVertexCount + lon + (lat-1) * (sides + 1);
|
|
Quaternion rot = Quaternion.AngleAxis(_revolve * anglePercent + rotation + 180f, -Vector3.forward) * Quaternion.AngleAxis(latAngle, Vector3.up);
|
|
_tsMesh.vertices[index] = center + lookRot * rot * -Vector3.right * (size * 0.5f * evalResult.size);
|
|
_tsMesh.colors[index] = vertexColor;
|
|
_tsMesh.normals[index] = (_tsMesh.vertices[index] - center).normalized;
|
|
float baseV = startV + capLengthPercent * latitudePercent;
|
|
Vector2 baseUV = new Vector2(anglePercent * uvScale.x - baseV * _uvTwist, baseV * uvScale.y) - uvOffset;
|
|
_tsMesh.uv[index] = Vector2.one * 0.5f + (Vector2)(Quaternion.AngleAxis(uvRotation + 180f, Vector3.forward) * (Vector2.one * 0.5f - baseUV));
|
|
}
|
|
}
|
|
|
|
|
|
//Triangles
|
|
int t = bodyTrisCount;
|
|
for (int z = -1; z < _roundCapLatitude - 1; z++)
|
|
{
|
|
for (int x = 0; x < sides; x++)
|
|
{
|
|
int current = bodyVertexCount + x + z * (sides + 1);
|
|
int next = current + (sides + 1);
|
|
if (z == -1)
|
|
{
|
|
current = x;
|
|
next = bodyVertexCount + x;
|
|
}
|
|
_tsMesh.triangles[t++] = next + 1;
|
|
_tsMesh.triangles[t++] = current + 1;
|
|
_tsMesh.triangles[t++] = current;
|
|
_tsMesh.triangles[t++] = next;
|
|
_tsMesh.triangles[t++] = next + 1;
|
|
_tsMesh.triangles[t++] = current;
|
|
}
|
|
}
|
|
|
|
|
|
//End Cap
|
|
GetSample(sampleCount - 1, ref evalResult);
|
|
center = evalResult.position;
|
|
resultSize = GetBaseSize(evalResult);
|
|
if (hasOffset)
|
|
{
|
|
center += (offset.x * resultSize) * evalResult.right + (offset.y * resultSize) * evalResult.up + (offset.z * resultSize) * evalResult.forward;
|
|
}
|
|
lookRot = Quaternion.LookRotation(evalResult.forward, evalResult.up);
|
|
switch (uvMode)
|
|
{
|
|
case UVMode.Clip: startV = (float)evalResult.percent; break;
|
|
case UVMode.UniformClip: startV = spline.CalculateLength(0.0, evalResult.percent); break;
|
|
case UVMode.Clamp: startV = 1f; break;
|
|
case UVMode.UniformClamp: startV = spline.CalculateLength(); break;
|
|
}
|
|
|
|
vertexColor = GetBaseColor(evalResult) * color;
|
|
for (int lat = 1; lat < _roundCapLatitude+1; lat++)
|
|
{
|
|
float latitudePercent = ((float)lat / _roundCapLatitude);
|
|
float latAngle = 90f * latitudePercent;
|
|
for (int lon = 0; lon <= sides; lon++)
|
|
{
|
|
float anglePercent = (float)lon / sides;
|
|
int index = bodyVertexCount + capVertexCount + lon + (lat - 1) * (sides + 1);
|
|
Quaternion rot = Quaternion.AngleAxis(_revolve * anglePercent + rotation + 180f, Vector3.forward) * Quaternion.AngleAxis(latAngle, -Vector3.up);
|
|
_tsMesh.vertices[index] = center + lookRot * rot * Vector3.right * size * 0.5f * evalResult.size;
|
|
_tsMesh.normals[index] = (_tsMesh.vertices[index] - center).normalized;
|
|
_tsMesh.colors[index] = vertexColor;
|
|
float baseV = startV + capLengthPercent * latitudePercent;
|
|
Vector2 baseUV = new Vector2(anglePercent * uvScale.x + baseV * _uvTwist, baseV * uvScale.y) - uvOffset;
|
|
_tsMesh.uv[index] = Vector2.one * 0.5f + (Vector2)(Quaternion.AngleAxis(uvRotation + 180f, Vector3.forward) * (Vector2.one * 0.5f - baseUV));
|
|
}
|
|
}
|
|
|
|
//Triangles
|
|
for (int z = -1; z < _roundCapLatitude - 1; z++)
|
|
{
|
|
for (int x = 0; x < sides; x++)
|
|
{
|
|
int current = bodyVertexCount + capVertexCount + x + z * (sides + 1);
|
|
int next = current + (sides + 1);
|
|
if (z == -1)
|
|
{
|
|
current = bodyVertexCount - (_sides+1) + x;
|
|
next = bodyVertexCount + capVertexCount + x;
|
|
}
|
|
|
|
_tsMesh.triangles[t++] = current+1;
|
|
_tsMesh.triangles[t++] = next + 1;
|
|
_tsMesh.triangles[t++] = next;
|
|
_tsMesh.triangles[t++] = next;
|
|
_tsMesh.triangles[t++] = current;
|
|
_tsMesh.triangles[t++] = current + 1;
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|