Process vertices -- create vertices for a track

Source: Internet
Author: User
Problem

Given a base point set in a 3D space, you want to create a track through all points. You want to create a vertex, calculate the normal, and paste the texture on the track.

Solution

You can create a track from the 3D point set in several steps. First, use the 3-dimensional Catmull-Rom interpolation discussed in step 5-16 to generate additional points on the spline between your predefined points. Step 1: Use the arrow in Figure 5-33 to point from "a" to "B ".

Triangles cannot be defined based on these points. For each point on a spline, You need to calculate the direction perpendicular to the spline and add a vertex on each side of the spline, as shown in "B" in 5-33.

Figure 5-33 generate a vertex for the track

Finally, create a TriangleList by converting these new calculated points to the vertex, as shown in "d" in 5-36.

Working Principle

First, you need to define some 3D points. For example, the following set defines the shape of a track similar to 8:

List<Vector3> trackPoints = new List<Vector3>(); trackPoints.Add(new Vector3(2, 0, 4)); trackPoints.Add(new Vector3(0, 0, 0)); trackPoints.Add(new Vector3(-2, 0, -4));trackPoints.Add(new Vector3(2, 0, -4)); trackPoints.Add(new Vector3(0, 1, 0)); trackPoints.Add(new Vector3(-2, 0, 4)); 

Note:The last point is a unit higher than other points, so you do not need to place the traffic lights on the track.

Additional points between calculation points

First, use Catmull-Rom interpolation to calculate many additional points between the basis points, as shown in "B" of 5-34. The InterpolateCR method created in tutorial 5-16 is useful because it can calculate additional points between any two basis points.

However, to create additional points between two basis points, you also need to provide two adjacent basis points. For example, if you want to calculate the extra points between point 1 and point 2 in Figure 5-34, you need to pass points 0, 1, 2 and 3 to the InterpolateCR method.

Figure 5-34 use Catmull-Rom head-tail connection

Generally, if you want to calculate the additional points between the base point I and I + 1, you need to provide the point I-1, I, I + 1 and I + 2. This causes a problem at the end of the track, as shown in Figure 5-34. This track consists of eight points, and you can build parts that are located between [1, 2], [2, 3], [3, 4], [4, 5], and [5, 6] without any problem. However, when calculating the extra points between [6, 7], you need to transmit the points 5, 6, 7, and 8. However, because the set contains only the base point 0 to 7, a problem occurs here. Even worse, when calculating the last 7 to 1 segments, you still need base points 9 and 10, and they do not exist.

Fortunately, you know that the last point of the track is connected to the first point, so you know that base point 8 is base point 0. In the same sense, base point 9 is base point 1, and base point 10 is base point 2. This means you can add base point 0, 1, and 2 to the end of the set to solve this problem. This step needs to be performed at the beginning of the GenerateTrackPoints method. This method creates a set of all the extra points on the entire track:

private List<Vector3> GenerateTrackPoints(List<Vector3> basePoints) {     basePoints.Add(basePoints[0]);     basePoints.Add(basePoints[1]);     basePoints.Add(basePoints[2]);         List<Vector3> allPoints = new List<Vector3>();     for (int i = 1; i < basePoints.Count-2; i++)     {        List<Vector3> part = InterpolateCR(basePoints[i - 1], basePoints[i], basePoints[i + 1], basePoints[i + 2]);         allPoints.AddRange(part);     }    allPoints.Add(allPoints[0]);         return allPoints; } 

After copying the first three basis points to the end of the point set, you create a new empty set that contains all the center points of the track, corresponding to "B" in Figure 5-33 ".

In the for loop, it will jump from a segment to the next segment, use the InterpolateCR method to create an extra vertex for a segment, and add all vertices to the allPoints set.

For each segment, the for loop calls the InterpolateCR method, passing the base point I-1, I, I + 1 and I + 2, I starting from 1. This means that the first paragraph starts from [1, 2], as shown in the left figure of 5-34. The InterpolateCR method returns the base point 1 and 19 additional points between base point 1 and 2. These 20 points will be added to the allPoints set.

The last segment is added to [], which is the same.

Note:You can adjust the number of extra points using the detail variable in the InterpolateCR method.

The for loop continues until all additional points of all parts are added to the allPoints set.

Now you know all the center points of the track, as shown in "B" in 5-33.

Calculate external side points

For each vertex, you want to define a new vertex on both sides of the track, "c" in 5-33, so that you can connect them together to define triangles.

To achieve this, you must first calculate the side direction of each vertex. The Side direction is perpendicular to the driving direction of the CAR and to the normal direction of the track. The (0, 1, 0) Up vector serves as the normal direction of each point on the track.

If you know the two directions, you can take the two directions to obtain the direction perpendicular to the two directions. In this example, the two directions are (0, 1, 0) Up direction and the driving direction of the car. The driving direction is from the current point to the next point, you can get this direction by subtracting the current vertex from the next vertex:

Vector3 carDir = basePoints[i + 1] - basePoints[i]; Vector3 sideDir =Vector3.Cross(new Vector3(0, 1, 0), carDir); sideDir.Normalize(); Vector3 outerPoint = basePoints[i] + sideDir * halfTrackWidth; Vector3 innerPoint = basePoints[i] - sideDir * halfTrackWidth; 

Get the side direction. Multiply it by the width of the track and add/subtract the current point. Then, you calculate the side point of "c" in Figure 5-33.

Create Vertex

Before creating vertices for these points, you need to provide their normal and texture coordinates. Here you use the (0, 1, 0) Up vector as the normal direction.

Texture coordinates are a little difficult. If you add a constant to the Y coordinate of each vertex, the textures at the positions close to each other at the base point will be reduced together. This is because the InterpolateCR method will add 20 additional points under any circumstances, whether two basis points are very close or far away.

To solve this problem, you need to save the distance between two points in a distance variable. The longer part requires more Y coordinates than the shorter one:

VertexPositionNormalTexture vertex;vertex = new VertexPositionNormalTexture(innerPoint, new Vector3(0, 1, 0), new Vector2(0, distance / textureLength)); verticesList.Add(vertex); vertex = new VertexPositionNormalTexture(outerPoint, new Vector3(0, 1, 0), new Vector2(1, distance / textureLength)); verticesList.Add(vertex); distance += carDir.Length(); 

Note:The D value is added after each point on the track is processed. The Length variable of the texture allows you to scale the track texture. You need to perform the following operations on every center point of the track:

private VertexPositionNormalTexture[] GenerateTrackVertices(List<Vector3> basePoints) {    float halfTrackWidth = 0.2f;     float textureLength = 0.5f;     float distance = 0;     List<VertexPositionNormalTexture> verticesList = new List<VertexPositionNormalTexture>();         for (int i = 1; i < basePoints.Count-1; i++)     {        Vector3 carDir = basePoints[i + 1] - basePoints[i];         Vector3 sideDir = Vector3.Cross(new Vector3(0, 1, 0), carDir);         sideDir.Normalize();         Vector3 outerPoint = basePoints[i] + sideDir * halfTrackWidth;         Vector3 innerPoint = basePoints[i] - sideDir * halfTrackWidth;                 VertexPositionNormalTexture vertex;         vertex = new VertexPositionNormalTexture(innerPoint, new Vector3(0, 1, 0), new Vector2(0, distance / textureLength));        verticesList.Add(vertex);                vertex = new VertexPositionNormalTexture(outerPoint, new Vector3(0, 1, 0), new Vector2(1, distance / textureLength));         verticesList.Add(vertex); istance += carDir.Length();     }        VertexPositionNormalTexture extraVert = verticesList[0];     extraVert.TextureCoordinate.Y = distance / textureLength;     verticesList.Add(extraVert);        extraVert = verticesList[1];     extraVert.TextureCoordinate.Y = distance / textureLength;         verticesList.Add(extraVert);        return verticesList.ToArray(); }

After a for loop, the verticesList contains two vertices in each center of the track. However, when you draw a triangle from the vertex set, a gap still exists between the last center and the first center. To connect to this gap, copy the side points of the first two centers to the collection. However, because the Y coordinate of the first two vertices is 0, you need to adjust them to the current texture coordinate value. Otherwise, the last two triangles will return their Y coordinates to 0, resulting in a large number of textures stacked on two small triangles.

Finally, convert the set to an array and return the call code.

Draw tracks

With the base point and method defined, add the following code to convert the base point to a vertex array:

List<Vector3> extendedTrackPoints = GenerateTrackPoints(basePoints); trackVertices = GenerateTrackVertices(extendedTrackPoints); 

After the vertex is defined, you can draw some triangles on the screen:

basicEffect.World = Matrix.Identity; basicEffect.View = fpsCam.ViewMatrix; basicEffect.Projection = fpsCam.ProjectionMatrix;basicEffect.Texture = road; basicEffect.TextureEnabled = true; basicEffect.VertexColorEnabled = false; basicEffect.Begin(); foreach (EffectPass pass in basicEffect.CurrentTechnique.Passes) {    pass.Begin();     device.VertexDeclaration = myVertexDeclaration;    device.DrawUserPrimitives<VertexPositionNormalTexture>(PrimitiveType.TriangleStrip,trackVertices, 0, trackVertices.Length - 2);     pass.End(); }basicEffect.End(); 

See tutorial 6-1 learn how to draw a TriangleList and tutorial 6-2 learn how to draw a triangle with textures.

Fencing

The previous Code generates a completely flat track. This is because the normal of each point on the track is (0, 1, 0) Up. Although this can already generate an available track, you still want to keep the car on the track rather than rush out of the track.

To make the track look more authentic, you need to add a guardrail on the track to prevent the car from flying out of the track.

The preceding Code uses the (0, 1, 0) Up vector as the normal to calculate the side vector. This time, you use a more appropriate normal vector. It is almost impossible to calculate the normal based on the guardrail. To solve this problem, remember the last normal vector and adjust this normal for each point on the track.

To use this method, you must specify an initial value for the normal. If you are sure that the starting point of the track is flat, you can simply use the (0, 1, 0) Up vector as the initial normal, so define the following code in the for Loop:

Vector3 currentNormal = Vector3.Up; 

For each point on the track, you need to consider the degree of curvature of the track to adjust this vector. This bending degree can be expressed in the centrifugal direction: this is the direction you pull the car back, so that the car will not fly out of the track, as shown in 5-35.

Figure 5-35 locate the centrifugal direction

You can obtain this direction through two cross-multiplication. First, it is necessary to take a cross-ride in the direction of the current car (lastCarDir) and the direction of the car pointing to the next point (carDir ). The left graph of the two directions is 5-35. The calculated result vector is perpendicular to these two directions and points out of the paper, so it is difficult to express it on paper. Then, take the result vector and carDir to get the centriDir vector, which always points to the inside of the curved track.

Figure 5-35 shows a complex 3D scene on the right.

The following code is used:

Vector3 carDir = trackPoints[i + 1] - trackPoints[i]; carDir.Normalize(); Vector3 lastCarDir = trackPoints[i] - trackPoints[i - 1]; lastCarDir.Normalize(); Vector3 perpDir = Vector3.Cross(carDir, lastCarDir); Vector3 centriDir = Vector3.Cross(carDir, perpDir); 

Because centriDir points to the inside of the curve, the track barrier must be perpendicular to this vector.

For the above reason, you need to add this vector to the normal vector of each vertex of the track. This will make the side vector slowly perpendicular to the centriDir vector.

But in a long process, this will become too prominent, so you need to add a reset factor. Use the Up vector for this Reset factor to restore the normal to the Up vector at the end of the process:

currentNormal = currentNormal + centriDir * banking + Vector3.Up/banking; currentNormal.Normalize(); 

The larger the banking variable value, the more barrier the track is added. Here you can use the previous code, but don't forget to use currentNormal instead of (0, 1, 0) Up vector:

Vector3 sideDir = Vector3.Cross(currentNormal, carDir); sideDir.Normalize(); currentNormal = Vector3.Cross(carDir, sideDir);

When using this code, your track will have a fence. In addition, this code can loop the track!

Code

The GenerateTrackPoints method accepts and extends the base point array, allowing this method to add track details:

private List<Vector3> GenerateTrackPoints(List<Vector3> basePoints) {    basePoints.Add(basePoints[0]);     basePoints.Add(basePoints[1]);     basePoints.Add(basePoints[2]);         List<Vector3> allPoints = new List<Vector3>();    for (int i = 1; i < basePoints.Count-2; i++)     {        List<Vector3> part = InterpolateCR(basePoints[i - 1], basePoints[i], basePoints[i + 1], basePoints[i + 2]);         allPoints.AddRange(part);     }    return allPoints; }

Based on this extended set, the GenerateTrackVertices method creates an array of track vertices that can be drawn with a barrier:

private VertexPositionNormalTexture[] GenerateTrackVertices(List<Vector3>trackPoints) {    float halfTrackWidth = 0.2f;     float textureLength = 0.5f;     float banking = 2.0f;     float distance = 0;     List<VertexPositionNormalTexture> verticesList = new List<VertexPositionNormalTexture>();     Vector3 currentNormal = Vector3.Up;         for (int i = 1; i < trackPoints.Count-1; i++)     {        Vector3 carDir = trackPoints[i + 1] - trackPoints[i];         carDir.Normalize();        Vector3 lastCarDir = trackPoints[i] - trackPoints[i - 1];         lastCarDir.Normalize();         Vector3 perpDir =Vector3.Cross(carDir, lastCarDir);         Vector3 centriDir = Vector3.Cross(carDir,perpDir);                        currentNormal = currentNormal + Vector3.Up/banking + centriDir * banking;         currentNormal.Normalize();         Vector3 sideDir = Vector3.Cross(currentNormal, carDir);        sideDir.Normalize();         currentNormal = Vector3.Cross(carDir, sideDir);                 Vector3 outerPoint = trackPoints[i] + sideDir * halfTrackWidth;        Vector3 innerPoint = trackPoints[i] - sideDir * halfTrackWidth;         distance += carDir.Length();                VertexPositionNormalTexture vertex;         vertex = new VertexPositionNormalTexture(innerPoint, currentNormal, new Vector2(0, distance / textureLength));        verticesList.Add(vertex);        vertex = new VertexPositionNormalTexture(outerPoint, currentNormal, new Vector2(1, distance / textureLength));         verticesList.Add(vertex);     }        VertexPositionNormalTexture extraVert = verticesList[0];     extraVert.TextureCoordinate.Y = distance / textureLength;     verticesList.Add(extraVert);     extraVert = verticesList[1];     extraVert.TextureCoordinate.Y = distance / textureLength;     verticesList.Add(extraVert);         return verticesList.ToArray();}

With these two methods, you can easily draw a track from a collection of 3D points:

List<Vector3> basePoints = new List<Vector3>();basePoints.Add(new Vector3(2, 0, 4)); basePoints.Add(new Vector3(0, 0, 0)); basePoints.Add(new Vector3(-2, 0, -4)); basePoints.Add(new Vector3(2, 0, -4)); basePoints.Add(new Vector3(0, 1, 0)); basePoints.Add(new Vector3(-2, 0, 4));List<Vector3> extendedTrackPoints = GenerateTrackPoints(basePoints); trackVertices = GenerateTrackVertices(extendedTrackPoints); 

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.