Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Fixed

* `Joint._create` no longer rotates `current_axis` once more every time it runs. It transformed the axis in place instead of recomputing it from `axis`, so re-initializing the transformation tree of a model that has joints (e.g. after its structure changed) left the joint axes wrong — 90 degrees out per extra call for a joint whose origin is rotated by 90 degrees. Models built or loaded in one pass were unaffected, which is why this went unnoticed.
* The COLLADA mesh importer now skips empty and non-surface geometry instead of aborting files that otherwise contain valid meshes, and reads colors from Lambert, Blinn, and constant shaders in addition to Phong.

### Removed

Expand Down
34 changes: 24 additions & 10 deletions src/compas_robots/resources/mesh_importer.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,15 +95,18 @@ def tag(name):
M = M[0:4], M[4:8], M[8:12], M[12:16]
transform = Transformation.from_matrix(M)

# primitive elements can be any combination of:
# lines, linestrips, polygons, polylist, triangles, trifans, tristrips
# The current implementation only supports triangles and polylist of triangular meshes
# Primitive elements can be any combination of:
# lines, linestrips, polygons, polylist, triangles, trifans, tristrips.
# The current implementation only supports triangles and polylist surface meshes.
# Other primitives and empty geometry nodes are ignored because this importer returns
# face-based Mesh objects. Raise only if the complete file has no supported surfaces.
primitive_element_sets = []
primitive_element_sets.extend(mesh_xml.findall(tag("triangles")))
primitive_element_sets.extend(mesh_xml.findall(tag("polylist")))

if len(primitive_element_sets) == 0:
raise Exception("No primitive elements found (currently only triangles and polylist are supported)")
LOGGER.debug("Skipping Collada geometry %s because it contains no supported surface primitives", mesh_id)
continue

for primitive_element_set in primitive_element_sets:
primitive_tag = _xml_local_name(primitive_element_set.tag)
Expand All @@ -124,12 +127,20 @@ def tag(name):
if instance_effect is not None:
instance_effect_id = instance_effect.attrib["url"][1:]
effect = effects.find('{}[@id="{}"]'.format(tag("effect"), instance_effect_id))
phong = effect.find("{}/{}/{}".format(tag("profile_COMMON"), tag("technique"), tag("phong")))
colors = phong.findall(".//{}".format(tag("color")))
for color_node in colors:
rgba = [float(i) for i in color_node.text.split()]
if "sid" in color_node.attrib:
mesh_colors["mesh_color.{}".format(color_node.attrib["sid"])] = rgba
technique = effect.find("{}/{}".format(tag("profile_COMMON"), tag("technique")))
shader = None

for shader_name in ("phong", "lambert", "blinn", "constant"):
shader = technique.find(tag(shader_name))
if shader is not None:
break

if shader is not None:
colors = shader.findall(".//{}".format(tag("color")))
for color_node in colors:
rgba = [float(i) for i in color_node.text.split()]
if "sid" in color_node.attrib:
mesh_colors["mesh_color.{}".format(color_node.attrib["sid"])] = rgba
except Exception:
LOGGER.exception("Exception while loading materials, all materials of mesh file %s will be ignored ", filename)

Expand Down Expand Up @@ -191,4 +202,7 @@ def tag(name):

meshes.append(mesh)

if not meshes:
raise Exception("No supported surface primitives found (currently only triangles and polylist are supported)")

return meshes
61 changes: 61 additions & 0 deletions tests/fixtures/surfaces_lines_and_empty_geometry.dae
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<?xml version="1.0" encoding="utf-8"?>
<COLLADA xmlns="http://www.collada.org/2005/11/COLLADASchema" version="1.4.1">
<library_effects>
<effect id="red-effect">
<profile_COMMON>
<technique sid="common">
<lambert>
<diffuse>
<color sid="diffuse">1 0 0 1</color>
</diffuse>
</lambert>
</technique>
</profile_COMMON>
</effect>
</library_effects>
<library_materials>
<material id="red-material">
<instance_effect url="#red-effect"/>
</material>
</library_materials>
<library_geometries>
<geometry id="surface">
<mesh>
<source id="surface-positions">
<float_array id="surface-positions-array" count="9">0 0 0 1 0 0 0 1 0</float_array>
</source>
<vertices id="surface-vertices">
<input semantic="POSITION" source="#surface-positions"/>
</vertices>
<triangles material="red-material" count="1">
<input semantic="VERTEX" source="#surface-vertices" offset="0"/>
<p>0 1 2</p>
</triangles>
</mesh>
</geometry>
<geometry id="lines-only">
<mesh>
<source id="line-positions">
<float_array id="line-positions-array" count="6">0 0 0 1 0 0</float_array>
</source>
<vertices id="line-vertices">
<input semantic="POSITION" source="#line-positions"/>
</vertices>
<lines count="1">
<input semantic="VERTEX" source="#line-vertices" offset="0"/>
<p>0 1</p>
</lines>
</mesh>
</geometry>
<geometry id="empty">
<mesh>
<source id="empty-positions">
<float_array id="empty-positions-array" count="0"/>
</source>
<vertices id="empty-vertices">
<input semantic="POSITION" source="#empty-positions"/>
</vertices>
</mesh>
</geometry>
</library_geometries>
</COLLADA>
11 changes: 11 additions & 0 deletions tests/test_resources.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,14 @@ def test_mesh_import_namespaced_collada():
assert meshes[0].number_of_faces() == 2598
assert meshes[1].number_of_vertices() == 1158
assert meshes[1].number_of_faces() == 2240


def test_mesh_import_collada_ignores_non_surface_geometry_and_loads_lambert_material():
filename = os.path.join(os.path.dirname(__file__), "fixtures", "surfaces_lines_and_empty_geometry.dae")

meshes = mesh_import(filename, filename)

assert len(meshes) == 1
assert meshes[0].number_of_vertices() == 3
assert meshes[0].number_of_faces() == 1
assert meshes[0].attributes["mesh_color.diffuse"] == [1.0, 0.0, 0.0, 1.0]