def load_objs_as_meshes(files: list, device=None, load_textures: bool = True): """ Load meshes from a list of .obj files using the load_obj function, and return them as a Meshes object. This only works for meshes which have a single texture image for the whole mesh. See the load_obj function for more details. material_colors and normals are not stored. Args: f: A list of file-like objects (with methods read, readline, tell, and seek), pathlib paths or strings containing file names. device: Desired device of returned Meshes. Default: uses the current device for the default tensor type. load_textures: Boolean indicating whether material files are loaded Returns: New Meshes object. """ mesh_list = [] for f_obj in files: verts, faces, aux = load_obj(f_obj, load_textures=load_textures) verts = verts.to(device) tex = None tex_maps = aux.texture_images if tex_maps is not None and len(tex_maps) > 0: verts_uvs = aux.verts_uvs[None, ...].to(device) # (1, V, 2) faces_uvs = faces.textures_idx[None, ...].to(device) # (1, F, 3) image = list(tex_maps.values())[0].to(device)[None] tex = Textures(verts_uvs=verts_uvs, faces_uvs=faces_uvs, maps=image) mesh = Meshes(verts=[verts], faces=[faces.verts_idx.to(device)], textures=tex) mesh_list.append(mesh) if len(mesh_list) == 1: return mesh_list[0] return join_meshes(mesh_list)
def test_join_meshes(self): """ Test that join_meshes and load_objs_as_meshes are consistent with single meshes. """ def check_triple(mesh, mesh3): """ Verify that mesh3 is three copies of mesh. """ def check_item(x, y): self.assertEqual(x is None, y is None) if x is not None: self.assertClose(torch.cat([x, x, x]), y) check_item(mesh.verts_padded(), mesh3.verts_padded()) check_item(mesh.faces_padded(), mesh3.faces_padded()) if mesh.textures is not None: check_item(mesh.textures.maps_padded(), mesh3.textures.maps_padded()) check_item( mesh.textures.faces_uvs_padded(), mesh3.textures.faces_uvs_padded(), ) check_item( mesh.textures.verts_uvs_padded(), mesh3.textures.verts_uvs_padded(), ) check_item( mesh.textures.verts_rgb_padded(), mesh3.textures.verts_rgb_padded(), ) DATA_DIR = (Path(__file__).resolve().parent.parent / "docs/tutorials/data") obj_filename = DATA_DIR / "cow_mesh/cow.obj" mesh = load_objs_as_meshes([obj_filename]) mesh3 = load_objs_as_meshes([obj_filename, obj_filename, obj_filename]) check_triple(mesh, mesh3) self.assertTupleEqual(mesh.textures.maps_padded().shape, (1, 1024, 1024, 3)) mesh_notex = load_objs_as_meshes([obj_filename], load_textures=False) mesh3_notex = load_objs_as_meshes( [obj_filename, obj_filename, obj_filename], load_textures=False) check_triple(mesh_notex, mesh3_notex) self.assertIsNone(mesh_notex.textures) verts = torch.randn((4, 3), dtype=torch.float32) faces = torch.tensor([[2, 1, 0], [3, 1, 0]], dtype=torch.int64) vert_tex = torch.tensor([[0, 1, 0], [0, 1, 1], [1, 1, 0], [1, 1, 1]], dtype=torch.float32) tex = Textures(verts_rgb=vert_tex[None, :]) mesh_rgb = Meshes(verts=[verts], faces=[faces], textures=tex) mesh_rgb3 = join_meshes([mesh_rgb, mesh_rgb, mesh_rgb]) check_triple(mesh_rgb, mesh_rgb3) teapot_obj = DATA_DIR / "teapot.obj" mesh_teapot = load_objs_as_meshes([teapot_obj]) teapot_verts, teapot_faces = mesh_teapot.get_mesh_verts_faces(0) mix_mesh = load_objs_as_meshes([obj_filename, teapot_obj], load_textures=False) self.assertEqual(len(mix_mesh), 2) self.assertClose(mix_mesh.verts_list()[0], mesh.verts_list()[0]) self.assertClose(mix_mesh.faces_list()[0], mesh.faces_list()[0]) self.assertClose(mix_mesh.verts_list()[1], teapot_verts) self.assertClose(mix_mesh.faces_list()[1], teapot_faces) cow3_tea = join_meshes([mesh3, mesh_teapot], include_textures=False) self.assertEqual(len(cow3_tea), 4) check_triple(mesh_notex, cow3_tea[:3]) self.assertClose(cow3_tea.verts_list()[3], mesh_teapot.verts_list()[0]) self.assertClose(cow3_tea.faces_list()[3], mesh_teapot.faces_list()[0])