Ejemplo n.º 1
0
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)
Ejemplo n.º 2
0
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,
    )
Ejemplo n.º 3
0
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