Files
2026-06-07 22:59:32 +08:00

1644 lines
61 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/Spline Mesh")]
public class SplineMesh : MeshGenerator
{
//Mesh data
[SerializeField]
[HideInInspector]
private List<Channel> channels = new List<Channel>();
private bool useLastResult = false;
private List<TS_Mesh> combineMeshes = new List<TS_Mesh>();
private int meshCount = 0;
Matrix4x4 vertexMatrix = new Matrix4x4();
Matrix4x4 normalMatrix = new Matrix4x4();
SplineSample lastResult = new SplineSample(), modifiedResult = new SplineSample();
#if UNITY_EDITOR
public override void EditorAwake()
{
for (int i = 0; i < channels.Count; i++)
{
for (int j = 0; j < channels[i].GetMeshCount(); j++) channels[i].GetMesh(j).Refresh();
}
base.EditorAwake();
}
#endif
protected override void Awake()
{
base.Awake();
mesh.name = "Extruded Mesh";
}
protected override void Reset()
{
base.Reset();
AddChannel("Channel 1");
}
public void RemoveChannel(int index)
{
channels.RemoveAt(index);
Rebuild();
}
public void SwapChannels(int a, int b)
{
if (a < 0 || a >= channels.Count || b < 0 || b >= channels.Count) return;
Channel temp = channels[b];
channels[b] = channels[a];
channels[a] = temp;
Rebuild();
}
public Channel AddChannel(Mesh inputMesh, string name)
{
Channel channel = new Channel(name, inputMesh, this);
channels.Add(channel);
return channel;
}
public Channel AddChannel(string name)
{
Channel channel = new Channel(name, this);
channels.Add(channel);
return channel;
}
public int GetChannelCount()
{
return channels.Count;
}
public Channel GetChannel(int index)
{
return channels[index];
}
protected override void BuildMesh()
{
if (sampleCount == 0) return;
base.BuildMesh();
Generate();
}
void Generate()
{
meshCount = 0;
for (int i = 0; i < channels.Count; i++)
{
if (channels[i].GetMeshCount() == 0) continue;
meshCount += channels[i].count;
}
if(meshCount == 0)
{
tsMesh.Clear();
return;
}
if (combineMeshes.Count < meshCount) combineMeshes.AddRange(new TS_Mesh[meshCount - combineMeshes.Count]);
else if (combineMeshes.Count > meshCount) combineMeshes.RemoveRange((combineMeshes.Count - 1) - (combineMeshes.Count - meshCount), combineMeshes.Count - meshCount);
int combineMeshIndex = 0;
for (int i = 0; i < channels.Count; i++)
{
if (channels[i].GetMeshCount() == 0) continue;
if (channels[i].autoCount)
{
float avgBounds = 0f;
for (int j = 0; j < channels[i].GetMeshCount(); j++) avgBounds += channels[i].GetMesh(j).bounds.size.z;
if (channels[i].GetMeshCount() > 1) avgBounds /= channels[i].GetMeshCount();
if(avgBounds > 0f)
{
float length = CalculateLength(channels[i].clipFrom, channels[i].clipTo);
channels[i].count = Mathf.RoundToInt(length / avgBounds);
if (channels[i].count < 1) channels[i].count = 1;
}
}
channels[i].ResetIteration();
useLastResult = false;
double step = 1.0 / channels[i].count;
double space = step * channels[i].spacing * 0.5;
switch (channels[i].type)
{
case Channel.Type.Extrude:
for (int j = 0; j < channels[i].count; j++)
{
double from = DMath.Lerp(channels[i].clipFrom, channels[i].clipTo, j * step + space);
double to = DMath.Lerp(channels[i].clipFrom, channels[i].clipTo, j * step + step - space);
if (combineMeshes[combineMeshIndex] == null) combineMeshes[combineMeshIndex] = new TS_Mesh();
Stretch(channels[i], combineMeshes[combineMeshIndex], from, to);
combineMeshIndex++;
}
if (space == 0f) useLastResult = true;
break;
case Channel.Type.Place:
for (int j = 0; j < channels[i].count; j++)
{
if (combineMeshes[combineMeshIndex] == null) combineMeshes[combineMeshIndex] = new TS_Mesh();
Place(channels[i], combineMeshes[combineMeshIndex], DMath.Lerp(channels[i].clipFrom, channels[i].clipTo, (double)j / Mathf.Max(channels[i].count - 1, 1)));
combineMeshIndex++;
}
break;
}
}
if (tsMesh == null) tsMesh = new TS_Mesh();
else tsMesh.Clear();
tsMesh.Combine(combineMeshes, false);
}
private void Place(Channel channel, TS_Mesh target, double percent)
{
Channel.MeshDefinition definition = channel.NextMesh();
if (target == null) target = new TS_Mesh();
definition.Write(target, channel.overrideMaterialID ? channel.targetMaterialID : -1);
Vector2 channeloffset = channel.NextOffset(percent);
Evaluate(percent, evalResult);
ModifySample(evalResult);
Vector3 originalNormal = evalResult.up;
Vector3 originalRight = evalResult.right;
Vector3 originalDirection = evalResult.forward;
if (channel.overrideNormal)
{
evalResult.forward = Vector3.Cross(evalResult.right, channel.customNormal);
evalResult.up = channel.customNormal;
}
Quaternion axisRotation = Quaternion.identity;
vertexMatrix.SetTRS(evalResult.position + originalRight * (offset.x + channeloffset.x) * evalResult.size + originalNormal * (offset.y + channeloffset.y) * evalResult.size + originalDirection * offset.z, evalResult.rotation * channel.NextPlaceRotation(percent) * Quaternion.AngleAxis(rotation, Vector3.forward), channel.NextPlaceScale() * evalResult.size);
normalMatrix = vertexMatrix.inverse.transpose;
for (int i = 0; i < target.vertexCount; i++)
{
target.vertices[i] = vertexMatrix.MultiplyPoint3x4(definition.vertices[i]);
target.normals[i] = normalMatrix.MultiplyVector(definition.normals[i]);
}
for (int i = 0; i < Mathf.Min(target.colors.Length, definition.colors.Length); i++) target.colors[i] = definition.colors[i] * evalResult.color;
}
private void Stretch(Channel channel, TS_Mesh target, double from, double to)
{
Channel.MeshDefinition definition = channel.NextMesh();
if (target == null) target = new TS_Mesh();
definition.Write(target, channel.overrideMaterialID ? channel.targetMaterialID : -1);
Vector2 uv = Vector2.zero;
Vector3 trsVector = Vector3.zero;
for (int i = 0; i < definition.vertexGroups.Count; i++)
{
if (useLastResult && i == definition.vertexGroups.Count) evalResult = lastResult;
else Evaluate(DMath.Lerp(from, to, definition.vertexGroups[i].percent), evalResult);
ModifySample(evalResult, modifiedResult);
Vector3 originalNormal = modifiedResult.up;
Vector3 originalRight = modifiedResult.right;
Vector3 originalDirection = modifiedResult.forward;
if (channel.overrideNormal)
{
modifiedResult.forward = Vector3.Cross(modifiedResult.right, channel.customNormal);
modifiedResult.up = channel.customNormal;
}
Vector2 channelOffset = channel.NextOffset(modifiedResult.percent);
Vector3 channelScale = channel.NextExtrudeScale(modifiedResult.percent);
float channelRotation = channel.NextExtrudeRotation(modifiedResult.percent);
vertexMatrix.SetTRS(modifiedResult.position + originalRight * (offset.x + channelOffset.x) * modifiedResult.size + originalNormal * (offset.y + channelOffset.y) * modifiedResult.size + originalDirection * offset.z,
modifiedResult.rotation * Quaternion.AngleAxis(rotation + channelRotation, Vector3.forward),
channelScale * modifiedResult.size);
normalMatrix = vertexMatrix.inverse.transpose;
if (i == 0) lastResult.CopyFrom(evalResult);
for (int n = 0; n < definition.vertexGroups[i].ids.Length; n++)
{
int index = definition.vertexGroups[i].ids[n];
trsVector = definition.vertices[index];
trsVector.z = 0f;
target.vertices[index] = vertexMatrix.MultiplyPoint3x4(trsVector);
trsVector = definition.normals[index];
target.normals[index] = normalMatrix.MultiplyVector(trsVector);
target.colors[index] = target.colors[index] * modifiedResult.color;
if (target.uv.Length > index)
{
uv = target.uv[index];
switch (channel.overrideUVs)
{
case Channel.UVOverride.ClampU: uv.x = (float)modifiedResult.percent; break;
case Channel.UVOverride.ClampV: uv.y = (float)modifiedResult.percent; break;
case Channel.UVOverride.UniformU: uv.x = CalculateLength(0.0, ClipPercent(modifiedResult.percent)); break;
case Channel.UVOverride.UniformV: uv.y = CalculateLength(0.0, ClipPercent(modifiedResult.percent)); break;
}
target.uv[index] = new Vector2(uv.x * uvScale.x * channel.uvScale.x, uv.y * uvScale.y * channel.uvScale.y);
target.uv[index] += uvOffset + channel.uvOffset;
}
}
}
}
[System.Serializable]
public class Channel
{
public delegate float FloatHandler(double percent);
public delegate Vector2 Vector2Handler(double percent);
public delegate Vector3 Vector3Handler(double percent);
public delegate Quaternion QuaternionHandler(double percent);
public string name = "Channel";
public enum Type { Extrude, Place }
public enum UVOverride { None, ClampU, ClampV, UniformU, UniformV }
private System.Random iterationRandom;
[SerializeField]
[HideInInspector]
private int _iterationSeed = 0;
[SerializeField]
[HideInInspector]
private int _offsetSeed = 0;
private System.Random offsetRandom;
private Vector2Handler _offsetHandler = null;
[SerializeField]
[HideInInspector]
private int _rotationSeed = 0;
private System.Random rotationRandom;
private QuaternionHandler _placeRotationHandler = null;
private FloatHandler _extrudeRotationHandler = null;
[SerializeField]
[HideInInspector]
private int _scaleSeed = 0;
private System.Random scaleRandom;
private Vector3Handler _scaleHandler = null;
[SerializeField]
internal SplineMesh owner = null;
[SerializeField]
[HideInInspector]
private List<MeshDefinition> meshes = new List<MeshDefinition>();
[SerializeField]
[HideInInspector]
private double _clipFrom = 0.0;
[SerializeField]
[HideInInspector]
private double _clipTo = 1.0;
[SerializeField]
[HideInInspector]
private bool _randomOrder = false;
[SerializeField]
[HideInInspector]
private UVOverride _overrideUVs = UVOverride.None;
[SerializeField]
[HideInInspector]
private Vector2 _uvScale = Vector2.one;
[SerializeField]
[HideInInspector]
private Vector2 _uvOffset = Vector2.zero;
[SerializeField]
[HideInInspector]
private bool _overrideNormal = false;
[SerializeField]
[HideInInspector]
private Vector3 _customNormal = Vector3.up;
[SerializeField]
[HideInInspector]
private Type _type = Type.Extrude;
[SerializeField]
[HideInInspector]
private int _count = 1;
[SerializeField]
[HideInInspector]
private bool _autoCount = false;
[SerializeField]
[HideInInspector]
private double _spacing = 0.0;
[SerializeField]
[HideInInspector]
private bool _randomRotation = false;
[SerializeField]
[HideInInspector]
private Vector3 _minRotation = Vector3.zero;
[SerializeField]
[HideInInspector]
private Vector3 _maxRotation = Vector3.zero;
[SerializeField]
[HideInInspector]
private bool _randomOffset = false;
[SerializeField]
[HideInInspector]
private Vector2 _minOffset = Vector2.one;
[SerializeField]
[HideInInspector]
private Vector2 _maxOffset = Vector2.one;
[SerializeField]
[HideInInspector]
private bool _randomScale = false;
[SerializeField]
[HideInInspector]
private bool _uniformRandomScale = false;
[SerializeField]
[HideInInspector]
private Vector3 _minScale = Vector3.one;
[SerializeField]
[HideInInspector]
private Vector3 _maxScale = Vector3.one;
private int iterator = 0;
[SerializeField]
[HideInInspector]
private bool _overrideMaterialID = false;
[SerializeField]
[HideInInspector]
private int _targetMaterialID = 0;
public double clipFrom
{
get { return _clipFrom; }
set {
if (value != _clipFrom)
{
_clipFrom = value;
Rebuild();
}
}
}
public double clipTo
{
get { return _clipTo; }
set
{
if (value != _clipTo)
{
_clipTo = value;
Rebuild();
}
}
}
public bool randomOffset
{
get { return _randomOffset; }
set
{
if (value != _randomOffset)
{
_randomOffset = value;
Rebuild();
}
}
}
public Vector2Handler offsetHandler
{
get { return _offsetHandler; }
set
{
if (value != _offsetHandler)
{
_offsetHandler = value;
Rebuild();
}
}
}
public bool overrideMaterialID
{
get { return _overrideMaterialID; }
set
{
if (value != _overrideMaterialID)
{
_overrideMaterialID = value;
Rebuild();
}
}
}
public int targetMaterialID
{
get { return _targetMaterialID; }
set
{
if (value != _targetMaterialID)
{
_targetMaterialID = value;
Rebuild();
}
}
}
public bool randomRotation
{
get { return _randomRotation; }
set
{
if (value != _randomRotation)
{
_randomRotation = value;
Rebuild();
}
}
}
public QuaternionHandler placeRotationHandler
{
get { return _placeRotationHandler; }
set
{
if (value != _placeRotationHandler)
{
_placeRotationHandler = value;
Rebuild();
}
}
}
public FloatHandler extrudeRotationHandler
{
get { return _extrudeRotationHandler; }
set
{
if (value != _extrudeRotationHandler)
{
_extrudeRotationHandler = value;
Rebuild();
}
}
}
public bool randomScale
{
get { return _randomScale; }
set
{
if (value != _randomScale)
{
_randomScale = value;
Rebuild();
}
}
}
public Vector3Handler scaleHandler
{
get { return _scaleHandler; }
set
{
if (value != _scaleHandler)
{
_scaleHandler = value;
Rebuild();
}
}
}
public bool uniformRandomScale
{
get { return _uniformRandomScale; }
set
{
if (value != _uniformRandomScale)
{
_uniformRandomScale = value;
Rebuild();
}
}
}
public int offsetSeed
{
get { return _offsetSeed; }
set
{
if (value != _offsetSeed)
{
_offsetSeed = value;
Rebuild();
}
}
}
public int rotationSeed
{
get { return _rotationSeed; }
set
{
if (value != _rotationSeed)
{
_rotationSeed = value;
Rebuild();
}
}
}
public int scaleSeed
{
get { return _scaleSeed; }
set
{
if (value != _scaleSeed)
{
_scaleSeed = value;
Rebuild();
}
}
}
public double spacing
{
get { return _spacing; }
set
{
if (value != _spacing)
{
_spacing = value;
Rebuild();
}
}
}
public Vector2 minOffset
{
get { return _minOffset; }
set
{
if (value != _minOffset)
{
_minOffset = value;
Rebuild();
}
}
}
public Vector2 maxOffset
{
get { return _maxOffset; }
set
{
if (value != _maxOffset)
{
_maxOffset = value;
Rebuild();
}
}
}
public Vector3 minRotation
{
get { return _minRotation; }
set
{
if (value != _minRotation)
{
_minRotation = value;
Rebuild();
}
}
}
public Vector3 maxRotation
{
get { return _maxRotation; }
set
{
if (value != _maxRotation)
{
_maxRotation = value;
Rebuild();
}
}
}
public Vector3 minScale
{
get { return _minScale; }
set
{
if (value != _minScale)
{
_minScale = value;
Rebuild();
}
}
}
public Vector3 maxScale
{
get { return _maxScale; }
set
{
if (value != _maxScale)
{
_maxScale = value;
Rebuild();
}
}
}
public Type type
{
get { return _type; }
set
{
if (value != _type)
{
_type = value;
Rebuild();
}
}
}
public bool randomOrder
{
get { return _randomOrder; }
set
{
if (value != _randomOrder)
{
_randomOrder = value;
Rebuild();
}
}
}
public int randomSeed
{
get { return _iterationSeed; }
set
{
if (value != _iterationSeed)
{
_iterationSeed = value;
if(_randomOrder) Rebuild();
}
}
}
public int count
{
get { return _count; }
set
{
if (value != _count)
{
_count = value;
if (_count < 1) _count = 1;
Rebuild();
}
}
}
public bool autoCount
{
get { return _autoCount; }
set
{
if (value != _autoCount)
{
_autoCount = value;
Rebuild();
}
}
}
public UVOverride overrideUVs
{
get { return _overrideUVs; }
set
{
if (value != _overrideUVs)
{
_overrideUVs = value;
Rebuild();
}
}
}
public Vector2 uvOffset
{
get { return _uvOffset; }
set
{
if (value != _uvOffset)
{
_uvOffset = value;
Rebuild();
}
}
}
public Vector2 uvScale
{
get { return _uvScale; }
set
{
if (value != _uvScale)
{
_uvScale = value;
Rebuild();
}
}
}
public bool overrideNormal
{
get { return _overrideNormal; }
set
{
if (value != _overrideNormal)
{
_overrideNormal = value;
Rebuild();
}
}
}
public Vector3 customNormal
{
get { return _customNormal; }
set
{
if (value != _customNormal)
{
_customNormal = value;
Rebuild();
}
}
}
public Channel(string n, SplineMesh parent)
{
name = n;
owner = parent;
Init();
}
public Channel(string n, Mesh inputMesh, SplineMesh parent)
{
name = n;
owner = parent;
meshes.Add(new MeshDefinition(inputMesh));
Init();
Rebuild();
}
void Init()
{
_minScale = _maxScale = Vector3.one;
_minOffset = _maxOffset = Vector3.zero;
_minRotation = _maxRotation = Vector3.zero;
}
public void CopyTo(Channel target)
{
target.meshes.Clear();
for (int i = 0; i < meshes.Count; i++) target.meshes.Add(meshes[i].Copy());
target._clipFrom = _clipFrom;
target._clipTo = _clipTo;
target._customNormal = _customNormal;
target._iterationSeed = _iterationSeed;
target._minOffset = _minOffset;
target._minRotation = _minRotation;
target._minScale = _minScale;
target._maxOffset = _maxOffset;
target._maxRotation = _maxRotation;
target._maxScale = _maxScale;
target._randomOffset = _randomOffset;
target._randomRotation = _randomRotation;
target._randomScale = _randomScale;
target._offsetSeed = _offsetSeed;
target._offsetHandler = _offsetHandler;
target._rotationSeed = _rotationSeed;
target._placeRotationHandler = _placeRotationHandler;
target._extrudeRotationHandler = _extrudeRotationHandler;
target._scaleSeed = _scaleSeed;
target._scaleHandler = _scaleHandler;
target._iterationSeed = _iterationSeed;
target._count = _count;
target._spacing = _spacing;
target._overrideUVs = _overrideUVs;
target._type = _type;
target._overrideMaterialID = _overrideMaterialID;
target._targetMaterialID = _targetMaterialID;
target._overrideNormal = _overrideNormal;
}
public int GetMeshCount()
{
return meshes.Count;
}
public void SwapMeshes(int a, int b)
{
if (a < 0 || a >= meshes.Count || b < 0 || b >= meshes.Count) return;
MeshDefinition temp = meshes[b];
meshes[b] = meshes[a];
meshes[a] = temp;
Rebuild();
}
public void DuplicateMesh(int index)
{
if (index < 0 || index >= meshes.Count) return;
meshes.Add(meshes[index].Copy());
Rebuild();
}
public MeshDefinition GetMesh(int index)
{
return meshes[index];
}
public void AddMesh(Mesh input)
{
meshes.Add(new MeshDefinition(input));
Rebuild();
}
public void RemoveMesh(int index)
{
meshes.RemoveAt(index);
Rebuild();
}
public void ResetIteration()
{
if (_randomOrder) iterationRandom = new System.Random(_iterationSeed);
if (_randomOffset) offsetRandom = new System.Random(_offsetSeed);
if (_randomRotation) rotationRandom = new System.Random(_rotationSeed);
if (_randomScale) scaleRandom = new System.Random(_scaleSeed);
iterator = 0;
}
public Vector2 NextOffset(double percent)
{
if (_offsetHandler != null) return _offsetHandler(percent);
if (_randomOffset) return new Vector2(Mathf.Lerp(_minOffset.x, _maxOffset.x, (float)offsetRandom.NextDouble()), Mathf.Lerp(_minOffset.y, _maxOffset.y, (float)offsetRandom.NextDouble()));
return _minOffset;
}
public Quaternion NextPlaceRotation(double percent)
{
if (_placeRotationHandler != null) return _placeRotationHandler(percent);
if (_randomRotation) return Quaternion.Euler(new Vector3(Mathf.Lerp(_minRotation.x, _maxRotation.x, (float)rotationRandom.NextDouble()), Mathf.Lerp(_minRotation.y, _maxRotation.y, (float)rotationRandom.NextDouble()), Mathf.Lerp(_minRotation.z, _maxRotation.z, (float)rotationRandom.NextDouble())));
return Quaternion.Euler(_minRotation);
}
public float NextExtrudeRotation(double percent)
{
if (_extrudeRotationHandler != null) return _extrudeRotationHandler(percent);
if (_randomRotation) return Mathf.Lerp(_minRotation.z, _maxRotation.z, (float)rotationRandom.NextDouble());
return _minRotation.z;
}
public Vector3 NextExtrudeScale(double percent)
{
if (_scaleHandler != null) return _scaleHandler(percent);
if (_randomScale)
{
if (_uniformRandomScale) return Vector3.Lerp(new Vector3(_minScale.x, _minScale.y, 1f), new Vector3(_maxScale.x, _maxScale.y, 1f), (float)scaleRandom.NextDouble());
return new Vector3(Mathf.Lerp(_minScale.x, _maxScale.x, (float)scaleRandom.NextDouble()), Mathf.Lerp(_minScale.y, _maxScale.y, (float)scaleRandom.NextDouble()), 1f);
}
return new Vector3(_minScale.x, _minScale.y, 1f);
}
public Vector3 NextPlaceScale()
{
if (_randomScale)
{
if (_uniformRandomScale) return Vector3.Lerp(_minScale, _maxScale, (float)scaleRandom.NextDouble());
return new Vector3(Mathf.Lerp(_minScale.x, _maxScale.x, (float)scaleRandom.NextDouble()), Mathf.Lerp(_minScale.y, _maxScale.y, (float)scaleRandom.NextDouble()), Mathf.Lerp(_minScale.z, _maxScale.z, (float)scaleRandom.NextDouble()));
}
return _minScale;
}
public MeshDefinition NextMesh()
{
if(_randomOrder) return meshes[iterationRandom.Next(meshes.Count)];
else
{
if (iterator >= meshes.Count) iterator = 0;
return meshes[iterator++];
}
}
internal void Rebuild()
{
if (owner != null) owner.Rebuild();
}
void Refresh()
{
for (int i = 0; i < meshes.Count; i++) meshes[i].Refresh();
Rebuild();
}
[System.Serializable]
public class MeshDefinition
{
public enum MirrorMethod { None, X, Y, Z }
[SerializeField]
[HideInInspector]
internal Vector3[] vertices = new Vector3[0];
[SerializeField]
[HideInInspector]
internal Vector3[] normals = new Vector3[0];
[SerializeField]
[HideInInspector]
internal Vector4[] tangents = new Vector4[0];
[SerializeField]
[HideInInspector]
internal Color[] colors = new Color[0];
[SerializeField]
[HideInInspector]
internal Vector2[] uv = new Vector2[0];
[SerializeField]
[HideInInspector]
internal Vector2[] uv2 = new Vector2[0];
[SerializeField]
[HideInInspector]
internal Vector2[] uv3 = new Vector2[0];
[SerializeField]
[HideInInspector]
internal Vector2[] uv4 = new Vector2[0];
[SerializeField]
[HideInInspector]
internal int[] triangles = new int[0];
[SerializeField]
[HideInInspector]
internal List<Submesh> subMeshes = new List<Submesh>();
[SerializeField]
[HideInInspector]
internal TS_Bounds bounds = new TS_Bounds(Vector3.zero, Vector3.zero);
[SerializeField]
[HideInInspector]
internal List<VertexGroup> vertexGroups = new List<VertexGroup>();
[SerializeField]
[HideInInspector]
private Mesh _mesh = null;
[SerializeField]
[HideInInspector]
private Vector3 _rotation = Vector3.zero;
[SerializeField]
[HideInInspector]
private Vector2 _offset = Vector2.zero;
[SerializeField]
[HideInInspector]
private Vector3 _scale = Vector3.one;
[SerializeField]
[HideInInspector]
private Vector2 _uvScale = Vector2.one;
[SerializeField]
[HideInInspector]
private Vector2 _uvOffset = Vector2.zero;
[SerializeField]
[HideInInspector]
private float _uvRotation = 0f;
[SerializeField]
[HideInInspector]
private MirrorMethod _mirror = MirrorMethod.None;
[SerializeField]
[HideInInspector]
private float _vertexGroupingMargin = 0f;
[SerializeField]
[HideInInspector]
private bool _removeInnerFaces = false;
[SerializeField]
[HideInInspector]
private bool _flipFaces = false;
[SerializeField]
[HideInInspector]
private bool _doubleSided = false;
public Mesh mesh
{
get
{
return _mesh;
}
set
{
if (_mesh != value)
{
_mesh = value;
Refresh();
}
}
}
public Vector3 rotation
{
get
{
return _rotation;
}
set
{
if (rotation != value)
{
_rotation = value;
Refresh();
}
}
}
public Vector2 offset
{
get
{
return _offset;
}
set
{
if (_offset != value)
{
_offset = value;
Refresh();
}
}
}
public Vector3 scale
{
get
{
return _scale;
}
set
{
if (_scale != value)
{
_scale = value;
Refresh();
}
}
}
public Vector2 uvScale
{
get
{
return _uvScale;
}
set
{
if (_uvScale != value)
{
_uvScale = value;
Refresh();
}
}
}
public Vector2 uvOffset
{
get
{
return _uvOffset;
}
set
{
if (_uvOffset != value)
{
_uvOffset = value;
Refresh();
}
}
}
public float uvRotation
{
get
{
return _uvRotation;
}
set
{
if (_uvRotation != value)
{
_uvRotation = value;
Refresh();
}
}
}
public float vertexGroupingMargin
{
get
{
return _vertexGroupingMargin;
}
set
{
if (_vertexGroupingMargin != value)
{
_vertexGroupingMargin = value;
Refresh();
}
}
}
public MirrorMethod mirror
{
get { return _mirror; }
set
{
if (_mirror != value)
{
_mirror = value;
Refresh();
}
}
}
public bool removeInnerFaces
{
get { return _removeInnerFaces; }
set
{
if (_removeInnerFaces != value)
{
_removeInnerFaces = value;
Refresh();
}
}
}
public bool flipFaces
{
get { return _flipFaces; }
set
{
if (_flipFaces != value)
{
_flipFaces = value;
Refresh();
}
}
}
public bool doubleSided
{
get { return _doubleSided; }
set
{
if (_doubleSided != value)
{
_doubleSided = value;
Refresh();
}
}
}
internal MeshDefinition Copy()
{
MeshDefinition target = new MeshDefinition(_mesh);
target.vertices = new Vector3[vertices.Length];
target.normals = new Vector3[normals.Length];
target.colors = new Color[colors.Length];
target.tangents = new Vector4[tangents.Length];
target.uv = new Vector2[uv.Length];
target.uv2 = new Vector2[uv2.Length];
target.uv3 = new Vector2[uv3.Length];
target.uv4 = new Vector2[uv4.Length];
target.triangles = new int[triangles.Length];
vertices.CopyTo(target.vertices, 0);
normals.CopyTo(target.normals, 0);
colors.CopyTo(target.colors, 0);
tangents.CopyTo(target.tangents, 0);
uv.CopyTo(target.uv, 0);
uv2.CopyTo(target.uv2, 0);
uv3.CopyTo(target.uv3, 0);
uv4.CopyTo(target.uv4, 0);
triangles.CopyTo(target.triangles, 0);
target.bounds = new TS_Bounds(bounds.min, bounds.max);
target.subMeshes = new List<Submesh>();
for (int i = 0; i < subMeshes.Count; i++)
{
target.subMeshes.Add(new Submesh(new int[subMeshes[i].triangles.Length]));
subMeshes[i].triangles.CopyTo(target.subMeshes[target.subMeshes.Count - 1].triangles, 0);
}
target._mirror = _mirror;
target._offset = _offset;
target._rotation = _rotation;
target._scale = _scale;
target._uvOffset = _uvOffset;
target._uvScale = _uvScale;
target._uvRotation = _uvRotation;
target._flipFaces = _flipFaces;
target._doubleSided = _doubleSided;
return target;
}
public MeshDefinition(Mesh input)
{
_mesh = input;
Refresh();
}
public void Refresh()
{
if(_mesh == null)
{
vertices = new Vector3[0];
normals = new Vector3[0];
colors = new Color[0];
uv = new Vector2[0];
uv2 = new Vector2[0];
uv3 = new Vector2[0];
uv4 = new Vector2[0];
tangents = new Vector4[0];
triangles = new int[0];
subMeshes = new List<Submesh>();
vertexGroups = new List<VertexGroup>();
return;
}
if (vertices.Length != _mesh.vertexCount) vertices = new Vector3[_mesh.vertexCount];
if (normals.Length != _mesh.normals.Length) normals = new Vector3[_mesh.normals.Length];
if (colors.Length != _mesh.colors.Length) colors = new Color[_mesh.colors.Length];
if (uv.Length != _mesh.uv.Length) uv = new Vector2[_mesh.uv.Length];
if (uv2.Length != _mesh.uv2.Length) uv2 = new Vector2[_mesh.uv2.Length];
if (uv3.Length != _mesh.uv3.Length) uv3 = new Vector2[_mesh.uv3.Length];
if (uv4.Length != _mesh.uv4.Length) uv4 = new Vector2[_mesh.uv4.Length];
if (tangents.Length != _mesh.tangents.Length) tangents = new Vector4[_mesh.tangents.Length];
if (triangles.Length != _mesh.triangles.Length) triangles = new int[_mesh.triangles.Length];
vertices = _mesh.vertices;
normals = _mesh.normals;
colors = _mesh.colors;
uv = _mesh.uv;
uv2 = _mesh.uv2;
uv3 = _mesh.uv3;
uv4 = _mesh.uv4;
tangents = _mesh.tangents;
triangles = _mesh.triangles;
colors = _mesh.colors;
while (subMeshes.Count > _mesh.subMeshCount) subMeshes.RemoveAt(0);
while (subMeshes.Count < _mesh.subMeshCount) subMeshes.Add(new Submesh(new int[0]));
for (int i = 0; i < subMeshes.Count; i++) subMeshes[i].triangles = _mesh.GetTriangles(i);
if (colors.Length != vertices.Length)
{
colors = new Color[vertices.Length];
for (int i = 0; i < colors.Length; i++) colors[i] = Color.white;
}
Mirror();
if (_doubleSided) DoubleSided();
else if (_flipFaces) FlipFaces();
TransformVertices();
CalculateBounds();
if (_removeInnerFaces) RemoveInnerFaces();
GroupVertices();
}
void RemoveInnerFaces()
{
float min = float.MaxValue, max = 0f;
for (int i = 0; i < vertices.Length; i++)
{
if (vertices[i].z < min) min = vertices[i].z;
if (vertices[i].z > max) max = vertices[i].z;
}
for (int i = 0; i < subMeshes.Count; i++)
{
List<int> newTris = new List<int>();
for (int j = 0; j < subMeshes[i].triangles.Length; j+= 3)
{
bool innerMax = true, innerMin = true;
for (int k = j; k < j+3; k++)
{
int index = subMeshes[i].triangles[k];
if (!Mathf.Approximately(vertices[index].z, max)) innerMax = false;
if (!Mathf.Approximately(vertices[index].z, min)) innerMin = false;
}
if (!innerMax && !innerMin)
{
newTris.Add(subMeshes[i].triangles[j]);
newTris.Add(subMeshes[i].triangles[j + 1]);
newTris.Add(subMeshes[i].triangles[j + 2]);
}
}
subMeshes[i].triangles = newTris.ToArray();
}
}
void FlipFaces()
{
TS_Mesh temp = new TS_Mesh();
temp.normals = normals;
temp.tangents = tangents;
temp.triangles = triangles;
for (int i = 0; i < subMeshes.Count; i++) temp.subMeshes.Add(subMeshes[i].triangles);
MeshUtility.FlipFaces(temp);
}
void DoubleSided()
{
TS_Mesh temp = new TS_Mesh();
temp.vertices = vertices;
temp.normals = normals;
temp.tangents = tangents;
temp.colors = colors;
temp.uv = uv;
temp.uv2 = uv2;
temp.uv3 = uv3;
temp.uv4 = uv4;
temp.triangles = triangles;
for (int i = 0; i < subMeshes.Count; i++) temp.subMeshes.Add(subMeshes[i].triangles);
MeshUtility.MakeDoublesided(temp);
vertices = temp.vertices;
normals = temp.normals;
tangents = temp.tangents;
colors = temp.colors;
uv = temp.uv;
uv2 = temp.uv2;
uv3 = temp.uv3;
uv4 = temp.uv4;
triangles = temp.triangles;
for (int i = 0; i < subMeshes.Count; i++) subMeshes[i].triangles = temp.subMeshes[i];
}
public void Write(TS_Mesh target, int forceMaterialId = -1)
{
if (target.vertices.Length != vertices.Length) target.vertices = new Vector3[vertices.Length];
if (target.normals.Length != normals.Length) target.normals = new Vector3[normals.Length];
if (target.colors.Length != colors.Length) target.colors = new Color[colors.Length];
if (target.uv.Length != uv.Length) target.uv = new Vector2[uv.Length];
if (target.uv2.Length != uv2.Length) target.uv2 = new Vector2[uv2.Length];
if (target.uv3.Length != uv3.Length) target.uv3 = new Vector2[uv3.Length];
if (target.uv4.Length != uv4.Length) target.uv4 = new Vector2[uv4.Length];
if (target.tangents.Length != tangents.Length) target.tangents = new Vector4[tangents.Length];
if (target.triangles.Length != triangles.Length) target.triangles = new int[triangles.Length];
vertices.CopyTo(target.vertices, 0);
normals.CopyTo(target.normals, 0);
colors.CopyTo(target.colors, 0);
uv.CopyTo(target.uv, 0);
uv2.CopyTo(target.uv2, 0);
uv3.CopyTo(target.uv3, 0);
uv4.CopyTo(target.uv4, 0);
tangents.CopyTo(target.tangents, 0);
triangles.CopyTo(target.triangles, 0);
if (target.subMeshes == null) target.subMeshes = new List<int[]>();
if (forceMaterialId >= 0)
{
while (target.subMeshes.Count > forceMaterialId+1) target.subMeshes.RemoveAt(0);
while (target.subMeshes.Count < forceMaterialId+1) target.subMeshes.Add(new int[0]);
for (int i = 0; i < target.subMeshes.Count; i++)
{
if(i != forceMaterialId)
{
if (target.subMeshes[i].Length > 0) target.subMeshes[i] = new int[0];
}
else
{
if (target.subMeshes[i].Length != triangles.Length) target.subMeshes[i] = new int[triangles.Length];
triangles.CopyTo(target.subMeshes[i], 0);
}
}
}
else
{
while (target.subMeshes.Count > subMeshes.Count) target.subMeshes.RemoveAt(0);
while (target.subMeshes.Count < subMeshes.Count) target.subMeshes.Add(new int[0]);
for (int i = 0; i < subMeshes.Count; i++)
{
if (subMeshes[i].triangles.Length != target.subMeshes[i].Length) target.subMeshes[i] = new int[subMeshes[i].triangles.Length];
subMeshes[i].triangles.CopyTo(target.subMeshes[i], 0);
}
}
}
void CalculateBounds()
{
Vector3 min = Vector3.zero;
Vector3 max = Vector3.zero;
for (int i = 0; i < vertices.Length; i++)
{
if (vertices[i].x < min.x) min.x = vertices[i].x;
else if (vertices[i].x > max.x) max.x = vertices[i].x;
if (vertices[i].y < min.y) min.y = vertices[i].y;
else if (vertices[i].y > max.y) max.y = vertices[i].y;
if (vertices[i].z < min.z) min.z = vertices[i].z;
else if (vertices[i].z > max.z) max.z = vertices[i].z;
}
bounds.CreateFromMinMax(min, max);
}
private void Mirror()
{
if (_mirror == MirrorMethod.None) return;
switch (_mirror)
{
case MirrorMethod.X:
for (int i = 0; i < vertices.Length; i++)
{
vertices[i].x *= -1f;
normals[i].x = -normals[i].x;
}
break;
case MirrorMethod.Y:
for (int i = 0; i < vertices.Length; i++)
{
vertices[i].y *= -1f;
normals[i].y = -normals[i].y;
}
break;
case MirrorMethod.Z:
for (int i = 0; i < vertices.Length; i++)
{
vertices[i].z *= -1f;
normals[i].z = -normals[i].z;
}
break;
}
for (int i = 0; i < triangles.Length; i+=3)
{
int temp = triangles[i];
triangles[i] = triangles[i + 2];
triangles[i + 2] = temp;
}
for (int i = 0; i < subMeshes.Count; i++)
{
for (int j = 0; j < subMeshes[i].triangles.Length; j += 3)
{
int temp = subMeshes[i].triangles[j];
subMeshes[i].triangles[j] = subMeshes[i].triangles[j + 2];
subMeshes[i].triangles[j + 2] = temp;
}
}
CalculateTangents();
}
void TransformVertices()
{
Matrix4x4 vertexMatrix = new Matrix4x4();
vertexMatrix.SetTRS(_offset, Quaternion.Euler(_rotation), _scale);
Matrix4x4 normalMatrix = vertexMatrix.inverse.transpose;
for (int i = 0; i < vertices.Length; i++)
{
vertices[i] = vertexMatrix.MultiplyPoint3x4(vertices[i]);
normals[i] = normalMatrix.MultiplyVector(normals[i]).normalized;
}
for (int i = 0; i < tangents.Length; i++) tangents[i] = normalMatrix.MultiplyVector(tangents[i]);
for (int i = 0; i < uv.Length; i++)
{
uv[i].x *= _uvScale.x;
uv[i].y *= _uvScale.y;
uv[i] += _uvOffset;
uv[i] = Quaternion.AngleAxis(uvRotation, Vector3.forward) * uv[i];
}
}
void GroupVertices()
{
vertexGroups = new List<VertexGroup>();
for (int i = 0; i < vertices.Length; i++)
{
float value = vertices[i].z;
double percent = DMath.Clamp01(DMath.InverseLerp(bounds.min.z, bounds.max.z, value));
int index = FindInsertIndex(vertices[i], value);
if (index >= vertexGroups.Count) vertexGroups.Add(new VertexGroup(value, percent, new int[] { i }));
else
{
float valueDelta = Mathf.Abs(vertexGroups[index].value - value);
if (valueDelta < vertexGroupingMargin || Mathf.Approximately(valueDelta, vertexGroupingMargin)) vertexGroups[index].AddId(i);
else if (vertexGroups[index].value < value) vertexGroups.Insert(index, new VertexGroup(value, percent, new int[] { i }));
else
{
if (index < vertexGroups.Count - 1) vertexGroups.Insert(index + 1, new VertexGroup(value, percent, new int[] { i }));
else vertexGroups.Add(new VertexGroup(value, percent, new int[] { i }));
}
}
}
}
int FindInsertIndex(Vector3 pos, float value)
{
int lower = 0;
int upper = vertexGroups.Count - 1;
while (lower <= upper)
{
int middle = lower + (upper - lower) / 2;
if (vertexGroups[middle].value == value) return middle;
else if (vertexGroups[middle].value < value) upper = middle - 1;
else lower = middle + 1;
}
return lower;
}
void CalculateTangents()
{
if (vertices.Length == 0)
{
tangents = new Vector4[0];
return;
}
tangents = new Vector4[vertices.Length];
Vector3[] tan1 = new Vector3[vertices.Length];
Vector3[] tan2 = new Vector3[vertices.Length];
for (int i = 0; i < subMeshes.Count; i++)
{
for (int j = 0; j < subMeshes[i].triangles.Length; j += 3)
{
int i1 = subMeshes[i].triangles[j];
int i2 = subMeshes[i].triangles[j + 1];
int i3 = subMeshes[i].triangles[j + 2];
float x1 = vertices[i2].x - vertices[i1].x;
float x2 = vertices[i3].x - vertices[i1].x;
float y1 = vertices[i2].y - vertices[i1].y;
float y2 = vertices[i3].y - vertices[i1].y;
float z1 = vertices[i2].z - vertices[i1].z;
float z2 = vertices[i3].z - vertices[i1].z;
float s1 = uv[i2].x - uv[i1].x;
float s2 = uv[i3].x - uv[i1].x;
float t1 = uv[i2].y - uv[i1].y;
float t2 = uv[i3].y - uv[i1].y;
float div = s1 * t2 - s2 * t1;
float r = div == 0f ? 0f : 1f / div;
Vector3 sdir = new Vector3((t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r, (t2 * z1 - t1 * z2) * r);
Vector3 tdir = new Vector3((s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r, (s1 * z2 - s2 * z1) * r);
tan1[i1] += sdir;
tan1[i2] += sdir;
tan1[i3] += sdir;
tan2[i1] += tdir;
tan2[i2] += tdir;
tan2[i3] += tdir;
}
}
for (int i = 0; i < vertices.Length; i++)
{
Vector3 n = normals[i];
Vector3 t = tan1[i];
Vector3.OrthoNormalize(ref n, ref t);
tangents[i].x = t.x;
tangents[i].y = t.y;
tangents[i].z = t.z;
tangents[i].w = (Vector3.Dot(Vector3.Cross(n, t), tan2[i]) < 0.0f) ? -1.0f : 1.0f;
}
}
[System.Serializable]
public class Submesh
{
public int[] triangles = new int[0];
public Submesh()
{
}
public Submesh(int[] input)
{
triangles = new int[input.Length];
input.CopyTo(triangles, 0);
}
}
[System.Serializable]
public class VertexGroup
{
public float value = 0f;
public double percent = 0.0;
public int[] ids;
public VertexGroup(float val, double perc, int[] vertIds)
{
percent = perc;
value = val;
ids = vertIds;
}
public void AddId(int id)
{
int[] newIds = new int[ids.Length + 1];
ids.CopyTo(newIds, 0);
newIds[newIds.Length - 1] = id;
ids = newIds;
}
}
}
}
}
}