Creating Simple Tunnel for basic racing game

Hello All,
New the jMonkeyEngine, this seems like a great community and game engine!

Anyhow after extensive searching I have a question I could not find answered anywhere.

I wish to create a tunnel for a basic racing game. Not looking for this tunnel to have complex geometry maybe something like:
__
(__)

I would however like it to be able to twist it, change the elevation, and obviously turn. I would like to procedurally generate the path of this track. The exterior environment of the tunnel is of no concern. I have looked around at the libraries included in jME3 and have found a few things I think might work. I am looking for advise on the best and/or easiest implementation options.

My thoughts:

  1. Connect many quads. Seems like a poor idea, a lot of math involved and the ‘resolution’ of the tunnel will suffer.
  2. Model the tunnel objects out of game (in say Blender), a viable option however I am no experienced modeler and it kind of strikes out the procedurally generated option.

Ideas I have in Jme:

  1. Use custom mesh. Since the track does not need to change shape other than the above mentioned translations and deformations the verticies would be easily calculated and positioned based on the desired turn, twist, or elevation. Question: how to keep track of the track placement? Use a height field and place the lower verticies on to the cooresponding height map value. Could also use this for the vehicle placement along the track. Splines?

  2. Use a Curve mesh.

  3. Combine lots of tetrahedrons using nodes (As in the blockNodeTest). This way seems promising. Though it seems like needless creation of polygons when I just need a single sided mesh.

Looking for ideas on implementations that will allow a simple tunnel system as described that allow for some basic physics and texturing. Thanks.

Well definately ,you’ll need to apply a curve and an array modifier to a base tunnel so you can twist it and change its elevation and be able to procedurally regenerate it.
I have a video tutorial on how to use the modifiers in blender and archieve this.
[video]Modelling a racing track in Blender part 1 - YouTube

1 Like

Ok,
Just to make sure I understand you correctly you are saying I should make model in blender of my base tunnel (race track) and then use ArrayModifiers from within jME to achieve changes in the mesh?

Thanks

“Procedual tunnel” is the term you are searching for, Google should give you some nice examples… maybe not written for jME… but it should not be that hard to translate…

“Procedural tunnel” is the term… sorry.

[video]http://www.youtube.com/watch?v=UIrwCTqKLvo[/video]
Here is another tutorial on how to model a tunnel ,just the technique is what you need then use the blender modifiers

1 Like

Thanks guys, I went through all of those tutorials. Very helpful. However during my quest the problem was solved in a different way. This is a picture of basic implementation in JOGL.

The tunnel is drawn like this:

[java]
for(int f=0; f<brace1Pts.length; f++) {

		setColor(gl2, TestBracePanel.getColorAttribute(brace2.getBracePoints()[f].attribute));
		currPt1 = brace1Pts[f];
		currPt2 = brace2Pts[f];
		Vector3.subtract(currPt2, currPt1, edge1);
		Vector3.subtract(currPt1, prevPt1, edge2);
		Vector3.cross(edge2, edge1, normal);
		
		Vector3.normalize(normal);

// System.out.println("Normal: "+normal);

		gl2.glNormal3f(normal.x, normal.y, normal.z);
		gl2.glVertex3f(prevPt1.x, prevPt1.y, prevPt1.z);
		gl2.glVertex3f(currPt1.x, currPt1.y, currPt1.z);
		gl2.glVertex3f(currPt2.x, currPt2.y, currPt2.z);
		gl2.glVertex3f(prevPt2.x, prevPt2.y, prevPt2.z);

prevPt1 = currPt1;
prevPt2 = currPt2;
}
gl2.glEnd();
}
[/java]

This method draws 2 segments of the tunnel. A tunnel segment is the set of vertices that make up the 2d outline of the tunnel.

I wish to port this into Jme for use of its other features such as lighting, etc. I tried to generate a mesh, but am not sure how to generate the indexes of this track for the VBO. I need to do the generation programmatically because new levels may have different track configurations.

I guess I am confused on what the indexes must represent. Thanks for clarification

@GunnerMan said: Thanks guys, I went through all of those tutorials. Very helpful. However during my quest the problem was solved in a different way.

(snip)

I wish to port this into Jme for use of its other features such as lighting, etc. I tried to generate a mesh, but am not sure how to generate the indexes of this track for the VBO. I need to do the generation programmatically because new levels may have different track configurations.

I guess I am confused on what the indexes must represent. Thanks for clarification

Which indexes? If you mean the index buffer, you technically don’t need it. It’s a way of reducing memory if you have a lot of triangles that share vertexes. Without an index buffer, every three vertexes is a triangle. With an index buffer, every three indexes is a triangle. The indexes then point to the vertex.

If this wasn’t covered in the custom mesh tutorial then that’s unfortunate.

@pspeed said: Which indexes? If you mean the index buffer, you technically don't need it. It's a way of reducing memory if you have a lot of triangles that share vertexes. Without an index buffer, every three vertexes is a triangle. With an index buffer, every three indexes is a triangle. The indexes then point to the vertex.

If this wasn’t covered in the custom mesh tutorial then that’s unfortunate.

Doesn’t DMesh (I believe that is what it is called) allow for bending a mesh along a curve? Might be helpful for this particular issue, no?

1 Like
@t0neg0d said: Doesn't DMesh (I believe that is what it is called) allow for bending a mesh along a curve? Might be helpful for this particular issue, no?

DMesh allows you to apply any modifier to the vertex coordinate and normal. There is a built in function for warping along a curve.

So in that sense, yes, probably. It might also do strange things depending on his source material and setting the parameters for the curve can be somewhat arcane.

But it could be a good thing to play with if custom mesh generation turns out to be too hard for OP. (Note to OP: DMesh is part of Lemur… if nothing else you could look at the source and see how I did it.)

Thanks,
I got the indexes set (I think) and now have this:

Though computing the texture coordinates is proving difficult. I have not quite worked out how to compute these things but since I am just composing this thing out of quads made by two triangles they should be similar to the demo.

However when I assign the texture coordinates and try to call TangentBinormalGenerator on the mesh so I can apply the texture I get some errors I don’t quite understand:

A long list of these:
Apr 30, 2014 6:48:13 PM com.jme3.util.TangentBinormalGenerator processTriangle
WARNING: Colinear uv coordinates for triangle [0, 0, 0]; tex0 = [0, 0], tex1 = [0, 0], tex2 = [0, 0]

Followed by:
WARNING: Angle between tangents exceeds tolerance for vertex 0.
Apr 30, 2014 6:48:14 PM com.jme3.util.TangentBinormalGenerator processTriangleData
WARNING: Shared tangent is zero for vertex 0.
Apr 30, 2014 6:48:14 PM com.jme3.util.TangentBinormalGenerator processTriangleData

and then
java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
at java.util.ArrayList.rangeCheck(ArrayList.java:635)
at java.util.ArrayList.get(ArrayList.java:411)
at com.jme3.util.TangentBinormalGenerator.processTriangleData(TangentBinormalGenerator.java:672)
at com.jme3.util.TangentBinormalGenerator.generate(TangentBinormalGenerator.java:182)

The Java Doc on these was vague and I don’t know how to get past the first warning, no matter how I set the texture vectors it seems to give this warning for every triangle and then loops through the 0th triangle again many times.

I have it set up right now following the demo (can show code if that would be helpful). Do I need to set texture indexes as well or is this something erroneous indexes would cause?

Thanks for all the help,
really appreciate it.

This:
WARNING: Colinear uv coordinates for triangle [0, 0, 0]; tex0 = [0, 0], tex1 = [0, 0], tex2 = [0, 0]

…seems to indicate that you have a triangle with all the same texture coordinate. Probably a mistake.

The last error is the serious one. Your mesh has normals?

I have ensured when I set the texture coordinates that they are all different. Does the texture coordinate array go by the index array?

Yes, I have set the normals, the texture you see is the showNormals material.

@GunnerMan said: I have ensured when I set the texture coordinates that they are all different. Does the texture coordinate array go by the index array?

Of course.

Whatever the case, your mesh is messed up somehow because you have triangles that have the same texture coordinate for all three corners.

The index out of bounds exception indicates that you have an index buffer but it is empty.

Here is my method for creating the mesh. Long post but wanted to include some of the data. Nothing is null, or oversized. I understand my mesh is messed up. I am having a hard time figuring out why/where. I understand my texCoords may not be correct, but they should be different enough so that triangles do not have vertexes with the same cords.

[java]
public static int calcNumVertices(Brace[] trackBraces){
int braceVertices=0;
for (int h = 1; h < trackBraces.length; h++) {
int bracePointsLen = trackBraces[h - 1].getBracePointPositions().length;
braceVertices += bracePointsLen*6;
}
return braceVertices;
}
public static Mesh newTrackGeom() {
//Setup mesh
Mesh mesh = new Mesh();
mesh.setMode(Mesh.Mode.Triangles);
mesh.setStatic();

    //Create Test Track
    Track track = TrackOne.createTrack();
    
    //All of the track sections
    Brace[] trackBraces = track.getGraphicsSection(0, track.size());
    
    int verticesNum = calcNumVertices(trackBraces);
    Vector3f[] vertices = new Vector3f[verticesNum];
    Vector2f[] texCoord = new Vector2f[verticesNum];
    float[] normals = new float[verticesNum*3];
    int[] indexes = new int[verticesNum];
    
    int count = 0;
    int idxCount = 0;
    int ncount = 0;
    int texcount = 0;
    
    for(int h = 1; h &lt; trackBraces.length; h++){
        Vector3 normal = new Vector3();
        Vector3 edge1 = new Vector3();
        Vector3 edge2 = new Vector3();

        Vector3[] brace1Pts = trackBraces[h - 1].getBracePointPositions();
        Vector3[] brace2Pts = trackBraces[h].getBracePointPositions();
        Vector3 prevPt1 = brace1Pts[brace1Pts.length - 1];
        Vector3 prevPt2 = brace2Pts[brace2Pts.length - 1];
        Vector3 currPt1, currPt2;

        for(int f = 0; f &lt; brace1Pts.length; f++){
            currPt1 = brace1Pts[f];
            currPt2 = brace2Pts[f];
            Vector3.subtract(currPt2, currPt1, edge1);
            Vector3.subtract(currPt1, prevPt1, edge2);
            Vector3.cross(edge2, edge1, normal);

            Vector3.normalize(normal);
          
            //Vectors to make each quad.
            Vector3f vec1 = new Vector3f(prevPt1.x, prevPt1.y, prevPt1.z);
            Vector3f vec2 = new Vector3f(currPt1.x, currPt1.y, currPt1.z);
            Vector3f vec3 = new Vector3f(currPt2.x, currPt2.y, currPt2.z);
            Vector3f vec4 = new Vector3f(prevPt2.x, prevPt2.y, prevPt2.z);

            //Set TexCoord Array
            texCoord[texcount++] = new Vector2f(0,0);
            texCoord[texcount++] = new Vector2f(1,0);
            texCoord[texcount++] = new Vector2f(0,1);
            texCoord[texcount++] = new Vector2f(1,1);
            texCoord[texcount++] = new Vector2f(0,1);
            texCoord[texcount++] = new Vector2f(0,0);
            
            //Set vertices, indexes, and normals.
            //Note: for testing I am setting more vertices than needed.
            vertices[count] = vec1;
            indexes[idxCount++] = count;
            setNormal(normals, normal,ncount);
            ncount+=3;
            count++;
     
            vertices[count] = vec2;
            indexes[idxCount++] = count;
            setNormal(normals, normal,ncount);
            ncount+=3;
            count++;
      
            vertices[count] = vec3;
            indexes[idxCount++] = count;
            setNormal(normals, normal,ncount);
            ncount+=3;
            count++;
        
            vertices[count] = vec3;
            indexes[idxCount++] = count-1;
            setNormal(normals, normal,ncount);
            ncount+=3;
            count++;
            
            vertices[count] = vec4;
            indexes[idxCount++]= count;
            setNormal(normals, normal,ncount);
            ncount+=3;
            count++;
            
            vertices[count] = vec1;
            indexes[idxCount++] = count-5;
            setNormal(normals, normal,ncount);
            ncount+=3;
            count++;

            prevPt1 = currPt1;
            prevPt2 = currPt2;
        }
    }

//Printing loops here

    //Set the vertex position, index, texture, and normal buffers
    mesh.setBuffer(VertexBuffer.Type.Position, 3, BufferUtils.createFloatBuffer(vertices));
    mesh.setBuffer(VertexBuffer.Type.Index, 3, BufferUtils.createIntBuffer(indexes));
    mesh.setBuffer(Type.TexCoord, 2, BufferUtils.createFloatBuffer(texCoord));
    mesh.setBuffer(Type.Normal, 3, BufferUtils.createFloatBuffer(normals));
    mesh.updateBound();
    mesh.updateCounts();
   
    return mesh;
}

}
[/java]

Sample output:
Index Array:
Indexes[0] = 0
Indexes[1] = 1
Indexes[2] = 2
Indexes[3] = 2
Indexes[4] = 4
Indexes[5] = 0
Indexes[6] = 6
Indexes[7] = 7
Indexes[8] = 8
Indexes[9] = 8
Indexes[10] = 10
Indexes[11] = 6
Indexes[12] = 12
Indexes[13] = 13
Indexes[14] = 14
Indexes[15] = 14
Indexes[16] = 16
Indexes[17] = 12
Indexes[18] = 18
Indexes[19] = 19
Indexes[20] = 20

Vertex Array:
Vertices[0] = (-2.0, -1.5, 0.0)
Vertices[1] = (2.0, -1.5, 0.0)
Vertices[2] = (2.0, -1.5, -10.0)
Vertices[3] = (2.0, -1.5, -10.0)
Vertices[4] = (-2.0, -1.5, -10.0)
Vertices[5] = (-2.0, -1.5, 0.0)
Vertices[6] = (2.0, -1.5, 0.0)
Vertices[7] = (3.0, -1.0, 0.0)
Vertices[8] = (3.0, -1.0, -10.0)
Vertices[9] = (3.0, -1.0, -10.0)
Vertices[10] = (2.0, -1.5, -10.0)
Vertices[11] = (2.0, -1.5, 0.0)
Vertices[12] = (3.0, -1.0, 0.0)
Vertices[13] = (3.0, 1.0, 0.0)
Vertices[14] = (3.0, 1.0, -10.0)
Vertices[15] = (3.0, 1.0, -10.0)
Vertices[16] = (3.0, -1.0, -10.0)
Vertices[17] = (3.0, -1.0, 0.0)
Vertices[18] = (3.0, 1.0, 0.0)
Vertices[19] = (2.0, 1.5, 0.0)
Vertices[20] = (2.0, 1.5, -10.0)

Texture Array:
texCoord[0] = (0.0, 0.0)
texCoord[1] = (1.0, 0.0)
texCoord[2] = (0.0, 1.0)
texCoord[3] = (1.0, 1.0)
texCoord[4] = (0.0, 1.0)
texCoord[5] = (0.0, 0.0)
texCoord[6] = (0.0, 0.0)
texCoord[7] = (1.0, 0.0)
texCoord[8] = (0.0, 1.0)
texCoord[9] = (1.0, 1.0)
texCoord[10] = (0.0, 1.0)

Thanks again.

Well, right off the bat I can see your vertex array is 21 long and your texture array is only 11 long.

Is there more than one way to interpret this exchange?

@GunnerMan said: I have ensured when I set the texture coordinates that they are all different. Does the texture coordinate array go by the index array?
@pspeed said: Of course.

Every vertex needs attributes. If you use normals then every vertex needs a corresponding normal. If you use texture coordinates then every vertex needs a corresponding texture coordinate… and so on.

The way you handle your indexes is kind of confusing and it’s strange that you add vertexes that you seem to never use. (blank spots in the index array).

Really adding quads to buffers is not so hard. In pseudo code:
[java]
int count = 0;
for( my stuff that makes quads ) {
// in counter clockwise order
add vert 1, normal, texcoord
add vert 2, normal, texcoord
add vert 3, normal, texcoord
add vert 4, normal, textcoord
add indexes: count + 0, count + 1, count + 2
add indexes: count + 2, count + 3, count + 0
count += 4
}
[/java]

Oh, I am sorry. I just pasted a small section of the arrays, since their length is very large, close to 9000. I just put what I thought would be enough to see what is going on. I will delete the unused vertices and clean up my code a bit more. Just had it that way from before I was sure my indices were proper and thought I would leave them in while I tested.