Problem with creating mesh and object rotation
Hi,
I'm using Bezier Splines to create a mesh based on them. And it's working pretty well apart from when the object gets rotated... then the mesh becomes distorted. I think the main problem here is this piece of code:
Vector3 point = transform.InverseTransformPoint(LocalToWorld((spline.GetPoint(1f / (float)i)), Quaternion.Euler(spline.GetDirection(1f / (float)i)), new Vector3(0f, road.points[j].y, (width / 2) - road.points[j].x)));
it seems to me as if when it rotates the X axis doesn't rotate with it (hence X moved to the Z column if I put X in X it's the same just with a 90 degree difference)
But have the full code as well
ProceduralSpline.cs using UnityEngine; using System.Collections;
[RequireComponent(typeof(Spline), typeof(Road))]
[RequireComponent(typeof(MeshRenderer), typeof(MeshFilter), typeof(MeshCollider))]
public class ProceduralSpline : MonoBehaviour
{
public Spline spline;
public Road road;
public float spacing = 5f;
void Start ()
{
spline = GetComponent<Spline>();
road = GetComponent<Road>();
spline.InitTable();
MeshFilter meshFilter = GetComponent<MeshFilter>();
MeshCollider meshCollider = GetComponent<MeshCollider>();
Mesh mesh = GenerateMeshFromRoad(this.road);
meshFilter.mesh = mesh;
meshCollider.sharedMesh = mesh;
}
public Vector3 LocalToWorld(Vector3 position, Quaternion rotation, Vector3 point)
{
return position + rotation * point;
}
public Mesh GenerateMeshFromRoad(Road road)
{
Mesh mesh = new Mesh();
int vertsInShape = road.points.Length;
int segments = spline.Spacing - 1;
int edgeLoops = spline.Spacing;
int vertCount = vertsInShape * edgeLoops;
int triCount = road.lines.Length * segments;
int triIndexCount = triCount * 6;
int width = (int) road.points[road.lines[road.lines.Length - 1]].x;
int[] triangleIndices = new int[triIndexCount];
Vector3[] vertices = new Vector3[vertCount];
Vector3[] normals = new Vector3[vertCount];
Vector2[] uvs = new Vector2[vertCount];
for(int i = 0; i < spline.Spacing; i++)
{
int offset = i * vertsInShape;
for(int j = 0; j < vertsInShape; j++)
{
int id = offset + j;
Vector3 point = transform.InverseTransformPoint(LocalToWorld((spline.GetPoint(1f / (float)i)), Quaternion.Euler(spline.GetDirection(1f / (float)i)), new Vector3(0f, road.points[j].y, (width / 2) - road.points[j].x)));// + transform.InverseTransformPoint(new Vector3((width / 2) - road.points[j].x, road.points[j].y, 0f))));
vertices[id] = point;// + new Vector3(0f, 0f, j == 1 ? 10: 0) ;
normals[id] = transform.InverseTransformDirection(road.normals[j]);
uvs[id] = new Vector2(road.us[j].x, i / ((float)edgeLoops));
}
}
int ti = 0;
for(int i = 0; i < segments; i++)
{
int offset = i * vertsInShape;
for(int l = 0; l < road.lines.Length; l += 2)
{
int a = offset + road.lines[l] + vertsInShape;
int b = offset + road.lines[l];
int c = offset + road.lines[l + 1];
int d = offset + road.lines[l + 1] + vertsInShape;
triangleIndices[ti] = a; ti++;
triangleIndices[ti] = d; ti++;
triangleIndices[ti] = c; ti++;
triangleIndices[ti] = c; ti++;
triangleIndices[ti] = b; ti++;
triangleIndices[ti] = a; ti++;
triangleIndices[ti] = a; ti++;
triangleIndices[ti] = b; ti++;
triangleIndices[ti] = c; ti++;
triangleIndices[ti] = c; ti++;
triangleIndices[ti] = d; ti++;
triangleIndices[ti] = a; ti++;
}
}
mesh.Clear();
mesh.vertices = vertices;
mesh.triangles = triangleIndices;
mesh.normals = normals;
mesh.uv = uvs;
return mesh;
}
void Update ()
{
}
}
Spline.cs using UnityEngine; using System; using System.Collections;
public enum ControlPointMode
{
Free,
Aligned,
Mirrored
}
public class Spline : MonoBehaviour
{
[SerializeField]
private Vector3[] points;
[SerializeField]
private bool loop;
[SerializeField]
private ControlPointMode[] modes;
[SerializeField]
private int spacing;
private float length;
private float[] lengthTable;
public void Reset()
{
points = new Vector3[]
{
new Vector3(0f, 0f, 0f),
new Vector3(10f, 5f, 0f),
new Vector3(20f, 0f, 0f),
new Vector3(30f, 0f, 0f)
};
modes = new ControlPointMode[]
{
ControlPointMode.Free,
ControlPointMode.Free
};
}
void Start()
{
InitTable();
}
public void InitTable()
{
this.lengthTable = BuildSpacingTable();
}
public Vector3 GetPoint(float t)
{
int i;
if (t >= 1f)
{
t = 1f;
i = points.Length - 4;
}
else
{
t = Mathf.Clamp01(t) * CurveCount;
i = (int)t;
t -= i;
i *= 3;
}
float omt = 1f - t;
Vector3 p0 = points[i];
Vector3 p1 = points[i + 1];
Vector3 p2 = points[i + 2];
Vector3 p3 = points[i + 3];
return transform.TransformPoint(omt * omt * omt * p0 + 3f * omt * omt * t * p1 + 3f * omt * t * t * p2 + t * t * t * p3);
// slower code return transform.TransformPoint(Vector3.Lerp(Vector3.Lerp(points[0], points[1], t), Vector3.Lerp(points[1], points[2], t), t));
}
public Vector3 GetFirstDerivative(Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, float t)
{
float omt = 1f - t;
return 3f * omt * omt * (p1 - p0) + 6f * omt * t * (p2 - p1) + 3f * t * t * (p3 - p2);
}
public Vector3 GetVelocity(float t)
{
int i;
if (t >= 1f)
{
t = 1f;
i = points.Length - 4;
}
else
{
t = Mathf.Clamp01(t) * CurveCount;
i = (int)t;
t -= i;
i *= 3;
}
float omt = 1f - t;
Vector3 p0 = points[i];
Vector3 p1 = points[i + 1];
Vector3 p2 = points[i + 2];
Vector3 p3 = points[i + 3];
return transform.TransformPoint(GetFirstDerivative(p0, p1, p2, p3, t)) - transform.position;
}
public Vector3 GetDirection(float t)
{
return GetVelocity(t).normalized;
}
public void AddCurve()
{
Vector3 point = points[points.Length - 1];
Array.Resize(ref points, points.Length + 3);
point.x += 1f;
points[points.Length - 3] = point;
point.x += 1f;
points[points.Length - 2] = point;
point.x += 1f;
points[points.Length - 1] = point;
Array.Resize(ref modes, modes.Length + 1);
modes[modes.Length - 1] = modes[modes.Length - 2];
EnforceMode(points.Length - 4);
if (loop)
{
points[points.Length - 1] = points[0];
modes[modes.Length - 1] = modes[0];
EnforceMode(0);
}
}
public int CurveCount
{
get
{
return (points.Length - 1) / 3;
}
}
public int ControlPointCount
{
get
{
return points.Length;
}
}
public Vector3 GetControlPoint(int point)
{
return points[point];
}
public void SetControlPoint(int point, Vector3 value)
{
if (point % 3 == 0)
{
Vector3 delta = value - points[point];
if (loop)
{
if (point == 0)
{
points[1] += delta;
points[points.Length - 2] += delta;
points[points.Length - 1] = value;
}
else if (point == points.Length - 1)
{
points[0] = value;
points[1] += delta;
points[point - 1] += delta;
}
else
{
points[point - 1] += delta;
points[point + 1] += delta;
}
}
else
{
if (point > 0)
{
points[point - 1] += delta;
}
if (point + 1 < points.Length)
{
points[point + 1] += delta;
}
}
}
points[point] = value;
EnforceMode(point);
}
public ControlPointMode GetControlPointMode(int point)
{
return modes[(point + 1) / 3];
}
public void SetControlPointMode(int point, ControlPointMode mode)
{
int modeIndex = (point + 1) / 3;
modes[modeIndex] = mode;
if (loop)
{
if (modeIndex == 0)
{
modes[modes.Length - 1] = mode;
}
else if (modeIndex == modes.Length - 1)
{
modes[0] = mode;
}
}
EnforceMode(point);
}
private void EnforceMode(int point)
{
int modeIndex = (point + 1) / 3;
ControlPointMode mode = modes[modeIndex];
if(mode == ControlPointMode.Free || !loop && modeIndex == 0 || modeIndex == modes.Length - 1)
{
return;
}
int middleIndex = modeIndex * 3;
int fixedIndex, enforcedIndex;
if (point <= middleIndex)
{
fixedIndex = middleIndex - 1;
if (fixedIndex < 0)
{
fixedIndex = points.Length - 2;
}
enforcedIndex = middleIndex + 1;
if (enforcedIndex >= points.Length)
{
enforcedIndex = 1;
}
}
else
{
fixedIndex = middleIndex + 1;
if (fixedIndex >= points.Length)
{
fixedIndex = 1;
}
enforcedIndex = middleIndex - 1;
if (enforcedIndex < 0)
{
enforcedIndex = points.Length - 2;
}
}
Vector3 middle = points[middleIndex];
Vector3 enforcedTangent = middle - points[fixedIndex];
if (mode == ControlPointMode.Aligned)
{
enforcedTangent = enforcedTangent.normalized * Vector3.Distance(middle, points[enforcedIndex]);
}
points[enforcedIndex] = middle + enforcedTangent;
}
public bool Loop
{
get
{
return loop;
}
set
{
loop = value;
if (value == true)
{
modes[modes.Length - 1] = modes[0];
SetControlPoint(0, points[0]);
}
}
}
public float GetSplineLength()
{
return length;
}
public int Spacing
{
get
{
return spacing;
}
set
{
this.spacing = value;
}
}
public float[] BuildSpacingTable()
{
float length = 0f;
float[] spacingTable = new float[spacing];
Vector3 lastPos = points[0];
for(int i = 0; i < spacing; i++)
{
Vector3 pos = GetPoint((float)i / (spacing)); // GetPoint(1f / (float) i);
float distance = (lastPos - pos).magnitude;
length += distance;
//Debug.Log(i);
spacingTable[i] = length;
lastPos = pos;
}
this.length = length;
return spacingTable;
}
public float Sample(float t)
{
if(lengthTable == null)
{
return 0f;
}
int count = lengthTable.Length;
if(count == 0)
{
Debug.LogError("Cannot sample because array has no elements.");
return 0f;
}
else if(count == 1)
{
return lengthTable[0];
}
float f = t * (count - 1);
int low = Mathf.FloorToInt(f);
int high = Mathf.FloorToInt(f + 1);
if(high >= count)
{
return lengthTable[count - 1];
}
else if(low < 0)
{
return lengthTable[0];
}
Debug.Log(lengthTable[low] + " : " + lengthTable[high] + " : " + " : " + (f - low));
return Mathf.Lerp(lengthTable[low], lengthTable[high], f - low);
}
public float GetConstPathFromTime(float t)
{
if(t > 0f && t < 1f)
{
float tLen = length * t;
float t0 = 0f;
float l0 = 0f;
float t1 = 0f;
float l1 = 0f;
int alen = lengthTable.Length;
for(int i = 0; i < alen; ++i)
{
if(lengthTable[i] > tLen)
{
t1 = ((float) spacing / 1f) * i;
l1 = lengthTable[i];
if(i > 0)
{
l0 = lengthTable[i - 1];
break;
}
t0 = ((float)spacing / 1f) * i;
}
t = t0 + ((tLen - l0) / (l1 - l0)) * (t1 - t0);
}
}
if(t > 1f)
{
t = 1f;
}
else if(t < 0f)
{
t = 0f;
}
return t;
}
public float GetConstPathPercFromTimePerc(float t)
{
if (float.IsNaN(length) || length == 0f) return t;
//Apply constant speed
if (t > 0f && t < 1f)
{
float tLen = length * t;
float t0 = 0f, l0 = 0f, t1 = 0f, l1 = 0f;
int alen = lengthTable.Length;
for (int i = 0; i < alen; ++i)
{
if (lengthTable[i] > tLen)
{
t1 = (1f / spacing) * i;
l1 = lengthTable[i];
if (i > 0) l0 = lengthTable[i - 1];
break;
}
t0 = (1f / spacing) * i;
}
t = t0 + ((tLen - l0) / (l1 - l0)) * (t1 - t0);
}
if (t > 1f) t = 1f;
else if (t < 0f) t = 0f;
return t;
}
}
Road.cs using UnityEngine; using System.Collections; using System;
public class Road : MonoBehaviour
{
public Vector2[] points;
public Vector2[] normals;
public Vector2[] us;
public int[] lines;
}
Proper Rendering (no rotation): http://i.imgur.com/G4YwTtB.png Rotated 180 90 degrees: http://imgur.com/w3B6gVk
Thanks, any help is appreciated.
Answer by tobystrong · Jun 05, 2016 at 11:19 AM
Fixed by adding transform.rotation *
before the 2D point