Skip to content

Commit be935ba

Browse files
Merge pull request #26 from ft-lab/dev/yyoshisaka/convert_stl_to_usd
Convert STL to USD
2 parents 348c558 + fdb2242 commit be935ba

File tree

3 files changed

+151
-92
lines changed

3 files changed

+151
-92
lines changed

tests/data/assets/box.stl

Lines changed: 86 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -1,86 +1,86 @@
1-
solid stdin
2-
facet normal 0.000000000e+00 0.000000000e+00 1.000000000e+00
3-
outer loop
4-
vertex -5.000000000e-01 5.000000000e-01 5.000000000e-01
5-
vertex 5.000000000e-01 5.000000000e-01 5.000000000e-01
6-
vertex 5.000000000e-01 -5.000000000e-01 5.000000000e-01
7-
endloop
8-
endfacet
9-
facet normal 0.000000000e+00 0.000000000e+00 -1.000000000e+00
10-
outer loop
11-
vertex 5.000000000e-01 5.000000000e-01 -5.000000000e-01
12-
vertex -5.000000000e-01 5.000000000e-01 -5.000000000e-01
13-
vertex -5.000000000e-01 -5.000000000e-01 -5.000000000e-01
14-
endloop
15-
endfacet
16-
facet normal 0.000000000e+00 1.000000000e+00 0.000000000e+00
17-
outer loop
18-
vertex 5.000000000e-01 5.000000000e-01 5.000000000e-01
19-
vertex -5.000000000e-01 5.000000000e-01 5.000000000e-01
20-
vertex -5.000000000e-01 5.000000000e-01 -5.000000000e-01
21-
endloop
22-
endfacet
23-
facet normal 0.000000000e+00 -1.000000000e+00 0.000000000e+00
24-
outer loop
25-
vertex -5.000000000e-01 -5.000000000e-01 5.000000000e-01
26-
vertex 5.000000000e-01 -5.000000000e-01 5.000000000e-01
27-
vertex 5.000000000e-01 -5.000000000e-01 -5.000000000e-01
28-
endloop
29-
endfacet
30-
facet normal 1.000000000e+00 0.000000000e+00 0.000000000e+00
31-
outer loop
32-
vertex 5.000000000e-01 5.000000000e-01 5.000000000e-01
33-
vertex 5.000000000e-01 5.000000000e-01 -5.000000000e-01
34-
vertex 5.000000000e-01 -5.000000000e-01 -5.000000000e-01
35-
endloop
36-
endfacet
37-
facet normal -1.000000000e+00 0.000000000e+00 0.000000000e+00
38-
outer loop
39-
vertex -5.000000000e-01 5.000000000e-01 -5.000000000e-01
40-
vertex -5.000000000e-01 5.000000000e-01 5.000000000e-01
41-
vertex -5.000000000e-01 -5.000000000e-01 5.000000000e-01
42-
endloop
43-
endfacet
44-
facet normal -1.000000000e+00 0.000000000e+00 0.000000000e+00
45-
outer loop
46-
vertex -5.000000000e-01 -5.000000000e-01 5.000000000e-01
47-
vertex -5.000000000e-01 -5.000000000e-01 -5.000000000e-01
48-
vertex -5.000000000e-01 5.000000000e-01 -5.000000000e-01
49-
endloop
50-
endfacet
51-
facet normal 1.000000000e+00 0.000000000e+00 0.000000000e+00
52-
outer loop
53-
vertex 5.000000000e-01 -5.000000000e-01 -5.000000000e-01
54-
vertex 5.000000000e-01 -5.000000000e-01 5.000000000e-01
55-
vertex 5.000000000e-01 5.000000000e-01 5.000000000e-01
56-
endloop
57-
endfacet
58-
facet normal 0.000000000e+00 -1.000000000e+00 0.000000000e+00
59-
outer loop
60-
vertex 5.000000000e-01 -5.000000000e-01 -5.000000000e-01
61-
vertex -5.000000000e-01 -5.000000000e-01 -5.000000000e-01
62-
vertex -5.000000000e-01 -5.000000000e-01 5.000000000e-01
63-
endloop
64-
endfacet
65-
facet normal 0.000000000e+00 1.000000000e+00 0.000000000e+00
66-
outer loop
67-
vertex -5.000000000e-01 5.000000000e-01 -5.000000000e-01
68-
vertex 5.000000000e-01 5.000000000e-01 -5.000000000e-01
69-
vertex 5.000000000e-01 5.000000000e-01 5.000000000e-01
70-
endloop
71-
endfacet
72-
facet normal 0.000000000e+00 0.000000000e+00 -1.000000000e+00
73-
outer loop
74-
vertex -5.000000000e-01 -5.000000000e-01 -5.000000000e-01
75-
vertex 5.000000000e-01 -5.000000000e-01 -5.000000000e-01
76-
vertex 5.000000000e-01 5.000000000e-01 -5.000000000e-01
77-
endloop
78-
endfacet
79-
facet normal 0.000000000e+00 0.000000000e+00 1.000000000e+00
80-
outer loop
81-
vertex 5.000000000e-01 -5.000000000e-01 5.000000000e-01
82-
vertex -5.000000000e-01 -5.000000000e-01 5.000000000e-01
83-
vertex -5.000000000e-01 5.000000000e-01 5.000000000e-01
84-
endloop
85-
endfacet
86-
endsolid
1+
solid
2+
facet normal -1 -0 -0
3+
outer loop
4+
vertex -0.5 -0.5 -0.5
5+
vertex -0.5 -0.5 0.5
6+
vertex -0.5 0.5 0.5
7+
endloop
8+
endfacet
9+
facet normal -1 -0 0
10+
outer loop
11+
vertex -0.5 -0.5 -0.5
12+
vertex -0.5 0.5 0.5
13+
vertex -0.5 0.5 -0.5
14+
endloop
15+
endfacet
16+
facet normal 0 1 0
17+
outer loop
18+
vertex -0.5 0.5 -0.5
19+
vertex -0.5 0.5 0.5
20+
vertex 0.5 0.5 0.5
21+
endloop
22+
endfacet
23+
facet normal 0 1 -0
24+
outer loop
25+
vertex -0.5 0.5 -0.5
26+
vertex 0.5 0.5 0.5
27+
vertex 0.5 0.5 -0.5
28+
endloop
29+
endfacet
30+
facet normal 1 -0 0
31+
outer loop
32+
vertex 0.5 0.5 -0.5
33+
vertex 0.5 0.5 0.5
34+
vertex 0.5 -0.5 0.5
35+
endloop
36+
endfacet
37+
facet normal 1 -0 0
38+
outer loop
39+
vertex 0.5 0.5 -0.5
40+
vertex 0.5 -0.5 0.5
41+
vertex 0.5 -0.5 -0.5
42+
endloop
43+
endfacet
44+
facet normal 0 -1 0
45+
outer loop
46+
vertex 0.5 -0.5 -0.5
47+
vertex 0.5 -0.5 0.5
48+
vertex -0.5 -0.5 0.5
49+
endloop
50+
endfacet
51+
facet normal 0 -1 0
52+
outer loop
53+
vertex 0.5 -0.5 -0.5
54+
vertex -0.5 -0.5 0.5
55+
vertex -0.5 -0.5 -0.5
56+
endloop
57+
endfacet
58+
facet normal 0 0 -1
59+
outer loop
60+
vertex -0.5 0.5 -0.5
61+
vertex 0.5 0.5 -0.5
62+
vertex 0.5 -0.5 -0.5
63+
endloop
64+
endfacet
65+
facet normal 0 0 -1
66+
outer loop
67+
vertex -0.5 0.5 -0.5
68+
vertex 0.5 -0.5 -0.5
69+
vertex -0.5 -0.5 -0.5
70+
endloop
71+
endfacet
72+
facet normal 0 0 1
73+
outer loop
74+
vertex 0.5 0.5 0.5
75+
vertex -0.5 0.5 0.5
76+
vertex -0.5 -0.5 0.5
77+
endloop
78+
endfacet
79+
facet normal 0 -0 1
80+
outer loop
81+
vertex 0.5 0.5 0.5
82+
vertex -0.5 -0.5 0.5
83+
vertex 0.5 -0.5 0.5
84+
endloop
85+
endfacet
86+
endsolid

tests/testMesh.py

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,28 +3,60 @@
33
import pathlib
44

55
import usdex.test
6-
from pxr import Tf
6+
from pxr import Tf, Usd, UsdGeom
77

88
import urdf_usd_converter
99
from tests.util.ConverterTestCase import ConverterTestCase
1010

1111

1212
class TestMesh(ConverterTestCase):
13-
def test_mesh_conversion(self):
13+
def setUp(self):
14+
super().setUp()
15+
1416
input_path = "tests/data/meshes.urdf"
1517
output_dir = self.tmpDir()
1618

1719
converter = urdf_usd_converter.Converter()
1820
with usdex.test.ScopedDiagnosticChecker(
1921
self,
2022
[
21-
(Tf.TF_DIAGNOSTIC_WARNING_TYPE, ".*The stl format is not yet supported:.*"),
2223
(Tf.TF_DIAGNOSTIC_WARNING_TYPE, ".*The obj format is not yet supported:.*"),
2324
(Tf.TF_DIAGNOSTIC_WARNING_TYPE, ".*The dae format is not yet supported:.*"),
2425
(Tf.TF_DIAGNOSTIC_WARNING_TYPE, ".*Unsupported mesh format:.*"),
2526
],
2627
level=usdex.core.DiagnosticsLevel.eWarning,
2728
):
2829
asset_path = converter.convert(input_path, output_dir)
30+
2931
self.assertIsNotNone(asset_path)
3032
self.assertTrue(pathlib.Path(asset_path.path).exists())
33+
34+
self.stage: Usd.Stage = Usd.Stage.Open(asset_path.path)
35+
self.assertIsValidUsd(self.stage)
36+
37+
def test_stl_mesh(self):
38+
default_prim = self.stage.GetDefaultPrim()
39+
geometry_scope_prim = self.stage.GetPrimAtPath(default_prim.GetPath().AppendChild("Geometry"))
40+
self.assertTrue(geometry_scope_prim.IsValid())
41+
42+
# Test STL mesh conversion
43+
link_stl_prim = self.stage.GetPrimAtPath(geometry_scope_prim.GetPath().AppendChild("link_mesh_stl"))
44+
self.assertTrue(link_stl_prim.IsValid())
45+
self.assertTrue(link_stl_prim.IsA(UsdGeom.Xform))
46+
47+
stl_mesh_prim = self.stage.GetPrimAtPath(link_stl_prim.GetPath().AppendChild("box"))
48+
self.assertTrue(stl_mesh_prim.IsValid())
49+
self.assertTrue(stl_mesh_prim.IsA(UsdGeom.Mesh))
50+
self.assertTrue(stl_mesh_prim.HasAuthoredReferences())
51+
52+
usd_mesh_stl = UsdGeom.Mesh(stl_mesh_prim)
53+
self.assertTrue(usd_mesh_stl.GetPointsAttr().HasAuthoredValue())
54+
self.assertTrue(usd_mesh_stl.GetFaceVertexCountsAttr().HasAuthoredValue())
55+
self.assertTrue(usd_mesh_stl.GetFaceVertexIndicesAttr().HasAuthoredValue())
56+
# The sample box.stl has normals and they are authored as a primvar
57+
self.assertFalse(usd_mesh_stl.GetNormalsAttr().HasAuthoredValue())
58+
normals_primvar: UsdGeom.Primvar = UsdGeom.PrimvarsAPI(usd_mesh_stl).GetPrimvar("normals")
59+
self.assertTrue(normals_primvar.IsDefined())
60+
self.assertTrue(normals_primvar.HasAuthoredValue())
61+
self.assertTrue(normals_primvar.GetIndicesAttr().HasAuthoredValue())
62+
self.assertEqual(UsdGeom.Imageable(stl_mesh_prim).GetPurposeAttr().Get(), UsdGeom.Tokens.default_)

urdf_usd_converter/_impl/mesh.py

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@
22
# SPDX-License-Identifier: Apache-2.0
33
import pathlib
44

5+
import stl
56
import usdex.core
6-
from pxr import Tf, Usd
7+
from pxr import Tf, Usd, UsdGeom, Vt
78

89
from .data import ConversionData, Tokens
10+
from .numpy import convert_vec3f_array
911

1012
__all__ = ["convert_meshes"]
1113

@@ -51,8 +53,7 @@ def convert_meshes(data: ConversionData):
5153
def convert_mesh(prim: Usd.Prim, input_path: pathlib.Path, data: ConversionData):
5254
mesh_prim = None
5355
if input_path.suffix.lower() == ".stl":
54-
# TODO: Implement STL conversion.
55-
Tf.Warn(f"The stl format is not yet supported: {input_path}")
56+
mesh_prim = convert_stl(prim, input_path, data)
5657
elif input_path.suffix.lower() == ".obj":
5758
# TODO: Implement OBJ conversion.
5859
Tf.Warn(f"The obj format is not yet supported: {input_path}")
@@ -63,3 +64,29 @@ def convert_mesh(prim: Usd.Prim, input_path: pathlib.Path, data: ConversionData)
6364
Tf.Warn(f"Unsupported mesh format: {input_path}")
6465

6566
return mesh_prim
67+
68+
69+
def convert_stl(prim: Usd.Prim, input_path: pathlib.Path, data: ConversionData) -> UsdGeom.Mesh:
70+
stl_mesh = stl.Mesh.from_file(input_path, calculate_normals=False)
71+
72+
points = usdex.core.Vec3fPrimvarData(UsdGeom.Tokens.vertex, convert_vec3f_array(stl_mesh.points))
73+
points.index()
74+
face_vertex_indices = points.indices()
75+
face_vertex_counts = [3] * stl_mesh.points.shape[0]
76+
77+
normals = None
78+
if stl_mesh.normals.any():
79+
normals = usdex.core.Vec3fPrimvarData(UsdGeom.Tokens.uniform, convert_vec3f_array(stl_mesh.normals))
80+
normals.index()
81+
82+
usd_mesh = usdex.core.definePolyMesh(
83+
prim.GetParent(),
84+
prim.GetName(),
85+
faceVertexCounts=Vt.IntArray(face_vertex_counts),
86+
faceVertexIndices=Vt.IntArray(face_vertex_indices),
87+
points=points.values(),
88+
normals=normals,
89+
)
90+
if not usd_mesh:
91+
Tf.RaiseRuntimeError(f'Failed to convert mesh "{prim.GetPath()}" from {input_path}')
92+
return usd_mesh

0 commit comments

Comments
 (0)