BITFALL/Assets/Plugins/FImpossible Creations/Plugins - Level Design/Tile Designer/Extra Generators/TileDesigner.CableGenerator.cs

869 lines
36 KiB
C#

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
/// <summary> Looped X-Y cable ring shape with +-1.0 scale offset </summary>
static List<Vector3> cableCircle = new List<Vector3>();
public static void GenerateCableCircle(List<Vector3> 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]);
}
/// <summary> Cable trail path using just positions </summary>
static List<Vector3> cableTrail = new List<Vector3>();
public static void GenerateCableTrailPoints(List<Vector3> 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<Vector3> pointsBackup = null;
static List<List<Vector3>> attachementSources = null;
static List<CombineInstance> toCombineSingle = new List<CombineInstance>();
static List<CombineInstance> toCombine = new List<CombineInstance>();
static bool resetLengthReference = true;
/// <summary> For equal uv tiling </summary>
static float lengthReferenceScale = 1f;
static void RandomizaPoints(List<Vector3> points, List<Vector3> 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));
}
}
}
/// <summary> Generating cable using multiple points, with randomization, cloning and generating attachements </summary>
public static Mesh GenerateFullCablesMesh(List<Vector3> 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<Vector3>();
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<List<Vector3>>();
else attachementSources.Clear();
}
#endregion
List<Vector3Int> clonesToSkip = null;
List<Vector3> 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<Vector3Int> clonesIdx = new List<Vector3Int>();
#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<Vector3Int>();
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<Vector3> attachements = null;
if (useAttachements) attachements = new List<Vector3>();
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>();
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<Vector3> attachements = new List<Vector3>();
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<Vector3> 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;
}
/// <summary> Generating cable trail out of multiple points, without cloning but with use of randomization </summary>
public static Mesh GenerateCablesMesh(List<Vector3> 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;
}
/// <summary> Single cable trail from point a to point b with applied randomization </summary>
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<Vector2> uvs = new List<Vector2>();
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;
}
}
}