Draco encode mesh object in rhino3dm.js

Hi!

I know that this question has been asked quite a lot but I was not able to find a solution to my specific problem, so I’m posting again. I have a rhino mesh in javascript that I’d like to encode into a draco compressed string. The mesh comes from a three mesh that I have converted using

let rhinoMesh = rhino.Mesh.createFromThreejsJSON( { data: threeMesh.geometry } ); 

When I try

let compressed = rhino.DracoCompression.compress(rhinoMesh); 

on the rhinoMesh I get a binding error saying that Expected null or instance of Mesh, got an instance of CommonObject. I’m not sure if I completely understand the difference between encode(), compress() & toBase64String(), and I’m not sure if I’m supposed to cast toJSON() on the rhinoMesh before I try to pass it to rhino.DracoCompression.compress(). Maybe I’m missing something else?

If this is the case, could you help me out with a code snippet of what this could look like, since I don’t really understand the code in the node package.

Thanks a lot!
Erik

@fraguada Do you have any suggestions on this?

Could it be that rhinoMesh isn’t created correctly from your threeMesh.geometry? I tried the following and got no error:

const sphereGeometry = new THREE.SphereGeometry( 5, 16, 16 )
const rhinoMesh = rhino.Mesh.createFromThreejsJSON( { data: sphereGeometry } )
const compressed = rhino.DracoCompression.compress( rhinoMesh )
console.log( compressed )
1 Like

Hi @fraguada!

Thanks a lot for taking your time to have a look at this. Now I understand how the code is supposed to work and when I tried your example it actually worked, so you are right in that is has to do with the rhinoMesh.

I’ve been doing some extensive testing today, but I still can’t figure it out though. My mesh doesn’t seem to be invalid and I also tested to use const encodedMesh = rhinoMesh.encode() and save this to a text file. When I use

return Rhino.Runtime.CommonObject.FromJSON(encodedMesh);

on the json-formated string encodedMesh I get a perfectly valid rhino mesh in grasshopper (providing the .txt if you want to give it a go but you can see in the picture as well .) encodedMesh.txt (54.4 KB)

The only differences I can see between your example and my mesh are:

  1. The sphere.geometry has type “SphereGeometry” while my mesh has type “BufferGeometry” (this should not matter I think).
  2. My mesh has only the position attribute, while the sphere also has normal and uv attributes.
  3. My mesh is indexed just like the sphere, but it could be that some vertices are really close together for my mesh. Could this be a problem?

Thanks again!
Best,
Erik

I’m attaching a sample where I’m loading your encoded mesh, decoding to a Rhino mesh, converting that to a threejs mesh, then creating a rhino mesh from that again, and compressing that. All of that ping pong seems to work ok.
SampleEncodeDecodeMesh.zip (34.7 KB)

I am looking at the BufferGeometry geomerated by the method toThreejsJSON and I see that it includes position and normal attributes and is indexed:

I did some other tests to see that we are passing all of the data contained in BufferGeometry (normals, vertices, faces, uvs, etc) and it seems we take whatever is in BufferGeometry.

All this is to say that whatever your encoded mesh is, it seems rhino3dm happily takes it.

For reference, here is the code we use to convert the threejs BufferGeometry to a Rhino mesh: rhino3dm/bnd_mesh.cpp at main · mcneel/rhino3dm · GitHub

Looking at this code, it does seem that we at least assume that a mesh coming from threejs at least has positions and normals when converting it from threejs. The Draco compression method checks if the Rhino mesh has normals, but should be able to run without them as well.

The only thing I can think of is to compute the normals on the threejs mesh before passing it to Rhino to see if it solves anything.
threeMesh.geometry.computeVertexNormals() should be what you call.

1 Like

Hi again @fraguada and thanks again for the effort, it really helps.

I tried to use replicate what you did with

let jsonMesh = rhinoMesh.encode();
let decodedRhinoMesh = rhino.CommonObject.decode(jsonMesh);

and then compress the decodedRhinoMesh, and this worked so I’ll go for this solution for now. It probably has something to do with the ordering of properties or some small change in the structure of the object that gets altered when encoding and decoding the object that makes it work. Could it be that the vertex normals are calculated implicitly when decoding the encoded mesh maybe?

Anyway, the error that I got was an Uncaught BindingError for anyone that might have the same issue in the future, and this solution worked. I’ll updated if I find more details on what really is happening here.

Thanks!
Erik

1 Like