using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace FIMSpace.Generating
{
public static class TileCableGenerator
{
#region Mesh Variables
[System.Serializable]
public class CableMeshSettings
{
[Range(1, 36)]
public int LengthSubdivs = 12;
[Range(3, 20)]
public int CircleSubdivs = 6;
[Range(0, 360)]
public float RollOffset = 0;
[Space(3)]
[Tooltip("Joininig end vertices of trail points when you use more than two cable points")]
public bool JoinEnds = false;
//public bool SmoothNormals = false;
}
#endregion
#region Texturing Variables
[System.Serializable]
public class CableTexturingSettings
{
public float LengthTiling = 4f;
public float VerticalTiling = 1f;
[Range(0, 360)] public float UVRotate = 0;
}
#endregion
#region Cloner Variables
[System.Serializable]
public class CableClonerSettings
{
[Tooltip("Cable clones count in X Y and Z axis")]
public Vector3Int InstancesCount = new Vector3Int(1, 1, 1);
[Tooltip("Distance between each clone")]
public Vector3 ClonesOffsets = Vector3.one;
[Tooltip("Quick adjust clones offset value")]
[Range(0f, 2f)] public float ScaleOffsets = 1f;
[Space(2)]
[Tooltip("Generating XY clones in circular shape instead of rect shape")]
public bool CircularGrid = false;
[Space(5)]
[Tooltip("If you're creating cable curve using multiple path points it can help getting desired shape of the cloned cables")]
public bool PathReGenerate = false;
[Tooltip("If using multiple path points, it will use dominant axis for clone direction at path start and at end")]
public bool FlattenEnds = false;
}
#endregion
#region Randomization Variables
[System.Serializable]
public class CableRandomizationSettings
{
[Tooltip("When X = 0.2 it will offset trail to the right/left with random position offset from -0.2 o 0.2")]
public Vector2 RandomizeTrails = Vector2.zero;
[Tooltip("How frequent should be random disturbances on the cable shape")]
public float NoiseScale = 0.5f;
[Tooltip("When X = 0.8 and Y = 1.2 it will multiply 'Loose' parameter with randomized values from 0.8 o 1.2")]
public Vector2 RandomizeLoose = Vector2.one;
[Space(4)]
[Tooltip("Randomizing position of target path points")]
public Vector3 RandomizePathPoints = Vector3.zero;
[Tooltip("Cut out some random clones")]
public Vector2Int CutOutClones = Vector2Int.zero;
}
#endregion
#region Attachement Variables
[System.Serializable]
public class CableAttachementSettings
{
public Mesh Mesh;
public Material Material;
[Space(3)]
public Vector3 Offset = Vector3.zero;
public Vector3 Rotation = Vector3.zero;
public bool FlatRotation = true;
public Vector3 Scale = Vector3.one;
public float ScaleMultiplier = 1f;
[Space(3)]
public bool AddOnStart = true;
public bool AddInTheMiddle = true;
public bool AddOnTheEnd = true;
[Space(3)]
public bool ApplyToAllClones = false;
}
#endregion
static Mesh cablesMesh = null;
static Vector3[] _vertices = null;
static int[] _tris = null;
static int[] _trisRev = null;
static int[] _trisCircleHelperCurrent = null;
static int[] _trisCircleHelperPre = null;
#region Sweep Elements Preparation Methods
/// Looped X-Y cable ring shape with +-1.0 scale offset
static List cableCircle = new List();
public static void GenerateCableCircle(List cableRing, int ringSubdivs)
{
if (ringSubdivs < 2) ringSubdivs = 2;
if (cableRing.Count == ringSubdivs + 1) return; // Already generated
cableRing.Clear();
float stepAngle = 360f / (float)(ringSubdivs);
for (int i = 0; i < ringSubdivs; i++)
{
float step = (i * stepAngle) * Mathf.Deg2Rad;
cableRing.Add(new Vector3(Mathf.Sin(step), Mathf.Cos(step), 0f));
}
// Loop ring
cableRing.Add(cableRing[0]);
}
/// Cable trail path using just positions
static List cableTrail = new List();
public static void GenerateCableTrailPoints(List trail, Vector3 a, Vector3 b, Vector3 stretchPoint, int lengthSubdivs, out float trailLength, float hanging = 0f)
{
bool addPoints = false;
float tlength = 0f;
Vector3 prePos = a;
if (trail.Count != lengthSubdivs + 1) { addPoints = true; trail.Clear(); }
if (hanging <= 0f)
{
for (int i = 0; i < lengthSubdivs + 1; i++) // +1 For end subdiv
{
float t = i / ((float)lengthSubdivs);
Vector3 targetPos = GetBezierQuad(a, b, stretchPoint, t);
tlength += Vector3.Distance(prePos, targetPos);
prePos = targetPos;
if (addPoints) trail.Add(targetPos); else trail[i] = targetPos;
}
}
else
{
Vector3 towardsB = b - a;
Vector3 towardsBFlat = Vector3.ProjectOnPlane(towardsB, Vector3.up);
float length = towardsB.magnitude;
int totalLenSubdivs = lengthSubdivs + 1; // +1 For end subdiv
int hangPartLength = (lengthSubdivs) / 4;
float rHang = 1f - hanging;
Vector3 aEnd = a + towardsB * (0.1f + 0.05f * hanging);
aEnd.y = Mathf.LerpUnclamped(aEnd.y, stretchPoint.y, 0.1f + rHang * 0.05f);
Vector3 aStretchTo = a + towardsBFlat * (0.025f + 0.1f * hanging);
aStretchTo.y += (0.015f * hanging) * 0.05f * (a.y - stretchPoint.y);
Vector3 bContinueStart = b - towardsB * (0.1f + 0.05f * hanging);
bContinueStart.y = Mathf.LerpUnclamped(bContinueStart.y, stretchPoint.y, 0.1f + rHang * 0.05f);
Vector3 bStretchTo = b - towardsBFlat * (0.025f + 0.1f * hanging);
bStretchTo.y += (0.015f * hanging) * 0.05f * (b.y - stretchPoint.y);
for (int i = 0; i < hangPartLength; i++) // Left cable part
{
float t = (float)i * (1f / ((float)hangPartLength));
Vector3 targetPos = GetBezierQuad(a, aEnd, aStretchTo, t);
if (addPoints) trail.Add(targetPos); else trail[i] = targetPos;
tlength += Vector3.Distance(prePos, targetPos);
prePos = targetPos;
}
for (int i = hangPartLength; i < lengthSubdivs - hangPartLength + 1; i++) // Middle cable part
{
float t = (i - hangPartLength) / ((float)totalLenSubdivs - hangPartLength - hangPartLength - 1);
Vector3 targetPos = GetBezierQuad(aEnd, bContinueStart, stretchPoint, t);
if (addPoints) trail.Add(targetPos); else trail[i] = targetPos;
tlength += Vector3.Distance(prePos, targetPos);
prePos = targetPos;
}
for (int i = totalLenSubdivs - hangPartLength; i < totalLenSubdivs; i++) // Right - end cable part
{
float t = (i - (totalLenSubdivs - hangPartLength - 1)) / ((float)hangPartLength);
Vector3 targetPos = GetBezierQuad(bContinueStart, b, bStretchTo, t);
if (addPoints) trail.Add(targetPos); else trail[i] = targetPos;
tlength += Vector3.Distance(prePos, targetPos);
prePos = targetPos;
}
}
//if(trail.Count > 1) tlength += Vector3.Distance(trail[trail.Count - 2], trail[trail.Count - 1]);
trailLength = tlength;
}
public static Vector3 GetBezierQuad(Vector3 a, Vector3 b, Vector3 mid, float t)
{
float revT = 1 - t;
return (revT * revT) * a + 2 * revT * t * mid + (t * t) * b;
}
#endregion
static List pointsBackup = null;
static List> attachementSources = null;
static List toCombineSingle = new List();
static List toCombine = new List();
static bool resetLengthReference = true;
/// For equal uv tiling
static float lengthReferenceScale = 1f;
static void RandomizaPoints(List points, List backupPoints, CableRandomizationSettings settings)
{
if (settings.RandomizePathPoints != Vector3.zero)
{
if (points.Count < 2) return;
points.Clear();
//Quaternion dir = Quaternion.identity;
for (int b = 0; b < backupPoints.Count; b++)
{
Vector3 p = backupPoints[b];
//if (b < backupPoints.Count - 1) dir = Quaternion.LookRotation(backupPoints[b + 1] - p);
points.Add(p + FGenerators.GetRandom(settings.RandomizePathPoints));
//points.Add(p + dir * FGenerators.GetRandom(settings.RandomizePathPoints));
}
}
}
/// Generating cable using multiple points, with randomization, cloning and generating attachements
public static Mesh GenerateFullCablesMesh(List points, float cableLoose, float hanging, float radius, TileCableGenerator.CableMeshSettings meshSettings, TileCableGenerator.CableTexturingSettings texturingSettings, TileCableGenerator.CableClonerSettings clonerSettings, TileCableGenerator.CableRandomizationSettings randomSettings, TileCableGenerator.CableAttachementSettings attachementSettings)
{
#region Prepare trail randomization
if (pointsBackup != null) pointsBackup.Clear();
if (randomSettings.RandomizePathPoints != Vector3.zero)
{
if (pointsBackup == null) pointsBackup = new List();
for (int p = 0; p < points.Count; p++) pointsBackup.Add(points[p]);
RandomizaPoints(points, pointsBackup, randomSettings);
}
#endregion
Mesh fullCableTrail = GenerateCablesMesh(points, cableLoose, hanging, radius, meshSettings, texturingSettings, randomSettings);
Mesh fullMesh = fullCableTrail;
bool usingRandomization = false;
toCombine.Clear();
#region Prepare Attachement Support
bool useAttachements = false;
if (attachementSettings != null)
if (attachementSettings.Mesh != null)
{
useAttachements = true;
if (attachementSources == null) attachementSources = new List>();
else attachementSources.Clear();
}
#endregion
List clonesToSkip = null;
List targetPoints = points; // reference for ReGenerate feature is used
if (clonerSettings != null)
{
if (randomSettings != null)
{
if (randomSettings.RandomizeLoose != Vector2.one) usingRandomization = true;
if (!usingRandomization) if (randomSettings.RandomizeTrails != Vector2.zero) usingRandomization = true;
// Supporting clone cut-out
if (randomSettings.CutOutClones != Vector2.zero)
{
List clonesIdx = new List();
#region Sum all possible clones count
for (int x = 0; x < clonerSettings.InstancesCount.x; x++)
for (int y = 0; y < clonerSettings.InstancesCount.y; y++)
for (int z = 0; z < clonerSettings.InstancesCount.z; z++)
{
if (clonerSettings.CircularGrid)
{
Vector3 circleRef = new Vector3(0f, 0f, 0f);
circleRef.x = x - clonerSettings.InstancesCount.x / 2;
circleRef.y = y - clonerSettings.InstancesCount.y / 2;
if (x > y)
{ if (circleRef.magnitude > clonerSettings.InstancesCount.x / 2) continue; }
else
{ if (circleRef.magnitude > clonerSettings.InstancesCount.y / 2) continue; }
}
clonesIdx.Add(new Vector3Int(x, y, z));
}
#endregion
//UnityEngine.Debug.Log("allclones " + allClones);
if (clonesIdx.Count > 0)
{
if (randomSettings.CutOutClones.x < 0) randomSettings.CutOutClones.x = 0;
if (randomSettings.CutOutClones.y < 0) randomSettings.CutOutClones.y = 0;
int toCut = FGenerators.GetRandom(randomSettings.CutOutClones.x, randomSettings.CutOutClones.y + 1);
if (toCut > 0)
{
if (toCut >= clonesIdx.Count) return new Mesh();
clonesToSkip = new List();
for (int c = 0; c < toCut; c++)
{
if (clonesIdx.Count == 0) break;
int i = FGenerators.GetRandom(0, clonesIdx.Count);
clonesToSkip.Add(clonesIdx[i]);
clonesIdx.RemoveAt(i);
}
}
}
}
}
for (int x = 0; x < clonerSettings.InstancesCount.x; x++)
{
for (int y = 0; y < clonerSettings.InstancesCount.y; y++)
{
List attachements = null;
if (useAttachements) attachements = new List();
for (int z = 0; z < clonerSettings.InstancesCount.z; z++)
{
if (clonerSettings.CircularGrid)
{
Vector3 circleRef = new Vector3(0f, 0f, 0f);
circleRef.x = x - clonerSettings.InstancesCount.x / 2;
circleRef.y = y - clonerSettings.InstancesCount.y / 2;
if (x > y)
{
if (circleRef.magnitude > clonerSettings.InstancesCount.x / 2) continue;
}
else
{
if (circleRef.magnitude > clonerSettings.InstancesCount.y / 2) continue;
}
}
if (clonesToSkip != null)
{
if (clonesToSkip.Contains(new Vector3Int(x, y, z))) continue;
}
Vector3 translationValue = new Vector3();
translationValue.x = x;
translationValue.y = y;
translationValue.z = z;
Vector3 centerOffset = new Vector3();
centerOffset.x = -(clonerSettings.InstancesCount.x - 1) / 2;
if (clonerSettings.InstancesCount.x % 2 == 0) centerOffset.x -= 0.5f;
centerOffset.y = -(clonerSettings.InstancesCount.y - 1) / 2;
if (clonerSettings.InstancesCount.y % 2 == 0) centerOffset.y -= 0.5f;
centerOffset.z = -(clonerSettings.InstancesCount.z - 1) / 2;
if (clonerSettings.InstancesCount.z % 2 == 0) centerOffset.z -= 0.5f;
translationValue += centerOffset;
translationValue.x *= clonerSettings.ClonesOffsets.x * clonerSettings.ScaleOffsets;
translationValue.y *= clonerSettings.ClonesOffsets.y * clonerSettings.ScaleOffsets;
translationValue.z *= clonerSettings.ClonesOffsets.z;
CombineInstance comb = new CombineInstance();
if (clonerSettings.PathReGenerate)
{
targetPoints = new List();
Vector3 lDir = Vector3.zero;
for (int p = 0; p < points.Count; p++)
{
Vector3 clonerPathPoint = points[p];
Quaternion dir;
if (p == points.Count - 1 && clonerSettings.FlattenEnds)
{
lDir = FVectorMethods.ChooseDominantAxis(points[p] - points[p - 1]);
}
else if (p == 0 && clonerSettings.FlattenEnds)
{
lDir = FVectorMethods.ChooseDominantAxis(points[p + 1] - points[p]);
}
else if (p < points.Count - 1) lDir = points[p + 1] - points[p];
if (lDir == Vector3.zero) dir = Quaternion.identity;
else dir = Quaternion.LookRotation(lDir);
clonerPathPoint += dir * translationValue;
targetPoints.Add(clonerPathPoint);
}
}
if (usingRandomization || clonerSettings.PathReGenerate)
{
RandomizaPoints(targetPoints, pointsBackup, randomSettings);
comb.mesh = GenerateCablesMesh(targetPoints, cableLoose, hanging, radius, meshSettings, texturingSettings, randomSettings);
}
else comb.mesh = fullCableTrail;
if (useAttachements)
{
if (attachementSettings.ApplyToAllClones)
{
for (int p = 0; p < targetPoints.Count; p++)
{
Vector3 pointPos = targetPoints[p] + translationValue;
if (!attachements.Contains(pointPos)) attachements.Add(pointPos);
}
}
else
{
if (attachementSources.Count == 0)
for (int p = 0; p < targetPoints.Count; p++)
{
Vector3 off = new Vector3(0f, 0f, translationValue.z);
Vector3 pointPos = targetPoints[p] + off;
if (!attachements.Contains(pointPos)) attachements.Add(pointPos);
}
}
}
if (clonerSettings.PathReGenerate) translationValue = Vector3.zero;
comb.transform = Matrix4x4.Translate(translationValue);
toCombine.Add(comb);
targetPoints = points;
}
if (attachements != null) if (attachements.Count > 0) attachementSources.Add(attachements);
}
}
if (toCombine.Count > 0)
{
Mesh clonerCombination = new Mesh();
clonerCombination.name = "CablesClonerCombination";
clonerCombination.CombineMeshes(toCombine.ToArray());
fullMesh = clonerCombination;
}
}
#region Generate attachements and restore
if (attachementSources != null)
if (toCombine.Count == 0)
{
List attachements = new List();
for (int p = 0; p < points.Count; p++) attachements.Add(points[p]);
attachementSources.Add(attachements);
}
if (useAttachements)
if (attachementSources.Count > 0)
{
Mesh attachementsMesh;
if (attachementSettings.ApplyToAllClones)
{
toCombineSingle.Clear();
for (int a = 0; a < attachementSources.Count; a++)
{
Mesh m = GenerateAttachements(attachementSources[a], attachementSettings);
CombineInstance comb = new CombineInstance();
comb.mesh = m;
comb.transform = Matrix4x4.identity;
toCombineSingle.Add(comb);
}
attachementsMesh = new Mesh();
attachementsMesh.CombineMeshes(toCombineSingle.ToArray());
}
else
{
attachementsMesh = GenerateAttachements(attachementSources[0], attachementSettings);
}
if (attachementsMesh != null)
{
toCombineSingle.Clear();
CombineInstance comb = new CombineInstance();
comb.mesh = fullMesh;
comb.subMeshIndex = 0;
comb.transform = Matrix4x4.identity;
toCombineSingle.Add(comb);
comb = new CombineInstance();
comb.mesh = attachementsMesh;
comb.subMeshIndex = 0;
comb.transform = Matrix4x4.identity;
toCombineSingle.Add(comb);
Mesh subMeshed = new Mesh();
subMeshed.name = "CablesMesh";
subMeshed.CombineMeshes(toCombineSingle.ToArray(), false);
fullMesh = subMeshed;
}
}
if (pointsBackup != null)
if (pointsBackup.Count > 0)
{
points.Clear();
for (int p = 0; p < pointsBackup.Count; p++) points.Add(pointsBackup[p]);
}
#endregion
return fullMesh;
}
static Quaternion GetAxisRotation(Vector3 angles)
{
Quaternion rot = Quaternion.identity;
rot *= Quaternion.AngleAxis(angles.x, Vector3.right);
rot *= Quaternion.AngleAxis(angles.y, Vector3.up);
rot *= Quaternion.AngleAxis(angles.z, Vector3.forward);
return rot;
}
static Mesh GenerateAttachements(List attachementsTrail, TileCableGenerator.CableAttachementSettings settings)
{
Vector3 towards = Vector3.forward;
if (attachementsTrail.Count > 1) towards = attachementsTrail[1] - attachementsTrail[0];
toCombine.Clear();
Quaternion rotOffset = GetAxisRotation(settings.Rotation);
for (int t = 0; t < attachementsTrail.Count; t++)
{
if (t == 0) if (settings.AddOnStart == false) continue;
if (t == attachementsTrail.Count - 1) if (settings.AddOnTheEnd == false) continue;
if (t > 0 && t < attachementsTrail.Count - 1) if (settings.AddInTheMiddle == false) continue;
Vector3 pos = attachementsTrail[t];
if (t < attachementsTrail.Count - 1) towards = attachementsTrail[t + 1] - attachementsTrail[t];
Quaternion rot;
if (settings.FlatRotation) rot = Quaternion.LookRotation(Vector3.ProjectOnPlane(towards, Vector3.up));
else rot = Quaternion.LookRotation(towards);
pos += rot * settings.Offset;
CombineInstance comb = new CombineInstance();
comb.mesh = settings.Mesh;
comb.transform = Matrix4x4.TRS(pos, rot * rotOffset, settings.Scale * settings.ScaleMultiplier);
toCombine.Add(comb);
}
Mesh attachements = new Mesh();
attachements.name = "Attachments";
attachements.CombineMeshes(toCombine.ToArray());
return attachements;
}
/// Generating cable trail out of multiple points, without cloning but with use of randomization
public static Mesh GenerateCablesMesh(List points, float Loose, float Hanging, float Radius, TileCableGenerator.CableMeshSettings MeshSettings, TileCableGenerator.CableTexturingSettings TexturingSettings, TileCableGenerator.CableRandomizationSettings randomSettings)
{
Mesh fullCableTrail = new Mesh();
toCombineSingle.Clear();
resetLengthReference = true;
for (int p = 0; p < points.Count - 1; p++)
{
CombineInstance comb = new CombineInstance();
comb.mesh = GetSingleCableMesh(points[p], points[p + 1], Loose, Hanging, Radius, MeshSettings, TexturingSettings, randomSettings);
comb.transform = Matrix4x4.identity;
toCombineSingle.Add(comb);
if (MeshSettings.JoinEnds) if (p > 0) JoinCableSegmentsEnds(toCombineSingle[p - 1].mesh, toCombineSingle[p].mesh, MeshSettings.CircleSubdivs + 1);
}
fullCableTrail.CombineMeshes(toCombineSingle.ToArray());
return fullCableTrail;
}
public static void JoinCableSegmentsEnds(Mesh start, Mesh end, int circlePoints)
{
Vector3[] verts = start.vertices;
Vector3[] vertse = end.vertices;
for (int c = 1; c <= circlePoints; c++)
{
Vector3 nVal = Vector3.LerpUnclamped(verts[start.vertexCount - c], vertse[circlePoints - c], 0.5f);
verts[start.vertexCount - c] = nVal;
vertse[circlePoints - c] = nVal;
//verts[start.vertexCount - c] = end.vertices[cableCircle.Count - c];
}
start.vertices = verts;
end.vertices = vertse;
}
/// Single cable trail from point a to point b with applied randomization
public static Mesh GetSingleCableMesh(Vector3 a, Vector3 b, float Loose, float Hanging, float Radius, TileCableGenerator.CableMeshSettings MeshSettings, TileCableGenerator.CableTexturingSettings TexturingSettings, TileCableGenerator.CableRandomizationSettings randomSettings = null)
{
Mesh mesh = new Mesh();
mesh.name = "CABLE";
Vector3 diff = b - a;
float uvAngleByDiff = 0f;
//if (diff != Vector3.zero) uvAngleByDiff = Quaternion.LookRotation(-diff).eulerAngles.x;
if (randomSettings != null) if (randomSettings.RandomizeLoose != Vector2.one)
{
Loose *= FGenerators.GetRandomSwap(randomSettings.RandomizeLoose.x, randomSettings.RandomizeLoose.y);
}
float looseFactor = .1f;
if (Loose > 0) looseFactor = Loose * diff.sqrMagnitude;
Vector3 stretchTowardsPos = (a + b) / 2f;
float verticalFactor = 1f;
verticalFactor = 1f - (Mathf.Abs(Vector3.Dot(diff.normalized, Vector3.up)));
stretchTowardsPos += new Vector3(0, -looseFactor * verticalFactor);
GenerateCableCircle(cableCircle, MeshSettings.CircleSubdivs);
float calculatedLength;
GenerateCableTrailPoints(cableTrail, a, b, stretchTowardsPos, MeshSettings.LengthSubdivs, out calculatedLength, Hanging);
if (resetLengthReference)
{
lengthReferenceScale = calculatedLength;
resetLengthReference = false;
}
else
{
calculatedLength = lengthReferenceScale;
}
int targetVertsCount = (MeshSettings.LengthSubdivs + 1) * (MeshSettings.CircleSubdivs + 1);
if (_vertices == null || _vertices.Length != targetVertsCount) _vertices = new Vector3[targetVertsCount];
int totalTrisCount = (cableCircle.Count * 6) * (MeshSettings.LengthSubdivs + 1);
if (_tris == null || _tris.Length != totalTrisCount) _tris = new int[totalTrisCount];
if (_trisRev == null || _tris.Length != _trisRev.Length) _trisRev = new int[totalTrisCount];
int circlePointsCount = cableCircle.Count;
if (_trisCircleHelperCurrent == null || _trisCircleHelperCurrent.Length != circlePointsCount)
_trisCircleHelperCurrent = new int[circlePointsCount];
if (_trisCircleHelperPre == null || _trisCircleHelperPre.Length != circlePointsCount)
_trisCircleHelperPre = new int[circlePointsCount];
List uvs = new List();
Vector2 uvStep = new Vector2(1f / (float)(cableTrail.Count - 1), 1f / (float)(circlePointsCount - 1));
float uvElapsed = 0f;
Quaternion circleRotation = Quaternion.identity;
Vector3 prePos = cableTrail[0];
Vector3 trailDiff;
Vector3 trailDirection;
#region Randomize trails code
// Randomize trail shape on middle subdivisions and keep start/end intact
if (randomSettings != null) if (randomSettings.RandomizeTrails != Vector2.zero)
{
float halfLen = (cableTrail.Count / 2f);
if (halfLen <= 0f) halfLen = 1f;
Vector3 rOffsets = FGenerators.GetRandom(new Vector3(100f, 100f, 100f));
for (int i = 1; i < cableTrail.Count - 1; i++)
{
trailDiff = cableTrail[i + 1] - cableTrail[i];
trailDirection = trailDiff.normalized;
if (trailDirection != Vector3.zero)
{
circleRotation = Quaternion.LookRotation(trailDirection, Vector3.up);
if (MeshSettings.RollOffset > 0f) circleRotation = Quaternion.AngleAxis(MeshSettings.RollOffset, trailDirection) * circleRotation;
}
float stepMul;
float iM = i * randomSettings.NoiseScale * (lengthReferenceScale * 0.1f);
if (i < halfLen) stepMul = Mathf.Lerp(0.0f, 1f, i / (halfLen));
else stepMul = Mathf.Lerp(1f, 0.0f, (i - halfLen) / (halfLen));
stepMul *= 0.1f;
Vector3 nTrail = cableTrail[i];
Vector3 offset = new Vector3();
offset.x = (-0.5f + Mathf.PerlinNoise(rOffsets.x + iM, rOffsets.y + iM)) * stepMul * randomSettings.RandomizeTrails.x;
offset.y = (-0.5f + Mathf.PerlinNoise(rOffsets.y + iM - 80f, rOffsets.z + iM + 80f)) * stepMul * randomSettings.RandomizeTrails.y;
cableTrail[i] = nTrail + circleRotation * offset;
}
}
#endregion
circleRotation = Quaternion.identity;
for (int t = 0; t < cableTrail.Count; t++) // Step through cable trail point
{
//UnityEngine.Debug.Log("pos["+t+" = " + cableTrail[t]);
//if( t > 0) UnityEngine.Debug.DrawLine(cableTrail[t], cableTrail[t-1], Color.green, 1.01f);
// Define circle look rotation towards next trail point
if (t < cableTrail.Count - 1)
{
trailDiff = cableTrail[t + 1] - cableTrail[t];
trailDirection = trailDiff.normalized;
if (trailDirection != Vector3.zero)
{
circleRotation = Quaternion.LookRotation(trailDirection, Vector3.up);
if (MeshSettings.RollOffset > 0f) circleRotation = Quaternion.AngleAxis(MeshSettings.RollOffset, trailDirection) * circleRotation;
}
}
uvElapsed += Vector3.Distance(cableTrail[t], prePos);
prePos = cableTrail[t];
// Populate circle vertices
for (int c = 0; c < circlePointsCount; c++)
{
// Calculate vertice position
Vector3 cPos = cableTrail[t];
cPos += circleRotation * (cableCircle[c] * Radius);
_vertices[t * circlePointsCount + c] = cPos;
// Set up UV
Vector2 uv = new Vector2();
uv.x = uvElapsed / calculatedLength * TexturingSettings.LengthTiling;
if (c > (circlePointsCount) / 2)
uv.y = ((float)(c) * uvStep.y) * TexturingSettings.VerticalTiling;
else
uv.y = ((float)(-c + circlePointsCount + 1) * uvStep.y) * TexturingSettings.VerticalTiling;
uvs.Add(uv);
// Prepare tris helpers
_trisCircleHelperPre[c] = _trisCircleHelperCurrent[c];
_trisCircleHelperCurrent[c] = t * circlePointsCount + c;
}
// Store mesh tris -> Two 3-point triangles
for (int c = 0; c < circlePointsCount; c++)
{
if (t == 0 || c >= cableCircle.Count - 1) continue;
int start = (t * circlePointsCount + c) * 6;
_tris[start] = _trisCircleHelperPre[c];
_tris[start + 1] = _trisCircleHelperPre[(c + 1) % circlePointsCount];
_tris[start + 2] = _trisCircleHelperCurrent[c];
_tris[start + 3] = _tris[start + 2];
_tris[start + 4] = _tris[start + 1];
_tris[start + 5] = _trisCircleHelperCurrent[(c + 1) % circlePointsCount];
}
}
mesh.vertices = _vertices;
for (int t = 0; t < _tris.Length; t++)
{
_trisRev[t] = _tris[_tris.Length - 1 - t];
}
mesh.triangles = _trisRev;
mesh.RecalculateNormals();
mesh.uv = uvs.ToArray();
float uvAngle = uvAngleByDiff + TexturingSettings.UVRotate;
if (uvAngle > 0f) FMeshUtils.RotateUV(mesh, uvAngle);
mesh.RecalculateTangents();
mesh.RecalculateBounds();
cablesMesh = mesh;
return cablesMesh;
}
}
}