drake icon indicating copy to clipboard operation
drake copied to clipboard

STL support

Open SeanCurtis-TRI opened this issue 2 years ago • 8 comments

What happened?

In a recent slack coversation a user tried instantiating a model using STL files. They went through the following unhelpful process.

  1. Simply reference the STL and receive the following errror:
    • RuntimeError: ProximityEngine: expect an Obj file for non-hydroelastics but get .stl file /workspaces/bdai/ws/install/spot_description/share/spot_description/meshes/base/collision/body_collision.stl) instead.
  2. Try adding the tag <drake:compliant_hydroelastic>
    • RuntimeError: A mesh must contain at least one tetrahedron
  3. Try switching to <drake:rigid_hydroelastic>:
    • RuntimeError: hydroelastic::MakeRigidRepresentation(): unsupported mesh file: /workspaces/bdai/ws/install/spot_description/share/spot_description/meshes/base/collision/body_collision.stl

The real problem is:

  1. Drake doesn't consume STL files. Period. And, more generally, supports a limited domain of mesh files.
  2. Users should be given direct feedback on this subject with clear directions on guidance for converting.

Proposal:

  1. SceneGraph should take responsibility for assessing Mesh types upon registration.
    • If it's an unsupported type, SceneGraph should make that clear and direct the user to a drake webpage discussing how to get supported geometry.
    • For the legacy behavior of, "you've specified an STL but have an identically named OBJ next to it", we should emit a warning right then (that we are swapping the STL for the OBJ) and then swap it internally so no downstream geometry consumers have to worry about it.

Some nuances:

  1. We support more than OBJ. We also support VTK, but for limited purposes (e.g., proximity with compliant hydorelastic.). The principle of "the mesh may depend on the application" is a principle that will grow (e.g., gltf for perception roles). So, we may need to defer some of the final validation until we see what role is applied. But this would still lie within SceneGraph's purview (see AssignRole()).
  2. This lies outside the domain of parsing (which would be a natural home). Do we want this validation to be in stand-alone functions that parsing and SceneGraph can use?
  3. The page to which we refer the reader may not yet exist and may need to be created.

Version

No response

What operating system are you using?

No response

What installation option are you using?

No response

Relevant log output

No response

SeanCurtis-TRI avatar May 15 '23 13:05 SeanCurtis-TRI

See also #19055.

jwnimmer-tri avatar May 15 '23 15:05 jwnimmer-tri

Just got bit by this. Until the issue is resolved, do you have a recommended workflow for doing the conversion? I see there is the stl2obj https://github.com/RobotLocomotion/drake/blob/316b8201810801cb537c9c0a5f0a05d2c6cecae7/manipulation/util/BUILD.bazel#L146 tool. Is this tool available if I installed the python version of drake using pip?

markisus avatar Jul 06 '23 17:07 markisus

I found your converter script using trimesh and adapted it to work for stl as well. Converting all stls and daes to obj fixed the error for me.

import argparse            
import pathlib
from trimesh import load_mesh
from trimesh.exchange.obj import export_obj

def convert(root, extension):
    for mesh_file in pathlib.Path(root).glob(f'*/**/*.{extension}'):
        print(f"Converting {mesh_file}")
        mesh = load_mesh(mesh_file)
        obj, data = export_obj(
                    mesh, return_texture=True, mtl_name=mesh_file.with_suffix('.mtl'))
        obj_file = mesh_file.with_suffix('.obj')
        with open(obj_file, "w") as f:
            f.write(obj)
            print(f"Wrote {obj_file}")
        # save the MTL and images                                
        for k, v in data.items():
            with open(k, 'wb') as f:
                f.write(v)
                print(f"Wrote {k}")

parser = argparse.ArgumentParser("Convert meshes to obj")
parser.add_argument("root", type=str, help="The root directory to glob for stl files")
parser.add_argument("--extension", type=str, default="stl", help="The type of mesh to convert. You may specify dae. Other extensions may work as well.")

args = parser.parse_args()

convert(args.root, extension=args.extension)

There is a bug in the current version of Trimesh that I had to resolve locally to fix meshes that had no associated materials. See https://github.com/mikedh/trimesh/issues/1970

markisus avatar Jul 06 '23 17:07 markisus

FYI I think the points above about giving good, early feedback in case of file type problems are right on point.

However, for the particular case of STL files, now that we have VTK packaged well I think it would be a good idea to add direct support for STL files. Possibly that rebalances the priorities here somewhat.

jwnimmer-tri avatar Oct 05 '23 22:10 jwnimmer-tri

Given my prior comment, I'm repurposing this issue:

The new call to action is either:

(1) When given an STL mesh, throw an error with a hyperlink to a troubleshooting guide that convincingly explains how to convert the model file to use a different supported file format -- either OBJ or glTF. Our MuJoCo parser has an epsilon version of this, but we need it for SDFormat / URDF parsers as well, and we still need a guide document for how to convert.

(2) Actually parse and support the STL meshes, for all geometry endpoints. (This means Meshcat, Meldis, all RenderEngines, implicit inertia calculations, etc.)

Obviously (2) is better if we can swing it. I anticipate it will not be that difficult.

jwnimmer-tri avatar Jul 09 '24 16:07 jwnimmer-tri

I also like the idea of a single prepare_model_for_drake.py. https://github.com/RobotLocomotion/drake/issues/19109#issuecomment-2219287999

RussTedrake avatar Jul 10 '24 01:07 RussTedrake

Note: I believe that this could become much easier to support once the in-memory mesh PR train lands.

RussTedrake avatar Sep 22 '24 19:09 RussTedrake

I anticipate that the geometry endpoints for all illustration (meshcat, meldis), all proximity, and most rendering (render engine vtk, render engine gltf client) would consume STL natively, without OBJ as an intermediary.

I am not sure exactly what the plan would be for render engine gl. The question there would be if we want to eschew vtk entirely from its dependency tree, in which case we'd need to find a standalone STL parser instead of using VTK conversion. Since the MbP would already pull in VTK to read the STL files for the proximity engine, probably using the VTK STL reader for render engine gl will be fine.

jwnimmer-tri avatar Sep 22 '24 20:09 jwnimmer-tri

bouncing to new component owner @SeanCurtis-TRI for re-triage.

rpoyner-tri avatar Sep 09 '25 20:09 rpoyner-tri