OBJ to to string

Has somebody an idea how to use the obj-description of an object in a python script. At the moment I export it to an obj file and read it from there but this is a time consuming task. Would it be possible to obtain this information without exporting an store it in a string?

Hi @sam.degeyter,

I’m confused as to what information, or descrption are you looking for. Do you have any example?

– Dale

Hi @dale,

I would want something like this:
“mtllib 0a390cea-6890-4124-9213-a7fd139eed16.mtl
usemtl diffuse_Yellow
v 220.65118408203125 5.1851553916931152 197.16098022460938
v 220.65118408203125 1.2758995294570923 197.16098022460938
v 221.1591796875 5.1851553916931152 197.16098022460938
v 221.1591796875 1.2758995294570923 197.16098022460938
vn 0 0 1
vn 0 0 1
vn 0 0 1
vn 0 0 1
f 1//1 2//2 4//4 3//3”

But in a strig, now I export everything to an obj file.

Sam

Hi @dale,

I’m working together with Sam. Let me reprase it a bit: the goal is to avoid read-write operations to the disk, in order to save time when exporting geometry to a web service. Therefore, we’re looking for the right method in python to turn Rhino geometry, in-memory, into a text string (e.g. for OBJ) that can be send as is. Ideally, this would also be possible for binary export formats PLY binary, Rhino, etc.

As an example, let’s say we have a Rhino project with three geometries and we want to export one of them to our webservice as OBJ. We do not want to export it to an OBJ file on the disk, before sending it to our webservice. Instead, we would like to be able to have the OBJ string as a variable in Python, so we can use it directly when communicating with our web service

Maybe @PaulPoinet from the Speckle project can help here, since they probably used used such methods?

You would save yourself tons of work by just deserializing you object to a byte array. And then serializing the byte array to your specific object. Create a wrapper class which contains your data.

1 Like

Here are some lines of code from Speckle where a Rhino mesh is translated into a Speckle object. Speckle then uses the data it serialized inside the Speckle object to recreate a threejs mesh within its viewer interface.

1 Like

Hi @PaulPoinet, thanks for the reply :slight_smile: . I didn’t know Speckle has its own mesh format! Do you have a special three.js loader for Speckle geometry? Are there any sources describing the new format?

We were hoping that the built-in Rhino exporters for existing, widely-used geometry formats (e.g. for OBJ, STEP, PLY, etc.) have some option to export geometry that can reside in-memory, thus avoiding to write to disk. Would be painful if we have to manually write such exporters for each geometry format we want to use!

Thanks for the suggestions, @rawitscher-torres . Do you have some pointers to get started with this in Python for Rhino?

And then serializing the byte array to your specific object.

Do you know if Rhino has some built-in functions to turn Rhino geometry from the byte array into widely-used geometry formats such as OBJ, STEP, PLY, COLLADA, etc.?

The threejs stuff for Speckle happens here, but I never really looked into it…

I am afraid that you can’t really avoid the painful way… As far as I know there isn’t such magic serialization that works for all geometry types and that you can deserialize in a viewer/web service for free. Speckle has converters for each software environment it connects to, and for each object type, attaches a corresponding display value, so that it can be displayed in the viewer with threejs.

But Rhino already has an exporter for a bunch of geometry formats such as OBJ, STEP, etc. but in general these functions result in a file being written to the disk. Internally, those Rhino functions will most likely generate OBJ, STEP, etc. in-memory and I would like to know if it’s possible to get them as such (without writing to the disk). Then I don’t need to create an OBJ, STEP, etc. exporter myself

I would then follow @rawitscher-torres advice to serialize your objects into a byte array.

but the byte array will be structured according to the Rhino geometry format, right?

We’re looking for serializations in other existing geometry formats such as OBJ, STEP, etc. I’ve found a post were someone wrote his own serializer in C# to get in-memory OBJ, but I hope to be able to reuse the standard Rhino serializers/exporters for OBJ, STEP, etc. to have a similar result (using python ideally), but for all the geometry formats supported by Rhino’s export. Fingers crossed the people of McNeel can help us with this :slight_smile:

@mathib

Please check sample file I sent… this quickly shows you how you can create your own custom type, which is basically an abstract implementation of what a mesh is:

  • A collection of vertices
  • A collection of points
  • A collection of normals ( which I have not included in my sample)
  • A collection of colors ( which I have not included in my sample)

With this custom Mesh, you can use to convert from any other type of mesh back and forth. For example from MyMesh to a Three.js Mesh and visa-versa. Or from a Rhino Mesh to a MyMesh and vise-versa, as illustrated in the sample code.

I also included a MyPoint3d wrapper class which does the same as explained above. Though could be omitted, depends on your needs. You can instead have a double [][] where each index will contain a double [3] which represent XYZ coordinates inside the MyMesh class

Runtime:
The worst case scenario in terms of performance is O(N^2) that is when you will convert more than one mesh… nested forloops etc.

Please download Newtonsoft and reference it inside both C# components so you can use the script. When you open the file before this, you will see red components complaining.

Hope this helps.

By the way, ignore the static keyword. I forgot to take it out, I reused some of my code to build this sample for you


/// <summary>
  /// Your Custom Mesh type
  /// </summary>
  public class MyMesh
  {
    public int[][] FaceIndexes;

    // maybe unecesary... you could instead have a jagged array of double
    // each index will have an array of three values
    // correspoinding to XYZ
    public MyPoint3d[] Vertices;


    public MyMesh(int[][] faceIndexes, MyPoint3d[] vertices)
    {

      FaceIndexes = faceIndexes;
      Vertices = vertices;
    }

    public override string  ToString()
    {
      return string.Format("MyMesh, V:{0} , F:{1}", Vertices.Length, FaceIndexes.Length);
    }

  }

  /// <summary>
  /// Your custom Point type
  /// Maybe this should not be even neccesry in your case
  /// </summary>
  public struct MyPoint3d
  {
    public double X ,Y,Z;
    public MyPoint3d(double x, double y, double z)
    {
      X = x;
      Y = y;
      Z = z;
    }
  }


  //////////////// RHINO CONVERTERS ////////////////
  // **NOTE: You can convert from MyMesh to any other type of Mesh. Revit Mesh, Three.JS Mesh etc
  // You just have to write your own methods to do it!
  public static  MyPoint3d ToMyType(Point3d pt)
  {
    return new MyPoint3d(pt.X, pt.Y, pt.Z);
  }




  public  MyMesh ToMyType(Mesh mesh)
  {

    if (!mesh.IsValid) throw new ArgumentException("Please input a valid Rhino Mesh!");

    Rhino.Geometry.Collections.MeshFaceList faces = mesh.Faces;
    int[][] faceIndexes = new int[faces.Count][];
    MyPoint3d[] vertices = mesh.Vertices.ToPoint3dArray().Select(a => ToMyType(a)).ToArray();

    for (int i = 0; i < faces.Count; i++)
    {
      if (faces[i].IsTriangle)
        faceIndexes[i] = new int[] { faces[i].A, faces[i].B, faces[i].C };

      if (faces[i].IsQuad)
        faceIndexes[i] = new int[] { faces[i].A, faces[i].B, faces[i].C, faces[i].D };
    }

    return new MyMesh(faceIndexes, vertices);

  }


  //////////////// RHINO CONVERTERS ////////////////
  // **NOTE: You can convert from MyMesh to any other type of Mesh. Revit Mesh, Three.JS Mesh etc
  // You just have to write your own methods to do it!



  /// <summary>
  /// Convert MyPoint3d to Point3d
  /// </summary>
  /// <param name="p"></param>
  /// <returns></returns>
  public static Point3d ToRhinoType(MyPoint3d p)
  {
    return new Point3d(p.X, p.Y, p.Z);
  }

  /// <summary>
  /// Convert A collection of Point3d to a collection of MyPoint3d
  /// </summary>
  /// <param name="points"></param>
  /// <returns></returns>

  public static IEnumerable<Point3d> ToRhinoType(IEnumerable<MyPoint3d> points)
  {
    return points.Select(a => ToRhinoType(a)).ToArray();
  }

  /// <summary>
  /// Convert MyMesh to Rhino Mesh.
  /// </summary>
  /// <param name="pMesh"></param>
  /// <returns></returns>
  public static Mesh ToRhinoType(MyMesh pMesh)
  {
    int[][] faceIndexes = pMesh.FaceIndexes;
    Point3d [] vertices = ToRhinoType(pMesh.Vertices).ToArray();
    Mesh m = new Mesh();

    for (int i = 0; i < faceIndexes.Length; i++)
    {
      int indexA, indexB, indexC, indexD;

      if (faceIndexes[i].Length == 3)
      {
        indexA = faceIndexes[i][0];
        indexB = faceIndexes[i][1];
        indexC = faceIndexes[i][2];
        m.Faces.AddFace(indexA, indexB, indexC);
      }

      if (faceIndexes[i].Length == 4)
      {
        indexA = faceIndexes[i][0];
        indexB = faceIndexes[i][1];
        indexC = faceIndexes[i][2];
        indexD = faceIndexes[i][3];
        m.Faces.AddFace(indexA, indexB, indexC, indexD);
      }


    }

    m.Vertices.AddVertices(vertices);

    return m;
  }

Cyan Mesh : Your original Rhino Mesh
Pink Mesh: Your original Rhino Mesh again, after being converted from a Rhino Mesh, to MyMesh, then serialized, and then deserialized and converted back again to a RhinoMesh

ConverterSample_Forum.gh (8.7 KB)

Please check the JSON docs:

PS: forget about all that OBJ business… JSON is much more flexible to use and its made to interact with objects, following the OOP paradigm.

1 Like

Thanks, that’s a clear approach, if all you need is meshes. But in some cases, it would also be necessary to have support for more complex exchange formats such as STEP that support non-mesh geometry types. I agree that any geometry format based on JSON is more flexible, but then you’ll end up making a new geometry format, which I would like to avoid in order to max the reuse possibilities of the geometry exported from Rhino in other existing applications.

I hope to get some access method to the native Rhino export functions, i.e. the ones we currently call with:

command_string = '-_Export "{}" _Enter _Enter'.format(full_filepath)
rs.Command(command_string)

which results in a file being written to the disk.

You can make convertions to whatever geometry you want, not only meshes… Nurbs Curves, PolyLines, Nurbs Surfaces, BReps… etc Geometry is universal, it does not only belong to a specific CAD platform, maybe some small implementation details change… but the core logic is still the same.

You can then just convert them in to a JSON object, and in the other side Deserlialize them and convert them to the other applications type geometry. If its a browser, to whatever geometry type they support… and so on…I can guarantee you, that its the most robust way… Its essentially what Specle does with all the server caviar on top to send and receive data.

but for this custom geometry export to be loaded in other CAD tools, I would have to write importers for each single application. This is really not trivial and a lot of work. I don’t aim at a single or few applications (and versions), but as many as possible. Therefore, I think it makes sense to use widely-used geometry formats that are application independent such as OBJ, STEP, etc. I know this process will probably be slower compared to your approach, but as long as I don’t have to write to disk I think it will be quite OK

This is not currently available. Rhino’s file exporters currently always write a file. I am working on tuning up how Rhino can save from the SDK and will consider this.

I would imagine that transfer times over the internet or compression times are going to outweigh any time saved skipping local file I/O.

1 Like

I think one piece of the puzzle you may be missing is that the exporters are just arbitrary code in a library, doing whatever they want to do, as long as it ends up producing a file in the format they promise to write.

For example, I have a simple sketchup exporter for my renderer, in which I just open a file in ruby and start writing out text, until the whole model has been translated and written. So, there is actually never any in-memory representation at all, and I could write one like that for rhino too, if I wished to, and you could call it through the _Export command – and there would be no data for you to get hold of, since it never existed.

File exporters may often be written to stream to disk this way, especially if they supported 32-bit at some point in time, to enable writing files too large to fit in memory.

Yeah, I’m sure of that. But this approach would make it possible to make the geometry more reusable :slight_smile: Thanks for the headsup!