- Home /
Generating a Flat Mesh from Spline/Bezier Curve?
I've been scratching my head over this problem for the past several days... I finally got something close to what I was hoping for.
Basically I'm attempting to create a new Mesh, the vertices will make a 2D road that follows a Bezier curve. I finally figured out that Meshes use Local Space, after doing the conversations I was able to get the curve to end in the right place. But now I'm stuck trying to figure out how to get both edges of the road to follow a curve. I basically need an extrude point along the sideways Vector of the current curve position.
This is my code for each iteration of a mesh part in the curve:
for (var i2 = 0; i2 < curveResolution; i2++) {
var newI2 : float = i2;
var catT : float = newI2 / (curveResolution - 1);
// Get the point on our curve using the 4 points generated above
var p : Vector3 = PointOnPath(catT, points[0], points[1], points[2], points[3]);
var p2 : Vector3 = p;
//What do I do with p2 to give the road thickness?
//and p starts in the center of the transform...
//how do I shift p over while still following the curve?
catVerts.Add(p);
catVerts.Add(p2);
if (catVerts.Count >= 4) {
// We have completed a new quad, create 2 triangles
var start : int = catVerts.Count - 4;
catTris.Add(start + 2);//0
catTris.Add(start + 1);
catTris.Add(start + 0);//2
catTris.Add(start + 0);//1
catTris.Add(start + 3);
catTris.Add(start + 2);
}
}
If that was too confusing basically what I need is a way to get the green lines here:
Obviously I have the black line from the PointOnPath function, but now I need the translations of it that will let create thickness for the road mesh.
Thanks! Or if you have your own example of Mesh Generation on a spline, that would be really helpful as well!
Answer by troien · Mar 11, 2016 at 01:59 PM
Recently I made something similar. How I got it to work is to not only get the point, but also get the forward at any given point along the path. (I used this tutorial, and there they included how to get the forward). Once you have the forward at that position, you also need an up in order to create a quaternion using Quaternion.LookRotation, since you want 2d, you can simply use Vector3.up I suppose.
Now that you have the rotation, you can calculate the point left and right to it using this simple calculation:
Vector3 left = rotation * Vector3.left;
Vector3 right = rotation * Vector3.right;
Now note that left and right here are just the offset to the PointAtPath, so you need to add them to that to get the correct position. You can ofcourse multiply Vector3.left and right by any value to get wider/smaller roads.
Thanks a ton! After a ton of tweaking, I got it to work somewhat correctly. However, now I'm getting some weird behaviour with short distance curves... The vertices are starting ahead/behind of where they're supposed to land. Do you know why that's happening?
If it's any help, here's my current code:
var velAtPoint = GetCurveVelocity(catT, points[0], points[1], points[3], road.transform).normalized;
var rotAtPoint = Quaternion.LookRotation(velAtPoint, Vector3.up);
Debug.Log(rotAtPoint);
var left = rotAtPoint * -Vector3.right;
var right = rotAtPoint * Vector3.right;
p += right;
p2 += left;
The CurveVelocity is the same as from the tutorial you linked to. I'm assu$$anonymous$$g this is what you meant by forward right?
It seems like the points are messing up when the curve goes in a certain direction. When they go one way, everything works as expected. But the other way, the points will overshoot and shrink. Switching which point in the sequence gets the "left" vector only reverses the problem.
I tried the Vector3 Cross Product, hoping it would clear the issue, but the results are the same. Here's the code:
var velAtPoint = GetCurveVelocity(catT, points[0], points[1], points[3], road.transform).normalized;
var crossAtPoint = Vector3.Cross(velAtPoint, Vector3.up);
p += crossAtPoint;
p2 += -crossAtPoint;
Clearly I'm overlooking some sort of Vector $$anonymous$$ath issue... I just don't know what it is...
with foward I mean the GetDirection method, wich in turn calls GetVelocity and normalizes it. Though normalizing shouldn't be needed when you use it just for the forward.
The getvelocity then calls this method
public static Vector3 GetFirstDerivative(Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, float t)
{
t = $$anonymous$$athf.Clamp01(t);
float one$$anonymous$$inusT = 1f - t;
return
3f * one$$anonymous$$inusT * one$$anonymous$$inusT * (p1 - p0) +
6f * one$$anonymous$$inusT * t * (p2 - p1) +
3f * t * t * (p3 - p2);
}
I coud use the result of this method as my forward. I personally haven't found any point at which it doesn't work (as long as I use Vector3.up for the LookRotation up at least). What points do you use when it doesn't work?
That's exactly what I did--I just renamed GetVelocity into GetCurveVelocity. It still gets the First Derivative and as you can see above I normalize the result.
In theory everything should work. I even debugged the direction and the final rotation produced by LookRotation and everything seemed correct. It's only when I add it to the points then things start to mess up... At this point I'm not even doing the "curve" part of the curve, I'm just calling everything in a straight line to make sure it works. When the curve is built in the right direction, it seems to work as expected. But going left, the triangles inverse. And at close range, the points overshoot...
If this comment is a duplicate plz ignore it... Unity answers buttons are not responding:(
And what if you remove the transform.TransformPoint inside the GetVelocity method? (So you basicallly only use GetFirstDerivative with the 4 points and t as parameter)?
For me this works :p Sadly I can't upload an image of it here, asunity answers throws a 405 error when I click the upload button :(
But perhaps in order to help you further I can post the code I used for my mesh generation. Note that this is code for a proof of concept, and not optimized, commented or even completely finished, however, it gets the job done for me :p
private void Start()
{
$$anonymous$$esh mesh = new $$anonymous$$esh();
List<Vector3> vertices = new List<Vector3>();
List<int> triangles = new List<int>();
List<Vector3> normals = new List<Vector3>();
Vector3 Start = GetPoint(0f);
Quaternion rotation = GetRotation(0);
Vector3 left = rotation * Vector3.left;
Vector3 right = rotation * Vector3.right;
Vector3 up = rotation * Vector3.up;
vertices.Add(Start + right);
vertices.Add(Start + left);
normals.Add(up);
normals.Add(up);
int triIndex = 0;
int size = 10; // higher number means smoother but also more verts/tris
for (int i = 0; i <= size; i++)
{
float t = (float)i / (float)size;
Vector3 End = GetPoint(t);
rotation = GetRotation(t);
left = rotation * Vector3.left;
right = rotation * Vector3.right;
up = rotation * Vector3.up;
vertices.Add(End + right);
vertices.Add(End + left);
normals.Add(up);
normals.Add(up);
triangles.Add(triIndex);
triangles.Add(triIndex + 1);
triangles.Add(triIndex + 2);
triangles.Add(triIndex + 2);
triangles.Add(triIndex + 1);
triangles.Add(triIndex + 3);
triIndex += 2;
Start = End;
}
mesh.SetVertices(vertices);
mesh.SetNormals(normals);
mesh.SetTriangles(triangles, 0);
GetComponent<$$anonymous$$eshFilter>().mesh = mesh;
}