Generated mesh is not ideal square

Hi, I’m using mesh generation example and it seems like generated mesh is not ideal square,
increasing extent (the ‘size’ of a mesh plane) increases the ‘gap’ too, in this example 100x100 shall be generated, instead the output is 99x99, the entity scale is set to 1,1,1

  // Generate a 3D grid plane with world size of 20, and resolution of 60
    const resolution = 100;
    this.resolution = resolution;
    
    const extent = 100;
    const scale = extent / resolution;

     // Create a mesh with dynamic vertex buffer and static index buffer
    this.mesh = new pc.Mesh(this.app.graphicsDevice);

    // Generate positions and uv coordinates for verticies, store them in Float32Arrays
    const positions = new Float32Array(3 * resolution * resolution);
    this.positions = positions;
    
    const uvs =  new Float32Array(2 * resolution * resolution);
    this.uvs = uvs;

    let i = 0;
    for (let x = 0; x < resolution; x++) {
        for (let z = 0; z < resolution; z++) {
            positions[3 * i] = scale * (x - resolution * 0.5);
            positions[3 * i + 1] = 0;  // no elevation, flat grid
            positions[3 * i + 2] = scale * (z - resolution * 0.5);
            uvs[2 * i] = x / resolution;
            uvs[2 * i + 1] = 1 - z / resolution;
            i++;
        }
    }

    // Generate array of indicies to form triangle list - two triangles per grid square
    const indexArray = [];
    this.indexArray = indexArray;
    
    for (let x = 0; x < resolution - 1; x++) {
        for (let y = 0; y < resolution - 1; y++) {
            indexArray.push(x * resolution + y + 1, (x + 1) * resolution + y, x * resolution + y,
                            (x + 1) * resolution + y, x * resolution + y + 1, (x + 1) * resolution + y + 1);
        }
    }
 

Any ideas or a way on how to avoid that? Base project PlayCanvas 3D HTML5 Game Engine
Thanks

Hey @Newbie_Coder,

Thank you for sharing your project. I’m investigating this and will get back to you ASAP.

1 Like

Hey @Newbie_Coder ,

I’ve tried out your project, and couldn’t identify your issue. Increasing the size of the mesh through extents as well as its resolution both work as expected. What problem exactly are you encountering? What exactly do you mean with the output being 99x99?

Hey, sorry for a late reply, if we place a 100x100 plane beneath generated mesh we are getting this result:


The mesh is 99x99 or the plane is rendered wrongly?
Updated project with orbital camera:
https://playcanvas.com/project/978199/overview/test
Thank you

Still haven’t found a solution, grateful for any help

Hey @Newbie_Coder,

The issue lies in a slight misinterpretation of how the resolution parameter and its related math works. It’s a bit unintuitive at first, but bear with me.

Before we start, let’s simplify the problem - let’s try to generate a plane with resolution 3 and 100 extent instead. This makes things simpler so we can understand the numbers. The result of generating a mesh with those parameters should be this:

With lower numbers, we can still observe the issue where the mesh is smaller than we expected. But why? Let’s try to get some more clarity by visualising the composing elements of the mesh - the vertices. I’ll spawn red dots where we expect vertices to be:

Now we start to get some more useful info. While the mesh is still smaller than we expected, we can see that our resolution, 3x3, is indeed being generated as it should - we can see three dots in either direction.

The problem, though, is that we want to have a fourth, last line of vertices/points to build the very end of the mesh with. We can’t just increase the resolution by one because this will only make the problem smaller, not outright fix it:

This occurs because of a small mistake in the code where we don’t account for the extra necessary vertex when calculating the total amount of vertices. In the regions where we account for the total number of vertices, we should use resolution + 1.

With the fix, the code looks like this:

var MeshGeneration = pc.createScript('meshGeneration');

MeshGeneration.attributes.add('meshDeformTarget', { type: 'entity' });

// initialize code called once per entity
MeshGeneration.prototype.initialize = function () {

    const app = this.app;
    this.time = 0;

    // Generate a 3D grid plane with world size of 20, and resolution of 60
    const resolution = 100;
    const vertexCount = resolution + 1;
    this.resolution = resolution;

    const extent = 100;
    const scale = extent / resolution;

    // Generate positions and uv coordinates for verticies, store them in Float32Arrays
    const positions = new Float32Array(3 * vertexCount * vertexCount);
    this.positions = positions;

    const uvs = new Float32Array(2 * vertexCount * vertexCount);
    this.uvs = uvs;

    let index = 0;
    for (let x = 0; x <= resolution; x++) {
        for (let z = 0; z <= resolution; z++) {
            let px = scale * (x - resolution * 0.5);
            let py = 0;
            let pz = scale * (z - resolution * 0.5);
            positions[3 * index] = px;
            positions[3 * index + 1] = py;
            positions[3 * index + 2] = pz;

            uvs[2 * index] = x / resolution;
            uvs[2 * index + 1] = 1 - z / resolution;

            index++;
        }
    }

    // Generate array of indices to form triangle list - two triangles per grid square
    const indexArray = [];
    this.indexArray = indexArray;

    for (let x = 0; x <= resolution - 1; x++) {
        for (let y = 0; y <= resolution - 1; y++) {
            var cornerA = x * vertexCount + y + 1;
            var cornerB = (x + 1) * vertexCount + y;
            var cornerC = x * vertexCount + y;

            var cornerD = (x + 1) * vertexCount + y;
            var cornerE = x * vertexCount + y + 1;
            var cornerF = (x + 1) * vertexCount + y + 1;

            indexArray.push(
                cornerA,
                cornerB,
                cornerC,
                cornerD,
                cornerE,
                cornerF
            );
        }
    }

    // Create a mesh with dynamic vertex buffer and static index buffer
    const mesh = new pc.Mesh(app.graphicsDevice);
    this.mesh = mesh;

    mesh.clear(true, false);
    this.updateMesh(mesh, true);

    // create material
    const material = new pc.StandardMaterial();
    material.diffuseMap = this.app.assets.find('playcanvas-grey.png').resource;
    material.shininess = 50;
    material.metalness = 0.3;
    material.useMetalness = true;
    material.update();

    // Create the mesh instance
    const meshInstance = new pc.MeshInstance(mesh, material);

    // Create the entity with render component using meshInstances
    const entity = this.entity;
    entity.addComponent("render", {
        meshInstances: [meshInstance]
    });
};

MeshGeneration.prototype.updateMesh = function (mesh, initAll) {

    mesh.setPositions(this.positions);
    mesh.setNormals(pc.calculateNormals(this.positions, this.indexArray));

    // update mesh Uvs and Indices only one time, as they do not change each frame
    if (initAll) {
        mesh.setUvs(0, this.uvs);
        mesh.setIndices(this.indexArray);
    }

    // Let mesh update Vertex and Index buffer as needed
    mesh.update(pc.PRIMITIVE_TRIANGLES);
};

And it will output the mesh as we desire:

If you wish to look deeper into it, go ahead and take a look at the fork of your project I made. It contains the working fix. Let me know if you need further help!

4 Likes

Now that’s what I call an informative reply!

We can’t just increase the resolution by one because this will only make the problem smaller

My mistake was there, tried calculating resolution instead
Thank you for your preciuos time
Will test this as soon as I’ll fix my lap, seems like WebGL isn’t working properly (all projects freeze)

1 Like