draco icon indicating copy to clipboard operation
draco copied to clipboard

Simple example of encoding and decoding with NPM JS version not working

Open barvirm opened this issue 2 years ago • 1 comments

Hello dear developers, I'm trying to encode and decode hardcoded data without success.

I tried to encode this model (cube) with JS and decode it with draco_decoder utility to obj. This works without any problems. But when I'm trying to decode it in JS, I get odd vertices and indices back.

Env

Windows 11 Node 19.3.0 NPM package draco3d 1.5.5


Decoded information from JS

vertices:
 1 -1  1
-1  1  1
 1  1 -1
 1 -1 -1
-1 -1  1
-1  1  1 - TWICE
 1  1  1 - TWICE
 1  1  1

indices:
0, 1, 2,
2, 1, 3, - No match in original indices
3, 1, 4,
1, 0, 4,
4, 0, 5,
5, 0, 6,
0, 2, 6,
2, 3, 6,
6, 3, 7,
3, 4, 7,
4, 5, 7,
6, 7, 5

Here is my simple example:

Encoder

import draco3d from "draco3d";
import fs from "fs";

{
  const mesh = {
    indices: new Uint32Array([
      0, 1, 2, 1, 3, 4, 3, 5, 6, 0, 7, 5, 7, 2, 4, 5, 3, 1, 7, 0, 2, 2, 1, 4, 4,
      3, 6, 5, 7, 6, 6, 7, 4, 0, 5, 1,
    ]),
    vertices: new Float32Array([
      -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0, -1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
      -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0,
    ]),
  };

  const encoderModule = await draco3d.createEncoderModule({});
  const encoder = new encoderModule.Encoder();
  const meshBuilder = new encoderModule.MeshBuilder();
  const dracoMesh = new encoderModule.Mesh();

  const numFaces = mesh.indices.length / 3;
  const numPoints = mesh.vertices.length;
  meshBuilder.AddFacesToMesh(dracoMesh, numFaces, mesh.indices);

  meshBuilder.AddFloatAttributeToMesh(
    dracoMesh,
    encoderModule.POSITION,
    numPoints,
    3,
    mesh.vertices
  );

  const encodedData = new encoderModule.DracoInt8Array();
  // Use default encoding setting.
  const encodedLen = encoder.EncodeMeshToDracoBuffer(dracoMesh, encodedData);
  console.log("encoded len", encodedLen);
  encoderModule.destroy(dracoMesh);
  encoderModule.destroy(encoder);
  encoderModule.destroy(meshBuilder);

  const outputBuffer = new ArrayBuffer(encodedLen);
  const outputData = new Int8Array(outputBuffer);
  for (let i = 0; i < encodedLen; ++i) {
    outputData[i] = encodedData.GetValue(i);
  }

  fs.writeFileSync("output.drc", Buffer.from(outputBuffer), "binary");
}

Decoder

import draco3d from "draco3d";
import fs from "fs";

{
  const byteArray = fs.readFileSync("output.drc");
  const decoderModule = await draco3d.createDecoderModule({});

  const buffer = new decoderModule.DecoderBuffer();
  buffer.Init(byteArray, byteArray.length);

  // // Create a buffer to hold the encoded data.
  const decoder = new decoderModule.Decoder();
  const geometryType = decoder.GetEncodedGeometryType(buffer);
  if (geometryType !== decoderModule.TRIANGULAR_MESH) {
    console.log("Not a mesh");
  }
  const mesh = new decoderModule.Mesh();
  const status = decoder.DecodeBufferToMesh(buffer, mesh);
  console.log("status", status.ok(), status.error_msg());

  // You must explicitly delete objects created from the DracoDecoderModule
  // or Decoder.

  const numPoints = mesh.num_points();
  const numAttribues = mesh.num_attributes();
  const numFaces = mesh.num_faces();
  console.log("Number of numPoints " + numPoints);
  console.log("Number of numAttribues " + numAttribues);
  console.log("Number of numFaces " + numFaces);

  /// GET POSITIONS
  const attributeId = decoder.GetAttributeId(mesh, draco3d.POSITION);
  if (attributeId < 0) {
    console.log("Missing position attribute");
  }

  const attribute = decoder.GetAttribute(mesh, attributeId);
  const numComponents = attribute.num_components();
  console.log("numComponents", numComponents);
  const attributeData = new decoderModule.DracoFloat32Array();
  decoder.GetAttributeFloatForAllPoints(mesh, attribute, attributeData);

  const numValues = numPoints * numComponents;
  const attributeDataArray = new Float32Array(numValues);
  for (let i = 0; i < numValues; ++i) {
    attributeDataArray[i] = attributeData.GetValue(i);
  }

  for (let i = 0; i < numValues / 3; ++i) {
    console.log(
      attributeDataArray[i + 0],
      attributeDataArray[i + 1],
      attributeDataArray[i + 2]
    );
  }

  // Get indices
  const indices = new Uint32Array(numFaces * 3);
  const ia = new decoderModule.DracoUInt32Array();
  for (let i = 0; i < numFaces; i++) {
    decoder.GetFaceFromMesh(mesh, i, ia);
    const index = i * 3;
    indices[index + 0] = ia.GetValue(0);
    indices[index + 1] = ia.GetValue(1);
    indices[index + 2] = ia.GetValue(2);
  }

  console.log(indices);

  decoderModule.destroy(ia);
  decoderModule.destroy(decoder);
  decoderModule.destroy(buffer);
}

barvirm avatar Jan 12 '23 12:01 barvirm

I didn't look into the this particular example but in general Draco does not preserve order so indices can look different. Also some vertices may get duplicated (e.g. on non-manifold edges/vertices).

If you want to preserve order / vertices you may want to try encoding the data with a sequential encoder, e.g.:

encoder.SetEncodingMethod(encoderModule.MESH_SEQUENTIAL_ENCODING);

ondys avatar Feb 01 '23 20:02 ondys