Generated mesh does not render completely

Hi,
I’m having an issue displaying a generated mesh.

The weird thing is that when I display it as wireframe, everything is fine. But when I want to display the faces themselves, I only get a few.
Here’s my code :

function generateCylinderMeshFromArmature(armature, cylinderResolution, cylinderRadius)
{
    const armatureCount = armature.length;

    //generate vertices
    let generatedVertices = [];

    //for each armature vertex
    for (let armatureVertexIndex = 0; armatureVertexIndex < armatureCount; armatureVertexIndex++) {
        const currentVertice = armature[armatureVertexIndex];

        let previousVertice = undefined;
        if(armatureVertexIndex > 0)
        {
            previousVertice = armature[armatureVertexIndex - 1];
        }

        let nextVertice = undefined;
        if(armatureVertexIndex < armatureCount - 1)
        {
            nextVertice = armature[armatureVertexIndex + 1];
        }
        
        let averageForwardDirection = new Vector3(0,0,0);
        if(previousVertice && nextVertice)
        {
            const previousToCurrent = new Vector3(0,0,0);
            previousToCurrent.subVectors(currentVertice, previousVertice);
            previousToCurrent.normalize();

            const currentToNext = new Vector3(0,0,0);
            currentToNext.subVectors(nextVertice, currentVertice);
            currentToNext.normalize();

            averageForwardDirection.addVectors(previousToCurrent, currentToNext);
        }
        else if(previousVertice)
        {
            averageForwardDirection.subVectors(currentVertice, previousVertice);
        }
        else if(nextVertice)
        {
            averageForwardDirection.subVectors(nextVertice, currentVertice);
        }
        else
        {
            console.warn('Error : couldn\'t get direction of vertice');
        }

        averageForwardDirection.normalize();

        const random = new Vector3(0, 1, 0)
        var up = new Vector3(0,0,0);
        up.crossVectors(averageForwardDirection, random);
        var right = new Vector3(0,0,0);
        right.crossVectors(averageForwardDirection,up);

        up.normalize();
        right.normalize();
        //for each vertex of the circle
        for(let meshCircleIndex = 0; meshCircleIndex < cylinderResolution; meshCircleIndex++)
        {
            let verticalComponent = new Vector3(0,0,0);
            verticalComponent.copy(up);
            verticalComponent.multiplyScalar(Math.cos((meshCircleIndex / cylinderResolution) * 2 * Math.PI) * cylinderRadius);
            let horizontalComponent = new Vector3(0,0,0);
            horizontalComponent.copy(right);
            horizontalComponent.multiplyScalar(Math.sin((meshCircleIndex / cylinderResolution) * 2 * Math.PI) * cylinderRadius);
            let newVertice = new Vector3(0,0,0);
            newVertice.copy(currentVertice);
            newVertice.add(verticalComponent);
            newVertice.add(horizontalComponent);
            generatedVertices[armatureVertexIndex * cylinderResolution + meshCircleIndex] = newVertice;
        }

    }
    generatedVertices.push(armature[0]);
    generatedVertices.push(armature[armatureCount - 1]);

    //generate triangles
    // const generatedTriangles = new Uint32Array(cylinderResolution * (armatureCount - 1) * 2 * 3 + 2 * cylinderResolution * 3);
    const generatedTriangles = [];
    for (let armatureVertexIndex = 0; armatureVertexIndex < armatureCount - 1; armatureVertexIndex++) 
    {
        for(let meshCircleIndex = 0; meshCircleIndex < cylinderResolution - 1; meshCircleIndex++)
        {
            generatedTriangles[armatureVertexIndex * cylinderResolution * 2 * 3 + meshCircleIndex * 2 * 3 + 1] = armatureVertexIndex * cylinderResolution + meshCircleIndex + 1;
            generatedTriangles[armatureVertexIndex * cylinderResolution * 2 * 3 + meshCircleIndex * 2 * 3 + 0] = armatureVertexIndex * cylinderResolution + meshCircleIndex;
            generatedTriangles[armatureVertexIndex * cylinderResolution * 2 * 3 + meshCircleIndex * 2 * 3 + 2] = (armatureVertexIndex + 1) * cylinderResolution + meshCircleIndex;
            
            generatedTriangles[armatureVertexIndex * cylinderResolution * 2 * 3 + meshCircleIndex * 2 * 3 + 4] = (armatureVertexIndex + 1) * cylinderResolution + meshCircleIndex + 1;
            generatedTriangles[armatureVertexIndex * cylinderResolution * 2 * 3 + meshCircleIndex * 2 * 3 + 3] = armatureVertexIndex * cylinderResolution + meshCircleIndex + 1;
            generatedTriangles[armatureVertexIndex * cylinderResolution * 2 * 3 + meshCircleIndex * 2 * 3 + 5] = (armatureVertexIndex + 1) * cylinderResolution + meshCircleIndex;
        }

        //for the last face of the cylinder contour
        generatedTriangles[armatureVertexIndex * cylinderResolution * 2 * 3 + (cylinderResolution - 1) * 2 * 3 + 1] = armatureVertexIndex * cylinderResolution + 0;
        generatedTriangles[armatureVertexIndex * cylinderResolution * 2 * 3 + (cylinderResolution - 1) * 2 * 3 + 0] = armatureVertexIndex * cylinderResolution + (cylinderResolution - 1);
        generatedTriangles[armatureVertexIndex * cylinderResolution * 2 * 3 + (cylinderResolution - 1) * 2 * 3 + 2] = (armatureVertexIndex + 1) * cylinderResolution + (cylinderResolution - 1);
        
        generatedTriangles[armatureVertexIndex * cylinderResolution * 2 * 3 + (cylinderResolution - 1) * 2 * 3 + 4] = (armatureVertexIndex + 1) * cylinderResolution + 0;
        generatedTriangles[armatureVertexIndex * cylinderResolution * 2 * 3 + (cylinderResolution - 1) * 2 * 3 + 3] = armatureVertexIndex * cylinderResolution + 0;
        generatedTriangles[armatureVertexIndex * cylinderResolution * 2 * 3 + (cylinderResolution - 1) * 2 * 3 + 5] = (armatureVertexIndex + 1) * cylinderResolution + (cylinderResolution - 1);
    }

    let generatedTrianglesIndex = cylinderResolution * (armatureCount - 1) * 2 * 3;
    for(let i = 0; i < cylinderResolution - 1; i++)
    {
        generatedTriangles[generatedTrianglesIndex++] = i;
        generatedTriangles[generatedTrianglesIndex++] = i + 1;
        generatedTriangles[generatedTrianglesIndex++] = generatedVertices.length - 2;

        generatedTriangles[generatedTrianglesIndex++] = generatedVertices.length - 2 - cylinderResolution + i;
        generatedTriangles[generatedTrianglesIndex++] = generatedVertices.length - 2 - cylinderResolution + i + 1;
        generatedTriangles[generatedTrianglesIndex++] = generatedVertices.length - 1;
    }
    generatedTriangles[generatedTrianglesIndex++] = cylinderResolution - 1;
    generatedTriangles[generatedTrianglesIndex++] = 0;
    generatedTriangles[generatedTrianglesIndex++] = generatedVertices.length - 2;

    generatedTriangles[generatedTrianglesIndex++] = generatedVertices.length - 3;
    generatedTriangles[generatedTrianglesIndex++] = generatedVertices.length - 2 - cylinderResolution;
    generatedTriangles[generatedTrianglesIndex++] = generatedVertices.length - 1;

    //generate mesh
    const geom = new THREE.BufferGeometry();
    geom.setAttribute('position', new THREE.Float32BufferAttribute(vector3ArrayToFloat32Array(generatedVertices), 3));
    geom.setIndex( new THREE.Uint32BufferAttribute(generatedTriangles, 3));
    // geom.setIndex(generatedTriangles);
    // geom.computeFaceNormals();
    const material = new THREE.MeshBasicMaterial( { color: 0xff0000 } );
    // var material = new THREE.MeshPhongMaterial({ color: 0xff0000 } );

    
    const wireframeMaterial = new THREE.MeshBasicMaterial( { color: 0xff0000, wireframe : true } );
    const wireframeMesh = new THREE.Mesh(geom, wireframeMaterial);
    scene.add(wireframeMesh);

    const generatedMesh = new THREE.Mesh(geom, material);
	generatedMesh.position.set( 0, 0, 0);
    generatedMesh.updateMatrix();
    generatedMesh.geometry.computeFaceNormals();
    return generatedMesh;
}

The function takes as a parameter an armature around which I have to draw the cylinder. I also have a resolution (the number of vertices per cylinder circle), and a radius.
I call he function like so :

let armature = [];
armature.push(new Vector3(0,0,0));
armature.push(new Vector3(0,0,1));
armature.push(new Vector3(1,0,1));
armature.push(new Vector3(1,0,-1));
armature.push(new Vector3(-1,0,-1));
armature.push(new Vector3(-1,0,2));
armature.push(new Vector3(2,0,2));
armature.push(new Vector3(2,0,-2));
armature.push(new Vector3(-2,0,-2));
armature.push(new Vector3(-2,0,3));
armature.push(new Vector3(3,0,3));

const armaturesCylinderMesh = generateCylinderMeshFromArmature(armature, 10, 0.3);
scene.add(armaturesCylinderMesh);

As you can see, I commented some of the instructions I tried to solve the problem.
Also, interesting fact, when i use

geom.setIndex( new THREE.Int32BufferAttribute(generatedTriangles, 3));

instead of

geom.setIndex( new THREE.Uint32BufferAttribute(generatedTriangles, 3));

, I get this error : GL_INVALID_ENUM: Enum is not currently supported.
which makes no sense at all…

Any ideas ? Thanks

Considering what an index does for you, I would consider the concept of a signed index to be senseless. There is no such thing as a negative index into an array. For my understanding, it makes perfect sense to force an index array to be of an unsigned type, i.e. its members to always be positive.

I didn’t take the time to go through all of your code. But from the result it looks like you don’t have the same winding order when defining triangle faces.

I propose to assigne a double-sided material to the faces, with different colours on front and back side. That should show you the missing (i.e. pointing away from you) faces in a different color.

The thing that makes no sense is the error that is thrown. I have done nothing with enums yet it throws an error about it.

I already tried changing the winding order and rendering double-sided. Neither of the solutions work.

Thanks for the quick answer though :slight_smile:

Of course you (or your code) are using enums, even though you are not aware of it.

An enum is a predefined, immutable array with a limited set of predefined values in it. Many programming languages natively support enums, although JavaScript does not.

Since it gave you a GL_INVALID_ENUM error, the component that threw the error was likely GL (or maybe WebGL). If you take a look into the GL specification and search for “enum”, you’ll find tons of situations listed where the error code will be GL_INVALID_ENUM .

I’m aware this doesn’t solve your initial problem, but it’s a good occasion to learn how to make sense out of seemingly senseless error messages nevertheless. In your specific situation, this error was triggered because you were requesting a value which was not part of the predefined set of allowed values.