sdsandbox-rl-scripts/Scripts/RepeatObjectAlong.cs

226 lines
8.8 KiB
C#
Executable File

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
[RequireComponent(typeof(MeshFilter))]
[RequireComponent(typeof(MeshRenderer))]
[RequireComponent(typeof(MeshCollider))]
public class RepeatObjectAlong : MonoBehaviour, IWaitCarPath
{
public PathManager pathManager; // we will use the carPath from the pathManager
public Mesh[] meshesToRepeat;
[Header("Offsets")]
public float leftWidthOffset = 1f;
public float rightWidthOffset = 1f;
public float[] yOffset;
public float[] xOffset;
public float[] rotateYOffset;
public float[] rotateXOffset;
[Header("Repeating params")]
public Vector3 MeshScale = new Vector3(1, 1, 1);
public float placeEvery = 5f;
[Header("Generation params")]
public bool generateAtRuntime = true;
public bool leftside = true;
public bool rightside = true;
public bool mirrorRotateObject = true; // apply a mirror rotation to the right side
public string savePath = "Assets\\object_mesh.asset";
private List<CombineInstance> combineInstances;
public void Generate()
{
if (pathManager != null && pathManager.carPath != null)
{
if (xOffset.Length != meshesToRepeat.Length)
{
xOffset = new float[meshesToRepeat.Length];
}
if (yOffset.Length != meshesToRepeat.Length)
{
yOffset = new float[meshesToRepeat.Length];
}
if (rotateYOffset.Length != meshesToRepeat.Length)
{
rotateYOffset = new float[meshesToRepeat.Length];
}
if (rotateXOffset.Length != meshesToRepeat.Length)
{
rotateXOffset = new float[meshesToRepeat.Length];
}
combineInstances = new List<CombineInstance>();
Vector3 leftScaling = MeshScale;
Vector3 rightScaling = MeshScale;
if (mirrorRotateObject)
{
rightScaling.x = -rightScaling.x;
}
List<List<Vector3>> leftPositions = new List<List<Vector3>>();
List<List<Vector3>> rightPositions = new List<List<Vector3>>();
for (int iM = 0; iM < meshesToRepeat.Length; iM++)
{
leftPositions.Add(new List<Vector3>());
rightPositions.Add(new List<Vector3>());
PathNode node = pathManager.carPath.centerNodes[0];
PathNode prevNode = pathManager.carPath.centerNodes[pathManager.carPath.centerNodes.Count - 1];
Vector3 leftPrevPosition = prevNode.pos + node.rotation * (Vector3.left * leftWidthOffset) + node.rotation * (Vector3.forward * xOffset[iM]) + yOffset[iM] * Vector3.up;
Vector3 rightPrevPosition = prevNode.pos + node.rotation * (Vector3.right * rightWidthOffset) + node.rotation * (Vector3.forward * xOffset[iM]) + yOffset[iM] * Vector3.up;
for (int i = 0; i < pathManager.carPath.centerNodes.Count + 2; i++)
{
node = pathManager.carPath.centerNodes[i % pathManager.carPath.centerNodes.Count];
if (leftside)
{
Vector3 tmp_point = node.pos + node.rotation * (Vector3.left * leftWidthOffset) + node.rotation * (Vector3.forward * xOffset[iM]) + yOffset[iM] * Vector3.up;
float prev_distance = Vector3.Distance(leftPrevPosition, tmp_point);
if (prev_distance >= placeEvery)
{
float t_factor = placeEvery / prev_distance;
Vector3 position = Vector3.Lerp(leftPrevPosition, tmp_point, t_factor);
leftPositions[iM].Add(position);
leftPrevPosition = position;
}
}
if (rightside)
{
Vector3 tmp_point = node.pos + node.rotation * (Vector3.right * rightWidthOffset) + node.rotation * (Vector3.forward * xOffset[iM]) + yOffset[iM] * Vector3.up;
float prev_distance = Vector3.Distance(rightPrevPosition, tmp_point);
if (prev_distance >= placeEvery)
{
float t_factor = placeEvery / prev_distance;
Vector3 position = Vector3.Lerp(rightPrevPosition, tmp_point, t_factor);
rightPositions[iM].Add(position);
rightPrevPosition = position;
}
}
prevNode = node;
}
}
// go through every points and try to snap the mesh on them
for (int iM = 0; iM < meshesToRepeat.Length; iM++)
{
for (int i = 0; i < leftPositions[iM].Count; i++) // left side
{
Vector3 position = leftPositions[iM][i];
Vector3 nextPosition = leftPositions[iM][(i + 1) % leftPositions[iM].Count];
Quaternion rot = Quaternion.LookRotation(nextPosition - position, Vector3.up) * Quaternion.AngleAxis(rotateYOffset[iM], Vector3.up) * Quaternion.AngleAxis(rotateXOffset[iM], Vector3.left);
Matrix4x4 transform = Matrix4x4.TRS((nextPosition + position) / 2, rot, leftScaling);
Mesh mesh = Instantiate(meshesToRepeat[iM]);
CombineInstance comb = new CombineInstance();
comb.mesh = mesh;
comb.transform = transform;
comb.subMeshIndex = iM;
combineInstances.Add(comb);
}
}
for (int iM = 0; iM < meshesToRepeat.Length; iM++)
{
for (int i = 0; i < rightPositions[iM].Count; i++) // right side
{
Vector3 position = rightPositions[iM][i];
Vector3 nextPosition = rightPositions[iM][(i + 1) % rightPositions[iM].Count];
Quaternion rot = Quaternion.LookRotation(nextPosition - position, Vector3.up) * Quaternion.AngleAxis(-rotateYOffset[iM], Vector3.up) * Quaternion.AngleAxis(rotateXOffset[iM], Vector3.left);
Matrix4x4 transform = Matrix4x4.TRS((nextPosition + position) / 2, rot, rightScaling);
Mesh mesh = Instantiate(meshesToRepeat[iM]);
CombineInstance comb = new CombineInstance();
comb.mesh = mesh;
comb.transform = transform;
comb.subMeshIndex = iM;
combineInstances.Add(comb);
}
}
// combine meshes and sub meshes into a unique mesh
MeshFilter mf = GetComponent<MeshFilter>();
MeshRenderer mr = GetComponent<MeshRenderer>();
MeshCollider mc = GetComponent<MeshCollider>();
Mesh finalMesh = mf.sharedMesh;
// sort submeshes by index, seperate them
List<List<CombineInstance>> intermediateComb = new List<List<CombineInstance>>();
for (int iM = 0; iM < meshesToRepeat.Length; iM++)
{
intermediateComb.Add(new List<CombineInstance>());
}
for (int i = 0; i < combineInstances.Count; i++)
{
CombineInstance comb = combineInstances[i];
int idx = comb.subMeshIndex;
comb.subMeshIndex = 0;
intermediateComb[idx].Add(comb);
}
// once the submeshes are combined, combine the bigger meshes
List<CombineInstance> finalMeshCombs = new List<CombineInstance>();
foreach (List<CombineInstance> submesh_combs in intermediateComb)
{
Mesh submesh = new Mesh();
submesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
submesh.CombineMeshes(submesh_combs.ToArray(), true, true);
CombineInstance comb = new CombineInstance();
comb.mesh = submesh;
finalMeshCombs.Add(comb);
}
if (finalMesh == null)
{
finalMesh = new Mesh();
}
finalMesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
finalMesh.subMeshCount = meshesToRepeat.Length;
finalMesh.CombineMeshes(finalMeshCombs.ToArray(), false, false);
finalMesh.subMeshCount = meshesToRepeat.Length;
finalMesh.Optimize();
finalMesh.RecalculateBounds();
finalMesh.RecalculateNormals();
finalMesh.RecalculateTangents();
mf.sharedMesh = finalMesh;
mc.sharedMesh = finalMesh;
}
}
public void Init()
{
if (generateAtRuntime)
{
Generate();
}
}
}