[SOLVED] Normals on Custom Cylinder Mesh

I need a custom cylinder mesh for my game (I can’t use the built-in mesh as it is on the XY plane rather than the XZ plane and rotating it seems to be ineffective.). I have created a DiscMesh class that contains a cylinder on the XZ plane, but the normals do not appear correctly. I should also note that I need to set the FaceCullMode to Front otherwise the mesh renders inside-out. It still does this even when I reverse the order of the indecies in each triangle.

I am calculating the normals as described here in the second answer with a directly up and a directly down normal for the center point.

Screenshot:

Wireframe Screenshot:

Code:

import com.jme3.math.Vector3f;
import com.jme3.scene.Mesh;
import com.jme3.scene.VertexBuffer.Type;
import com.jme3.util.BufferUtils;

public class DiscMesh extends Mesh{
    private static final int SUBDIVISION_COUNT = 64;
    private static final float SUBDIVISION_SIZE = 6.28319f / SUBDIVISION_COUNT;
   
    public DiscMesh(float radius, float height){
        //setup mesh data variables
        Vector3f[] verticies = new Vector3f[(SUBDIVISION_COUNT * 2) + 2];
        Vector3f[] normals = new Vector3f[verticies.length];
        int[] indecies = new int[SUBDIVISION_COUNT * 12];

        int currentVertex = 0;
        int currentIndex = 0;

        //setup mesh data generation variables
        float halfHeight = height * .5f;
        int[] lowerVertexNumbers = new int[SUBDIVISION_COUNT];
        int[] upperVertexNumbers = new int[SUBDIVISION_COUNT];
        
        //add center points
        Vector3f lowerCenterVertex = new Vector3f(0, 0, 0);
        Vector3f upperCenterVertex = new Vector3f(0, height, 0);
        
        int lowerCenterVertexNumber = currentVertex++;
        int upperCenterVertexNumber = currentVertex++;
        
        verticies[lowerCenterVertexNumber] = lowerCenterVertex;
        verticies[upperCenterVertexNumber] = upperCenterVertex;
        normals[lowerCenterVertexNumber] = new Vector3f(0, -1, 0).normalizeLocal();
        normals[upperCenterVertexNumber] = new Vector3f(0, 1, 0).normalizeLocal();
        
        //add outer points
        for(int i=0;i<SUBDIVISION_COUNT;i++){
            float angle = SUBDIVISION_SIZE * i;
            float x = (float) (radius * Math.cos(angle));
            float z = (float) (radius * Math.sin(angle));
            
            int lowerVertexNumber = currentVertex++;
            int upperVertexNumber = currentVertex++;
            
            verticies[lowerVertexNumber] = new Vector3f(x, 0, z);
            verticies[upperVertexNumber] = new Vector3f(x, height, z);
            normals[lowerVertexNumber] = new Vector3f(x, 0, z).subtractLocal(0, halfHeight, 0).normalizeLocal();
            normals[upperVertexNumber] = new Vector3f(x, height, z).subtractLocal(0, halfHeight, 0).normalizeLocal();
            
            lowerVertexNumbers[i] = lowerVertexNumber;
            upperVertexNumbers[i] = upperVertexNumber;
        }
        
        //add triangles
        for(int i=0;i<SUBDIVISION_COUNT;i++){
           int next = (i + 1) % SUBDIVISION_COUNT;
           
           int lowerVertex = lowerVertexNumbers[i];
           int upperVertex = upperVertexNumbers[i];
           int nextLowerVertex = lowerVertexNumbers[next];
           int nextUpperVertex = upperVertexNumbers[next];
           
           indecies[currentIndex++] = nextUpperVertex;
           indecies[currentIndex++] = upperCenterVertexNumber;
           indecies[currentIndex++] = upperVertex;
           
           indecies[currentIndex++] = nextUpperVertex;
           indecies[currentIndex++] = upperVertex;
           indecies[currentIndex++] = lowerVertex;
           
           indecies[currentIndex++] = nextLowerVertex;
           indecies[currentIndex++] = nextUpperVertex;
           indecies[currentIndex++] = lowerVertex;
           
           indecies[currentIndex++] = lowerVertex;
           indecies[currentIndex++] = lowerCenterVertexNumber;
           indecies[currentIndex++] = nextLowerVertex;
        }
        
        setBuffer(Type.Position, 3, BufferUtils.createFloatBuffer(verticies));
        setBuffer(Type.Index, 3, BufferUtils.createIntBuffer(indecies));
        setBuffer(Type.Normal, 3, BufferUtils.createFloatBuffer(normals));
        updateBound();
    }
    
}

We could also deal with this issue… since rotating it should be fine if you do it right.

As to your other, without looking at your code, most likely you are trying to share vertexes between the tops and the sides. The top needs its own vertexes with normals pointing up.

I tried rotating it (although, I don’t understand quaternions so I was almost certainly doing it wrong) and it either didn’t rotate at all, rotate in such a way that the rotation can’t be seen, or rotated at some odd angle depending on exactly what parameters I tried. Also, I would prefer to leave my options more open to make a more complex mesh in the future if it becomes necessary.

I’ll try giving the top surface it’s own vertices next time I get a chance to work on this and post back here if it works.

cyl.setLocalRotation(new Quaternion().fromAngles(FastMath.HALF_PI, 0, 0));
…or something to that effect. In other words, rotate it 90 degrees about the x-axis.

Also can be done with:
cyl.rotate(FastMath.HALF_PI, 0, 0);
…if you know there isn’t any existing rotation already.

This is basic stuff that would be good to learn because it will trip you every other step otherwise.

I made the surface of the disc have it’s own vertices with normals pointing directly up or down. It seems to have worked. Thank you for your help. I’ll make sure to take another look at quaternions next time I run into them.

Consider switching to imported models as well if you plan to use more complex mesh in future.

If I added more complex models they would be procedural, hence preimporting them isn’t an option.

Why?

Because he would be generating them at runtime.

…very common in modern indie games.

1 Like