def _load_materials(material_names: List[str], f, data_dir: str, *, load_textures: bool, device): """ Load materials and optionally textures from the specified path. Args: material_names: a list of the material names found in the .obj file. f: a file-like object of the material information. data_dir: the directory where the material texture files are located. load_textures: whether textures should be loaded. device: string or torch.device on which to return the new tensors. Returns: material_colors: dict of properties for each material. texture_images: dict of material names and texture images. """ if not load_textures: return None, None if not material_names or f is None: if material_names: warnings.warn("No mtl file provided") return None, None if not os.path.isfile(f): warnings.warn(f"Mtl file does not exist: {f}") return None, None # Texture mode uv wrap return load_mtl(f, material_names, data_dir, device=device)
def _load_materials( material_names: List[str], f: Optional[str], *, data_dir: str, load_textures: bool, device: Device, path_manager: PathManager, ): """ Load materials and optionally textures from the specified path. Args: material_names: a list of the material names found in the .obj file. f: path to the material information. data_dir: the directory where the material texture files are located. load_textures: whether textures should be loaded. device: Device (as str or torch.device) on which to return the new tensors. path_manager: PathManager object to interpret paths. Returns: material_colors: dict of properties for each material. texture_images: dict of material names and texture images. """ if not load_textures: return None, None if not material_names or f is None: if material_names: warnings.warn("No mtl file provided") return None, None if not path_manager.exists(f): warnings.warn(f"Mtl file does not exist: {f}") return None, None # Texture mode uv wrap return load_mtl( f, material_names=material_names, data_dir=data_dir, path_manager=path_manager, device=device, )
def _load_obj( f_obj, data_dir, load_textures: bool = True, create_texture_atlas: bool = False, texture_atlas_size: int = 4, texture_wrap: Optional[str] = "repeat", device="cpu", ): """ Load a mesh from a file-like object. See load_obj function more details. Any material files associated with the obj are expected to be in the directory given by data_dir. """ if texture_wrap is not None and texture_wrap not in ["repeat", "clamp"]: msg = "texture_wrap must be one of ['repeat', 'clamp'] or None, got %s" raise ValueError(msg % texture_wrap) lines = [line.strip() for line in f_obj] verts = [] normals = [] verts_uvs = [] faces_verts_idx = [] faces_normals_idx = [] faces_textures_idx = [] material_names = [] faces_materials_idx = [] f_mtl = None materials_idx = -1 # startswith expects each line to be a string. If the file is read in as # bytes then first decode to strings. if lines and isinstance(lines[0], bytes): lines = [el.decode("utf-8") for el in lines] for line in lines: if line.startswith("mtllib"): if len(line.split()) < 2: raise ValueError("material file name is not specified") # NOTE: only allow one .mtl file per .obj. # Definitions for multiple materials can be included # in this one .mtl file. f_mtl = os.path.join(data_dir, line.split()[1]) elif len(line.split()) != 0 and line.split()[0] == "usemtl": material_name = line.split()[1] # materials are often repeated for different parts # of a mesh. if material_name not in material_names: material_names.append(material_name) materials_idx = len(material_names) - 1 else: materials_idx = material_names.index(material_name) elif line.startswith("v "): # Line is a vertex. vert = [float(x) for x in line.split()[1:4]] if len(vert) != 3: msg = "Vertex %s does not have 3 values. Line: %s" raise ValueError(msg % (str(vert), str(line))) verts.append(vert) elif line.startswith("vt "): # Line is a texture. tx = [float(x) for x in line.split()[1:3]] if len(tx) != 2: raise ValueError( "Texture %s does not have 2 values. Line: %s" % (str(tx), str(line)) ) verts_uvs.append(tx) elif line.startswith("vn "): # Line is a normal. norm = [float(x) for x in line.split()[1:4]] if len(norm) != 3: msg = "Normal %s does not have 3 values. Line: %s" raise ValueError(msg % (str(norm), str(line))) normals.append(norm) elif line.startswith("f "): # Line is a face update face properties info. _parse_face( line, materials_idx, faces_verts_idx, faces_normals_idx, faces_textures_idx, faces_materials_idx, ) verts = _make_tensor(verts, cols=3, dtype=torch.float32, device=device) # (V, 3) normals = _make_tensor( normals, cols=3, dtype=torch.float32, device=device ) # (N, 3) verts_uvs = _make_tensor( verts_uvs, cols=2, dtype=torch.float32, device=device ) # (T, 2) faces_verts_idx = _format_faces_indices( faces_verts_idx, verts.shape[0], device=device ) # Repeat for normals and textures if present. if len(faces_normals_idx) > 0: faces_normals_idx = _format_faces_indices( faces_normals_idx, normals.shape[0], device=device, pad_value=-1 ) if len(faces_textures_idx) > 0: faces_textures_idx = _format_faces_indices( faces_textures_idx, verts_uvs.shape[0], device=device, pad_value=-1 ) if len(faces_materials_idx) > 0: faces_materials_idx = torch.tensor( faces_materials_idx, dtype=torch.int64, device=device ) # Load materials material_colors, texture_images, texture_atlas = None, None, None if load_textures: if (len(material_names) > 0) and (f_mtl is not None): # pyre-fixme[6]: Expected `Union[_PathLike[typing.Any], bytes, str]` for # 1st param but got `Optional[str]`. if os.path.isfile(f_mtl): # Texture mode uv wrap material_colors, texture_images = load_mtl( f_mtl, material_names, data_dir, device=device ) if create_texture_atlas: # Using the images and properties from the # material file make a per face texture map. # Create an array of strings of material names for each face. # If faces_materials_idx == -1 then that face doesn't have a material. idx = faces_materials_idx.cpu().numpy() face_material_names = np.array(material_names)[idx] # (F,) face_material_names[idx == -1] = "" # Get the uv coords for each vert in each face faces_verts_uvs = verts_uvs[faces_textures_idx] # (F, 3, 2) # Construct the atlas. texture_atlas = make_mesh_texture_atlas( material_colors, texture_images, face_material_names, faces_verts_uvs, texture_atlas_size, texture_wrap, ) else: warnings.warn(f"Mtl file does not exist: {f_mtl}") elif len(material_names) > 0: warnings.warn("No mtl file provided") faces = _Faces( verts_idx=faces_verts_idx, normals_idx=faces_normals_idx, textures_idx=faces_textures_idx, materials_idx=faces_materials_idx, ) aux = _Aux( normals=normals if len(normals) > 0 else None, verts_uvs=verts_uvs if len(verts_uvs) > 0 else None, material_colors=material_colors, texture_images=texture_images, texture_atlas=texture_atlas, ) return verts, faces, aux