0
\$\begingroup\$

I have a mesh that is generated using the marching cubes algorithm that is drawn using gl.drawArrays(), however the whole mesh isn't rendered, only part of it. I checked if the count I was specifiying in my gl.drawArrays() method was equal to the number of triangles, it was, and I checked if the mesh data was valid, and it was. So I am out of ideas on what to do. My meshes are split into 16x16 chunks. Here is what the issue looks like:

For a 1x1 mesh, it didn't even render, even though the 1x1 mesh data looks correct. If it helps, the vertex data where every 3 entries is an x, y, z position looked like this:

[1, 0.5, 0, 0, 0.5, 0, 1, 0.5, 1, 1, 0.5, 1, 0, 0.5, 0, 0, 0.5, 1]

Partly Rendered Chunks

Have you guys ever had meshes being partially rendered, if so, any ideas on how to solve it? If it helps, here is the chunk class:

function Chunk(chunkX, chunkY, cameraMatrix) {
  this.chunkX = chunkX;
  this.chunkY = chunkY;
  this.cameraMatrix = cameraMatrix;
  this.positionData = [];
  this.textureIdData = [];
  this.uvData = [];
  this.normalData = [];

  const chunkSizePlusOne = chunkSize + 1;
  const full = -1;
  const empty = 1;
  const totallyInside = 255;
  const totallyOutside = 0;
  const edgeIntersectionsSize = 16;
  const surface = 0;
  const chunkXPosition = chunkSize * chunkX;
  const chunkYPosition = chunkSize * chunkY;

  this.build = function () {
    const normalsOfTrianglesAdjacentToVertex = {};
    const vertexNormals = {};
    const perlinValues = [];

    for (let y = 0; y < chunkSizePlusOne; y++) {
      for (let x = 0; x < chunkSizePlusOne; x++) {
        const trueX = x + chunkXPosition;
        const trueY = y + chunkYPosition;

        perlinValues[y * chunkSizePlusOne + x] = perlin(trueX, trueY);
      };
    };

    for (let y = 0; y < chunkSize; y++) {
      for (let x = 0; x < chunkSize; x++) {
        const trueX = x + chunkXPosition;
        const trueY = y + chunkYPosition;

        for (let z = 0; z < worldHeight; z++) {
          const cubePositions = [
            [trueX, trueY, z],
            [trueX + 1, trueY, z],
            [trueX + 1, trueY + 1, z],
            [trueX, trueY + 1, z],
            [trueX, trueY, z + 1],
            [trueX + 1, trueY, z + 1],
            [trueX + 1, trueY + 1, z + 1],
            [trueX, trueY + 1, z + 1]
          ];
          const cubeScalarField = [];
          let lookupIndexOrrer = 1;
          let lookupIndex = 0;

          for (let cubePosition of cubePositions) {
            const [cubeX, cubeY, cubeZ] = cubePosition;
            const perlinValuesIndex = chunkSizePlusOne * (cubeY - chunkYPosition) + (cubeX - chunkXPosition);
            const height = worldHeight * perlinValues[perlinValuesIndex];

            if (cubeZ <= height) {
              cubeScalarField.push(full);

              lookupIndex |= lookupIndexOrrer;
            } else {
              cubeScalarField.push(empty);
            };

            lookupIndexOrrer <<= 1;
          };

          if (lookupIndex != totallyInside && lookupIndex != totallyOutside) {
            const edgeIntersections = triangulationTable[lookupIndex];
            
            for (let i = 0; i < edgeIntersectionsSize; i += 3) {
              const edgeIntersectionAtI = edgeIntersections[i];

              if (edgeIntersectionAtI !== -1) {
                const triangleEdgeIntersections = [
                  edgeIntersectionAtI,
                  edgeIntersections[i + 1],
                  edgeIntersections[i + 2]
                ];
                const xArray = [];
                const yArray = [];
                const zArray = [];

                for (let edgeIntersection of triangleEdgeIntersections) {
                  const [vertex1, vertex2] = verticesByEdge[edgeIntersection];
                  const [x1, y1, z1] = cubePositions[vertex1];
                  const [x2, y2, z2] = cubePositions[vertex2];
                  const vertex1Value = cubeScalarField[vertex1];
                  const vertex2Value = cubeScalarField[vertex2];
                  const vertex2Bias = (surface - vertex1Value) / (vertex2Value - vertex1Value);
                  const vertex1Bias = 1 - vertex2Bias;
                  const interpolatedX = x1 * vertex1Bias + x2 * vertex2Bias;
                  const interpolatedY = y1 * vertex1Bias + y2 * vertex2Bias;
                  const interpolatedZ = z1 * vertex1Bias + z2 * vertex2Bias;

                  xArray.push(interpolatedX);
                  yArray.push(interpolatedY);
                  zArray.push(interpolatedZ);

                  // push in order of actual webgl coordinates
                  this.positionData.push(interpolatedX);
                  this.positionData.push(interpolatedZ);
                  this.positionData.push(interpolatedY);
                };

                this.textureIdData.push(textureId(zArray));

                const uvData2D = uv(xArray, yArray);
                // parameters in order of webgl coordinates
                const normalOfTriangle = triangleNormal(xArray, zArray, yArray);

                for (let i = 0; i < numberOfTriangleVertices; i++) {
                  for (let j = 0; j < 2; j++) {
                    this.uvData.push(uvData2D[i][j]);
                  };

                  // in order of webgl coordinates
                  const vertexCoordinate = [xArray[i], zArray[i], yArray[i]];

                  if (normalsOfTrianglesAdjacentToVertex[vertexCoordinate]) {
                    normalsOfTrianglesAdjacentToVertex[vertexCoordinate].push(normalOfTriangle);
                  } else {
                    normalsOfTrianglesAdjacentToVertex[vertexCoordinate] = [normalOfTriangle];
                  };
                };  
              };
            };
          };
        };
      };
    };

    for (let vertex of Object.keys(normalsOfTrianglesAdjacentToVertex)) {
      vertexNormals[vertex] = meanOfVector3Ds(normalsOfTrianglesAdjacentToVertex[vertex]);
    };

    const positionData = this.positionData;
    const positionDataLength = positionData.length;

    for (let i = 0; i < positionDataLength; i += 3) {
      const vertex = [positionData[i], positionData[i + 1], positionData[i + 2]];
      this.normalData.push(...vertexNormals[vertex]);
    };
  };
  this.render = function () {
    gl.useProgram(program);

    const positionBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(this.positionData), gl.DYNAMIC_DRAW);
  
    gl.enableVertexAttribArray(positionLocation);
    gl.vertexAttribPointer(positionLocation, 3, gl.FLOAT, false, 0, 0);

    const textureIdBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, textureIdBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(this.textureIdData), gl.DYNAMIC_DRAW);

    gl.enableVertexAttribArray(textureIdLocation);
    gl.vertexAttribPointer(textureIdLocation, 1, gl.FLOAT, false, 0, 0);
  
    const uvBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, uvBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(this.uvData), gl.DYNAMIC_DRAW);
  
    gl.enableVertexAttribArray(uvLocation);
    gl.vertexAttribPointer(uvLocation, 2, gl.FLOAT, false, 0, 0);
  
    const normalBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, normalBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(this.normalData), gl.DYNAMIC_DRAW);
  
    gl.enableVertexAttribArray(normalLocation);
    gl.vertexAttribPointer(normalLocation, 3, gl.FLOAT, false, 0, 0); 

    gl.uniformMatrix4fv(matrixLocation, false, this.cameraMatrix);

    gl.drawArrays(gl.TRIANGLES, 0, this.textureIdData.length);
  };
};
\$\endgroup\$
4
  • \$\begingroup\$ Can you reproduce the problem with a very small chunk? Say something 1x1 or 2x2 instead of 16x16? Then you can print out the full contents of your mesh data and we can walk through it, comparing it to the image, to identify where in the mesh things go wrong. \$\endgroup\$
    – DMGregory
    Aug 27 '21 at 12:12
  • \$\begingroup\$ I got some interesting results, it seems a 1x1 chunk doesn't render even though the mesh data looks right. By the way, this is what the mesh data was, where every 3 entries is a vertex x, y, z position: [1, 0.5, 0, 0, 0.5, 0, 1, 0.5, 1, 1, 0.5, 1, 0, 0.5, 0, 0, 0.5, 1] \$\endgroup\$ Aug 27 '21 at 12:37
  • \$\begingroup\$ Edit your question to include that example — it won't be legible in a comment. In general, every piece of information that helps explain your problem belongs in the question, not in a comment. \$\endgroup\$
    – DMGregory
    Aug 27 '21 at 12:37
  • \$\begingroup\$ I edited my question, thank you for your help! \$\endgroup\$ Aug 27 '21 at 12:41
0
\$\begingroup\$

For the count parameter, I needed to not use the number of triangles, but the number of vertices, also if I do that, I need to add a textureId to this.textureIdData for each vertex, and not for each triangle. I guess I forgot how gl.drawArrays worked... Here is the updated chunk class

function Chunk(chunkX, chunkY, cameraMatrix) {
  this.chunkX = chunkX;
  this.chunkY = chunkY;
  this.cameraMatrix = cameraMatrix;
  this.positionData = [];
  this.textureIdData = [];
  this.uvData = [];
  this.normalData = [];

  const chunkSizePlusOne = chunkSize + 1;
  const full = -1;
  const empty = 1;
  const totallyInside = 255;
  const totallyOutside = 0;
  const edgeIntersectionsSize = 16;
  const surface = 0;
  const chunkXPosition = chunkSize * chunkX;
  const chunkYPosition = chunkSize * chunkY;

  this.build = function () {
    const normalsOfTrianglesAdjacentToVertex = {};
    const vertexNormals = {};
    const perlinValues = [];

    for (let y = 0; y < chunkSizePlusOne; y++) {
      for (let x = 0; x < chunkSizePlusOne; x++) {
        const trueX = x + chunkXPosition;
        const trueY = y + chunkYPosition;

        perlinValues[y * chunkSizePlusOne + x] = perlin(trueX, trueY);
      };
    };

    for (let y = 0; y < chunkSize; y++) {
      for (let x = 0; x < chunkSize; x++) {
        const trueX = x + chunkXPosition;
        const trueY = y + chunkYPosition;

        for (let z = 0; z < worldHeight; z++) {
          const cubePositions = [
            [trueX, trueY, z],
            [trueX + 1, trueY, z],
            [trueX + 1, trueY + 1, z],
            [trueX, trueY + 1, z],
            [trueX, trueY, z + 1],
            [trueX + 1, trueY, z + 1],
            [trueX + 1, trueY + 1, z + 1],
            [trueX, trueY + 1, z + 1]
          ];
          const cubeScalarField = [];
          let lookupIndexOrrer = 1;
          let lookupIndex = 0;

          for (let cubePosition of cubePositions) {
            const [cubeX, cubeY, cubeZ] = cubePosition;
            const perlinValuesIndex = chunkSizePlusOne * (cubeY - chunkYPosition) + (cubeX - chunkXPosition);
            const height = worldHeight * perlinValues[perlinValuesIndex];

            if (cubeZ <= height) {
              cubeScalarField.push(full);

              lookupIndex |= lookupIndexOrrer;
            } else {
              cubeScalarField.push(empty);
            };

            lookupIndexOrrer <<= 1;
          };

          if (lookupIndex != totallyInside && lookupIndex != totallyOutside) {
            const edgeIntersections = triangulationTable[lookupIndex];
            
            for (let i = 0; i < edgeIntersectionsSize; i += 3) {
              const edgeIntersectionAtI = edgeIntersections[i];

              if (edgeIntersectionAtI !== -1) {
                const triangleEdgeIntersections = [
                  edgeIntersectionAtI,
                  edgeIntersections[i + 1],
                  edgeIntersections[i + 2]
                ];
                const xArray = [];
                const yArray = [];
                const zArray = [];

                for (let edgeIntersection of triangleEdgeIntersections) {
                  const [vertex1, vertex2] = verticesByEdge[edgeIntersection];
                  const [x1, y1, z1] = cubePositions[vertex1];
                  const [x2, y2, z2] = cubePositions[vertex2];
                  const vertex1Value = cubeScalarField[vertex1];
                  const vertex2Value = cubeScalarField[vertex2];
                  const vertex2Bias = (surface - vertex1Value) / (vertex2Value - vertex1Value);
                  const vertex1Bias = 1 - vertex2Bias;
                  const interpolatedX = x1 * vertex1Bias + x2 * vertex2Bias;
                  const interpolatedY = y1 * vertex1Bias + y2 * vertex2Bias;
                  const interpolatedZ = z1 * vertex1Bias + z2 * vertex2Bias;

                  xArray.push(interpolatedX);
                  yArray.push(interpolatedY);
                  zArray.push(interpolatedZ);

                  // push in order of actual webgl coordinates
                  this.positionData.push(interpolatedX);
                  this.positionData.push(interpolatedZ);
                  this.positionData.push(interpolatedY);
                };

                const triangleTextureId = textureId(zArray);
                const uvData2D = uv(xArray, yArray);
                // parameters in order of webgl coordinates
                const normalOfTriangle = triangleNormal(xArray, zArray, yArray);

                for (let i = 0; i < numberOfTriangleVertices; i++) {
                  this.textureIdData.push(triangleTextureId);

                  for (let j = 0; j < 2; j++) {
                    this.uvData.push(uvData2D[i][j]);
                  };

                  // in order of webgl coordinates
                  const vertexCoordinate = [xArray[i], zArray[i], yArray[i]];

                  if (normalsOfTrianglesAdjacentToVertex[vertexCoordinate]) {
                    normalsOfTrianglesAdjacentToVertex[vertexCoordinate].push(normalOfTriangle);
                  } else {
                    normalsOfTrianglesAdjacentToVertex[vertexCoordinate] = [normalOfTriangle];
                  };
                };  
              };
            };
          };
        };
      };
    };

    for (let vertex of Object.keys(normalsOfTrianglesAdjacentToVertex)) {
      vertexNormals[vertex] = meanOfVector3Ds(normalsOfTrianglesAdjacentToVertex[vertex]);
    };

    const positionData = this.positionData;
    const positionDataLength = positionData.length;

    for (let i = 0; i < positionDataLength; i += 3) {
      const vertex = [positionData[i], positionData[i + 1], positionData[i + 2]];
      this.normalData.push(...vertexNormals[vertex]);
    };
  };
  this.render = function () {
    gl.useProgram(program);

    const positionBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(this.positionData), gl.DYNAMIC_DRAW);
  
    gl.enableVertexAttribArray(positionLocation);
    gl.vertexAttribPointer(positionLocation, 3, gl.FLOAT, false, 0, 0);

    const textureIdBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, textureIdBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(this.textureIdData), gl.DYNAMIC_DRAW);

    gl.enableVertexAttribArray(textureIdLocation);
    gl.vertexAttribPointer(textureIdLocation, 1, gl.FLOAT, false, 0, 0);
  
    const uvBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, uvBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(this.uvData), gl.DYNAMIC_DRAW);
  
    gl.enableVertexAttribArray(uvLocation);
    gl.vertexAttribPointer(uvLocation, 2, gl.FLOAT, false, 0, 0);
  
    const normalBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, normalBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(this.normalData), gl.DYNAMIC_DRAW);
  
    gl.enableVertexAttribArray(normalLocation);
    gl.vertexAttribPointer(normalLocation, 3, gl.FLOAT, false, 0, 0); 

    gl.uniformMatrix4fv(matrixLocation, false, this.cameraMatrix);

    gl.drawArrays(gl.TRIANGLES, 0, this.positionData.length / 3);
  };
};
```
\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .